aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitattributes16
-rw-r--r--.github/workflows/codeql-analysis.yml42
-rw-r--r--cmake.deps/CMakeLists.txt5
-rw-r--r--cmake.deps/cmake/BuildGettext.cmake4
-rw-r--r--cmake.deps/cmake/BuildLibiconv.cmake4
-rw-r--r--cmake.deps/cmake/BuildLibtermkey.cmake2
-rw-r--r--cmake.deps/cmake/BuildLibvterm.cmake7
-rw-r--r--cmake.deps/cmake/BuildLuv.cmake2
-rw-r--r--cmake.deps/cmake/BuildMsgpack.cmake3
-rw-r--r--cmake.deps/cmake/BuildTreesitter.cmake2
-rw-r--r--cmake.deps/cmake/BuildTreesitterParsers.cmake2
-rw-r--r--cmake.deps/cmake/BuildUnibilium.cmake2
-rw-r--r--cmake.deps/cmake/GettextCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/LibiconvCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/LibvtermCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/TreesitterCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/TreesitterParserCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/UnibiliumCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/libtermkeyCMakeLists.txt2
-rw-r--r--runtime/doc/api.txt43
-rw-r--r--runtime/doc/editing.txt11
-rw-r--r--runtime/doc/index.txt1
-rw-r--r--runtime/doc/lsp.txt34
-rw-r--r--runtime/doc/motion.txt3
-rw-r--r--runtime/doc/options.txt13
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--runtime/doc/windows.txt1
-rw-r--r--runtime/ftplugin/elixir.vim20
-rw-r--r--runtime/lua/vim/lsp.lua5
-rw-r--r--runtime/lua/vim/lsp/buf.lua14
-rw-r--r--runtime/syntax/README.txt3
-rw-r--r--runtime/syntax/shared/README.txt2
-rw-r--r--runtime/syntax/shared/typescriptcommon.vim2099
-rw-r--r--runtime/syntax/swayconfig.vim8
-rw-r--r--runtime/syntax/typescript.vim2
-rw-r--r--runtime/syntax/typescriptreact.vim2
-rwxr-xr-xsrc/nvim/CMakeLists.txt17
-rw-r--r--src/nvim/api/buffer.c1
-rw-r--r--src/nvim/api/command.c4
-rw-r--r--src/nvim/api/extmark.c4
-rw-r--r--src/nvim/api/keysets.lua3
-rw-r--r--src/nvim/api/ui.c4
-rw-r--r--src/nvim/api/vim.c66
-rw-r--r--src/nvim/api/vimscript.c4
-rw-r--r--src/nvim/api/win_config.c2
-rw-r--r--src/nvim/api/window.c27
-rw-r--r--src/nvim/arglist.c1157
-rw-r--r--src/nvim/arglist.h11
-rw-r--r--src/nvim/autocmd.c2
-rw-r--r--src/nvim/buffer.c277
-rw-r--r--src/nvim/buffer.h2
-rw-r--r--src/nvim/buffer_defs.h14
-rw-r--r--src/nvim/change.c2
-rw-r--r--src/nvim/channel.c2
-rw-r--r--src/nvim/cursor.c2
-rw-r--r--src/nvim/debugger.c2
-rw-r--r--src/nvim/decoration.c54
-rw-r--r--src/nvim/decoration_provider.c6
-rw-r--r--src/nvim/decoration_provider.h1
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/digraph.c2
-rw-r--r--src/nvim/drawline.c2706
-rw-r--r--src/nvim/drawline.h24
-rw-r--r--src/nvim/drawscreen.c2325
-rw-r--r--src/nvim/drawscreen.h25
-rw-r--r--src/nvim/edit.c40
-rw-r--r--src/nvim/eval.c657
-rw-r--r--src/nvim/eval/funcs.c332
-rw-r--r--src/nvim/eval/userfunc.c35
-rw-r--r--src/nvim/eval/vars.c2
-rw-r--r--src/nvim/ex_cmds.c1156
-rw-r--r--src/nvim/ex_cmds.h6
-rw-r--r--src/nvim/ex_cmds.lua8
-rw-r--r--src/nvim/ex_cmds2.c527
-rw-r--r--src/nvim/ex_docmd.c225
-rw-r--r--src/nvim/ex_eval.c18
-rw-r--r--src/nvim/ex_getln.c77
-rw-r--r--src/nvim/ex_session.c1
-rw-r--r--src/nvim/file_search.c2
-rw-r--r--src/nvim/fileio.c36
-rw-r--r--src/nvim/fold.c8
-rw-r--r--src/nvim/getchar.c152
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/grid.c131
-rw-r--r--src/nvim/grid.h3
-rw-r--r--src/nvim/hardcopy.c9
-rw-r--r--src/nvim/help.c1178
-rw-r--r--src/nvim/help.h11
-rw-r--r--src/nvim/highlight.c234
-rw-r--r--src/nvim/highlight.h8
-rw-r--r--src/nvim/highlight_defs.h20
-rw-r--r--src/nvim/highlight_group.c45
-rw-r--r--src/nvim/if_cscope.c2
-rw-r--r--src/nvim/indent.c308
-rw-r--r--src/nvim/indent_c.c12
-rw-r--r--src/nvim/insexpand.c4
-rw-r--r--src/nvim/lua/executor.c4
-rw-r--r--src/nvim/lua/executor.h2
-rw-r--r--src/nvim/main.c27
-rw-r--r--src/nvim/match.c5
-rw-r--r--src/nvim/mbyte.c19
-rw-r--r--src/nvim/memline.c24
-rw-r--r--src/nvim/memory.c3
-rw-r--r--src/nvim/menu.c8
-rw-r--r--src/nvim/message.c53
-rw-r--r--src/nvim/mouse.c7
-rw-r--r--src/nvim/move.c26
-rw-r--r--src/nvim/normal.c16
-rw-r--r--src/nvim/ops.c25
-rw-r--r--src/nvim/option.c823
-rw-r--r--src/nvim/os/fs.c19
-rw-r--r--src/nvim/os/pty_conpty_win.h3
-rw-r--r--src/nvim/path.c24
-rw-r--r--src/nvim/popupmenu.c (renamed from src/nvim/popupmnu.c)28
-rw-r--r--src/nvim/popupmenu.h (renamed from src/nvim/popupmnu.h)8
-rw-r--r--src/nvim/quickfix.c142
-rw-r--r--src/nvim/regexp.c8
-rw-r--r--src/nvim/regexp_bt.c14
-rw-r--r--src/nvim/regexp_nfa.c4
-rw-r--r--src/nvim/runtime.c35
-rw-r--r--src/nvim/screen.c5521
-rw-r--r--src/nvim/screen.h34
-rw-r--r--src/nvim/search.c66
-rw-r--r--src/nvim/sign.c83
-rw-r--r--src/nvim/sign_defs.h14
-rw-r--r--src/nvim/spell.c127
-rw-r--r--src/nvim/spellfile.c75
-rw-r--r--src/nvim/state.c2
-rw-r--r--src/nvim/strings.c12
-rw-r--r--src/nvim/syntax.c48
-rw-r--r--src/nvim/tag.c33
-rw-r--r--src/nvim/terminal.c2
-rw-r--r--src/nvim/testdir/runtest.vim1
-rw-r--r--src/nvim/testdir/test_arglist.vim44
-rw-r--r--src/nvim/testdir/test_autocmd.vim53
-rw-r--r--src/nvim/testdir/test_bufline.vim2
-rw-r--r--src/nvim/testdir/test_cmdline.vim79
-rw-r--r--src/nvim/testdir/test_diffmode.vim2
-rw-r--r--src/nvim/testdir/test_edit.vim129
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim12
-rw-r--r--src/nvim/testdir/test_expand_func.vim27
-rw-r--r--src/nvim/testdir/test_filechanged.vim9
-rw-r--r--src/nvim/testdir/test_gf.vim16
-rw-r--r--src/nvim/testdir/test_global.vim5
-rw-r--r--src/nvim/testdir/test_goto.vim18
-rw-r--r--src/nvim/testdir/test_listdict.vim2
-rw-r--r--src/nvim/testdir/test_messages.vim65
-rw-r--r--src/nvim/testdir/test_normal.vim258
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim4
-rw-r--r--src/nvim/testdir/test_registers.vim11
-rw-r--r--src/nvim/testdir/test_selectmode.vim150
-rw-r--r--src/nvim/testdir/test_spellfile.vim39
-rw-r--r--src/nvim/testdir/test_substitute.vim38
-rw-r--r--src/nvim/testdir/test_tabpage.vim16
-rw-r--r--src/nvim/testdir/test_tagjump.vim13
-rw-r--r--src/nvim/testdir/test_textobjects.vim1
-rw-r--r--src/nvim/testdir/test_undo.vim6
-rw-r--r--src/nvim/testdir/test_visual.vim186
-rw-r--r--src/nvim/testdir/test_writefile.vim44
-rw-r--r--src/nvim/ui.c5
-rw-r--r--src/nvim/ui_compositor.c4
-rw-r--r--src/nvim/undo.c730
-rw-r--r--src/nvim/version.c3
-rw-r--r--src/nvim/vim.h4
-rw-r--r--src/nvim/window.c171
-rw-r--r--test/functional/api/highlight_spec.lua2
-rw-r--r--test/functional/api/vim_spec.lua48
-rw-r--r--test/functional/core/job_spec.lua5
-rw-r--r--test/functional/editor/ctrl_c_spec.lua12
-rw-r--r--test/functional/ex_cmds/oldfiles_spec.lua3
-rw-r--r--test/functional/ex_cmds/source_spec.lua59
-rw-r--r--test/functional/ex_cmds/wincmd_spec.lua13
-rw-r--r--test/functional/helpers.lua4
-rw-r--r--test/functional/legacy/ex_mode_spec.lua4
-rw-r--r--test/functional/legacy/filechanged_spec.lua9
-rw-r--r--test/functional/legacy/gf_spec.lua15
-rw-r--r--test/functional/legacy/global_spec.lua6
-rw-r--r--test/functional/legacy/mapping_spec.lua18
-rw-r--r--test/functional/legacy/messages_spec.lua89
-rw-r--r--test/functional/lua/overrides_spec.lua45
-rw-r--r--test/functional/provider/clipboard_spec.lua10
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua74
-rw-r--r--test/functional/ui/decorations_spec.lua8
-rw-r--r--test/functional/ui/highlight_spec.lua341
-rw-r--r--test/functional/ui/hlstate_spec.lua20
-rw-r--r--test/functional/ui/inccommand_spec.lua17
-rw-r--r--test/functional/ui/input_spec.lua29
-rw-r--r--test/functional/ui/messages_spec.lua13
-rw-r--r--test/functional/ui/mouse_spec.lua2
-rw-r--r--test/functional/ui/output_spec.lua29
-rw-r--r--test/functional/ui/screen.lua8
-rw-r--r--test/functional/ui/statusline_spec.lua2
-rw-r--r--test/functional/vimscript/null_spec.lua2
-rw-r--r--test/unit/indent_spec.lua30
-rw-r--r--test/unit/option_spec.lua24
195 files changed, 14142 insertions, 10656 deletions
diff --git a/.gitattributes b/.gitattributes
index e09a918303..1770c9b164 100755
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,16 @@
-*.h linguist-language=C
+*.h.in linguist-language=C
+*.c.in linguist-language=C
+*CMakeLists.txt linguist-language=CMake
+
+runtime/doc/* linguist-documentation
+
+src/xdiff/** linguist-vendored
+src/cjson/** linguist-vendored
+src/unicode/** linguist-vendored
+
src/nvim/testdir/test42.in diff
-.github/ export-ignore
+
+.github/ export-ignore
.travis.yml export-ignore
codecov.yml export-ignore
-.builds/ export-ignore
+.builds/ export-ignore
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000000..b31382af37
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,42 @@
+name: "CodeQL"
+
+on:
+ schedule:
+ - cron: '42 0 * * 0'
+ workflow_dispatch:
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'cpp', 'python' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Setup common environment variables
+ run: ./.github/workflows/env.sh
+
+ - name: Install apt packages
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y autoconf automake build-essential cmake cpanminus cscope gcc-multilib gdb gettext language-pack-tr libtool-bin locales ninja-build pkg-config python3 python3-pip python3-setuptools unzip valgrind xclip
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+
+ - if: matrix.language == 'cpp'
+ run: make
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 72d26d5127..27659b2a56 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -8,6 +8,11 @@ include(CheckCCompilerFlag)
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" "${PROJECT_SOURCE_DIR}/../cmake")
+get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(NOT isMultiConfig)
+ set(BUILD_TYPE_STRING -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})
+endif()
+
# In Windows/MSVC CMAKE_BUILD_TYPE changes the paths/linking of the build
# recipes (libuv, msgpack), make sure it is set
if(NOT CMAKE_BUILD_TYPE)
diff --git a/cmake.deps/cmake/BuildGettext.cmake b/cmake.deps/cmake/BuildGettext.cmake
index 6128ecfa69..f36c00c559 100644
--- a/cmake.deps/cmake/BuildGettext.cmake
+++ b/cmake.deps/cmake/BuildGettext.cmake
@@ -1,5 +1,4 @@
if(MSVC)
-
ExternalProject_Add(gettext
PREFIX ${DEPS_BUILD_DIR}
URL ${GETTEXT_URL}
@@ -19,14 +18,13 @@ if(MSVC)
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
# Pass toolchain
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DLIBICONV_INCLUDE_DIRS=${DEPS_INSTALL_DIR}/include
-DLIBICONV_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}libcharset${CMAKE_STATIC_LIBRARY_SUFFIX}$<SEMICOLON>${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}libiconv${CMAKE_STATIC_LIBRARY_SUFFIX}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
-
else()
message(FATAL_ERROR "Trying to build gettext in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
endif()
diff --git a/cmake.deps/cmake/BuildLibiconv.cmake b/cmake.deps/cmake/BuildLibiconv.cmake
index 5ff33e0cd3..434bfd6979 100644
--- a/cmake.deps/cmake/BuildLibiconv.cmake
+++ b/cmake.deps/cmake/BuildLibiconv.cmake
@@ -1,5 +1,4 @@
if(MSVC)
-
ExternalProject_Add(libiconv
PREFIX ${DEPS_BUILD_DIR}
URL ${LIBICONV_URL}
@@ -19,12 +18,11 @@ if(MSVC)
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
# Pass toolchain
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
-
else()
message(FATAL_ERROR "Trying to build libiconv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}")
endif()
diff --git a/cmake.deps/cmake/BuildLibtermkey.cmake b/cmake.deps/cmake/BuildLibtermkey.cmake
index d44e09d734..66bac57d23 100644
--- a/cmake.deps/cmake/BuildLibtermkey.cmake
+++ b/cmake.deps/cmake/BuildLibtermkey.cmake
@@ -18,7 +18,7 @@ ExternalProject_Add(libtermkey
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
# Pass toolchain
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
# Hack to avoid -rdynamic in Mingw
-DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS=""
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
diff --git a/cmake.deps/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake
index 2c300dda7c..a905733abc 100644
--- a/cmake.deps/cmake/BuildLibvterm.cmake
+++ b/cmake.deps/cmake/BuildLibvterm.cmake
@@ -48,8 +48,13 @@ if(WIN32)
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC"
-DCMAKE_GENERATOR=${CMAKE_GENERATOR})
+
+ if(MSVC)
+ list(APPEND LIBVTERM_CONFIGURE_COMMAND "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}")
+ else()
+ list(APPEND LIBVTERM_CONFIGURE_COMMAND "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC")
+ endif()
set(LIBVTERM_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE})
set(LIBVTERM_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
else()
diff --git a/cmake.deps/cmake/BuildLuv.cmake b/cmake.deps/cmake/BuildLuv.cmake
index eb60a6496e..f960b24992 100644
--- a/cmake.deps/cmake/BuildLuv.cmake
+++ b/cmake.deps/cmake/BuildLuv.cmake
@@ -59,7 +59,7 @@ set(LUV_INCLUDE_FLAGS
set(LUV_CONFIGURE_COMMAND_COMMON
${CMAKE_COMMAND} ${LUV_SRC_DIR}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
-DLUA_BUILD_TYPE=System
diff --git a/cmake.deps/cmake/BuildMsgpack.cmake b/cmake.deps/cmake/BuildMsgpack.cmake
index 10bf1c8e37..ea3fa84d7b 100644
--- a/cmake.deps/cmake/BuildMsgpack.cmake
+++ b/cmake.deps/cmake/BuildMsgpack.cmake
@@ -36,7 +36,6 @@ set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack
-DMSGPACK_BUILD_EXAMPLES=OFF
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC"
-DCMAKE_GENERATOR=${CMAKE_GENERATOR})
@@ -52,8 +51,8 @@ if(MSVC)
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
+ ${BUILD_TYPE_STRING}
"-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}"
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
# Make sure we use the same generator, otherwise we may
# accidentally end up using different MSVC runtimes
-DCMAKE_GENERATOR=${CMAKE_GENERATOR})
diff --git a/cmake.deps/cmake/BuildTreesitter.cmake b/cmake.deps/cmake/BuildTreesitter.cmake
index 01fdb837e2..6d98f6179c 100644
--- a/cmake.deps/cmake/BuildTreesitter.cmake
+++ b/cmake.deps/cmake/BuildTreesitter.cmake
@@ -43,7 +43,7 @@ if(MSVC)
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE}
diff --git a/cmake.deps/cmake/BuildTreesitterParsers.cmake b/cmake.deps/cmake/BuildTreesitterParsers.cmake
index 11ffb792de..1ff86e89b9 100644
--- a/cmake.deps/cmake/BuildTreesitterParsers.cmake
+++ b/cmake.deps/cmake/BuildTreesitterParsers.cmake
@@ -17,7 +17,7 @@ CMAKE_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_ALT_SEP}
# Pass toolchain
diff --git a/cmake.deps/cmake/BuildUnibilium.cmake b/cmake.deps/cmake/BuildUnibilium.cmake
index 2f940bdfd3..fd5d68f9a8 100644
--- a/cmake.deps/cmake/BuildUnibilium.cmake
+++ b/cmake.deps/cmake/BuildUnibilium.cmake
@@ -19,7 +19,7 @@ if(WIN32)
# Pass toolchain
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN}
-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
- -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
+ ${BUILD_TYPE_STRING}
-DCMAKE_GENERATOR=${CMAKE_GENERATOR}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE})
diff --git a/cmake.deps/cmake/GettextCMakeLists.txt b/cmake.deps/cmake/GettextCMakeLists.txt
index c3f78716d0..d9f251897d 100644
--- a/cmake.deps/cmake/GettextCMakeLists.txt
+++ b/cmake.deps/cmake/GettextCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
project(gettext C)
# Adds PREFIX to each item in LIST
diff --git a/cmake.deps/cmake/LibiconvCMakeLists.txt b/cmake.deps/cmake/LibiconvCMakeLists.txt
index 8ad3cc9352..b4ba6b5d7e 100644
--- a/cmake.deps/cmake/LibiconvCMakeLists.txt
+++ b/cmake.deps/cmake/LibiconvCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
project(libiconv C)
include_directories(
diff --git a/cmake.deps/cmake/LibvtermCMakeLists.txt b/cmake.deps/cmake/LibvtermCMakeLists.txt
index 16c4d542c4..ff1d2d6b79 100644
--- a/cmake.deps/cmake/LibvtermCMakeLists.txt
+++ b/cmake.deps/cmake/LibvtermCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
project(libvterm LANGUAGES C)
include(GNUInstallDirs)
diff --git a/cmake.deps/cmake/TreesitterCMakeLists.txt b/cmake.deps/cmake/TreesitterCMakeLists.txt
index 9e3ba3eeda..e20b47dd74 100644
--- a/cmake.deps/cmake/TreesitterCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
project(tree-sitter LANGUAGES C)
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/lib/src/*.c)
diff --git a/cmake.deps/cmake/TreesitterParserCMakeLists.txt b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
index 2808a9ee14..54bc35fb8a 100644
--- a/cmake.deps/cmake/TreesitterParserCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
# some parsers have c++ scanner, problem?
project(parser C) # CXX
diff --git a/cmake.deps/cmake/UnibiliumCMakeLists.txt b/cmake.deps/cmake/UnibiliumCMakeLists.txt
index 08a8599352..9112b416fa 100644
--- a/cmake.deps/cmake/UnibiliumCMakeLists.txt
+++ b/cmake.deps/cmake/UnibiliumCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
project(unibilium LANGUAGES C)
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/*.c)
diff --git a/cmake.deps/cmake/libtermkeyCMakeLists.txt b/cmake.deps/cmake/libtermkeyCMakeLists.txt
index af54c1daec..6c02b7549d 100644
--- a/cmake.deps/cmake/libtermkeyCMakeLists.txt
+++ b/cmake.deps/cmake/libtermkeyCMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.10)
project(libtermkey)
add_definitions(-D _CRT_SECURE_NO_WARNINGS)
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 12edaa1f08..a388592981 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -603,20 +603,6 @@ nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()*
NB: if your UI doesn't use hlstate, this will not return hlstate first
time.
-nvim__set_hl_ns({ns_id}) *nvim__set_hl_ns()*
- Set active namespace for highlights.
-
- NB: this function can be called from async contexts, but the semantics are
- not yet well-defined. To start with |nvim_set_decoration_provider| on_win
- and on_line callbacks are explicitly allowed to change the namespace
- during a redraw cycle.
-
- Attributes: ~
- |api-fast|
-
- Parameters: ~
- {ns_id} the namespace to activate
-
nvim__stats() *nvim__stats()*
Gets internal stats.
@@ -1414,6 +1400,26 @@ nvim_set_hl({ns_id}, {name}, {*val}) *nvim_set_hl()*
set, cterm attributes will match those from the attribute
map documented above.
+nvim_set_hl_ns({ns_id}) *nvim_set_hl_ns()*
+ Set active namespace for highlights. This can be set for a single window,
+ see |nvim_win_set_hl_ns|.
+
+ Parameters: ~
+ {ns_id} the namespace to use
+
+nvim_set_hl_ns_fast({ns_id}) *nvim_set_hl_ns_fast()*
+ Set active namespace for highlights while redrawing.
+
+ This function meant to be called while redrawing, primarily from
+ |nvim_set_decoration_provider| on_win and on_line callbacks, which are
+ allowed to change the namespace during a redraw cycle.
+
+ Attributes: ~
+ |api-fast|
+
+ Parameters: ~
+ {ns_id} the namespace to activate
+
nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()*
Sets a global |mapping| for the given mode.
@@ -2875,6 +2881,15 @@ nvim_win_set_height({window}, {height}) *nvim_win_set_height()*
{window} Window handle, or 0 for current window
{height} Height as a count of rows
+nvim_win_set_hl_ns({window}, {ns_id}) *nvim_win_set_hl_ns()*
+ Set highlight namespace for a window. This will use highlights defined in
+ this namespace, but fall back to global highlights (ns=0) when missing.
+
+ This takes predecence over the 'winhighlight' option.
+
+ Parameters: ~
+ {ns_id} the namespace to use
+
nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()*
Sets a window-scoped (w:) variable
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index c19d9f482b..dcb0bf8a2e 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -613,12 +613,19 @@ list of the current window.
And after the last one:
:+2argadd y a b c x y
There is no check for duplicates, it is possible to
- add a file to the argument list twice.
- The currently edited file is not changed.
+ add a file to the argument list twice. You can use
+ |:argdedupe| to fix it afterwards: >
+ :argadd *.txt | argdedupe
+< The currently edited file is not changed.
Note: you can also use this method: >
:args ## x
< This will add the "x" item and sort the new list.
+:argded[upe] *:argded* *:argdedupe*
+ Remove duplicate filenames from the argument list.
+ If your current file is a duplicate, your current file
+ will change to the original file index.
+
:argd[elete] {pattern} .. *:argd* *:argdelete* *E480* *E610*
Delete files from the argument list that match the
{pattern}s. {pattern} is used like a file pattern,
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 7d8a89887a..7f3ef20762 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1143,6 +1143,7 @@ tag command action ~
be remapped
|:args| :ar[gs] print the argument list
|:argadd| :arga[dd] add items to the argument list
+|:argdedupe| :argded[upe] remove duplicates from the argument list
|:argdelete| :argd[elete] delete items from the argument list
|:argedit| :arge[dit] add item to the argument list and edit it
|:argdo| :argdo do a command on all items in the argument list
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 21026f2ef8..7fc0daa0ca 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -415,7 +415,7 @@ For the format of the response message, see:
For the format of the notification message, see:
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage
- *on-list-handler*
+ *lsp-on-list-handler*
`on_list` receives a table with:
@@ -568,24 +568,6 @@ buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()*
Return: ~
true if any client returns true; false otherwise
- *vim.lsp.buf_request()*
-buf_request({bufnr}, {method}, {params}, {handler})
- Sends an async request for all active clients attached to the buffer.
-
- Parameters: ~
- {bufnr} (number) Buffer handle, or 0 for current.
- {method} (string) LSP method name
- {params} (optional, table) Parameters to send to the server
- {handler} (optional, function) See |lsp-handler| If nil, follows
- resolution strategy defined in |lsp-handler-configuration|
-
- Return: ~
- 2-tuple:
- • Map of client-id:request-id pairs for all successful requests.
- • Function which can be used to cancel all the requests. You could
- instead iterate all clients and call their `cancel_request()`
- methods.
-
*vim.lsp.buf_request_all()*
buf_request_all({bufnr}, {method}, {params}, {callback})
Sends an async request for all active clients attached to the buffer.
@@ -1070,7 +1052,7 @@ declaration({options}) *vim.lsp.buf.declaration()*
• reuse_win: (boolean) Jump to existing window if buffer is
already open.
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
definition({options}) *vim.lsp.buf.definition()*
Jumps to the definition of the symbol under the cursor.
@@ -1080,7 +1062,7 @@ definition({options}) *vim.lsp.buf.definition()*
• reuse_win: (boolean) Jump to existing window if buffer is
already open.
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
document_highlight() *vim.lsp.buf.document_highlight()*
Send request to the server to resolve document highlights for the current
@@ -1102,7 +1084,7 @@ document_symbol({options}) *vim.lsp.buf.document_symbol()*
Parameters: ~
{options} (table|nil) additional options
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
execute_command({command_params}) *vim.lsp.buf.execute_command()*
Executes an LSP server command.
@@ -1211,7 +1193,7 @@ implementation({options}) *vim.lsp.buf.implementation()*
Parameters: ~
{options} (table|nil) additional options
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
incoming_calls() *vim.lsp.buf.incoming_calls()*
Lists all the call sites of the symbol under the cursor in the |quickfix|
@@ -1260,7 +1242,7 @@ references({context}, {options}) *vim.lsp.buf.references()*
{context} (table) Context for the request
{options} (table|nil) additional options
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
@@ -1302,7 +1284,7 @@ type_definition({options}) *vim.lsp.buf.type_definition()*
• reuse_win: (boolean) Jump to existing window if buffer is
already open.
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
workspace_symbol({query}, {options}) *vim.lsp.buf.workspace_symbol()*
Lists all symbols in the current workspace in the quickfix window.
@@ -1315,7 +1297,7 @@ workspace_symbol({query}, {options}) *vim.lsp.buf.workspace_symbol()*
{query} (string, optional)
{options} (table|nil) additional options
• on_list: (function) handler for list results. See
- |on-list-handler|
+ |lsp-on-list-handler|
==============================================================================
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 9655d07a84..511b1bd7b2 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -588,7 +588,8 @@ i) *v_i)* *i)* *i(*
i( *vib* *v_ib* *v_i(* *ib*
ib "inner block", select [count] blocks, from "[count] [("
to the matching ')', excluding the '(' and ')' (see
- |[(|).
+ |[(|). If the cursor is not inside a () block, then
+ find the next "(".
When used in Visual mode it is made charwise.
a> *v_a>* *v_a<* *a>* *a<*
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 579c76d57e..a1f2eac5ed 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1326,7 +1326,8 @@ A jump table for the options with a short description can be found at |Q_op|.
When 'cmdheight' is zero, there is no command-line unless it is being
used. Some informative messages will not be displayed, any other
- messages will cause the |hit-enter| prompt.
+ messages will cause the |hit-enter| prompt. Expect some other
+ unexpected behavior too.
*'cmdwinheight'* *'cwh'*
'cmdwinheight' 'cwh' number (default 7)
@@ -7095,10 +7096,12 @@ A jump table for the options with a short description can be found at |Q_op|.
'winhighlight' 'winhl' string (default empty)
local to window
Window-local highlights. Comma-delimited list of highlight
- |group-name| pairs "{hl-builtin}:{hl},..." where each {hl-builtin} is
- a built-in |highlight-groups| item to be overridden by {hl} group in
- the window. Only built-in |highlight-groups| are supported, not
- syntax highlighting (use |:ownsyntax| for that).
+ |group-name| pairs "{hl-from}:{hl-to},..." where each {hl-from} is
+ a |highlight-groups| item to be overridden by {hl-to} group in
+ the window.
+
+ Note: highlight namespaces take precedence over 'winhighlight'.
+ See |nvim_win_set_hl_ns| and |nvim_set_hl|.
Highlights of vertical separators are determined by the window to the
left of the separator. The 'tabline' highlight of a tabpage is
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 822a03feb6..76beaf9830 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -372,6 +372,7 @@ Lua interface (|lua.txt|):
Commands:
|:doautocmd| does not warn about "No matching autocommands".
+ |:wincmd| accepts a count.
Functions:
|input()| and |inputdialog()| support for each other’s features (return on
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 9d6a790a9c..7355cec522 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -442,6 +442,7 @@ position is set to keep the same Visual area selected.
These commands can also be executed with ":wincmd":
:[count]winc[md] {arg}
+:winc[md] [count] {arg}
Like executing CTRL-W [count] {arg}. Example: >
:wincmd j
< Moves to the window below the current one.
diff --git a/runtime/ftplugin/elixir.vim b/runtime/ftplugin/elixir.vim
index 3b38051d08..c423c2acb7 100644
--- a/runtime/ftplugin/elixir.vim
+++ b/runtime/ftplugin/elixir.vim
@@ -1,11 +1,29 @@
" Elixir filetype plugin
" Language: Elixir
" Maintainer: Mitchell Hanberg <vimNOSPAM@mitchellhanberg.com>
-" Last Change: 2022 Apr 20
+" Last Change: 2022 August 10
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
+let s:save_cpo = &cpo
+set cpo&vim
+
+" Matchit support
+if exists('loaded_matchit') && !exists('b:match_words')
+ let b:match_ignorecase = 0
+
+ let b:match_words = '\:\@<!\<\%(do\|fn\)\:\@!\>' .
+ \ ':' .
+ \ '\<\%(else\|catch\|after\|rescue\)\:\@!\>' .
+ \ ':' .
+ \ '\:\@<!\<end\>' .
+ \ ',{:},\[:\],(:)'
+endif
+
setlocal commentstring=#\ %s
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 8d4f388e23..fd64c1a495 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1845,13 +1845,14 @@ api.nvim_create_autocmd('VimLeavePre', {
end,
})
+---@private
--- Sends an async request for all active clients attached to the
--- buffer.
---
---@param bufnr (number) Buffer handle, or 0 for current.
---@param method (string) LSP method name
----@param params (optional, table) Parameters to send to the server
----@param handler (optional, function) See |lsp-handler|
+---@param params table|nil Parameters to send to the server
+---@param handler function|nil See |lsp-handler|
--- If nil, follows resolution strategy defined in |lsp-handler-configuration|
---
---@returns 2-tuple:
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 63f4688d94..6a070928d9 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -61,7 +61,7 @@ end
---
---@param options table|nil additional options
--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.declaration(options)
local params = util.make_position_params()
request_with_options('textDocument/declaration', params, options)
@@ -71,7 +71,7 @@ end
---
---@param options table|nil additional options
--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.definition(options)
local params = util.make_position_params()
request_with_options('textDocument/definition', params, options)
@@ -81,7 +81,7 @@ end
---
---@param options table|nil additional options
--- - reuse_win: (boolean) Jump to existing window if buffer is already open.
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.type_definition(options)
local params = util.make_position_params()
request_with_options('textDocument/typeDefinition', params, options)
@@ -91,7 +91,7 @@ end
--- quickfix window.
---
---@param options table|nil additional options
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.implementation(options)
local params = util.make_position_params()
request_with_options('textDocument/implementation', params, options)
@@ -503,7 +503,7 @@ end
---@param context (table) Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
---@param options table|nil additional options
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.references(context, options)
validate({ context = { context, 't', true } })
local params = util.make_position_params()
@@ -516,7 +516,7 @@ end
--- Lists all symbols in the current buffer in the quickfix window.
---
---@param options table|nil additional options
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.document_symbol(options)
local params = { textDocument = util.make_text_document_params() }
request_with_options('textDocument/documentSymbol', params, options)
@@ -659,7 +659,7 @@ end
---
---@param query (string, optional)
---@param options table|nil additional options
---- - on_list: (function) handler for list results. See |on-list-handler|
+--- - on_list: (function) handler for list results. See |lsp-on-list-handler|
function M.workspace_symbol(query, options)
query = query or npcall(vim.fn.input, 'Query: ')
if query == nil then
diff --git a/runtime/syntax/README.txt b/runtime/syntax/README.txt
index d88bd7ed5d..d6a86e5ca9 100644
--- a/runtime/syntax/README.txt
+++ b/runtime/syntax/README.txt
@@ -13,6 +13,9 @@ synload.vim Contains autocommands to load a language file when a certain
nosyntax.vim Used for the ":syntax off" command. Undo the loading of
synload.vim.
+The "shared" directory contains generated files and what is used by more than
+one syntax.
+
A few special files:
diff --git a/runtime/syntax/shared/README.txt b/runtime/syntax/shared/README.txt
new file mode 100644
index 0000000000..f519d44faf
--- /dev/null
+++ b/runtime/syntax/shared/README.txt
@@ -0,0 +1,2 @@
+This directory "runtime/syntax/shared" contains Vim script files that are
+generated or used by more then one syntax file.
diff --git a/runtime/syntax/shared/typescriptcommon.vim b/runtime/syntax/shared/typescriptcommon.vim
new file mode 100644
index 0000000000..ef362fc721
--- /dev/null
+++ b/runtime/syntax/shared/typescriptcommon.vim
@@ -0,0 +1,2099 @@
+" Vim syntax file
+" Language: TypeScript and TypeScriptReact
+" Maintainer: Bram Moolenaar, Herrington Darkholme
+" Last Change: 2021 Sep 22
+" Based On: Herrington Darkholme's yats.vim
+" Changes: See https:github.com/HerringtonDarkholme/yats.vim
+" Credits: See yats.vim on github
+
+if &cpo =~ 'C'
+ let s:cpo_save = &cpo
+ set cpo&vim
+endif
+
+
+" NOTE: this results in accurate highlighting, but can be slow.
+syntax sync fromstart
+
+"Dollar sign is permitted anywhere in an identifier
+setlocal iskeyword-=$
+if main_syntax == 'typescript' || main_syntax == 'typescriptreact'
+ setlocal iskeyword+=$
+ " syntax cluster htmlJavaScript contains=TOP
+endif
+" For private field added from TypeScript 3.8
+setlocal iskeyword+=#
+
+" lowest priority on least used feature
+syntax match typescriptLabel /[a-zA-Z_$]\k*:/he=e-1 contains=typescriptReserved nextgroup=@typescriptStatement skipwhite skipempty
+
+" other keywords like return,case,yield uses containedin
+syntax region typescriptBlock matchgroup=typescriptBraces start=/{/ end=/}/ contains=@typescriptStatement,@typescriptComments fold
+syntax cluster afterIdentifier contains=
+ \ typescriptDotNotation,
+ \ typescriptFuncCallArg,
+ \ typescriptTemplate,
+ \ typescriptIndexExpr,
+ \ @typescriptSymbols,
+ \ typescriptTypeArguments
+
+syntax match typescriptIdentifierName /\<\K\k*/
+ \ nextgroup=@afterIdentifier
+ \ transparent
+ \ contains=@_semantic
+ \ skipnl skipwhite
+
+syntax match typescriptProp contained /\K\k*!\?/
+ \ transparent
+ \ contains=@props
+ \ nextgroup=@afterIdentifier
+ \ skipwhite skipempty
+
+syntax region typescriptIndexExpr contained matchgroup=typescriptProperty start=/\[/rs=s+1 end=/]/he=e-1 contains=@typescriptValue nextgroup=@typescriptSymbols,typescriptDotNotation,typescriptFuncCallArg skipwhite skipempty
+
+syntax match typescriptDotNotation /\.\|?\.\|!\./ nextgroup=typescriptProp skipnl
+syntax match typescriptDotStyleNotation /\.style\./ nextgroup=typescriptDOMStyle transparent
+" syntax match typescriptFuncCall contained /[a-zA-Z]\k*\ze(/ nextgroup=typescriptFuncCallArg
+syntax region typescriptParenExp matchgroup=typescriptParens start=/(/ end=/)/ contains=@typescriptComments,@typescriptValue,typescriptCastKeyword nextgroup=@typescriptSymbols skipwhite skipempty
+syntax region typescriptFuncCallArg contained matchgroup=typescriptParens start=/(/ end=/)/ contains=@typescriptValue,@typescriptComments nextgroup=@typescriptSymbols,typescriptDotNotation skipwhite skipempty skipnl
+syntax region typescriptEventFuncCallArg contained matchgroup=typescriptParens start=/(/ end=/)/ contains=@typescriptEventExpression
+syntax region typescriptEventString contained start=/\z(["']\)/ skip=/\\\\\|\\\z1\|\\\n/ end=/\z1\|$/ contains=typescriptASCII,@events
+
+syntax region typescriptDestructureString
+ \ start=/\z(["']\)/ skip=/\\\\\|\\\z1\|\\\n/ end=/\z1\|$/
+ \ contains=typescriptASCII
+ \ nextgroup=typescriptDestructureAs
+ \ contained skipwhite skipempty
+
+syntax cluster typescriptVariableDeclarations
+ \ contains=typescriptVariableDeclaration,@typescriptDestructures
+
+syntax match typescriptVariableDeclaration /[A-Za-z_$]\k*/
+ \ nextgroup=typescriptTypeAnnotation,typescriptAssign
+ \ contained skipwhite skipempty
+
+syntax cluster typescriptDestructureVariables contains=
+ \ typescriptRestOrSpread,
+ \ typescriptDestructureComma,
+ \ typescriptDestructureLabel,
+ \ typescriptDestructureVariable,
+ \ @typescriptDestructures
+
+syntax match typescriptDestructureVariable /[A-Za-z_$]\k*/ contained
+ \ nextgroup=typescriptDefaultParam
+ \ contained skipwhite skipempty
+
+syntax match typescriptDestructureLabel /[A-Za-z_$]\k*\ze\_s*:/
+ \ nextgroup=typescriptDestructureAs
+ \ contained skipwhite skipempty
+
+syntax match typescriptDestructureAs /:/
+ \ nextgroup=typescriptDestructureVariable,@typescriptDestructures
+ \ contained skipwhite skipempty
+
+syntax match typescriptDestructureComma /,/ contained
+
+syntax cluster typescriptDestructures contains=
+ \ typescriptArrayDestructure,
+ \ typescriptObjectDestructure
+
+syntax region typescriptArrayDestructure matchgroup=typescriptBraces
+ \ start=/\[/ end=/]/
+ \ contains=@typescriptDestructureVariables,@typescriptComments
+ \ nextgroup=typescriptTypeAnnotation,typescriptAssign
+ \ transparent contained skipwhite skipempty fold
+
+syntax region typescriptObjectDestructure matchgroup=typescriptBraces
+ \ start=/{/ end=/}/
+ \ contains=typescriptDestructureString,@typescriptDestructureVariables,@typescriptComments
+ \ nextgroup=typescriptTypeAnnotation,typescriptAssign
+ \ transparent contained skipwhite skipempty fold
+
+"Syntax in the JavaScript code
+
+" String
+syntax match typescriptASCII contained /\\\d\d\d/
+
+syntax region typescriptTemplateSubstitution matchgroup=typescriptTemplateSB
+ \ start=/\${/ end=/}/
+ \ contains=@typescriptValue
+ \ contained
+
+
+syntax region typescriptString
+ \ start=+\z(["']\)+ skip=+\\\%(\z1\|$\)+ end=+\z1+ end=+$+
+ \ contains=typescriptSpecial,@Spell
+ \ extend
+
+syntax match typescriptSpecial contained "\v\\%(x\x\x|u%(\x{4}|\{\x{1,6}})|c\u|.)"
+
+" From vim runtime
+" <https://github.com/vim/vim/blob/master/runtime/syntax/javascript.vim#L48>
+syntax region typescriptRegexpString start=+/[^/*]+me=e-1 skip=+\\\\\|\\/+ end=+/[gimuy]\{0,5\}\s*$+ end=+/[gimuy]\{0,5\}\s*[;.,)\]}:]+me=e-1 nextgroup=typescriptDotNotation oneline
+
+syntax region typescriptTemplate
+ \ start=/`/ skip=/\\\\\|\\`\|\n/ end=/`\|$/
+ \ contains=typescriptTemplateSubstitution,typescriptSpecial,@Spell
+ \ nextgroup=@typescriptSymbols
+ \ skipwhite skipempty
+
+"Array
+syntax region typescriptArray matchgroup=typescriptBraces
+ \ start=/\[/ end=/]/
+ \ contains=@typescriptValue,@typescriptComments
+ \ nextgroup=@typescriptSymbols,typescriptDotNotation
+ \ skipwhite skipempty fold
+
+" Number
+syntax match typescriptNumber /\<0[bB][01][01_]*\>/ nextgroup=@typescriptSymbols skipwhite skipempty
+syntax match typescriptNumber /\<0[oO][0-7][0-7_]*\>/ nextgroup=@typescriptSymbols skipwhite skipempty
+syntax match typescriptNumber /\<0[xX][0-9a-fA-F][0-9a-fA-F_]*\>/ nextgroup=@typescriptSymbols skipwhite skipempty
+syntax match typescriptNumber /\<\%(\d[0-9_]*\%(\.\d[0-9_]*\)\=\|\.\d[0-9_]*\)\%([eE][+-]\=\d[0-9_]*\)\=\>/
+ \ nextgroup=typescriptSymbols skipwhite skipempty
+
+syntax region typescriptObjectLiteral matchgroup=typescriptBraces
+ \ start=/{/ end=/}/
+ \ contains=@typescriptComments,typescriptObjectLabel,typescriptStringProperty,typescriptComputedPropertyName,typescriptObjectAsyncKeyword
+ \ fold contained
+
+syntax keyword typescriptObjectAsyncKeyword async contained
+
+syntax match typescriptObjectLabel contained /\k\+\_s*/
+ \ nextgroup=typescriptObjectColon,@typescriptCallImpl
+ \ skipwhite skipempty
+
+syntax region typescriptStringProperty contained
+ \ start=/\z(["']\)/ skip=/\\\\\|\\\z1\|\\\n/ end=/\z1/
+ \ nextgroup=typescriptObjectColon,@typescriptCallImpl
+ \ skipwhite skipempty
+
+" syntax region typescriptPropertyName contained start=/\z(["']\)/ skip=/\\\\\|\\\z1\|\\\n/ end=/\z1(/me=e-1 nextgroup=@typescriptCallSignature skipwhite skipempty oneline
+syntax region typescriptComputedPropertyName contained matchgroup=typescriptBraces
+ \ start=/\[/rs=s+1 end=/]/
+ \ contains=@typescriptValue
+ \ nextgroup=typescriptObjectColon,@typescriptCallImpl
+ \ skipwhite skipempty
+
+" syntax region typescriptComputedPropertyName contained matchgroup=typescriptPropertyName start=/\[/rs=s+1 end=/]\_s*:/he=e-1 contains=@typescriptValue nextgroup=@typescriptValue skipwhite skipempty
+" syntax region typescriptComputedPropertyName contained matchgroup=typescriptPropertyName start=/\[/rs=s+1 end=/]\_s*(/me=e-1 contains=@typescriptValue nextgroup=@typescriptCallSignature skipwhite skipempty
+" Value for object, statement for label statement
+syntax match typescriptRestOrSpread /\.\.\./ contained
+syntax match typescriptObjectSpread /\.\.\./ contained containedin=typescriptObjectLiteral,typescriptArray nextgroup=@typescriptValue
+
+syntax match typescriptObjectColon contained /:/ nextgroup=@typescriptValue skipwhite skipempty
+
+" + - ^ ~
+syntax match typescriptUnaryOp /[+\-~!]/
+ \ nextgroup=@typescriptValue
+ \ skipwhite
+
+syntax region typescriptTernary matchgroup=typescriptTernaryOp start=/?[.?]\@!/ end=/:/ contained contains=@typescriptValue,@typescriptComments nextgroup=@typescriptValue skipwhite skipempty
+
+syntax match typescriptAssign /=/ nextgroup=@typescriptValue
+ \ skipwhite skipempty
+
+" 2: ==, ===
+syntax match typescriptBinaryOp contained /===\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 6: >>>=, >>>, >>=, >>, >=, >
+syntax match typescriptBinaryOp contained />\(>>=\|>>\|>=\|>\|=\)\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 4: <<=, <<, <=, <
+syntax match typescriptBinaryOp contained /<\(<=\|<\|=\)\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 3: ||, |=, |, ||=
+syntax match typescriptBinaryOp contained /||\?=\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 4: &&, &=, &, &&=
+syntax match typescriptBinaryOp contained /&&\?=\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 2: ??, ??=
+syntax match typescriptBinaryOp contained /??=\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 2: *=, *
+syntax match typescriptBinaryOp contained /\*=\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 2: %=, %
+syntax match typescriptBinaryOp contained /%=\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 2: /=, /
+syntax match typescriptBinaryOp contained +/\(=\|[^\*/]\@=\)+ nextgroup=@typescriptValue skipwhite skipempty
+syntax match typescriptBinaryOp contained /!==\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 2: !=, !==
+syntax match typescriptBinaryOp contained /+\(+\|=\)\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 3: +, ++, +=
+syntax match typescriptBinaryOp contained /-\(-\|=\)\?/ nextgroup=@typescriptValue skipwhite skipempty
+" 3: -, --, -=
+
+" exponentiation operator
+" 2: **, **=
+syntax match typescriptBinaryOp contained /\*\*=\?/ nextgroup=@typescriptValue
+
+syntax cluster typescriptSymbols contains=typescriptBinaryOp,typescriptKeywordOp,typescriptTernary,typescriptAssign,typescriptCastKeyword
+
+" runtime syntax/basic/reserved.vim
+"Import
+syntax keyword typescriptImport from as
+syntax keyword typescriptImport import
+ \ nextgroup=typescriptImportType
+ \ skipwhite
+syntax keyword typescriptImportType type
+ \ contained
+syntax keyword typescriptExport export
+ \ nextgroup=typescriptExportType
+ \ skipwhite
+syntax match typescriptExportType /\<type\s*{\@=/
+ \ contained skipwhite skipempty skipnl
+syntax keyword typescriptModule namespace module
+
+"this
+
+"JavaScript Prototype
+syntax keyword typescriptPrototype prototype
+ \ nextgroup=@afterIdentifier
+
+syntax keyword typescriptCastKeyword as
+ \ nextgroup=@typescriptType
+ \ skipwhite
+
+"Program Keywords
+syntax keyword typescriptIdentifier arguments this super
+ \ nextgroup=@afterIdentifier
+
+syntax keyword typescriptVariable let var
+ \ nextgroup=@typescriptVariableDeclarations
+ \ skipwhite skipempty
+
+syntax keyword typescriptVariable const
+ \ nextgroup=typescriptEnum,@typescriptVariableDeclarations
+ \ skipwhite skipempty
+
+syntax region typescriptEnum matchgroup=typescriptEnumKeyword start=/enum / end=/\ze{/
+ \ nextgroup=typescriptBlock
+ \ skipwhite
+
+syntax keyword typescriptKeywordOp
+ \ contained in instanceof nextgroup=@typescriptValue
+syntax keyword typescriptOperator delete new typeof void
+ \ nextgroup=@typescriptValue
+ \ skipwhite skipempty
+
+syntax keyword typescriptForOperator contained in of
+syntax keyword typescriptBoolean true false nextgroup=@typescriptSymbols skipwhite skipempty
+syntax keyword typescriptNull null undefined nextgroup=@typescriptSymbols skipwhite skipempty
+syntax keyword typescriptMessage alert confirm prompt status
+ \ nextgroup=typescriptDotNotation,typescriptFuncCallArg
+syntax keyword typescriptGlobal self top parent
+ \ nextgroup=@afterIdentifier
+
+"Statement Keywords
+syntax keyword typescriptConditional if else switch
+ \ nextgroup=typescriptConditionalParen
+ \ skipwhite skipempty skipnl
+syntax keyword typescriptConditionalElse else
+syntax keyword typescriptRepeat do while for nextgroup=typescriptLoopParen skipwhite skipempty
+syntax keyword typescriptRepeat for nextgroup=typescriptLoopParen,typescriptAsyncFor skipwhite skipempty
+syntax keyword typescriptBranch break continue containedin=typescriptBlock
+syntax keyword typescriptCase case nextgroup=@typescriptPrimitive skipwhite containedin=typescriptBlock
+syntax keyword typescriptDefault default containedin=typescriptBlock nextgroup=@typescriptValue,typescriptClassKeyword,typescriptInterfaceKeyword skipwhite oneline
+syntax keyword typescriptStatementKeyword with
+syntax keyword typescriptStatementKeyword yield skipwhite nextgroup=@typescriptValue containedin=typescriptBlock
+syntax keyword typescriptStatementKeyword return skipwhite contained nextgroup=@typescriptValue containedin=typescriptBlock
+
+syntax keyword typescriptTry try
+syntax keyword typescriptExceptions catch throw finally
+syntax keyword typescriptDebugger debugger
+
+syntax keyword typescriptAsyncFor await nextgroup=typescriptLoopParen skipwhite skipempty contained
+
+syntax region typescriptLoopParen contained matchgroup=typescriptParens
+ \ start=/(/ end=/)/
+ \ contains=typescriptVariable,typescriptForOperator,typescriptEndColons,@typescriptValue,@typescriptComments
+ \ nextgroup=typescriptBlock
+ \ skipwhite skipempty
+syntax region typescriptConditionalParen contained matchgroup=typescriptParens
+ \ start=/(/ end=/)/
+ \ contains=@typescriptValue,@typescriptComments
+ \ nextgroup=typescriptBlock
+ \ skipwhite skipempty
+syntax match typescriptEndColons /[;,]/ contained
+
+syntax keyword typescriptAmbientDeclaration declare nextgroup=@typescriptAmbients
+ \ skipwhite skipempty
+
+syntax cluster typescriptAmbients contains=
+ \ typescriptVariable,
+ \ typescriptFuncKeyword,
+ \ typescriptClassKeyword,
+ \ typescriptAbstract,
+ \ typescriptEnumKeyword,typescriptEnum,
+ \ typescriptModule
+
+"Syntax coloring for Node.js shebang line
+syntax match shellbang "^#!.*node\>"
+syntax match shellbang "^#!.*iojs\>"
+
+
+"JavaScript comments
+syntax keyword typescriptCommentTodo TODO FIXME XXX TBD
+syntax match typescriptMagicComment "@ts-\%(ignore\|expect-error\)\>"
+syntax match typescriptLineComment "//.*"
+ \ contains=@Spell,typescriptCommentTodo,typescriptRef,typescriptMagicComment
+syntax region typescriptComment
+ \ start="/\*" end="\*/"
+ \ contains=@Spell,typescriptCommentTodo extend
+syntax cluster typescriptComments
+ \ contains=typescriptDocComment,typescriptComment,typescriptLineComment
+
+syntax match typescriptRef +///\s*<reference\s\+.*\/>$+
+ \ contains=typescriptString
+syntax match typescriptRef +///\s*<amd-dependency\s\+.*\/>$+
+ \ contains=typescriptString
+syntax match typescriptRef +///\s*<amd-module\s\+.*\/>$+
+ \ contains=typescriptString
+
+"JSDoc
+syntax case ignore
+
+syntax region typescriptDocComment matchgroup=typescriptComment
+ \ start="/\*\*" end="\*/"
+ \ contains=typescriptDocNotation,typescriptCommentTodo,@Spell
+ \ fold keepend
+syntax match typescriptDocNotation contained /@/ nextgroup=typescriptDocTags
+
+syntax keyword typescriptDocTags contained constant constructor constructs function ignore inner private public readonly static
+syntax keyword typescriptDocTags contained const dict expose inheritDoc interface nosideeffects override protected struct internal
+syntax keyword typescriptDocTags contained example global
+syntax keyword typescriptDocTags contained alpha beta defaultValue eventProperty experimental label
+syntax keyword typescriptDocTags contained packageDocumentation privateRemarks remarks sealed typeParam
+
+" syntax keyword typescriptDocTags contained ngdoc nextgroup=typescriptDocNGDirective
+syntax keyword typescriptDocTags contained ngdoc scope priority animations
+syntax keyword typescriptDocTags contained ngdoc restrict methodOf propertyOf eventOf eventType nextgroup=typescriptDocParam skipwhite
+syntax keyword typescriptDocNGDirective contained overview service object function method property event directive filter inputType error
+
+syntax keyword typescriptDocTags contained abstract virtual access augments
+
+syntax keyword typescriptDocTags contained arguments callback lends memberOf name type kind link mixes mixin tutorial nextgroup=typescriptDocParam skipwhite
+syntax keyword typescriptDocTags contained variation nextgroup=typescriptDocNumParam skipwhite
+
+syntax keyword typescriptDocTags contained author class classdesc copyright default defaultvalue nextgroup=typescriptDocDesc skipwhite
+syntax keyword typescriptDocTags contained deprecated description external host nextgroup=typescriptDocDesc skipwhite
+syntax keyword typescriptDocTags contained file fileOverview overview namespace requires since version nextgroup=typescriptDocDesc skipwhite
+syntax keyword typescriptDocTags contained summary todo license preserve nextgroup=typescriptDocDesc skipwhite
+
+syntax keyword typescriptDocTags contained borrows exports nextgroup=typescriptDocA skipwhite
+syntax keyword typescriptDocTags contained param arg argument property prop module nextgroup=typescriptDocNamedParamType,typescriptDocParamName skipwhite
+syntax keyword typescriptDocTags contained define enum extends implements this typedef nextgroup=typescriptDocParamType skipwhite
+syntax keyword typescriptDocTags contained return returns throws exception nextgroup=typescriptDocParamType,typescriptDocParamName skipwhite
+syntax keyword typescriptDocTags contained see nextgroup=typescriptDocRef skipwhite
+
+syntax keyword typescriptDocTags contained function func method nextgroup=typescriptDocName skipwhite
+syntax match typescriptDocName contained /\h\w*/
+
+syntax keyword typescriptDocTags contained fires event nextgroup=typescriptDocEventRef skipwhite
+syntax match typescriptDocEventRef contained /\h\w*#\(\h\w*\:\)\?\h\w*/
+
+syntax match typescriptDocNamedParamType contained /{.\+}/ nextgroup=typescriptDocParamName skipwhite
+syntax match typescriptDocParamName contained /\[\?0-9a-zA-Z_\.]\+\]\?/ nextgroup=typescriptDocDesc skipwhite
+syntax match typescriptDocParamType contained /{.\+}/ nextgroup=typescriptDocDesc skipwhite
+syntax match typescriptDocA contained /\%(#\|\w\|\.\|:\|\/\)\+/ nextgroup=typescriptDocAs skipwhite
+syntax match typescriptDocAs contained /\s*as\s*/ nextgroup=typescriptDocB skipwhite
+syntax match typescriptDocB contained /\%(#\|\w\|\.\|:\|\/\)\+/
+syntax match typescriptDocParam contained /\%(#\|\w\|\.\|:\|\/\|-\)\+/
+syntax match typescriptDocNumParam contained /\d\+/
+syntax match typescriptDocRef contained /\%(#\|\w\|\.\|:\|\/\)\+/
+syntax region typescriptDocLinkTag contained matchgroup=typescriptDocLinkTag start=/{/ end=/}/ contains=typescriptDocTags
+
+syntax cluster typescriptDocs contains=typescriptDocParamType,typescriptDocNamedParamType,typescriptDocParam
+
+if exists("main_syntax") && main_syntax == "typescript"
+ syntax sync clear
+ syntax sync ccomment typescriptComment minlines=200
+endif
+
+syntax case match
+
+" Types
+syntax match typescriptOptionalMark /?/ contained
+
+syntax cluster typescriptTypeParameterCluster contains=
+ \ typescriptTypeParameter,
+ \ typescriptGenericDefault
+
+syntax region typescriptTypeParameters matchgroup=typescriptTypeBrackets
+ \ start=/</ end=/>/
+ \ contains=@typescriptTypeParameterCluster
+ \ contained
+
+syntax match typescriptTypeParameter /\K\k*/
+ \ nextgroup=typescriptConstraint
+ \ contained skipwhite skipnl
+
+syntax keyword typescriptConstraint extends
+ \ nextgroup=@typescriptType
+ \ contained skipwhite skipnl
+
+syntax match typescriptGenericDefault /=/
+ \ nextgroup=@typescriptType
+ \ contained skipwhite
+
+"><
+" class A extend B<T> {} // ClassBlock
+" func<T>() // FuncCallArg
+syntax region typescriptTypeArguments matchgroup=typescriptTypeBrackets
+ \ start=/\></ end=/>/
+ \ contains=@typescriptType
+ \ nextgroup=typescriptFuncCallArg,@typescriptTypeOperator
+ \ contained skipwhite
+
+
+syntax cluster typescriptType contains=
+ \ @typescriptPrimaryType,
+ \ typescriptUnion,
+ \ @typescriptFunctionType,
+ \ typescriptConstructorType
+
+" array type: A[]
+" type indexing A['key']
+syntax region typescriptTypeBracket contained
+ \ start=/\[/ end=/\]/
+ \ contains=typescriptString,typescriptNumber
+ \ nextgroup=@typescriptTypeOperator
+ \ skipwhite skipempty
+
+syntax cluster typescriptPrimaryType contains=
+ \ typescriptParenthesizedType,
+ \ typescriptPredefinedType,
+ \ typescriptTypeReference,
+ \ typescriptObjectType,
+ \ typescriptTupleType,
+ \ typescriptTypeQuery,
+ \ typescriptStringLiteralType,
+ \ typescriptTemplateLiteralType,
+ \ typescriptReadonlyArrayKeyword,
+ \ typescriptAssertType
+
+syntax region typescriptStringLiteralType contained
+ \ start=/\z(["']\)/ skip=/\\\\\|\\\z1\|\\\n/ end=/\z1\|$/
+ \ nextgroup=typescriptUnion
+ \ skipwhite skipempty
+
+syntax region typescriptTemplateLiteralType contained
+ \ start=/`/ skip=/\\\\\|\\`\|\n/ end=/`\|$/
+ \ contains=typescriptTemplateSubstitutionType
+ \ nextgroup=typescriptTypeOperator
+ \ skipwhite skipempty
+
+syntax region typescriptTemplateSubstitutionType matchgroup=typescriptTemplateSB
+ \ start=/\${/ end=/}/
+ \ contains=@typescriptType
+ \ contained
+
+syntax region typescriptParenthesizedType matchgroup=typescriptParens
+ \ start=/(/ end=/)/
+ \ contains=@typescriptType
+ \ nextgroup=@typescriptTypeOperator
+ \ contained skipwhite skipempty fold
+
+syntax match typescriptTypeReference /\K\k*\(\.\K\k*\)*/
+ \ nextgroup=typescriptTypeArguments,@typescriptTypeOperator,typescriptUserDefinedType
+ \ skipwhite contained skipempty
+
+syntax keyword typescriptPredefinedType any number boolean string void never undefined null object unknown
+ \ nextgroup=@typescriptTypeOperator
+ \ contained skipwhite skipempty
+
+syntax match typescriptPredefinedType /unique symbol/
+ \ nextgroup=@typescriptTypeOperator
+ \ contained skipwhite skipempty
+
+syntax region typescriptObjectType matchgroup=typescriptBraces
+ \ start=/{/ end=/}/
+ \ contains=@typescriptTypeMember,typescriptEndColons,@typescriptComments,typescriptAccessibilityModifier,typescriptReadonlyModifier
+ \ nextgroup=@typescriptTypeOperator
+ \ contained skipwhite skipnl fold
+
+syntax cluster typescriptTypeMember contains=
+ \ @typescriptCallSignature,
+ \ typescriptConstructSignature,
+ \ typescriptIndexSignature,
+ \ @typescriptMembers
+
+syntax match typescriptTupleLable /\K\k*?\?:/
+ \ contained
+
+syntax region typescriptTupleType matchgroup=typescriptBraces
+ \ start=/\[/ end=/\]/
+ \ contains=@typescriptType,@typescriptComments,typescriptRestOrSpread,typescriptTupleLable
+ \ contained skipwhite
+
+syntax cluster typescriptTypeOperator
+ \ contains=typescriptUnion,typescriptTypeBracket,typescriptConstraint,typescriptConditionalType
+
+syntax match typescriptUnion /|\|&/ contained nextgroup=@typescriptPrimaryType skipwhite skipempty
+
+syntax match typescriptConditionalType /?\|:/ contained nextgroup=@typescriptPrimaryType skipwhite skipempty
+
+syntax cluster typescriptFunctionType contains=typescriptGenericFunc,typescriptFuncType
+syntax region typescriptGenericFunc matchgroup=typescriptTypeBrackets
+ \ start=/</ end=/>/
+ \ contains=typescriptTypeParameter
+ \ nextgroup=typescriptFuncType
+ \ containedin=typescriptFunctionType
+ \ contained skipwhite skipnl
+
+syntax region typescriptFuncType matchgroup=typescriptParens
+ \ start=/(/ end=/)\s*=>/me=e-2
+ \ contains=@typescriptParameterList
+ \ nextgroup=typescriptFuncTypeArrow
+ \ contained skipwhite skipnl oneline
+
+syntax match typescriptFuncTypeArrow /=>/
+ \ nextgroup=@typescriptType
+ \ containedin=typescriptFuncType
+ \ contained skipwhite skipnl
+
+
+syntax keyword typescriptConstructorType new
+ \ nextgroup=@typescriptFunctionType
+ \ contained skipwhite skipnl
+
+syntax keyword typescriptUserDefinedType is
+ \ contained nextgroup=@typescriptType skipwhite skipempty
+
+syntax keyword typescriptTypeQuery typeof keyof
+ \ nextgroup=typescriptTypeReference
+ \ contained skipwhite skipnl
+
+syntax keyword typescriptAssertType asserts
+ \ nextgroup=typescriptTypeReference
+ \ contained skipwhite skipnl
+
+syntax cluster typescriptCallSignature contains=typescriptGenericCall,typescriptCall
+syntax region typescriptGenericCall matchgroup=typescriptTypeBrackets
+ \ start=/</ end=/>/
+ \ contains=typescriptTypeParameter
+ \ nextgroup=typescriptCall
+ \ contained skipwhite skipnl
+syntax region typescriptCall matchgroup=typescriptParens
+ \ start=/(/ end=/)/
+ \ contains=typescriptDecorator,@typescriptParameterList,@typescriptComments
+ \ nextgroup=typescriptTypeAnnotation,typescriptBlock
+ \ contained skipwhite skipnl
+
+syntax match typescriptTypeAnnotation /:/
+ \ nextgroup=@typescriptType
+ \ contained skipwhite skipnl
+
+syntax cluster typescriptParameterList contains=
+ \ typescriptTypeAnnotation,
+ \ typescriptAccessibilityModifier,
+ \ typescriptReadonlyModifier,
+ \ typescriptOptionalMark,
+ \ typescriptRestOrSpread,
+ \ typescriptFuncComma,
+ \ typescriptDefaultParam
+
+syntax match typescriptFuncComma /,/ contained
+
+syntax match typescriptDefaultParam /=/
+ \ nextgroup=@typescriptValue
+ \ contained skipwhite
+
+syntax keyword typescriptConstructSignature new
+ \ nextgroup=@typescriptCallSignature
+ \ contained skipwhite
+
+syntax region typescriptIndexSignature matchgroup=typescriptBraces
+ \ start=/\[/ end=/\]/
+ \ contains=typescriptPredefinedType,typescriptMappedIn,typescriptString
+ \ nextgroup=typescriptTypeAnnotation
+ \ contained skipwhite oneline
+
+syntax keyword typescriptMappedIn in
+ \ nextgroup=@typescriptType
+ \ contained skipwhite skipnl skipempty
+
+syntax keyword typescriptAliasKeyword type
+ \ nextgroup=typescriptAliasDeclaration
+ \ skipwhite skipnl skipempty
+
+syntax region typescriptAliasDeclaration matchgroup=typescriptUnion
+ \ start=/ / end=/=/
+ \ nextgroup=@typescriptType
+ \ contains=typescriptConstraint,typescriptTypeParameters
+ \ contained skipwhite skipempty
+
+syntax keyword typescriptReadonlyArrayKeyword readonly
+ \ nextgroup=@typescriptPrimaryType
+ \ skipwhite
+
+
+" extension
+if get(g:, 'yats_host_keyword', 1)
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Function Boolean
+ " use of nextgroup Suggested by Doug Kearns
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Error EvalError nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName InternalError
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName RangeError ReferenceError
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName StopIteration
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName SyntaxError TypeError
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName URIError Date
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Float32Array
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Float64Array
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Int16Array Int32Array
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Int8Array Uint16Array
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Uint32Array Uint8Array
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Uint8ClampedArray
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName ParallelArray
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName ArrayBuffer DataView
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Iterator Generator
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Reflect Proxy
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName arguments
+ hi def link typescriptGlobal Structure
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName eval uneval nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName isFinite nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName isNaN parseFloat nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName parseInt nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName decodeURI nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName decodeURIComponent nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName encodeURI nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName encodeURIComponent nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptGlobalMethod
+ hi def link typescriptGlobalMethod Structure
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Number nextgroup=typescriptGlobalNumberDot,typescriptFuncCallArg
+ syntax match typescriptGlobalNumberDot /\./ contained nextgroup=typescriptNumberStaticProp,typescriptNumberStaticMethod,typescriptProp
+ syntax keyword typescriptNumberStaticProp contained EPSILON MAX_SAFE_INTEGER MAX_VALUE
+ syntax keyword typescriptNumberStaticProp contained MIN_SAFE_INTEGER MIN_VALUE NEGATIVE_INFINITY
+ syntax keyword typescriptNumberStaticProp contained NaN POSITIVE_INFINITY
+ hi def link typescriptNumberStaticProp Keyword
+ syntax keyword typescriptNumberStaticMethod contained isFinite isInteger isNaN isSafeInteger nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptNumberStaticMethod contained parseFloat parseInt nextgroup=typescriptFuncCallArg
+ hi def link typescriptNumberStaticMethod Keyword
+ syntax keyword typescriptNumberMethod contained toExponential toFixed toLocaleString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptNumberMethod contained toPrecision toSource toString valueOf nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptNumberMethod
+ hi def link typescriptNumberMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName String nextgroup=typescriptGlobalStringDot,typescriptFuncCallArg
+ syntax match typescriptGlobalStringDot /\./ contained nextgroup=typescriptStringStaticMethod,typescriptProp
+ syntax keyword typescriptStringStaticMethod contained fromCharCode fromCodePoint raw nextgroup=typescriptFuncCallArg
+ hi def link typescriptStringStaticMethod Keyword
+ syntax keyword typescriptStringMethod contained anchor charAt charCodeAt codePointAt nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained concat endsWith includes indexOf lastIndexOf nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained link localeCompare match normalize nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained padStart padEnd repeat replace search nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained slice split startsWith substr substring nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained toLocaleLowerCase toLocaleUpperCase nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained toLowerCase toString toUpperCase trim nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptStringMethod contained valueOf nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptStringMethod
+ hi def link typescriptStringMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Array nextgroup=typescriptGlobalArrayDot,typescriptFuncCallArg
+ syntax match typescriptGlobalArrayDot /\./ contained nextgroup=typescriptArrayStaticMethod,typescriptProp
+ syntax keyword typescriptArrayStaticMethod contained from isArray of nextgroup=typescriptFuncCallArg
+ hi def link typescriptArrayStaticMethod Keyword
+ syntax keyword typescriptArrayMethod contained concat copyWithin entries every fill nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptArrayMethod contained filter find findIndex forEach indexOf nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptArrayMethod contained includes join keys lastIndexOf map nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptArrayMethod contained pop push reduce reduceRight reverse nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptArrayMethod contained shift slice some sort splice toLocaleString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptArrayMethod contained toSource toString unshift nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptArrayMethod
+ hi def link typescriptArrayMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Object nextgroup=typescriptGlobalObjectDot,typescriptFuncCallArg
+ syntax match typescriptGlobalObjectDot /\./ contained nextgroup=typescriptObjectStaticMethod,typescriptProp
+ syntax keyword typescriptObjectStaticMethod contained create defineProperties defineProperty nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectStaticMethod contained entries freeze getOwnPropertyDescriptors nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectStaticMethod contained getOwnPropertyDescriptor getOwnPropertyNames nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectStaticMethod contained getOwnPropertySymbols getPrototypeOf nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectStaticMethod contained is isExtensible isFrozen isSealed nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectStaticMethod contained keys preventExtensions values nextgroup=typescriptFuncCallArg
+ hi def link typescriptObjectStaticMethod Keyword
+ syntax keyword typescriptObjectMethod contained getOwnPropertyDescriptors hasOwnProperty nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectMethod contained isPrototypeOf propertyIsEnumerable nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectMethod contained toLocaleString toString valueOf seal nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptObjectMethod contained setPrototypeOf nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptObjectMethod
+ hi def link typescriptObjectMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Symbol nextgroup=typescriptGlobalSymbolDot,typescriptFuncCallArg
+ syntax match typescriptGlobalSymbolDot /\./ contained nextgroup=typescriptSymbolStaticProp,typescriptSymbolStaticMethod,typescriptProp
+ syntax keyword typescriptSymbolStaticProp contained length iterator match replace
+ syntax keyword typescriptSymbolStaticProp contained search split hasInstance isConcatSpreadable
+ syntax keyword typescriptSymbolStaticProp contained unscopables species toPrimitive
+ syntax keyword typescriptSymbolStaticProp contained toStringTag
+ hi def link typescriptSymbolStaticProp Keyword
+ syntax keyword typescriptSymbolStaticMethod contained for keyFor nextgroup=typescriptFuncCallArg
+ hi def link typescriptSymbolStaticMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Function
+ syntax keyword typescriptFunctionMethod contained apply bind call nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptFunctionMethod
+ hi def link typescriptFunctionMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Math nextgroup=typescriptGlobalMathDot,typescriptFuncCallArg
+ syntax match typescriptGlobalMathDot /\./ contained nextgroup=typescriptMathStaticProp,typescriptMathStaticMethod,typescriptProp
+ syntax keyword typescriptMathStaticProp contained E LN10 LN2 LOG10E LOG2E PI SQRT1_2
+ syntax keyword typescriptMathStaticProp contained SQRT2
+ hi def link typescriptMathStaticProp Keyword
+ syntax keyword typescriptMathStaticMethod contained abs acos acosh asin asinh atan nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptMathStaticMethod contained atan2 atanh cbrt ceil clz32 cos nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptMathStaticMethod contained cosh exp expm1 floor fround hypot nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptMathStaticMethod contained imul log log10 log1p log2 max nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptMathStaticMethod contained min pow random round sign sin nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptMathStaticMethod contained sinh sqrt tan tanh trunc nextgroup=typescriptFuncCallArg
+ hi def link typescriptMathStaticMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Date nextgroup=typescriptGlobalDateDot,typescriptFuncCallArg
+ syntax match typescriptGlobalDateDot /\./ contained nextgroup=typescriptDateStaticMethod,typescriptProp
+ syntax keyword typescriptDateStaticMethod contained UTC now parse nextgroup=typescriptFuncCallArg
+ hi def link typescriptDateStaticMethod Keyword
+ syntax keyword typescriptDateMethod contained getDate getDay getFullYear getHours nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained getMilliseconds getMinutes getMonth nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained getSeconds getTime getTimezoneOffset nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained getUTCDate getUTCDay getUTCFullYear nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained getUTCHours getUTCMilliseconds getUTCMinutes nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained getUTCMonth getUTCSeconds setDate setFullYear nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained setHours setMilliseconds setMinutes nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained setMonth setSeconds setTime setUTCDate nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained setUTCFullYear setUTCHours setUTCMilliseconds nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained setUTCMinutes setUTCMonth setUTCSeconds nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained toDateString toISOString toJSON toLocaleDateString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained toLocaleFormat toLocaleString toLocaleTimeString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained toSource toString toTimeString toUTCString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDateMethod contained valueOf nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptDateMethod
+ hi def link typescriptDateMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName JSON nextgroup=typescriptGlobalJSONDot,typescriptFuncCallArg
+ syntax match typescriptGlobalJSONDot /\./ contained nextgroup=typescriptJSONStaticMethod,typescriptProp
+ syntax keyword typescriptJSONStaticMethod contained parse stringify nextgroup=typescriptFuncCallArg
+ hi def link typescriptJSONStaticMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName RegExp nextgroup=typescriptGlobalRegExpDot,typescriptFuncCallArg
+ syntax match typescriptGlobalRegExpDot /\./ contained nextgroup=typescriptRegExpStaticProp,typescriptProp
+ syntax keyword typescriptRegExpStaticProp contained lastIndex
+ hi def link typescriptRegExpStaticProp Keyword
+ syntax keyword typescriptRegExpProp contained global ignoreCase multiline source sticky
+ syntax cluster props add=typescriptRegExpProp
+ hi def link typescriptRegExpProp Keyword
+ syntax keyword typescriptRegExpMethod contained exec test nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptRegExpMethod
+ hi def link typescriptRegExpMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Map WeakMap
+ syntax keyword typescriptES6MapProp contained size
+ syntax cluster props add=typescriptES6MapProp
+ hi def link typescriptES6MapProp Keyword
+ syntax keyword typescriptES6MapMethod contained clear delete entries forEach get has nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptES6MapMethod contained keys set values nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptES6MapMethod
+ hi def link typescriptES6MapMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Set WeakSet
+ syntax keyword typescriptES6SetProp contained size
+ syntax cluster props add=typescriptES6SetProp
+ hi def link typescriptES6SetProp Keyword
+ syntax keyword typescriptES6SetMethod contained add clear delete entries forEach has nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptES6SetMethod contained values nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptES6SetMethod
+ hi def link typescriptES6SetMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Proxy
+ syntax keyword typescriptProxyAPI contained getOwnPropertyDescriptor getOwnPropertyNames
+ syntax keyword typescriptProxyAPI contained defineProperty deleteProperty freeze seal
+ syntax keyword typescriptProxyAPI contained preventExtensions has hasOwn get set enumerate
+ syntax keyword typescriptProxyAPI contained iterate ownKeys apply construct
+ hi def link typescriptProxyAPI Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Promise nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg
+ syntax match typescriptGlobalPromiseDot /\./ contained nextgroup=typescriptPromiseStaticMethod,typescriptProp
+ syntax keyword typescriptPromiseStaticMethod contained resolve reject all race nextgroup=typescriptFuncCallArg
+ hi def link typescriptPromiseStaticMethod Keyword
+ syntax keyword typescriptPromiseMethod contained then catch finally nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptPromiseMethod
+ hi def link typescriptPromiseMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Reflect
+ syntax keyword typescriptReflectMethod contained apply construct defineProperty deleteProperty nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptReflectMethod contained enumerate get getOwnPropertyDescriptor nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptReflectMethod contained getPrototypeOf has isExtensible ownKeys nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptReflectMethod contained preventExtensions set setPrototypeOf nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptReflectMethod
+ hi def link typescriptReflectMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Intl
+ syntax keyword typescriptIntlMethod contained Collator DateTimeFormat NumberFormat nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptIntlMethod contained PluralRules nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptIntlMethod
+ hi def link typescriptIntlMethod Keyword
+
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName global process
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName console Buffer
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName module exports
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName setTimeout
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName clearTimeout
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName setInterval
+ syntax keyword typescriptNodeGlobal containedin=typescriptIdentifierName clearInterval
+ hi def link typescriptNodeGlobal Structure
+
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName describe
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName it test before
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName after beforeEach
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName afterEach
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName beforeAll
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName afterAll
+ syntax keyword typescriptTestGlobal containedin=typescriptIdentifierName expect assert
+
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName AbortController
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName AbstractWorker AnalyserNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName App Apps ArrayBuffer
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ArrayBufferView
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Attr AudioBuffer
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName AudioBufferSourceNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName AudioContext AudioDestinationNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName AudioListener AudioNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName AudioParam BatteryManager
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName BiquadFilterNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName BlobEvent BluetoothAdapter
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName BluetoothDevice
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName BluetoothManager
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CameraCapabilities
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CameraControl CameraManager
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CanvasGradient CanvasImageSource
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CanvasPattern CanvasRenderingContext2D
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CaretPosition CDATASection
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ChannelMergerNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ChannelSplitterNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CharacterData ChildNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ChromeWorker Comment
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Connection Console
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ContactManager Contacts
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ConvolverNode Coordinates
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSS CSSConditionRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSGroupingRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSKeyframeRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSKeyframesRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSMediaRule CSSNamespaceRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSPageRule CSSRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSRuleList CSSStyleDeclaration
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSStyleRule CSSStyleSheet
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName CSSSupportsRule
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DataTransfer DataView
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DedicatedWorkerGlobalScope
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DelayNode DeviceAcceleration
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DeviceRotationRate
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DeviceStorage DirectoryEntry
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DirectoryEntrySync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DirectoryReader
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DirectoryReaderSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Document DocumentFragment
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DocumentTouch DocumentType
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMCursor DOMError
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMException DOMHighResTimeStamp
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMImplementation
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMImplementationRegistry
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMParser DOMRequest
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMString DOMStringList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMStringMap DOMTimeStamp
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName DOMTokenList DynamicsCompressorNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Element Entry EntrySync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Extensions FileException
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Float32Array Float64Array
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName FMRadio FormData
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName GainNode Gamepad
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName GamepadButton Geolocation
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName History HTMLAnchorElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLAreaElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLAudioElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLBaseElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLBodyElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLBRElement HTMLButtonElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLCanvasElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLCollection HTMLDataElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLDataListElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLDivElement HTMLDListElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLDocument HTMLElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLEmbedElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLFieldSetElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLFormControlsCollection
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLFormElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLHeadElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLHeadingElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLHRElement HTMLHtmlElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLIFrameElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLImageElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLInputElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLKeygenElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLLabelElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLLegendElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLLIElement HTMLLinkElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLMapElement HTMLMediaElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLMetaElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLMeterElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLModElement HTMLObjectElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLOListElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLOptGroupElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLOptionElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLOptionsCollection
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLOutputElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLParagraphElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLParamElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLPreElement HTMLProgressElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLQuoteElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLScriptElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLSelectElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLSourceElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLSpanElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLStyleElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableCaptionElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableCellElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableColElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableDataCellElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableHeaderCellElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableRowElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTableSectionElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTextAreaElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTimeElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTitleElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLTrackElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLUListElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLUnknownElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName HTMLVideoElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBCursor IDBCursorSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBCursorWithValue
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBDatabase IDBDatabaseSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBEnvironment IDBEnvironmentSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBFactory IDBFactorySync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBIndex IDBIndexSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBKeyRange IDBObjectStore
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBObjectStoreSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBOpenDBRequest
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBRequest IDBTransaction
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBTransactionSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName IDBVersionChangeEvent
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ImageData IndexedDB
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Int16Array Int32Array
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Int8Array L10n LinkStyle
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName LocalFileSystem
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName LocalFileSystemSync
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Location LockedFile
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName MediaQueryList MediaQueryListListener
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName MediaRecorder MediaSource
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName MediaStream MediaStreamTrack
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName MutationObserver
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Navigator NavigatorGeolocation
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName NavigatorID NavigatorLanguage
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName NavigatorOnLine
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName NavigatorPlugins
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Node NodeFilter
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName NodeIterator NodeList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Notification OfflineAudioContext
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName OscillatorNode PannerNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ParentNode Performance
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName PerformanceNavigation
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName PerformanceTiming
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Permissions PermissionSettings
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Plugin PluginArray
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Position PositionError
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName PositionOptions
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName PowerManager ProcessingInstruction
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName PromiseResolver
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName PushManager Range
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName RTCConfiguration
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName RTCPeerConnection
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName RTCPeerConnectionErrorCallback
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName RTCSessionDescription
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName RTCSessionDescriptionCallback
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ScriptProcessorNode
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Selection SettingsLock
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SettingsManager
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SharedWorker StyleSheet
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName StyleSheetList SVGAElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAngle SVGAnimateColorElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedAngle
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedBoolean
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedEnumeration
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedInteger
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedLength
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedLengthList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedNumber
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedNumberList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedPoints
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedPreserveAspectRatio
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedRect
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedString
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimatedTransformList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimateElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimateMotionElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimateTransformElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGAnimationElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGCircleElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGClipPathElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGCursorElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGDefsElement SVGDescElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGElement SVGEllipseElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGFilterElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGFontElement SVGFontFaceElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGFontFaceFormatElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGFontFaceNameElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGFontFaceSrcElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGFontFaceUriElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGForeignObjectElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGGElement SVGGlyphElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGGradientElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGHKernElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGImageElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGLength SVGLengthList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGLinearGradientElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGLineElement SVGMaskElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGMatrix SVGMissingGlyphElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGMPathElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGNumber SVGNumberList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGPathElement SVGPatternElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGPoint SVGPolygonElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGPolylineElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGPreserveAspectRatio
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGRadialGradientElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGRect SVGRectElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGScriptElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGSetElement SVGStopElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGStringList SVGStylable
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGStyleElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGSVGElement SVGSwitchElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGSymbolElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGTests SVGTextElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGTextPositioningElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGTitleElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGTransform SVGTransformable
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGTransformList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGTRefElement SVGTSpanElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGUseElement SVGViewElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName SVGVKernElement
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName TCPServerSocket
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName TCPSocket Telephony
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName TelephonyCall Text
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName TextDecoder TextEncoder
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName TextMetrics TimeRanges
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Touch TouchList
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Transferable TreeWalker
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Uint16Array Uint32Array
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Uint8Array Uint8ClampedArray
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName URLSearchParams
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName URLUtilsReadOnly
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName UserProximityEvent
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName ValidityState VideoPlaybackQuality
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName WaveShaperNode WebBluetooth
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName WebGLRenderingContext
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName WebSMS WebSocket
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName WebVTT WifiManager
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName Window Worker WorkerConsole
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName WorkerLocation WorkerNavigator
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName XDomainRequest XMLDocument
+ syntax keyword typescriptBOM containedin=typescriptIdentifierName XMLHttpRequestEventTarget
+ hi def link typescriptBOM Structure
+
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName applicationCache
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName closed
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName Components
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName controllers
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName dialogArguments
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName document
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName frameElement
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName frames
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName fullScreen
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName history
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName innerHeight
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName innerWidth
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName length
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName location
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName locationbar
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName menubar
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName messageManager
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName name navigator
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName opener
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName outerHeight
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName outerWidth
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName pageXOffset
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName pageYOffset
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName parent
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName performance
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName personalbar
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName returnValue
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName screen
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName screenX
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName screenY
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName scrollbars
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName scrollMaxX
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName scrollMaxY
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName scrollX
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName scrollY
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName self sidebar
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName status
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName statusbar
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName toolbar
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName top visualViewport
+ syntax keyword typescriptBOMWindowProp containedin=typescriptIdentifierName window
+ syntax cluster props add=typescriptBOMWindowProp
+ hi def link typescriptBOMWindowProp Structure
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName alert nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName atob nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName blur nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName btoa nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName clearImmediate nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName clearInterval nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName clearTimeout nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName close nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName confirm nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName dispatchEvent nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName find nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName focus nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName getAttention nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName getAttentionWithCycleCount nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName getComputedStyle nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName getDefaulComputedStyle nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName getSelection nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName matchMedia nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName maximize nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName moveBy nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName moveTo nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName open nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName openDialog nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName postMessage nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName print nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName prompt nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName removeEventListener nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName resizeBy nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName resizeTo nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName restore nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName scroll nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName scrollBy nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName scrollByLines nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName scrollByPages nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName scrollTo nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName setCursor nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName setImmediate nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName setInterval nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName setResizable nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName setTimeout nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName showModalDialog nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName sizeToContent nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName stop nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMWindowMethod containedin=typescriptIdentifierName updateCommands nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptBOMWindowMethod
+ hi def link typescriptBOMWindowMethod Structure
+ syntax keyword typescriptBOMWindowEvent contained onabort onbeforeunload onblur onchange
+ syntax keyword typescriptBOMWindowEvent contained onclick onclose oncontextmenu ondevicelight
+ syntax keyword typescriptBOMWindowEvent contained ondevicemotion ondeviceorientation
+ syntax keyword typescriptBOMWindowEvent contained ondeviceproximity ondragdrop onerror
+ syntax keyword typescriptBOMWindowEvent contained onfocus onhashchange onkeydown onkeypress
+ syntax keyword typescriptBOMWindowEvent contained onkeyup onload onmousedown onmousemove
+ syntax keyword typescriptBOMWindowEvent contained onmouseout onmouseover onmouseup
+ syntax keyword typescriptBOMWindowEvent contained onmozbeforepaint onpaint onpopstate
+ syntax keyword typescriptBOMWindowEvent contained onreset onresize onscroll onselect
+ syntax keyword typescriptBOMWindowEvent contained onsubmit onunload onuserproximity
+ syntax keyword typescriptBOMWindowEvent contained onpageshow onpagehide
+ hi def link typescriptBOMWindowEvent Keyword
+ syntax keyword typescriptBOMWindowCons containedin=typescriptIdentifierName DOMParser
+ syntax keyword typescriptBOMWindowCons containedin=typescriptIdentifierName QueryInterface
+ syntax keyword typescriptBOMWindowCons containedin=typescriptIdentifierName XMLSerializer
+ hi def link typescriptBOMWindowCons Structure
+
+ syntax keyword typescriptBOMNavigatorProp contained battery buildID connection cookieEnabled
+ syntax keyword typescriptBOMNavigatorProp contained doNotTrack maxTouchPoints oscpu
+ syntax keyword typescriptBOMNavigatorProp contained productSub push serviceWorker
+ syntax keyword typescriptBOMNavigatorProp contained vendor vendorSub
+ syntax cluster props add=typescriptBOMNavigatorProp
+ hi def link typescriptBOMNavigatorProp Keyword
+ syntax keyword typescriptBOMNavigatorMethod contained addIdleObserver geolocation nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMNavigatorMethod contained getDeviceStorage getDeviceStorages nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMNavigatorMethod contained getGamepads getUserMedia registerContentHandler nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMNavigatorMethod contained removeIdleObserver requestWakeLock nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMNavigatorMethod contained share vibrate watch registerProtocolHandler nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptBOMNavigatorMethod contained sendBeacon nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptBOMNavigatorMethod
+ hi def link typescriptBOMNavigatorMethod Keyword
+ syntax keyword typescriptServiceWorkerMethod contained register nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptServiceWorkerMethod
+ hi def link typescriptServiceWorkerMethod Keyword
+
+ syntax keyword typescriptBOMLocationProp contained href protocol host hostname port
+ syntax keyword typescriptBOMLocationProp contained pathname search hash username password
+ syntax keyword typescriptBOMLocationProp contained origin
+ syntax cluster props add=typescriptBOMLocationProp
+ hi def link typescriptBOMLocationProp Keyword
+ syntax keyword typescriptBOMLocationMethod contained assign reload replace toString nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptBOMLocationMethod
+ hi def link typescriptBOMLocationMethod Keyword
+
+ syntax keyword typescriptBOMHistoryProp contained length current next previous state
+ syntax keyword typescriptBOMHistoryProp contained scrollRestoration
+ syntax cluster props add=typescriptBOMHistoryProp
+ hi def link typescriptBOMHistoryProp Keyword
+ syntax keyword typescriptBOMHistoryMethod contained back forward go pushState replaceState nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptBOMHistoryMethod
+ hi def link typescriptBOMHistoryMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName console
+ syntax keyword typescriptConsoleMethod contained count dir error group groupCollapsed nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptConsoleMethod contained groupEnd info log time timeEnd trace nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptConsoleMethod contained warn nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptConsoleMethod
+ hi def link typescriptConsoleMethod Keyword
+
+ syntax keyword typescriptXHRGlobal containedin=typescriptIdentifierName XMLHttpRequest
+ hi def link typescriptXHRGlobal Structure
+ syntax keyword typescriptXHRProp contained onreadystatechange readyState response
+ syntax keyword typescriptXHRProp contained responseText responseType responseXML status
+ syntax keyword typescriptXHRProp contained statusText timeout ontimeout upload withCredentials
+ syntax cluster props add=typescriptXHRProp
+ hi def link typescriptXHRProp Keyword
+ syntax keyword typescriptXHRMethod contained abort getAllResponseHeaders getResponseHeader nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptXHRMethod contained open overrideMimeType send setRequestHeader nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptXHRMethod
+ hi def link typescriptXHRMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Blob BlobBuilder
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName File FileReader
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName FileReaderSync
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName URL nextgroup=typescriptGlobalURLDot,typescriptFuncCallArg
+ syntax match typescriptGlobalURLDot /\./ contained nextgroup=typescriptURLStaticMethod,typescriptProp
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName URLUtils
+ syntax keyword typescriptFileMethod contained readAsArrayBuffer readAsBinaryString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptFileMethod contained readAsDataURL readAsText nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptFileMethod
+ hi def link typescriptFileMethod Keyword
+ syntax keyword typescriptFileReaderProp contained error readyState result
+ syntax cluster props add=typescriptFileReaderProp
+ hi def link typescriptFileReaderProp Keyword
+ syntax keyword typescriptFileReaderMethod contained abort readAsArrayBuffer readAsBinaryString nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptFileReaderMethod contained readAsDataURL readAsText nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptFileReaderMethod
+ hi def link typescriptFileReaderMethod Keyword
+ syntax keyword typescriptFileListMethod contained item nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptFileListMethod
+ hi def link typescriptFileListMethod Keyword
+ syntax keyword typescriptBlobMethod contained append getBlob getFile nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptBlobMethod
+ hi def link typescriptBlobMethod Keyword
+ syntax keyword typescriptURLUtilsProp contained hash host hostname href origin password
+ syntax keyword typescriptURLUtilsProp contained pathname port protocol search searchParams
+ syntax keyword typescriptURLUtilsProp contained username
+ syntax cluster props add=typescriptURLUtilsProp
+ hi def link typescriptURLUtilsProp Keyword
+ syntax keyword typescriptURLStaticMethod contained createObjectURL revokeObjectURL nextgroup=typescriptFuncCallArg
+ hi def link typescriptURLStaticMethod Keyword
+
+ syntax keyword typescriptCryptoGlobal containedin=typescriptIdentifierName crypto
+ hi def link typescriptCryptoGlobal Structure
+ syntax keyword typescriptSubtleCryptoMethod contained encrypt decrypt sign verify nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptSubtleCryptoMethod contained digest nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptSubtleCryptoMethod
+ hi def link typescriptSubtleCryptoMethod Keyword
+ syntax keyword typescriptCryptoProp contained subtle
+ syntax cluster props add=typescriptCryptoProp
+ hi def link typescriptCryptoProp Keyword
+ syntax keyword typescriptCryptoMethod contained getRandomValues nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptCryptoMethod
+ hi def link typescriptCryptoMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Headers Request
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Response
+ syntax keyword typescriptGlobalMethod containedin=typescriptIdentifierName fetch nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptGlobalMethod
+ hi def link typescriptGlobalMethod Structure
+ syntax keyword typescriptHeadersMethod contained append delete get getAll has set nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptHeadersMethod
+ hi def link typescriptHeadersMethod Keyword
+ syntax keyword typescriptRequestProp contained method url headers context referrer
+ syntax keyword typescriptRequestProp contained mode credentials cache
+ syntax cluster props add=typescriptRequestProp
+ hi def link typescriptRequestProp Keyword
+ syntax keyword typescriptRequestMethod contained clone nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptRequestMethod
+ hi def link typescriptRequestMethod Keyword
+ syntax keyword typescriptResponseProp contained type url status statusText headers
+ syntax keyword typescriptResponseProp contained redirected
+ syntax cluster props add=typescriptResponseProp
+ hi def link typescriptResponseProp Keyword
+ syntax keyword typescriptResponseMethod contained clone nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptResponseMethod
+ hi def link typescriptResponseMethod Keyword
+
+ syntax keyword typescriptServiceWorkerProp contained controller ready
+ syntax cluster props add=typescriptServiceWorkerProp
+ hi def link typescriptServiceWorkerProp Keyword
+ syntax keyword typescriptServiceWorkerMethod contained register getRegistration nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptServiceWorkerMethod
+ hi def link typescriptServiceWorkerMethod Keyword
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Cache
+ syntax keyword typescriptCacheMethod contained match matchAll add addAll put delete nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptCacheMethod contained keys nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptCacheMethod
+ hi def link typescriptCacheMethod Keyword
+
+ syntax keyword typescriptEncodingGlobal containedin=typescriptIdentifierName TextEncoder
+ syntax keyword typescriptEncodingGlobal containedin=typescriptIdentifierName TextDecoder
+ hi def link typescriptEncodingGlobal Structure
+ syntax keyword typescriptEncodingProp contained encoding fatal ignoreBOM
+ syntax cluster props add=typescriptEncodingProp
+ hi def link typescriptEncodingProp Keyword
+ syntax keyword typescriptEncodingMethod contained encode decode nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptEncodingMethod
+ hi def link typescriptEncodingMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName Geolocation
+ syntax keyword typescriptGeolocationMethod contained getCurrentPosition watchPosition nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptGeolocationMethod contained clearWatch nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptGeolocationMethod
+ hi def link typescriptGeolocationMethod Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName NetworkInformation
+ syntax keyword typescriptBOMNetworkProp contained downlink downlinkMax effectiveType
+ syntax keyword typescriptBOMNetworkProp contained rtt type
+ syntax cluster props add=typescriptBOMNetworkProp
+ hi def link typescriptBOMNetworkProp Keyword
+
+ syntax keyword typescriptGlobal containedin=typescriptIdentifierName PaymentRequest
+ syntax keyword typescriptPaymentMethod contained show abort canMakePayment nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptPaymentMethod
+ hi def link typescriptPaymentMethod Keyword
+ syntax keyword typescriptPaymentProp contained shippingAddress shippingOption result
+ syntax cluster props add=typescriptPaymentProp
+ hi def link typescriptPaymentProp Keyword
+ syntax keyword typescriptPaymentEvent contained onshippingaddresschange onshippingoptionchange
+ hi def link typescriptPaymentEvent Keyword
+ syntax keyword typescriptPaymentResponseMethod contained complete nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptPaymentResponseMethod
+ hi def link typescriptPaymentResponseMethod Keyword
+ syntax keyword typescriptPaymentResponseProp contained details methodName payerEmail
+ syntax keyword typescriptPaymentResponseProp contained payerPhone shippingAddress
+ syntax keyword typescriptPaymentResponseProp contained shippingOption
+ syntax cluster props add=typescriptPaymentResponseProp
+ hi def link typescriptPaymentResponseProp Keyword
+ syntax keyword typescriptPaymentAddressProp contained addressLine careOf city country
+ syntax keyword typescriptPaymentAddressProp contained country dependentLocality languageCode
+ syntax keyword typescriptPaymentAddressProp contained organization phone postalCode
+ syntax keyword typescriptPaymentAddressProp contained recipient region sortingCode
+ syntax cluster props add=typescriptPaymentAddressProp
+ hi def link typescriptPaymentAddressProp Keyword
+ syntax keyword typescriptPaymentShippingOptionProp contained id label amount selected
+ syntax cluster props add=typescriptPaymentShippingOptionProp
+ hi def link typescriptPaymentShippingOptionProp Keyword
+
+ syntax keyword typescriptDOMNodeProp contained attributes baseURI baseURIObject childNodes
+ syntax keyword typescriptDOMNodeProp contained firstChild lastChild localName namespaceURI
+ syntax keyword typescriptDOMNodeProp contained nextSibling nodeName nodePrincipal
+ syntax keyword typescriptDOMNodeProp contained nodeType nodeValue ownerDocument parentElement
+ syntax keyword typescriptDOMNodeProp contained parentNode prefix previousSibling textContent
+ syntax cluster props add=typescriptDOMNodeProp
+ hi def link typescriptDOMNodeProp Keyword
+ syntax keyword typescriptDOMNodeMethod contained appendChild cloneNode compareDocumentPosition nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMNodeMethod contained getUserData hasAttributes hasChildNodes nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMNodeMethod contained insertBefore isDefaultNamespace isEqualNode nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMNodeMethod contained isSameNode isSupported lookupNamespaceURI nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMNodeMethod contained lookupPrefix normalize removeChild nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMNodeMethod contained replaceChild setUserData nextgroup=typescriptFuncCallArg
+ syntax match typescriptDOMNodeMethod contained /contains/
+ syntax cluster props add=typescriptDOMNodeMethod
+ hi def link typescriptDOMNodeMethod Keyword
+ syntax keyword typescriptDOMNodeType contained ELEMENT_NODE ATTRIBUTE_NODE TEXT_NODE
+ syntax keyword typescriptDOMNodeType contained CDATA_SECTION_NODEN_NODE ENTITY_REFERENCE_NODE
+ syntax keyword typescriptDOMNodeType contained ENTITY_NODE PROCESSING_INSTRUCTION_NODEN_NODE
+ syntax keyword typescriptDOMNodeType contained COMMENT_NODE DOCUMENT_NODE DOCUMENT_TYPE_NODE
+ syntax keyword typescriptDOMNodeType contained DOCUMENT_FRAGMENT_NODE NOTATION_NODE
+ hi def link typescriptDOMNodeType Keyword
+
+ syntax keyword typescriptDOMElemAttrs contained accessKey clientHeight clientLeft
+ syntax keyword typescriptDOMElemAttrs contained clientTop clientWidth id innerHTML
+ syntax keyword typescriptDOMElemAttrs contained length onafterscriptexecute onbeforescriptexecute
+ syntax keyword typescriptDOMElemAttrs contained oncopy oncut onpaste onwheel scrollHeight
+ syntax keyword typescriptDOMElemAttrs contained scrollLeft scrollTop scrollWidth tagName
+ syntax keyword typescriptDOMElemAttrs contained classList className name outerHTML
+ syntax keyword typescriptDOMElemAttrs contained style
+ hi def link typescriptDOMElemAttrs Keyword
+ syntax keyword typescriptDOMElemFuncs contained getAttributeNS getAttributeNode getAttributeNodeNS
+ syntax keyword typescriptDOMElemFuncs contained getBoundingClientRect getClientRects
+ syntax keyword typescriptDOMElemFuncs contained getElementsByClassName getElementsByTagName
+ syntax keyword typescriptDOMElemFuncs contained getElementsByTagNameNS hasAttribute
+ syntax keyword typescriptDOMElemFuncs contained hasAttributeNS insertAdjacentHTML
+ syntax keyword typescriptDOMElemFuncs contained matches querySelector querySelectorAll
+ syntax keyword typescriptDOMElemFuncs contained removeAttribute removeAttributeNS
+ syntax keyword typescriptDOMElemFuncs contained removeAttributeNode requestFullscreen
+ syntax keyword typescriptDOMElemFuncs contained requestPointerLock scrollIntoView
+ syntax keyword typescriptDOMElemFuncs contained setAttribute setAttributeNS setAttributeNode
+ syntax keyword typescriptDOMElemFuncs contained setAttributeNodeNS setCapture supports
+ syntax keyword typescriptDOMElemFuncs contained getAttribute
+ hi def link typescriptDOMElemFuncs Keyword
+
+ syntax keyword typescriptDOMDocProp contained activeElement body cookie defaultView
+ syntax keyword typescriptDOMDocProp contained designMode dir domain embeds forms head
+ syntax keyword typescriptDOMDocProp contained images lastModified links location plugins
+ syntax keyword typescriptDOMDocProp contained postMessage readyState referrer registerElement
+ syntax keyword typescriptDOMDocProp contained scripts styleSheets title vlinkColor
+ syntax keyword typescriptDOMDocProp contained xmlEncoding characterSet compatMode
+ syntax keyword typescriptDOMDocProp contained contentType currentScript doctype documentElement
+ syntax keyword typescriptDOMDocProp contained documentURI documentURIObject firstChild
+ syntax keyword typescriptDOMDocProp contained implementation lastStyleSheetSet namespaceURI
+ syntax keyword typescriptDOMDocProp contained nodePrincipal ononline pointerLockElement
+ syntax keyword typescriptDOMDocProp contained popupNode preferredStyleSheetSet selectedStyleSheetSet
+ syntax keyword typescriptDOMDocProp contained styleSheetSets textContent tooltipNode
+ syntax cluster props add=typescriptDOMDocProp
+ hi def link typescriptDOMDocProp Keyword
+ syntax keyword typescriptDOMDocMethod contained caretPositionFromPoint close createNodeIterator nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained createRange createTreeWalker elementFromPoint nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained getElementsByName adoptNode createAttribute nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained createCDATASection createComment createDocumentFragment nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained createElement createElementNS createEvent nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained createExpression createNSResolver nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained createProcessingInstruction createTextNode nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained enableStyleSheetsForSet evaluate execCommand nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained exitPointerLock getBoxObjectFor getElementById nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained getElementsByClassName getElementsByTagName nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained getElementsByTagNameNS getSelection nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained hasFocus importNode loadOverlay open nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained queryCommandSupported querySelector nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMDocMethod contained querySelectorAll write writeln nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptDOMDocMethod
+ hi def link typescriptDOMDocMethod Keyword
+
+ syntax keyword typescriptDOMEventTargetMethod contained addEventListener removeEventListener nextgroup=typescriptEventFuncCallArg
+ syntax keyword typescriptDOMEventTargetMethod contained dispatchEvent waitUntil nextgroup=typescriptEventFuncCallArg
+ syntax cluster props add=typescriptDOMEventTargetMethod
+ hi def link typescriptDOMEventTargetMethod Keyword
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName AnimationEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName AudioProcessingEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName BeforeInputEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName BeforeUnloadEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName BlobEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName ClipboardEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName CloseEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName CompositionEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName CSSFontFaceLoadEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName CustomEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName DeviceLightEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName DeviceMotionEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName DeviceOrientationEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName DeviceProximityEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName DOMTransactionEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName DragEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName EditingBeforeInputEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName ErrorEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName FocusEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName GamepadEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName HashChangeEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName IDBVersionChangeEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName KeyboardEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName MediaStreamEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName MessageEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName MouseEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName MutationEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName OfflineAudioCompletionEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName PageTransitionEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName PointerEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName PopStateEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName ProgressEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName RelatedEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName RTCPeerConnectionIceEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName SensorEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName StorageEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName SVGEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName SVGZoomEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName TimeEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName TouchEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName TrackEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName TransitionEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName UIEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName UserProximityEvent
+ syntax keyword typescriptDOMEventCons containedin=typescriptIdentifierName WheelEvent
+ hi def link typescriptDOMEventCons Structure
+ syntax keyword typescriptDOMEventProp contained bubbles cancelable currentTarget defaultPrevented
+ syntax keyword typescriptDOMEventProp contained eventPhase target timeStamp type isTrusted
+ syntax keyword typescriptDOMEventProp contained isReload
+ syntax cluster props add=typescriptDOMEventProp
+ hi def link typescriptDOMEventProp Keyword
+ syntax keyword typescriptDOMEventMethod contained initEvent preventDefault stopImmediatePropagation nextgroup=typescriptEventFuncCallArg
+ syntax keyword typescriptDOMEventMethod contained stopPropagation respondWith default nextgroup=typescriptEventFuncCallArg
+ syntax cluster props add=typescriptDOMEventMethod
+ hi def link typescriptDOMEventMethod Keyword
+
+ syntax keyword typescriptDOMStorage contained sessionStorage localStorage
+ hi def link typescriptDOMStorage Keyword
+ syntax keyword typescriptDOMStorageProp contained length
+ syntax cluster props add=typescriptDOMStorageProp
+ hi def link typescriptDOMStorageProp Keyword
+ syntax keyword typescriptDOMStorageMethod contained getItem key setItem removeItem nextgroup=typescriptFuncCallArg
+ syntax keyword typescriptDOMStorageMethod contained clear nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptDOMStorageMethod
+ hi def link typescriptDOMStorageMethod Keyword
+
+ syntax keyword typescriptDOMFormProp contained acceptCharset action elements encoding
+ syntax keyword typescriptDOMFormProp contained enctype length method name target
+ syntax cluster props add=typescriptDOMFormProp
+ hi def link typescriptDOMFormProp Keyword
+ syntax keyword typescriptDOMFormMethod contained reportValidity reset submit nextgroup=typescriptFuncCallArg
+ syntax cluster props add=typescriptDOMFormMethod
+ hi def link typescriptDOMFormMethod Keyword
+
+ syntax keyword typescriptDOMStyle contained alignContent alignItems alignSelf animation
+ syntax keyword typescriptDOMStyle contained animationDelay animationDirection animationDuration
+ syntax keyword typescriptDOMStyle contained animationFillMode animationIterationCount
+ syntax keyword typescriptDOMStyle contained animationName animationPlayState animationTimingFunction
+ syntax keyword typescriptDOMStyle contained appearance backfaceVisibility background
+ syntax keyword typescriptDOMStyle contained backgroundAttachment backgroundBlendMode
+ syntax keyword typescriptDOMStyle contained backgroundClip backgroundColor backgroundImage
+ syntax keyword typescriptDOMStyle contained backgroundOrigin backgroundPosition backgroundRepeat
+ syntax keyword typescriptDOMStyle contained backgroundSize border borderBottom borderBottomColor
+ syntax keyword typescriptDOMStyle contained borderBottomLeftRadius borderBottomRightRadius
+ syntax keyword typescriptDOMStyle contained borderBottomStyle borderBottomWidth borderCollapse
+ syntax keyword typescriptDOMStyle contained borderColor borderImage borderImageOutset
+ syntax keyword typescriptDOMStyle contained borderImageRepeat borderImageSlice borderImageSource
+ syntax keyword typescriptDOMStyle contained borderImageWidth borderLeft borderLeftColor
+ syntax keyword typescriptDOMStyle contained borderLeftStyle borderLeftWidth borderRadius
+ syntax keyword typescriptDOMStyle contained borderRight borderRightColor borderRightStyle
+ syntax keyword typescriptDOMStyle contained borderRightWidth borderSpacing borderStyle
+ syntax keyword typescriptDOMStyle contained borderTop borderTopColor borderTopLeftRadius
+ syntax keyword typescriptDOMStyle contained borderTopRightRadius borderTopStyle borderTopWidth
+ syntax keyword typescriptDOMStyle contained borderWidth bottom boxDecorationBreak
+ syntax keyword typescriptDOMStyle contained boxShadow boxSizing breakAfter breakBefore
+ syntax keyword typescriptDOMStyle contained breakInside captionSide caretColor caretShape
+ syntax keyword typescriptDOMStyle contained caret clear clip clipPath color columns
+ syntax keyword typescriptDOMStyle contained columnCount columnFill columnGap columnRule
+ syntax keyword typescriptDOMStyle contained columnRuleColor columnRuleStyle columnRuleWidth
+ syntax keyword typescriptDOMStyle contained columnSpan columnWidth content counterIncrement
+ syntax keyword typescriptDOMStyle contained counterReset cursor direction display
+ syntax keyword typescriptDOMStyle contained emptyCells flex flexBasis flexDirection
+ syntax keyword typescriptDOMStyle contained flexFlow flexGrow flexShrink flexWrap
+ syntax keyword typescriptDOMStyle contained float font fontFamily fontFeatureSettings
+ syntax keyword typescriptDOMStyle contained fontKerning fontLanguageOverride fontSize
+ syntax keyword typescriptDOMStyle contained fontSizeAdjust fontStretch fontStyle fontSynthesis
+ syntax keyword typescriptDOMStyle contained fontVariant fontVariantAlternates fontVariantCaps
+ syntax keyword typescriptDOMStyle contained fontVariantEastAsian fontVariantLigatures
+ syntax keyword typescriptDOMStyle contained fontVariantNumeric fontVariantPosition
+ syntax keyword typescriptDOMStyle contained fontWeight grad grid gridArea gridAutoColumns
+ syntax keyword typescriptDOMStyle contained gridAutoFlow gridAutoPosition gridAutoRows
+ syntax keyword typescriptDOMStyle contained gridColumn gridColumnStart gridColumnEnd
+ syntax keyword typescriptDOMStyle contained gridRow gridRowStart gridRowEnd gridTemplate
+ syntax keyword typescriptDOMStyle contained gridTemplateAreas gridTemplateRows gridTemplateColumns
+ syntax keyword typescriptDOMStyle contained height hyphens imageRendering imageResolution
+ syntax keyword typescriptDOMStyle contained imageOrientation imeMode inherit justifyContent
+ syntax keyword typescriptDOMStyle contained left letterSpacing lineBreak lineHeight
+ syntax keyword typescriptDOMStyle contained listStyle listStyleImage listStylePosition
+ syntax keyword typescriptDOMStyle contained listStyleType margin marginBottom marginLeft
+ syntax keyword typescriptDOMStyle contained marginRight marginTop marks mask maskType
+ syntax keyword typescriptDOMStyle contained maxHeight maxWidth minHeight minWidth
+ syntax keyword typescriptDOMStyle contained mixBlendMode objectFit objectPosition
+ syntax keyword typescriptDOMStyle contained opacity order orphans outline outlineColor
+ syntax keyword typescriptDOMStyle contained outlineOffset outlineStyle outlineWidth
+ syntax keyword typescriptDOMStyle contained overflow overflowWrap overflowX overflowY
+ syntax keyword typescriptDOMStyle contained overflowClipBox padding paddingBottom
+ syntax keyword typescriptDOMStyle contained paddingLeft paddingRight paddingTop pageBreakAfter
+ syntax keyword typescriptDOMStyle contained pageBreakBefore pageBreakInside perspective
+ syntax keyword typescriptDOMStyle contained perspectiveOrigin pointerEvents position
+ syntax keyword typescriptDOMStyle contained quotes resize right shapeImageThreshold
+ syntax keyword typescriptDOMStyle contained shapeMargin shapeOutside tableLayout tabSize
+ syntax keyword typescriptDOMStyle contained textAlign textAlignLast textCombineHorizontal
+ syntax keyword typescriptDOMStyle contained textDecoration textDecorationColor textDecorationLine
+ syntax keyword typescriptDOMStyle contained textDecorationStyle textIndent textOrientation
+ syntax keyword typescriptDOMStyle contained textOverflow textRendering textShadow
+ syntax keyword typescriptDOMStyle contained textTransform textUnderlinePosition top
+ syntax keyword typescriptDOMStyle contained touchAction transform transformOrigin
+ syntax keyword typescriptDOMStyle contained transformStyle transition transitionDelay
+ syntax keyword typescriptDOMStyle contained transitionDuration transitionProperty
+ syntax keyword typescriptDOMStyle contained transitionTimingFunction unicodeBidi unicodeRange
+ syntax keyword typescriptDOMStyle contained userSelect userZoom verticalAlign visibility
+ syntax keyword typescriptDOMStyle contained whiteSpace width willChange wordBreak
+ syntax keyword typescriptDOMStyle contained wordSpacing wordWrap writingMode zIndex
+ hi def link typescriptDOMStyle Keyword
+
+
+
+ let typescript_props = 1
+ syntax keyword typescriptAnimationEvent contained animationend animationiteration
+ syntax keyword typescriptAnimationEvent contained animationstart beginEvent endEvent
+ syntax keyword typescriptAnimationEvent contained repeatEvent
+ syntax cluster events add=typescriptAnimationEvent
+ hi def link typescriptAnimationEvent Title
+ syntax keyword typescriptCSSEvent contained CssRuleViewRefreshed CssRuleViewChanged
+ syntax keyword typescriptCSSEvent contained CssRuleViewCSSLinkClicked transitionend
+ syntax cluster events add=typescriptCSSEvent
+ hi def link typescriptCSSEvent Title
+ syntax keyword typescriptDatabaseEvent contained blocked complete error success upgradeneeded
+ syntax keyword typescriptDatabaseEvent contained versionchange
+ syntax cluster events add=typescriptDatabaseEvent
+ hi def link typescriptDatabaseEvent Title
+ syntax keyword typescriptDocumentEvent contained DOMLinkAdded DOMLinkRemoved DOMMetaAdded
+ syntax keyword typescriptDocumentEvent contained DOMMetaRemoved DOMWillOpenModalDialog
+ syntax keyword typescriptDocumentEvent contained DOMModalDialogClosed unload
+ syntax cluster events add=typescriptDocumentEvent
+ hi def link typescriptDocumentEvent Title
+ syntax keyword typescriptDOMMutationEvent contained DOMAttributeNameChanged DOMAttrModified
+ syntax keyword typescriptDOMMutationEvent contained DOMCharacterDataModified DOMContentLoaded
+ syntax keyword typescriptDOMMutationEvent contained DOMElementNameChanged DOMNodeInserted
+ syntax keyword typescriptDOMMutationEvent contained DOMNodeInsertedIntoDocument DOMNodeRemoved
+ syntax keyword typescriptDOMMutationEvent contained DOMNodeRemovedFromDocument DOMSubtreeModified
+ syntax cluster events add=typescriptDOMMutationEvent
+ hi def link typescriptDOMMutationEvent Title
+ syntax keyword typescriptDragEvent contained drag dragdrop dragend dragenter dragexit
+ syntax keyword typescriptDragEvent contained draggesture dragleave dragover dragstart
+ syntax keyword typescriptDragEvent contained drop
+ syntax cluster events add=typescriptDragEvent
+ hi def link typescriptDragEvent Title
+ syntax keyword typescriptElementEvent contained invalid overflow underflow DOMAutoComplete
+ syntax keyword typescriptElementEvent contained command commandupdate
+ syntax cluster events add=typescriptElementEvent
+ hi def link typescriptElementEvent Title
+ syntax keyword typescriptFocusEvent contained blur change DOMFocusIn DOMFocusOut focus
+ syntax keyword typescriptFocusEvent contained focusin focusout
+ syntax cluster events add=typescriptFocusEvent
+ hi def link typescriptFocusEvent Title
+ syntax keyword typescriptFormEvent contained reset submit
+ syntax cluster events add=typescriptFormEvent
+ hi def link typescriptFormEvent Title
+ syntax keyword typescriptFrameEvent contained DOMFrameContentLoaded
+ syntax cluster events add=typescriptFrameEvent
+ hi def link typescriptFrameEvent Title
+ syntax keyword typescriptInputDeviceEvent contained click contextmenu DOMMouseScroll
+ syntax keyword typescriptInputDeviceEvent contained dblclick gamepadconnected gamepaddisconnected
+ syntax keyword typescriptInputDeviceEvent contained keydown keypress keyup MozGamepadButtonDown
+ syntax keyword typescriptInputDeviceEvent contained MozGamepadButtonUp mousedown mouseenter
+ syntax keyword typescriptInputDeviceEvent contained mouseleave mousemove mouseout
+ syntax keyword typescriptInputDeviceEvent contained mouseover mouseup mousewheel MozMousePixelScroll
+ syntax keyword typescriptInputDeviceEvent contained pointerlockchange pointerlockerror
+ syntax keyword typescriptInputDeviceEvent contained wheel
+ syntax cluster events add=typescriptInputDeviceEvent
+ hi def link typescriptInputDeviceEvent Title
+ syntax keyword typescriptMediaEvent contained audioprocess canplay canplaythrough
+ syntax keyword typescriptMediaEvent contained durationchange emptied ended ended loadeddata
+ syntax keyword typescriptMediaEvent contained loadedmetadata MozAudioAvailable pause
+ syntax keyword typescriptMediaEvent contained play playing ratechange seeked seeking
+ syntax keyword typescriptMediaEvent contained stalled suspend timeupdate volumechange
+ syntax keyword typescriptMediaEvent contained waiting complete
+ syntax cluster events add=typescriptMediaEvent
+ hi def link typescriptMediaEvent Title
+ syntax keyword typescriptMenuEvent contained DOMMenuItemActive DOMMenuItemInactive
+ syntax cluster events add=typescriptMenuEvent
+ hi def link typescriptMenuEvent Title
+ syntax keyword typescriptNetworkEvent contained datachange dataerror disabled enabled
+ syntax keyword typescriptNetworkEvent contained offline online statuschange connectionInfoUpdate
+ syntax cluster events add=typescriptNetworkEvent
+ hi def link typescriptNetworkEvent Title
+ syntax keyword typescriptProgressEvent contained abort error load loadend loadstart
+ syntax keyword typescriptProgressEvent contained progress timeout uploadprogress
+ syntax cluster events add=typescriptProgressEvent
+ hi def link typescriptProgressEvent Title
+ syntax keyword typescriptResourceEvent contained cached error load
+ syntax cluster events add=typescriptResourceEvent
+ hi def link typescriptResourceEvent Title
+ syntax keyword typescriptScriptEvent contained afterscriptexecute beforescriptexecute
+ syntax cluster events add=typescriptScriptEvent
+ hi def link typescriptScriptEvent Title
+ syntax keyword typescriptSensorEvent contained compassneedscalibration devicelight
+ syntax keyword typescriptSensorEvent contained devicemotion deviceorientation deviceproximity
+ syntax keyword typescriptSensorEvent contained orientationchange userproximity
+ syntax cluster events add=typescriptSensorEvent
+ hi def link typescriptSensorEvent Title
+ syntax keyword typescriptSessionHistoryEvent contained pagehide pageshow popstate
+ syntax cluster events add=typescriptSessionHistoryEvent
+ hi def link typescriptSessionHistoryEvent Title
+ syntax keyword typescriptStorageEvent contained change storage
+ syntax cluster events add=typescriptStorageEvent
+ hi def link typescriptStorageEvent Title
+ syntax keyword typescriptSVGEvent contained SVGAbort SVGError SVGLoad SVGResize SVGScroll
+ syntax keyword typescriptSVGEvent contained SVGUnload SVGZoom
+ syntax cluster events add=typescriptSVGEvent
+ hi def link typescriptSVGEvent Title
+ syntax keyword typescriptTabEvent contained visibilitychange
+ syntax cluster events add=typescriptTabEvent
+ hi def link typescriptTabEvent Title
+ syntax keyword typescriptTextEvent contained compositionend compositionstart compositionupdate
+ syntax keyword typescriptTextEvent contained copy cut paste select text
+ syntax cluster events add=typescriptTextEvent
+ hi def link typescriptTextEvent Title
+ syntax keyword typescriptTouchEvent contained touchcancel touchend touchenter touchleave
+ syntax keyword typescriptTouchEvent contained touchmove touchstart
+ syntax cluster events add=typescriptTouchEvent
+ hi def link typescriptTouchEvent Title
+ syntax keyword typescriptUpdateEvent contained checking downloading error noupdate
+ syntax keyword typescriptUpdateEvent contained obsolete updateready
+ syntax cluster events add=typescriptUpdateEvent
+ hi def link typescriptUpdateEvent Title
+ syntax keyword typescriptValueChangeEvent contained hashchange input readystatechange
+ syntax cluster events add=typescriptValueChangeEvent
+ hi def link typescriptValueChangeEvent Title
+ syntax keyword typescriptViewEvent contained fullscreen fullscreenchange fullscreenerror
+ syntax keyword typescriptViewEvent contained resize scroll
+ syntax cluster events add=typescriptViewEvent
+ hi def link typescriptViewEvent Title
+ syntax keyword typescriptWebsocketEvent contained close error message open
+ syntax cluster events add=typescriptWebsocketEvent
+ hi def link typescriptWebsocketEvent Title
+ syntax keyword typescriptWindowEvent contained DOMWindowCreated DOMWindowClose DOMTitleChanged
+ syntax cluster events add=typescriptWindowEvent
+ hi def link typescriptWindowEvent Title
+ syntax keyword typescriptUncategorizedEvent contained beforeunload message open show
+ syntax cluster events add=typescriptUncategorizedEvent
+ hi def link typescriptUncategorizedEvent Title
+ syntax keyword typescriptServiceWorkerEvent contained install activate fetch
+ syntax cluster events add=typescriptServiceWorkerEvent
+ hi def link typescriptServiceWorkerEvent Title
+
+
+endif
+
+" patch
+" patch for generated code
+syntax keyword typescriptGlobal Promise
+ \ nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg,typescriptTypeArguments oneline
+syntax keyword typescriptGlobal Map WeakMap
+ \ nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg,typescriptTypeArguments oneline
+
+syntax keyword typescriptConstructor contained constructor
+ \ nextgroup=@typescriptCallSignature
+ \ skipwhite skipempty
+
+
+syntax cluster memberNextGroup contains=typescriptMemberOptionality,typescriptTypeAnnotation,@typescriptCallSignature
+
+syntax match typescriptMember /#\?\K\k*/
+ \ nextgroup=@memberNextGroup
+ \ contained skipwhite
+
+syntax match typescriptMethodAccessor contained /\v(get|set)\s\K/me=e-1
+ \ nextgroup=@typescriptMembers
+
+syntax cluster typescriptPropertyMemberDeclaration contains=
+ \ typescriptClassStatic,
+ \ typescriptAccessibilityModifier,
+ \ typescriptReadonlyModifier,
+ \ typescriptMethodAccessor,
+ \ @typescriptMembers
+ " \ typescriptMemberVariableDeclaration
+
+syntax match typescriptMemberOptionality /?\|!/ contained
+ \ nextgroup=typescriptTypeAnnotation,@typescriptCallSignature
+ \ skipwhite skipempty
+
+syntax cluster typescriptMembers contains=typescriptMember,typescriptStringMember,typescriptComputedMember
+
+syntax keyword typescriptClassStatic static
+ \ nextgroup=@typescriptMembers,typescriptAsyncFuncKeyword,typescriptReadonlyModifier
+ \ skipwhite contained
+
+syntax keyword typescriptAccessibilityModifier public private protected contained
+
+syntax keyword typescriptReadonlyModifier readonly contained
+
+syntax region typescriptStringMember contained
+ \ start=/\z(["']\)/ skip=/\\\\\|\\\z1\|\\\n/ end=/\z1/
+ \ nextgroup=@memberNextGroup
+ \ skipwhite skipempty
+
+syntax region typescriptComputedMember contained matchgroup=typescriptProperty
+ \ start=/\[/rs=s+1 end=/]/
+ \ contains=@typescriptValue,typescriptMember,typescriptMappedIn
+ \ nextgroup=@memberNextGroup
+ \ skipwhite skipempty
+
+"don't add typescriptMembers to nextgroup, let outer scope match it
+" so we won't match abstract method outside abstract class
+syntax keyword typescriptAbstract abstract
+ \ nextgroup=typescriptClassKeyword
+ \ skipwhite skipnl
+syntax keyword typescriptClassKeyword class
+ \ nextgroup=typescriptClassName,typescriptClassExtends,typescriptClassBlock
+ \ skipwhite
+
+syntax match typescriptClassName contained /\K\k*/
+ \ nextgroup=typescriptClassBlock,typescriptClassExtends,typescriptClassTypeParameter
+ \ skipwhite skipnl
+
+syntax region typescriptClassTypeParameter
+ \ start=/</ end=/>/
+ \ contains=@typescriptTypeParameterCluster
+ \ nextgroup=typescriptClassBlock,typescriptClassExtends
+ \ contained skipwhite skipnl
+
+syntax keyword typescriptClassExtends contained extends implements nextgroup=typescriptClassHeritage skipwhite skipnl
+
+syntax match typescriptClassHeritage contained /\v(\k|\.|\(|\))+/
+ \ nextgroup=typescriptClassBlock,typescriptClassExtends,typescriptMixinComma,typescriptClassTypeArguments
+ \ contains=@typescriptValue
+ \ skipwhite skipnl
+ \ contained
+
+syntax region typescriptClassTypeArguments matchgroup=typescriptTypeBrackets
+ \ start=/</ end=/>/
+ \ contains=@typescriptType
+ \ nextgroup=typescriptClassExtends,typescriptClassBlock,typescriptMixinComma
+ \ contained skipwhite skipnl
+
+syntax match typescriptMixinComma /,/ contained nextgroup=typescriptClassHeritage skipwhite skipnl
+
+" we need add arrowFunc to class block for high order arrow func
+" see test case
+syntax region typescriptClassBlock matchgroup=typescriptBraces start=/{/ end=/}/
+ \ contains=@typescriptPropertyMemberDeclaration,typescriptAbstract,@typescriptComments,typescriptBlock,typescriptAssign,typescriptDecorator,typescriptAsyncFuncKeyword,typescriptArrowFunc
+ \ contained fold
+
+syntax keyword typescriptInterfaceKeyword interface nextgroup=typescriptInterfaceName skipwhite
+syntax match typescriptInterfaceName contained /\k\+/
+ \ nextgroup=typescriptObjectType,typescriptInterfaceExtends,typescriptInterfaceTypeParameter
+ \ skipwhite skipnl
+syntax region typescriptInterfaceTypeParameter
+ \ start=/</ end=/>/
+ \ contains=@typescriptTypeParameterCluster
+ \ nextgroup=typescriptObjectType,typescriptInterfaceExtends
+ \ contained
+ \ skipwhite skipnl
+
+syntax keyword typescriptInterfaceExtends contained extends nextgroup=typescriptInterfaceHeritage skipwhite skipnl
+
+syntax match typescriptInterfaceHeritage contained /\v(\k|\.)+/
+ \ nextgroup=typescriptObjectType,typescriptInterfaceComma,typescriptInterfaceTypeArguments
+ \ skipwhite
+
+syntax region typescriptInterfaceTypeArguments matchgroup=typescriptTypeBrackets
+ \ start=/</ end=/>/ skip=/\s*,\s*/
+ \ contains=@typescriptType
+ \ nextgroup=typescriptObjectType,typescriptInterfaceComma
+ \ contained skipwhite
+
+syntax match typescriptInterfaceComma /,/ contained nextgroup=typescriptInterfaceHeritage skipwhite skipnl
+
+"Block VariableStatement EmptyStatement ExpressionStatement IfStatement IterationStatement ContinueStatement BreakStatement ReturnStatement WithStatement LabelledStatement SwitchStatement ThrowStatement TryStatement DebuggerStatement
+syntax cluster typescriptStatement
+ \ contains=typescriptBlock,typescriptVariable,
+ \ @typescriptTopExpression,typescriptAssign,
+ \ typescriptConditional,typescriptRepeat,typescriptBranch,
+ \ typescriptLabel,typescriptStatementKeyword,
+ \ typescriptFuncKeyword,
+ \ typescriptTry,typescriptExceptions,typescriptDebugger,
+ \ typescriptExport,typescriptInterfaceKeyword,typescriptEnum,
+ \ typescriptModule,typescriptAliasKeyword,typescriptImport
+
+syntax cluster typescriptPrimitive contains=typescriptString,typescriptTemplate,typescriptRegexpString,typescriptNumber,typescriptBoolean,typescriptNull,typescriptArray
+
+syntax cluster typescriptEventTypes contains=typescriptEventString,typescriptTemplate,typescriptNumber,typescriptBoolean,typescriptNull
+
+" top level expression: no arrow func
+" also no func keyword. funcKeyword is contained in statement
+" funcKeyword allows overloading (func without body)
+" funcImpl requires body
+syntax cluster typescriptTopExpression
+ \ contains=@typescriptPrimitive,
+ \ typescriptIdentifier,typescriptIdentifierName,
+ \ typescriptOperator,typescriptUnaryOp,
+ \ typescriptParenExp,typescriptRegexpString,
+ \ typescriptGlobal,typescriptAsyncFuncKeyword,
+ \ typescriptClassKeyword,typescriptTypeCast
+
+" no object literal, used in type cast and arrow func
+" TODO: change func keyword to funcImpl
+syntax cluster typescriptExpression
+ \ contains=@typescriptTopExpression,
+ \ typescriptArrowFuncDef,
+ \ typescriptFuncImpl
+
+syntax cluster typescriptValue
+ \ contains=@typescriptExpression,typescriptObjectLiteral
+
+syntax cluster typescriptEventExpression contains=typescriptArrowFuncDef,typescriptParenExp,@typescriptValue,typescriptRegexpString,@typescriptEventTypes,typescriptOperator,typescriptGlobal,jsxRegion
+
+syntax keyword typescriptAsyncFuncKeyword async
+ \ nextgroup=typescriptFuncKeyword,typescriptArrowFuncDef
+ \ skipwhite
+
+syntax keyword typescriptAsyncFuncKeyword await
+ \ nextgroup=@typescriptValue
+ \ skipwhite
+
+syntax keyword typescriptFuncKeyword function
+ \ nextgroup=typescriptAsyncFunc,typescriptFuncName,@typescriptCallSignature
+ \ skipwhite skipempty
+
+syntax match typescriptAsyncFunc contained /*/
+ \ nextgroup=typescriptFuncName,@typescriptCallSignature
+ \ skipwhite skipempty
+
+syntax match typescriptFuncName contained /\K\k*/
+ \ nextgroup=@typescriptCallSignature
+ \ skipwhite
+
+" destructuring ({ a: ee }) =>
+syntax match typescriptArrowFuncDef contained /(\(\s*\({\_[^}]*}\|\k\+\)\(:\_[^)]\)\?,\?\)\+)\s*=>/
+ \ contains=typescriptArrowFuncArg,typescriptArrowFunc
+ \ nextgroup=@typescriptExpression,typescriptBlock
+ \ skipwhite skipempty
+
+" matches `(a) =>` or `([a]) =>` or
+" `(
+" a) =>`
+syntax match typescriptArrowFuncDef contained /(\(\_s*[a-zA-Z\$_\[.]\_[^)]*\)*)\s*=>/
+ \ contains=typescriptArrowFuncArg,typescriptArrowFunc
+ \ nextgroup=@typescriptExpression,typescriptBlock
+ \ skipwhite skipempty
+
+syntax match typescriptArrowFuncDef contained /\K\k*\s*=>/
+ \ contains=typescriptArrowFuncArg,typescriptArrowFunc
+ \ nextgroup=@typescriptExpression,typescriptBlock
+ \ skipwhite skipempty
+
+" TODO: optimize this pattern
+syntax region typescriptArrowFuncDef contained start=/(\_[^(^)]*):/ end=/=>/
+ \ contains=typescriptArrowFuncArg,typescriptArrowFunc,typescriptTypeAnnotation
+ \ nextgroup=@typescriptExpression,typescriptBlock
+ \ skipwhite skipempty keepend
+
+syntax match typescriptArrowFunc /=>/
+syntax match typescriptArrowFuncArg contained /\K\k*/
+syntax region typescriptArrowFuncArg contained start=/<\|(/ end=/\ze=>/ contains=@typescriptCallSignature
+
+syntax region typescriptReturnAnnotation contained start=/:/ end=/{/me=e-1 contains=@typescriptType nextgroup=typescriptBlock
+
+
+syntax region typescriptFuncImpl contained start=/function\>/ end=/{/me=e-1
+ \ contains=typescriptFuncKeyword
+ \ nextgroup=typescriptBlock
+
+syntax cluster typescriptCallImpl contains=typescriptGenericImpl,typescriptParamImpl
+syntax region typescriptGenericImpl matchgroup=typescriptTypeBrackets
+ \ start=/</ end=/>/ skip=/\s*,\s*/
+ \ contains=typescriptTypeParameter
+ \ nextgroup=typescriptParamImpl
+ \ contained skipwhite
+syntax region typescriptParamImpl matchgroup=typescriptParens
+ \ start=/(/ end=/)/
+ \ contains=typescriptDecorator,@typescriptParameterList,@typescriptComments
+ \ nextgroup=typescriptReturnAnnotation,typescriptBlock
+ \ contained skipwhite skipnl
+
+syntax match typescriptDecorator /@\([_$a-zA-Z][_$a-zA-Z0-9]*\.\)*[_$a-zA-Z][_$a-zA-Z0-9]*\>/
+ \ nextgroup=typescriptFuncCallArg,typescriptTypeArguments
+ \ contains=@_semantic,typescriptDotNotation
+
+" Define the default highlighting.
+hi def link typescriptReserved Error
+
+hi def link typescriptEndColons Exception
+hi def link typescriptSymbols Normal
+hi def link typescriptBraces Function
+hi def link typescriptParens Normal
+hi def link typescriptComment Comment
+hi def link typescriptLineComment Comment
+hi def link typescriptDocComment Comment
+hi def link typescriptCommentTodo Todo
+hi def link typescriptMagicComment SpecialComment
+hi def link typescriptRef Include
+hi def link typescriptDocNotation SpecialComment
+hi def link typescriptDocTags SpecialComment
+hi def link typescriptDocNGParam typescriptDocParam
+hi def link typescriptDocParam Function
+hi def link typescriptDocNumParam Function
+hi def link typescriptDocEventRef Function
+hi def link typescriptDocNamedParamType Type
+hi def link typescriptDocParamName Type
+hi def link typescriptDocParamType Type
+hi def link typescriptString String
+hi def link typescriptSpecial Special
+hi def link typescriptStringLiteralType String
+hi def link typescriptTemplateLiteralType String
+hi def link typescriptStringMember String
+hi def link typescriptTemplate String
+hi def link typescriptEventString String
+hi def link typescriptDestructureString String
+hi def link typescriptASCII Special
+hi def link typescriptTemplateSB Label
+hi def link typescriptRegexpString String
+hi def link typescriptGlobal Constant
+hi def link typescriptTestGlobal Function
+hi def link typescriptPrototype Type
+hi def link typescriptConditional Conditional
+hi def link typescriptConditionalElse Conditional
+hi def link typescriptCase Conditional
+hi def link typescriptDefault typescriptCase
+hi def link typescriptBranch Conditional
+hi def link typescriptIdentifier Structure
+hi def link typescriptVariable Identifier
+hi def link typescriptDestructureVariable PreProc
+hi def link typescriptEnumKeyword Identifier
+hi def link typescriptRepeat Repeat
+hi def link typescriptForOperator Repeat
+hi def link typescriptStatementKeyword Statement
+hi def link typescriptMessage Keyword
+hi def link typescriptOperator Identifier
+hi def link typescriptKeywordOp Identifier
+hi def link typescriptCastKeyword Special
+hi def link typescriptType Type
+hi def link typescriptNull Boolean
+hi def link typescriptNumber Number
+hi def link typescriptBoolean Boolean
+hi def link typescriptObjectLabel typescriptLabel
+hi def link typescriptDestructureLabel Function
+hi def link typescriptLabel Label
+hi def link typescriptTupleLable Label
+hi def link typescriptStringProperty String
+hi def link typescriptImport Special
+hi def link typescriptImportType Special
+hi def link typescriptAmbientDeclaration Special
+hi def link typescriptExport Special
+hi def link typescriptExportType Special
+hi def link typescriptModule Special
+hi def link typescriptTry Special
+hi def link typescriptExceptions Special
+
+hi def link typescriptMember Function
+hi def link typescriptMethodAccessor Operator
+
+hi def link typescriptAsyncFuncKeyword Keyword
+hi def link typescriptObjectAsyncKeyword Keyword
+hi def link typescriptAsyncFor Keyword
+hi def link typescriptFuncKeyword Keyword
+hi def link typescriptAsyncFunc Keyword
+hi def link typescriptArrowFunc Type
+hi def link typescriptFuncName Function
+hi def link typescriptFuncArg PreProc
+hi def link typescriptArrowFuncArg PreProc
+hi def link typescriptFuncComma Operator
+
+hi def link typescriptClassKeyword Keyword
+hi def link typescriptClassExtends Keyword
+" hi def link typescriptClassName Function
+hi def link typescriptAbstract Special
+" hi def link typescriptClassHeritage Function
+" hi def link typescriptInterfaceHeritage Function
+hi def link typescriptClassStatic StorageClass
+hi def link typescriptReadonlyModifier Keyword
+hi def link typescriptInterfaceKeyword Keyword
+hi def link typescriptInterfaceExtends Keyword
+hi def link typescriptInterfaceName Function
+
+hi def link shellbang Comment
+
+hi def link typescriptTypeParameter Identifier
+hi def link typescriptConstraint Keyword
+hi def link typescriptPredefinedType Type
+hi def link typescriptReadonlyArrayKeyword Keyword
+hi def link typescriptUnion Operator
+hi def link typescriptFuncTypeArrow Function
+hi def link typescriptConstructorType Function
+hi def link typescriptTypeQuery Keyword
+hi def link typescriptAccessibilityModifier Keyword
+hi def link typescriptOptionalMark PreProc
+hi def link typescriptFuncType Special
+hi def link typescriptMappedIn Special
+hi def link typescriptCall PreProc
+hi def link typescriptParamImpl PreProc
+hi def link typescriptConstructSignature Identifier
+hi def link typescriptAliasDeclaration Identifier
+hi def link typescriptAliasKeyword Keyword
+hi def link typescriptUserDefinedType Keyword
+hi def link typescriptTypeReference Identifier
+hi def link typescriptConstructor Keyword
+hi def link typescriptDecorator Special
+hi def link typescriptAssertType Keyword
+
+hi link typeScript NONE
+
+if exists('s:cpo_save')
+ let &cpo = s:cpo_save
+ unlet s:cpo_save
+endif
diff --git a/runtime/syntax/swayconfig.vim b/runtime/syntax/swayconfig.vim
index 2abfa38bd9..d9f31da47b 100644
--- a/runtime/syntax/swayconfig.vim
+++ b/runtime/syntax/swayconfig.vim
@@ -2,8 +2,8 @@
" Language: sway window manager config
" Original Author: James Eapen <james.eapen@vai.org>
" Maintainer: James Eapen <james.eapen@vai.org>
-" Version: 0.11.0
-" Last Change: 2022 Jun 07
+" Version: 0.11.1
+" Last Change: 2022 Aug 08
" References:
" http://i3wm.org/docs/userguide.html#configuring
@@ -36,8 +36,8 @@ syn keyword swayConfigBindGestureCommand swipe pinch hold contained
syn keyword swayConfigBindGestureDirection up down left right next prev contained
syn keyword swayConfigBindGesturePinchDirection inward outward clockwise counterclockwise contained
syn match swayConfigBindGestureHold /^\s*\(bindgesture\)\s\+hold\(:[1-5]\)\?\s\+.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,i3ConfigWorkspaceKeyword,i3ConfigAction
-syn match swayConfigBindGestureSwipe /^\s*\(bindgesture\)\s\+swipe\(:[1-5]\)\?:\(up\|down\|left\|right\)\s\+.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,i3ConfigWorkspaceKeyword,i3ConfigAction
-syn match swayConfigBindGesturePinch /^\s*\(bindgesture\)\s\+\(pinch\):.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,swayConfigBindGesturePinchDirection,i3ConfigWorkspaceKeyword,i3ConfigAction
+syn match swayConfigBindGestureSwipe /^\s*\(bindgesture\)\s\+swipe\(:[3-5]\)\?:\(up\|down\|left\|right\)\s\+.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,i3ConfigWorkspaceKeyword,i3ConfigAction
+syn match swayConfigBindGesturePinch /^\s*\(bindgesture\)\s\+pinch\(:[2-5]\)\?:\(up\|down\|left\|right\|inward\|outward\|clockwise\|counterclockwise\)\(+\(up\|down\|left\|right\|inward\|outward\|clockwise\|counterclockwise\)\)\?.*$/ contains=swayConfigBindKeyword,swayConfigBindGestureCommand,swayConfigBindGestureDirection,swayConfigBindGesturePinchDirection,i3ConfigWorkspaceKeyword,i3ConfigAction
" floating
syn keyword swayConfigFloatingKeyword floating contained
diff --git a/runtime/syntax/typescript.vim b/runtime/syntax/typescript.vim
index 767ba56d42..af71938a8e 100644
--- a/runtime/syntax/typescript.vim
+++ b/runtime/syntax/typescript.vim
@@ -35,7 +35,7 @@ syntax region typescriptTypeCast matchgroup=typescriptTypeBrackets
"""""""""""""""""""""""""""""""""""""""""""""""""""
" Source the part common with typescriptreact.vim
-source <sfile>:h/typescriptcommon.vim
+source <sfile>:h/shared/typescriptcommon.vim
let b:current_syntax = "typescript"
diff --git a/runtime/syntax/typescriptreact.vim b/runtime/syntax/typescriptreact.vim
index f29fe785b9..c4c2d45745 100644
--- a/runtime/syntax/typescriptreact.vim
+++ b/runtime/syntax/typescriptreact.vim
@@ -133,7 +133,7 @@ syntax region tsxEscJs
"""""""""""""""""""""""""""""""""""""""""""""""""""
" Source the part common with typescriptreact.vim
-source <sfile>:h/typescriptcommon.vim
+source <sfile>:h/shared/typescriptcommon.vim
syntax cluster typescriptExpression add=tsxRegion,tsxFragment
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index b743e9923f..635833748d 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -676,12 +676,17 @@ function(get_test_target prefix sfile relative_path_var target_var)
endif()
endfunction()
-set(NO_SINGLE_CHECK_HEADERS
- os/win_defs.h
- os/pty_process_win.h
- os/pty_conpty_win.h
- os/os_win_console.h
-)
+if(WIN32)
+ set(NO_SINGLE_CHECK_HEADERS
+ os/pty_process_unix.h
+ os/unix_defs.h)
+else()
+ set(NO_SINGLE_CHECK_HEADERS
+ os/win_defs.h
+ os/pty_process_win.h
+ os/pty_conpty_win.h
+ os/os_win_console.h)
+endif()
foreach(hfile ${NVIM_HEADERS})
get_test_target(test-includes "${hfile}" relative_path texe)
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index d3895d31cf..5e90e40dd3 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -18,6 +18,7 @@
#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
+#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index e77add6210..1323fc347b 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -626,6 +626,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
garray_T capture_local;
const int save_msg_silent = msg_silent;
garray_T * const save_capture_ga = capture_ga;
+ const int save_msg_col = msg_col;
if (output) {
ga_init(&capture_local, 1, 80);
@@ -636,6 +637,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
try_start();
if (output) {
msg_silent++;
+ msg_col = 0; // prevent leading spaces
}
WITH_SCRIPT_CONTEXT(channel_id, {
@@ -645,6 +647,8 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
if (output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
+ // Put msg_col back where it was, since nothing should have been written.
+ msg_col = save_msg_col;
}
try_end(err);
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index c36f127507..933aa85530 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -10,11 +10,11 @@
#include "nvim/api/private/helpers.h"
#include "nvim/charset.h"
#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
-#include "nvim/screen.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/extmark.c.generated.h"
@@ -1031,6 +1031,8 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
}
p->active = true;
+ p->hl_valid++;
+ p->hl_cached = false;
return;
error:
decor_provider_clear(p);
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 4f4ac40ce9..6fad52ba75 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -104,7 +104,6 @@ return {
"reverse";
"nocombine";
"default";
- "global";
"cterm";
"foreground"; "fg";
"background"; "bg";
@@ -112,9 +111,9 @@ return {
"ctermbg";
"special"; "sp";
"link";
+ "global_link";
"fallback";
"blend";
- "temp";
};
highlight_cterm = {
"bold";
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 9cd4c97508..6f7bfa244a 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -11,14 +11,14 @@
#include "nvim/api/ui.h"
#include "nvim/channel.h"
#include "nvim/cursor_shape.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/option.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 2622f4bb81..e4dc219e9a 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -23,6 +23,7 @@
#include "nvim/context.h"
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -34,6 +35,7 @@
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -52,9 +54,8 @@
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/os/process.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/types.h"
#include "nvim/ui.h"
@@ -92,7 +93,6 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
}
/// Gets a highlight definition by id. |hlID()|
-///
/// @param hl_id Highlight id as returned by |hlID()|
/// @param rgb Export RGB colors
/// @param[out] err Error details, if any
@@ -181,35 +181,38 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
}
}
-/// Set active namespace for highlights.
-///
-/// NB: this function can be called from async contexts, but the
-/// semantics are not yet well-defined. To start with
-/// |nvim_set_decoration_provider| on_win and on_line callbacks
-/// are explicitly allowed to change the namespace during a redraw cycle.
+/// Set active namespace for highlights. This can be set for a single window,
+/// see |nvim_win_set_hl_ns|.
///
-/// @param ns_id the namespace to activate
+/// @param ns_id the namespace to use
/// @param[out] err Error details, if any
-void nvim__set_hl_ns(Integer ns_id, Error *err)
- FUNC_API_FAST
+void nvim_set_hl_ns(Integer ns_id, Error *err)
+ FUNC_API_SINCE(10)
{
- if (ns_id >= 0) {
- ns_hl_active = (NS)ns_id;
+ if (ns_id < 0) {
+ api_set_error(err, kErrorTypeValidation, "no such namespace");
+ return;
}
- // TODO(bfredl): this is a little bit hackish. Eventually we want a standard
- // event path for redraws caused by "fast" events. This could tie in with
- // better throttling of async events causing redraws, such as non-batched
- // nvim_buf_set_extmark calls from async contexts.
- if (!provider_active && !ns_hl_changed && must_redraw < NOT_VALID) {
- multiqueue_put(main_loop.events, on_redraw_event, 0);
- }
- ns_hl_changed = true;
+ ns_hl_global = (NS)ns_id;
+ hl_check_ns();
+ redraw_all_later(NOT_VALID);
}
-static void on_redraw_event(void **argv)
+/// Set active namespace for highlights while redrawing.
+///
+/// This function meant to be called while redrawing, primarily from
+/// |nvim_set_decoration_provider| on_win and on_line callbacks, which
+/// are allowed to change the namespace during a redraw cycle.
+///
+/// @param ns_id the namespace to activate
+/// @param[out] err Error details, if any
+void nvim_set_hl_ns_fast(Integer ns_id, Error *err)
+ FUNC_API_SINCE(10)
+ FUNC_API_FAST
{
- redraw_all_later(NOT_VALID);
+ ns_hl_fast = (NS)ns_id;
+ hl_check_ns();
}
/// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
@@ -479,7 +482,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
ADD_C(args, INTEGER_OBJ(log_level));
ADD_C(args, DICTIONARY_OBJ(opts));
- return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err);
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, err);
}
/// Calculates the number of display cells occupied by `text`.
@@ -1834,11 +1837,9 @@ Array nvim_get_proc_children(Integer pid, Error *err)
if (rv == 2) {
// syscall failed (possibly because of kernel options), try shelling out.
DLOG("fallback to vim._os_proc_children()");
- Array a = ARRAY_DICT_INIT;
+ MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- String s = STATIC_CSTR_AS_STRING("return vim._os_proc_children(...)");
- Object o = nlua_exec(s, a, err);
- api_free_array(a);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, err);
if (o.type == kObjectTypeArray) {
rvobj = o.data.array;
} else if (!ERROR_SET(err)) {
@@ -1879,12 +1880,9 @@ Object nvim_get_proc(Integer pid, Error *err)
}
#else
// Cross-platform process info APIs are miserable, so use `ps` instead.
- Array a = ARRAY_DICT_INIT;
+ MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- String s = cstr_to_string("return vim._os_proc_info(select(1, ...))");
- Object o = nlua_exec(s, a, err);
- api_free_string(s);
- api_free_array(a);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, err);
if (o.type == kObjectTypeArray && o.data.array.size == 0) {
return NIL; // Process not found.
} else if (o.type == kObjectTypeDictionary) {
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 3b25e3aa38..a28bfd2ab9 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -49,6 +49,7 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
{
const int save_msg_silent = msg_silent;
garray_T *const save_capture_ga = capture_ga;
+ const int save_msg_col = msg_col;
garray_T capture_local;
if (output) {
ga_init(&capture_local, 1, 80);
@@ -58,6 +59,7 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
try_start();
if (output) {
msg_silent++;
+ msg_col = 0; // prevent leading spaces
}
const sctx_T save_current_sctx = api_set_sctx(channel_id);
@@ -66,6 +68,8 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
if (output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
+ // Put msg_col back where it was, since nothing should have been written.
+ msg_col = save_msg_col;
}
current_sctx = save_current_sctx;
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index a153f746b6..6c37df6af8 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -10,9 +10,9 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii.h"
+#include "nvim/drawscreen.h"
#include "nvim/highlight_group.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 5a4ff70257..580dfd8639 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -12,12 +12,12 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/ex_docmd.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -426,3 +426,28 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
try_end(err);
return res;
}
+
+/// Set highlight namespace for a window. This will use highlights defined in
+/// this namespace, but fall back to global highlights (ns=0) when missing.
+///
+/// This takes predecence over the 'winhighlight' option.
+///
+/// @param ns_id the namespace to use
+/// @param[out] err Error details, if any
+void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)
+ FUNC_API_SINCE(10)
+{
+ win_T *win = find_window_by_handle(window, err);
+ if (!win) {
+ return;
+ }
+
+ // -1 is allowed as inherit global namespace
+ if (ns_id < -1) {
+ api_set_error(err, kErrorTypeValidation, "no such namespace");
+ }
+
+ win->w_ns_hl = (NS)ns_id;
+ win->w_hl_needs_update = true;
+ redraw_later(win, NOT_VALID);
+}
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
new file mode 100644
index 0000000000..7d8917cc73
--- /dev/null
+++ b/src/nvim/arglist.c
@@ -0,0 +1,1157 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// arglist.c: functions for dealing with the argument list
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include "nvim/arglist.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/eval.h"
+#include "nvim/ex_cmds2.h"
+#include "nvim/ex_getln.h"
+#include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/mark.h"
+#include "nvim/memory.h"
+#include "nvim/os/input.h"
+#include "nvim/path.h"
+#include "nvim/regexp.h"
+#include "nvim/strings.h"
+#include "nvim/undo.h"
+#include "nvim/version.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "arglist.c.generated.h"
+#endif
+
+enum {
+ AL_SET = 1,
+ AL_ADD = 2,
+ AL_DEL = 3,
+};
+
+/// Clear an argument list: free all file names and reset it to zero entries.
+void alist_clear(alist_T *al)
+{
+#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
+ GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
+}
+
+/// Init an argument list.
+void alist_init(alist_T *al)
+{
+ ga_init(&al->al_ga, (int)sizeof(aentry_T), 5);
+}
+
+/// Remove a reference from an argument list.
+/// Ignored when the argument list is the global one.
+/// If the argument list is no longer used by any window, free it.
+void alist_unlink(alist_T *al)
+{
+ if (al != &global_alist && --al->al_refcount <= 0) {
+ alist_clear(al);
+ xfree(al);
+ }
+}
+
+/// Create a new argument list and use it for the current window.
+void alist_new(void)
+{
+ curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
+ curwin->w_alist->al_refcount = 1;
+ curwin->w_alist->id = ++max_alist_id;
+ alist_init(curwin->w_alist);
+}
+
+#if !defined(UNIX)
+
+/// Expand the file names in the global argument list.
+/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
+/// numbers to be re-used.
+void alist_expand(int *fnum_list, int fnum_len)
+{
+ char *save_p_su = p_su;
+
+ // Don't use 'suffixes' here. This should work like the shell did the
+ // expansion. Also, the vimrc file isn't read yet, thus the user
+ // can't set the options.
+ p_su = empty_option;
+ char **old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT);
+ for (int i = 0; i < GARGCOUNT; i++) {
+ old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname);
+ }
+ int old_arg_count = GARGCOUNT;
+ char **new_arg_files;
+ int new_arg_file_count;
+ if (expand_wildcards(old_arg_count, old_arg_files,
+ &new_arg_file_count, &new_arg_files,
+ EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK
+ && new_arg_file_count > 0) {
+ alist_set(&global_alist, new_arg_file_count, new_arg_files,
+ true, fnum_list, fnum_len);
+ FreeWild(old_arg_count, old_arg_files);
+ }
+ p_su = save_p_su;
+}
+#endif
+
+/// Set the argument list for the current window.
+/// Takes over the allocated files[] and the allocated fnames in it.
+void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len)
+{
+ static int recursive = 0;
+
+ if (recursive) {
+ emsg(_(e_au_recursive));
+ return;
+ }
+ recursive++;
+
+ alist_clear(al);
+ ga_grow(&al->al_ga, count);
+ {
+ for (int i = 0; i < count; i++) {
+ if (got_int) {
+ // When adding many buffers this can take a long time. Allow
+ // interrupting here.
+ while (i < count) {
+ xfree(files[i++]);
+ }
+ break;
+ }
+
+ // May set buffer name of a buffer previously used for the
+ // argument list, so that it's re-used by alist_add.
+ if (fnum_list != NULL && i < fnum_len) {
+ buf_set_name(fnum_list[i], files[i]);
+ }
+
+ alist_add(al, files[i], use_curbuf ? 2 : 1);
+ os_breakcheck();
+ }
+ xfree(files);
+ }
+
+ if (al == &global_alist) {
+ arg_had_last = false;
+ }
+ recursive--;
+}
+
+/// Add file "fname" to argument list "al".
+/// "fname" must have been allocated and "al" must have been checked for room.
+///
+/// @param set_fnum 1: set buffer number; 2: re-use curbuf
+void alist_add(alist_T *al, char *fname, int set_fnum)
+{
+ if (fname == NULL) { // don't add NULL file names
+ return;
+ }
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(fname);
+#endif
+ AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname;
+ if (set_fnum > 0) {
+ AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
+ buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
+ }
+ al->al_ga.ga_len++;
+}
+
+#if defined(BACKSLASH_IN_FILENAME)
+
+/// Adjust slashes in file names. Called after 'shellslash' was set.
+void alist_slash_adjust(void)
+{
+ for (int i = 0; i < GARGCOUNT; i++) {
+ if (GARGLIST[i].ae_fname != NULL) {
+ slash_adjust(GARGLIST[i].ae_fname);
+ }
+ }
+
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_alist != &global_alist) {
+ for (int i = 0; i < WARGCOUNT(wp); i++) {
+ if (WARGLIST(wp)[i].ae_fname != NULL) {
+ slash_adjust(WARGLIST(wp)[i].ae_fname);
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+/// Isolate one argument, taking backticks.
+/// Changes the argument in-place, puts a NUL after it. Backticks remain.
+///
+/// @return a pointer to the start of the next argument.
+static char *do_one_arg(char *str)
+{
+ char *p;
+ bool inbacktick;
+
+ inbacktick = false;
+ for (p = str; *str; str++) {
+ // When the backslash is used for escaping the special meaning of a
+ // character we need to keep it until wildcard expansion.
+ if (rem_backslash((char_u *)str)) {
+ *p++ = *str++;
+ *p++ = *str;
+ } else {
+ // An item ends at a space not in backticks
+ if (!inbacktick && ascii_isspace(*str)) {
+ break;
+ }
+ if (*str == '`') {
+ inbacktick ^= true;
+ }
+ *p++ = *str;
+ }
+ }
+ str = skipwhite(str);
+ *p = NUL;
+
+ return str;
+}
+
+/// Separate the arguments in "str" and return a list of pointers in the
+/// growarray "gap".
+static void get_arglist(garray_T *gap, char *str, int escaped)
+{
+ ga_init(gap, (int)sizeof(char_u *), 20);
+ while (*str != NUL) {
+ GA_APPEND(char *, gap, str);
+
+ // If str is escaped, don't handle backslashes or spaces
+ if (!escaped) {
+ return;
+ }
+
+ // Isolate one argument, change it in-place, put a NUL after it.
+ str = do_one_arg(str);
+ }
+}
+
+/// Parse a list of arguments (file names), expand them and return in
+/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
+///
+/// @return FAIL or OK.
+int get_arglist_exp(char_u *str, int *fcountp, char ***fnamesp, bool wig)
+{
+ garray_T ga;
+ int i;
+
+ get_arglist(&ga, (char *)str, true);
+
+ if (wig) {
+ i = expand_wildcards(ga.ga_len, ga.ga_data,
+ fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
+ } else {
+ i = gen_expand_wildcards(ga.ga_len, ga.ga_data,
+ fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
+ }
+
+ ga_clear(&ga);
+ return i;
+}
+
+/// Check the validity of the arg_idx for each other window.
+static void alist_check_arg_idx(void)
+{
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_alist == curwin->w_alist) {
+ check_arg_idx(win);
+ }
+ }
+}
+
+/// Add files[count] to the arglist of the current window after arg "after".
+/// The file names in files[count] must have been allocated and are taken over.
+/// Files[] itself is not taken over.
+///
+/// @param after: where to add: 0 = before first one
+/// @param will_edit will edit adding argument
+static void alist_add_list(int count, char **files, int after, bool will_edit)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int old_argcount = ARGCOUNT;
+ ga_grow(&ALIST(curwin)->al_ga, count);
+ {
+ if (after < 0) {
+ after = 0;
+ }
+ if (after > ARGCOUNT) {
+ after = ARGCOUNT;
+ }
+ if (after < ARGCOUNT) {
+ memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
+ (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
+ }
+ for (int i = 0; i < count; i++) {
+ const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
+ ARGLIST[after + i].ae_fname = (char_u *)files[i];
+ ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
+ }
+ ALIST(curwin)->al_ga.ga_len += count;
+ if (old_argcount > 0 && curwin->w_arg_idx >= after) {
+ curwin->w_arg_idx += count;
+ }
+ return;
+ }
+}
+
+/// @param str
+/// @param what
+/// AL_SET: Redefine the argument list to 'str'.
+/// AL_ADD: add files in 'str' to the argument list after "after".
+/// AL_DEL: remove files in 'str' from the argument list.
+/// @param after
+/// 0 means before first one
+/// @param will_edit will edit added argument
+///
+/// @return FAIL for failure, OK otherwise.
+static int do_arglist(char *str, int what, int after, bool will_edit)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T new_ga;
+ int exp_count;
+ char **exp_files;
+ char *p;
+ int match;
+ int arg_escaped = true;
+
+ // Set default argument for ":argadd" command.
+ if (what == AL_ADD && *str == NUL) {
+ if (curbuf->b_ffname == NULL) {
+ return FAIL;
+ }
+ str = curbuf->b_fname;
+ arg_escaped = false;
+ }
+
+ // Collect all file name arguments in "new_ga".
+ get_arglist(&new_ga, str, arg_escaped);
+
+ if (what == AL_DEL) {
+ regmatch_T regmatch;
+ bool didone;
+
+ // Delete the items: use each item as a regexp and find a match in the
+ // argument list.
+ regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
+ for (int i = 0; i < new_ga.ga_len && !got_int; i++) {
+ p = ((char **)new_ga.ga_data)[i];
+ p = file_pat_to_reg_pat(p, NULL, NULL, false);
+ if (p == NULL) {
+ break;
+ }
+ regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
+ if (regmatch.regprog == NULL) {
+ xfree(p);
+ break;
+ }
+
+ didone = false;
+ for (match = 0; match < ARGCOUNT; match++) {
+ if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
+ didone = true;
+ xfree(ARGLIST[match].ae_fname);
+ memmove(ARGLIST + match, ARGLIST + match + 1,
+ (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len--;
+ if (curwin->w_arg_idx > match) {
+ curwin->w_arg_idx--;
+ }
+ match--;
+ }
+ }
+
+ vim_regfree(regmatch.regprog);
+ xfree(p);
+ if (!didone) {
+ semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
+ }
+ }
+ ga_clear(&new_ga);
+ } else {
+ int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data,
+ &exp_count, &exp_files,
+ EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
+ ga_clear(&new_ga);
+ if (i == FAIL || exp_count == 0) {
+ emsg(_(e_nomatch));
+ return FAIL;
+ }
+
+ if (what == AL_ADD) {
+ alist_add_list(exp_count, exp_files, after, will_edit);
+ xfree(exp_files);
+ } else {
+ assert(what == AL_SET);
+ alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
+ }
+ }
+
+ alist_check_arg_idx();
+
+ return OK;
+}
+
+/// Redefine the argument list.
+void set_arglist(char *str)
+{
+ do_arglist(str, AL_SET, 0, false);
+}
+
+/// @return true if window "win" is editing the file at the current argument
+/// index.
+bool editing_arg_idx(win_T *win)
+{
+ return !(win->w_arg_idx >= WARGCOUNT(win)
+ || (win->w_buffer->b_fnum
+ != WARGLIST(win)[win->w_arg_idx].ae_fnum
+ && (win->w_buffer->b_ffname == NULL
+ || !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]),
+ win->w_buffer->b_ffname, true,
+ true) & kEqualFiles))));
+}
+
+/// Check if window "win" is editing the w_arg_idx file in its argument list.
+void check_arg_idx(win_T *win)
+{
+ if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) {
+ // We are not editing the current entry in the argument list.
+ // Set "arg_had_last" if we are editing the last one.
+ win->w_arg_idx_invalid = true;
+ if (win->w_arg_idx != WARGCOUNT(win) - 1
+ && arg_had_last == false
+ && ALIST(win) == &global_alist
+ && GARGCOUNT > 0
+ && win->w_arg_idx < GARGCOUNT
+ && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
+ || (win->w_buffer->b_ffname != NULL
+ && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]),
+ win->w_buffer->b_ffname, true, true)
+ & kEqualFiles)))) {
+ arg_had_last = true;
+ }
+ } else {
+ // We are editing the current entry in the argument list.
+ // Set "arg_had_last" if it's also the last one
+ win->w_arg_idx_invalid = false;
+ if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {
+ arg_had_last = true;
+ }
+ }
+}
+
+/// ":args", ":argslocal" and ":argsglobal".
+void ex_args(exarg_T *eap)
+{
+ if (eap->cmdidx != CMD_args) {
+ alist_unlink(ALIST(curwin));
+ if (eap->cmdidx == CMD_argglobal) {
+ ALIST(curwin) = &global_alist;
+ } else { // eap->cmdidx == CMD_arglocal
+ alist_new();
+ }
+ }
+
+ if (*eap->arg != NUL) {
+ // ":args file ..": define new argument list, handle like ":next"
+ // Also for ":argslocal file .." and ":argsglobal file ..".
+ ex_next(eap);
+ } else if (eap->cmdidx == CMD_args) {
+ // ":args": list arguments.
+ if (ARGCOUNT > 0) {
+ char **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT);
+ // Overwrite the command, for a short list there is no scrolling
+ // required and no wait_return().
+ gotocmdline(true);
+ for (int i = 0; i < ARGCOUNT; i++) {
+ items[i] = alist_name(&ARGLIST[i]);
+ }
+ list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
+ xfree(items);
+ }
+ } else if (eap->cmdidx == CMD_arglocal) {
+ garray_T *gap = &curwin->w_alist->al_ga;
+
+ // ":argslocal": make a local copy of the global argument list.
+ ga_grow(gap, GARGCOUNT);
+ for (int i = 0; i < GARGCOUNT; i++) {
+ if (GARGLIST[i].ae_fname != NULL) {
+ AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
+ vim_strsave(GARGLIST[i].ae_fname);
+ AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
+ GARGLIST[i].ae_fnum;
+ gap->ga_len++;
+ }
+ }
+ }
+}
+
+/// ":previous", ":sprevious", ":Next" and ":sNext".
+void ex_previous(exarg_T *eap)
+{
+ // If past the last one already, go to the last one.
+ if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) {
+ do_argfile(eap, ARGCOUNT - 1);
+ } else {
+ do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
+ }
+}
+
+/// ":rewind", ":first", ":sfirst" and ":srewind".
+void ex_rewind(exarg_T *eap)
+{
+ do_argfile(eap, 0);
+}
+
+/// ":last" and ":slast".
+void ex_last(exarg_T *eap)
+{
+ do_argfile(eap, ARGCOUNT - 1);
+}
+
+/// ":argument" and ":sargument".
+void ex_argument(exarg_T *eap)
+{
+ int i;
+
+ if (eap->addr_count > 0) {
+ i = (int)eap->line2 - 1;
+ } else {
+ i = curwin->w_arg_idx;
+ }
+ do_argfile(eap, i);
+}
+
+/// Edit file "argn" of the argument lists.
+void do_argfile(exarg_T *eap, int argn)
+{
+ int other;
+ char *p;
+ int old_arg_idx = curwin->w_arg_idx;
+
+ if (argn < 0 || argn >= ARGCOUNT) {
+ if (ARGCOUNT <= 1) {
+ emsg(_("E163: There is only one file to edit"));
+ } else if (argn < 0) {
+ emsg(_("E164: Cannot go before first file"));
+ } else {
+ emsg(_("E165: Cannot go beyond last file"));
+ }
+ } else {
+ setpcmark();
+
+ // split window or create new tab page first
+ if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
+ if (win_split(0, 0) == FAIL) {
+ return;
+ }
+ RESET_BINDING(curwin);
+ } else {
+ // if 'hidden' set, only check for changed file when re-editing
+ // the same buffer
+ other = true;
+ if (buf_hide(curbuf)) {
+ p = fix_fname(alist_name(&ARGLIST[argn]));
+ other = otherfile(p);
+ xfree(p);
+ }
+ if ((!buf_hide(curbuf) || !other)
+ && check_changed(curbuf, CCGD_AW
+ | (other ? 0 : CCGD_MULTWIN)
+ | (eap->forceit ? CCGD_FORCEIT : 0)
+ | CCGD_EXCMD)) {
+ return;
+ }
+ }
+
+ curwin->w_arg_idx = argn;
+ if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
+ arg_had_last = true;
+ }
+
+ // Edit the file; always use the last known line number.
+ // When it fails (e.g. Abort for already edited file) restore the
+ // argument index.
+ if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
+ eap, ECMD_LAST,
+ (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
+ + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
+ curwin->w_arg_idx = old_arg_idx;
+ } else if (eap->cmdidx != CMD_argdo) {
+ // like Vi: set the mark where the cursor is in the file.
+ setmark('\'');
+ }
+ }
+}
+
+/// ":next", and commands that behave like it.
+void ex_next(exarg_T *eap)
+{
+ int i;
+
+ // check for changed buffer now, if this fails the argument list is not
+ // redefined.
+ if (buf_hide(curbuf)
+ || eap->cmdidx == CMD_snext
+ || !check_changed(curbuf, CCGD_AW
+ | (eap->forceit ? CCGD_FORCEIT : 0)
+ | CCGD_EXCMD)) {
+ if (*eap->arg != NUL) { // redefine file list
+ if (do_arglist(eap->arg, AL_SET, 0, true) == FAIL) {
+ return;
+ }
+ i = 0;
+ } else {
+ i = curwin->w_arg_idx + (int)eap->line2;
+ }
+ do_argfile(eap, i);
+ }
+}
+
+/// ":argdedupe"
+void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
+{
+ for (int i = 0; i < ARGCOUNT; i++) {
+ for (int j = i + 1; j < ARGCOUNT; j++) {
+ if (FNAMECMP(ARGLIST[i].ae_fname, ARGLIST[j].ae_fname) == 0) {
+ xfree(ARGLIST[j].ae_fname);
+ memmove(ARGLIST + j, ARGLIST + j + 1,
+ (size_t)(ARGCOUNT - j - 1) * sizeof(aentry_T));
+ ARGCOUNT--;
+
+ if (curwin->w_arg_idx == j) {
+ curwin->w_arg_idx = i;
+ } else if (curwin->w_arg_idx > j) {
+ curwin->w_arg_idx--;
+ }
+
+ j--;
+ }
+ }
+ }
+}
+
+/// ":argedit"
+void ex_argedit(exarg_T *eap)
+{
+ int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
+ // Whether curbuf will be reused, curbuf->b_ffname will be set.
+ bool curbuf_is_reusable = curbuf_reusable();
+
+ if (do_arglist(eap->arg, AL_ADD, i, true) == FAIL) {
+ return;
+ }
+ maketitle();
+
+ if (curwin->w_arg_idx == 0
+ && (curbuf->b_ml.ml_flags & ML_EMPTY)
+ && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
+ i = 0;
+ }
+ // Edit the argument.
+ if (i < ARGCOUNT) {
+ do_argfile(eap, i);
+ }
+}
+
+/// ":argadd"
+void ex_argadd(exarg_T *eap)
+{
+ do_arglist(eap->arg, AL_ADD,
+ eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
+ false);
+ maketitle();
+}
+
+/// ":argdelete"
+void ex_argdelete(exarg_T *eap)
+{
+ if (eap->addr_count > 0 || *eap->arg == NUL) {
+ // ":argdel" works like ":.argdel"
+ if (eap->addr_count == 0) {
+ if (curwin->w_arg_idx >= ARGCOUNT) {
+ emsg(_("E610: No argument to delete"));
+ return;
+ }
+ eap->line1 = eap->line2 = curwin->w_arg_idx + 1;
+ } else if (eap->line2 > ARGCOUNT) {
+ // ":1,4argdel": Delete all arguments in the range.
+ eap->line2 = ARGCOUNT;
+ }
+ linenr_T n = eap->line2 - eap->line1 + 1;
+ if (*eap->arg != NUL) {
+ // Can't have both a range and an argument.
+ emsg(_(e_invarg));
+ } else if (n <= 0) {
+ // Don't give an error for ":%argdel" if the list is empty.
+ if (eap->line1 != 1 || eap->line2 != 0) {
+ emsg(_(e_invrange));
+ }
+ } else {
+ for (linenr_T i = eap->line1; i <= eap->line2; i++) {
+ xfree(ARGLIST[i - 1].ae_fname);
+ }
+ memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
+ (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len -= (int)n;
+ if (curwin->w_arg_idx >= eap->line2) {
+ curwin->w_arg_idx -= (int)n;
+ } else if (curwin->w_arg_idx > eap->line1) {
+ curwin->w_arg_idx = (int)eap->line1;
+ }
+ if (ARGCOUNT == 0) {
+ curwin->w_arg_idx = 0;
+ } else if (curwin->w_arg_idx >= ARGCOUNT) {
+ curwin->w_arg_idx = ARGCOUNT - 1;
+ }
+ }
+ } else {
+ do_arglist(eap->arg, AL_DEL, 0, false);
+ }
+ maketitle();
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// argedit and argdelete commands.
+char *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= ARGCOUNT) {
+ return NULL;
+ }
+ return alist_name(&ARGLIST[idx]);
+}
+
+/// Get the file name for an argument list entry.
+char *alist_name(aentry_T *aep)
+{
+ buf_T *bp;
+
+ // Use the name from the associated buffer if it exists.
+ bp = buflist_findnr(aep->ae_fnum);
+ if (bp == NULL || bp->b_fname == NULL) {
+ return (char *)aep->ae_fname;
+ }
+ return bp->b_fname;
+}
+
+/// do_arg_all(): Open up to 'count' windows, one for each argument.
+///
+/// @param forceit hide buffers in current windows
+/// @param keep_tabs keep current tabs, for ":tab drop file"
+static void do_arg_all(int count, int forceit, int keep_tabs)
+{
+ uint8_t *opened; // Array of weight for which args are open:
+ // 0: not opened
+ // 1: opened in other tab
+ // 2: opened in curtab
+ // 3: opened in curtab and curwin
+
+ int opened_len; // length of opened[]
+ int use_firstwin = false; // use first window for arglist
+ bool tab_drop_empty_window = false;
+ int split_ret = OK;
+ bool p_ea_save;
+ alist_T *alist; // argument list to be used
+ buf_T *buf;
+ tabpage_T *tpnext;
+ int had_tab = cmdmod.cmod_tab;
+ win_T *old_curwin, *last_curwin;
+ tabpage_T *old_curtab, *last_curtab;
+ win_T *new_curwin = NULL;
+ tabpage_T *new_curtab = NULL;
+
+ assert(firstwin != NULL); // satisfy coverity
+
+ if (ARGCOUNT <= 0) {
+ // Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
+ return;
+ }
+ setpcmark();
+
+ opened_len = ARGCOUNT;
+ opened = xcalloc((size_t)opened_len, 1);
+
+ // Autocommands may do anything to the argument list. Make sure it's not
+ // freed while we are working here by "locking" it. We still have to
+ // watch out for its size to be changed.
+ alist = curwin->w_alist;
+ alist->al_refcount++;
+
+ old_curwin = curwin;
+ old_curtab = curtab;
+
+ // Try closing all windows that are not in the argument list.
+ // Also close windows that are not full width;
+ // When 'hidden' or "forceit" set the buffer becomes hidden.
+ // Windows that have a changed buffer and can't be hidden won't be closed.
+ // When the ":tab" modifier was used do this for all tab pages.
+ if (had_tab > 0) {
+ goto_tabpage_tp(first_tabpage, true, true);
+ }
+ for (;;) {
+ win_T *wpnext = NULL;
+ tpnext = curtab->tp_next;
+ for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
+ int i;
+ wpnext = wp->w_next;
+ buf = wp->w_buffer;
+ if (buf->b_ffname == NULL
+ || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
+ i = opened_len;
+ } else {
+ // check if the buffer in this window is in the arglist
+ for (i = 0; i < opened_len; i++) {
+ if (i < alist->al_ga.ga_len
+ && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
+ || path_full_compare(alist_name(&AARGLIST(alist)[i]),
+ buf->b_ffname,
+ true, true) & kEqualFiles)) {
+ int weight = 1;
+
+ if (old_curtab == curtab) {
+ weight++;
+ if (old_curwin == wp) {
+ weight++;
+ }
+ }
+
+ if (weight > (int)opened[i]) {
+ opened[i] = (uint8_t)weight;
+ if (i == 0) {
+ if (new_curwin != NULL) {
+ new_curwin->w_arg_idx = opened_len;
+ }
+ new_curwin = wp;
+ new_curtab = curtab;
+ }
+ } else if (keep_tabs) {
+ i = opened_len;
+ }
+
+ if (wp->w_alist != alist) {
+ // Use the current argument list for all windows containing a file from it.
+ alist_unlink(wp->w_alist);
+ wp->w_alist = alist;
+ wp->w_alist->al_refcount++;
+ }
+ break;
+ }
+ }
+ }
+ wp->w_arg_idx = i;
+
+ if (i == opened_len && !keep_tabs) { // close this window
+ if (buf_hide(buf) || forceit || buf->b_nwindows > 1
+ || !bufIsChanged(buf)) {
+ // If the buffer was changed, and we would like to hide it, try autowriting.
+ if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
+ bufref_T bufref;
+ set_bufref(&bufref, buf);
+ (void)autowrite(buf, false);
+ // Check if autocommands removed the window.
+ if (!win_valid(wp) || !bufref_valid(&bufref)) {
+ wpnext = firstwin; // Start all over...
+ continue;
+ }
+ }
+ // don't close last window
+ if (ONE_WINDOW
+ && (first_tabpage->tp_next == NULL || !had_tab)) {
+ use_firstwin = true;
+ } else {
+ win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
+ // check if autocommands removed the next window
+ if (!win_valid(wpnext)) {
+ // start all over...
+ wpnext = firstwin;
+ }
+ }
+ }
+ }
+ }
+
+ // Without the ":tab" modifier only do the current tab page.
+ if (had_tab == 0 || tpnext == NULL) {
+ break;
+ }
+
+ // check if autocommands removed the next tab page
+ if (!valid_tabpage(tpnext)) {
+ tpnext = first_tabpage; // start all over...
+ }
+ goto_tabpage_tp(tpnext, true, true);
+ }
+
+ // Open a window for files in the argument list that don't have one.
+ // ARGCOUNT may change while doing this, because of autocommands.
+ if (count > opened_len || count <= 0) {
+ count = opened_len;
+ }
+
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
+ autocmd_no_leave++;
+ last_curwin = curwin;
+ last_curtab = curtab;
+ win_enter(lastwin, false);
+ // ":tab drop file" should re-use an empty window to avoid "--remote-tab"
+ // leaving an empty tab page when executed locally.
+ if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
+ && curbuf->b_ffname == NULL && !curbuf->b_changed) {
+ use_firstwin = true;
+ tab_drop_empty_window = true;
+ }
+
+ for (int i = 0; i < count && !got_int; i++) {
+ if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
+ arg_had_last = true;
+ }
+ if (opened[i] > 0) {
+ // Move the already present window to below the current window
+ if (curwin->w_arg_idx != i) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_arg_idx == i) {
+ if (keep_tabs) {
+ new_curwin = wp;
+ new_curtab = curtab;
+ } else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
+ emsg(_("E249: window layout changed unexpectedly"));
+ i = count;
+ break;
+ } else {
+ win_move_after(wp, curwin);
+ }
+ break;
+ }
+ }
+ }
+ } else if (split_ret == OK) {
+ // trigger events for tab drop
+ if (tab_drop_empty_window && i == count - 1) {
+ autocmd_no_enter--;
+ }
+ if (!use_firstwin) { // split current window
+ p_ea_save = p_ea;
+ p_ea = true; // use space from all windows
+ split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
+ p_ea = p_ea_save;
+ if (split_ret == FAIL) {
+ continue;
+ }
+ } else { // first window: do autocmd for leaving this buffer
+ autocmd_no_leave--;
+ }
+
+ // edit file "i"
+ curwin->w_arg_idx = i;
+ if (i == 0) {
+ new_curwin = curwin;
+ new_curtab = curtab;
+ }
+ (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
+ ((buf_hide(curwin->w_buffer)
+ || bufIsChanged(curwin->w_buffer))
+ ? ECMD_HIDE : 0) + ECMD_OLDBUF,
+ curwin);
+ if (tab_drop_empty_window && i == count - 1) {
+ autocmd_no_enter++;
+ }
+ if (use_firstwin) {
+ autocmd_no_leave++;
+ }
+ use_firstwin = false;
+ }
+ os_breakcheck();
+
+ // When ":tab" was used open a new tab for a new window repeatedly.
+ if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
+ cmdmod.cmod_tab = 9999;
+ }
+ }
+
+ // Remove the "lock" on the argument list.
+ alist_unlink(alist);
+
+ autocmd_no_enter--;
+ // restore last referenced tabpage's curwin
+ if (last_curtab != new_curtab) {
+ if (valid_tabpage(last_curtab)) {
+ goto_tabpage_tp(last_curtab, true, true);
+ }
+ if (win_valid(last_curwin)) {
+ win_enter(last_curwin, false);
+ }
+ }
+ // to window with first arg
+ if (valid_tabpage(new_curtab)) {
+ goto_tabpage_tp(new_curtab, true, true);
+ }
+ if (win_valid(new_curwin)) {
+ win_enter(new_curwin, false);
+ }
+
+ autocmd_no_leave--;
+ xfree(opened);
+}
+
+/// ":all" and ":sall".
+/// Also used for ":tab drop file ..." after setting the argument list.
+void ex_all(exarg_T *eap)
+{
+ if (eap->addr_count == 0) {
+ eap->line2 = 9999;
+ }
+ do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop);
+}
+
+/// Concatenate all files in the argument list, separated by spaces, and return
+/// it in one allocated string.
+/// Spaces and backslashes in the file names are escaped with a backslash.
+char *arg_all(void)
+{
+ char *retval = NULL;
+
+ // Do this loop two times:
+ // first time: compute the total length
+ // second time: concatenate the names
+ for (;;) {
+ int len = 0;
+ for (int idx = 0; idx < ARGCOUNT; idx++) {
+ char *p = alist_name(&ARGLIST[idx]);
+ if (p == NULL) {
+ continue;
+ }
+ if (len > 0) {
+ // insert a space in between names
+ if (retval != NULL) {
+ retval[len] = ' ';
+ }
+ len++;
+ }
+ for (; *p != NUL; p++) {
+ if (*p == ' '
+#ifndef BACKSLASH_IN_FILENAME
+ || *p == '\\'
+#endif
+ || *p == '`') {
+ // insert a backslash
+ if (retval != NULL) {
+ retval[len] = '\\';
+ }
+ len++;
+ }
+ if (retval != NULL) {
+ retval[len] = *p;
+ }
+ len++;
+ }
+ }
+
+ // second time: break here
+ if (retval != NULL) {
+ retval[len] = NUL;
+ break;
+ }
+
+ // allocate memory
+ retval = xmalloc((size_t)len + 1);
+ }
+
+ return retval;
+}
+
+/// "argc([window id])" function
+void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // use the current window
+ rettv->vval.v_number = ARGCOUNT;
+ } else if (argvars[0].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[0]) == -1) {
+ // use the global argument list
+ rettv->vval.v_number = GARGCOUNT;
+ } else {
+ // use the argument list of the specified window
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp != NULL) {
+ rettv->vval.v_number = WARGCOUNT(wp);
+ } else {
+ rettv->vval.v_number = -1;
+ }
+ }
+}
+
+/// "argidx()" function
+void f_argidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = curwin->w_arg_idx;
+}
+
+/// "arglistid()" function
+void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = -1;
+ win_T *wp = find_tabwin(&argvars[0], &argvars[1]);
+ if (wp != NULL) {
+ rettv->vval.v_number = wp->w_alist->id;
+ }
+}
+
+/// Get the argument list for a given window
+static void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
+{
+ tv_list_alloc_ret(rettv, argcount);
+ if (arglist != NULL) {
+ for (int idx = 0; idx < argcount; idx++) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)alist_name(&arglist[idx]), -1);
+ }
+ }
+}
+
+/// "argv(nr)" function
+void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ aentry_T *arglist = NULL;
+ int argcount = -1;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ arglist = ARGLIST;
+ argcount = ARGCOUNT;
+ } else if (argvars[1].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[1]) == -1) {
+ arglist = GARGLIST;
+ argcount = GARGCOUNT;
+ } else {
+ win_T *wp = find_win_by_nr_or_id(&argvars[1]);
+ if (wp != NULL) {
+ // Use the argument list of the specified window
+ arglist = WARGLIST(wp);
+ argcount = WARGCOUNT(wp);
+ }
+ }
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ int idx = (int)tv_get_number_chk(&argvars[0], NULL);
+ if (arglist != NULL && idx >= 0 && idx < argcount) {
+ rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
+ } else if (idx == -1) {
+ get_arglist_as_rettv(arglist, argcount, rettv);
+ }
+ } else {
+ get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
+ }
+}
diff --git a/src/nvim/arglist.h b/src/nvim/arglist.h
new file mode 100644
index 0000000000..b2e0f411d4
--- /dev/null
+++ b/src/nvim/arglist.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_ARGLIST_H
+#define NVIM_ARGLIST_H
+
+#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "arglist.h.generated.h"
+#endif
+
+#endif // NVIM_ARGLIST_H
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 0d8efbb830..b5b2a73be1 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -11,6 +11,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
@@ -20,6 +21,7 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
#include "nvim/map.h"
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 6262f2d61c..f9bce2476f 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -25,6 +25,7 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/autocmd.h"
@@ -37,6 +38,7 @@
#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
@@ -51,6 +53,7 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -71,7 +74,6 @@
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
@@ -4639,279 +4641,6 @@ void fname_expand(buf_T *buf, char **ffname, char **sfname)
#endif
}
-/// Get the file name for an argument list entry.
-char *alist_name(aentry_T *aep)
-{
- buf_T *bp;
-
- // Use the name from the associated buffer if it exists.
- bp = buflist_findnr(aep->ae_fnum);
- if (bp == NULL || bp->b_fname == NULL) {
- return (char *)aep->ae_fname;
- }
- return bp->b_fname;
-}
-
-/// do_arg_all(): Open up to 'count' windows, one for each argument.
-///
-/// @param forceit hide buffers in current windows
-/// @param keep_tabs keep current tabs, for ":tab drop file"
-void do_arg_all(int count, int forceit, int keep_tabs)
-{
- uint8_t *opened; // Array of weight for which args are open:
- // 0: not opened
- // 1: opened in other tab
- // 2: opened in curtab
- // 3: opened in curtab and curwin
-
- int opened_len; // length of opened[]
- int use_firstwin = false; // use first window for arglist
- bool tab_drop_empty_window = false;
- int split_ret = OK;
- bool p_ea_save;
- alist_T *alist; // argument list to be used
- buf_T *buf;
- tabpage_T *tpnext;
- int had_tab = cmdmod.cmod_tab;
- win_T *old_curwin, *last_curwin;
- tabpage_T *old_curtab, *last_curtab;
- win_T *new_curwin = NULL;
- tabpage_T *new_curtab = NULL;
-
- assert(firstwin != NULL); // satisfy coverity
-
- if (ARGCOUNT <= 0) {
- // Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
- return;
- }
- setpcmark();
-
- opened_len = ARGCOUNT;
- opened = xcalloc((size_t)opened_len, 1);
-
- // Autocommands may do anything to the argument list. Make sure it's not
- // freed while we are working here by "locking" it. We still have to
- // watch out for its size to be changed.
- alist = curwin->w_alist;
- alist->al_refcount++;
-
- old_curwin = curwin;
- old_curtab = curtab;
-
- // Try closing all windows that are not in the argument list.
- // Also close windows that are not full width;
- // When 'hidden' or "forceit" set the buffer becomes hidden.
- // Windows that have a changed buffer and can't be hidden won't be closed.
- // When the ":tab" modifier was used do this for all tab pages.
- if (had_tab > 0) {
- goto_tabpage_tp(first_tabpage, true, true);
- }
- for (;;) {
- win_T *wpnext = NULL;
- tpnext = curtab->tp_next;
- for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
- int i;
- wpnext = wp->w_next;
- buf = wp->w_buffer;
- if (buf->b_ffname == NULL
- || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
- i = opened_len;
- } else {
- // check if the buffer in this window is in the arglist
- for (i = 0; i < opened_len; i++) {
- if (i < alist->al_ga.ga_len
- && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
- || path_full_compare(alist_name(&AARGLIST(alist)[i]),
- buf->b_ffname,
- true, true) & kEqualFiles)) {
- int weight = 1;
-
- if (old_curtab == curtab) {
- weight++;
- if (old_curwin == wp) {
- weight++;
- }
- }
-
- if (weight > (int)opened[i]) {
- opened[i] = (uint8_t)weight;
- if (i == 0) {
- if (new_curwin != NULL) {
- new_curwin->w_arg_idx = opened_len;
- }
- new_curwin = wp;
- new_curtab = curtab;
- }
- } else if (keep_tabs) {
- i = opened_len;
- }
-
- if (wp->w_alist != alist) {
- // Use the current argument list for all windows containing a file from it.
- alist_unlink(wp->w_alist);
- wp->w_alist = alist;
- wp->w_alist->al_refcount++;
- }
- break;
- }
- }
- }
- wp->w_arg_idx = i;
-
- if (i == opened_len && !keep_tabs) { // close this window
- if (buf_hide(buf) || forceit || buf->b_nwindows > 1
- || !bufIsChanged(buf)) {
- // If the buffer was changed, and we would like to hide it, try autowriting.
- if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
- bufref_T bufref;
- set_bufref(&bufref, buf);
- (void)autowrite(buf, false);
- // Check if autocommands removed the window.
- if (!win_valid(wp) || !bufref_valid(&bufref)) {
- wpnext = firstwin; // Start all over...
- continue;
- }
- }
- // don't close last window
- if (ONE_WINDOW
- && (first_tabpage->tp_next == NULL || !had_tab)) {
- use_firstwin = true;
- } else {
- win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
- // check if autocommands removed the next window
- if (!win_valid(wpnext)) {
- // start all over...
- wpnext = firstwin;
- }
- }
- }
- }
- }
-
- // Without the ":tab" modifier only do the current tab page.
- if (had_tab == 0 || tpnext == NULL) {
- break;
- }
-
- // check if autocommands removed the next tab page
- if (!valid_tabpage(tpnext)) {
- tpnext = first_tabpage; // start all over...
- }
- goto_tabpage_tp(tpnext, true, true);
- }
-
- // Open a window for files in the argument list that don't have one.
- // ARGCOUNT may change while doing this, because of autocommands.
- if (count > opened_len || count <= 0) {
- count = opened_len;
- }
-
- // Don't execute Win/Buf Enter/Leave autocommands here.
- autocmd_no_enter++;
- autocmd_no_leave++;
- last_curwin = curwin;
- last_curtab = curtab;
- win_enter(lastwin, false);
- // ":tab drop file" should re-use an empty window to avoid "--remote-tab"
- // leaving an empty tab page when executed locally.
- if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
- && curbuf->b_ffname == NULL && !curbuf->b_changed) {
- use_firstwin = true;
- tab_drop_empty_window = true;
- }
-
- for (int i = 0; i < count && !got_int; i++) {
- if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
- arg_had_last = true;
- }
- if (opened[i] > 0) {
- // Move the already present window to below the current window
- if (curwin->w_arg_idx != i) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_arg_idx == i) {
- if (keep_tabs) {
- new_curwin = wp;
- new_curtab = curtab;
- } else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
- emsg(_("E249: window layout changed unexpectedly"));
- i = count;
- break;
- } else {
- win_move_after(wp, curwin);
- }
- break;
- }
- }
- }
- } else if (split_ret == OK) {
- // trigger events for tab drop
- if (tab_drop_empty_window && i == count - 1) {
- autocmd_no_enter--;
- }
- if (!use_firstwin) { // split current window
- p_ea_save = p_ea;
- p_ea = true; // use space from all windows
- split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
- p_ea = p_ea_save;
- if (split_ret == FAIL) {
- continue;
- }
- } else { // first window: do autocmd for leaving this buffer
- autocmd_no_leave--;
- }
-
- // edit file "i"
- curwin->w_arg_idx = i;
- if (i == 0) {
- new_curwin = curwin;
- new_curtab = curtab;
- }
- (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
- ((buf_hide(curwin->w_buffer)
- || bufIsChanged(curwin->w_buffer))
- ? ECMD_HIDE : 0) + ECMD_OLDBUF,
- curwin);
- if (tab_drop_empty_window && i == count - 1) {
- autocmd_no_enter++;
- }
- if (use_firstwin) {
- autocmd_no_leave++;
- }
- use_firstwin = false;
- }
- os_breakcheck();
-
- // When ":tab" was used open a new tab for a new window repeatedly.
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
- cmdmod.cmod_tab = 9999;
- }
- }
-
- // Remove the "lock" on the argument list.
- alist_unlink(alist);
-
- autocmd_no_enter--;
- // restore last referenced tabpage's curwin
- if (last_curtab != new_curtab) {
- if (valid_tabpage(last_curtab)) {
- goto_tabpage_tp(last_curtab, true, true);
- }
- if (win_valid(last_curwin)) {
- win_enter(last_curwin, false);
- }
- }
- // to window with first arg
- if (valid_tabpage(new_curtab)) {
- goto_tabpage_tp(new_curtab, true, true);
- }
- if (win_valid(new_curwin)) {
- win_enter(new_curwin, false);
- }
-
- autocmd_no_leave--;
- xfree(opened);
-}
-
/// @return true if "buf" is a prompt buffer.
bool bt_prompt(buf_T *buf)
FUNC_ATTR_PURE
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 70dda8eaec..7627b6a596 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -4,10 +4,10 @@
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/func_attr.h"
+#include "nvim/grid_defs.h" // for StlClickRecord
#include "nvim/macros.h"
#include "nvim/memline.h"
#include "nvim/pos.h" // for linenr_T
-#include "nvim/screen.h" // for StlClickRecord
// Values for buflist_getfile()
enum getf_values {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index f5dd86cb98..15b964f5ed 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1143,11 +1143,14 @@ struct window_S {
synblock_T *w_s; ///< for :ownsyntax
+ int w_ns_hl;
+ int w_ns_hl_winhl;
+ int w_ns_hl_active;
+ int *w_ns_hl_attr;
+
int w_hl_id_normal; ///< 'winhighlight' normal id
int w_hl_attr_normal; ///< 'winhighlight' normal final attrs
-
- int w_hl_ids[HLF_COUNT]; ///< 'winhighlight' id
- int w_hl_attrs[HLF_COUNT]; ///< 'winhighlight' final attrs
+ int w_hl_attr_normalnc; ///< 'winhighlight' NormalNC final attrs
int w_hl_needs_update; ///< attrs need to be recalculated
@@ -1469,11 +1472,6 @@ struct window_S {
size_t w_winbar_click_defs_size;
};
-static inline int win_hl_attr(win_T *wp, int hlf)
-{
- return wp->w_hl_attrs[hlf];
-}
-
/// Macros defined in Vim, but not in Neovim
#define CHANGEDTICK(buf) \
(=== Include buffer.h & use buf_(get|set|inc) _changedtick ===)
diff --git a/src/nvim/change.c b/src/nvim/change.c
index f0ca5f3af3..5184dc0689 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -10,6 +10,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/extmark.h"
@@ -23,7 +24,6 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/ui.h"
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 169f2dacde..5910053025 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -315,8 +315,6 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader
ChannelStdinMode stdin_mode, const char *cwd, uint16_t pty_width,
uint16_t pty_height, dict_T *env, varnumber_T *status_out)
{
- assert(cwd == NULL || os_isdir_executable(cwd));
-
Channel *chan = channel_alloc(kChannelStreamProc);
chan->on_data = on_stdout;
chan->on_stderr = on_stderr;
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 1446257f7e..d4670dedc7 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -9,6 +9,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/fold.h"
#include "nvim/mark.h"
@@ -17,7 +18,6 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/vim.h"
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 72d776d1e4..a061bd961b 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -8,6 +8,7 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
@@ -18,7 +19,6 @@
#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/types.h"
#include "nvim/vim.h"
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 8258f2afd5..a93fb599c4 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -4,12 +4,12 @@
#include "nvim/api/ui.h"
#include "nvim/buffer.h"
#include "nvim/decoration.h"
+#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/move.h"
-#include "nvim/screen.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -358,7 +358,8 @@ next_mark:
return attr;
}
-void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs[])
+void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattrs[],
+ HlPriAttr *num_attrs, HlPriAttr *line_attrs, HlPriAttr *cul_attrs)
{
if (!buf->b_signs) {
return;
@@ -383,30 +384,37 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs
goto next_mark;
}
- int j;
- for (j = (*num_signs); j > 0; j--) {
- if (sattrs[j - 1].sat_prio >= decor->priority) {
- break;
- }
- sattrs[j] = sattrs[j - 1];
- }
- if (j < SIGN_SHOW_MAX) {
- CLEAR_FIELD(sattrs[j]);
- sattrs[j].sat_text = decor->sign_text;
- if (decor->sign_hl_id != 0) {
- sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id);
- }
- if (decor->number_hl_id != 0) {
- sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id);
+ if (decor->sign_text) {
+ int j;
+ for (j = (*num_signs); j > 0; j--) {
+ if (sattrs[j - 1].priority >= decor->priority) {
+ break;
+ }
+ sattrs[j] = sattrs[j - 1];
}
- if (decor->line_hl_id != 0) {
- sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id);
+ if (j < SIGN_SHOW_MAX) {
+ sattrs[j] = (SignTextAttrs) {
+ .text = decor->sign_text,
+ .hl_attr_id = decor->sign_hl_id == 0 ? 0 : syn_id2attr(decor->sign_hl_id),
+ .priority = decor->priority
+ };
+ (*num_signs)++;
}
- if (decor->cursorline_hl_id != 0) {
- sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id);
+ }
+
+ struct { HlPriAttr *dest; int hl; } cattrs[] = {
+ { line_attrs, decor->line_hl_id },
+ { num_attrs, decor->number_hl_id },
+ { cul_attrs, decor->cursorline_hl_id },
+ { NULL, -1 },
+ };
+ for (int i = 0; cattrs[i].dest; i++) {
+ if (cattrs[i].hl != 0 && decor->priority >= cattrs[i].dest->priority) {
+ *cattrs[i].dest = (HlPriAttr) {
+ .attr_id = syn_id2attr(cattrs[i].hl),
+ .priority = decor->priority
+ };
}
- sattrs[j].sat_prio = decor->priority;
- (*num_signs)++;
}
next_mark:
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 04d875c4e3..95e13b4240 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -14,7 +14,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
{ ns_id, false, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, -1 }
+ LUA_NOREF, -1, false }
static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args,
bool default_true, char **perr)
@@ -107,8 +107,6 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
}
}
}
-
- win_check_ns_hl(wp);
}
/// For each provider invoke the 'line' callback for a given window row.
@@ -135,7 +133,7 @@ void providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *
kv_A(*providers, k) = NULL;
}
- win_check_ns_hl(wp);
+ hl_check_ns();
}
}
}
diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h
index 3ec7c80357..dd1ed6c581 100644
--- a/src/nvim/decoration_provider.h
+++ b/src/nvim/decoration_provider.h
@@ -13,6 +13,7 @@ typedef struct {
LuaRef redraw_end;
LuaRef hl_def;
int hl_valid;
+ bool hl_cached;
} DecorProvider;
typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index d483e22a30..c1fdbc1b9a 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -20,6 +20,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
@@ -36,7 +37,6 @@
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index c4e5f25719..0f511bd37c 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -12,6 +12,7 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
@@ -24,7 +25,6 @@
#include "nvim/normal.h"
#include "nvim/os/input.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
new file mode 100644
index 0000000000..95026ff8ed
--- /dev/null
+++ b/src/nvim/drawline.c
@@ -0,0 +1,2706 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// drawline.c: Functions for drawing window lines on the screen.
+// This is the middle level, drawscreen.c is the top and grid.c/screen.c the lower level.
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/arabic.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/cursor_shape.h"
+#include "nvim/diff.h"
+#include "nvim/drawline.h"
+#include "nvim/fold.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
+#include "nvim/match.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/plines.h"
+#include "nvim/quickfix.h"
+#include "nvim/search.h"
+#include "nvim/sign.h"
+#include "nvim/spell.h"
+#include "nvim/state.h"
+#include "nvim/syntax.h"
+#include "nvim/undo.h"
+#include "nvim/window.h"
+
+#define MB_FILLER_CHAR '<' // character used when a double-width character
+ // doesn't fit.
+
+/// for line_putchar. Contains the state that needs to be remembered from
+/// putting one character to the next.
+typedef struct {
+ const char *p;
+ int prev_c; ///< previous Arabic character
+ int prev_c1; ///< first composing char for prev_c
+} LineState;
+#define LINE_STATE(p) { p, 0, 0 }
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawline.c.generated.h"
+#endif
+
+/// Advance **color_cols
+///
+/// @return true when there are columns to draw.
+static bool advance_color_col(int vcol, int **color_cols)
+{
+ while (**color_cols >= 0 && vcol > **color_cols) {
+ (*color_cols)++;
+ }
+ return **color_cols >= 0;
+}
+
+/// Used when 'cursorlineopt' contains "screenline": compute the margins between
+/// which the highlighting is used.
+static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
+{
+ // cache previous calculations depending on w_virtcol
+ static int saved_w_virtcol;
+ static win_T *prev_wp;
+ static int prev_left_col;
+ static int prev_right_col;
+ static int prev_col_off;
+
+ int cur_col_off = win_col_off(wp);
+ int width1;
+ int width2;
+
+ if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp
+ && prev_col_off == cur_col_off) {
+ *right_col = prev_right_col;
+ *left_col = prev_left_col;
+ return;
+ }
+
+ width1 = wp->w_width - cur_col_off;
+ width2 = width1 + win_col_off2(wp);
+
+ *left_col = 0;
+ *right_col = width1;
+
+ if (wp->w_virtcol >= (colnr_T)width1) {
+ *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2;
+ }
+ if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
+ *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1;
+ }
+
+ // cache values
+ prev_left_col = *left_col;
+ prev_right_col = *right_col;
+ prev_wp = wp;
+ saved_w_virtcol = wp->w_virtcol;
+ prev_col_off = cur_col_off;
+}
+
+/// Put a single char from an UTF-8 buffer into a line buffer.
+///
+/// Handles composing chars and arabic shaping state.
+static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol)
+{
+ const char_u *p = (char_u *)s->p;
+ int cells = utf_ptr2cells((char *)p);
+ int c_len = utfc_ptr2len((char *)p);
+ int u8c, u8cc[MAX_MCO];
+ if (cells > maxcells) {
+ return -1;
+ }
+ u8c = utfc_ptr2char(p, u8cc);
+ if (*p == TAB) {
+ cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
+ for (int c = 0; c < cells; c++) {
+ schar_from_ascii(dest[c], ' ');
+ }
+ goto done;
+ } else if (*p < 0x80 && u8cc[0] == 0) {
+ schar_from_ascii(dest[0], (char)(*p));
+ s->prev_c = u8c;
+ } else {
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
+ // Do Arabic shaping.
+ int pc, pc1, nc;
+ int pcc[MAX_MCO];
+ int firstbyte = *p;
+
+ // The idea of what is the previous and next
+ // character depends on 'rightleft'.
+ if (rl) {
+ pc = s->prev_c;
+ pc1 = s->prev_c1;
+ nc = utf_ptr2char((char *)p + c_len);
+ s->prev_c1 = u8cc[0];
+ } else {
+ pc = utfc_ptr2char(p + c_len, pcc);
+ nc = s->prev_c;
+ pc1 = pcc[0];
+ }
+ s->prev_c = u8c;
+
+ u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc);
+ } else {
+ s->prev_c = u8c;
+ }
+ schar_from_cc(dest[0], u8c, u8cc);
+ }
+ if (cells > 1) {
+ dest[1][0] = 0;
+ }
+done:
+ s->p += c_len;
+ return cells;
+}
+
+static inline void provider_err_virt_text(linenr_T lnum, char *err)
+{
+ Decoration err_decor = DECORATION_INIT;
+ int hl_err = syn_check_group(S_LEN("ErrorMsg"));
+ kv_push(err_decor.virt_text,
+ ((VirtTextChunk){ .text = err,
+ .hl_id = hl_err }));
+ err_decor.virt_text_width = (int)mb_string2cells(err);
+ decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
+}
+
+static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col,
+ int win_row)
+{
+ DecorState *state = &decor_state;
+ int right_pos = max_col;
+ bool do_eol = state->eol_col > -1;
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ DecorRange *item = &kv_A(state->active, i);
+ if (!(item->start_row == state->row
+ && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) {
+ continue;
+ }
+ if (item->win_col == -1) {
+ if (item->decor.virt_text_pos == kVTRightAlign) {
+ right_pos -= item->decor.virt_text_width;
+ item->win_col = right_pos;
+ } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
+ item->win_col = state->eol_col;
+ } else if (item->decor.virt_text_pos == kVTWinCol) {
+ item->win_col = MAX(item->decor.col + col_off, 0);
+ }
+ }
+ if (item->win_col < 0) {
+ continue;
+ }
+ int col;
+ if (item->decor.ui_watched) {
+ // send mark position to UI
+ col = item->win_col;
+ WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col };
+ kv_push(win_extmark_arr, m);
+ }
+ if (kv_size(item->decor.virt_text)) {
+ col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
+ item->decor.hl_mode, max_col, item->win_col - col_off);
+ }
+ item->win_col = -2; // deactivate
+ if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
+ state->eol_col = col + 1;
+ }
+
+ *end_col = MAX(*end_col, col);
+ }
+}
+
+static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
+ int vcol)
+{
+ LineState s = LINE_STATE("");
+ int virt_attr = 0;
+ size_t virt_pos = 0;
+
+ while (col < max_col) {
+ if (!*s.p) {
+ if (virt_pos >= kv_size(vt)) {
+ break;
+ }
+ virt_attr = 0;
+ do {
+ s.p = kv_A(vt, virt_pos).text;
+ int hl_id = kv_A(vt, virt_pos).hl_id;
+ virt_attr = hl_combine_attr(virt_attr,
+ hl_id > 0 ? syn_id2attr(hl_id) : 0);
+ virt_pos++;
+ } while (!s.p && virt_pos < kv_size(vt));
+ if (!s.p) {
+ break;
+ }
+ }
+ if (!*s.p) {
+ continue;
+ }
+ int attr;
+ bool through = false;
+ if (hl_mode == kHlModeCombine) {
+ attr = hl_combine_attr(linebuf_attr[col], virt_attr);
+ } else if (hl_mode == kHlModeBlend) {
+ through = (*s.p == ' ');
+ attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
+ } else {
+ attr = virt_attr;
+ }
+ schar_T dummy[2];
+ int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
+ max_col - col, false, vcol);
+ // if we failed to emit a char, we still need to advance
+ cells = MAX(cells, 1);
+
+ for (int c = 0; c < cells; c++) {
+ linebuf_attr[col++] = attr;
+ }
+ vcol += cells;
+ }
+ return col;
+}
+
+/// Return true if CursorLineSign highlight is to be used.
+static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR);
+}
+
+// Get information needed to display the sign in line 'lnum' in window 'wp'.
+// If 'nrcol' is TRUE, the sign is going to be displayed in the number column.
+// Otherwise the sign is going to be displayed in the sign column.
+//
+// @param count max number of signs
+// @param[out] n_extrap number of characters from pp_extra to display
+// @param sign_idxp Index of the displayed sign
+static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, SignTextAttrs sattrs[],
+ int row, int startrow, int filler_lines, int filler_todo,
+ int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
+ char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx,
+ int cul_attr)
+{
+ // Draw cells with the sign value or blank.
+ *c_extrap = ' ';
+ *c_finalp = NUL;
+ if (nrcol) {
+ *n_extrap = number_width(wp) + 1;
+ } else {
+ if (use_cursor_line_sign(wp, lnum)) {
+ *char_attrp = win_hl_attr(wp, HLF_CLS);
+ } else {
+ *char_attrp = win_hl_attr(wp, HLF_SC);
+ }
+ *n_extrap = win_signcol_width(wp);
+ }
+
+ if (row == startrow + filler_lines && filler_todo <= 0) {
+ SignTextAttrs *sattr = sign_get_attr(sign_idx, sattrs, wp->w_scwidth);
+ if (sattr != NULL) {
+ *pp_extra = sattr->text;
+ if (*pp_extra != NULL) {
+ *c_extrap = NUL;
+ *c_finalp = NUL;
+
+ if (nrcol) {
+ int n, width = number_width(wp) - 2;
+ for (n = 0; n < width; n++) {
+ extra[n] = ' ';
+ }
+ extra[n] = NUL;
+ STRCAT(extra, *pp_extra);
+ STRCAT(extra, " ");
+ *pp_extra = extra;
+ *n_extrap = (int)STRLEN(*pp_extra);
+ } else {
+ size_t symbol_blen = STRLEN(*pp_extra);
+
+ // TODO(oni-link): Is sign text already extended to
+ // full cell width?
+ assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra)));
+ // symbol(s) bytes + (filling spaces) (one byte each)
+ *n_extrap = (int)symbol_blen + win_signcol_width(wp) -
+ (int)mb_string2cells((char *)(*pp_extra));
+
+ assert(extra_size > symbol_blen);
+ memset(extra, ' ', extra_size);
+ memcpy(extra, *pp_extra, symbol_blen);
+
+ *pp_extra = extra;
+ (*pp_extra)[*n_extrap] = NUL;
+ }
+ }
+
+ if (use_cursor_line_sign(wp, lnum) && cul_attr > 0) {
+ *char_attrp = cul_attr;
+ } else {
+ *char_attrp = sattr->hl_attr_id;
+ }
+ }
+ }
+}
+
+static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int *line_attr,
+ int *num_attr, int *cul_attr)
+{
+ HlPriAttr line_attrs = { *line_attr, 0 };
+ HlPriAttr num_attrs = { *num_attr, 0 };
+ HlPriAttr cul_attrs = { *cul_attr, 0 };
+
+ // TODO(bfredl, vigoux): line_attr should not take priority over decoration!
+ int num_signs = buf_get_signattrs(buf, lnum, sattrs, &num_attrs, &line_attrs, &cul_attrs);
+ decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs, &num_attrs, &line_attrs, &cul_attrs);
+
+ *line_attr = line_attrs.attr_id;
+ *num_attr = num_attrs.attr_id;
+ *cul_attr = cul_attrs.attr_id;
+
+ return num_signs;
+}
+
+/// Return true if CursorLineNr highlight is to be used for the number column.
+///
+/// - 'cursorline' must be set
+/// - lnum must be the cursor line
+/// - 'cursorlineopt' has "number"
+/// - don't highlight filler lines (when in diff mode)
+/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number
+/// itself on the first screenline of the wrapped line, otherwise highlight the number column of
+/// all screenlines of the wrapped line.
+static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR)
+ && (row == startrow + filler_lines
+ || (row > startrow + filler_lines
+ && (wp->w_p_culopt_flags & CULOPT_LINE)));
+}
+
+static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
+{
+ long num;
+ char *fmt = "%*ld ";
+
+ if (wp->w_p_nu && !wp->w_p_rnu) {
+ // 'number' + 'norelativenumber'
+ num = (long)lnum;
+ } else {
+ // 'relativenumber', don't use negative numbers
+ num = labs((long)get_cursor_rel_lnum(wp, lnum));
+ if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
+ // 'number' + 'relativenumber'
+ num = lnum;
+ fmt = "%-*ld ";
+ }
+ }
+
+ snprintf((char *)buf, buf_len, fmt, number_width(wp), num);
+}
+
+static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
+{
+ if (wp->w_p_rnu) {
+ if (lnum < wp->w_cursor.lnum) {
+ // Use LineNrAbove
+ return win_hl_attr(wp, HLF_LNA);
+ }
+ if (lnum > wp->w_cursor.lnum) {
+ // Use LineNrBelow
+ return win_hl_attr(wp, HLF_LNB);
+ }
+ }
+
+ if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) {
+ // TODO(vim): Can we use CursorLine instead of CursorLineNr
+ // when CursorLineNr isn't set?
+ return win_hl_attr(wp, HLF_CLN);
+ }
+
+ return win_hl_attr(wp, HLF_N);
+}
+
+static void apply_cursorline_highlight(win_T *wp, linenr_T lnum, int *line_attr, int *cul_attr,
+ int *line_attr_lowprio)
+{
+ *cul_attr = win_hl_attr(wp, HLF_CUL);
+ HlAttrs ae = syn_attr2entry(*cul_attr);
+ // We make a compromise here (#7383):
+ // * low-priority CursorLine if fg is not set
+ // * high-priority ("same as Vim" priority) CursorLine if fg is set
+ if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
+ *line_attr_lowprio = *cul_attr;
+ } else {
+ if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
+ && qf_current_entry(wp) == lnum) {
+ *line_attr = hl_combine_attr(*cul_attr, *line_attr);
+ } else {
+ *line_attr = *cul_attr;
+ }
+ }
+}
+
+/// Display line "lnum" of window 'wp' on the screen.
+/// wp->w_virtcol needs to be valid.
+///
+/// @param lnum line to display
+/// @param startrow first row relative to window grid
+/// @param endrow last grid row to be redrawn
+/// @param nochange not updating for changed text
+/// @param number_only only update the number column
+/// @param foldinfo fold info for this line
+/// @param[in, out] providers decoration providers active this line
+/// items will be disables if they cause errors
+/// or explicitly return `false`.
+///
+/// @return the number of last row the line occupies.
+int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool number_only,
+ foldinfo_T foldinfo, DecorProviders *providers, char **provider_err)
+{
+ int c = 0; // init for GCC
+ long vcol = 0; // virtual column (for tabs)
+ long vcol_sbr = -1; // virtual column after showbreak
+ long vcol_prev = -1; // "vcol" of previous character
+ char_u *line; // current line
+ char_u *ptr; // current position in "line"
+ int row; // row in the window, excl w_winrow
+ ScreenGrid *grid = &wp->w_grid; // grid specific to the window
+
+ char_u extra[57]; // sign, line number and 'fdc' must
+ // fit in here
+ int n_extra = 0; // number of extra chars
+ char_u *p_extra = NULL; // string of extra chars, plus NUL
+ char_u *p_extra_free = NULL; // p_extra needs to be freed
+ int c_extra = NUL; // extra chars, all the same
+ int c_final = NUL; // final char, mandatory if set
+ int extra_attr = 0; // attributes when n_extra != 0
+ static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
+ // curwin->w_p_lcs_chars.eol at
+ // end-of-line
+ int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
+ int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
+ bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
+
+ // saved "extra" items for when draw_state becomes WL_LINE (again)
+ int saved_n_extra = 0;
+ char_u *saved_p_extra = NULL;
+ int saved_c_extra = 0;
+ int saved_c_final = 0;
+ int saved_char_attr = 0;
+
+ int n_attr = 0; // chars with special attr
+ int saved_attr2 = 0; // char_attr saved for n_attr
+ int n_attr3 = 0; // chars with overruling special attr
+ int saved_attr3 = 0; // char_attr saved for n_attr3
+
+ int n_skip = 0; // nr of chars to skip for 'nowrap'
+
+ int fromcol = -10; // start of inverting
+ int tocol = MAXCOL; // end of inverting
+ int fromcol_prev = -2; // start of inverting after cursor
+ bool noinvcur = false; // don't invert the cursor
+ bool lnum_in_visual_area = false;
+ pos_T pos;
+ long v;
+
+ int char_attr = 0; // attributes for next character
+ bool attr_pri = false; // char_attr has priority
+ bool area_highlighting = false; // Visual or incsearch highlighting in this line
+ int attr = 0; // attributes for area highlighting
+ int area_attr = 0; // attributes desired by highlighting
+ int search_attr = 0; // attributes desired by 'hlsearch'
+ int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
+ int syntax_attr = 0; // attributes desired by syntax
+ bool has_syntax = false; // this buffer has syntax highl.
+ int save_did_emsg;
+ int eol_hl_off = 0; // 1 if highlighted char after EOL
+ bool draw_color_col = false; // highlight colorcolumn
+ int *color_cols = NULL; // pointer to according columns array
+ bool has_spell = false; // this buffer has spell checking
+#define SPWORDLEN 150
+ char_u nextline[SPWORDLEN * 2]; // text with start of the next line
+ int nextlinecol = 0; // column where nextline[] starts
+ int nextline_idx = 0; // index in nextline[] where next line
+ // starts
+ int spell_attr = 0; // attributes desired by spelling
+ int word_end = 0; // last byte with same spell_attr
+ static linenr_T checked_lnum = 0; // line number for "checked_col"
+ static int checked_col = 0; // column in "checked_lnum" up to which
+ // there are no spell errors
+ static int cap_col = -1; // column to check for Cap word
+ static linenr_T capcol_lnum = 0; // line number where "cap_col"
+ int cur_checked_col = 0; // checked column for current line
+ int extra_check = 0; // has syntax or linebreak
+ int multi_attr = 0; // attributes desired by multibyte
+ int mb_l = 1; // multi-byte byte length
+ int mb_c = 0; // decoded multi-byte character
+ bool mb_utf8 = false; // screen char is UTF-8 char
+ int u8cc[MAX_MCO]; // composing UTF-8 chars
+ int filler_lines; // nr of filler lines to be drawn
+ int filler_todo; // nr of filler lines still to do + 1
+ hlf_T diff_hlf = (hlf_T)0; // type of diff highlighting
+ int change_start = MAXCOL; // first col of changed area
+ int change_end = -1; // last col of changed area
+ colnr_T trailcol = MAXCOL; // start of trailing spaces
+ colnr_T leadcol = 0; // start of leading spaces
+ bool in_multispace = false; // in multiple consecutive spaces
+ int multispace_pos = 0; // position in lcs-multispace string
+ bool need_showbreak = false; // overlong line, skip first x chars
+ int line_attr = 0; // attribute for the whole line
+ int line_attr_save;
+ int line_attr_lowprio = 0; // low-priority attribute for the line
+ int line_attr_lowprio_save;
+ int prev_c = 0; // previous Arabic character
+ int prev_c1 = 0; // first composing char for prev_c
+
+ bool search_attr_from_match = false; // if search_attr is from :match
+ bool has_decor = false; // this buffer has decoration
+ int win_col_offset = 0; // offset for window columns
+
+ char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
+
+ bool area_active = false;
+
+ int cul_attr = 0; // set when 'cursorline' active
+ // 'cursorlineopt' has "screenline" and cursor is in this line
+ bool cul_screenline = false;
+ // margin columns for the screen line, needed for when 'cursorlineopt'
+ // contains "screenline"
+ int left_curline_col = 0;
+ int right_curline_col = 0;
+
+ // draw_state: items that are drawn in sequence:
+#define WL_START 0 // nothing done yet
+#define WL_CMDLINE (WL_START + 1) // cmdline window column
+#define WL_FOLD (WL_CMDLINE + 1) // 'foldcolumn'
+#define WL_SIGN (WL_FOLD + 1) // column for signs
+#define WL_NR (WL_SIGN + 1) // line number
+#define WL_BRI (WL_NR + 1) // 'breakindent'
+#define WL_SBR (WL_BRI + 1) // 'showbreak' or 'diff'
+#define WL_LINE (WL_SBR + 1) // text in the line
+ int draw_state = WL_START; // what to draw next
+
+ int syntax_flags = 0;
+ int syntax_seqnr = 0;
+ int prev_syntax_id = 0;
+ int conceal_attr = win_hl_attr(wp, HLF_CONCEAL);
+ bool is_concealing = false;
+ int boguscols = 0; ///< nonexistent columns added to
+ ///< force wrapping
+ int vcol_off = 0; ///< offset for concealed characters
+ int did_wcol = false;
+ int match_conc = 0; ///< cchar for match functions
+ int old_boguscols = 0;
+#define VCOL_HLC (vcol - vcol_off)
+#define FIX_FOR_BOGUSCOLS \
+ { \
+ n_extra += vcol_off; \
+ vcol -= vcol_off; \
+ vcol_off = 0; \
+ col -= boguscols; \
+ old_boguscols = boguscols; \
+ boguscols = 0; \
+ }
+
+ if (startrow > endrow) { // past the end already!
+ return startrow;
+ }
+
+ row = startrow;
+
+ buf_T *buf = wp->w_buffer;
+ bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
+
+ if (!number_only) {
+ // To speed up the loop below, set extra_check when there is linebreak,
+ // trailing white space and/or syntax processing to be done.
+ extra_check = wp->w_p_lbr;
+ if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow
+ && !has_fold && !end_fill) {
+ // Prepare for syntax highlighting in this line. When there is an
+ // error, stop syntax highlighting.
+ save_did_emsg = did_emsg;
+ did_emsg = false;
+ syntax_start(wp, lnum);
+ if (did_emsg) {
+ wp->w_s->b_syn_error = true;
+ } else {
+ did_emsg = save_did_emsg;
+ if (!wp->w_s->b_syn_slow) {
+ has_syntax = true;
+ extra_check = true;
+ }
+ }
+ }
+
+ has_decor = decor_redraw_line(buf, lnum - 1, &decor_state);
+
+ providers_invoke_line(wp, providers, lnum - 1, &has_decor, provider_err);
+
+ if (*provider_err) {
+ provider_err_virt_text(lnum, *provider_err);
+ has_decor = true;
+ *provider_err = NULL;
+ }
+
+ if (has_decor) {
+ extra_check = true;
+ }
+
+ // Check for columns to display for 'colorcolumn'.
+ color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
+ if (color_cols != NULL) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ if (wp->w_p_spell
+ && !has_fold
+ && !end_fill
+ && *wp->w_s->b_p_spl != NUL
+ && !GA_EMPTY(&wp->w_s->b_langp)
+ && *(char **)(wp->w_s->b_langp.ga_data) != NULL) {
+ // Prepare for spell checking.
+ has_spell = true;
+ extra_check = true;
+
+ // Get the start of the next line, so that words that wrap to the next
+ // line are found too: "et<line-break>al.".
+ // Trick: skip a few chars for C/shell/Vim comments
+ nextline[SPWORDLEN] = NUL;
+ if (lnum < wp->w_buffer->b_ml.ml_line_count) {
+ line = ml_get_buf(wp->w_buffer, lnum + 1, false);
+ spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
+ }
+
+ // When a word wrapped from the previous line the start of the current
+ // line is valid.
+ if (lnum == checked_lnum) {
+ cur_checked_col = checked_col;
+ }
+ checked_lnum = 0;
+
+ // When there was a sentence end in the previous line may require a
+ // word starting with capital in this line. In line 1 always check
+ // the first word.
+ if (lnum != capcol_lnum) {
+ cap_col = -1;
+ }
+ if (lnum == 1) {
+ cap_col = 0;
+ }
+ capcol_lnum = 0;
+ }
+
+ // handle Visual active in this window
+ if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
+ pos_T *top, *bot;
+
+ if (ltoreq(curwin->w_cursor, VIsual)) {
+ // Visual is after curwin->w_cursor
+ top = &curwin->w_cursor;
+ bot = &VIsual;
+ } else {
+ // Visual is before curwin->w_cursor
+ top = &VIsual;
+ bot = &curwin->w_cursor;
+ }
+ lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum);
+ if (VIsual_mode == Ctrl_V) {
+ // block mode
+ if (lnum_in_visual_area) {
+ fromcol = wp->w_old_cursor_fcol;
+ tocol = wp->w_old_cursor_lcol;
+ }
+ } else {
+ // non-block mode
+ if (lnum > top->lnum && lnum <= bot->lnum) {
+ fromcol = 0;
+ } else if (lnum == top->lnum) {
+ if (VIsual_mode == 'V') { // linewise
+ fromcol = 0;
+ } else {
+ getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL);
+ if (gchar_pos(top) == NUL) {
+ tocol = fromcol + 1;
+ }
+ }
+ }
+ if (VIsual_mode != 'V' && lnum == bot->lnum) {
+ if (*p_sel == 'e' && bot->col == 0
+ && bot->coladd == 0) {
+ fromcol = -10;
+ tocol = MAXCOL;
+ } else if (bot->col == MAXCOL) {
+ tocol = MAXCOL;
+ } else {
+ pos = *bot;
+ if (*p_sel == 'e') {
+ getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL);
+ } else {
+ getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol);
+ tocol++;
+ }
+ }
+ }
+ }
+
+ // Check if the char under the cursor should be inverted (highlighted).
+ if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
+ && cursor_is_block_during_visual(*p_sel == 'e')) {
+ noinvcur = true;
+ }
+
+ // if inverting in this line set area_highlighting
+ if (fromcol >= 0) {
+ area_highlighting = true;
+ attr = win_hl_attr(wp, HLF_V);
+ }
+ // handle 'incsearch' and ":s///c" highlighting
+ } else if (highlight_match
+ && wp == curwin
+ && !has_fold
+ && lnum >= curwin->w_cursor.lnum
+ && lnum <= curwin->w_cursor.lnum + search_match_lines) {
+ if (lnum == curwin->w_cursor.lnum) {
+ getvcol(curwin, &(curwin->w_cursor),
+ (colnr_T *)&fromcol, NULL, NULL);
+ } else {
+ fromcol = 0;
+ }
+ if (lnum == curwin->w_cursor.lnum + search_match_lines) {
+ pos.lnum = lnum;
+ pos.col = search_match_endcol;
+ getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL);
+ }
+ // do at least one character; happens when past end of line
+ if (fromcol == tocol && search_match_endcol) {
+ tocol = fromcol + 1;
+ }
+ area_highlighting = true;
+ attr = win_hl_attr(wp, HLF_I);
+ }
+ }
+
+ int bg_attr = win_bg_attr(wp);
+
+ filler_lines = diff_check(wp, lnum);
+ if (filler_lines < 0) {
+ if (filler_lines == -1) {
+ if (diff_find_change(wp, lnum, &change_start, &change_end)) {
+ diff_hlf = HLF_ADD; // added line
+ } else if (change_start == 0) {
+ diff_hlf = HLF_TXD; // changed text
+ } else {
+ diff_hlf = HLF_CHD; // changed line
+ }
+ } else {
+ diff_hlf = HLF_ADD; // added line
+ }
+ filler_lines = 0;
+ area_highlighting = true;
+ }
+ VirtLines virt_lines = KV_INITIAL_VALUE;
+ int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines);
+ filler_lines += n_virt_lines;
+ if (lnum == wp->w_topline) {
+ filler_lines = wp->w_topfill;
+ n_virt_lines = MIN(n_virt_lines, filler_lines);
+ }
+ filler_todo = filler_lines;
+
+ // Cursor line highlighting for 'cursorline' in the current window.
+ if (lnum == wp->w_cursor.lnum) {
+ // Do not show the cursor line in the text when Visual mode is active,
+ // because it's not clear what is selected then.
+ if (wp->w_p_cul && !(wp == curwin && VIsual_active)
+ && wp->w_p_culopt_flags != CULOPT_NBR) {
+ cul_screenline = (wp->w_p_wrap
+ && (wp->w_p_culopt_flags & CULOPT_SCRLINE));
+ if (!cul_screenline) {
+ apply_cursorline_highlight(wp, lnum, &line_attr, &cul_attr, &line_attr_lowprio);
+ } else {
+ margin_columns_win(wp, &left_curline_col, &right_curline_col);
+ }
+ area_highlighting = true;
+ }
+ }
+
+ SignTextAttrs sattrs[SIGN_SHOW_MAX]; // sign attributes for the sign column
+ int sign_num_attr = 0; // sign attribute for the number column
+ int sign_cul_attr = 0; // sign attribute for cursorline
+ CLEAR_FIELD(sattrs);
+ int num_signs = get_sign_attrs(buf, lnum, sattrs, &line_attr, &sign_num_attr, &sign_cul_attr);
+
+ // Highlight the current line in the quickfix window.
+ if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
+ line_attr = win_hl_attr(wp, HLF_QFL);
+ }
+
+ if (line_attr_lowprio || line_attr) {
+ area_highlighting = true;
+ }
+
+ if (cul_screenline) {
+ line_attr_save = line_attr;
+ line_attr_lowprio_save = line_attr_lowprio;
+ }
+
+ line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false);
+ ptr = line;
+
+ if (has_spell && !number_only) {
+ // For checking first word with a capital skip white space.
+ if (cap_col == 0) {
+ cap_col = (int)getwhitecols(line);
+ }
+
+ // To be able to spell-check over line boundaries copy the end of the
+ // current line into nextline[]. Above the start of the next line was
+ // copied to nextline[SPWORDLEN].
+ if (nextline[SPWORDLEN] == NUL) {
+ // No next line or it is empty.
+ nextlinecol = MAXCOL;
+ nextline_idx = 0;
+ } else {
+ v = (long)STRLEN(line);
+ if (v < SPWORDLEN) {
+ // Short line, use it completely and append the start of the
+ // next line.
+ nextlinecol = 0;
+ memmove(nextline, line, (size_t)v);
+ STRMOVE(nextline + v, nextline + SPWORDLEN);
+ nextline_idx = (int)v + 1;
+ } else {
+ // Long line, use only the last SPWORDLEN bytes.
+ nextlinecol = (int)v - SPWORDLEN;
+ memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512
+ nextline_idx = SPWORDLEN + 1;
+ }
+ }
+ }
+
+ if (wp->w_p_list && !has_fold && !end_fill) {
+ if (wp->w_p_lcs_chars.space
+ || wp->w_p_lcs_chars.multispace != NULL
+ || wp->w_p_lcs_chars.leadmultispace != NULL
+ || wp->w_p_lcs_chars.trail
+ || wp->w_p_lcs_chars.lead
+ || wp->w_p_lcs_chars.nbsp) {
+ extra_check = true;
+ }
+ // find start of trailing whitespace
+ if (wp->w_p_lcs_chars.trail) {
+ trailcol = (colnr_T)STRLEN(ptr);
+ while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
+ trailcol--;
+ }
+ trailcol += (colnr_T)(ptr - line);
+ }
+ // find end of leading whitespace
+ if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) {
+ leadcol = 0;
+ while (ascii_iswhite(ptr[leadcol])) {
+ leadcol++;
+ }
+ if (ptr[leadcol] == NUL) {
+ // in a line full of spaces all of them are treated as trailing
+ leadcol = (colnr_T)0;
+ } else {
+ // keep track of the first column not filled with spaces
+ leadcol += (colnr_T)(ptr - line) + 1;
+ }
+ }
+ }
+
+ // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
+ // first character to be displayed.
+ if (wp->w_p_wrap) {
+ v = wp->w_skipcol;
+ } else {
+ v = wp->w_leftcol;
+ }
+ if (v > 0 && !number_only) {
+ char_u *prev_ptr = ptr;
+ while (vcol < v && *ptr != NUL) {
+ c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL);
+ vcol += c;
+ prev_ptr = ptr;
+ MB_PTR_ADV(ptr);
+ }
+
+ // When:
+ // - 'cuc' is set, or
+ // - 'colorcolumn' is set, or
+ // - 'virtualedit' is set, or
+ // - the visual mode is active,
+ // the end of the line may be before the start of the displayed part.
+ if (vcol < v && (wp->w_p_cuc
+ || draw_color_col
+ || virtual_active()
+ || (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
+ vcol = v;
+ }
+
+ // Handle a character that's not completely on the screen: Put ptr at
+ // that character but skip the first few screen characters.
+ if (vcol > v) {
+ vcol -= c;
+ ptr = prev_ptr;
+ // If the character fits on the screen, don't need to skip it.
+ // Except for a TAB.
+ if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) {
+ n_skip = (int)(v - vcol);
+ }
+ }
+
+ // Adjust for when the inverted text is before the screen,
+ // and when the start of the inverted text is before the screen.
+ if (tocol <= vcol) {
+ fromcol = 0;
+ } else if (fromcol >= 0 && fromcol < vcol) {
+ fromcol = (int)vcol;
+ }
+
+ // When w_skipcol is non-zero, first line needs 'showbreak'
+ if (wp->w_p_wrap) {
+ need_showbreak = true;
+ }
+ // When spell checking a word we need to figure out the start of the
+ // word and if it's badly spelled or not.
+ if (has_spell) {
+ size_t len;
+ colnr_T linecol = (colnr_T)(ptr - line);
+ hlf_T spell_hlf = HLF_COUNT;
+
+ pos = wp->w_cursor;
+ wp->w_cursor.lnum = lnum;
+ wp->w_cursor.col = linecol;
+ len = spell_move_to(wp, FORWARD, true, true, &spell_hlf);
+
+ // spell_move_to() may call ml_get() and make "line" invalid
+ line = ml_get_buf(wp->w_buffer, lnum, false);
+ ptr = line + linecol;
+
+ if (len == 0 || (int)wp->w_cursor.col > ptr - line) {
+ // no bad word found at line start, don't check until end of a
+ // word
+ spell_hlf = HLF_COUNT;
+ word_end = (int)(spell_to_word_end(ptr, wp) - line + 1);
+ } else {
+ // bad word found, use attributes until end of word
+ assert(len <= INT_MAX);
+ word_end = wp->w_cursor.col + (int)len + 1;
+
+ // Turn index into actual attributes.
+ if (spell_hlf != HLF_COUNT) {
+ spell_attr = highlight_attr[spell_hlf];
+ }
+ }
+ wp->w_cursor = pos;
+
+ // Need to restart syntax highlighting for this line.
+ if (has_syntax) {
+ syntax_start(wp, lnum);
+ }
+ }
+ }
+
+ // Correct highlighting for cursor that can't be disabled.
+ // Avoids having to check this for each character.
+ if (fromcol >= 0) {
+ if (noinvcur) {
+ if ((colnr_T)fromcol == wp->w_virtcol) {
+ // highlighting starts at cursor, let it start just after the
+ // cursor
+ fromcol_prev = fromcol;
+ fromcol = -1;
+ } else if ((colnr_T)fromcol < wp->w_virtcol) {
+ // restart highlighting after the cursor
+ fromcol_prev = wp->w_virtcol;
+ }
+ }
+ if (fromcol >= tocol) {
+ fromcol = -1;
+ }
+ }
+
+ if (!number_only && !has_fold && !end_fill) {
+ v = ptr - line;
+ area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
+ &line, &screen_search_hl, &search_attr,
+ &search_attr_from_match);
+ ptr = line + v; // "line" may have been updated
+ }
+
+ int off = 0; // Offset relative start of line
+ int col = 0; // Visual column on screen.
+ if (wp->w_p_rl) {
+ // Rightleft window: process the text in the normal direction, but put
+ // it in linebuf_char[off] from right to left. Start at the
+ // rightmost column of the window.
+ col = grid->cols - 1;
+ off += col;
+ }
+
+ // won't highlight after TERM_ATTRS_MAX columns
+ int term_attrs[TERM_ATTRS_MAX] = { 0 };
+ if (wp->w_buffer->terminal) {
+ terminal_get_line_attributes(wp->w_buffer->terminal, wp, lnum, term_attrs);
+ extra_check = true;
+ }
+
+ int sign_idx = 0;
+ // Repeat for the whole displayed line.
+ for (;;) {
+ int has_match_conc = 0; ///< match wants to conceal
+ int decor_conceal = 0;
+
+ bool did_decrement_ptr = false;
+
+ // Skip this quickly when working on the text.
+ if (draw_state != WL_LINE) {
+ if (cul_screenline) {
+ cul_attr = 0;
+ line_attr = line_attr_save;
+ line_attr_lowprio = line_attr_lowprio_save;
+ }
+
+ if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
+ draw_state = WL_CMDLINE;
+ if (cmdwin_type != 0 && wp == curwin) {
+ // Draw the cmdline character.
+ n_extra = 1;
+ c_extra = cmdwin_type;
+ c_final = NUL;
+ char_attr = win_hl_attr(wp, HLF_AT);
+ }
+ }
+
+ if (draw_state == WL_FOLD - 1 && n_extra == 0) {
+ int fdc = compute_foldcolumn(wp, 0);
+
+ draw_state = WL_FOLD;
+ if (fdc > 0) {
+ // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
+ // already be in use.
+ xfree(p_extra_free);
+ p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1);
+ n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
+ p_extra_free[n_extra] = NUL;
+ p_extra = p_extra_free;
+ c_extra = NUL;
+ c_final = NUL;
+ if (use_cursor_line_sign(wp, lnum)) {
+ char_attr = win_hl_attr(wp, HLF_CLF);
+ } else {
+ char_attr = win_hl_attr(wp, HLF_FC);
+ }
+ }
+ }
+
+ // sign column, this is hit until sign_idx reaches count
+ if (draw_state == WL_SIGN - 1 && n_extra == 0) {
+ draw_state = WL_SIGN;
+ // Show the sign column when there are any signs in this buffer
+ if (wp->w_scwidth > 0) {
+ get_sign_display_info(false, wp, lnum, sattrs, row,
+ startrow, filler_lines, filler_todo,
+ &c_extra, &c_final, extra, sizeof(extra),
+ &p_extra, &n_extra, &char_attr, sign_idx,
+ sign_cul_attr);
+ sign_idx++;
+ if (sign_idx < wp->w_scwidth) {
+ draw_state = WL_SIGN - 1;
+ } else {
+ sign_idx = 0;
+ }
+ }
+ }
+
+ if (draw_state == WL_NR - 1 && n_extra == 0) {
+ draw_state = WL_NR;
+ // Display the absolute or relative line number. After the
+ // first fill with blanks when the 'n' flag isn't in 'cpo'
+ if ((wp->w_p_nu || wp->w_p_rnu)
+ && (row == startrow + filler_lines
+ || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) {
+ // If 'signcolumn' is set to 'number' and a sign is present
+ // in 'lnum', then display the sign instead of the line
+ // number.
+ if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) {
+ get_sign_display_info(true, wp, lnum, sattrs, row,
+ startrow, filler_lines, filler_todo,
+ &c_extra, &c_final, extra, sizeof(extra),
+ &p_extra, &n_extra, &char_attr, sign_idx,
+ sign_cul_attr);
+ } else {
+ // Draw the line number (empty space after wrapping).
+ if (row == startrow + filler_lines) {
+ get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
+ if (wp->w_skipcol > 0) {
+ for (p_extra = extra; *p_extra == ' '; p_extra++) {
+ *p_extra = '-';
+ }
+ }
+ if (wp->w_p_rl) { // reverse line numbers
+ // like rl_mirror(), but keep the space at the end
+ char_u *p2 = (char_u *)skipwhite((char *)extra);
+ p2 = skiptowhite(p2) - 1;
+ for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
+ const char_u t = *p1;
+ *p1 = *p2;
+ *p2 = t;
+ }
+ }
+ p_extra = extra;
+ c_extra = NUL;
+ } else {
+ c_extra = ' ';
+ }
+ c_final = NUL;
+ n_extra = number_width(wp) + 1;
+ if (sign_num_attr > 0) {
+ char_attr = sign_num_attr;
+ } else {
+ char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines);
+ }
+ }
+ }
+ }
+
+ if (draw_state == WL_NR && n_extra == 0) {
+ win_col_offset = off;
+ }
+
+ if (wp->w_briopt_sbr && draw_state == WL_BRI - 1
+ && n_extra == 0 && *get_showbreak_value(wp) != NUL) {
+ // draw indent after showbreak value
+ draw_state = WL_BRI;
+ } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) {
+ // after the showbreak, draw the breakindent
+ draw_state = WL_BRI - 1;
+ }
+
+ // draw 'breakindent': indent wrapped text accordingly
+ if (draw_state == WL_BRI - 1 && n_extra == 0) {
+ draw_state = WL_BRI;
+ // if need_showbreak is set, breakindent also applies
+ if (wp->w_p_bri && (row != startrow || need_showbreak)
+ && filler_lines == 0) {
+ char_attr = 0;
+
+ if (diff_hlf != (hlf_T)0) {
+ char_attr = win_hl_attr(wp, (int)diff_hlf);
+ }
+ p_extra = NULL;
+ c_extra = ' ';
+ c_final = NUL;
+ n_extra =
+ get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false));
+ if (row == startrow) {
+ n_extra -= win_col_off2(wp);
+ if (n_extra < 0) {
+ n_extra = 0;
+ }
+ }
+ if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
+ need_showbreak = false;
+ }
+ // Correct end of highlighted area for 'breakindent',
+ // required wen 'linebreak' is also set.
+ if (tocol == vcol) {
+ tocol += n_extra;
+ }
+ }
+ }
+
+ if (draw_state == WL_SBR - 1 && n_extra == 0) {
+ draw_state = WL_SBR;
+ if (filler_todo > filler_lines - n_virt_lines) {
+ // TODO(bfredl): check this doesn't inhibit TUI-style
+ // clear-to-end-of-line.
+ c_extra = ' ';
+ c_final = NUL;
+ if (wp->w_p_rl) {
+ n_extra = col + 1;
+ } else {
+ n_extra = grid->cols - col;
+ }
+ char_attr = 0;
+ } else if (filler_todo > 0) {
+ // draw "deleted" diff line(s)
+ if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
+ c_extra = '-';
+ c_final = NUL;
+ } else {
+ c_extra = wp->w_p_fcs_chars.diff;
+ c_final = NUL;
+ }
+ if (wp->w_p_rl) {
+ n_extra = col + 1;
+ } else {
+ n_extra = grid->cols - col;
+ }
+ char_attr = win_hl_attr(wp, HLF_DED);
+ }
+ char_u *const sbr = get_showbreak_value(wp);
+ if (*sbr != NUL && need_showbreak) {
+ // Draw 'showbreak' at the start of each broken line.
+ p_extra = sbr;
+ c_extra = NUL;
+ c_final = NUL;
+ n_extra = (int)STRLEN(sbr);
+ char_attr = win_hl_attr(wp, HLF_AT);
+ if (wp->w_skipcol == 0 || !wp->w_p_wrap) {
+ need_showbreak = false;
+ }
+ vcol_sbr = vcol + mb_charlen(sbr);
+ // Correct end of highlighted area for 'showbreak',
+ // required when 'linebreak' is also set.
+ if (tocol == vcol) {
+ tocol += n_extra;
+ }
+ // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
+ if (cul_attr) {
+ char_attr = hl_combine_attr(cul_attr, char_attr);
+ }
+ }
+ }
+
+ if (draw_state == WL_LINE - 1 && n_extra == 0) {
+ sign_idx = 0;
+ draw_state = WL_LINE;
+
+ if (has_decor && row == startrow + filler_lines) {
+ // hide virt_text on text hidden by 'nowrap'
+ decor_redraw_col(wp->w_buffer, (int)vcol, off, true, &decor_state);
+ }
+
+ if (saved_n_extra) {
+ // Continue item from end of wrapped line.
+ n_extra = saved_n_extra;
+ c_extra = saved_c_extra;
+ c_final = saved_c_final;
+ p_extra = saved_p_extra;
+ char_attr = saved_char_attr;
+ } else {
+ char_attr = 0;
+ }
+ }
+ }
+
+ if (cul_screenline && draw_state == WL_LINE
+ && vcol >= left_curline_col
+ && vcol < right_curline_col) {
+ apply_cursorline_highlight(wp, lnum, &line_attr, &cul_attr, &line_attr_lowprio);
+ }
+
+ // When still displaying '$' of change command, stop at cursor
+ if (((dollar_vcol >= 0
+ && wp == curwin
+ && lnum == wp->w_cursor.lnum
+ && vcol >= (long)wp->w_virtcol)
+ || (number_only && draw_state > WL_NR))
+ && filler_todo <= 0) {
+ draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
+ grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, bg_attr, false);
+ // Pretend we have finished updating the window. Except when
+ // 'cursorcolumn' is set.
+ if (wp->w_p_cuc) {
+ row = wp->w_cline_row + wp->w_cline_height;
+ } else {
+ row = grid->rows;
+ }
+ break;
+ }
+
+ if (draw_state == WL_LINE
+ && has_fold
+ && col == win_col_offset
+ && n_extra == 0
+ && row == startrow) {
+ char_attr = win_hl_attr(wp, HLF_FL);
+
+ linenr_T lnume = lnum + foldinfo.fi_lines - 1;
+ memset(buf_fold, ' ', FOLD_TEXT_LEN);
+ p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ n_extra = (int)STRLEN(p_extra);
+
+ if (p_extra != buf_fold) {
+ xfree(p_extra_free);
+ p_extra_free = p_extra;
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ p_extra[n_extra] = NUL;
+ }
+
+ if (draw_state == WL_LINE
+ && has_fold
+ && col < grid->cols
+ && n_extra == 0
+ && row == startrow) {
+ // fill rest of line with 'fold'
+ c_extra = wp->w_p_fcs_chars.fold;
+ c_final = NUL;
+
+ n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col);
+ }
+
+ if (draw_state == WL_LINE
+ && has_fold
+ && col >= grid->cols
+ && n_extra != 0
+ && row == startrow) {
+ // Truncate the folding.
+ n_extra = 0;
+ }
+
+ if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
+ // handle Visual or match highlighting in this line
+ if (vcol == fromcol
+ || (vcol + 1 == fromcol && n_extra == 0
+ && utf_ptr2cells((char *)ptr) > 1)
+ || ((int)vcol_prev == fromcol_prev
+ && vcol_prev < vcol // not at margin
+ && vcol < tocol)) {
+ area_attr = attr; // start highlighting
+ if (area_highlighting) {
+ area_active = true;
+ }
+ } else if (area_attr != 0 && (vcol == tocol
+ || (noinvcur
+ && (colnr_T)vcol == wp->w_virtcol))) {
+ area_attr = 0; // stop highlighting
+ area_active = false;
+ }
+
+ if (!n_extra) {
+ // Check for start/end of 'hlsearch' and other matches.
+ // After end, check for start/end of next match.
+ // When another match, have to check for start again.
+ v = (ptr - line);
+ search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl,
+ &has_match_conc,
+ &match_conc, lcs_eol_one, &search_attr_from_match);
+ ptr = line + v; // "line" may have been changed
+
+ // Do not allow a conceal over EOL otherwise EOL will be missed
+ // and bad things happen.
+ if (*ptr == NUL) {
+ has_match_conc = 0;
+ }
+ }
+
+ if (diff_hlf != (hlf_T)0) {
+ if (diff_hlf == HLF_CHD && ptr - line >= change_start
+ && n_extra == 0) {
+ diff_hlf = HLF_TXD; // changed text
+ }
+ if (diff_hlf == HLF_TXD && ptr - line > change_end
+ && n_extra == 0) {
+ diff_hlf = HLF_CHD; // changed line
+ }
+ line_attr = win_hl_attr(wp, (int)diff_hlf);
+ // Overlay CursorLine onto diff-mode highlight.
+ if (cul_attr) {
+ line_attr = 0 != line_attr_lowprio // Low-priority CursorLine
+ ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr),
+ hl_get_underline())
+ : hl_combine_attr(line_attr, cul_attr);
+ }
+ }
+
+ // Decide which of the highlight attributes to use.
+ attr_pri = true;
+
+ if (area_attr != 0) {
+ char_attr = hl_combine_attr(line_attr, area_attr);
+ if (!highlight_match) {
+ // let search highlight show in Visual area if possible
+ char_attr = hl_combine_attr(search_attr, char_attr);
+ }
+ } else if (search_attr != 0) {
+ char_attr = hl_combine_attr(line_attr, search_attr);
+ } else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL)
+ || vcol < fromcol || vcol_prev < fromcol_prev
+ || vcol >= tocol)) {
+ // Use line_attr when not in the Visual or 'incsearch' area
+ // (area_attr may be 0 when "noinvcur" is set).
+ char_attr = line_attr;
+ } else {
+ attr_pri = false;
+ if (has_syntax) {
+ char_attr = syntax_attr;
+ } else {
+ char_attr = 0;
+ }
+ }
+ }
+
+ // Get the next character to put on the screen.
+ //
+ // The "p_extra" points to the extra stuff that is inserted to
+ // represent special characters (non-printable stuff) and other
+ // things. When all characters are the same, c_extra is used.
+ // If c_final is set, it will compulsorily be used at the end.
+ // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past
+ // "p_extra[n_extra]".
+ // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
+ if (n_extra > 0) {
+ if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
+ c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
+ mb_c = c; // doesn't handle non-utf-8 multi-byte!
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false;
+ }
+ } else {
+ assert(p_extra != NULL);
+ c = *p_extra;
+ mb_c = c;
+ // If the UTF-8 character is more than one byte:
+ // Decode it into "mb_c".
+ mb_l = utfc_ptr2len((char *)p_extra);
+ mb_utf8 = false;
+ if (mb_l > n_extra) {
+ mb_l = 1;
+ } else if (mb_l > 1) {
+ mb_c = utfc_ptr2char(p_extra, u8cc);
+ mb_utf8 = true;
+ c = 0xc0;
+ }
+ if (mb_l == 0) { // at the NUL at end-of-line
+ mb_l = 1;
+ }
+
+ // If a double-width char doesn't fit display a '>' in the last column.
+ if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1))
+ && utf_char2cells(mb_c) == 2) {
+ c = '>';
+ mb_c = c;
+ mb_l = 1;
+ (void)mb_l;
+ multi_attr = win_hl_attr(wp, HLF_AT);
+
+ if (cul_attr) {
+ multi_attr = 0 != line_attr_lowprio
+ ? hl_combine_attr(cul_attr, multi_attr)
+ : hl_combine_attr(multi_attr, cul_attr);
+ }
+
+ // put the pointer back to output the double-width
+ // character at the start of the next line.
+ n_extra++;
+ p_extra--;
+ } else {
+ n_extra -= mb_l - 1;
+ p_extra += mb_l - 1;
+ }
+ p_extra++;
+ }
+ n_extra--;
+ } else if (foldinfo.fi_lines > 0) {
+ // skip writing the buffer line itself
+ c = NUL;
+ XFREE_CLEAR(p_extra_free);
+ } else {
+ int c0;
+
+ XFREE_CLEAR(p_extra_free);
+
+ // Get a character from the line itself.
+ c0 = c = *ptr;
+ mb_c = c;
+ // If the UTF-8 character is more than one byte: Decode it
+ // into "mb_c".
+ mb_l = utfc_ptr2len((char *)ptr);
+ mb_utf8 = false;
+ if (mb_l > 1) {
+ mb_c = utfc_ptr2char(ptr, u8cc);
+ // Overlong encoded ASCII or ASCII with composing char
+ // is displayed normally, except a NUL.
+ if (mb_c < 0x80) {
+ c0 = c = mb_c;
+ }
+ mb_utf8 = true;
+
+ // At start of the line we can have a composing char.
+ // Draw it as a space with a composing char.
+ if (utf_iscomposing(mb_c)) {
+ int i;
+
+ for (i = MAX_MCO - 1; i > 0; i--) {
+ u8cc[i] = u8cc[i - 1];
+ }
+ u8cc[0] = mb_c;
+ mb_c = ' ';
+ }
+ }
+
+ if ((mb_l == 1 && c >= 0x80)
+ || (mb_l >= 1 && mb_c == 0)
+ || (mb_l > 1 && (!vim_isprintc(mb_c)))) {
+ // Illegal UTF-8 byte: display as <xx>.
+ // Non-BMP character : display as ? or fullwidth ?.
+ transchar_hex((char *)extra, mb_c);
+ if (wp->w_p_rl) { // reverse
+ rl_mirror(extra);
+ }
+
+ p_extra = extra;
+ c = *p_extra;
+ mb_c = mb_ptr2char_adv((const char_u **)&p_extra);
+ mb_utf8 = (c >= 0x80);
+ n_extra = (int)STRLEN(p_extra);
+ c_extra = NUL;
+ c_final = NUL;
+ if (area_attr == 0 && search_attr == 0) {
+ n_attr = n_extra + 1;
+ extra_attr = win_hl_attr(wp, HLF_8);
+ saved_attr2 = char_attr; // save current attr
+ }
+ } else if (mb_l == 0) { // at the NUL at end-of-line
+ mb_l = 1;
+ } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) {
+ // Do Arabic shaping.
+ int pc, pc1, nc;
+ int pcc[MAX_MCO];
+
+ // The idea of what is the previous and next
+ // character depends on 'rightleft'.
+ if (wp->w_p_rl) {
+ pc = prev_c;
+ pc1 = prev_c1;
+ nc = utf_ptr2char((char *)ptr + mb_l);
+ prev_c1 = u8cc[0];
+ } else {
+ pc = utfc_ptr2char(ptr + mb_l, pcc);
+ nc = prev_c;
+ pc1 = pcc[0];
+ }
+ prev_c = mb_c;
+
+ mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc);
+ } else {
+ prev_c = mb_c;
+ }
+ // If a double-width char doesn't fit display a '>' in the
+ // last column; the character is displayed at the start of the
+ // next line.
+ if ((wp->w_p_rl ? (col <= 0) :
+ (col >= grid->cols - 1))
+ && utf_char2cells(mb_c) == 2) {
+ c = '>';
+ mb_c = c;
+ mb_utf8 = false;
+ mb_l = 1;
+ multi_attr = win_hl_attr(wp, HLF_AT);
+ // Put pointer back so that the character will be
+ // displayed at the start of the next line.
+ ptr--;
+ did_decrement_ptr = true;
+ } else if (*ptr != NUL) {
+ ptr += mb_l - 1;
+ }
+
+ // If a double-width char doesn't fit at the left side display a '<' in
+ // the first column. Don't do this for unprintable characters.
+ if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
+ n_extra = 1;
+ c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
+ c = ' ';
+ if (area_attr == 0 && search_attr == 0) {
+ n_attr = n_extra + 1;
+ extra_attr = win_hl_attr(wp, HLF_AT);
+ saved_attr2 = char_attr; // save current attr
+ }
+ mb_c = c;
+ mb_utf8 = false;
+ mb_l = 1;
+ }
+ ptr++;
+
+ if (extra_check) {
+ bool can_spell = true;
+
+ // Get syntax attribute, unless still at the start of the line
+ // (double-wide char that doesn't fit).
+ v = (ptr - line);
+ if (has_syntax && v > 0) {
+ // Get the syntax attribute for the character. If there
+ // is an error, disable syntax highlighting.
+ save_did_emsg = did_emsg;
+ did_emsg = false;
+
+ syntax_attr = get_syntax_attr((colnr_T)v - 1,
+ has_spell ? &can_spell : NULL, false);
+
+ if (did_emsg) {
+ wp->w_s->b_syn_error = true;
+ has_syntax = false;
+ } else {
+ did_emsg = save_did_emsg;
+ }
+
+ if (wp->w_s->b_syn_slow) {
+ has_syntax = false;
+ }
+
+ // Need to get the line again, a multi-line regexp may
+ // have made it invalid.
+ line = ml_get_buf(wp->w_buffer, lnum, false);
+ ptr = line + v;
+
+ if (!attr_pri) {
+ if (cul_attr) {
+ char_attr = 0 != line_attr_lowprio
+ ? hl_combine_attr(cul_attr, syntax_attr)
+ : hl_combine_attr(syntax_attr, cul_attr);
+ } else {
+ char_attr = syntax_attr;
+ }
+ } else {
+ char_attr = hl_combine_attr(syntax_attr, char_attr);
+ }
+ // no concealing past the end of the line, it interferes
+ // with line highlighting.
+ if (c == NUL) {
+ syntax_flags = 0;
+ } else {
+ syntax_flags = get_syntax_info(&syntax_seqnr);
+ }
+ } else if (!attr_pri) {
+ char_attr = 0;
+ }
+
+ // Check spelling (unless at the end of the line).
+ // Only do this when there is no syntax highlighting, the
+ // @Spell cluster is not used or the current syntax item
+ // contains the @Spell cluster.
+ v = (ptr - line);
+ if (has_spell && v >= word_end && v > cur_checked_col) {
+ spell_attr = 0;
+ if (!attr_pri) {
+ char_attr = syntax_attr;
+ }
+ if (c != 0 && (!has_syntax || can_spell)) {
+ char_u *prev_ptr;
+ char_u *p;
+ int len;
+ hlf_T spell_hlf = HLF_COUNT;
+ prev_ptr = ptr - mb_l;
+ v -= mb_l - 1;
+
+ // Use nextline[] if possible, it has the start of the
+ // next line concatenated.
+ if ((prev_ptr - line) - nextlinecol >= 0) {
+ p = nextline + ((prev_ptr - line) - nextlinecol);
+ } else {
+ p = prev_ptr;
+ }
+ cap_col -= (int)(prev_ptr - line);
+ size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange);
+ assert(tmplen <= INT_MAX);
+ len = (int)tmplen;
+ word_end = (int)v + len;
+
+ // In Insert mode only highlight a word that
+ // doesn't touch the cursor.
+ if (spell_hlf != HLF_COUNT
+ && (State & MODE_INSERT)
+ && wp->w_cursor.lnum == lnum
+ && wp->w_cursor.col >=
+ (colnr_T)(prev_ptr - line)
+ && wp->w_cursor.col < (colnr_T)word_end) {
+ spell_hlf = HLF_COUNT;
+ spell_redraw_lnum = lnum;
+ }
+
+ if (spell_hlf == HLF_COUNT && p != prev_ptr
+ && (p - nextline) + len > nextline_idx) {
+ // Remember that the good word continues at the
+ // start of the next line.
+ checked_lnum = lnum + 1;
+ checked_col = (int)((p - nextline) + len - nextline_idx);
+ }
+
+ // Turn index into actual attributes.
+ if (spell_hlf != HLF_COUNT) {
+ spell_attr = highlight_attr[spell_hlf];
+ }
+
+ if (cap_col > 0) {
+ if (p != prev_ptr
+ && (p - nextline) + cap_col >= nextline_idx) {
+ // Remember that the word in the next line
+ // must start with a capital.
+ capcol_lnum = lnum + 1;
+ cap_col = (int)((p - nextline) + cap_col
+ - nextline_idx);
+ } else {
+ // Compute the actual column.
+ cap_col += (int)(prev_ptr - line);
+ }
+ }
+ }
+ }
+ if (spell_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, spell_attr);
+ } else {
+ char_attr = hl_combine_attr(spell_attr, char_attr);
+ }
+ }
+
+ if (wp->w_buffer->terminal) {
+ char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
+ }
+
+ if (has_decor && v > 0) {
+ bool selected = (area_active || (area_highlighting && noinvcur
+ && (colnr_T)vcol == wp->w_virtcol));
+ int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off,
+ selected, &decor_state);
+ if (extmark_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, extmark_attr);
+ } else {
+ char_attr = hl_combine_attr(extmark_attr, char_attr);
+ }
+ }
+
+ decor_conceal = decor_state.conceal;
+ if (decor_conceal && decor_state.conceal_char) {
+ decor_conceal = 2; // really??
+ }
+ }
+
+ // Found last space before word: check for line break.
+ if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
+ && !vim_isbreak((int)(*ptr))) {
+ int mb_off = utf_head_off(line, ptr - 1);
+ char_u *p = ptr - (mb_off + 1);
+ // TODO(neovim): is passing p for start of the line OK?
+ n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
+
+ // We have just drawn the showbreak value, no need to add
+ // space for it again.
+ if (vcol == vcol_sbr) {
+ n_extra -= mb_charlen(get_showbreak_value(wp));
+ if (n_extra < 0) {
+ n_extra = 0;
+ }
+ }
+
+ if (c == TAB && n_extra + col > grid->cols) {
+ n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array) - 1;
+ }
+ c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
+ c_final = NUL;
+ if (ascii_iswhite(c)) {
+ if (c == TAB) {
+ // See "Tab alignment" below.
+ FIX_FOR_BOGUSCOLS;
+ }
+ if (!wp->w_p_list) {
+ c = ' ';
+ }
+ }
+ }
+
+ in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
+ if (!in_multispace) {
+ multispace_pos = 0;
+ }
+
+ // 'list': Change char 160 to 'nbsp' and space to 'space'.
+ // But not when the character is followed by a composing
+ // character (use mb_l to check that).
+ if (wp->w_p_list
+ && ((((c == 160 && mb_l == 1)
+ || (mb_utf8
+ && ((mb_c == 160 && mb_l == 2)
+ || (mb_c == 0x202f && mb_l == 3))))
+ && wp->w_p_lcs_chars.nbsp)
+ || (c == ' '
+ && mb_l == 1
+ && (wp->w_p_lcs_chars.space
+ || (in_multispace && wp->w_p_lcs_chars.multispace != NULL))
+ && ptr - line >= leadcol
+ && ptr - line <= trailcol))) {
+ if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) {
+ c = wp->w_p_lcs_chars.multispace[multispace_pos++];
+ if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ } else {
+ c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
+ }
+ n_attr = 1;
+ extra_attr = win_hl_attr(wp, HLF_0);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false;
+ }
+ }
+
+ if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol)
+ || (leadcol != 0 && ptr < line + leadcol))) {
+ if (leadcol != 0 && in_multispace && ptr < line + leadcol
+ && wp->w_p_lcs_chars.leadmultispace != NULL) {
+ c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
+ if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
+ c = wp->w_p_lcs_chars.trail;
+ } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
+ c = wp->w_p_lcs_chars.lead;
+ } else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
+ c = wp->w_p_lcs_chars.space;
+ }
+
+ n_attr = 1;
+ extra_attr = win_hl_attr(wp, HLF_0);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false;
+ }
+ }
+ }
+
+ // Handling of non-printable characters.
+ if (!vim_isprintc(c)) {
+ // when getting a character from the file, we may have to
+ // turn it into something else on the way to putting it on the screen.
+ if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
+ int tab_len = 0;
+ long vcol_adjusted = vcol; // removed showbreak length
+ char_u *const sbr = get_showbreak_value(wp);
+
+ // Only adjust the tab_len, when at the first column after the
+ // showbreak value was drawn.
+ if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) {
+ vcol_adjusted = vcol - mb_charlen(sbr);
+ }
+ // tab amount depends on current column
+ tab_len = tabstop_padding((colnr_T)vcol_adjusted,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array) - 1;
+
+ if (!wp->w_p_lbr || !wp->w_p_list) {
+ n_extra = tab_len;
+ } else {
+ char_u *p;
+ int i;
+ int saved_nextra = n_extra;
+
+ if (vcol_off > 0) {
+ // there are characters to conceal
+ tab_len += vcol_off;
+ }
+ // boguscols before FIX_FOR_BOGUSCOLS macro from above.
+ if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
+ && n_extra > tab_len) {
+ tab_len += n_extra - tab_len;
+ }
+
+ // If n_extra > 0, it gives the number of chars
+ // to use for a tab, else we need to calculate the width
+ // for a tab.
+ int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2));
+ if (wp->w_p_lcs_chars.tab3) {
+ len += utf_char2len(wp->w_p_lcs_chars.tab3);
+ }
+ if (n_extra > 0) {
+ len += n_extra - tab_len;
+ }
+ c = wp->w_p_lcs_chars.tab1;
+ p = xmalloc((size_t)len + 1);
+ memset(p, ' ', (size_t)len);
+ p[len] = NUL;
+ xfree(p_extra_free);
+ p_extra_free = p;
+ for (i = 0; i < tab_len; i++) {
+ if (*p == NUL) {
+ tab_len = i;
+ break;
+ }
+ int lcs = wp->w_p_lcs_chars.tab2;
+
+ // if tab3 is given, use it for the last char
+ if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
+ lcs = wp->w_p_lcs_chars.tab3;
+ }
+ p += utf_char2bytes(lcs, (char *)p);
+ n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
+ }
+ p_extra = p_extra_free;
+
+ // n_extra will be increased by FIX_FOX_BOGUSCOLS
+ // macro below, so need to adjust for that here
+ if (vcol_off > 0) {
+ n_extra -= vcol_off;
+ }
+ }
+
+ {
+ int vc_saved = vcol_off;
+
+ // Tab alignment should be identical regardless of
+ // 'conceallevel' value. So tab compensates of all
+ // previous concealed characters, and thus resets
+ // vcol_off and boguscols accumulated so far in the
+ // line. Note that the tab can be longer than
+ // 'tabstop' when there are concealed characters.
+ FIX_FOR_BOGUSCOLS;
+
+ // Make sure, the highlighting for the tab char will be
+ // correctly set further below (effectively reverts the
+ // FIX_FOR_BOGSUCOLS macro).
+ if (n_extra == tab_len + vc_saved && wp->w_p_list
+ && wp->w_p_lcs_chars.tab1) {
+ tab_len += vc_saved;
+ }
+ }
+
+ mb_utf8 = false; // don't draw as UTF-8
+ if (wp->w_p_list) {
+ c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
+ ? wp->w_p_lcs_chars.tab3
+ : wp->w_p_lcs_chars.tab1;
+ if (wp->w_p_lbr) {
+ c_extra = NUL; // using p_extra from above
+ } else {
+ c_extra = wp->w_p_lcs_chars.tab2;
+ }
+ c_final = wp->w_p_lcs_chars.tab3;
+ n_attr = tab_len + 1;
+ extra_attr = win_hl_attr(wp, HLF_0);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ }
+ } else {
+ c_final = NUL;
+ c_extra = ' ';
+ c = ' ';
+ }
+ } else if (c == NUL
+ && (wp->w_p_list
+ || ((fromcol >= 0 || fromcol_prev >= 0)
+ && tocol > vcol
+ && VIsual_mode != Ctrl_V
+ && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))
+ && !(noinvcur
+ && lnum == wp->w_cursor.lnum
+ && (colnr_T)vcol == wp->w_virtcol)))
+ && lcs_eol_one > 0) {
+ // Display a '$' after the line or highlight an extra
+ // character if the line break is included.
+ // For a diff line the highlighting continues after the "$".
+ if (diff_hlf == (hlf_T)0
+ && line_attr == 0
+ && line_attr_lowprio == 0) {
+ // In virtualedit, visual selections may extend beyond end of line
+ if (area_highlighting && virtual_active()
+ && tocol != MAXCOL && vcol < tocol) {
+ n_extra = 0;
+ } else {
+ p_extra = at_end_str;
+ n_extra = 1;
+ c_extra = NUL;
+ c_final = NUL;
+ }
+ }
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
+ c = wp->w_p_lcs_chars.eol;
+ } else {
+ c = ' ';
+ }
+ lcs_eol_one = -1;
+ ptr--; // put it back at the NUL
+ extra_attr = win_hl_attr(wp, HLF_AT);
+ n_attr = 1;
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false; // don't draw as UTF-8
+ }
+ } else if (c != NUL) {
+ p_extra = transchar_buf(wp->w_buffer, c);
+ if (n_extra == 0) {
+ n_extra = byte2cells(c) - 1;
+ }
+ if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
+ rl_mirror(p_extra); // reverse "<12>"
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ if (wp->w_p_lbr) {
+ char_u *p;
+
+ c = *p_extra;
+ p = xmalloc((size_t)n_extra + 1);
+ memset(p, ' ', (size_t)n_extra);
+ STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf)
+ p[n_extra] = NUL;
+ xfree(p_extra_free);
+ p_extra_free = p_extra = p;
+ } else {
+ n_extra = byte2cells(c) - 1;
+ c = *p_extra++;
+ }
+ n_attr = n_extra + 1;
+ extra_attr = win_hl_attr(wp, HLF_8);
+ saved_attr2 = char_attr; // save current attr
+ mb_utf8 = false; // don't draw as UTF-8
+ } else if (VIsual_active
+ && (VIsual_mode == Ctrl_V || VIsual_mode == 'v')
+ && virtual_active()
+ && tocol != MAXCOL
+ && vcol < tocol
+ && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) {
+ c = ' ';
+ ptr--; // put it back at the NUL
+ }
+ }
+
+ if (wp->w_p_cole > 0
+ && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp))
+ && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)
+ && !(lnum_in_visual_area && vim_strchr((char *)wp->w_p_cocu, 'v') == NULL)) {
+ char_attr = conceal_attr;
+ if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
+ || has_match_conc > 1 || decor_conceal > 1)
+ && (syn_get_sub_char() != NUL
+ || (has_match_conc && match_conc)
+ || (decor_conceal && decor_state.conceal_char)
+ || wp->w_p_cole == 1)
+ && wp->w_p_cole != 3) {
+ // First time at this concealed item: display one
+ // character.
+ if (has_match_conc && match_conc) {
+ c = match_conc;
+ } else if (decor_conceal && decor_state.conceal_char) {
+ c = decor_state.conceal_char;
+ if (decor_state.conceal_attr) {
+ char_attr = decor_state.conceal_attr;
+ }
+ } else if (syn_get_sub_char() != NUL) {
+ c = syn_get_sub_char();
+ } else if (wp->w_p_lcs_chars.conceal != NUL) {
+ c = wp->w_p_lcs_chars.conceal;
+ } else {
+ c = ' ';
+ }
+
+ prev_syntax_id = syntax_seqnr;
+
+ if (n_extra > 0) {
+ vcol_off += n_extra;
+ }
+ vcol += n_extra;
+ if (wp->w_p_wrap && n_extra > 0) {
+ if (wp->w_p_rl) {
+ col -= n_extra;
+ boguscols -= n_extra;
+ } else {
+ boguscols += n_extra;
+ col += n_extra;
+ }
+ }
+ n_extra = 0;
+ n_attr = 0;
+ } else if (n_skip == 0) {
+ is_concealing = true;
+ n_skip = 1;
+ }
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false; // don't draw as UTF-8
+ }
+ } else {
+ prev_syntax_id = 0;
+ is_concealing = false;
+ }
+
+ if (n_skip > 0 && did_decrement_ptr) {
+ // not showing the '>', put pointer back to avoid getting stuck
+ ptr++;
+ }
+ } // end of printing from buffer content
+
+ // In the cursor line and we may be concealing characters: correct
+ // the cursor column when we reach its position.
+ if (!did_wcol && draw_state == WL_LINE
+ && wp == curwin && lnum == wp->w_cursor.lnum
+ && conceal_cursor_line(wp)
+ && (int)wp->w_virtcol <= vcol + n_skip) {
+ if (wp->w_p_rl) {
+ wp->w_wcol = grid->cols - col + boguscols - 1;
+ } else {
+ wp->w_wcol = col - boguscols;
+ }
+ wp->w_wrow = row;
+ did_wcol = true;
+ wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
+ }
+
+ // Don't override visual selection highlighting.
+ if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) {
+ char_attr = hl_combine_attr(char_attr, extra_attr);
+ }
+
+ // Handle the case where we are in column 0 but not on the first
+ // character of the line and the user wants us to show us a
+ // special character (via 'listchars' option "precedes:<char>".
+ if (lcs_prec_todo != NUL
+ && wp->w_p_list
+ && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0)
+ && filler_todo <= 0
+ && draw_state > WL_NR
+ && c != NUL) {
+ c = wp->w_p_lcs_chars.prec;
+ lcs_prec_todo = NUL;
+ if (utf_char2cells(mb_c) > 1) {
+ // Double-width character being overwritten by the "precedes"
+ // character, need to fill up half the character.
+ c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
+ n_extra = 1;
+ n_attr = 2;
+ extra_attr = win_hl_attr(wp, HLF_AT);
+ }
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false; // don't draw as UTF-8
+ }
+ saved_attr3 = char_attr; // save current attr
+ char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
+ n_attr3 = 1;
+ }
+
+ // At end of the text line or just after the last character.
+ if (c == NUL && eol_hl_off == 0) {
+ // flag to indicate whether prevcol equals startcol of search_hl or
+ // one of the matches
+ bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl,
+ (long)(ptr - line) - 1);
+
+ // Invert at least one char, used for Visual and empty line or
+ // highlight match at end of line. If it's beyond the last
+ // char on the screen, just overwrite that one (tricky!) Not
+ // needed when a '$' was displayed for 'list'.
+ if (wp->w_p_lcs_chars.eol == lcs_eol_one
+ && ((area_attr != 0 && vcol == fromcol
+ && (VIsual_mode != Ctrl_V
+ || lnum == VIsual.lnum
+ || lnum == curwin->w_cursor.lnum))
+ // highlight 'hlsearch' match at end of line
+ || prevcol_hl_flag)) {
+ int n = 0;
+
+ if (wp->w_p_rl) {
+ if (col < 0) {
+ n = 1;
+ }
+ } else {
+ if (col >= grid->cols) {
+ n = -1;
+ }
+ }
+ if (n != 0) {
+ // At the window boundary, highlight the last character
+ // instead (better than nothing).
+ off += n;
+ col += n;
+ } else {
+ // Add a blank character to highlight.
+ schar_from_ascii(linebuf_char[off], ' ');
+ }
+ if (area_attr == 0 && !has_fold) {
+ // Use attributes from match with highest priority among
+ // 'search_hl' and the match list.
+ get_search_match_hl(wp, &screen_search_hl, (long)(ptr - line), &char_attr);
+ }
+
+ int eol_attr = char_attr;
+ if (cul_attr) {
+ eol_attr = hl_combine_attr(cul_attr, eol_attr);
+ }
+ linebuf_attr[off] = eol_attr;
+ if (wp->w_p_rl) {
+ col--;
+ off--;
+ } else {
+ col++;
+ off++;
+ }
+ vcol++;
+ eol_hl_off = 1;
+ }
+ // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
+ if (wp->w_p_wrap) {
+ v = wp->w_skipcol;
+ } else {
+ v = wp->w_leftcol;
+ }
+
+ // check if line ends before left margin
+ if (vcol < v + col - win_col_off(wp)) {
+ vcol = v + col - win_col_off(wp);
+ }
+ // Get rid of the boguscols now, we want to draw until the right
+ // edge for 'cursorcolumn'.
+ col -= boguscols;
+ // boguscols = 0; // Disabled because value never read after this
+
+ if (draw_color_col) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ bool has_virttext = false;
+ // Make sure alignment is the same regardless
+ // if listchars=eol:X is used or not.
+ int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0
+ ? 1 : 0);
+
+ if (has_decor) {
+ has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr,
+ col + eol_skip);
+ }
+
+ if (((wp->w_p_cuc
+ && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
+ && (int)wp->w_virtcol <
+ (long)grid->cols * (row - startrow + 1) + v
+ && lnum != wp->w_cursor.lnum)
+ || draw_color_col || line_attr_lowprio || line_attr
+ || diff_hlf != (hlf_T)0 || has_virttext)) {
+ int rightmost_vcol = 0;
+ int i;
+
+ if (wp->w_p_cuc) {
+ rightmost_vcol = wp->w_virtcol;
+ }
+
+ if (draw_color_col) {
+ // determine rightmost colorcolumn to possibly draw
+ for (i = 0; color_cols[i] >= 0; i++) {
+ if (rightmost_vcol < color_cols[i]) {
+ rightmost_vcol = color_cols[i];
+ }
+ }
+ }
+
+ int cuc_attr = win_hl_attr(wp, HLF_CUC);
+ int mc_attr = win_hl_attr(wp, HLF_MC);
+
+ int diff_attr = 0;
+ if (diff_hlf == HLF_TXD) {
+ diff_hlf = HLF_CHD;
+ }
+ if (diff_hlf != 0) {
+ diff_attr = win_hl_attr(wp, (int)diff_hlf);
+ }
+
+ int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr);
+ if (base_attr || line_attr || has_virttext) {
+ rightmost_vcol = INT_MAX;
+ }
+
+ int col_stride = wp->w_p_rl ? -1 : 1;
+
+ while (wp->w_p_rl ? col >= 0 : col < grid->cols) {
+ schar_from_ascii(linebuf_char[off], ' ');
+ col += col_stride;
+ if (draw_color_col) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ int col_attr = base_attr;
+
+ if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
+ col_attr = cuc_attr;
+ } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ col_attr = mc_attr;
+ }
+
+ col_attr = hl_combine_attr(col_attr, line_attr);
+
+ linebuf_attr[off] = col_attr;
+ off += col_stride;
+
+ if (VCOL_HLC >= rightmost_vcol) {
+ break;
+ }
+
+ vcol += 1;
+ }
+ }
+
+ // TODO(bfredl): integrate with the common beyond-the-end-loop
+ if (wp->w_buffer->terminal) {
+ // terminal buffers may need to highlight beyond the end of the
+ // logical line
+ int n = wp->w_p_rl ? -1 : 1;
+ while (col >= 0 && col < grid->cols) {
+ schar_from_ascii(linebuf_char[off], ' ');
+ linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol];
+ off += n;
+ vcol += n;
+ col += n;
+ }
+ }
+
+ draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
+ grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp, bg_attr, false);
+ row++;
+
+ // Update w_cline_height and w_cline_folded if the cursor line was
+ // updated (saves a call to plines_win() later).
+ if (wp == curwin && lnum == curwin->w_cursor.lnum) {
+ curwin->w_cline_row = startrow;
+ curwin->w_cline_height = row - startrow;
+ curwin->w_cline_folded = foldinfo.fi_lines > 0;
+ curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
+ conceal_cursor_used = conceal_cursor_line(curwin);
+ }
+ break;
+ }
+
+ // Show "extends" character from 'listchars' if beyond the line end and
+ // 'list' is set.
+ if (wp->w_p_lcs_chars.ext != NUL
+ && draw_state == WL_LINE
+ && wp->w_p_list
+ && !wp->w_p_wrap
+ && filler_todo <= 0
+ && (wp->w_p_rl ? col == 0 : col == grid->cols - 1)
+ && !has_fold
+ && (*ptr != NUL
+ || lcs_eol_one > 0
+ || (n_extra && (c_extra != NUL || *p_extra != NUL)))) {
+ c = wp->w_p_lcs_chars.ext;
+ char_attr = win_hl_attr(wp, HLF_AT);
+ mb_c = c;
+ if (utf_char2len(c) > 1) {
+ mb_utf8 = true;
+ u8cc[0] = 0;
+ c = 0xc0;
+ } else {
+ mb_utf8 = false;
+ }
+ }
+
+ // advance to the next 'colorcolumn'
+ if (draw_color_col) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ // Highlight the cursor column if 'cursorcolumn' is set. But don't
+ // highlight the cursor position itself.
+ // Also highlight the 'colorcolumn' if it is different than
+ // 'cursorcolumn'
+ // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'
+ // options are set
+ vcol_save_attr = -1;
+ if ((draw_state == WL_LINE
+ || draw_state == WL_BRI
+ || draw_state == WL_SBR)
+ && !lnum_in_visual_area
+ && search_attr == 0
+ && area_attr == 0
+ && filler_todo <= 0) {
+ if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
+ && lnum != wp->w_cursor.lnum) {
+ vcol_save_attr = char_attr;
+ char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
+ } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ vcol_save_attr = char_attr;
+ char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
+ }
+ }
+
+ // Apply lowest-priority line attr now, so everything can override it.
+ if (draw_state == WL_LINE) {
+ char_attr = hl_combine_attr(line_attr_lowprio, char_attr);
+ }
+
+ // Store character to be displayed.
+ // Skip characters that are left of the screen for 'nowrap'.
+ vcol_prev = vcol;
+ if (draw_state < WL_LINE || n_skip <= 0) {
+ //
+ // Store the character.
+ //
+ if (wp->w_p_rl && utf_char2cells(mb_c) > 1) {
+ // A double-wide character is: put first half in left cell.
+ off--;
+ col--;
+ }
+ if (mb_utf8) {
+ schar_from_cc(linebuf_char[off], mb_c, u8cc);
+ } else {
+ schar_from_ascii(linebuf_char[off], (char)c);
+ }
+ if (multi_attr) {
+ linebuf_attr[off] = multi_attr;
+ multi_attr = 0;
+ } else {
+ linebuf_attr[off] = char_attr;
+ }
+
+ if (utf_char2cells(mb_c) > 1) {
+ // Need to fill two screen columns.
+ off++;
+ col++;
+ // UTF-8: Put a 0 in the second screen char.
+ linebuf_char[off][0] = 0;
+ if (draw_state > WL_NR && filler_todo <= 0) {
+ vcol++;
+ }
+ // When "tocol" is halfway through a character, set it to the end of
+ // the character, otherwise highlighting won't stop.
+ if (tocol == vcol) {
+ tocol++;
+ }
+ if (wp->w_p_rl) {
+ // now it's time to backup one cell
+ off--;
+ col--;
+ }
+ }
+ if (wp->w_p_rl) {
+ off--;
+ col--;
+ } else {
+ off++;
+ col++;
+ }
+ } else if (wp->w_p_cole > 0 && is_concealing) {
+ n_skip--;
+ vcol_off++;
+ if (n_extra > 0) {
+ vcol_off += n_extra;
+ }
+ if (wp->w_p_wrap) {
+ // Special voodoo required if 'wrap' is on.
+ //
+ // Advance the column indicator to force the line
+ // drawing to wrap early. This will make the line
+ // take up the same screen space when parts are concealed,
+ // so that cursor line computations aren't messed up.
+ //
+ // To avoid the fictitious advance of 'col' causing
+ // trailing junk to be written out of the screen line
+ // we are building, 'boguscols' keeps track of the number
+ // of bad columns we have advanced.
+ if (n_extra > 0) {
+ vcol += n_extra;
+ if (wp->w_p_rl) {
+ col -= n_extra;
+ boguscols -= n_extra;
+ } else {
+ col += n_extra;
+ boguscols += n_extra;
+ }
+ n_extra = 0;
+ n_attr = 0;
+ }
+
+ if (utf_char2cells(mb_c) > 1) {
+ // Need to fill two screen columns.
+ if (wp->w_p_rl) {
+ boguscols--;
+ col--;
+ } else {
+ boguscols++;
+ col++;
+ }
+ }
+
+ if (wp->w_p_rl) {
+ boguscols--;
+ col--;
+ } else {
+ boguscols++;
+ col++;
+ }
+ } else {
+ if (n_extra > 0) {
+ vcol += n_extra;
+ n_extra = 0;
+ n_attr = 0;
+ }
+ }
+ } else {
+ n_skip--;
+ }
+
+ // Only advance the "vcol" when after the 'number' or 'relativenumber'
+ // column.
+ if (draw_state > WL_NR
+ && filler_todo <= 0) {
+ vcol++;
+ }
+
+ if (vcol_save_attr >= 0) {
+ char_attr = vcol_save_attr;
+ }
+
+ // restore attributes after "predeces" in 'listchars'
+ if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) {
+ char_attr = saved_attr3;
+ }
+
+ // restore attributes after last 'listchars' or 'number' char
+ if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) {
+ char_attr = saved_attr2;
+ }
+
+ // At end of screen line and there is more to come: Display the line
+ // so far. If there is no more to display it is caught above.
+ if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols))
+ && foldinfo.fi_lines == 0
+ && (draw_state != WL_LINE
+ || *ptr != NUL
+ || filler_todo > 0
+ || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
+ && p_extra != at_end_str)
+ || (n_extra != 0
+ && (c_extra != NUL || *p_extra != NUL)))) {
+ bool wrap = wp->w_p_wrap // Wrapping enabled.
+ && filler_todo <= 0 // Not drawing diff filler lines.
+ && lcs_eol_one != -1 // Haven't printed the lcs_eol character.
+ && row != endrow - 1 // Not the last line being displayed.
+ && (grid->cols == Columns // Window spans the width of the screen,
+ || ui_has(kUIMultigrid)) // or has dedicated grid.
+ && !wp->w_p_rl; // Not right-to-left.
+
+ int draw_col = col - boguscols;
+ if (filler_todo > 0) {
+ int index = filler_todo - (filler_lines - n_virt_lines);
+ if (index > 0) {
+ int i = (int)kv_size(virt_lines) - index;
+ assert(i >= 0);
+ int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset;
+ draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line,
+ kHlModeReplace, grid->cols, offset);
+ }
+ } else {
+ draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
+ }
+
+ grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap);
+ if (wrap) {
+ ScreenGrid *current_grid = grid;
+ int current_row = row, dummy_col = 0; // dummy_col unused
+ grid_adjust(&current_grid, &current_row, &dummy_col);
+
+ // Force a redraw of the first column of the next line.
+ current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
+
+ // Remember that the line wraps, used for modeless copy.
+ current_grid->line_wraps[current_row] = true;
+ }
+
+ boguscols = 0;
+ row++;
+
+ // When not wrapping and finished diff lines, or when displayed
+ // '$' and highlighting until last column, break here.
+ if ((!wp->w_p_wrap && filler_todo <= 0) || lcs_eol_one == -1) {
+ break;
+ }
+
+ // When the window is too narrow draw all "@" lines.
+ if (draw_state != WL_LINE && filler_todo <= 0) {
+ win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT);
+ row = endrow;
+ }
+
+ // When line got too long for screen break here.
+ if (row == endrow) {
+ row++;
+ break;
+ }
+
+ col = 0;
+ off = 0;
+ if (wp->w_p_rl) {
+ col = grid->cols - 1; // col is not used if breaking!
+ off += col;
+ }
+
+ // reset the drawing state for the start of a wrapped line
+ draw_state = WL_START;
+ saved_n_extra = n_extra;
+ saved_p_extra = p_extra;
+ saved_c_extra = c_extra;
+ saved_c_final = c_final;
+ saved_char_attr = char_attr;
+ n_extra = 0;
+ lcs_prec_todo = wp->w_p_lcs_chars.prec;
+ if (filler_todo <= 0) {
+ need_showbreak = true;
+ }
+ filler_todo--;
+ // When the filler lines are actually below the last line of the
+ // file, don't draw the line itself, break here.
+ if (filler_todo == 0 && (wp->w_botfill || end_fill)) {
+ break;
+ }
+ }
+ } // for every character in the line
+
+ // After an empty line check first word for capital.
+ if (*skipwhite((char *)line) == NUL) {
+ capcol_lnum = lnum + 1;
+ cap_col = 0;
+ }
+
+ kv_destroy(virt_lines);
+ xfree(p_extra_free);
+ return row;
+}
diff --git a/src/nvim/drawline.h b/src/nvim/drawline.h
new file mode 100644
index 0000000000..e50969983e
--- /dev/null
+++ b/src/nvim/drawline.h
@@ -0,0 +1,24 @@
+#ifndef NVIM_DRAWLINE_H
+#define NVIM_DRAWLINE_H
+
+#include "nvim/decoration_provider.h"
+#include "nvim/fold.h"
+#include "nvim/screen.h"
+
+// Maximum columns for terminal highlight attributes
+#define TERM_ATTRS_MAX 1024
+
+typedef struct {
+ NS ns_id;
+ uint64_t mark_id;
+ int win_row;
+ int win_col;
+} WinExtmark;
+EXTERN kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE);
+
+EXTERN bool conceal_cursor_used INIT(= false);
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawline.h.generated.h"
+#endif
+#endif // NVIM_DRAWLINE_H
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
new file mode 100644
index 0000000000..8cd1bdddd8
--- /dev/null
+++ b/src/nvim/drawscreen.c
@@ -0,0 +1,2325 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// drawscreen.c: Code for updating all the windows on the screen.
+// This is the top level, drawline.c is the middle and grid.c/screen.c the lower level.
+
+// update_screen() is the function that updates all windows and status lines.
+// It is called from the main loop when must_redraw is non-zero. It may be
+// called from other places when an immediate screen update is needed.
+//
+// The part of the buffer that is displayed in a window is set with:
+// - w_topline (first buffer line in window)
+// - w_topfill (filler lines above the first line)
+// - w_leftcol (leftmost window cell in window),
+// - w_skipcol (skipped window cells of first line)
+//
+// Commands that only move the cursor around in a window, do not need to take
+// action to update the display. The main loop will check if w_topline is
+// valid and update it (scroll the window) when needed.
+//
+// Commands that scroll a window change w_topline and must call
+// check_cursor() to move the cursor into the visible part of the window, and
+// call redraw_later(wp, VALID) to have the window displayed by update_screen()
+// later.
+//
+// Commands that change text in the buffer must call changed_bytes() or
+// changed_lines() to mark the area that changed and will require updating
+// later. The main loop will call update_screen(), which will update each
+// window that shows the changed buffer. This assumes text above the change
+// can remain displayed as it is. Text after the change may need updating for
+// scrolling, folding and syntax highlighting.
+//
+// Commands that change how a window is displayed (e.g., setting 'list') or
+// invalidate the contents of a window in another way (e.g., change fold
+// settings), must call redraw_later(wp, NOT_VALID) to have the whole window
+// redisplayed by update_screen() later.
+//
+// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
+// must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
+// buffer redisplayed by update_screen() later.
+//
+// Commands that change highlighting and possibly cause a scroll too must call
+// redraw_later(wp, SOME_VALID) to update the whole window but still use
+// scrolling to avoid redrawing everything. But the length of displayed lines
+// must not change, use NOT_VALID then.
+//
+// Commands that move the window position must call redraw_later(wp, NOT_VALID).
+// TODO(neovim): should minimize redrawing by scrolling when possible.
+//
+// Commands that change everything (e.g., resizing the screen) must call
+// redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
+//
+// Things that are handled indirectly:
+// - When messages scroll the screen up, msg_scrolled will be set and
+// update_screen() called to redraw.
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
+#include "nvim/ex_getln.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
+#include "nvim/insexpand.h"
+#include "nvim/match.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/plines.h"
+#include "nvim/popupmenu.h"
+#include "nvim/profile.h"
+#include "nvim/regexp.h"
+#include "nvim/syntax.h"
+#include "nvim/ui_compositor.h"
+#include "nvim/undo.h"
+#include "nvim/version.h"
+#include "nvim/window.h"
+
+/// corner value flags for hsep_connected and vsep_connected
+typedef enum {
+ WC_TOP_LEFT = 0,
+ WC_TOP_RIGHT,
+ WC_BOTTOM_LEFT,
+ WC_BOTTOM_RIGHT,
+} WindowCorner;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawscreen.c.generated.h"
+#endif
+
+static bool redraw_popupmenu = false;
+static bool msg_grid_invalid = false;
+static bool resizing = false;
+
+static char *provider_err = NULL;
+
+/// Check if the cursor line needs to be redrawn because of 'concealcursor'.
+///
+/// When cursor is moved at the same time, both lines will be redrawn regardless.
+void conceal_check_cursor_line(void)
+{
+ bool should_conceal = conceal_cursor_line(curwin);
+ if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
+ redrawWinline(curwin, curwin->w_cursor.lnum);
+ // Need to recompute cursor column, e.g., when starting Visual mode
+ // without concealing.
+ curs_columns(curwin, true);
+ }
+}
+
+/// Resize the screen to Rows and Columns.
+///
+/// Allocate default_grid.chars[] and other grid arrays.
+///
+/// There may be some time between setting Rows and Columns and (re)allocating
+/// default_grid arrays. This happens when starting up and when
+/// (manually) changing the screen size. Always use default_grid.rows and
+/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
+/// and Columns for positioning text etc. where the final size of the screen is
+/// needed.
+void screenalloc(void)
+{
+ // It's possible that we produce an out-of-memory message below, which
+ // will cause this function to be called again. To break the loop, just
+ // return here.
+ if (resizing) {
+ return;
+ }
+ resizing = true;
+
+ int retry_count = 0;
+
+retry:
+ // Allocation of the screen buffers is done only when the size changes and
+ // when Rows and Columns have been set and we have started doing full
+ // screen stuff.
+ if ((default_grid.chars != NULL
+ && Rows == default_grid.rows
+ && Columns == default_grid.cols)
+ || Rows == 0
+ || Columns == 0
+ || (!full_screen && default_grid.chars == NULL)) {
+ resizing = false;
+ return;
+ }
+
+ // Note that the window sizes are updated before reallocating the arrays,
+ // thus we must not redraw here!
+ RedrawingDisabled++;
+
+ // win_new_screensize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_set_screen_valid(false);
+ if (msg_grid.chars) {
+ msg_grid_invalid = true;
+ }
+
+ win_new_screensize(); // fit the windows in the new sized screen
+
+ comp_col(); // recompute columns for shown command and ruler
+
+ // We're changing the size of the screen.
+ // - Allocate new arrays for default_grid
+ // - Move lines from the old arrays into the new arrays, clear extra
+ // lines (unless the screen is going to be cleared).
+ // - Free the old arrays.
+ //
+ // If anything fails, make grid arrays NULL, so we don't do anything!
+ // Continuing with the old arrays may result in a crash, because the
+ // size is wrong.
+
+ grid_alloc(&default_grid, Rows, Columns, true, true);
+ StlClickDefinition *new_tab_page_click_defs =
+ xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
+
+ stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
+ xfree(tab_page_click_defs);
+
+ tab_page_click_defs = new_tab_page_click_defs;
+ tab_page_click_defs_size = Columns;
+
+ default_grid.comp_height = Rows;
+ default_grid.comp_width = Columns;
+
+ default_grid.row_offset = 0;
+ default_grid.col_offset = 0;
+ default_grid.handle = DEFAULT_GRID_HANDLE;
+
+ must_redraw = CLEAR; // need to clear the screen later
+
+ RedrawingDisabled--;
+
+ // Do not apply autocommands more than 3 times to avoid an endless loop
+ // in case applying autocommands always changes Rows or Columns.
+ if (starting == 0 && ++retry_count <= 3) {
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
+ // In rare cases, autocommands may have altered Rows or Columns,
+ // jump back to check if we need to allocate the screen again.
+ goto retry;
+ }
+
+ resizing = false;
+}
+
+void screenclear(void)
+{
+ check_for_delay(false);
+ screenalloc(); // allocate screen buffers if size changed
+
+ int i;
+
+ if (starting == NO_SCREEN || default_grid.chars == NULL) {
+ return;
+ }
+
+ // blank out the default grid
+ for (i = 0; i < default_grid.rows; i++) {
+ grid_clear_line(&default_grid, default_grid.line_offset[i],
+ default_grid.cols, true);
+ default_grid.line_wraps[i] = false;
+ }
+
+ ui_call_grid_clear(1); // clear the display
+ ui_comp_set_screen_valid(true);
+
+ ns_hl_fast = -1;
+
+ clear_cmdline = false;
+ mode_displayed = false;
+
+ redraw_all_later(NOT_VALID);
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ redraw_popupmenu = true;
+ pum_invalidate();
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ wp->w_redr_type = CLEAR;
+ }
+ }
+ if (must_redraw == CLEAR) {
+ must_redraw = NOT_VALID; // no need to clear again
+ }
+ compute_cmdrow();
+ msg_row = cmdline_row; // put cursor on last line for messages
+ msg_col = 0;
+ msg_scrolled = 0; // can't scroll back
+ msg_didany = false;
+ msg_didout = false;
+ if (HL_ATTR(HLF_MSG) > 0 && msg_use_grid() && msg_grid.chars) {
+ grid_invalidate(&msg_grid);
+ msg_grid_validate();
+ msg_grid_invalid = false;
+ clear_cmdline = true;
+ }
+}
+
+/// Set dimensions of the Nvim application "screen".
+void screen_resize(int width, int height)
+{
+ // Avoid recursiveness, can happen when setting the window size causes
+ // another window-changed signal.
+ if (updating_screen || resizing_screen) {
+ return;
+ }
+
+ if (width < 0 || height < 0) { // just checking...
+ return;
+ }
+
+ if (State == MODE_HITRETURN || State == MODE_SETWSIZE) {
+ // postpone the resizing
+ State = MODE_SETWSIZE;
+ return;
+ }
+
+ // curwin->w_buffer can be NULL when we are closing a window and the
+ // buffer has already been closed and removing a scrollbar causes a resize
+ // event. Don't resize then, it will happen after entering another buffer.
+ if (curwin->w_buffer == NULL) {
+ return;
+ }
+
+ resizing_screen = true;
+
+ Rows = height;
+ Columns = width;
+ check_screensize();
+ int max_p_ch = Rows - min_rows() + 1;
+ if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) {
+ p_ch = max_p_ch ? max_p_ch : 1;
+ }
+ height = Rows;
+ width = Columns;
+ p_lines = Rows;
+ p_columns = Columns;
+ ui_call_grid_resize(1, width, height);
+
+ /// The window layout used to be adjusted here, but it now happens in
+ /// screenalloc() (also invoked from screenclear()). That is because the
+ /// recursize "resizing_screen" check above may skip this, but not screenalloc().
+
+ if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
+ screenclear();
+ }
+
+ if (starting != NO_SCREEN) {
+ maketitle();
+
+ changed_line_abv_curs();
+ invalidate_botline();
+
+ // We only redraw when it's needed:
+ // - While at the more prompt or executing an external command, don't
+ // redraw, but position the cursor.
+ // - While editing the command line, only redraw that.
+ // - in Ex mode, don't redraw anything.
+ // - Otherwise, redraw right now, and position the cursor.
+ // Always need to call update_screen() or screenalloc(), to make
+ // sure Rows/Columns and the size of the screen is correct!
+ if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
+ || exmode_active) {
+ screenalloc();
+ if (msg_grid.chars) {
+ msg_grid_validate();
+ }
+ // TODO(bfredl): sometimes messes up the output. Implement clear+redraw
+ // also for the pager? (or: what if the pager was just a modal window?)
+ ui_comp_set_screen_valid(true);
+ repeat_message();
+ } else {
+ if (curwin->w_p_scb) {
+ do_check_scrollbind(true);
+ }
+ if (State & MODE_CMDLINE) {
+ redraw_popupmenu = false;
+ update_screen(NOT_VALID);
+ redrawcmdline();
+ if (pum_drawn()) {
+ cmdline_pum_display(false);
+ }
+ } else {
+ update_topline(curwin);
+ if (pum_drawn()) {
+ // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
+ // For now make sure the nested update_screen(0) won't redraw the
+ // pum at the old position. Try to untangle this later.
+ redraw_popupmenu = false;
+ ins_compl_show_pum();
+ }
+ update_screen(NOT_VALID);
+ if (redrawing()) {
+ setcursor();
+ }
+ }
+ }
+ ui_flush();
+ }
+ resizing_screen = false;
+}
+
+/// Redraw the parts of the screen that is marked for redraw.
+///
+/// Most code shouldn't call this directly, rather use redraw_later() and
+/// and redraw_all_later() to mark parts of the screen as needing a redraw.
+///
+/// @param type set to a NOT_VALID to force redraw of entire screen
+int update_screen(int type)
+{
+ static bool did_intro = false;
+ bool is_stl_global = global_stl_height() > 0;
+
+ // Don't do anything if the screen structures are (not yet) valid.
+ // A VimResized autocmd can invoke redrawing in the middle of a resize,
+ // which would bypass the checks in screen_resize for popupmenu etc.
+ if (!default_grid.chars || resizing) {
+ return FAIL;
+ }
+
+ // May have postponed updating diffs.
+ if (need_diff_redraw) {
+ diff_redraw(true);
+ }
+
+ if (must_redraw) {
+ if (type < must_redraw) { // use maximal type
+ type = must_redraw;
+ }
+
+ // must_redraw is reset here, so that when we run into some weird
+ // reason to redraw while busy redrawing (e.g., asynchronous
+ // scrolling), or update_topline() in win_update() will cause a
+ // scroll, or a decoration provider requires a redraw, the screen
+ // will be redrawn later or in win_update().
+ must_redraw = 0;
+ }
+
+ // Need to update w_lines[].
+ if (curwin->w_lines_valid == 0 && type < NOT_VALID) {
+ type = NOT_VALID;
+ }
+
+ // Postpone the redrawing when it's not needed and when being called
+ // recursively.
+ if (!redrawing() || updating_screen) {
+ must_redraw = type;
+ if (type > INVERTED_ALL) {
+ curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
+ }
+ return FAIL;
+ }
+ updating_screen = 1;
+
+ display_tick++; // let syntax code know we're in a next round of
+ // display updating
+
+ // Tricky: vim code can reset msg_scrolled behind our back, so need
+ // separate bookkeeping for now.
+ if (msg_did_scroll) {
+ msg_did_scroll = false;
+ msg_scrolled_at_flush = 0;
+ }
+
+ if (type >= CLEAR || !default_grid.valid) {
+ ui_comp_set_screen_valid(false);
+ }
+
+ // if the screen was scrolled up when displaying a message, scroll it down
+ if (msg_scrolled || msg_grid_invalid) {
+ clear_cmdline = true;
+ int valid = MAX(Rows - msg_scrollsize(), 0);
+ if (msg_grid.chars) {
+ // non-displayed part of msg_grid is considered invalid.
+ for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
+ grid_clear_line(&msg_grid, msg_grid.line_offset[i],
+ msg_grid.cols, false);
+ }
+ }
+ if (msg_use_msgsep()) {
+ msg_grid.throttled = false;
+ // CLEAR is already handled
+ if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
+ ui_comp_set_screen_valid(false);
+ for (int i = valid; i < Rows - p_ch; i++) {
+ grid_clear_line(&default_grid, default_grid.line_offset[i],
+ Columns, false);
+ }
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ continue;
+ }
+ if (W_ENDROW(wp) > valid) {
+ wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID);
+ }
+ if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
+ wp->w_redr_status = true;
+ }
+ }
+ if (is_stl_global && Rows - p_ch - 1 > valid) {
+ curwin->w_redr_status = true;
+ }
+ }
+ msg_grid_set_pos(Rows - (int)p_ch, false);
+ msg_grid_invalid = false;
+ } else if (msg_scrolled > Rows - 5) { // clearing is faster
+ type = CLEAR;
+ } else if (type != CLEAR) {
+ check_for_delay(false);
+ grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ continue;
+ }
+ if (wp->w_winrow < msg_scrolled) {
+ if (W_ENDROW(wp) > msg_scrolled
+ && wp->w_redr_type < REDRAW_TOP
+ && wp->w_lines_valid > 0
+ && wp->w_topline == wp->w_lines[0].wl_lnum) {
+ wp->w_upd_rows = msg_scrolled - wp->w_winrow;
+ wp->w_redr_type = REDRAW_TOP;
+ } else {
+ wp->w_redr_type = NOT_VALID;
+ if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
+ wp->w_redr_status = true;
+ }
+ }
+ }
+ }
+ if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
+ curwin->w_redr_status = true;
+ }
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ }
+ msg_scrolled = 0;
+ msg_scrolled_at_flush = 0;
+ need_wait_return = false;
+ }
+
+ win_ui_flush();
+ msg_ext_check_clear();
+
+ // reset cmdline_row now (may have been changed temporarily)
+ compute_cmdrow();
+
+ bool hl_changed = false;
+ // Check for changed highlighting
+ if (need_highlight_changed) {
+ highlight_changed();
+ hl_changed = true;
+ }
+
+ if (type == CLEAR) { // first clear screen
+ screenclear(); // will reset clear_cmdline
+ cmdline_screen_cleared(); // clear external cmdline state
+ type = NOT_VALID;
+ // must_redraw may be set indirectly, avoid another redraw later
+ must_redraw = 0;
+ } else if (!default_grid.valid) {
+ grid_invalidate(&default_grid);
+ default_grid.valid = true;
+ }
+
+ // After disabling msgsep the grid might not have been deallocated yet,
+ // hence we also need to check msg_grid.chars
+ if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) {
+ grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
+ }
+
+ ui_comp_set_screen_valid(true);
+
+ DecorProviders providers;
+ decor_providers_start(&providers, type, &provider_err);
+
+ // "start" callback could have changed highlights for global elements
+ if (win_check_ns_hl(NULL)) {
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ }
+
+ if (clear_cmdline) { // going to clear cmdline (done below)
+ check_for_delay(false);
+ }
+
+ // Force redraw when width of 'number' or 'relativenumber' column
+ // changes.
+ if (curwin->w_redr_type < NOT_VALID
+ && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
+ ? number_width(curwin) : 0)) {
+ curwin->w_redr_type = NOT_VALID;
+ }
+
+ // Only start redrawing if there is really something to do.
+ if (type == INVERTED) {
+ update_curswant();
+ }
+ if (curwin->w_redr_type < type
+ && !((type == VALID
+ && curwin->w_lines[0].wl_valid
+ && curwin->w_topfill == curwin->w_old_topfill
+ && curwin->w_botfill == curwin->w_old_botfill
+ && curwin->w_topline == curwin->w_lines[0].wl_lnum)
+ || (type == INVERTED
+ && VIsual_active
+ && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
+ && curwin->w_old_visual_mode == VIsual_mode
+ && (curwin->w_valid & VALID_VIRTCOL)
+ && curwin->w_old_curswant == curwin->w_curswant))) {
+ curwin->w_redr_type = type;
+ }
+
+ // Redraw the tab pages line if needed.
+ if (redraw_tabline || type >= NOT_VALID) {
+ update_window_hl(curwin, type >= NOT_VALID);
+ FOR_ALL_TABS(tp) {
+ if (tp != curtab) {
+ update_window_hl(tp->tp_curwin, type >= NOT_VALID);
+ }
+ }
+ draw_tabline();
+ }
+
+ // Correct stored syntax highlighting info for changes in each displayed
+ // buffer. Each buffer must only be done once.
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ update_window_hl(wp, type >= NOT_VALID || hl_changed);
+
+ buf_T *buf = wp->w_buffer;
+ if (buf->b_mod_set) {
+ if (buf->b_mod_tick_syn < display_tick
+ && syntax_present(wp)) {
+ syn_stack_apply_changes(buf);
+ buf->b_mod_tick_syn = display_tick;
+ }
+
+ if (buf->b_mod_tick_decor < display_tick) {
+ decor_providers_invoke_buf(buf, &providers, &provider_err);
+ buf->b_mod_tick_decor = display_tick;
+ }
+ }
+ }
+
+ // Go from top to bottom through the windows, redrawing the ones that need it.
+ bool did_one = false;
+ screen_search_hl.rm.regprog = NULL;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
+ grid_invalidate(&wp->w_grid_alloc);
+ wp->w_redr_type = NOT_VALID;
+ }
+
+ win_check_ns_hl(wp);
+
+ // reallocate grid if needed.
+ win_grid_alloc(wp);
+
+ if (wp->w_redr_border || wp->w_redr_type >= NOT_VALID) {
+ win_redr_border(wp);
+ }
+
+ if (wp->w_redr_type != 0) {
+ if (!did_one) {
+ did_one = true;
+ start_search_hl();
+ }
+ win_update(wp, &providers);
+ }
+
+ // redraw status line and window bar after the window to minimize cursor movement
+ if (wp->w_redr_status) {
+ win_redr_winbar(wp);
+ win_redr_status(wp);
+ }
+ }
+
+ end_search_hl();
+
+ // May need to redraw the popup menu.
+ if (pum_drawn() && must_redraw_pum) {
+ win_check_ns_hl(curwin);
+ pum_redraw();
+ }
+
+ win_check_ns_hl(NULL);
+
+ // Reset b_mod_set flags. Going through all windows is probably faster
+ // than going through all buffers (there could be many buffers).
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ wp->w_buffer->b_mod_set = false;
+ }
+
+ updating_screen = 0;
+
+ // Clear or redraw the command line. Done last, because scrolling may
+ // mess up the command line.
+ if (clear_cmdline || redraw_cmdline || redraw_mode) {
+ showmode();
+ }
+
+ // May put up an introductory message when not editing a file
+ if (!did_intro) {
+ maybe_intro_message();
+ }
+ did_intro = true;
+
+ decor_providers_invoke_end(&providers, &provider_err);
+ kvi_destroy(providers);
+
+ // either cmdline is cleared, not drawn or mode is last drawn
+ cmdline_was_last_drawn = false;
+ return OK;
+}
+
+static void win_redr_border(win_T *wp)
+{
+ wp->w_redr_border = false;
+ if (!(wp->w_floating && wp->w_float_config.border)) {
+ return;
+ }
+
+ ScreenGrid *grid = &wp->w_grid_alloc;
+
+ schar_T *chars = wp->w_float_config.border_chars;
+ int *attrs = wp->w_float_config.border_attr;
+
+ int *adj = wp->w_border_adj;
+ int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
+
+ if (adj[0]) {
+ grid_puts_line_start(grid, 0);
+ if (adj[3]) {
+ grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ }
+ for (int i = 0; i < icol; i++) {
+ grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
+ }
+ if (adj[1]) {
+ grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
+ }
+ grid_puts_line_flush(false);
+ }
+
+ for (int i = 0; i < irow; i++) {
+ if (adj[3]) {
+ grid_puts_line_start(grid, i + adj[0]);
+ grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
+ grid_puts_line_flush(false);
+ }
+ if (adj[1]) {
+ int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
+ grid_puts_line_start(grid, i + adj[0]);
+ grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
+ grid_puts_line_flush(false);
+ }
+ }
+
+ if (adj[2]) {
+ grid_puts_line_start(grid, irow + adj[0]);
+ if (adj[3]) {
+ grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
+ }
+ for (int i = 0; i < icol; i++) {
+ int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
+ grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
+ }
+ if (adj[1]) {
+ grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
+ }
+ grid_puts_line_flush(false);
+ }
+}
+
+/// Redraw the status line of window `wp`.
+///
+/// If inversion is possible we use it. Else '=' characters are used.
+static void win_redr_status(win_T *wp)
+{
+ int row;
+ int col;
+ char_u *p;
+ int len;
+ int fillchar;
+ int attr;
+ int width;
+ int this_ru_col;
+ bool is_stl_global = global_stl_height() > 0;
+ static bool busy = false;
+
+ // May get here recursively when 'statusline' (indirectly)
+ // invokes ":redrawstatus". Simply ignore the call then.
+ if (busy
+ // Also ignore if wildmenu is showing.
+ || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) {
+ return;
+ }
+ busy = true;
+
+ wp->w_redr_status = false;
+ if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
+ // no status line, either global statusline is enabled or the window is a last window
+ redraw_cmdline = true;
+ } else if (!redrawing()) {
+ // Don't redraw right now, do it later. Don't update status line when
+ // popup menu is visible and may be drawn over it
+ wp->w_redr_status = true;
+ } else if (*p_stl != NUL || *wp->w_p_stl != NUL) {
+ // redraw custom status line
+ redraw_custom_statusline(wp);
+ } else {
+ fillchar = fillchar_status(&attr, wp);
+ width = is_stl_global ? Columns : wp->w_width;
+
+ get_trans_bufname(wp->w_buffer);
+ p = NameBuff;
+ len = (int)STRLEN(p);
+
+ if (bt_help(wp->w_buffer)
+ || wp->w_p_pvw
+ || bufIsChanged(wp->w_buffer)
+ || wp->w_buffer->b_p_ro) {
+ *(p + len++) = ' ';
+ }
+ if (bt_help(wp->w_buffer)) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
+ len += (int)STRLEN(p + len);
+ }
+ if (wp->w_p_pvw) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
+ len += (int)STRLEN(p + len);
+ }
+ if (bufIsChanged(wp->w_buffer)) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]");
+ len += (int)STRLEN(p + len);
+ }
+ if (wp->w_buffer->b_p_ro) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
+ // len += (int)STRLEN(p + len); // dead assignment
+ }
+
+ this_ru_col = ru_col - (Columns - width);
+ if (this_ru_col < (width + 1) / 2) {
+ this_ru_col = (width + 1) / 2;
+ }
+ if (this_ru_col <= 1) {
+ p = (char_u *)"<"; // No room for file name!
+ len = 1;
+ } else {
+ int clen = 0, i;
+
+ // Count total number of display cells.
+ clen = (int)mb_string2cells((char *)p);
+
+ // Find first character that will fit.
+ // Going from start to end is much faster for DBCS.
+ for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
+ i += utfc_ptr2len((char *)p + i)) {
+ clen -= utf_ptr2cells((char *)p + i);
+ }
+ len = clen;
+ if (i > 0) {
+ p = p + i - 1;
+ *p = '<';
+ len++;
+ }
+ }
+
+ row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
+ col = is_stl_global ? 0 : wp->w_wincol;
+ grid_puts(&default_grid, p, row, col, attr);
+ grid_fill(&default_grid, row, row + 1, len + col,
+ this_ru_col + col, fillchar, fillchar, attr);
+
+ if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL)
+ && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
+ grid_puts(&default_grid, NameBuff, row,
+ (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr);
+ }
+
+ win_redr_ruler(wp, true);
+ }
+
+ // May need to draw the character below the vertical separator.
+ if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) {
+ if (stl_connected(wp)) {
+ fillchar = fillchar_status(&attr, wp);
+ } else {
+ fillchar = fillchar_vsep(wp, &attr);
+ }
+ grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr);
+ }
+ busy = false;
+}
+
+/// Redraw the status line according to 'statusline' and take care of any
+/// errors encountered.
+static void redraw_custom_statusline(win_T *wp)
+{
+ static bool entered = false;
+ int saved_did_emsg = did_emsg;
+
+ // When called recursively return. This can happen when the statusline
+ // contains an expression that triggers a redraw.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ did_emsg = false;
+ win_redr_custom(wp, false, false);
+ if (did_emsg) {
+ // When there is an error disable the statusline, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct("statusline", -1, "",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ entered = false;
+}
+
+static void win_redr_winbar(win_T *wp)
+{
+ static bool entered = false;
+
+ // Return when called recursively. This can happen when the winbar contains an expression
+ // that triggers a redraw.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ if (wp->w_winbar_height == 0 || !redrawing()) {
+ // Do nothing.
+ } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
+ int saved_did_emsg = did_emsg;
+
+ did_emsg = false;
+ win_redr_custom(wp, true, false);
+ if (did_emsg) {
+ // When there is an error disable the winbar, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct("winbar", -1, "",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ }
+ entered = false;
+}
+
+/// Show current status info in ruler and various other places
+///
+/// @param always if false, only show ruler if position has changed.
+void showruler(bool always)
+{
+ if (!always && !redrawing()) {
+ return;
+ }
+ if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
+ && (curwin->w_status_height || global_stl_height())) {
+ redraw_custom_statusline(curwin);
+ } else {
+ win_redr_ruler(curwin, always);
+ }
+ if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
+ win_redr_winbar(curwin);
+ }
+
+ if (need_maketitle
+ || (p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))) {
+ maketitle();
+ }
+
+ // Redraw the tab pages line if needed.
+ if (redraw_tabline) {
+ draw_tabline();
+ }
+}
+
+static void redraw_win_signcol(win_T *wp)
+{
+ // If we can compute a change in the automatic sizing of the sign column
+ // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
+ // figuring it out here so we can redraw the entire screen for it.
+ int scwidth = wp->w_scwidth;
+ wp->w_scwidth = win_signcol_count(wp);
+ if (wp->w_scwidth != scwidth) {
+ changed_line_abv_curs_win(wp);
+ }
+}
+
+/// Check if horizontal separator of window "wp" at specified window corner is connected to the
+/// horizontal separator of another window
+/// Assumes global statusline is enabled
+static bool hsep_connected(win_T *wp, WindowCorner corner)
+{
+ bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT);
+ int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT)
+ ? wp->w_winrow - 1 : W_ENDROW(wp);
+ frame_T *fr = wp->w_frame;
+
+ while (fr->fr_parent != NULL) {
+ if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) {
+ fr = before ? fr->fr_prev : fr->fr_next;
+ break;
+ }
+ fr = fr->fr_parent;
+ }
+ if (fr->fr_parent == NULL) {
+ return false;
+ }
+ while (fr->fr_layout != FR_LEAF) {
+ fr = fr->fr_child;
+ if (fr->fr_parent->fr_layout == FR_ROW && before) {
+ while (fr->fr_next != NULL) {
+ fr = fr->fr_next;
+ }
+ } else {
+ while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) {
+ fr = fr->fr_next;
+ }
+ }
+ }
+
+ return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win));
+}
+
+/// Check if vertical separator of window "wp" at specified window corner is connected to the
+/// vertical separator of another window
+static bool vsep_connected(win_T *wp, WindowCorner corner)
+{
+ bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT);
+ int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT)
+ ? wp->w_wincol - 1 : W_ENDCOL(wp);
+ frame_T *fr = wp->w_frame;
+
+ while (fr->fr_parent != NULL) {
+ if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) {
+ fr = before ? fr->fr_prev : fr->fr_next;
+ break;
+ }
+ fr = fr->fr_parent;
+ }
+ if (fr->fr_parent == NULL) {
+ return false;
+ }
+ while (fr->fr_layout != FR_LEAF) {
+ fr = fr->fr_child;
+ if (fr->fr_parent->fr_layout == FR_COL && before) {
+ while (fr->fr_next != NULL) {
+ fr = fr->fr_next;
+ }
+ } else {
+ while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) {
+ fr = fr->fr_next;
+ }
+ }
+ }
+
+ return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win));
+}
+
+/// Draw the vertical separator right of window "wp"
+static void draw_vsep_win(win_T *wp)
+{
+ int hl;
+ int c;
+
+ if (wp->w_vsep_width) {
+ // draw the vertical separator right of this window
+ c = fillchar_vsep(wp, &hl);
+ grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
+ W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
+ }
+}
+
+/// Draw the horizontal separator below window "wp"
+static void draw_hsep_win(win_T *wp)
+{
+ int hl;
+ int c;
+
+ if (wp->w_hsep_height) {
+ // draw the horizontal separator below this window
+ c = fillchar_hsep(wp, &hl);
+ grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
+ wp->w_wincol, W_ENDCOL(wp), c, c, hl);
+ }
+}
+
+/// Get the separator connector for specified window corner of window "wp"
+static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
+{
+ // It's impossible for windows to be connected neither vertically nor horizontally
+ // So if they're not vertically connected, assume they're horizontally connected
+ if (vsep_connected(wp, corner)) {
+ if (hsep_connected(wp, corner)) {
+ return wp->w_p_fcs_chars.verthoriz;
+ } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
+ return wp->w_p_fcs_chars.vertright;
+ } else {
+ return wp->w_p_fcs_chars.vertleft;
+ }
+ } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
+ return wp->w_p_fcs_chars.horizdown;
+ } else {
+ return wp->w_p_fcs_chars.horizup;
+ }
+}
+
+/// Draw separator connecting characters on the corners of window "wp"
+static void draw_sep_connectors_win(win_T *wp)
+{
+ // Don't draw separator connectors unless global statusline is enabled and the window has
+ // either a horizontal or vertical separator
+ if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) {
+ return;
+ }
+
+ int hl = win_hl_attr(wp, HLF_C);
+
+ // Determine which edges of the screen the window is located on so we can avoid drawing separators
+ // on corners contained in those edges
+ bool win_at_top;
+ bool win_at_bottom = wp->w_hsep_height == 0;
+ bool win_at_left;
+ bool win_at_right = wp->w_vsep_width == 0;
+ frame_T *frp;
+
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
+ break;
+ }
+ }
+ win_at_top = frp->fr_parent == NULL;
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
+ break;
+ }
+ }
+ win_at_left = frp->fr_parent == NULL;
+
+ // Draw the appropriate separator connector in every corner where drawing them is necessary
+ if (!(win_at_top || win_at_left)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
+ wp->w_winrow - 1, wp->w_wincol - 1, hl);
+ }
+ if (!(win_at_top || win_at_right)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
+ wp->w_winrow - 1, W_ENDCOL(wp), hl);
+ }
+ if (!(win_at_bottom || win_at_left)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
+ W_ENDROW(wp), wp->w_wincol - 1, hl);
+ }
+ if (!(win_at_bottom || win_at_right)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
+ W_ENDROW(wp), W_ENDCOL(wp), hl);
+ }
+}
+
+/// Update a single window.
+///
+/// This may cause the windows below it also to be redrawn (when clearing the
+/// screen or scrolling lines).
+///
+/// How the window is redrawn depends on wp->w_redr_type. Each type also
+/// implies the one below it.
+/// NOT_VALID redraw the whole window
+/// SOME_VALID redraw the whole window but do scroll when possible
+/// REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID
+/// INVERTED redraw the changed part of the Visual area
+/// INVERTED_ALL redraw the whole Visual area
+/// VALID 1. scroll up/down to adjust for a changed w_topline
+/// 2. update lines at the top when scrolled down
+/// 3. redraw changed text:
+/// - if wp->w_buffer->b_mod_set set, update lines between
+/// b_mod_top and b_mod_bot.
+/// - if wp->w_redraw_top non-zero, redraw lines between
+/// wp->w_redraw_top and wp->w_redr_bot.
+/// - continue redrawing when syntax status is invalid.
+/// 4. if scrolled up, update lines at the bottom.
+/// This results in three areas that may need updating:
+/// top: from first row to top_end (when scrolled down)
+/// mid: from mid_start to mid_end (update inversion or changed text)
+/// bot: from bot_start to last row (when scrolled up)
+static void win_update(win_T *wp, DecorProviders *providers)
+{
+ bool called_decor_providers = false;
+win_update_start:
+ ;
+ buf_T *buf = wp->w_buffer;
+ int type;
+ int top_end = 0; // Below last row of the top area that needs
+ // updating. 0 when no top area updating.
+ int mid_start = 999; // first row of the mid area that needs
+ // updating. 999 when no mid area updating.
+ int mid_end = 0; // Below last row of the mid area that needs
+ // updating. 0 when no mid area updating.
+ int bot_start = 999; // first row of the bot area that needs
+ // updating. 999 when no bot area updating
+ bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
+ bool top_to_mod = false; // redraw above mod_top
+
+ int row; // current window row to display
+ linenr_T lnum; // current buffer lnum to display
+ int idx; // current index in w_lines[]
+ int srow; // starting row of the current line
+
+ bool eof = false; // if true, we hit the end of the file
+ bool didline = false; // if true, we finished the last line
+ int i;
+ long j;
+ static bool recursive = false; // being called recursively
+ const linenr_T old_botline = wp->w_botline;
+ // Remember what happened to the previous line.
+#define DID_NONE 1 // didn't update a line
+#define DID_LINE 2 // updated a normal line
+#define DID_FOLD 3 // updated a folded line
+ int did_update = DID_NONE;
+ linenr_T syntax_last_parsed = 0; // last parsed text line
+ linenr_T mod_top = 0;
+ linenr_T mod_bot = 0;
+ int save_got_int;
+
+ type = wp->w_redr_type;
+
+ if (type >= NOT_VALID) {
+ wp->w_redr_status = true;
+ wp->w_lines_valid = 0;
+ }
+
+ // Window is zero-height: Only need to draw the separator
+ if (wp->w_grid.rows == 0) {
+ // draw the horizontal separator below this window
+ draw_hsep_win(wp);
+ draw_sep_connectors_win(wp);
+ wp->w_redr_type = 0;
+ return;
+ }
+
+ // Window is zero-width: Only need to draw the separator.
+ if (wp->w_grid.cols == 0) {
+ // draw the vertical separator right of this window
+ draw_vsep_win(wp);
+ draw_sep_connectors_win(wp);
+ wp->w_redr_type = 0;
+ return;
+ }
+
+ redraw_win_signcol(wp);
+
+ init_search_hl(wp, &screen_search_hl);
+
+ // Force redraw when width of 'number' or 'relativenumber' column
+ // changes.
+ i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
+ if (wp->w_nrwidth != i) {
+ type = NOT_VALID;
+ wp->w_nrwidth = i;
+
+ if (buf->terminal) {
+ terminal_check_size(buf->terminal);
+ }
+ } else if (buf->b_mod_set
+ && buf->b_mod_xlines != 0
+ && wp->w_redraw_top != 0) {
+ // When there are both inserted/deleted lines and specific lines to be
+ // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
+ // everything (only happens when redrawing is off for while).
+ type = NOT_VALID;
+ } else {
+ // Set mod_top to the first line that needs displaying because of
+ // changes. Set mod_bot to the first line after the changes.
+ mod_top = wp->w_redraw_top;
+ if (wp->w_redraw_bot != 0) {
+ mod_bot = wp->w_redraw_bot + 1;
+ } else {
+ mod_bot = 0;
+ }
+ if (buf->b_mod_set) {
+ if (mod_top == 0 || mod_top > buf->b_mod_top) {
+ mod_top = buf->b_mod_top;
+ // Need to redraw lines above the change that may be included
+ // in a pattern match.
+ if (syntax_present(wp)) {
+ mod_top -= buf->b_s.b_syn_sync_linebreaks;
+ if (mod_top < 1) {
+ mod_top = 1;
+ }
+ }
+ }
+ if (mod_bot == 0 || mod_bot < buf->b_mod_bot) {
+ mod_bot = buf->b_mod_bot;
+ }
+
+ // When 'hlsearch' is on and using a multi-line search pattern, a
+ // change in one line may make the Search highlighting in a
+ // previous line invalid. Simple solution: redraw all visible
+ // lines above the change.
+ // Same for a match pattern.
+ if (screen_search_hl.rm.regprog != NULL
+ && re_multiline(screen_search_hl.rm.regprog)) {
+ top_to_mod = true;
+ } else {
+ const matchitem_T *cur = wp->w_match_head;
+ while (cur != NULL) {
+ if (cur->match.regprog != NULL
+ && re_multiline(cur->match.regprog)) {
+ top_to_mod = true;
+ break;
+ }
+ cur = cur->next;
+ }
+ }
+ }
+ if (mod_top != 0 && hasAnyFolding(wp)) {
+ linenr_T lnumt, lnumb;
+
+ // A change in a line can cause lines above it to become folded or
+ // unfolded. Find the top most buffer line that may be affected.
+ // If the line was previously folded and displayed, get the first
+ // line of that fold. If the line is folded now, get the first
+ // folded line. Use the minimum of these two.
+
+ // Find last valid w_lines[] entry above mod_top. Set lnumt to
+ // the line below it. If there is no valid entry, use w_topline.
+ // Find the first valid w_lines[] entry below mod_bot. Set lnumb
+ // to this line. If there is no valid entry, use MAXLNUM.
+ lnumt = wp->w_topline;
+ lnumb = MAXLNUM;
+ for (i = 0; i < wp->w_lines_valid; i++) {
+ if (wp->w_lines[i].wl_valid) {
+ if (wp->w_lines[i].wl_lastlnum < mod_top) {
+ lnumt = wp->w_lines[i].wl_lastlnum + 1;
+ }
+ if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) {
+ lnumb = wp->w_lines[i].wl_lnum;
+ // When there is a fold column it might need updating
+ // in the next line ("J" just above an open fold).
+ if (compute_foldcolumn(wp, 0) > 0) {
+ lnumb++;
+ }
+ }
+ }
+ }
+
+ (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL);
+ if (mod_top > lnumt) {
+ mod_top = lnumt;
+ }
+
+ // Now do the same for the bottom line (one above mod_bot).
+ mod_bot--;
+ (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL);
+ mod_bot++;
+ if (mod_bot < lnumb) {
+ mod_bot = lnumb;
+ }
+ }
+
+ // When a change starts above w_topline and the end is below
+ // w_topline, start redrawing at w_topline.
+ // If the end of the change is above w_topline: do like no change was
+ // made, but redraw the first line to find changes in syntax.
+ if (mod_top != 0 && mod_top < wp->w_topline) {
+ if (mod_bot > wp->w_topline) {
+ mod_top = wp->w_topline;
+ } else if (syntax_present(wp)) {
+ top_end = 1;
+ }
+ }
+
+ // When line numbers are displayed need to redraw all lines below
+ // inserted/deleted lines.
+ if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) {
+ mod_bot = MAXLNUM;
+ }
+ }
+ wp->w_redraw_top = 0; // reset for next time
+ wp->w_redraw_bot = 0;
+
+ // When only displaying the lines at the top, set top_end. Used when
+ // window has scrolled down for msg_scrolled.
+ if (type == REDRAW_TOP) {
+ j = 0;
+ for (i = 0; i < wp->w_lines_valid; i++) {
+ j += wp->w_lines[i].wl_size;
+ if (j >= wp->w_upd_rows) {
+ top_end = (int)j;
+ break;
+ }
+ }
+ if (top_end == 0) {
+ // not found (cannot happen?): redraw everything
+ type = NOT_VALID;
+ } else {
+ // top area defined, the rest is VALID
+ type = VALID;
+ }
+ }
+
+ // If there are no changes on the screen that require a complete redraw,
+ // handle three cases:
+ // 1: we are off the top of the screen by a few lines: scroll down
+ // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
+ // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
+ // w_lines[] that needs updating.
+ if ((type == VALID || type == SOME_VALID
+ || type == INVERTED || type == INVERTED_ALL)
+ && !wp->w_botfill && !wp->w_old_botfill) {
+ if (mod_top != 0
+ && wp->w_topline == mod_top
+ && (!wp->w_lines[0].wl_valid
+ || wp->w_topline == wp->w_lines[0].wl_lnum)) {
+ // w_topline is the first changed line and window is not scrolled,
+ // the scrolling from changed lines will be done further down.
+ } else if (wp->w_lines[0].wl_valid
+ && (wp->w_topline < wp->w_lines[0].wl_lnum
+ || (wp->w_topline == wp->w_lines[0].wl_lnum
+ && wp->w_topfill > wp->w_old_topfill))) {
+ // New topline is above old topline: May scroll down.
+ if (hasAnyFolding(wp)) {
+ linenr_T ln;
+
+ // count the number of lines we are off, counting a sequence
+ // of folded lines as one
+ j = 0;
+ for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
+ j++;
+ if (j >= wp->w_grid.rows - 2) {
+ break;
+ }
+ (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL);
+ }
+ } else {
+ j = wp->w_lines[0].wl_lnum - wp->w_topline;
+ }
+ if (j < wp->w_grid.rows - 2) { // not too far off
+ i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
+ // insert extra lines for previously invisible filler lines
+ if (wp->w_lines[0].wl_lnum != wp->w_topline) {
+ i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
+ }
+ if (i != 0 && i < wp->w_grid.rows - 2) { // less than a screen off
+ // Try to insert the correct number of lines.
+ // If not the last window, delete the lines at the bottom.
+ // win_ins_lines may fail when the terminal can't do it.
+ win_scroll_lines(wp, 0, i);
+ if (wp->w_lines_valid != 0) {
+ // Need to update rows that are new, stop at the
+ // first one that scrolled down.
+ top_end = i;
+ scrolled_down = true;
+
+ // Move the entries that were scrolled, disable
+ // the entries for the lines to be redrawn.
+ if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) {
+ wp->w_lines_valid = wp->w_grid.rows;
+ }
+ for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
+ wp->w_lines[idx] = wp->w_lines[idx - j];
+ }
+ while (idx >= 0) {
+ wp->w_lines[idx--].wl_valid = false;
+ }
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
+ } else {
+ // New topline is at or below old topline: May scroll up.
+ // When topline didn't change, find first entry in w_lines[] that
+ // needs updating.
+
+ // try to find wp->w_topline in wp->w_lines[].wl_lnum
+ j = -1;
+ row = 0;
+ for (i = 0; i < wp->w_lines_valid; i++) {
+ if (wp->w_lines[i].wl_valid
+ && wp->w_lines[i].wl_lnum == wp->w_topline) {
+ j = i;
+ break;
+ }
+ row += wp->w_lines[i].wl_size;
+ }
+ if (j == -1) {
+ // if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all
+ // lines
+ mid_start = 0;
+ } else {
+ // Try to delete the correct number of lines.
+ // wp->w_topline is at wp->w_lines[i].wl_lnum.
+
+ // If the topline didn't change, delete old filler lines,
+ // otherwise delete filler lines of the new topline...
+ if (wp->w_lines[0].wl_lnum == wp->w_topline) {
+ row += wp->w_old_topfill;
+ } else {
+ row += win_get_fill(wp, wp->w_topline);
+ }
+ // ... but don't delete new filler lines.
+ row -= wp->w_topfill;
+ if (row > 0) {
+ win_scroll_lines(wp, 0, -row);
+ bot_start = wp->w_grid.rows - row;
+ }
+ if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
+ // Skip the lines (below the deleted lines) that are still
+ // valid and don't need redrawing. Copy their info
+ // upwards, to compensate for the deleted lines. Set
+ // bot_start to the first row that needs redrawing.
+ bot_start = 0;
+ idx = 0;
+ for (;;) {
+ wp->w_lines[idx] = wp->w_lines[j];
+ // stop at line that didn't fit, unless it is still
+ // valid (no lines deleted)
+ if (row > 0 && bot_start + row
+ + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) {
+ wp->w_lines_valid = idx + 1;
+ break;
+ }
+ bot_start += wp->w_lines[idx++].wl_size;
+
+ // stop at the last valid entry in w_lines[].wl_size
+ if (++j >= wp->w_lines_valid) {
+ wp->w_lines_valid = idx;
+ break;
+ }
+ }
+
+ // Correct the first entry for filler lines at the top
+ // when it won't get updated below.
+ if (win_may_fill(wp) && bot_start > 0) {
+ wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true)
+ + wp->w_topfill);
+ }
+ }
+ }
+ }
+
+ // When starting redraw in the first line, redraw all lines.
+ if (mid_start == 0) {
+ mid_end = wp->w_grid.rows;
+ }
+ } else {
+ // Not VALID or INVERTED: redraw all lines.
+ mid_start = 0;
+ mid_end = wp->w_grid.rows;
+ }
+
+ if (type == SOME_VALID) {
+ // SOME_VALID: redraw all lines.
+ mid_start = 0;
+ mid_end = wp->w_grid.rows;
+ type = NOT_VALID;
+ }
+
+ // check if we are updating or removing the inverted part
+ if ((VIsual_active && buf == curwin->w_buffer)
+ || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) {
+ linenr_T from, to;
+
+ if (VIsual_active) {
+ if (VIsual_mode != wp->w_old_visual_mode || type == INVERTED_ALL) {
+ // If the type of Visual selection changed, redraw the whole
+ // selection. Also when the ownership of the X selection is
+ // gained or lost.
+ if (curwin->w_cursor.lnum < VIsual.lnum) {
+ from = curwin->w_cursor.lnum;
+ to = VIsual.lnum;
+ } else {
+ from = VIsual.lnum;
+ to = curwin->w_cursor.lnum;
+ }
+ // redraw more when the cursor moved as well
+ if (wp->w_old_cursor_lnum < from) {
+ from = wp->w_old_cursor_lnum;
+ }
+ if (wp->w_old_cursor_lnum > to) {
+ to = wp->w_old_cursor_lnum;
+ }
+ if (wp->w_old_visual_lnum < from) {
+ from = wp->w_old_visual_lnum;
+ }
+ if (wp->w_old_visual_lnum > to) {
+ to = wp->w_old_visual_lnum;
+ }
+ } else {
+ // Find the line numbers that need to be updated: The lines
+ // between the old cursor position and the current cursor
+ // position. Also check if the Visual position changed.
+ if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum) {
+ from = curwin->w_cursor.lnum;
+ to = wp->w_old_cursor_lnum;
+ } else {
+ from = wp->w_old_cursor_lnum;
+ to = curwin->w_cursor.lnum;
+ if (from == 0) { // Visual mode just started
+ from = to;
+ }
+ }
+
+ if (VIsual.lnum != wp->w_old_visual_lnum
+ || VIsual.col != wp->w_old_visual_col) {
+ if (wp->w_old_visual_lnum < from
+ && wp->w_old_visual_lnum != 0) {
+ from = wp->w_old_visual_lnum;
+ }
+ if (wp->w_old_visual_lnum > to) {
+ to = wp->w_old_visual_lnum;
+ }
+ if (VIsual.lnum < from) {
+ from = VIsual.lnum;
+ }
+ if (VIsual.lnum > to) {
+ to = VIsual.lnum;
+ }
+ }
+ }
+
+ // If in block mode and changed column or curwin->w_curswant:
+ // update all lines.
+ // First compute the actual start and end column.
+ if (VIsual_mode == Ctrl_V) {
+ colnr_T fromc, toc;
+ unsigned int save_ve_flags = curwin->w_ve_flags;
+
+ if (curwin->w_p_lbr) {
+ curwin->w_ve_flags = VE_ALL;
+ }
+
+ getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
+ toc++;
+ curwin->w_ve_flags = save_ve_flags;
+ // Highlight to the end of the line, unless 'virtualedit' has
+ // "block".
+ if (curwin->w_curswant == MAXCOL) {
+ if (get_ve_flags() & VE_BLOCK) {
+ pos_T pos;
+ int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
+
+ // Need to find the longest line.
+ toc = 0;
+ pos.coladd = 0;
+ for (pos.lnum = curwin->w_cursor.lnum;
+ cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum;
+ pos.lnum += cursor_above ? 1 : -1) {
+ colnr_T t;
+
+ pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ getvvcol(wp, &pos, NULL, NULL, &t);
+ if (toc < t) {
+ toc = t;
+ }
+ }
+ toc++;
+ } else {
+ toc = MAXCOL;
+ }
+ }
+
+ if (fromc != wp->w_old_cursor_fcol
+ || toc != wp->w_old_cursor_lcol) {
+ if (from > VIsual.lnum) {
+ from = VIsual.lnum;
+ }
+ if (to < VIsual.lnum) {
+ to = VIsual.lnum;
+ }
+ }
+ wp->w_old_cursor_fcol = fromc;
+ wp->w_old_cursor_lcol = toc;
+ }
+ } else {
+ // Use the line numbers of the old Visual area.
+ if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) {
+ from = wp->w_old_cursor_lnum;
+ to = wp->w_old_visual_lnum;
+ } else {
+ from = wp->w_old_visual_lnum;
+ to = wp->w_old_cursor_lnum;
+ }
+ }
+
+ // There is no need to update lines above the top of the window.
+ if (from < wp->w_topline) {
+ from = wp->w_topline;
+ }
+
+ // If we know the value of w_botline, use it to restrict the update to
+ // the lines that are visible in the window.
+ if (wp->w_valid & VALID_BOTLINE) {
+ if (from >= wp->w_botline) {
+ from = wp->w_botline - 1;
+ }
+ if (to >= wp->w_botline) {
+ to = wp->w_botline - 1;
+ }
+ }
+
+ // Find the minimal part to be updated.
+ // Watch out for scrolling that made entries in w_lines[] invalid.
+ // E.g., CTRL-U makes the first half of w_lines[] invalid and sets
+ // top_end; need to redraw from top_end to the "to" line.
+ // A middle mouse click with a Visual selection may change the text
+ // above the Visual area and reset wl_valid, do count these for
+ // mid_end (in srow).
+ if (mid_start > 0) {
+ lnum = wp->w_topline;
+ idx = 0;
+ srow = 0;
+ if (scrolled_down) {
+ mid_start = top_end;
+ } else {
+ mid_start = 0;
+ }
+ while (lnum < from && idx < wp->w_lines_valid) { // find start
+ if (wp->w_lines[idx].wl_valid) {
+ mid_start += wp->w_lines[idx].wl_size;
+ } else if (!scrolled_down) {
+ srow += wp->w_lines[idx].wl_size;
+ }
+ idx++;
+ if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) {
+ lnum = wp->w_lines[idx].wl_lnum;
+ } else {
+ lnum++;
+ }
+ }
+ srow += mid_start;
+ mid_end = wp->w_grid.rows;
+ for (; idx < wp->w_lines_valid; idx++) { // find end
+ if (wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum >= to + 1) {
+ // Only update until first row of this line
+ mid_end = srow;
+ break;
+ }
+ srow += wp->w_lines[idx].wl_size;
+ }
+ }
+ }
+
+ if (VIsual_active && buf == curwin->w_buffer) {
+ wp->w_old_visual_mode = (char)VIsual_mode;
+ wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
+ wp->w_old_visual_lnum = VIsual.lnum;
+ wp->w_old_visual_col = VIsual.col;
+ wp->w_old_curswant = curwin->w_curswant;
+ } else {
+ wp->w_old_visual_mode = 0;
+ wp->w_old_cursor_lnum = 0;
+ wp->w_old_visual_lnum = 0;
+ wp->w_old_visual_col = 0;
+ }
+
+ // reset got_int, otherwise regexp won't work
+ save_got_int = got_int;
+ got_int = 0;
+ // Set the time limit to 'redrawtime'.
+ proftime_T syntax_tm = profile_setlimit(p_rdt);
+ syn_set_timeout(&syntax_tm);
+
+ // Update all the window rows.
+ idx = 0; // first entry in w_lines[].wl_size
+ row = 0;
+ srow = 0;
+ lnum = wp->w_topline; // first line shown in window
+
+ win_extmark_arr.size = 0;
+
+ decor_redraw_reset(buf, &decor_state);
+
+ DecorProviders line_providers;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ (void)win_signcol_count(wp); // check if provider changed signcol width
+ if (must_redraw != 0) {
+ must_redraw = 0;
+ if (!called_decor_providers) {
+ called_decor_providers = true;
+ goto win_update_start;
+ }
+ }
+
+ bool cursorline_standout = win_cursorline_standout(wp);
+
+ win_check_ns_hl(wp);
+
+ for (;;) {
+ // stop updating when reached the end of the window (check for _past_
+ // the end of the window is at the end of the loop)
+ if (row == wp->w_grid.rows) {
+ didline = true;
+ break;
+ }
+
+ // stop updating when hit the end of the file
+ if (lnum > buf->b_ml.ml_line_count) {
+ eof = true;
+ break;
+ }
+
+ // Remember the starting row of the line that is going to be dealt
+ // with. It is used further down when the line doesn't fit.
+ srow = row;
+
+ // Update a line when it is in an area that needs updating, when it
+ // has changes or w_lines[idx] is invalid.
+ // "bot_start" may be halfway a wrapped line after using
+ // win_scroll_lines(), check if the current line includes it.
+ // When syntax folding is being used, the saved syntax states will
+ // already have been updated, we can't see where the syntax state is
+ // the same again, just update until the end of the window.
+ if (row < top_end
+ || (row >= mid_start && row < mid_end)
+ || top_to_mod
+ || idx >= wp->w_lines_valid
+ || (row + wp->w_lines[idx].wl_size > bot_start)
+ || (mod_top != 0
+ && (lnum == mod_top
+ || (lnum >= mod_top
+ && (lnum < mod_bot
+ || did_update == DID_FOLD
+ || (did_update == DID_LINE
+ && syntax_present(wp)
+ && ((foldmethodIsSyntax(wp)
+ && hasAnyFolding(wp))
+ || syntax_check_changed(lnum)))
+ // match in fixed position might need redraw
+ // if lines were inserted or deleted
+ || (wp->w_match_head != NULL
+ && buf->b_mod_xlines != 0)))))
+ || (cursorline_standout && lnum == wp->w_cursor.lnum)
+ || lnum == wp->w_last_cursorline) {
+ if (lnum == mod_top) {
+ top_to_mod = false;
+ }
+
+ // When at start of changed lines: May scroll following lines
+ // up or down to minimize redrawing.
+ // Don't do this when the change continues until the end.
+ // Don't scroll when dollar_vcol >= 0, keep the "$".
+ // Don't scroll when redrawing the top, scrolled already above.
+ if (lnum == mod_top
+ && mod_bot != MAXLNUM
+ && !(dollar_vcol >= 0 && mod_bot == mod_top + 1)
+ && row >= top_end) {
+ int old_rows = 0;
+ int new_rows = 0;
+ int xtra_rows;
+ linenr_T l;
+
+ // Count the old number of window rows, using w_lines[], which
+ // should still contain the sizes for the lines as they are
+ // currently displayed.
+ for (i = idx; i < wp->w_lines_valid; i++) {
+ // Only valid lines have a meaningful wl_lnum. Invalid
+ // lines are part of the changed area.
+ if (wp->w_lines[i].wl_valid
+ && wp->w_lines[i].wl_lnum == mod_bot) {
+ break;
+ }
+ old_rows += wp->w_lines[i].wl_size;
+ if (wp->w_lines[i].wl_valid
+ && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) {
+ // Must have found the last valid entry above mod_bot.
+ // Add following invalid entries.
+ i++;
+ while (i < wp->w_lines_valid
+ && !wp->w_lines[i].wl_valid) {
+ old_rows += wp->w_lines[i++].wl_size;
+ }
+ break;
+ }
+ }
+
+ if (i >= wp->w_lines_valid) {
+ // We can't find a valid line below the changed lines,
+ // need to redraw until the end of the window.
+ // Inserting/deleting lines has no use.
+ bot_start = 0;
+ } else {
+ // Able to count old number of rows: Count new window
+ // rows, and may insert/delete lines
+ j = idx;
+ for (l = lnum; l < mod_bot; l++) {
+ if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
+ new_rows++;
+ } else if (l == wp->w_topline) {
+ new_rows += plines_win_nofill(wp, l, true) + wp->w_topfill;
+ } else {
+ new_rows += plines_win(wp, l, true);
+ }
+ j++;
+ if (new_rows > wp->w_grid.rows - row - 2) {
+ // it's getting too much, must redraw the rest
+ new_rows = 9999;
+ break;
+ }
+ }
+ xtra_rows = new_rows - old_rows;
+ if (xtra_rows < 0) {
+ // May scroll text up. If there is not enough
+ // remaining text or scrolling fails, must redraw the
+ // rest. If scrolling works, must redraw the text
+ // below the scrolled text.
+ if (row - xtra_rows >= wp->w_grid.rows - 2) {
+ mod_bot = MAXLNUM;
+ } else {
+ win_scroll_lines(wp, row, xtra_rows);
+ bot_start = wp->w_grid.rows + xtra_rows;
+ }
+ } else if (xtra_rows > 0) {
+ // May scroll text down. If there is not enough
+ // remaining text of scrolling fails, must redraw the
+ // rest.
+ if (row + xtra_rows >= wp->w_grid.rows - 2) {
+ mod_bot = MAXLNUM;
+ } else {
+ win_scroll_lines(wp, row + old_rows, xtra_rows);
+ if (top_end > row + old_rows) {
+ // Scrolled the part at the top that requires
+ // updating down.
+ top_end += xtra_rows;
+ }
+ }
+ }
+
+ // When not updating the rest, may need to move w_lines[]
+ // entries.
+ if (mod_bot != MAXLNUM && i != j) {
+ if (j < i) {
+ int x = row + new_rows;
+
+ // move entries in w_lines[] upwards
+ for (;;) {
+ // stop at last valid entry in w_lines[]
+ if (i >= wp->w_lines_valid) {
+ wp->w_lines_valid = (int)j;
+ break;
+ }
+ wp->w_lines[j] = wp->w_lines[i];
+ // stop at a line that won't fit
+ if (x + (int)wp->w_lines[j].wl_size
+ > wp->w_grid.rows) {
+ wp->w_lines_valid = (int)j + 1;
+ break;
+ }
+ x += wp->w_lines[j++].wl_size;
+ i++;
+ }
+ if (bot_start > x) {
+ bot_start = x;
+ }
+ } else { // j > i
+ // move entries in w_lines[] downwards
+ j -= i;
+ wp->w_lines_valid += (linenr_T)j;
+ if (wp->w_lines_valid > wp->w_grid.rows) {
+ wp->w_lines_valid = wp->w_grid.rows;
+ }
+ for (i = wp->w_lines_valid; i - j >= idx; i--) {
+ wp->w_lines[i] = wp->w_lines[i - j];
+ }
+
+ // The w_lines[] entries for inserted lines are
+ // now invalid, but wl_size may be used above.
+ // Reset to zero.
+ while (i >= idx) {
+ wp->w_lines[i].wl_size = 0;
+ wp->w_lines[i--].wl_valid = false;
+ }
+ }
+ }
+ }
+ }
+
+ // When lines are folded, display one line for all of them.
+ // Otherwise, display normally (can be several display lines when
+ // 'wrap' is on).
+ foldinfo_T foldinfo = fold_info(wp, lnum);
+
+ if (foldinfo.fi_lines == 0
+ && idx < wp->w_lines_valid
+ && wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum == lnum
+ && lnum > wp->w_topline
+ && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows
+ && win_get_fill(wp, lnum) == 0) {
+ // This line is not going to fit. Don't draw anything here,
+ // will draw "@ " lines below.
+ row = wp->w_grid.rows + 1;
+ } else {
+ prepare_search_hl(wp, &screen_search_hl, lnum);
+ // Let the syntax stuff know we skipped a few lines.
+ if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
+ && syntax_present(wp)) {
+ syntax_end_parsing(syntax_last_parsed + 1);
+ }
+
+ // Display one line
+ row = win_line(wp, lnum, srow,
+ foldinfo.fi_lines ? srow : wp->w_grid.rows,
+ mod_top == 0, false, foldinfo, &line_providers, &provider_err);
+
+ if (foldinfo.fi_lines == 0) {
+ wp->w_lines[idx].wl_folded = false;
+ wp->w_lines[idx].wl_lastlnum = lnum;
+ did_update = DID_LINE;
+ syntax_last_parsed = lnum;
+ } else {
+ foldinfo.fi_lines--;
+ wp->w_lines[idx].wl_folded = true;
+ wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
+ did_update = DID_FOLD;
+ }
+ }
+
+ wp->w_lines[idx].wl_lnum = lnum;
+ wp->w_lines[idx].wl_valid = true;
+
+ if (row > wp->w_grid.rows) { // past end of grid
+ // we may need the size of that too long line later on
+ if (dollar_vcol == -1) {
+ wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true);
+ }
+ idx++;
+ break;
+ }
+ if (dollar_vcol == -1) {
+ wp->w_lines[idx].wl_size = (uint16_t)(row - srow);
+ }
+ idx++;
+ lnum += foldinfo.fi_lines + 1;
+ } else {
+ if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) {
+ // 'relativenumber' set and cursor moved vertically: The
+ // text doesn't need to be drawn, but the number column does.
+ foldinfo_T info = fold_info(wp, lnum);
+ (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true,
+ info, &line_providers, &provider_err);
+ }
+
+ // This line does not need to be drawn, advance to the next one.
+ row += wp->w_lines[idx++].wl_size;
+ if (row > wp->w_grid.rows) { // past end of screen
+ break;
+ }
+ lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
+ did_update = DID_NONE;
+ }
+
+ if (lnum > buf->b_ml.ml_line_count) {
+ eof = true;
+ break;
+ }
+ }
+ // End of loop over all window lines.
+
+ // Now that the window has been redrawn with the old and new cursor line,
+ // update w_last_cursorline.
+ wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0;
+
+ wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
+
+ if (idx > wp->w_lines_valid) {
+ wp->w_lines_valid = idx;
+ }
+
+ // Let the syntax stuff know we stop parsing here.
+ if (syntax_last_parsed != 0 && syntax_present(wp)) {
+ syntax_end_parsing(syntax_last_parsed + 1);
+ }
+
+ // If we didn't hit the end of the file, and we didn't finish the last
+ // line we were working on, then the line didn't fit.
+ wp->w_empty_rows = 0;
+ wp->w_filler_rows = 0;
+ if (!eof && !didline) {
+ int at_attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, HLF_AT));
+ if (lnum == wp->w_topline) {
+ // Single line that does not fit!
+ // Don't overwrite it, it can be edited.
+ wp->w_botline = lnum + 1;
+ } else if (win_get_fill(wp, lnum) >= wp->w_grid.rows - srow) {
+ // Window ends in filler lines.
+ wp->w_botline = lnum;
+ wp->w_filler_rows = wp->w_grid.rows - srow;
+ } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
+ int scr_row = wp->w_grid.rows - 1;
+
+ // Last line isn't finished: Display "@@@" in the last screen line.
+ grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr);
+
+ grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols,
+ '@', ' ', at_attr);
+ set_empty_rows(wp, srow);
+ wp->w_botline = lnum;
+ } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
+ int start_col = wp->w_grid.cols - 3;
+
+ // Last line isn't finished: Display "@@@" at the end.
+ grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
+ MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
+ set_empty_rows(wp, srow);
+ wp->w_botline = lnum;
+ } else {
+ win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT);
+ wp->w_botline = lnum;
+ }
+ } else {
+ if (eof) { // we hit the end of the file
+ wp->w_botline = buf->b_ml.ml_line_count + 1;
+ j = win_get_fill(wp, wp->w_botline);
+ if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) {
+ // Display filler text below last line. win_line() will check
+ // for ml_line_count+1 and only draw filler lines
+ foldinfo_T info = FOLDINFO_INIT;
+ row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
+ false, false, info, &line_providers, &provider_err);
+ }
+ } else if (dollar_vcol == -1) {
+ wp->w_botline = lnum;
+ }
+
+ // make sure the rest of the screen is blank
+ // write the 'eob' character to rows that aren't part of the file.
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows,
+ HLF_EOB);
+ }
+
+ kvi_destroy(line_providers);
+
+ if (wp->w_redr_type >= REDRAW_TOP) {
+ draw_vsep_win(wp);
+ draw_hsep_win(wp);
+ draw_sep_connectors_win(wp);
+ }
+ syn_set_timeout(NULL);
+
+ // Reset the type of redrawing required, the window has been updated.
+ wp->w_redr_type = 0;
+ wp->w_old_topfill = wp->w_topfill;
+ wp->w_old_botfill = wp->w_botfill;
+
+ // Send win_extmarks if needed
+ for (size_t n = 0; n < kv_size(win_extmark_arr); n++) {
+ ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle,
+ kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id,
+ kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col);
+ }
+
+ if (dollar_vcol == -1) {
+ // There is a trick with w_botline. If we invalidate it on each
+ // change that might modify it, this will cause a lot of expensive
+ // calls to plines_win() in update_topline() each time. Therefore the
+ // value of w_botline is often approximated, and this value is used to
+ // compute the value of w_topline. If the value of w_botline was
+ // wrong, check that the value of w_topline is correct (cursor is on
+ // the visible part of the text). If it's not, we need to redraw
+ // again. Mostly this just means scrolling up a few lines, so it
+ // doesn't look too bad. Only do this for the current window (where
+ // changes are relevant).
+ wp->w_valid |= VALID_BOTLINE;
+ wp->w_viewport_invalid = true;
+ if (wp == curwin && wp->w_botline != old_botline && !recursive) {
+ recursive = true;
+ curwin->w_valid &= ~VALID_TOPLINE;
+ update_topline(curwin); // may invalidate w_botline again
+ if (must_redraw != 0) {
+ // Don't update for changes in buffer again.
+ i = curbuf->b_mod_set;
+ curbuf->b_mod_set = false;
+ win_update(curwin, providers);
+ must_redraw = 0;
+ curbuf->b_mod_set = i;
+ }
+ recursive = false;
+ }
+ }
+
+ // restore got_int, unless CTRL-C was hit while redrawing
+ if (!got_int) {
+ got_int = save_got_int;
+ }
+}
+
+/// Redraw a window later, with update_screen(type).
+///
+/// Set must_redraw only if not already set to a higher value.
+/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
+void redraw_later(win_T *wp, int type)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!exiting && wp->w_redr_type < type) {
+ wp->w_redr_type = type;
+ if (type >= NOT_VALID) {
+ wp->w_lines_valid = 0;
+ }
+ if (must_redraw < type) { // must_redraw is the maximum of all windows
+ must_redraw = type;
+ }
+ }
+}
+
+/// Mark all windows to be redrawn later.
+void redraw_all_later(int type)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ redraw_later(wp, type);
+ }
+ // This may be needed when switching tabs.
+ if (must_redraw < type) {
+ must_redraw = type;
+ }
+}
+
+void screen_invalidate_highlights(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ redraw_later(wp, NOT_VALID);
+ wp->w_grid_alloc.valid = false;
+ }
+}
+
+/// Mark all windows that are editing the current buffer to be updated later.
+void redraw_curbuf_later(int type)
+{
+ redraw_buf_later(curbuf, type);
+}
+
+void redraw_buf_later(buf_T *buf, int type)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf) {
+ redraw_later(wp, type);
+ }
+ }
+}
+
+void redraw_buf_line_later(buf_T *buf, linenr_T line)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf
+ && line >= wp->w_topline && line < wp->w_botline) {
+ redrawWinline(wp, line);
+ }
+ }
+}
+
+void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf
+ && lastline >= wp->w_topline && firstline < wp->w_botline) {
+ if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) {
+ wp->w_redraw_top = firstline;
+ }
+ if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
+ wp->w_redraw_bot = lastline;
+ }
+ redraw_later(wp, VALID);
+ }
+ }
+}
+
+/// called when the status bars for the buffer 'buf' need to be updated
+void redraw_buf_status_later(buf_T *buf)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf
+ && (wp->w_status_height
+ || (wp == curwin && global_stl_height())
+ || wp->w_winbar_height)) {
+ wp->w_redr_status = true;
+ if (must_redraw < VALID) {
+ must_redraw = VALID;
+ }
+ }
+ }
+}
+
+/// Mark all status lines and window bars for redraw; used after first :cd
+void status_redraw_all(void)
+{
+ bool is_stl_global = global_stl_height() != 0;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)
+ || wp->w_winbar_height) {
+ wp->w_redr_status = true;
+ redraw_later(wp, VALID);
+ }
+ }
+}
+
+/// Marks all status lines and window bars of the current buffer for redraw.
+void status_redraw_curbuf(void)
+{
+ status_redraw_buf(curbuf);
+}
+
+/// Marks all status lines and window bars of the given buffer for redraw.
+void status_redraw_buf(buf_T *buf)
+{
+ bool is_stl_global = global_stl_height() != 0;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height)
+ || (is_stl_global && wp == curwin) || wp->w_winbar_height)) {
+ wp->w_redr_status = true;
+ redraw_later(wp, VALID);
+ }
+ }
+}
+
+/// Redraw all status lines that need to be redrawn.
+void redraw_statuslines(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_redr_status) {
+ win_redr_winbar(wp);
+ win_redr_status(wp);
+ }
+ }
+ if (redraw_tabline) {
+ draw_tabline();
+ }
+}
+
+/// Changed something in the current window, at buffer line "lnum", that
+/// requires that line and possibly other lines to be redrawn.
+/// Used when entering/leaving Insert mode with the cursor on a folded line.
+/// Used to remove the "$" from a change command.
+/// Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
+/// may become invalid and the whole window will have to be redrawn.
+void redrawWinline(win_T *wp, linenr_T lnum)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (lnum >= wp->w_topline
+ && lnum < wp->w_botline) {
+ if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
+ wp->w_redraw_top = lnum;
+ }
+ if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
+ wp->w_redraw_bot = lnum;
+ }
+ redraw_later(wp, VALID);
+ }
+}
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
new file mode 100644
index 0000000000..3eac1caaa1
--- /dev/null
+++ b/src/nvim/drawscreen.h
@@ -0,0 +1,25 @@
+#ifndef NVIM_DRAWSCREEN_H
+#define NVIM_DRAWSCREEN_H
+
+#include "nvim/drawline.h"
+
+/// flags for update_screen()
+/// The higher the value, the higher the priority
+enum {
+ VALID = 10, ///< buffer not changed, or changes marked with b_mod_*
+ INVERTED = 20, ///< redisplay inverted part that changed
+ INVERTED_ALL = 25, ///< redisplay whole inverted part
+ REDRAW_TOP = 30, ///< display first w_upd_rows screen lines
+ SOME_VALID = 35, ///< like NOT_VALID but may scroll
+ NOT_VALID = 40, ///< buffer needs complete redraw
+ CLEAR = 50, ///< screen messed up, clear it
+};
+
+/// While redrawing the screen this flag is set. It means the screen size
+/// ('lines' and 'rows') must not be changed.
+EXTERN bool updating_screen INIT(= 0);
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawscreen.h.generated.h"
+#endif
+#endif // NVIM_DRAWSCREEN_H
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 7ddff92632..05abeee550 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -16,6 +16,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/event/loop.h"
@@ -25,6 +26,7 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -46,9 +48,8 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/quickfix.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/state.h"
@@ -1747,7 +1748,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
replace_push(replaced);
replaced = NUL;
}
- ++start_col;
+ start_col++;
}
}
@@ -1913,7 +1914,7 @@ int get_literal(bool no_simplify)
cc = cc * 10 + nc - '0';
}
- ++i;
+ i++;
}
if (cc > 255
@@ -1948,7 +1949,7 @@ int get_literal(bool no_simplify)
cc = '\n';
}
- --no_mapping;
+ no_mapping--;
if (nc) {
vungetc(nc);
// A character typed with i_CTRL-V_digit cannot have modifiers.
@@ -3253,7 +3254,7 @@ int cursor_down(long n, int upd_topline)
if (hasFolding(lnum, NULL, &last)) {
lnum = last + 1;
} else {
- ++lnum;
+ lnum++;
}
if (lnum >= curbuf->b_ml.ml_line_count) {
break;
@@ -3433,7 +3434,7 @@ void replace_push(int c)
memmove(p + 1, p, (size_t)replace_offset);
}
*p = (char_u)c;
- ++replace_stack_nr;
+ replace_stack_nr++;
}
/*
@@ -3468,7 +3469,7 @@ static void replace_join(int off)
{
for (ssize_t i = replace_stack_nr; --i >= 0;) {
if (replace_stack[i] == NUL && off-- <= 0) {
- --replace_stack_nr;
+ replace_stack_nr--;
memmove(replace_stack + i, replace_stack + i + 1,
(size_t)(replace_stack_nr - i));
return;
@@ -3794,12 +3795,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
while (*look == '>') {
look++;
}
- }
- /*
- * Is it a word: "=word"?
- */
- else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
- ++look;
+ // Is it a word: "=word"?
+ } else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
+ look++;
if (*look == '~') {
icase = true;
look++;
@@ -4251,7 +4249,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Otherwise remove the mode message.
if (reg_recording != 0 || restart_edit != NUL) {
showmode();
- } else if (p_smd) {
+ } else if (p_smd && (got_int || !skip_showmode())) {
msg("");
}
// Exit Insert mode
@@ -5219,8 +5217,8 @@ static bool ins_tab(void)
// Find first white before the cursor
fpos = curwin->w_cursor;
while (fpos.col > 0 && ascii_iswhite(ptr[-1])) {
- --fpos.col;
- --ptr;
+ fpos.col--;
+ ptr--;
}
// In Replace mode, don't change characters before the insert point.
@@ -5252,8 +5250,8 @@ static bool ins_tab(void)
}
}
}
- ++fpos.col;
- ++ptr;
+ fpos.col++;
+ ptr++;
vcol += i;
}
@@ -5264,8 +5262,8 @@ static bool ins_tab(void)
// Skip over the spaces we need.
while (vcol < want_vcol && *ptr == ' ') {
vcol += lbr_chartabsize(line, ptr, vcol);
- ++ptr;
- ++repl_off;
+ ptr++;
+ repl_off++;
}
if (vcol > want_vcol) {
// Must have a char with 'showbreak' just before it.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ff94cf5944..21068e9101 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1,9 +1,7 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * eval.c: Expression evaluation.
- */
+// eval.c: Expression evaluation.
#include <math.h>
#include <stdlib.h>
@@ -76,19 +74,15 @@ static char * const namespace_char = "abglstvw";
/// Variable used for g:
static ScopeDictDictItem globvars_var;
-/*
- * Old Vim variables such as "v:version" are also available without the "v:".
- * Also in functions. We need a special hashtable for them.
- */
+/// Old Vim variables such as "v:version" are also available without the "v:".
+/// Also in functions. We need a special hashtable for them.
static hashtab_T compat_hashtab;
/// Used for checking if local variables or arguments used in a lambda.
bool *eval_lavars_used = NULL;
-/*
- * Array to hold the hashtab with variables local to each sourced script.
- * Each item holds a variable (nameless) that points to the dict_T.
- */
+/// Array to hold the hashtab with variables local to each sourced script.
+/// Each item holds a variable (nameless) that points to the dict_T.
typedef struct {
ScopeDictDictItem sv_var;
dict_T sv_dict;
@@ -103,9 +97,7 @@ static int echo_attr = 0; // attributes used for ":echo"
// The names of packages that once were loaded are remembered.
static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL };
-/*
- * Info used by a ":for" loop.
- */
+/// Info used by a ":for" loop.
typedef struct {
int fi_semicolon; // TRUE if ending in '; var]'
int fi_varcount; // nr of variables in the list
@@ -361,8 +353,6 @@ void eval_init(void)
{
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
- struct vimvar *p;
-
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
vimvardict.dv_lock = VAR_FIXED;
@@ -370,7 +360,7 @@ void eval_init(void)
func_init();
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
- p = &vimvars[i];
+ struct vimvar *p = &vimvars[i];
assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN);
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO) {
@@ -452,10 +442,8 @@ void eval_init(void)
#if defined(EXITFREE)
void eval_clear(void)
{
- struct vimvar *p;
-
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
- p = &vimvars[i];
+ struct vimvar *p = &vimvars[i];
if (p->vv_di.di_tv.v_type == VAR_STRING) {
XFREE_CLEAR(p->vv_str);
} else if (p->vv_di.di_tv.v_type == VAR_LIST) {
@@ -478,13 +466,13 @@ void eval_clear(void)
// autoloaded script names
ga_clear_strings(&ga_loaded);
- /* Script-local variables. First clear all the variables and in a second
- * loop free the scriptvar_T, because a variable in one script might hold
- * a reference to the whole scope of another script. */
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
+ // Script-local variables. First clear all the variables and in a second
+ // loop free the scriptvar_T, because a variable in one script might hold
+ // a reference to the whole scope of another script.
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
vars_clear(&SCRIPT_VARS(i));
}
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
xfree(SCRIPT_SV(i));
}
ga_clear(&ga_scripts);
@@ -523,10 +511,6 @@ static char *redir_varname = NULL;
/// @return OK if successfully completed the setup. FAIL otherwise.
int var_redir_start(char *name, int append)
{
- int save_emsg;
- int err;
- typval_T tv;
-
// Catch a bad name early.
if (!eval_isnamec1(*name)) {
emsg(_(e_invarg));
@@ -558,10 +542,11 @@ int var_redir_start(char *name, int append)
return FAIL;
}
- /* check if we can write to the variable: set it to or append an empty
- * string */
- save_emsg = did_emsg;
- did_emsg = FALSE;
+ // check if we can write to the variable: set it to or append an empty
+ // string
+ int save_emsg = did_emsg;
+ did_emsg = false;
+ typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = "";
if (append) {
@@ -570,7 +555,7 @@ int var_redir_start(char *name, int append)
set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
- err = did_emsg;
+ int err = did_emsg;
did_emsg |= save_emsg;
if (err) {
redir_endp = NULL; // don't store a value, only cleanup
@@ -590,12 +575,11 @@ int var_redir_start(char *name, int append)
/// :redir END
void var_redir_str(char *value, int value_len)
{
- int len;
-
if (redir_lval == NULL) {
return;
}
+ int len;
if (value_len == -1) {
len = (int)STRLEN(value); // Append the entire string
} else {
@@ -611,12 +595,11 @@ void var_redir_str(char *value, int value_len)
/// Frees the allocated memory.
void var_redir_stop(void)
{
- typval_T tv;
-
if (redir_lval != NULL) {
// If there was no error: assign the text to the variable.
if (redir_endp != NULL) {
ga_append(&redir_ga, NUL); // Append the trailing NUL.
+ typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
// Call get_lval() again, if it's inside a Dict or List it may
@@ -940,7 +923,7 @@ varnumber_T eval_to_number(char *expr)
varnumber_T retval;
char *p = skipwhite(expr);
- ++emsg_off;
+ emsg_off++;
if (eval1(&p, &rettv, true) == FAIL) {
retval = -1;
@@ -948,7 +931,7 @@ varnumber_T eval_to_number(char *expr)
retval = tv_get_number_chk(&rettv, NULL);
tv_clear(&rettv);
}
- --emsg_off;
+ emsg_off--;
return retval;
}
@@ -1005,11 +988,9 @@ void prepare_vimvar(int idx, typval_T *save_tv)
/// When no longer defined, remove the variable from the v: hashtable.
void restore_vimvar(int idx, typval_T *save_tv)
{
- hashitem_T *hi;
-
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
- hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
+ hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi)) {
internal_error("restore_vimvar()");
} else {
@@ -1045,7 +1026,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
vimvars[VV_VAL].vv_type = VAR_STRING;
vimvars[VV_VAL].vv_str = badword;
if (p_verbose == 0) {
- ++emsg_off;
+ emsg_off++;
}
if (eval1(&p, &rettv, true) == OK) {
@@ -1057,7 +1038,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
}
if (p_verbose == 0) {
- --emsg_off;
+ emsg_off--;
}
restore_vimvar(VV_VAL, &save_val);
@@ -1139,12 +1120,11 @@ varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv)
FUNC_ATTR_NONNULL_ALL
{
typval_T rettv;
- varnumber_T retval;
if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
return -1;
}
- retval = tv_get_number_chk(&rettv, NULL);
+ varnumber_T retval = tv_get_number_chk(&rettv, NULL);
tv_clear(&rettv);
return retval;
}
@@ -1204,11 +1184,11 @@ int eval_foldexpr(char *arg, int *cp)
varnumber_T retval;
int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL);
- ++emsg_off;
+ emsg_off++;
if (use_sandbox) {
- ++sandbox;
+ sandbox++;
}
- ++textlock;
+ textlock++;
*cp = NUL;
if (eval0(arg, &tv, NULL, true) == FAIL) {
retval = 0;
@@ -1229,11 +1209,11 @@ int eval_foldexpr(char *arg, int *cp)
}
tv_clear(&tv);
}
- --emsg_off;
+ emsg_off--;
if (use_sandbox) {
- --sandbox;
+ sandbox--;
}
- --textlock;
+ textlock--;
return (int)retval;
}
@@ -1267,12 +1247,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
const bool skip, const int flags, const int fne_flags)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
- dictitem_T *v;
- typval_T var1;
- typval_T var2;
- int empty1 = FALSE;
- listitem_T *ni;
- hashtab_T *ht = NULL;
+ bool empty1 = false;
int quiet = flags & GLV_QUIET;
// Clear everything in "lp".
@@ -1324,11 +1299,13 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
return p;
}
+ hashtab_T *ht = NULL;
+
// Only pass &ht when we would write to the variable, it prevents autoload
// as well.
- v = find_var(lp->ll_name, lp->ll_name_len,
- (flags & GLV_READ_ONLY) ? NULL : &ht,
- flags & GLV_NO_AUTOLOAD);
+ dictitem_T *v = find_var(lp->ll_name, lp->ll_name_len,
+ (flags & GLV_READ_ONLY) ? NULL : &ht,
+ flags & GLV_NO_AUTOLOAD);
if (v == NULL && !quiet) {
semsg(_("E121: Undefined variable: %.*s"),
(int)lp->ll_name_len, lp->ll_name);
@@ -1339,7 +1316,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
// Loop until no more [idx] or .key is following.
lp->ll_tv = &v->di_tv;
+ typval_T var1;
var1.v_type = VAR_UNKNOWN;
+ typval_T var2;
var2.v_type = VAR_UNKNOWN;
while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
@@ -1581,7 +1560,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
tv_clear(&var2);
if (lp->ll_n2 < 0) {
- ni = tv_list_find(lp->ll_list, (int)lp->ll_n2);
+ listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2);
if (ni == NULL) {
if (!quiet) {
semsg(_(e_listidx), (int64_t)lp->ll_n2);
@@ -1734,9 +1713,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
ll_n1++;
}
- /*
- * Assign the List values to the list items.
- */
+ // Assign the List values to the list items.
for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) {
if (op != NULL && *op != '=') {
eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op);
@@ -1857,7 +1834,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
}
if (skip) {
- ++emsg_skip;
+ emsg_skip++;
}
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
*errp = false;
@@ -1899,7 +1876,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
}
}
if (skip) {
- --emsg_skip;
+ emsg_skip--;
}
return fi;
@@ -1978,7 +1955,7 @@ void free_for_info(void *fi_void)
void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
FUNC_ATTR_NONNULL_ALL
{
- int got_eq = FALSE;
+ bool got_eq = false;
int c;
char *p;
@@ -2004,7 +1981,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
if (c == '&') {
c = (uint8_t)xp->xp_pattern[1];
if (c == '&') {
- ++xp->xp_pattern;
+ xp->xp_pattern++;
xp->xp_context = cmdidx != CMD_let || got_eq
? EXPAND_EXPRESSION : EXPAND_NOTHING;
} else if (c != ' ') {
@@ -2017,7 +1994,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
// environment variable
xp->xp_context = EXPAND_ENV_VARS;
} else if (c == '=') {
- got_eq = TRUE;
+ got_eq = true;
xp->xp_context = EXPAND_EXPRESSION;
} else if (c == '#'
&& xp->xp_context == EXPAND_EXPRESSION) {
@@ -2042,7 +2019,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
xp->xp_context = EXPAND_NOTHING;
} else if (c == '|') {
if (xp->xp_pattern[1] == '|') {
- ++xp->xp_pattern;
+ xp->xp_pattern++;
xp->xp_context = EXPAND_EXPRESSION;
} else {
xp->xp_context = EXPAND_COMMANDS;
@@ -2092,11 +2069,9 @@ void del_menutrans_vars(void)
hash_unlock(&globvarht);
}
-/*
- * Local string buffer for the next two functions to store a variable name
- * with its prefix. Allocated in cat_prefix_varname(), freed later in
- * get_user_var_name().
- */
+/// Local string buffer for the next two functions to store a variable name
+/// with its prefix. Allocated in cat_prefix_varname(), freed later in
+/// get_user_var_name().
static char *varnamebuf = NULL;
static size_t varnamebuflen = 0;
@@ -2140,10 +2115,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (gdone++ == 0) {
hi = globvarht.ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
if (STRNCMP("g:", xp->xp_pattern, 2) == 0) {
return cat_prefix_varname('g', (char *)hi->hi_key);
@@ -2157,10 +2132,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (bdone++ == 0) {
hi = ht->ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
return cat_prefix_varname('b', (char *)hi->hi_key);
}
@@ -2171,10 +2146,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (wdone++ == 0) {
hi = ht->ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
return cat_prefix_varname('w', (char *)hi->hi_key);
}
@@ -2185,10 +2160,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (tdone++ == 0) {
hi = ht->ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
return cat_prefix_varname('t', (char *)hi->hi_key);
}
@@ -2286,11 +2261,9 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
// TODO(ZyX-I): move to eval/expressions
-/*
- * The "evaluate" argument: When FALSE, the argument is only parsed but not
- * executed. The function may return OK, but the rettv will be of type
- * VAR_UNKNOWN. The function still returns FAIL for a syntax error.
- */
+/// The "evaluate" argument: When FALSE, the argument is only parsed but not
+/// executed. The function may return OK, but the rettv will be of type
+/// VAR_UNKNOWN. The function still returns FAIL for a syntax error.
/// Handle zero level expression.
/// This calls eval1() and handles error message and nextcmd.
@@ -2341,18 +2314,16 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// @return OK or FAIL.
int eval1(char **arg, typval_T *rettv, int evaluate)
{
- int result;
+ bool result;
typval_T var2;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval2(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
if ((*arg)[0] == '?') {
- result = FALSE;
+ result = false;
if (evaluate) {
bool error = false;
@@ -2365,17 +2336,13 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
}
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 1);
if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive!
return FAIL;
}
- /*
- * Check for the ":".
- */
+ // Check for the ":".
if ((*arg)[0] != ':') {
emsg(_("E109: Missing ':' after '?'"));
if (evaluate && result) {
@@ -2384,9 +2351,7 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
return FAIL;
}
- /*
- * Get the third variable.
- */
+ // Get the third variable.
*arg = skipwhite(*arg + 1);
if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive!
if (evaluate && result) {
@@ -2414,22 +2379,16 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
static int eval2(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
- long result;
- int first;
bool error = false;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval3(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
- /*
- * Repeat until there is no following "||".
- */
- first = TRUE;
- result = FALSE;
+ // Repeat until there is no following "||".
+ bool first = true;
+ bool result = false;
while ((*arg)[0] == '|' && (*arg)[1] == '|') {
if (evaluate && first) {
if (tv_get_number_chk(rettv, &error) != 0) {
@@ -2442,17 +2401,13 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
first = false;
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 2);
if (eval3(arg, &var2, evaluate && !result) == FAIL) {
return FAIL;
}
- /*
- * Compute the result.
- */
+ // Compute the result.
if (evaluate && !result) {
if (tv_get_number_chk(&var2, &error) != 0) {
result = true;
@@ -2483,22 +2438,16 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
static int eval3(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
- long result;
- int first;
bool error = false;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval4(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
- /*
- * Repeat until there is no following "&&".
- */
- first = TRUE;
- result = TRUE;
+ // Repeat until there is no following "&&".
+ bool first = true;
+ bool result = true;
while ((*arg)[0] == '&' && (*arg)[1] == '&') {
if (evaluate && first) {
if (tv_get_number_chk(rettv, &error) == 0) {
@@ -2511,17 +2460,13 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
first = false;
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 2);
if (eval4(arg, &var2, evaluate && result) == FAIL) {
return FAIL;
}
- /*
- * Compute the result.
- */
+ // Compute the result.
if (evaluate && result) {
if (tv_get_number_chk(&var2, &error) == 0) {
result = false;
@@ -2566,9 +2511,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
int len = 2;
bool ic;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval5(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
@@ -2617,9 +2560,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
break;
}
- /*
- * If there is a comparative operator, use it.
- */
+ // If there is a comparative operator, use it.
if (type != EXPR_UNKNOWN) {
// extra question mark appended: ignore case
if (p[len] == '?') {
@@ -2670,16 +2611,12 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
float_T f1 = 0, f2 = 0;
char *p;
- /*
- * Get the first variable.
- */
- if (eval6(arg, rettv, evaluate, FALSE) == FAIL) {
+ // Get the first variable.
+ if (eval6(arg, rettv, evaluate, false) == FAIL) {
return FAIL;
}
- /*
- * Repeat computing, until no '+', '-' or '.' is following.
- */
+ // Repeat computing, until no '+', '-' or '.' is following.
for (;;) {
op = (char_u)(**arg);
if (op != '+' && op != '-' && op != '.') {
@@ -2701,9 +2638,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
}
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
if (op == '.' && *(*arg + 1) == '.') { // ..string concatenation
(*arg)++;
}
@@ -2714,9 +2649,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
}
if (evaluate) {
- /*
- * Compute the result.
- */
+ // Compute the result.
if (op == '.') {
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
@@ -2845,16 +2778,12 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
float_T f1 = 0, f2 = 0;
bool error = false;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval7(arg, rettv, evaluate, want_string) == FAIL) {
return FAIL;
}
- /*
- * Repeat computing, until no '*', '/' or '%' is following.
- */
+ // Repeat computing, until no '*', '/' or '%' is following.
for (;;) {
op = (char_u)(**arg);
if (op != '*' && op != '/' && op != '%') {
@@ -2877,9 +2806,7 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
n1 = 0;
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 1);
if (eval7(arg, &var2, evaluate, false) == FAIL) {
return FAIL;
@@ -2904,10 +2831,8 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
}
}
- /*
- * Compute the result.
- * When either side is a float the result is a float.
- */
+ // Compute the result.
+ // When either side is a float the result is a float.
if (use_float) {
if (op == '*') {
f1 = f1 * f2;
@@ -3020,9 +2945,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
get_float = true;
p = skipdigits(p + 2);
if (*p == 'e' || *p == 'E') {
- ++p;
+ p++;
if (*p == '-' || *p == '+') {
- ++p;
+ p++;
}
if (!ascii_isdigit(*p)) {
get_float = false;
@@ -3133,13 +3058,13 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Register contents: @r.
case '@':
- ++*arg;
+ (*arg)++;
if (evaluate) {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc);
}
if (**arg != NUL) {
- ++*arg;
+ (*arg)++;
}
break;
@@ -3148,7 +3073,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
*arg = skipwhite(*arg + 1);
ret = eval1(arg, rettv, evaluate); // recursive!
if (**arg == ')') {
- ++*arg;
+ (*arg)++;
} else if (ret == OK) {
emsg(_("E110: Missing ')'"));
tv_clear(rettv);
@@ -3472,9 +3397,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
typval_T var1 = TV_INITIAL_VALUE;
typval_T var2 = TV_INITIAL_VALUE;
if (**arg == '.') {
- /*
- * dict.name
- */
+ // dict.name
key = *arg + 1;
for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
if (len == 0) {
@@ -3482,11 +3405,9 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
}
*arg = skipwhite(key + len);
} else {
- /*
- * something[idx]
- *
- * Get the (first) variable from inside the [].
- */
+ // something[idx]
+ //
+ // Get the (first) variable from inside the [].
*arg = skipwhite(*arg + 1);
if (**arg == ':') {
empty1 = true;
@@ -3498,9 +3419,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
return FAIL;
}
- /*
- * Get the second variable from inside the [:].
- */
+ // Get the second variable from inside the [:].
if (**arg == ':') {
range = true;
*arg = skipwhite(*arg + 1);
@@ -3741,11 +3660,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
- long numval;
- char *stringval;
- getoption_T opt_type;
bool working = (**arg == '+'); // has("+option")
- int ret = OK;
int opt_flags;
// Isolate the option name and find its value.
@@ -3762,10 +3677,14 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return OK;
}
+ long numval;
+ char *stringval;
+ int ret = OK;
+
char c = *option_end;
*option_end = NUL;
- opt_type = get_option_value(*arg, &numval,
- rettv == NULL ? NULL : &stringval, opt_flags);
+ getoption_T opt_type = get_option_value(*arg, &numval,
+ rettv == NULL ? NULL : &stringval, opt_flags);
if (opt_type == gov_unknown) {
if (rettv != NULL) {
@@ -3806,9 +3725,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
char *p;
unsigned int extra = 0;
- /*
- * Find the end of the string, skipping backslashed characters.
- */
+ // Find the end of the string, skipping backslashed characters.
for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
p++;
@@ -3832,10 +3749,8 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
return OK;
}
- /*
- * Copy the string into allocated memory, handling backslashed
- * characters.
- */
+ // Copy the string into allocated memory, handling backslashed
+ // characters.
const int len = (int)(p - *arg + extra);
char *name = xmalloc((size_t)len);
rettv->v_type = VAR_STRING;
@@ -3874,7 +3789,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
}
nr = 0;
while (--n >= 0 && ascii_isxdigit(p[1])) {
- ++p;
+ p++;
nr = (nr << 4) + hex2nr(*p);
}
p++;
@@ -3904,7 +3819,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
*name = (char)((*name << 3) + *p++ - '0');
}
}
- ++name;
+ name++;
break;
// Special key, e.g.: "\<C-W>"
@@ -3948,19 +3863,16 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
{
char *p;
- char *str;
int reduce = 0;
- /*
- * Find the end of the string, skipping ''.
- */
+ // Find the end of the string, skipping ''.
for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) {
if (*p == '\'') {
if (p[1] != '\'') {
break;
}
- ++reduce;
- ++p;
+ reduce++;
+ p++;
}
}
@@ -3975,10 +3887,8 @@ static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
return OK;
}
- /*
- * Copy the string into allocated memory, handling '' to ' reduction.
- */
- str = xmalloc((size_t)((p - *arg) - reduce));
+ // Copy the string into allocated memory, handling '' to ' reduction.
+ char *str = xmalloc((size_t)((p - *arg) - reduce));
rettv->v_type = VAR_STRING;
rettv->vval.v_string = str;
@@ -3987,7 +3897,7 @@ static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
if (p[1] != '\'') {
break;
}
- ++p;
+ p++;
}
mb_copy_char((const char **)&p, &str);
}
@@ -4088,16 +3998,14 @@ failret:
/// @param ic ignore case
bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
{
- char_u *s1, *s2;
- dict_T *d1, *d2;
- int a1, a2;
-
// empty and NULL function name considered the same
- s1 = (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial));
+ char_u *s1 =
+ (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial));
if (s1 != NULL && *s1 == NUL) {
s1 = NULL;
}
- s2 = (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial));
+ char_u *s2 =
+ (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial));
if (s2 != NULL && *s2 == NUL) {
s2 = NULL;
}
@@ -4110,8 +4018,8 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
}
// empty dict and NULL dict is different
- d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
- d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
+ dict_T *d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
+ dict_T *d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
if (d1 == NULL || d2 == NULL) {
if (d1 != d2) {
return false;
@@ -4121,8 +4029,8 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
}
// empty list and no list considered the same
- a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
- a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
+ int a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
+ int a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
if (a1 != a2) {
return false;
}
@@ -4151,25 +4059,23 @@ int get_copyID(void)
return current_copyID;
}
-/*
- * Garbage collection for lists and dictionaries.
- *
- * We use reference counts to be able to free most items right away when they
- * are no longer used. But for composite items it's possible that it becomes
- * unused while the reference count is > 0: When there is a recursive
- * reference. Example:
- * :let l = [1, 2, 3]
- * :let d = {9: l}
- * :let l[1] = d
- *
- * Since this is quite unusual we handle this with garbage collection: every
- * once in a while find out which lists and dicts are not referenced from any
- * variable.
- *
- * Here is a good reference text about garbage collection (refers to Python
- * but it applies to all reference-counting mechanisms):
- * http://python.ca/nas/python/gc/
- */
+/// Garbage collection for lists and dictionaries.
+///
+/// We use reference counts to be able to free most items right away when they
+/// are no longer used. But for composite items it's possible that it becomes
+/// unused while the reference count is > 0: When there is a recursive
+/// reference. Example:
+/// :let l = [1, 2, 3]
+/// :let d = {9: l}
+/// :let l[1] = d
+///
+/// Since this is quite unusual we handle this with garbage collection: every
+/// once in a while find out which lists and dicts are not referenced from any
+/// variable.
+///
+/// Here is a good reference text about garbage collection (refers to Python
+/// but it applies to all reference-counting mechanisms):
+/// http://python.ca/nas/python/gc/
/// Do garbage collection for lists and dicts.
///
@@ -4218,7 +4124,7 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_previous_funccal)(copyID);
// script-local variables
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL);
}
@@ -4677,21 +4583,16 @@ static int get_literal_key(char **arg, typval_T *tv)
/// @return OK or FAIL. Returns NOTDONE for {expr}.
static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
{
- dict_T *d = NULL;
- typval_T tvkey;
typval_T tv;
char *key = NULL;
- dictitem_T *item;
char *start = skipwhite(*arg + 1);
char buf[NUMBUFLEN];
- /*
- * First check if it's not a curly-braces thing: {expr}.
- * Must do this without evaluating, otherwise a function may be called
- * twice. Unfortunately this means we need to call eval1() twice for the
- * first item.
- * But {} is an empty Dictionary.
- */
+ // First check if it's not a curly-braces thing: {expr}.
+ // Must do this without evaluating, otherwise a function may be called
+ // twice. Unfortunately this means we need to call eval1() twice for the
+ // first item.
+ // But {} is an empty Dictionary.
if (*start != '}') {
if (eval1(&start, &tv, false) == FAIL) { // recursive!
return FAIL;
@@ -4701,9 +4602,11 @@ static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
}
}
+ dict_T *d = NULL;
if (evaluate) {
d = tv_dict_alloc();
}
+ typval_T tvkey;
tvkey.v_type = VAR_UNKNOWN;
tv.v_type = VAR_UNKNOWN;
@@ -4736,7 +4639,7 @@ static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
goto failret;
}
if (evaluate) {
- item = tv_dict_find(d, (const char *)key, -1);
+ dictitem_T *item = tv_dict_find(d, (const char *)key, -1);
if (item != NULL) {
semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key);
tv_clear(&tvkey);
@@ -4791,8 +4694,6 @@ failret:
size_t string2float(const char *const text, float_T *const ret_value)
FUNC_ATTR_NONNULL_ALL
{
- char *s = NULL;
-
// MS-Windows does not deal with "inf" and "nan" properly
if (STRNICMP(text, "inf", 3) == 0) {
*ret_value = (float_T)INFINITY;
@@ -4806,6 +4707,7 @@ size_t string2float(const char *const text, float_T *const ret_value)
*ret_value = (float_T)NAN;
return 3;
}
+ char *s = NULL;
*ret_value = strtod(text, &s);
return (size_t)(s - text);
}
@@ -4819,23 +4721,18 @@ size_t string2float(const char *const text, float_T *const ret_value)
/// @return FAIL if the name is invalid.
static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
{
- char *name;
- char *string = NULL;
- int len;
- int cc;
-
- ++*arg;
- name = *arg;
- len = get_env_len((const char **)arg);
+ (*arg)++;
+ char *name = *arg;
+ int len = get_env_len((const char **)arg);
if (evaluate) {
if (len == 0) {
return FAIL; // Invalid empty name.
}
- cc = (char_u)name[len];
+ int cc = (int)name[len];
name[len] = NUL;
// First try vim_getenv(), fast for normal environment vars.
- string = vim_getenv(name);
+ char *string = vim_getenv(name);
if (string == NULL || *string == NUL) {
xfree(string);
@@ -4853,18 +4750,6 @@ static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-/// Get the argument list for a given window
-void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
-{
- tv_list_alloc_ret(rettv, argcount);
- if (arglist != NULL) {
- for (int idx = 0; idx < argcount; idx++) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)alist_name(&arglist[idx]), -1);
- }
- }
-}
-
/// Add an assert error to v:errors.
void assert_error(garray_T *gap)
{
@@ -4894,17 +4779,10 @@ win_T *find_win_by_nr_or_id(typval_T *vp)
/// Implementation of map() and filter().
void filter_map(typval_T *argvars, typval_T *rettv, int map)
{
- typval_T *expr;
list_T *l = NULL;
- dictitem_T *di;
- hashtab_T *ht;
- hashitem_T *hi;
dict_T *d = NULL;
- typval_T save_val;
- typval_T save_key;
blob_T *b = NULL;
int rem = false;
- int todo;
char *ermsg = map ? "map()" : "filter()";
const char *const arg_errmsg = (map
? N_("map() argument")
@@ -4935,18 +4813,20 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
return;
}
- expr = &argvars[1];
+ typval_T *expr = &argvars[1];
// On type errors, the preceding call has already displayed an error
// message. Avoid a misleading error message for an empty string that
// was not passed as argument.
if (expr->v_type != VAR_UNKNOWN) {
+ typval_T save_val;
prepare_vimvar(VV_VAL, &save_val);
// We reset "did_emsg" to be able to detect whether an error
// occurred during evaluation of the expression.
save_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
+ typval_T save_key;
prepare_vimvar(VV_KEY, &save_key);
if (argvars[0].v_type == VAR_DICT) {
vimvars[VV_KEY].vv_type = VAR_STRING;
@@ -4955,14 +4835,14 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (map && d->dv_lock == VAR_UNLOCKED) {
d->dv_lock = VAR_LOCKED;
}
- ht = &d->dv_hashtab;
+ hashtab_T *ht = &d->dv_hashtab;
hash_lock(ht);
- todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ int todo = (int)ht->ht_used;
+ for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
- di = TV_DICT_HI2DI(hi);
+ dictitem_T *di = TV_DICT_HI2DI(hi);
if (map
&& (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
|| var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
@@ -5315,29 +5195,6 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
return (linenr_T)tv_get_number_chk(tv, NULL);
}
-void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
-{
- if (what_arg->v_type == VAR_UNKNOWN) {
- tv_list_alloc_ret(rettv, kListLenMayKnow);
- if (is_qf || wp != NULL) {
- (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
- }
- } else {
- tv_dict_alloc_ret(rettv);
- if (is_qf || wp != NULL) {
- if (what_arg->v_type == VAR_DICT) {
- dict_T *d = what_arg->vval.v_dict;
-
- if (d != NULL) {
- qf_get_properties(wp, d, rettv->vval.v_dict);
- }
- } else {
- emsg(_(e_dictreq));
- }
- }
- }
-}
-
/// @return information (variables, options, etc.) about a tab page
/// as a dictionary.
dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
@@ -5674,11 +5531,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
FUNC_ATTR_NONNULL_ARG(4, 5)
{
linenr_T lnum = lnum_arg + (append ? 1 : 0);
- const char *line = NULL;
- list_T *l = NULL;
- listitem_T *li = NULL;
long added = 0;
- linenr_T append_lnum;
buf_T *curbuf_save = NULL;
win_T *curwin_save = NULL;
const bool is_curbuf = buf == curbuf;
@@ -5700,6 +5553,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
find_win_for_curbuf();
}
+ linenr_T append_lnum;
if (append) {
// appendbufline() uses the line number below which we insert
append_lnum = lnum - 1;
@@ -5709,6 +5563,9 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
append_lnum = curbuf->b_ml.ml_line_count;
}
+ list_T *l = NULL;
+ listitem_T *li = NULL;
+ const char *line = NULL;
if (lines->v_type == VAR_LIST) {
l = lines->vval.v_list;
li = tv_list_first(l);
@@ -5792,7 +5649,6 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
{
- const void *iter = NULL;
list_T *const list = tv_list_alloc(kListLenShouldKnow);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = list;
@@ -5801,6 +5657,7 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
if (dirs == NULL) {
return;
}
+ const void *iter = NULL;
do {
size_t dir_len;
const char *dir;
@@ -5910,9 +5767,9 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
#ifdef USE_CRNL
// translate <CR><NL> into <NL>
char *d = res;
- for (char *s = res; *s; ++s) {
+ for (char *s = res; *s; s++) {
if (s[0] == CAR && s[1] == NL) {
- ++s;
+ s++;
}
*d++ = *s;
@@ -6484,12 +6341,9 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
// Argument can be [lnum, col, coladd].
if (tv->v_type == VAR_LIST) {
- list_T *l;
- int len;
bool error = false;
- listitem_T *li;
- l = tv->vval.v_list;
+ list_T *l = tv->vval.v_list;
if (l == NULL) {
return NULL;
}
@@ -6506,6 +6360,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (error) {
return NULL;
}
+ int len;
if (charcol) {
len = mb_charlen(ml_get(pos.lnum));
} else {
@@ -6513,7 +6368,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
// We accept "$" for the column number: last column.
- li = tv_list_find(l, 1L);
+ listitem_T *li = tv_list_find(l, 1L);
if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING
&& TV_LIST_ITEM_TV(li)->vval.v_string != NULL
&& STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) {
@@ -6613,8 +6468,6 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol)
{
list_T *l;
- int i = 0;
- long n;
// List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only
// there when "fnump" isn't NULL; "coladd" and "curswant" are optional.
@@ -6625,6 +6478,8 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
return FAIL;
}
+ int i = 0;
+ long n;
if (fnump != NULL) {
n = tv_list_find_nr(l, i++, NULL); // fnum
if (n < 0) {
@@ -6677,15 +6532,13 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
/// @return 0 for error.
int get_env_len(const char **arg)
{
- int len;
-
const char *p;
for (p = *arg; vim_isIDc(*p); p++) {}
if (p == *arg) { // No name found.
return 0;
}
- len = (int)(p - *arg);
+ int len = (int)(p - *arg);
*arg = p;
return len;
}
@@ -6733,8 +6586,6 @@ int get_id_len(const char **const arg)
/// 0 if something else is wrong.
int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose)
{
- int len;
-
*alias = NULL; // default to no alias
if ((*arg)[0] == (char)K_SPECIAL && (*arg)[1] == (char)KS_EXTRA
@@ -6743,7 +6594,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
*arg += 3;
return get_id_len(arg) + 3;
}
- len = eval_fname_script(*arg);
+ int len = eval_fname_script(*arg);
if (len > 0) {
// literal "<SID>", "s:" or "<SNR>"
*arg += len;
@@ -6761,10 +6612,8 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
return len;
}
- /*
- * Include any <SID> etc in the expanded string:
- * Thus the -len here.
- */
+ // Include any <SID> etc in the expanded string:
+ // Thus the -len here.
char *temp_string = make_expanded_name(*arg - len, expr_start, expr_end, (char *)p);
if (temp_string == NULL) {
return -1;
@@ -6796,10 +6645,6 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
const char *find_name_end(const char *arg, const char **expr_start, const char **expr_end,
int flags)
{
- int mb_nest = 0;
- int br_nest = 0;
- int len;
-
if (expr_start != NULL) {
*expr_start = NULL;
*expr_end = NULL;
@@ -6810,6 +6655,10 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
return arg;
}
+ int mb_nest = 0;
+ int br_nest = 0;
+ int len;
+
const char *p;
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
@@ -6827,7 +6676,7 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
// skip over "str\"ing" to avoid counting [ and ] inside it.
for (p = p + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
- ++p;
+ p++;
}
}
if (*p == NUL) {
@@ -6845,9 +6694,9 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
if (mb_nest == 0) {
if (*p == '[') {
- ++br_nest;
+ br_nest++;
} else if (*p == ']') {
- --br_nest;
+ br_nest--;
}
}
@@ -6883,20 +6732,19 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
static char *make_expanded_name(const char *in_start, char *expr_start, char *expr_end,
char *in_end)
{
- char c1;
- char *retval = NULL;
- char *temp_result;
- char *nextcmd = NULL;
-
if (expr_end == NULL || in_end == NULL) {
return NULL;
}
+
+ char *retval = NULL;
+ char *nextcmd = NULL;
+
*expr_start = NUL;
*expr_end = NUL;
- c1 = *in_end;
+ char c1 = *in_end;
*in_end = NUL;
- temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
+ char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
if (temp_result != NULL && nextcmd == NULL) {
retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);
@@ -7339,7 +7187,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
if (rettv->v_type == VAR_DICT) {
selfdict = rettv->vval.v_dict;
if (selfdict != NULL) {
- ++selfdict->dv_refcount;
+ selfdict->dv_refcount++;
}
} else {
selfdict = NULL;
@@ -7416,8 +7264,6 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
const size_t varname_len, int no_autoload)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- hashitem_T *hi;
-
if (varname_len == 0) {
// Must be something like "s:", otherwise "ht" would be NULL.
switch (htname) {
@@ -7441,7 +7287,7 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
return NULL;
}
- hi = hash_find_len(ht, varname, varname_len);
+ hashitem_T *hi = hash_find_len(ht, varname, varname_len);
if (HASHITEM_EMPTY(hi)) {
// For global variables we may try auto-loading the script. If it
// worked find the variable again. Don't auto-load a script if it was
@@ -7476,7 +7322,6 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname,
dict_T **d)
{
- hashitem_T *hi;
funccall_T *funccal = get_funccal();
*d = NULL;
@@ -7492,7 +7337,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
*varname = name;
// "version" is "v:version" in all scopes
- hi = hash_find_len(&compat_hashtab, name, name_len);
+ hashitem_T *hi = hash_find_len(&compat_hashtab, name, name_len);
if (!HASHITEM_EMPTY(hi)) {
return &compat_hashtab;
}
@@ -7581,28 +7426,26 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var
/// sourcing this script and when executing functions defined in the script.
void new_script_vars(scid_T id)
{
- hashtab_T *ht;
scriptvar_T *sv;
ga_grow(&ga_scripts, id - ga_scripts.ga_len);
- {
- /* Re-allocating ga_data means that an ht_array pointing to
- * ht_smallarray becomes invalid. We can recognize this: ht_mask is
- * at its init value. Also reset "v_dict", it's always the same. */
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
- ht = &SCRIPT_VARS(i);
- if (ht->ht_mask == HT_INIT_SIZE - 1) {
- ht->ht_array = ht->ht_smallarray;
- }
- sv = SCRIPT_SV(i);
- sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
- }
- while (ga_scripts.ga_len < id) {
- sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T));
- init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
- ++ga_scripts.ga_len;
+ // Re-allocating ga_data means that an ht_array pointing to
+ // ht_smallarray becomes invalid. We can recognize this: ht_mask is
+ // at its init value. Also reset "v_dict", it's always the same.
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
+ hashtab_T *ht = &SCRIPT_VARS(i);
+ if (ht->ht_mask == HT_INIT_SIZE - 1) {
+ ht->ht_array = ht->ht_smallarray;
}
+ sv = SCRIPT_SV(i);
+ sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
+ }
+
+ while (ga_scripts.ga_len < id) {
+ sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T));
+ init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
+ ga_scripts.ga_len++;
}
}
@@ -7626,8 +7469,8 @@ void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, ScopeType scope)
/// Unreference a dictionary initialized by init_var_dict().
void unref_var_dict(dict_T *dict)
{
- /* Now the dict needs to be freed if no one else is using it, go back to
- * normal reference counting. */
+ // Now the dict needs to be freed if no one else is using it, go back to
+ // normal reference counting.
dict->dv_refcount -= DO_NOT_FREE_CNT - 1;
tv_dict_unref(dict);
}
@@ -7659,7 +7502,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
emsg(_("E698: variable nested too deep for making a copy"));
return FAIL;
}
- ++recurse;
+ recurse++;
switch (from->v_type) {
case VAR_NUMBER:
@@ -7712,7 +7555,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
} else if (copyID != 0 && from->vval.v_dict->dv_copyID == copyID) {
// use the copy made earlier
to->vval.v_dict = from->vval.v_dict->dv_copydict;
- ++to->vval.v_dict->dv_refcount;
+ to->vval.v_dict->dv_refcount++;
} else {
to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID);
}
@@ -7724,7 +7567,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
internal_error("var_item_copy(UNKNOWN)");
ret = FAIL;
}
- --recurse;
+ recurse--;
return ret;
}
@@ -7741,7 +7584,7 @@ void ex_echo(exarg_T *eap)
const int called_emsg_before = called_emsg;
if (eap->skip) {
- ++emsg_skip;
+ emsg_skip++;
}
while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) {
// If eval1() causes an error message the text from the command may
@@ -7821,12 +7664,11 @@ void ex_execute(exarg_T *eap)
typval_T rettv;
int ret = OK;
garray_T ga;
- int save_did_emsg;
ga_init(&ga, 1, 80);
if (eap->skip) {
- ++emsg_skip;
+ emsg_skip++;
}
while (*arg != NUL && *arg != '|' && *arg != '\n') {
ret = eval1_emsg(&arg, &rettv, !eap->skip);
@@ -7870,7 +7712,7 @@ void ex_execute(exarg_T *eap)
ui_flush();
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
- save_did_emsg = did_emsg;
+ int save_did_emsg = did_emsg;
msg_ext_set_kind("echoerr");
emsg(ga.ga_data);
if (!force_abort) {
@@ -7884,7 +7726,7 @@ void ex_execute(exarg_T *eap)
ga_clear(&ga);
if (eap->skip) {
- --emsg_skip;
+ emsg_skip--;
}
eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
@@ -7900,7 +7742,7 @@ const char *find_option_end(const char **const arg, int *const opt_flags)
{
const char *p = *arg;
- ++p;
+ p++;
if (*p == 'g' && p[1] == ':') {
*opt_flags = OPT_GLOBAL;
p += 2;
@@ -8087,11 +7929,9 @@ int store_session_globals(FILE *fd)
}
if ((fprintf(fd, "let %s = %c%s%c",
this_var->di_key,
- ((this_var->di_tv.v_type == VAR_STRING) ? '"'
- : ' '),
+ ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' '),
p,
- ((this_var->di_tv.v_type == VAR_STRING) ? '"'
- : ' ')) < 0)
+ ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' ')) < 0)
|| put_eol(fd) == FAIL) {
xfree(p);
return FAIL;
@@ -8176,10 +8016,8 @@ int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, cha
size_t *fnamelen)
{
int valid = 0;
- char *tail;
char *s, *p, *pbuf;
char dirname[MAXPATHL];
- int c;
bool has_fullname = false;
bool has_homerelative = false;
@@ -8241,6 +8079,8 @@ repeat:
}
}
+ int c;
+
// ":." - path relative to the current directory
// ":~" - path relative to the home directory
// ":8" - shortname path - postponed till after
@@ -8306,7 +8146,7 @@ repeat:
}
}
- tail = path_tail(*fnamep);
+ char *tail = path_tail(*fnamep);
*fnamelen = STRLEN(*fnamep);
// ":h" - head, remove "/file_name", can be repeated
@@ -8347,10 +8187,9 @@ repeat:
// ":r" - root, without extension, can be repeated
while (src[*usedlen] == ':'
&& (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) {
- /* find a '.' in the tail:
- * - for second :e: before the current fname
- * - otherwise: The last '.'
- */
+ // find a '.' in the tail:
+ // - for second :e: before the current fname
+ // - otherwise: The last '.'
const bool is_second_e = *fnamep > tail;
if (src[*usedlen + 1] == 'e' && is_second_e) {
s = (*fnamep) - 2;
@@ -8401,18 +8240,16 @@ repeat:
if (src[*usedlen] == ':'
&& (src[*usedlen + 1] == 's'
|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) {
- int sep;
- char *flags;
- int didit = false;
+ bool didit = false;
- flags = "";
+ char *flags = "";
s = src + *usedlen + 2;
if (src[*usedlen + 1] == 'g') {
flags = "g";
s++;
}
- sep = (char_u)(*s++);
+ int sep = (char_u)(*s++);
if (sep) {
// find end of pattern
p = vim_strchr(s, sep);
@@ -8430,7 +8267,7 @@ repeat:
*fnamelen = STRLEN(s);
xfree(*bufp);
*bufp = s;
- didit = TRUE;
+ didit = true;
xfree(sub);
xfree(str);
}
@@ -8471,26 +8308,22 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
{
int sublen;
regmatch_T regmatch;
- int do_all;
- char *tail;
- char *end;
garray_T ga;
- char *save_cpo;
char *zero_width = NULL;
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
- save_cpo = p_cpo;
+ char *save_cpo = p_cpo;
p_cpo = (char *)empty_option;
ga_init(&ga, 1, 200);
- do_all = (flags[0] == 'g');
+ int do_all = (flags[0] == 'g');
regmatch.rm_ic = p_ic;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
- tail = str;
- end = str + STRLEN(str);
+ char *tail = str;
+ char *end = str + STRLEN(str);
while (vim_regexec_nl(&regmatch, (char_u *)str, (colnr_T)(tail - str))) {
// Skip empty match except for first match.
if (regmatch.startp[0] == regmatch.endp[0]) {
@@ -8773,8 +8606,6 @@ void invoke_prompt_callback(void)
{
typval_T rettv;
typval_T argv[2];
- char *text;
- char *prompt;
linenr_T lnum = curbuf->b_ml.ml_line_count;
// Add a new line for the prompt before invoking the callback, so that
@@ -8786,8 +8617,8 @@ void invoke_prompt_callback(void)
if (curbuf->b_prompt_callback.type == kCallbackNone) {
return;
}
- text = (char *)ml_get(lnum);
- prompt = (char *)prompt_text();
+ char *text = (char *)ml_get(lnum);
+ char *prompt = (char *)prompt_text();
if (STRLEN(text) >= STRLEN(prompt)) {
text += STRLEN(prompt);
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 609d2f0f7b..456fd9fdf6 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -7,6 +7,7 @@
#include "nvim/api/private/converter.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/buffer.h"
@@ -33,6 +34,7 @@
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
@@ -55,11 +57,10 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/dl.h"
-#include "nvim/os/input.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
@@ -374,77 +375,6 @@ static void f_appendbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- if (argvars[0].v_type == VAR_UNKNOWN) {
- // use the current window
- rettv->vval.v_number = ARGCOUNT;
- } else if (argvars[0].v_type == VAR_NUMBER
- && tv_get_number(&argvars[0]) == -1) {
- // use the global argument list
- rettv->vval.v_number = GARGCOUNT;
- } else {
- // use the argument list of the specified window
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp != NULL) {
- rettv->vval.v_number = WARGCOUNT(wp);
- } else {
- rettv->vval.v_number = -1;
- }
- }
-}
-
-/// "argidx()" function
-static void f_argidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = curwin->w_arg_idx;
-}
-
-/// "arglistid" function
-static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = -1;
- win_T *wp = find_tabwin(&argvars[0], &argvars[1]);
- if (wp != NULL) {
- rettv->vval.v_number = wp->w_alist->id;
- }
-}
-
-/// "argv(nr)" function
-static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- aentry_T *arglist = NULL;
- int argcount = -1;
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type == VAR_UNKNOWN) {
- arglist = ARGLIST;
- argcount = ARGCOUNT;
- } else if (argvars[1].v_type == VAR_NUMBER
- && tv_get_number(&argvars[1]) == -1) {
- arglist = GARGLIST;
- argcount = GARGCOUNT;
- } else {
- win_T *wp = find_win_by_nr_or_id(&argvars[1]);
- if (wp != NULL) {
- // Use the argument list of the specified window
- arglist = WARGLIST(wp);
- argcount = WARGCOUNT(wp);
- }
- }
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- int idx = (int)tv_get_number_chk(&argvars[0], NULL);
- if (arglist != NULL && idx >= 0 && idx < argcount) {
- rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
- } else if (idx == -1) {
- get_arglist_as_rettv(arglist, argcount, rettv);
- }
- } else {
- get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
- }
-}
-
/// "atan2()" function
static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -2704,147 +2634,6 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "getchar()" and "getcharstr()" functions
-static void getchar_common(typval_T *argvars, typval_T *rettv)
- FUNC_ATTR_NONNULL_ALL
-{
- varnumber_T n;
- bool error = false;
-
- no_mapping++;
- allow_keys++;
- for (;;) {
- // Position the cursor. Needed after a message that ends in a space,
- // or if event processing caused a redraw.
- ui_cursor_goto(msg_row, msg_col);
-
- if (argvars[0].v_type == VAR_UNKNOWN) {
- // getchar(): blocking wait.
- // TODO(bfredl): deduplicate shared logic with state_enter ?
- if (!char_avail()) {
- (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
- if (!multiqueue_empty(main_loop.events)) {
- state_handle_k_event();
- continue;
- }
- }
- n = safe_vgetc();
- } else if (tv_get_number_chk(&argvars[0], &error) == 1) {
- // getchar(1): only check if char avail
- n = vpeekc_any();
- } else if (error || vpeekc_any() == NUL) {
- // illegal argument or getchar(0) and no char avail: return zero
- n = 0;
- } else {
- // getchar(0) and char avail() != NUL: get a character.
- // Note that vpeekc_any() returns K_SPECIAL for K_IGNORE.
- n = safe_vgetc();
- }
-
- if (n == K_IGNORE
- || n == K_MOUSEMOVE
- || n == K_VER_SCROLLBAR
- || n == K_HOR_SCROLLBAR) {
- continue;
- }
- break;
- }
- no_mapping--;
- allow_keys--;
-
- if (!ui_has_messages()) {
- // redraw the screen after getchar()
- update_screen(CLEAR);
- }
-
- set_vim_var_nr(VV_MOUSE_WIN, 0);
- set_vim_var_nr(VV_MOUSE_WINID, 0);
- set_vim_var_nr(VV_MOUSE_LNUM, 0);
- set_vim_var_nr(VV_MOUSE_COL, 0);
-
- rettv->vval.v_number = n;
- if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
- char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
- int i = 0;
-
- // Turn a special key into three bytes, plus modifier.
- if (mod_mask != 0) {
- temp[i++] = K_SPECIAL;
- temp[i++] = KS_MODIFIER;
- temp[i++] = (char_u)mod_mask;
- }
- if (IS_SPECIAL(n)) {
- temp[i++] = K_SPECIAL;
- temp[i++] = (char_u)K_SECOND(n);
- temp[i++] = K_THIRD(n);
- } else {
- i += utf_char2bytes((int)n, (char *)temp + i);
- }
- assert(i < 10);
- temp[i++] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)vim_strsave(temp);
-
- if (is_mouse_key((int)n)) {
- int row = mouse_row;
- int col = mouse_col;
- int grid = mouse_grid;
- linenr_T lnum;
- win_T *wp;
- int winnr = 1;
-
- if (row >= 0 && col >= 0) {
- // Find the window at the mouse coordinates and compute the
- // text position.
- win_T *const win = mouse_find_win(&grid, &row, &col);
- if (win == NULL) {
- return;
- }
- (void)mouse_comp_pos(win, &row, &col, &lnum);
- for (wp = firstwin; wp != win; wp = wp->w_next) {
- winnr++;
- }
- set_vim_var_nr(VV_MOUSE_WIN, winnr);
- set_vim_var_nr(VV_MOUSE_WINID, wp->handle);
- set_vim_var_nr(VV_MOUSE_LNUM, lnum);
- set_vim_var_nr(VV_MOUSE_COL, col + 1);
- }
- }
- }
-}
-
-/// "getchar()" function
-static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- getchar_common(argvars, rettv);
-}
-
-/// "getcharstr()" function
-static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- getchar_common(argvars, rettv);
-
- if (rettv->v_type == VAR_NUMBER) {
- char temp[7]; // mbyte-char: 6, NUL: 1
- const varnumber_T n = rettv->vval.v_number;
- int i = 0;
-
- if (n != 0) {
- i += utf_char2bytes((int)n, (char *)temp);
- }
- assert(i < 7);
- temp[i++] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(temp);
- }
-}
-
-/// "getcharmod()" function
-static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = mod_mask;
-}
-
static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool charcol)
{
pos_T *fp = NULL;
@@ -3308,13 +3097,6 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_buffer_lines(curbuf, lnum, end, retlist, rettv);
}
-/// "getloclist()" function
-static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- get_qf_loc_list(false, wp, &argvars[1], rettv);
-}
-
/// "getmarklist()" function
static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -3397,12 +3179,6 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getpos_both(argvars, rettv, false, false);
}
-/// "getqflist()" functions
-static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- get_qf_loc_list(true, NULL, &argvars[0], rettv);
-}
-
/// Common between getreg(), getreginfo() and getregtype(): get the register
/// name from the first argument.
/// Returns zero on error.
@@ -4778,7 +4554,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir_executable((const char *)cwd)) {
+ if (!os_isdir((const char_u *)cwd)) {
semsg(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -7972,110 +7748,12 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
}
-/// Create quickfix/location list from VimL values
-///
-/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
-/// args argument in which case errors out, including VAR_UNKNOWN parameters.
-///
-/// @param[in,out] wp Window to create location list for. May be NULL in
-/// which case quickfix list will be created.
-/// @param[in] args [list, action, what]
-/// @param[in] args[0] Quickfix list contents.
-/// @param[in] args[1] Optional. Action to perform:
-/// append to an existing list, replace its content,
-/// or create a new one.
-/// @param[in] args[2] Optional. Quickfix list properties or title.
-/// Defaults to caller function name.
-/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
-static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
- FUNC_ATTR_NONNULL_ARG(2, 3)
-{
- static char *e_invact = N_("E927: Invalid action: '%s'");
- const char *title = NULL;
- char action = ' ';
- static int recursive = 0;
- rettv->vval.v_number = -1;
- dict_T *what = NULL;
-
- typval_T *list_arg = &args[0];
- if (list_arg->v_type != VAR_LIST) {
- emsg(_(e_listreq));
- return;
- } else if (recursive != 0) {
- emsg(_(e_au_recursive));
- return;
- }
-
- typval_T *action_arg = &args[1];
- if (action_arg->v_type == VAR_UNKNOWN) {
- // Option argument was not given.
- goto skip_args;
- } else if (action_arg->v_type != VAR_STRING) {
- emsg(_(e_stringreq));
- return;
- }
- const char *const act = tv_get_string_chk(action_arg);
- if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
- && act[1] == NUL) {
- action = *act;
- } else {
- semsg(_(e_invact), act);
- return;
- }
-
- typval_T *const what_arg = &args[2];
- if (what_arg->v_type == VAR_UNKNOWN) {
- // Option argument was not given.
- goto skip_args;
- } else if (what_arg->v_type == VAR_STRING) {
- title = tv_get_string_chk(what_arg);
- if (!title) {
- // Type error. Error already printed by tv_get_string_chk().
- return;
- }
- } else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
- what = what_arg->vval.v_dict;
- } else {
- emsg(_(e_dictreq));
- return;
- }
-
-skip_args:
- if (!title) {
- title = (wp ? ":setloclist()" : ":setqflist()");
- }
-
- recursive++;
- list_T *const l = list_arg->vval.v_list;
- if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
- rettv->vval.v_number = 0;
- }
- recursive--;
-}
-
-/// "setloclist()" function
-static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = -1;
-
- win_T *win = find_win_by_nr_or_id(&argvars[0]);
- if (win != NULL) {
- set_qf_ll_list(win, &argvars[1], rettv);
- }
-}
-
/// "setpos()" function
static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
set_position(argvars, rettv, false);
}
-/// "setqflist()" function
-static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- set_qf_ll_list(NULL, argvars, rettv);
-}
-
/// Translate a register type string to the yank type and block length
static int get_yank_type(char **const pp, MotionType *const yank_type, long *const block_len)
FUNC_ATTR_NONNULL_ALL
@@ -9572,7 +9250,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir_executable(cwd)) {
+ if (!os_isdir((const char_u *)cwd)) {
semsg(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c527c70be0..c46cb6ba5d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -448,13 +448,13 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
ret = FAIL;
break;
}
- ++argcount;
+ argcount++;
if (*argp != ',') {
break;
}
}
if (*argp == ')') {
- ++argp;
+ argp++;
} else {
ret = FAIL;
}
@@ -845,7 +845,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
rettv->vval.v_number = -1;
return;
}
- ++depth;
+ depth++;
// Save search patterns and redo buffer.
save_search_patterns();
if (!ins_compl_active()) {
@@ -1017,7 +1017,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
estack_push_ufunc(fp, 1);
if (p_verbose >= 12) {
- ++no_wait_return;
+ no_wait_return++;
verbose_enter_scroll();
smsg(_("calling %s"), SOURCING_NAME);
@@ -1051,7 +1051,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
- --no_wait_return;
+ no_wait_return--;
}
const bool do_profiling_yes = do_profiling == PROF_YES;
@@ -1101,7 +1101,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
}
- --RedrawingDisabled;
+ RedrawingDisabled--;
// when the function was aborted because of an error, return -1
if ((did_emsg
@@ -1131,7 +1131,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// when being verbose, mention the return value
if (p_verbose >= 12) {
- ++no_wait_return;
+ no_wait_return++;
verbose_enter_scroll();
if (aborting()) {
@@ -1161,7 +1161,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
- --no_wait_return;
+ no_wait_return--;
}
estack_pop();
@@ -1929,7 +1929,7 @@ void ex_function(exarg_T *eap)
todo = (int)func_hashtab.ht_used;
for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
fp = HI2UF(hi);
if (message_filtered(fp->uf_name)) {
continue;
@@ -1962,7 +1962,7 @@ void ex_function(exarg_T *eap)
todo = (int)func_hashtab.ht_used;
for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
fp = HI2UF(hi);
if (!isdigit(*fp->uf_name)
&& vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
@@ -1974,7 +1974,7 @@ void ex_function(exarg_T *eap)
}
}
if (*p == '/') {
- ++p;
+ p++;
}
eap->nextcmd = (char *)check_nextcmd(p);
return;
@@ -2096,9 +2096,8 @@ void ex_function(exarg_T *eap)
}
if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
int j = (*arg == K_SPECIAL) ? 3 : 0;
- while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j])
- : eval_isnamec(arg[j]))) {
- ++j;
+ while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) {
+ j++;
}
if (arg[j] != NUL) {
emsg_funcname((char *)e_invarg2, arg);
@@ -2645,10 +2644,10 @@ char *get_user_func_name(expand_T *xp, int idx)
assert(hi);
if (done < func_hashtab.ht_used) {
if (done++ > 0) {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
fp = HI2UF(hi);
@@ -2856,7 +2855,7 @@ void ex_return(exarg_T *eap)
}
if (eap->skip) {
- ++emsg_skip;
+ emsg_skip++;
}
eap->nextcmd = NULL;
@@ -2888,7 +2887,7 @@ void ex_return(exarg_T *eap)
}
if (eap->skip) {
- --emsg_skip;
+ emsg_skip--;
}
}
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index ffaf636208..1ede7b35d3 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -7,6 +7,7 @@
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
@@ -18,6 +19,7 @@
#include "nvim/ex_eval.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/window.h"
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index ce15a9c86f..bc8e823797 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -15,6 +15,7 @@
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
@@ -25,6 +26,7 @@
#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
@@ -37,6 +39,7 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/help.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -63,7 +66,6 @@
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
@@ -302,7 +304,7 @@ void ex_align(exarg_T *eap)
new_indent--;
break;
}
- --new_indent;
+ new_indent--;
}
}
}
@@ -1093,12 +1095,12 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
if (line1 == n) {
line1 = curwin->w_cursor.lnum;
}
- ++line1;
+ line1++;
if (curwin->w_cursor.lnum < line1) {
- ++line1;
+ line1++;
}
if (curwin->w_cursor.lnum < line2) {
- ++line2;
+ line2++;
}
++curwin->w_cursor.lnum;
}
@@ -1200,7 +1202,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
break;
}
}
- ++p;
+ p++;
}
} while (trailarg != NULL);
@@ -1444,7 +1446,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
}
beginline(BL_WHITE | BL_FIX); // cursor on first non-blank
- --no_wait_return;
+ no_wait_return--;
if (linecount > p_report) {
if (do_in) {
@@ -1462,8 +1464,8 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
error:
// put cursor back in same position for ":w !cmd"
curwin->w_cursor = cursor_save;
- --no_wait_return;
- wait_return(FALSE);
+ no_wait_return--;
+ wait_return(false);
}
filterend:
@@ -2113,7 +2115,7 @@ void do_wqall(exarg_T *eap)
* 4. if overwriting is allowed (even after a dialog)
*/
if (not_writing()) {
- ++error;
+ error++;
break;
}
if (buf->b_ffname == NULL) {
@@ -2248,7 +2250,7 @@ int getfile(int fnum, char *ffname_arg, char *sfname_arg, int setpm, linenr_T ln
}
}
if (other) {
- --no_wait_return;
+ no_wait_return--;
}
if (setpm) {
setpcmark();
@@ -2961,7 +2963,7 @@ void ex_append(exarg_T *eap)
}
if (eap->cmdidx != CMD_append) {
- --lnum;
+ lnum--;
}
// when the buffer is empty need to delete the dummy line
@@ -3017,7 +3019,7 @@ void ex_append(exarg_T *eap)
vcol = 0;
for (p = theline; indent > vcol; ++p) {
if (*p == ' ') {
- ++vcol;
+ vcol++;
} else if (*p == TAB) {
vcol += 8 - vcol % 8;
} else {
@@ -3046,7 +3048,7 @@ void ex_append(exarg_T *eap)
}
xfree(theline);
- ++lnum;
+ lnum++;
if (empty) {
ml_delete(2L, false);
@@ -3138,10 +3140,10 @@ void ex_z(exarg_T *eap)
kind = x;
if (*kind == '-' || *kind == '+' || *kind == '='
|| *kind == '^' || *kind == '.') {
- ++x;
+ x++;
}
while (*x == '-' || *x == '+') {
- ++x;
+ x++;
}
if (*x != 0) {
@@ -3198,7 +3200,7 @@ void ex_z(exarg_T *eap)
if (*kind == '+') {
start += (linenr_T)bigness * (linenr_T)(x - kind - 1) + 1;
} else if (eap->addr_count == 0) {
- ++start;
+ start++;
}
end = start + (linenr_T)bigness - 1;
curs = end;
@@ -4299,7 +4301,7 @@ skip:
* has been appended to new_start, we don't need
* it in the buffer.
*/
- ++lnum;
+ lnum++;
if (u_savedel(lnum, nmatch_tl) != OK) {
break;
}
@@ -4778,1124 +4780,6 @@ bool prepare_tagpreview(bool undo_sync)
return false;
}
-/// ":help": open a read-only window on a help file
-void ex_help(exarg_T *eap)
-{
- char *arg;
- char *tag;
- FILE *helpfd; // file descriptor of help file
- int n;
- int i;
- win_T *wp;
- int num_matches;
- char **matches;
- char *p;
- int empty_fnum = 0;
- int alt_fnum = 0;
- buf_T *buf;
- int len;
- char *lang;
- const bool old_KeyTyped = KeyTyped;
-
- if (eap != NULL) {
- /*
- * A ":help" command ends at the first LF, or at a '|' that is
- * followed by some text. Set nextcmd to the following command.
- */
- for (arg = eap->arg; *arg; arg++) {
- if (*arg == '\n' || *arg == '\r'
- || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) {
- *arg++ = NUL;
- eap->nextcmd = arg;
- break;
- }
- }
- arg = eap->arg;
-
- if (eap->forceit && *arg == NUL && !curbuf->b_help) {
- emsg(_("E478: Don't panic!"));
- return;
- }
-
- if (eap->skip) { // not executing commands
- return;
- }
- } else {
- arg = "";
- }
-
- // remove trailing blanks
- p = arg + STRLEN(arg) - 1;
- while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') {
- *p-- = NUL;
- }
-
- // Check for a specified language
- lang = check_help_lang(arg);
-
- // When no argument given go to the index.
- if (*arg == NUL) {
- arg = "help.txt";
- }
-
- /*
- * Check if there is a match for the argument.
- */
- n = find_help_tags(arg, &num_matches, &matches, eap != NULL && eap->forceit);
-
- i = 0;
- if (n != FAIL && lang != NULL) {
- // Find first item with the requested language.
- for (i = 0; i < num_matches; ++i) {
- len = (int)STRLEN(matches[i]);
- if (len > 3 && matches[i][len - 3] == '@'
- && STRICMP(matches[i] + len - 2, lang) == 0) {
- break;
- }
- }
- }
- if (i >= num_matches || n == FAIL) {
- if (lang != NULL) {
- semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg);
- } else {
- semsg(_("E149: Sorry, no help for %s"), arg);
- }
- if (n != FAIL) {
- FreeWild(num_matches, matches);
- }
- return;
- }
-
- // The first match (in the requested language) is the best match.
- tag = xstrdup(matches[i]);
- FreeWild(num_matches, matches);
-
- /*
- * Re-use an existing help window or open a new one.
- * Always open a new one for ":tab help".
- */
- if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {
- if (cmdmod.cmod_tab != 0) {
- wp = NULL;
- } else {
- wp = NULL;
- FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
- if (bt_help(wp2->w_buffer)) {
- wp = wp2;
- break;
- }
- }
- }
- if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
- win_enter(wp, true);
- } else {
- // There is no help window yet.
- // Try to open the file specified by the "helpfile" option.
- if ((helpfd = os_fopen((char *)p_hf, READBIN)) == NULL) {
- smsg(_("Sorry, help file \"%s\" not found"), p_hf);
- goto erret;
- }
- fclose(helpfd);
-
- // Split off help window; put it at far top if no position
- // specified, the current window is vertically split and
- // narrow.
- n = WSP_HELP;
- if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
- && curwin->w_width < 80) {
- n |= WSP_TOP;
- }
- if (win_split(0, n) == FAIL) {
- goto erret;
- }
-
- if (curwin->w_height < p_hh) {
- win_setheight((int)p_hh);
- }
-
- /*
- * Open help file (do_ecmd() will set b_help flag, readfile() will
- * set b_p_ro flag).
- * Set the alternate file to the previously edited file.
- */
- alt_fnum = curbuf->b_fnum;
- (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
- ECMD_HIDE + ECMD_SET_HELP,
- NULL); // buffer is still open, don't store info
-
- if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
- curwin->w_alt_fnum = alt_fnum;
- }
- empty_fnum = curbuf->b_fnum;
- }
- }
-
- restart_edit = 0; // don't want insert mode in help file
-
- // Restore KeyTyped, setting 'filetype=help' may reset it.
- // It is needed for do_tag top open folds under the cursor.
- KeyTyped = old_KeyTyped;
-
- do_tag((char_u *)tag, DT_HELP, 1, false, true);
-
- // Delete the empty buffer if we're not using it. Careful: autocommands
- // may have jumped to another window, check that the buffer is not in a
- // window.
- if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
- buf = buflist_findnr(empty_fnum);
- if (buf != NULL && buf->b_nwindows == 0) {
- wipe_buffer(buf, true);
- }
- }
-
- // keep the previous alternate file
- if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum
- && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
- curwin->w_alt_fnum = alt_fnum;
- }
-
-erret:
- xfree(tag);
-}
-
-/// In an argument search for a language specifiers in the form "@xx".
-/// Changes the "@" to NUL if found, and returns a pointer to "xx".
-///
-/// @return NULL if not found.
-char *check_help_lang(char *arg)
-{
- int len = (int)STRLEN(arg);
-
- if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
- && ASCII_ISALPHA(arg[len - 1])) {
- arg[len - 3] = NUL; // remove the '@'
- return arg + len - 2;
- }
- return NULL;
-}
-
-/// Return a heuristic indicating how well the given string matches. The
-/// smaller the number, the better the match. This is the order of priorities,
-/// from best match to worst match:
-/// - Match with least alphanumeric characters is better.
-/// - Match with least total characters is better.
-/// - Match towards the start is better.
-/// - Match starting with "+" is worse (feature instead of command)
-/// Assumption is made that the matched_string passed has already been found to
-/// match some string for which help is requested. webb.
-///
-/// @param offset offset for match
-/// @param wrong_case no matching case
-///
-/// @return a heuristic indicating how well the given string matches.
-int help_heuristic(char *matched_string, int offset, int wrong_case)
- FUNC_ATTR_PURE
-{
- int num_letters;
- char *p;
-
- num_letters = 0;
- for (p = matched_string; *p; p++) {
- if (ASCII_ISALNUM(*p)) {
- num_letters++;
- }
- }
-
- /*
- * Multiply the number of letters by 100 to give it a much bigger
- * weighting than the number of characters.
- * If there only is a match while ignoring case, add 5000.
- * If the match starts in the middle of a word, add 10000 to put it
- * somewhere in the last half.
- * If the match is more than 2 chars from the start, multiply by 200 to
- * put it after matches at the start.
- */
- if (offset > 0
- && ASCII_ISALNUM(matched_string[offset])
- && ASCII_ISALNUM(matched_string[offset - 1])) {
- offset += 10000;
- } else if (offset > 2) {
- offset *= 200;
- }
- if (wrong_case) {
- offset += 5000;
- }
- // Features are less interesting than the subjects themselves, but "+"
- // alone is not a feature.
- if (matched_string[0] == '+' && matched_string[1] != NUL) {
- offset += 100;
- }
- return 100 * num_letters + (int)STRLEN(matched_string) + offset;
-}
-
-/// Compare functions for qsort() below, that checks the help heuristics number
-/// that has been put after the tagname by find_tags().
-static int help_compare(const void *s1, const void *s2)
-{
- char *p1;
- char *p2;
-
- p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
- p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
-
- // Compare by help heuristic number first.
- int cmp = strcmp(p1, p2);
- if (cmp != 0) {
- return cmp;
- }
-
- // Compare by strings as tie-breaker when same heuristic number.
- return strcmp(*(char **)s1, *(char **)s2);
-}
-
-/// Find all help tags matching "arg", sort them and return in matches[], with
-/// the number of matches in num_matches.
-/// The matches will be sorted with a "best" match algorithm.
-/// When "keep_lang" is true try keeping the language of the current buffer.
-int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep_lang)
-{
- int i;
-
- // Specific tags that either have a specific replacement or won't go
- // through the generic rules.
- static char *(except_tbl[][2]) = {
- { "*", "star" },
- { "g*", "gstar" },
- { "[*", "[star" },
- { "]*", "]star" },
- { ":*", ":star" },
- { "/*", "/star" }, // NOLINT
- { "/\\*", "/\\\\star" },
- { "\"*", "quotestar" },
- { "**", "starstar" },
- { "cpo-*", "cpo-star" },
- { "/\\(\\)", "/\\\\(\\\\)" },
- { "/\\%(\\)", "/\\\\%(\\\\)" },
- { "?", "?" },
- { "??", "??" },
- { ":?", ":?" },
- { "?<CR>", "?<CR>" },
- { "g?", "g?" },
- { "g?g?", "g?g?" },
- { "g??", "g??" },
- { "-?", "-?" },
- { "q?", "q?" },
- { "v_g?", "v_g?" },
- { "/\\?", "/\\\\?" },
- { "/\\z(\\)", "/\\\\z(\\\\)" },
- { "\\=", "\\\\=" },
- { ":s\\=", ":s\\\\=" },
- { "[count]", "\\[count]" },
- { "[quotex]", "\\[quotex]" },
- { "[range]", "\\[range]" },
- { ":[range]", ":\\[range]" },
- { "[pattern]", "\\[pattern]" },
- { "\\|", "\\\\bar" },
- { "\\%$", "/\\\\%\\$" },
- { "s/\\~", "s/\\\\\\~" },
- { "s/\\U", "s/\\\\U" },
- { "s/\\L", "s/\\\\L" },
- { "s/\\1", "s/\\\\1" },
- { "s/\\2", "s/\\\\2" },
- { "s/\\3", "s/\\\\3" },
- { "s/\\9", "s/\\\\9" },
- { NULL, NULL }
- };
-
- static const char *(expr_table[]) = {
- "!=?", "!~?", "<=?", "<?", "==?", "=~?",
- ">=?", ">?", "is?", "isnot?"
- };
- char *d = (char *)IObuff; // assume IObuff is long enough!
- d[0] = NUL;
-
- if (STRNICMP(arg, "expr-", 5) == 0) {
- // When the string starting with "expr-" and containing '?' and matches
- // the table, it is taken literally (but ~ is escaped). Otherwise '?'
- // is recognized as a wildcard.
- for (i = (int)ARRAY_SIZE(expr_table); --i >= 0;) {
- if (STRCMP(arg + 5, expr_table[i]) == 0) {
- for (int si = 0, di = 0;; si++) {
- if (arg[si] == '~') {
- d[di++] = '\\';
- }
- d[di++] = arg[si];
- if (arg[si] == NUL) {
- break;
- }
- }
- break;
- }
- }
- } else {
- // Recognize a few exceptions to the rule. Some strings that contain
- // '*'are changed to "star", otherwise '*' is recognized as a wildcard.
- for (i = 0; except_tbl[i][0] != NULL; i++) {
- if (STRCMP(arg, except_tbl[i][0]) == 0) {
- STRCPY(d, except_tbl[i][1]);
- break;
- }
- }
- }
-
- if (d[0] == NUL) { // no match in table
- // Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
- // Also replace "\%^" and "\%(", they match every tag too.
- // Also "\zs", "\z1", etc.
- // Also "\@<", "\@=", "\@<=", etc.
- // And also "\_$" and "\_^".
- if (arg[0] == '\\'
- && ((arg[1] != NUL && arg[2] == NUL)
- || (vim_strchr("%_z@", arg[1]) != NULL
- && arg[2] != NUL))) {
- vim_snprintf(d, IOSIZE, "/\\\\%s", arg + 1);
- // Check for "/\\_$", should be "/\\_\$"
- if (d[3] == '_' && d[4] == '$') {
- STRCPY(d + 4, "\\$");
- }
- } else {
- // Replace:
- // "[:...:]" with "\[:...:]"
- // "[++...]" with "\[++...]"
- // "\{" with "\\{" -- matching "} \}"
- if ((arg[0] == '[' && (arg[1] == ':'
- || (arg[1] == '+' && arg[2] == '+')))
- || (arg[0] == '\\' && arg[1] == '{')) {
- *d++ = '\\';
- }
-
- // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
- if (*arg == '(' && arg[1] == '\'') {
- arg++;
- }
- for (const char *s = arg; *s; s++) {
- // Replace "|" with "bar" and '"' with "quote" to match the name of
- // the tags for these commands.
- // Replace "*" with ".*" and "?" with "." to match command line
- // completion.
- // Insert a backslash before '~', '$' and '.' to avoid their
- // special meaning.
- if ((char_u *)d - IObuff > IOSIZE - 10) { // getting too long!?
- break;
- }
- switch (*s) {
- case '|':
- STRCPY(d, "bar");
- d += 3;
- continue;
- case '"':
- STRCPY(d, "quote");
- d += 5;
- continue;
- case '*':
- *d++ = '.';
- break;
- case '?':
- *d++ = '.';
- continue;
- case '$':
- case '.':
- case '~':
- *d++ = '\\';
- break;
- }
-
- /*
- * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
- * ":help i_^_CTRL-D" work.
- * Insert '-' before and after "CTRL-X" when applicable.
- */
- if (*s < ' '
- || (*s == '^' && s[1]
- && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
- if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
- *d++ = '_'; // prepend a '_' to make x_CTRL-x
- }
- STRCPY(d, "CTRL-");
- d += 5;
- if (*s < ' ') {
- *d++ = (char)(*s + '@');
- if (d[-1] == '\\') {
- *d++ = '\\'; // double a backslash
- }
- } else {
- *d++ = *++s;
- }
- if (s[1] != NUL && s[1] != '_') {
- *d++ = '_'; // append a '_'
- }
- continue;
- } else if (*s == '^') { // "^" or "CTRL-^" or "^_"
- *d++ = '\\';
- }
- /*
- * Insert a backslash before a backslash after a slash, for search
- * pattern tags: "/\|" --> "/\\|".
- */
- else if (s[0] == '\\' && s[1] != '\\'
- && *arg == '/' && s == arg + 1) {
- *d++ = '\\';
- }
-
- // "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
- // "CTRL-\_CTRL-N"
- if (STRNICMP(s, "CTRL-\\_", 7) == 0) {
- STRCPY(d, "CTRL-\\\\");
- d += 7;
- s += 6;
- }
-
- *d++ = *s;
-
- // If tag contains "({" or "([", tag terminates at the "(".
- // This is for help on functions, e.g.: abs({expr}).
- if (*s == '(' && (s[1] == '{' || s[1] == '[')) {
- break;
- }
-
- // If tag starts with ', toss everything after a second '. Fixes
- // CTRL-] on 'option'. (would include the trailing '.').
- if (*s == '\'' && s > arg && *arg == '\'') {
- break;
- }
- // Also '{' and '}'. Fixes CTRL-] on '{address}'.
- if (*s == '}' && s > arg && *arg == '{') {
- break;
- }
- }
- *d = NUL;
-
- if (*IObuff == '`') {
- if ((char_u *)d > IObuff + 2 && d[-1] == '`') {
- // remove the backticks from `command`
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
- d[-2] = NUL;
- } else if ((char_u *)d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
- // remove the backticks and comma from `command`,
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
- d[-3] = NUL;
- } else if ((char_u *)d > IObuff + 4 && d[-3] == '`'
- && d[-2] == '\\' && d[-1] == '.') {
- // remove the backticks and dot from `command`\.
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
- d[-4] = NUL;
- }
- }
- }
- }
-
- *matches = NULL;
- *num_matches = 0;
- int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC;
- if (keep_lang) {
- flags |= TAG_KEEP_LANG;
- }
- if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
- && *num_matches > 0) {
- // Sort the matches found on the heuristic number that is after the
- // tag name.
- qsort((void *)(*matches), (size_t)(*num_matches),
- sizeof(char_u *), help_compare);
- // Delete more than TAG_MANY to reduce the size of the listing.
- while (*num_matches > TAG_MANY) {
- xfree((*matches)[--*num_matches]);
- }
- }
- return OK;
-}
-
-/// Called when starting to edit a buffer for a help file.
-static void prepare_help_buffer(void)
-{
- curbuf->b_help = true;
- set_string_option_direct("buftype", -1, "help", OPT_FREE|OPT_LOCAL, 0);
-
- // Always set these options after jumping to a help tag, because the
- // user may have an autocommand that gets in the way.
- // Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
- // latin1 word characters (for translated help files).
- // Only set it when needed, buf_init_chartab() is some work.
- char *p = "!-~,^*,^|,^\",192-255";
- if (STRCMP(curbuf->b_p_isk, p) != 0) {
- set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
- check_buf_options(curbuf);
- (void)buf_init_chartab(curbuf, FALSE);
- }
-
- // Don't use the global foldmethod.
- set_string_option_direct("fdm", -1, "manual", OPT_FREE|OPT_LOCAL, 0);
-
- curbuf->b_p_ts = 8; // 'tabstop' is 8.
- curwin->w_p_list = FALSE; // No list mode.
-
- curbuf->b_p_ma = FALSE; // Not modifiable.
- curbuf->b_p_bin = FALSE; // Reset 'bin' before reading file.
- curwin->w_p_nu = 0; // No line numbers.
- curwin->w_p_rnu = 0; // No relative line numbers.
- RESET_BINDING(curwin); // No scroll or cursor binding.
- curwin->w_p_arab = FALSE; // No arabic mode.
- curwin->w_p_rl = FALSE; // Help window is left-to-right.
- curwin->w_p_fen = FALSE; // No folding in the help window.
- curwin->w_p_diff = FALSE; // No 'diff'.
- curwin->w_p_spell = FALSE; // No spell checking.
-
- set_buflisted(FALSE);
-}
-
-/// After reading a help file: May cleanup a help buffer when syntax
-/// highlighting is not used.
-void fix_help_buffer(void)
-{
- linenr_T lnum;
- char *line;
- bool in_example = false;
-
- // Set filetype to "help".
- if (STRCMP(curbuf->b_p_ft, "help") != 0) {
- curbuf->b_ro_locked++;
- set_option_value("ft", 0L, "help", OPT_LOCAL);
- curbuf->b_ro_locked--;
- }
-
- if (!syntax_present(curwin)) {
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
- line = (char *)ml_get_buf(curbuf, lnum, false);
- const size_t len = STRLEN(line);
- if (in_example && len > 0 && !ascii_iswhite(line[0])) {
- // End of example: non-white or '<' in first column.
- if (line[0] == '<') {
- // blank-out a '<' in the first column
- line = (char *)ml_get_buf(curbuf, lnum, true);
- line[0] = ' ';
- }
- in_example = false;
- }
- if (!in_example && len > 0) {
- if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) {
- // blank-out a '>' in the last column (start of example)
- line = (char *)ml_get_buf(curbuf, lnum, true);
- line[len - 1] = ' ';
- in_example = true;
- } else if (line[len - 1] == '~') {
- // blank-out a '~' at the end of line (header marker)
- line = (char *)ml_get_buf(curbuf, lnum, true);
- line[len - 1] = ' ';
- }
- }
- }
- }
-
- /*
- * In the "help.txt" and "help.abx" file, add the locally added help
- * files. This uses the very first line in the help file.
- */
- char *const fname = path_tail(curbuf->b_fname);
- if (FNAMECMP(fname, "help.txt") == 0
- || (FNAMENCMP(fname, "help.", 5) == 0
- && ASCII_ISALPHA(fname[5])
- && ASCII_ISALPHA(fname[6])
- && TOLOWER_ASC(fname[7]) == 'x'
- && fname[8] == NUL)) {
- for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) {
- line = (char *)ml_get_buf(curbuf, lnum, false);
- if (strstr(line, "*local-additions*") == NULL) {
- continue;
- }
-
- // Go through all directories in 'runtimepath', skipping
- // $VIMRUNTIME.
- char *p = (char *)p_rtp;
- while (*p != NUL) {
- copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
- char *const rt = vim_getenv("VIMRUNTIME");
- if (rt != NULL
- && path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
- int fcount;
- char **fnames;
- char *s;
- vimconv_T vc;
- char *cp;
-
- // Find all "doc/ *.txt" files in this directory.
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, "doc/*.??[tx]",
- sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- continue;
- }
-
- // Note: We cannot just do `&NameBuff` because it is a statically sized array
- // so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
- if (gen_expand_wildcards(1, buff_list, &fcount,
- &fnames, EW_FILE|EW_SILENT) == OK
- && fcount > 0) {
- // If foo.abx is found use it instead of foo.txt in
- // the same directory.
- for (int i1 = 0; i1 < fcount; i1++) {
- for (int i2 = 0; i2 < fcount; i2++) {
- if (i1 == i2) {
- continue;
- }
- if (fnames[i1] == NULL || fnames[i2] == NULL) {
- continue;
- }
- const char *const f1 = fnames[i1];
- const char *const f2 = fnames[i2];
- const char *const t1 = path_tail(f1);
- const char *const t2 = path_tail(f2);
- const char *const e1 = (char *)STRRCHR(t1, '.');
- const char *const e2 = (char *)STRRCHR(t2, '.');
- if (e1 == NULL || e2 == NULL) {
- continue;
- }
- if (FNAMECMP(e1, ".txt") != 0
- && FNAMECMP(e1, fname + 4) != 0) {
- // Not .txt and not .abx, remove it.
- XFREE_CLEAR(fnames[i1]);
- continue;
- }
- if (e1 - f1 != e2 - f2
- || FNAMENCMP(f1, f2, e1 - f1) != 0) {
- continue;
- }
- if (FNAMECMP(e1, ".txt") == 0
- && FNAMECMP(e2, fname + 4) == 0) {
- // use .abx instead of .txt
- XFREE_CLEAR(fnames[i1]);
- }
- }
- }
- for (int fi = 0; fi < fcount; fi++) {
- if (fnames[fi] == NULL) {
- continue;
- }
-
- FILE *const fd = os_fopen(fnames[fi], "r");
- if (fd == NULL) {
- continue;
- }
- vim_fgets(IObuff, IOSIZE, fd);
- if (IObuff[0] == '*'
- && (s = vim_strchr((char *)IObuff + 1, '*'))
- != NULL) {
- TriState this_utf = kNone;
- // Change tag definition to a
- // reference and remove <CR>/<NL>.
- IObuff[0] = '|';
- *s = '|';
- while (*s != NUL) {
- if (*s == '\r' || *s == '\n') {
- *s = NUL;
- }
- // The text is utf-8 when a byte
- // above 127 is found and no
- // illegal byte sequence is found.
- if ((char_u)(*s) >= 0x80 && this_utf != kFalse) {
- this_utf = kTrue;
- const int l = utf_ptr2len(s);
- if (l == 1) {
- this_utf = kFalse;
- }
- s += l - 1;
- }
- ++s;
- }
- // The help file is latin1 or utf-8;
- // conversion to the current
- // 'encoding' may be required.
- vc.vc_type = CONV_NONE;
- convert_setup(&vc,
- (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"),
- p_enc);
- if (vc.vc_type == CONV_NONE) {
- // No conversion needed.
- cp = (char *)IObuff;
- } else {
- // Do the conversion. If it fails
- // use the unconverted text.
- cp = (char *)string_convert(&vc, IObuff, NULL);
- if (cp == NULL) {
- cp = (char *)IObuff;
- }
- }
- convert_setup(&vc, NULL, NULL);
-
- ml_append(lnum, cp, (colnr_T)0, false);
- if ((char_u *)cp != IObuff) {
- xfree(cp);
- }
- lnum++;
- }
- fclose(fd);
- }
- FreeWild(fcount, fnames);
- }
- }
- xfree(rt);
- }
- break;
- }
- }
-}
-
-/// ":exusage"
-void ex_exusage(exarg_T *eap)
-{
- do_cmdline_cmd("help ex-cmd-index");
-}
-
-/// ":viusage"
-void ex_viusage(exarg_T *eap)
-{
- do_cmdline_cmd("help normal-index");
-}
-
-/// Generate tags in one help directory
-///
-/// @param dir Path to the doc directory
-/// @param ext Suffix of the help files (".txt", ".itx", ".frx", etc.)
-/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for
-/// French)
-/// @param add_help_tags Whether to add the "help-tags" tag
-/// @param ignore_writeerr ignore write error
-static void helptags_one(char *dir, const char *ext, const char *tagfname, bool add_help_tags,
- bool ignore_writeerr)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T ga;
- int filecount;
- char **files;
- char *p1, *p2;
- char *s;
- TriState utf8 = kNone;
- bool mix = false; // detected mixed encodings
-
- // Find all *.txt files.
- size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
- if (dirlen >= MAXPATHL
- || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
- || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- return;
- }
-
- // Note: We cannot just do `&NameBuff` because it is a statically sized array
- // so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
- const int res = gen_expand_wildcards(1, buff_list, &filecount, &files,
- EW_FILE|EW_SILENT);
- if (res == FAIL || filecount == 0) {
- if (!got_int) {
- semsg(_("E151: No match: %s"), NameBuff);
- }
- if (res != FAIL) {
- FreeWild(filecount, files);
- }
- return;
- }
-
- //
- // Open the tags file for writing.
- // Do this before scanning through all the files.
- //
- memcpy(NameBuff, dir, dirlen + 1);
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- return;
- }
-
- FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
- if (fd_tags == NULL) {
- if (!ignore_writeerr) {
- semsg(_("E152: Cannot open %s for writing"), NameBuff);
- }
- FreeWild(filecount, files);
- return;
- }
-
- // If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
- // add the "help-tags" tag.
- ga_init(&ga, (int)sizeof(char_u *), 100);
- if (add_help_tags
- || path_full_compare("$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) {
- size_t s_len = 18 + STRLEN(tagfname);
- s = xmalloc(s_len);
- snprintf(s, s_len, "help-tags\t%s\t1\n", tagfname);
- GA_APPEND(char *, &ga, s);
- }
-
- // Go over all the files and extract the tags.
- for (int fi = 0; fi < filecount && !got_int; fi++) {
- FILE *const fd = os_fopen(files[fi], "r");
- if (fd == NULL) {
- semsg(_("E153: Unable to open %s for reading"), files[fi]);
- continue;
- }
- const char *const fname = files[fi] + dirlen + 1;
-
- bool firstline = true;
- while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
- if (firstline) {
- // Detect utf-8 file by a non-ASCII char in the first line.
- TriState this_utf8 = kNone;
- for (s = (char *)IObuff; *s != NUL; s++) {
- if ((char_u)(*s) >= 0x80) {
- this_utf8 = kTrue;
- const int l = utf_ptr2len(s);
- if (l == 1) {
- // Illegal UTF-8 byte sequence.
- this_utf8 = kFalse;
- break;
- }
- s += l - 1;
- }
- }
- if (this_utf8 == kNone) { // only ASCII characters found
- this_utf8 = kFalse;
- }
- if (utf8 == kNone) { // first file
- utf8 = this_utf8;
- } else if (utf8 != this_utf8) {
- semsg(_("E670: Mix of help file encodings within a language: %s"),
- files[fi]);
- mix = !got_int;
- got_int = TRUE;
- }
- firstline = false;
- }
- p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
- while (p1 != NULL) {
- p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
- if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
- for (s = p1 + 1; s < p2; s++) {
- if (*s == ' ' || *s == '\t' || *s == '|') {
- break;
- }
- }
-
- // Only accept a *tag* when it consists of valid
- // characters, there is white space before it and is
- // followed by a white character or end-of-line.
- if (s == p2
- && ((char_u *)p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
- && (vim_strchr(" \t\n\r", s[1]) != NULL
- || s[1] == '\0')) {
- *p2 = '\0';
- p1++;
- size_t s_len= (size_t)(p2 - p1) + STRLEN(fname) + 2;
- s = xmalloc(s_len);
- GA_APPEND(char *, &ga, s);
- snprintf(s, s_len, "%s\t%s", p1, fname);
-
- // find next '*'
- p2 = vim_strchr(p2 + 1, '*');
- }
- }
- p1 = p2;
- }
- line_breakcheck();
- }
-
- fclose(fd);
- }
-
- FreeWild(filecount, files);
-
- if (!got_int && ga.ga_data != NULL) {
- // Sort the tags.
- sort_strings(ga.ga_data, ga.ga_len);
-
- // Check for duplicates.
- for (int i = 1; i < ga.ga_len; i++) {
- p1 = ((char **)ga.ga_data)[i - 1];
- p2 = ((char **)ga.ga_data)[i];
- while (*p1 == *p2) {
- if (*p2 == '\t') {
- *p2 = NUL;
- vim_snprintf((char *)NameBuff, MAXPATHL,
- _("E154: Duplicate tag \"%s\" in file %s/%s"),
- ((char_u **)ga.ga_data)[i], dir, p2 + 1);
- emsg((char *)NameBuff);
- *p2 = '\t';
- break;
- }
- ++p1;
- ++p2;
- }
- }
-
- if (utf8 == kTrue) {
- fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
- }
-
- // Write the tags into the file.
- for (int i = 0; i < ga.ga_len; i++) {
- s = ((char **)ga.ga_data)[i];
- if (STRNCMP(s, "help-tags\t", 10) == 0) {
- // help-tags entry was added in formatted form
- fputs(s, fd_tags);
- } else {
- fprintf(fd_tags, "%s\t/" "*", s);
- for (p1 = s; *p1 != '\t'; p1++) {
- // insert backslash before '\\' and '/'
- if (*p1 == '\\' || *p1 == '/') {
- putc('\\', fd_tags);
- }
- putc(*p1, fd_tags);
- }
- fprintf(fd_tags, "*\n");
- }
- }
- }
- if (mix) {
- got_int = false; // continue with other languages
- }
-
- GA_DEEP_CLEAR_PTR(&ga);
- fclose(fd_tags); // there is no check for an error...
-}
-
-/// Generate tags in one help directory, taking care of translations.
-static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
- FUNC_ATTR_NONNULL_ALL
-{
- int len;
- garray_T ga;
- char lang[2];
- char ext[5];
- char fname[8];
- int filecount;
- char **files;
-
- // Get a list of all files in the help directory and in subdirectories.
- STRLCPY(NameBuff, dirname, sizeof(NameBuff));
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- return;
- }
-
- // Note: We cannot just do `&NameBuff` because it is a statically sized array
- // so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
- if (gen_expand_wildcards(1, buff_list, &filecount, &files,
- EW_FILE|EW_SILENT) == FAIL
- || filecount == 0) {
- semsg(_("E151: No match: %s"), NameBuff);
- return;
- }
-
- // Go over all files in the directory to find out what languages are
- // present.
- int j;
- ga_init(&ga, 1, 10);
- for (int i = 0; i < filecount; i++) {
- len = (int)STRLEN(files[i]);
- if (len <= 4) {
- continue;
- }
-
- if (STRICMP(files[i] + len - 4, ".txt") == 0) {
- // ".txt" -> language "en"
- lang[0] = 'e';
- lang[1] = 'n';
- } else if (files[i][len - 4] == '.'
- && ASCII_ISALPHA(files[i][len - 3])
- && ASCII_ISALPHA(files[i][len - 2])
- && TOLOWER_ASC(files[i][len - 1]) == 'x') {
- // ".abx" -> language "ab"
- lang[0] = (char)TOLOWER_ASC(files[i][len - 3]);
- lang[1] = (char)TOLOWER_ASC(files[i][len - 2]);
- } else {
- continue;
- }
-
- // Did we find this language already?
- for (j = 0; j < ga.ga_len; j += 2) {
- if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) {
- break;
- }
- }
- if (j == ga.ga_len) {
- // New language, add it.
- ga_grow(&ga, 2);
- ((char *)ga.ga_data)[ga.ga_len++] = lang[0];
- ((char *)ga.ga_data)[ga.ga_len++] = lang[1];
- }
- }
-
- /*
- * Loop over the found languages to generate a tags file for each one.
- */
- for (j = 0; j < ga.ga_len; j += 2) {
- STRCPY(fname, "tags-xx");
- fname[5] = ((char *)ga.ga_data)[j];
- fname[6] = ((char *)ga.ga_data)[j + 1];
- if (fname[5] == 'e' && fname[6] == 'n') {
- // English is an exception: use ".txt" and "tags".
- fname[4] = NUL;
- STRCPY(ext, ".txt");
- } else {
- // Language "ab" uses ".abx" and "tags-ab".
- STRCPY(ext, ".xxx");
- ext[1] = fname[5];
- ext[2] = fname[6];
- }
- helptags_one(dirname, (char *)ext, (char *)fname, add_help_tags, ignore_writeerr);
- }
-
- ga_clear(&ga);
- FreeWild(filecount, files);
-}
-
-static void helptags_cb(char *fname, void *cookie)
- FUNC_ATTR_NONNULL_ALL
-{
- do_helptags(fname, *(bool *)cookie, true);
-}
-
-/// ":helptags"
-void ex_helptags(exarg_T *eap)
-{
- expand_T xpc;
- char *dirname;
- bool add_help_tags = false;
-
- // Check for ":helptags ++t {dir}".
- if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
- add_help_tags = true;
- eap->arg = skipwhite(eap->arg + 3);
- }
-
- if (STRCMP(eap->arg, "ALL") == 0) {
- do_in_path(p_rtp, "doc", DIP_ALL + DIP_DIR, helptags_cb, &add_help_tags);
- } else {
- ExpandInit(&xpc);
- xpc.xp_context = EXPAND_DIRECTORIES;
- dirname = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL,
- WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
- if (dirname == NULL || !os_isdir((char_u *)dirname)) {
- semsg(_("E150: Not a directory: %s"), eap->arg);
- } else {
- do_helptags(dirname, add_help_tags, false);
- }
- xfree(dirname);
- }
-}
-
-/// ":helpclose": Close one help window
-void ex_helpclose(exarg_T *eap)
-{
- FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
- if (bt_help(win->w_buffer)) {
- win_close(win, false, eap->forceit);
- return;
- }
- }
-}
-
/// Shows the effects of the :substitute command being typed ('inccommand').
/// If inccommand=split, shows a preview window and later restores the layout.
///
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index a55e74a789..3aaba9ce42 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -20,9 +20,9 @@
#define ECMD_NOWINENTER 0x40 // do not trigger BufWinEnter
// for lnum argument in do_ecmd()
-#define ECMD_LASTL (linenr_T)0 // use last position in loaded file
-#define ECMD_LAST ((linenr_T) - 1) // use last position in all files
-#define ECMD_ONE (linenr_T)1 // use first line
+#define ECMD_LASTL (linenr_T)0 // use last position in loaded file
+#define ECMD_LAST ((linenr_T)(-1)) // use last position in all files
+#define ECMD_ONE (linenr_T)1 // use first line
/// Previous :substitute replacement string definition
typedef struct {
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index a5ba5e0b30..4bed1e94b9 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -107,6 +107,12 @@ module.cmds = {
func='ex_listdo',
},
{
+ command='argdedupe',
+ flags=TRLBAR,
+ addr_type='ADDR_NONE',
+ func='ex_argdedupe',
+ },
+ {
command='argedit',
flags=bit.bor(BANG, NEEDARG, RANGE, ZEROR, FILES, CMDARG, ARGOPT, TRLBAR),
addr_type='ADDR_ARGUMENTS',
@@ -3175,7 +3181,7 @@ module.cmds = {
},
{
command='wincmd',
- flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN, LOCK_OK),
+ flags=bit.bor(NEEDARG, WORD1, RANGE, COUNT, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_wincmd',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 4d9a1b7e3c..54315a6417 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -11,6 +11,7 @@
#include <stdbool.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/globals.h"
@@ -42,11 +43,9 @@
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/quickfix.h"
-#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/undo.h"
-#include "nvim/version.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -445,483 +444,6 @@ int buf_write_all(buf_T *buf, int forceit)
return retval;
}
-/// Code to handle the argument list.
-
-#define AL_SET 1
-#define AL_ADD 2
-#define AL_DEL 3
-
-/// Isolate one argument, taking backticks.
-/// Changes the argument in-place, puts a NUL after it. Backticks remain.
-///
-/// @return a pointer to the start of the next argument.
-static char *do_one_arg(char *str)
-{
- char *p;
- bool inbacktick;
-
- inbacktick = false;
- for (p = str; *str; str++) {
- // When the backslash is used for escaping the special meaning of a
- // character we need to keep it until wildcard expansion.
- if (rem_backslash((char_u *)str)) {
- *p++ = *str++;
- *p++ = *str;
- } else {
- // An item ends at a space not in backticks
- if (!inbacktick && ascii_isspace(*str)) {
- break;
- }
- if (*str == '`') {
- inbacktick ^= true;
- }
- *p++ = *str;
- }
- }
- str = skipwhite(str);
- *p = NUL;
-
- return str;
-}
-
-/// Separate the arguments in "str" and return a list of pointers in the
-/// growarray "gap".
-static void get_arglist(garray_T *gap, char *str, int escaped)
-{
- ga_init(gap, (int)sizeof(char_u *), 20);
- while (*str != NUL) {
- GA_APPEND(char *, gap, str);
-
- // If str is escaped, don't handle backslashes or spaces
- if (!escaped) {
- return;
- }
-
- // Isolate one argument, change it in-place, put a NUL after it.
- str = do_one_arg(str);
- }
-}
-
-/// Parse a list of arguments (file names), expand them and return in
-/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
-///
-/// @return FAIL or OK.
-int get_arglist_exp(char_u *str, int *fcountp, char ***fnamesp, bool wig)
-{
- garray_T ga;
- int i;
-
- get_arglist(&ga, (char *)str, true);
-
- if (wig) {
- i = expand_wildcards(ga.ga_len, ga.ga_data,
- fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
- } else {
- i = gen_expand_wildcards(ga.ga_len, ga.ga_data,
- fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
- }
-
- ga_clear(&ga);
- return i;
-}
-
-/// @param str
-/// @param what
-/// AL_SET: Redefine the argument list to 'str'.
-/// AL_ADD: add files in 'str' to the argument list after "after".
-/// AL_DEL: remove files in 'str' from the argument list.
-/// @param after
-/// 0 means before first one
-/// @param will_edit will edit added argument
-///
-/// @return FAIL for failure, OK otherwise.
-static int do_arglist(char *str, int what, int after, bool will_edit)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T new_ga;
- int exp_count;
- char **exp_files;
- char *p;
- int match;
- int arg_escaped = true;
-
- // Set default argument for ":argadd" command.
- if (what == AL_ADD && *str == NUL) {
- if (curbuf->b_ffname == NULL) {
- return FAIL;
- }
- str = curbuf->b_fname;
- arg_escaped = false;
- }
-
- // Collect all file name arguments in "new_ga".
- get_arglist(&new_ga, str, arg_escaped);
-
- if (what == AL_DEL) {
- regmatch_T regmatch;
- bool didone;
-
- // Delete the items: use each item as a regexp and find a match in the
- // argument list.
- regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
- for (int i = 0; i < new_ga.ga_len && !got_int; i++) {
- p = ((char **)new_ga.ga_data)[i];
- p = file_pat_to_reg_pat(p, NULL, NULL, false);
- if (p == NULL) {
- break;
- }
- regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
- if (regmatch.regprog == NULL) {
- xfree(p);
- break;
- }
-
- didone = false;
- for (match = 0; match < ARGCOUNT; match++) {
- if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
- didone = true;
- xfree(ARGLIST[match].ae_fname);
- memmove(ARGLIST + match, ARGLIST + match + 1,
- (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
- ALIST(curwin)->al_ga.ga_len--;
- if (curwin->w_arg_idx > match) {
- curwin->w_arg_idx--;
- }
- match--;
- }
- }
-
- vim_regfree(regmatch.regprog);
- xfree(p);
- if (!didone) {
- semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
- }
- }
- ga_clear(&new_ga);
- } else {
- int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data,
- &exp_count, &exp_files,
- EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
- ga_clear(&new_ga);
- if (i == FAIL || exp_count == 0) {
- emsg(_(e_nomatch));
- return FAIL;
- }
-
- if (what == AL_ADD) {
- alist_add_list(exp_count, exp_files, after, will_edit);
- xfree(exp_files);
- } else {
- assert(what == AL_SET);
- alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
- }
- }
-
- alist_check_arg_idx();
-
- return OK;
-}
-
-/// Check the validity of the arg_idx for each other window.
-static void alist_check_arg_idx(void)
-{
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_alist == curwin->w_alist) {
- check_arg_idx(win);
- }
- }
-}
-
-/// @return true if window "win" is editing the file at the current argument
-/// index.
-static bool editing_arg_idx(win_T *win)
-{
- return !(win->w_arg_idx >= WARGCOUNT(win)
- || (win->w_buffer->b_fnum
- != WARGLIST(win)[win->w_arg_idx].ae_fnum
- && (win->w_buffer->b_ffname == NULL
- || !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]),
- win->w_buffer->b_ffname, true,
- true) & kEqualFiles))));
-}
-
-/// Check if window "win" is editing the w_arg_idx file in its argument list.
-void check_arg_idx(win_T *win)
-{
- if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) {
- // We are not editing the current entry in the argument list.
- // Set "arg_had_last" if we are editing the last one.
- win->w_arg_idx_invalid = true;
- if (win->w_arg_idx != WARGCOUNT(win) - 1
- && arg_had_last == false
- && ALIST(win) == &global_alist
- && GARGCOUNT > 0
- && win->w_arg_idx < GARGCOUNT
- && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
- || (win->w_buffer->b_ffname != NULL
- && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]),
- win->w_buffer->b_ffname, true, true)
- & kEqualFiles)))) {
- arg_had_last = true;
- }
- } else {
- // We are editing the current entry in the argument list.
- // Set "arg_had_last" if it's also the last one
- win->w_arg_idx_invalid = false;
- if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {
- arg_had_last = true;
- }
- }
-}
-
-/// ":args", ":argslocal" and ":argsglobal".
-void ex_args(exarg_T *eap)
-{
- if (eap->cmdidx != CMD_args) {
- alist_unlink(ALIST(curwin));
- if (eap->cmdidx == CMD_argglobal) {
- ALIST(curwin) = &global_alist;
- } else { // eap->cmdidx == CMD_arglocal
- alist_new();
- }
- }
-
- if (*eap->arg != NUL) {
- // ":args file ..": define new argument list, handle like ":next"
- // Also for ":argslocal file .." and ":argsglobal file ..".
- ex_next(eap);
- } else if (eap->cmdidx == CMD_args) {
- // ":args": list arguments.
- if (ARGCOUNT > 0) {
- char **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT);
- // Overwrite the command, for a short list there is no scrolling
- // required and no wait_return().
- gotocmdline(true);
- for (int i = 0; i < ARGCOUNT; i++) {
- items[i] = alist_name(&ARGLIST[i]);
- }
- list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
- xfree(items);
- }
- } else if (eap->cmdidx == CMD_arglocal) {
- garray_T *gap = &curwin->w_alist->al_ga;
-
- // ":argslocal": make a local copy of the global argument list.
- ga_grow(gap, GARGCOUNT);
- for (int i = 0; i < GARGCOUNT; i++) {
- if (GARGLIST[i].ae_fname != NULL) {
- AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
- vim_strsave(GARGLIST[i].ae_fname);
- AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
- GARGLIST[i].ae_fnum;
- gap->ga_len++;
- }
- }
- }
-}
-
-/// ":previous", ":sprevious", ":Next" and ":sNext".
-void ex_previous(exarg_T *eap)
-{
- // If past the last one already, go to the last one.
- if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) {
- do_argfile(eap, ARGCOUNT - 1);
- } else {
- do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
- }
-}
-
-/// ":rewind", ":first", ":sfirst" and ":srewind".
-void ex_rewind(exarg_T *eap)
-{
- do_argfile(eap, 0);
-}
-
-/// ":last" and ":slast".
-void ex_last(exarg_T *eap)
-{
- do_argfile(eap, ARGCOUNT - 1);
-}
-
-/// ":argument" and ":sargument".
-void ex_argument(exarg_T *eap)
-{
- int i;
-
- if (eap->addr_count > 0) {
- i = (int)eap->line2 - 1;
- } else {
- i = curwin->w_arg_idx;
- }
- do_argfile(eap, i);
-}
-
-/// Edit file "argn" of the argument lists.
-void do_argfile(exarg_T *eap, int argn)
-{
- int other;
- char *p;
- int old_arg_idx = curwin->w_arg_idx;
-
- if (argn < 0 || argn >= ARGCOUNT) {
- if (ARGCOUNT <= 1) {
- emsg(_("E163: There is only one file to edit"));
- } else if (argn < 0) {
- emsg(_("E164: Cannot go before first file"));
- } else {
- emsg(_("E165: Cannot go beyond last file"));
- }
- } else {
- setpcmark();
-
- // split window or create new tab page first
- if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
- if (win_split(0, 0) == FAIL) {
- return;
- }
- RESET_BINDING(curwin);
- } else {
- // if 'hidden' set, only check for changed file when re-editing
- // the same buffer
- other = true;
- if (buf_hide(curbuf)) {
- p = fix_fname(alist_name(&ARGLIST[argn]));
- other = otherfile(p);
- xfree(p);
- }
- if ((!buf_hide(curbuf) || !other)
- && check_changed(curbuf, CCGD_AW
- | (other ? 0 : CCGD_MULTWIN)
- | (eap->forceit ? CCGD_FORCEIT : 0)
- | CCGD_EXCMD)) {
- return;
- }
- }
-
- curwin->w_arg_idx = argn;
- if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
- arg_had_last = true;
- }
-
- // Edit the file; always use the last known line number.
- // When it fails (e.g. Abort for already edited file) restore the
- // argument index.
- if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
- eap, ECMD_LAST,
- (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
- + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
- curwin->w_arg_idx = old_arg_idx;
- } else if (eap->cmdidx != CMD_argdo) {
- // like Vi: set the mark where the cursor is in the file.
- setmark('\'');
- }
- }
-}
-
-/// ":next", and commands that behave like it.
-void ex_next(exarg_T *eap)
-{
- int i;
-
- // check for changed buffer now, if this fails the argument list is not
- // redefined.
- if (buf_hide(curbuf)
- || eap->cmdidx == CMD_snext
- || !check_changed(curbuf, CCGD_AW
- | (eap->forceit ? CCGD_FORCEIT : 0)
- | CCGD_EXCMD)) {
- if (*eap->arg != NUL) { // redefine file list
- if (do_arglist(eap->arg, AL_SET, 0, true) == FAIL) {
- return;
- }
- i = 0;
- } else {
- i = curwin->w_arg_idx + (int)eap->line2;
- }
- do_argfile(eap, i);
- }
-}
-
-/// ":argedit"
-void ex_argedit(exarg_T *eap)
-{
- int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
- // Whether curbuf will be reused, curbuf->b_ffname will be set.
- bool curbuf_is_reusable = curbuf_reusable();
-
- if (do_arglist(eap->arg, AL_ADD, i, true) == FAIL) {
- return;
- }
- maketitle();
-
- if (curwin->w_arg_idx == 0
- && (curbuf->b_ml.ml_flags & ML_EMPTY)
- && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
- i = 0;
- }
- // Edit the argument.
- if (i < ARGCOUNT) {
- do_argfile(eap, i);
- }
-}
-
-/// ":argadd"
-void ex_argadd(exarg_T *eap)
-{
- do_arglist(eap->arg, AL_ADD,
- eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
- false);
- maketitle();
-}
-
-/// ":argdelete"
-void ex_argdelete(exarg_T *eap)
-{
- if (eap->addr_count > 0 || *eap->arg == NUL) {
- // ":argdel" works like ":.argdel"
- if (eap->addr_count == 0) {
- if (curwin->w_arg_idx >= ARGCOUNT) {
- emsg(_("E610: No argument to delete"));
- return;
- }
- eap->line1 = eap->line2 = curwin->w_arg_idx + 1;
- } else if (eap->line2 > ARGCOUNT) {
- // ":1,4argdel": Delete all arguments in the range.
- eap->line2 = ARGCOUNT;
- }
- linenr_T n = eap->line2 - eap->line1 + 1;
- if (*eap->arg != NUL) {
- // Can't have both a range and an argument.
- emsg(_(e_invarg));
- } else if (n <= 0) {
- // Don't give an error for ":%argdel" if the list is empty.
- if (eap->line1 != 1 || eap->line2 != 0) {
- emsg(_(e_invrange));
- }
- } else {
- for (linenr_T i = eap->line1; i <= eap->line2; i++) {
- xfree(ARGLIST[i - 1].ae_fname);
- }
- memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
- (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
- ALIST(curwin)->al_ga.ga_len -= (int)n;
- if (curwin->w_arg_idx >= eap->line2) {
- curwin->w_arg_idx -= (int)n;
- } else if (curwin->w_arg_idx > eap->line1) {
- curwin->w_arg_idx = (int)eap->line1;
- }
- if (ARGCOUNT == 0) {
- curwin->w_arg_idx = 0;
- } else if (curwin->w_arg_idx >= ARGCOUNT) {
- curwin->w_arg_idx = ARGCOUNT - 1;
- }
- }
- } else {
- do_arglist(eap->arg, AL_DEL, 0, false);
- }
- maketitle();
-}
-
/// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
void ex_listdo(exarg_T *eap)
{
@@ -1173,51 +695,6 @@ void ex_listdo(exarg_T *eap)
}
}
-/// Add files[count] to the arglist of the current window after arg "after".
-/// The file names in files[count] must have been allocated and are taken over.
-/// Files[] itself is not taken over.
-///
-/// @param after: where to add: 0 = before first one
-/// @param will_edit will edit adding argument
-static void alist_add_list(int count, char **files, int after, bool will_edit)
- FUNC_ATTR_NONNULL_ALL
-{
- int old_argcount = ARGCOUNT;
- ga_grow(&ALIST(curwin)->al_ga, count);
- {
- if (after < 0) {
- after = 0;
- }
- if (after > ARGCOUNT) {
- after = ARGCOUNT;
- }
- if (after < ARGCOUNT) {
- memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
- (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
- }
- for (int i = 0; i < count; i++) {
- const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
- ARGLIST[after + i].ae_fname = (char_u *)files[i];
- ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
- }
- ALIST(curwin)->al_ga.ga_len += count;
- if (old_argcount > 0 && curwin->w_arg_idx >= after) {
- curwin->w_arg_idx += count;
- }
- return;
- }
-}
-
-// Function given to ExpandGeneric() to obtain the possible arguments of the
-// argedit and argdelete commands.
-char *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
-{
- if (idx >= ARGCOUNT) {
- return NULL;
- }
- return alist_name(&ARGLIST[idx]);
-}
-
/// ":compiler[!] {name}"
void ex_compiler(exarg_T *eap)
{
@@ -1675,7 +1152,7 @@ void ex_drop(exarg_T *eap)
// and mostly only one file is dropped.
// This also ignores wildcards, since it is very unlikely the user is
// editing a file name with a wildcard character.
- do_arglist(eap->arg, AL_SET, 0, false);
+ set_arglist(eap->arg);
// Expanding wildcards may result in an empty argument list. E.g. when
// editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 864db643ed..afa8a276c8 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
@@ -18,6 +19,7 @@
#include "nvim/debugger.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
@@ -39,6 +41,7 @@
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/hardcopy.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
#include "nvim/input.h"
@@ -63,11 +66,10 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
@@ -5510,16 +5512,6 @@ static void ex_only(exarg_T *eap)
close_others(true, eap->forceit);
}
-/// ":all" and ":sall".
-/// Also used for ":tab drop file ..." after setting the argument list.
-void ex_all(exarg_T *eap)
-{
- if (eap->addr_count == 0) {
- eap->line2 = 9999;
- }
- do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop);
-}
-
static void ex_hide(exarg_T *eap)
{
// ":hide" or ":hide | cmd": hide current window
@@ -5632,158 +5624,6 @@ static void ex_goto(exarg_T *eap)
goto_byte(eap->line2);
}
-/// Clear an argument list: free all file names and reset it to zero entries.
-void alist_clear(alist_T *al)
-{
-#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
- GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
-}
-
-/// Init an argument list.
-void alist_init(alist_T *al)
-{
- ga_init(&al->al_ga, (int)sizeof(aentry_T), 5);
-}
-
-/// Remove a reference from an argument list.
-/// Ignored when the argument list is the global one.
-/// If the argument list is no longer used by any window, free it.
-void alist_unlink(alist_T *al)
-{
- if (al != &global_alist && --al->al_refcount <= 0) {
- alist_clear(al);
- xfree(al);
- }
-}
-
-/// Create a new argument list and use it for the current window.
-void alist_new(void)
-{
- curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
- curwin->w_alist->al_refcount = 1;
- curwin->w_alist->id = ++max_alist_id;
- alist_init(curwin->w_alist);
-}
-
-#if !defined(UNIX)
-
-/// Expand the file names in the global argument list.
-/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
-/// numbers to be re-used.
-void alist_expand(int *fnum_list, int fnum_len)
-{
- char *save_p_su = p_su;
-
- // Don't use 'suffixes' here. This should work like the shell did the
- // expansion. Also, the vimrc file isn't read yet, thus the user
- // can't set the options.
- p_su = empty_option;
- char **old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT);
- for (int i = 0; i < GARGCOUNT; i++) {
- old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname);
- }
- int old_arg_count = GARGCOUNT;
- char **new_arg_files;
- int new_arg_file_count;
- if (expand_wildcards(old_arg_count, old_arg_files,
- &new_arg_file_count, &new_arg_files,
- EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK
- && new_arg_file_count > 0) {
- alist_set(&global_alist, new_arg_file_count, new_arg_files,
- true, fnum_list, fnum_len);
- FreeWild(old_arg_count, old_arg_files);
- }
- p_su = save_p_su;
-}
-#endif
-
-/// Set the argument list for the current window.
-/// Takes over the allocated files[] and the allocated fnames in it.
-void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len)
-{
- static int recursive = 0;
-
- if (recursive) {
- emsg(_(e_au_recursive));
- return;
- }
- recursive++;
-
- alist_clear(al);
- ga_grow(&al->al_ga, count);
- {
- for (int i = 0; i < count; i++) {
- if (got_int) {
- // When adding many buffers this can take a long time. Allow
- // interrupting here.
- while (i < count) {
- xfree(files[i++]);
- }
- break;
- }
-
- // May set buffer name of a buffer previously used for the
- // argument list, so that it's re-used by alist_add.
- if (fnum_list != NULL && i < fnum_len) {
- buf_set_name(fnum_list[i], files[i]);
- }
-
- alist_add(al, files[i], use_curbuf ? 2 : 1);
- os_breakcheck();
- }
- xfree(files);
- }
-
- if (al == &global_alist) {
- arg_had_last = false;
- }
- recursive--;
-}
-
-/// Add file "fname" to argument list "al".
-/// "fname" must have been allocated and "al" must have been checked for room.
-///
-/// @param set_fnum 1: set buffer number; 2: re-use curbuf
-void alist_add(alist_T *al, char *fname, int set_fnum)
-{
- if (fname == NULL) { // don't add NULL file names
- return;
- }
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(fname);
-#endif
- AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname;
- if (set_fnum > 0) {
- AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
- buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
- }
- al->al_ga.ga_len++;
-}
-
-#if defined(BACKSLASH_IN_FILENAME)
-
-/// Adjust slashes in file names. Called after 'shellslash' was set.
-void alist_slash_adjust(void)
-{
- for (int i = 0; i < GARGCOUNT; i++) {
- if (GARGLIST[i].ae_fname != NULL) {
- slash_adjust(GARGLIST[i].ae_fname);
- }
- }
-
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_alist != &global_alist) {
- for (int i = 0; i < WARGCOUNT(wp); i++) {
- if (WARGLIST(wp)[i].ae_fname != NULL) {
- slash_adjust(WARGLIST(wp)[i].ae_fname);
- }
- }
- }
- }
-}
-
-#endif
-
/// ":preserve".
static void ex_preserve(exarg_T *eap)
{
@@ -6994,6 +6834,7 @@ static void ex_redraw(exarg_T *eap)
update_topline(curwin);
if (eap->forceit) {
redraw_all_later(NOT_VALID);
+ redraw_cmdline = true;
}
update_screen(eap->forceit ? NOT_VALID
: VIsual_active ? INVERTED : 0);
@@ -7829,62 +7670,6 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
return (char_u *)result;
}
-/// Concatenate all files in the argument list, separated by spaces, and return
-/// it in one allocated string.
-/// Spaces and backslashes in the file names are escaped with a backslash.
-static char *arg_all(void)
-{
- char *retval = NULL;
-
- // Do this loop two times:
- // first time: compute the total length
- // second time: concatenate the names
- for (;;) {
- int len = 0;
- for (int idx = 0; idx < ARGCOUNT; idx++) {
- char *p = alist_name(&ARGLIST[idx]);
- if (p == NULL) {
- continue;
- }
- if (len > 0) {
- // insert a space in between names
- if (retval != NULL) {
- retval[len] = ' ';
- }
- len++;
- }
- for (; *p != NUL; p++) {
- if (*p == ' '
-#ifndef BACKSLASH_IN_FILENAME
- || *p == '\\'
-#endif
- || *p == '`') {
- // insert a backslash
- if (retval != NULL) {
- retval[len] = '\\';
- }
- len++;
- }
- if (retval != NULL) {
- retval[len] = *p;
- }
- len++;
- }
- }
-
- // second time: break here
- if (retval != NULL) {
- retval[len] = NUL;
- break;
- }
-
- // allocate memory
- retval = xmalloc((size_t)len + 1);
- }
-
- return retval;
-}
-
/// Expand the <sfile> string in "arg".
///
/// @return an allocated string, or NULL for any error.
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index c39bb16498..69d509abb7 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -505,7 +505,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
} else {
verbose_enter();
}
- ++no_wait_return;
+ no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
msg_scroll = TRUE; // always scroll up, don't overwrite
}
@@ -515,7 +515,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
if (debug_break_level > 0 || *p_vfile == NUL) {
cmdline_row = msg_row;
}
- --no_wait_return;
+ no_wait_return--;
if (debug_break_level > 0) {
msg_silent = save_msg_silent;
} else {
@@ -558,7 +558,7 @@ static void discard_exception(except_T *excp, bool was_finished)
} else {
verbose_enter();
}
- ++no_wait_return;
+ no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
msg_scroll = TRUE; // always scroll up, don't overwrite
}
@@ -626,7 +626,7 @@ static void catch_exception(except_T *excp)
} else {
verbose_enter();
}
- ++no_wait_return;
+ no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
msg_scroll = TRUE; // always scroll up, don't overwrite
}
@@ -636,7 +636,7 @@ static void catch_exception(except_T *excp)
if (debug_break_level > 0 || *p_vfile == NUL) {
cmdline_row = msg_row;
}
- --no_wait_return;
+ no_wait_return--;
if (debug_break_level > 0) {
msg_silent = save_msg_silent;
} else {
@@ -748,12 +748,12 @@ static void report_pending(int action, int pending, void *value)
if (debug_break_level > 0) {
msg_silent = FALSE; // display messages
}
- ++no_wait_return;
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ no_wait_return++;
+ msg_scroll = true; // always scroll up, don't overwrite
smsg(mesg, s);
msg_puts("\n"); // don't overwrite this either
cmdline_row = msg_row;
- --no_wait_return;
+ no_wait_return--;
if (debug_break_level > 0) {
msg_silent = save_msg_silent;
}
@@ -2054,7 +2054,7 @@ int has_loop_cmd(char *p)
// skip modifiers, white space and ':'
for (;;) {
while (*p == ' ' || *p == '\t' || *p == ':') {
- ++p;
+ p++;
}
len = modifier_len(p);
if (len == 0) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 6474225b26..c15d85967d 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -15,6 +15,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/arabic.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/buffer.h"
@@ -23,6 +24,7 @@
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
@@ -38,6 +40,8 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
+#include "nvim/help.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -64,10 +68,9 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sign.h"
#include "nvim/state.h"
@@ -2181,14 +2184,14 @@ static int command_line_handle_key(CommandLineState *s)
if (i > 0) {
ccline.cmdbuff[len] = '\\';
}
- ++len;
+ len++;
}
if (i > 0) {
ccline.cmdbuff[len] = p[j];
}
}
- ++len;
+ len++;
}
if (i == 0) {
@@ -3688,7 +3691,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
msg_col -= i;
if (msg_col < 0) {
msg_col += Columns;
- --msg_row;
+ msg_row--;
}
}
}
@@ -4234,9 +4237,9 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
if (findex == -1) {
findex = xp->xp_numfiles;
}
- --findex;
+ findex--;
} else { // mode == WILD_NEXT
- ++findex;
+ findex++;
}
/*
@@ -4976,7 +4979,7 @@ char_u *addstar(char_u *fname, size_t len, int context)
&& vim_strchr((char *)retval, '`') == NULL) {
retval[len++] = '*';
} else if (len > 0 && retval[len - 1] == '$') {
- --len;
+ len--;
}
retval[len] = NUL;
}
@@ -5124,58 +5127,6 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
return EXPAND_OK;
}
-// Cleanup matches for help tags:
-// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
-// tag matches it. Otherwise remove "@en" if "en" is the only language.
-static void cleanup_help_tags(int num_file, char **file)
-{
- char_u buf[4];
- char_u *p = buf;
-
- if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
- *p++ = '@';
- *p++ = p_hlg[0];
- *p++ = p_hlg[1];
- }
- *p = NUL;
-
- for (int i = 0; i < num_file; i++) {
- int len = (int)STRLEN(file[i]) - 3;
- if (len <= 0) {
- continue;
- }
- if (STRCMP(file[i] + len, "@en") == 0) {
- // Sorting on priority means the same item in another language may
- // be anywhere. Search all items for a match up to the "@en".
- int j;
- for (j = 0; j < num_file; j++) {
- if (j != i
- && (int)STRLEN(file[j]) == len + 3
- && STRNCMP(file[i], file[j], len + 1) == 0) {
- break;
- }
- }
- if (j == num_file) {
- // item only exists with @en, remove it
- file[i][len] = NUL;
- }
- }
- }
-
- if (*buf != NUL) {
- for (int i = 0; i < num_file; i++) {
- int len = (int)STRLEN(file[i]) - 3;
- if (len <= 0) {
- continue;
- }
- if (STRCMP(file[i] + len, buf) == 0) {
- // remove the default language
- file[i][len] = NUL;
- }
- }
- }
-}
-
typedef char *(*ExpandFunc)(expand_T *, int);
/// Do the expansion based on xp->xp_context and "pat".
@@ -5385,8 +5336,8 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
{ EXPAND_SYNTAX, get_syntax_name, true, true },
{ EXPAND_SYNTIME, get_syntime_arg, true, true },
{ EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true },
- { EXPAND_EVENTS, expand_get_event_name, true, true },
- { EXPAND_AUGROUP, expand_get_augroup_name, true, true },
+ { EXPAND_EVENTS, expand_get_event_name, true, false },
+ { EXPAND_AUGROUP, expand_get_augroup_name, true, false },
{ EXPAND_CSCOPE, get_cscope_name, true, true },
{ EXPAND_SIGN, get_sign_name, true, true },
{ EXPAND_PROFILE, get_profile_name, true, true },
@@ -5617,7 +5568,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int fl
}
}
if (*e != NUL) {
- ++e;
+ e++;
}
}
*file = ga.ga_data;
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 9ffe0e1f7f..9495ae1c4b 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/cursor.h"
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 176765e6ac..2d09e7aa71 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -307,7 +307,7 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len);
}
if (*++path != NUL) {
- ++path;
+ path++;
}
} else if (*path == NUL || !vim_isAbsName(path)) {
#ifdef BACKSLASH_IN_FILENAME
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index bc76e3225e..d29a0f0a08 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -18,6 +18,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -49,7 +50,6 @@
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sha256.h"
#include "nvim/shada.h"
@@ -632,7 +632,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
if (aborting()) { // autocmds may abort script processing
- --no_wait_return;
+ no_wait_return--;
msg_scroll = msg_save;
curbuf->b_p_ro = TRUE; // must use "w!" now
return FAIL;
@@ -1197,11 +1197,11 @@ retry:
}
// Deal with a bad byte and continue with the next.
- ++fromp;
- --from_size;
+ fromp++;
+ from_size--;
if (bad_char_behavior == BAD_KEEP) {
*top++ = *(fromp - 1);
- --to_size;
+ to_size--;
} else if (bad_char_behavior != BAD_DROP) {
*top++ = (char)bad_char_behavior;
to_size--;
@@ -1239,7 +1239,7 @@ retry:
// Check for a trailing incomplete UTF-8 sequence
tail = ptr + size - 1;
while (tail > ptr && (*tail & 0xc0) == 0x80) {
- --tail;
+ tail--;
}
if (tail + utf_byte2len(*tail) <= ptr + size) {
tail = NULL;
@@ -1557,7 +1557,7 @@ rewind_retry:
* Keep it fast!
*/
if (fileformat == EOL_MAC) {
- --ptr;
+ ptr--;
while (++ptr, --size >= 0) {
// catch most common case first
if ((c = *ptr) != NUL && c != CAR && c != NL) {
@@ -1578,20 +1578,20 @@ rewind_retry:
if (read_undo_file) {
sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
- ++lnum;
+ lnum++;
if (--read_count == 0) {
error = true; // break loop
line_start = ptr; // nothing left to write
break;
}
} else {
- --skip_count;
+ skip_count--;
}
line_start = ptr + 1;
}
}
} else {
- --ptr;
+ ptr--;
while (++ptr, --size >= 0) {
if ((c = *ptr) != NUL && c != NL) { // catch most common case
continue;
@@ -1634,14 +1634,14 @@ rewind_retry:
if (read_undo_file) {
sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
- ++lnum;
+ lnum++;
if (--read_count == 0) {
error = true; // break loop
line_start = ptr; // nothing left to write
break;
}
} else {
- --skip_count;
+ skip_count--;
}
line_start = ptr + 1;
}
@@ -2000,7 +2000,7 @@ static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp)
lnum = curbuf->b_ml.ml_line_count - linecnt + 1;
for (s = p; s < endp; ++s) {
if (*s == '\n') {
- ++lnum;
+ lnum++;
}
}
return lnum;
@@ -2478,7 +2478,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
} else { // less lines
end -= old_line_count - buf->b_ml.ml_line_count;
if (end < start) {
- --no_wait_return;
+ no_wait_return--;
msg_scroll = msg_save;
emsg(_("E204: Autocommand changed number of lines in unexpected way"));
return FAIL;
@@ -4820,8 +4820,8 @@ int check_timestamps(int focus)
}
}
}
- --no_wait_return;
- need_check_timestamps = FALSE;
+ no_wait_return--;
+ need_check_timestamps = false;
if (need_wait_return && didit == 2) {
// make sure msg isn't overwritten
msg_puts("\n");
@@ -5681,7 +5681,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
reg_pat[i++] = '.';
reg_pat[i++] = '*';
while (p[1] == '*') { // "**" matches like "*"
- ++p;
+ p++;
}
break;
case '.':
@@ -5769,7 +5769,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
case '}':
reg_pat[i++] = '\\';
reg_pat[i++] = ')';
- --nested;
+ nested--;
break;
case ',':
if (nested) {
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index b02c12c7cb..8ce24fd378 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -14,6 +14,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_session.h"
@@ -31,7 +32,6 @@
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -863,7 +863,7 @@ int foldMoveTo(const bool updown, const int dir, const long count)
if (fp - (fold_T *)gap->ga_data >= gap->ga_len) {
break;
}
- --fp;
+ fp--;
} else {
if (fp == (fold_T *)gap->ga_data) {
break;
@@ -1793,7 +1793,7 @@ static void foldtext_cleanup(char_u *str)
char_u *cms_start = (char_u *)skipwhite((char *)curbuf->b_p_cms);
size_t cms_slen = STRLEN(cms_start);
while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) {
- --cms_slen;
+ cms_slen--;
}
// locate "%s" in 'commentstring', use the part before and after it.
@@ -1805,7 +1805,7 @@ static void foldtext_cleanup(char_u *str)
// exclude white space before "%s"
while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) {
- --cms_slen;
+ cms_slen--;
}
// skip "%s" and white space after it
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index bc2c440bb1..a3af7dc372 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -15,8 +15,10 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/event/loop.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
@@ -41,7 +43,6 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -1337,7 +1338,7 @@ static void closescript(void)
file_free(scriptin[curscript], false);
scriptin[curscript] = NULL;
if (curscript > 0) {
- --curscript;
+ curscript--;
}
}
@@ -1696,6 +1697,149 @@ int char_avail(void)
return retval != NUL;
}
+/// "getchar()" and "getcharstr()" functions
+static void getchar_common(typval_T *argvars, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ varnumber_T n;
+ bool error = false;
+
+ no_mapping++;
+ allow_keys++;
+ for (;;) {
+ // Position the cursor. Needed after a message that ends in a space,
+ // or if event processing caused a redraw.
+ ui_cursor_goto(msg_row, msg_col);
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // getchar(): blocking wait.
+ // TODO(bfredl): deduplicate shared logic with state_enter ?
+ if (!char_avail()) {
+ // flush output before waiting
+ ui_flush();
+ (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
+ if (!multiqueue_empty(main_loop.events)) {
+ state_handle_k_event();
+ continue;
+ }
+ }
+ n = safe_vgetc();
+ } else if (tv_get_number_chk(&argvars[0], &error) == 1) {
+ // getchar(1): only check if char avail
+ n = vpeekc_any();
+ } else if (error || vpeekc_any() == NUL) {
+ // illegal argument or getchar(0) and no char avail: return zero
+ n = 0;
+ } else {
+ // getchar(0) and char avail() != NUL: get a character.
+ // Note that vpeekc_any() returns K_SPECIAL for K_IGNORE.
+ n = safe_vgetc();
+ }
+
+ if (n == K_IGNORE
+ || n == K_MOUSEMOVE
+ || n == K_VER_SCROLLBAR
+ || n == K_HOR_SCROLLBAR) {
+ continue;
+ }
+ break;
+ }
+ no_mapping--;
+ allow_keys--;
+
+ if (!ui_has_messages()) {
+ // redraw the screen after getchar()
+ update_screen(CLEAR);
+ }
+
+ set_vim_var_nr(VV_MOUSE_WIN, 0);
+ set_vim_var_nr(VV_MOUSE_WINID, 0);
+ set_vim_var_nr(VV_MOUSE_LNUM, 0);
+ set_vim_var_nr(VV_MOUSE_COL, 0);
+
+ rettv->vval.v_number = n;
+ if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
+ char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
+ int i = 0;
+
+ // Turn a special key into three bytes, plus modifier.
+ if (mod_mask != 0) {
+ temp[i++] = K_SPECIAL;
+ temp[i++] = KS_MODIFIER;
+ temp[i++] = (char_u)mod_mask;
+ }
+ if (IS_SPECIAL(n)) {
+ temp[i++] = K_SPECIAL;
+ temp[i++] = (char_u)K_SECOND(n);
+ temp[i++] = K_THIRD(n);
+ } else {
+ i += utf_char2bytes((int)n, (char *)temp + i);
+ }
+ assert(i < 10);
+ temp[i++] = NUL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char *)vim_strsave(temp);
+
+ if (is_mouse_key((int)n)) {
+ int row = mouse_row;
+ int col = mouse_col;
+ int grid = mouse_grid;
+ linenr_T lnum;
+ win_T *wp;
+ int winnr = 1;
+
+ if (row >= 0 && col >= 0) {
+ // Find the window at the mouse coordinates and compute the
+ // text position.
+ win_T *const win = mouse_find_win(&grid, &row, &col);
+ if (win == NULL) {
+ return;
+ }
+ (void)mouse_comp_pos(win, &row, &col, &lnum);
+ for (wp = firstwin; wp != win; wp = wp->w_next) {
+ winnr++;
+ }
+ set_vim_var_nr(VV_MOUSE_WIN, winnr);
+ set_vim_var_nr(VV_MOUSE_WINID, wp->handle);
+ set_vim_var_nr(VV_MOUSE_LNUM, lnum);
+ set_vim_var_nr(VV_MOUSE_COL, col + 1);
+ }
+ }
+ }
+}
+
+/// "getchar()" function
+void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ getchar_common(argvars, rettv);
+}
+
+/// "getcharstr()" function
+void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ getchar_common(argvars, rettv);
+
+ if (rettv->v_type == VAR_NUMBER) {
+ char temp[7]; // mbyte-char: 6, NUL: 1
+ const varnumber_T n = rettv->vval.v_number;
+ int i = 0;
+
+ if (n != 0) {
+ i += utf_char2bytes((int)n, (char *)temp);
+ }
+ assert(i < 7);
+ temp[i++] = NUL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrdup(temp);
+ }
+}
+
+/// "getcharmod()" function
+void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = mod_mask;
+}
+
typedef enum {
map_result_fail, // failed, break loop
map_result_get, // get a character from typeahead
@@ -2252,7 +2396,7 @@ static int vgetorpeek(bool advance)
return NUL;
}
- ++vgetc_busy;
+ vgetc_busy++;
if (advance) {
KeyStuffed = FALSE;
@@ -2639,7 +2783,7 @@ static int vgetorpeek(bool advance)
gotchars(nop_buf, 3);
}
- --vgetc_busy;
+ vgetc_busy--;
return c;
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 545a2729e6..954b62883e 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -96,9 +96,6 @@ EXTERN struct nvim_stats_s {
EXTERN int Rows INIT(= DFLT_ROWS); // nr of rows in the screen
EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen
-EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights
-EXTERN bool ns_hl_changed INIT(= false); // highlight need update
-
// We use 64-bit file functions here, if available. E.g. ftello() returns
// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit.
// We assume that when fseeko() is available then ftello() is too.
@@ -145,6 +142,7 @@ EXTERN int vgetc_char INIT(= 0);
EXTERN int cmdline_row;
EXTERN bool redraw_cmdline INIT(= false); // cmdline must be redrawn
+EXTERN bool redraw_mode INIT(= false); // mode must be redrawn
EXTERN bool clear_cmdline INIT(= false); // cmdline must be cleared
EXTERN bool mode_displayed INIT(= false); // mode is being displayed
EXTERN int cmdline_star INIT(= false); // cmdline is encrypted
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 72e85c425d..f95ef3e705 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -1,10 +1,19 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+// Most of the routines in this file perform screen (grid) manipulations. The
+// given operation is performed physically on the screen. The corresponding
+// change is also made to the internal screen image. In this way, the editor
+// anticipates the effect of editing changes on the appearance of the screen.
+// That way, when we call update_screen() a complete redraw isn't usually
+// necessary. Another advantage is that we can keep adding code to anticipate
+// screen changes, and in the meantime, everything still works.
+//
+// The grid_*() functions write to the screen and handle updating grid->lines[].
+
#include "nvim/arabic.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
-#include "nvim/screen.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -780,3 +789,123 @@ void grid_assign_handle(ScreenGrid *grid)
grid->handle = ++last_grid_handle;
}
}
+
+/// insert lines on the screen and move the existing lines down
+/// 'line_count' is the number of lines to be inserted.
+/// 'end' is the line after the scrolled part. Normally it is Rows.
+/// 'col' is the column from with we start inserting.
+//
+/// 'row', 'col' and 'end' are relative to the start of the region.
+void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
+{
+ int i;
+ int j;
+ unsigned temp;
+
+ int row_off = 0;
+ grid_adjust(&grid, &row_off, &col);
+ row += row_off;
+ end += row_off;
+
+ if (line_count <= 0) {
+ return;
+ }
+
+ // Shift line_offset[] line_count down to reflect the inserted lines.
+ // Clear the inserted lines.
+ for (i = 0; i < line_count; i++) {
+ if (width != grid->cols) {
+ // need to copy part of a line
+ j = end - 1 - i;
+ while ((j -= line_count) >= row) {
+ linecopy(grid, j + line_count, j, col, width);
+ }
+ j += line_count;
+ grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
+ grid->line_wraps[j] = false;
+ } else {
+ j = end - 1 - i;
+ temp = (unsigned)grid->line_offset[j];
+ while ((j -= line_count) >= row) {
+ grid->line_offset[j + line_count] = grid->line_offset[j];
+ grid->line_wraps[j + line_count] = grid->line_wraps[j];
+ }
+ grid->line_offset[j + line_count] = temp;
+ grid->line_wraps[j + line_count] = false;
+ grid_clear_line(grid, temp, grid->cols, false);
+ }
+ }
+
+ if (!grid->throttled) {
+ ui_call_grid_scroll(grid->handle, row, end, col, col + width, -line_count, 0);
+ }
+}
+
+/// delete lines on the screen and move lines up.
+/// 'end' is the line after the scrolled part. Normally it is Rows.
+/// When scrolling region used 'off' is the offset from the top for the region.
+/// 'row' and 'end' are relative to the start of the region.
+void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
+{
+ int j;
+ int i;
+ unsigned temp;
+
+ int row_off = 0;
+ grid_adjust(&grid, &row_off, &col);
+ row += row_off;
+ end += row_off;
+
+ if (line_count <= 0) {
+ return;
+ }
+
+ // Now shift line_offset[] line_count up to reflect the deleted lines.
+ // Clear the inserted lines.
+ for (i = 0; i < line_count; i++) {
+ if (width != grid->cols) {
+ // need to copy part of a line
+ j = row + i;
+ while ((j += line_count) <= end - 1) {
+ linecopy(grid, j - line_count, j, col, width);
+ }
+ j -= line_count;
+ grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
+ grid->line_wraps[j] = false;
+ } else {
+ // whole width, moving the line pointers is faster
+ j = row + i;
+ temp = (unsigned)grid->line_offset[j];
+ while ((j += line_count) <= end - 1) {
+ grid->line_offset[j - line_count] = grid->line_offset[j];
+ grid->line_wraps[j - line_count] = grid->line_wraps[j];
+ }
+ grid->line_offset[j - line_count] = temp;
+ grid->line_wraps[j - line_count] = false;
+ grid_clear_line(grid, temp, grid->cols, false);
+ }
+ }
+
+ if (!grid->throttled) {
+ ui_call_grid_scroll(grid->handle, row, end, col, col + width, line_count, 0);
+ }
+}
+
+static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
+{
+ unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col);
+ unsigned off_from = (unsigned)(grid->line_offset[from] + (size_t)col);
+
+ memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T));
+ memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T));
+}
+
+win_T *get_win_by_grid_handle(handle_T handle)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_grid_alloc.handle == handle) {
+ return wp;
+ }
+ }
+ return NULL;
+}
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
index 4a8072bd96..6a93bf3d90 100644
--- a/src/nvim/grid.h
+++ b/src/nvim/grid.h
@@ -19,6 +19,9 @@ EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
+/// While resizing the screen this flag is set.
+EXTERN bool resizing_screen INIT(= 0);
+
EXTERN schar_T *linebuf_char INIT(= NULL);
EXTERN sattr_T *linebuf_attr INIT(= NULL);
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 0be45ec9ae..6a1bbcb089 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -20,8 +20,10 @@
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/hardcopy.h"
#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -31,7 +33,6 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -350,7 +351,7 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
stringp = (char_u *)commap;
if (*stringp == ',') {
- ++stringp;
+ stringp++;
}
}
@@ -837,7 +838,7 @@ void ex_hardcopy(exarg_T *eap)
}
}
if (settings.duplex && prtpos.file_line <= eap->line2) {
- ++page_count;
+ page_count++;
}
// Remember the position where the next page starts.
@@ -900,7 +901,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
}
// syntax highlighting stuff.
if (psettings->do_syntax) {
- id = syn_get_id(curwin, ppos->file_line, col, 1, NULL, FALSE);
+ id = syn_get_id(curwin, ppos->file_line, col, 1, NULL, false);
if (id > 0) {
id = syn_get_final_id(id);
} else {
diff --git a/src/nvim/help.c b/src/nvim/help.c
new file mode 100644
index 0000000000..172d389e74
--- /dev/null
+++ b/src/nvim/help.c
@@ -0,0 +1,1178 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// help.c: functions for Vim help
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_getln.h"
+#include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/help.h"
+#include "nvim/memory.h"
+#include "nvim/option.h"
+#include "nvim/os/input.h"
+#include "nvim/path.h"
+#include "nvim/strings.h"
+#include "nvim/syntax.h"
+#include "nvim/tag.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "help.c.generated.h"
+#endif
+
+/// ":help": open a read-only window on a help file
+void ex_help(exarg_T *eap)
+{
+ char *arg;
+ char *tag;
+ FILE *helpfd; // file descriptor of help file
+ int n;
+ int i;
+ win_T *wp;
+ int num_matches;
+ char **matches;
+ char *p;
+ int empty_fnum = 0;
+ int alt_fnum = 0;
+ buf_T *buf;
+ int len;
+ char *lang;
+ const bool old_KeyTyped = KeyTyped;
+
+ if (eap != NULL) {
+ // A ":help" command ends at the first LF, or at a '|' that is
+ // followed by some text. Set nextcmd to the following command.
+ for (arg = eap->arg; *arg; arg++) {
+ if (*arg == '\n' || *arg == '\r'
+ || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) {
+ *arg++ = NUL;
+ eap->nextcmd = arg;
+ break;
+ }
+ }
+ arg = eap->arg;
+
+ if (eap->forceit && *arg == NUL && !curbuf->b_help) {
+ emsg(_("E478: Don't panic!"));
+ return;
+ }
+
+ if (eap->skip) { // not executing commands
+ return;
+ }
+ } else {
+ arg = "";
+ }
+
+ // remove trailing blanks
+ p = arg + STRLEN(arg) - 1;
+ while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') {
+ *p-- = NUL;
+ }
+
+ // Check for a specified language
+ lang = check_help_lang(arg);
+
+ // When no argument given go to the index.
+ if (*arg == NUL) {
+ arg = "help.txt";
+ }
+
+ // Check if there is a match for the argument.
+ n = find_help_tags(arg, &num_matches, &matches, eap != NULL && eap->forceit);
+
+ i = 0;
+ if (n != FAIL && lang != NULL) {
+ // Find first item with the requested language.
+ for (i = 0; i < num_matches; i++) {
+ len = (int)STRLEN(matches[i]);
+ if (len > 3 && matches[i][len - 3] == '@'
+ && STRICMP(matches[i] + len - 2, lang) == 0) {
+ break;
+ }
+ }
+ }
+ if (i >= num_matches || n == FAIL) {
+ if (lang != NULL) {
+ semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg);
+ } else {
+ semsg(_("E149: Sorry, no help for %s"), arg);
+ }
+ if (n != FAIL) {
+ FreeWild(num_matches, matches);
+ }
+ return;
+ }
+
+ // The first match (in the requested language) is the best match.
+ tag = xstrdup(matches[i]);
+ FreeWild(num_matches, matches);
+
+ // Re-use an existing help window or open a new one.
+ // Always open a new one for ":tab help".
+ if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {
+ if (cmdmod.cmod_tab != 0) {
+ wp = NULL;
+ } else {
+ wp = NULL;
+ FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
+ if (bt_help(wp2->w_buffer)) {
+ wp = wp2;
+ break;
+ }
+ }
+ }
+ if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
+ win_enter(wp, true);
+ } else {
+ // There is no help window yet.
+ // Try to open the file specified by the "helpfile" option.
+ if ((helpfd = os_fopen((char *)p_hf, READBIN)) == NULL) {
+ smsg(_("Sorry, help file \"%s\" not found"), p_hf);
+ goto erret;
+ }
+ fclose(helpfd);
+
+ // Split off help window; put it at far top if no position
+ // specified, the current window is vertically split and
+ // narrow.
+ n = WSP_HELP;
+ if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
+ && curwin->w_width < 80) {
+ n |= WSP_TOP;
+ }
+ if (win_split(0, n) == FAIL) {
+ goto erret;
+ }
+
+ if (curwin->w_height < p_hh) {
+ win_setheight((int)p_hh);
+ }
+
+ // Open help file (do_ecmd() will set b_help flag, readfile() will
+ // set b_p_ro flag).
+ // Set the alternate file to the previously edited file.
+ alt_fnum = curbuf->b_fnum;
+ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
+ ECMD_HIDE + ECMD_SET_HELP,
+ NULL); // buffer is still open, don't store info
+
+ if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
+ curwin->w_alt_fnum = alt_fnum;
+ }
+ empty_fnum = curbuf->b_fnum;
+ }
+ }
+
+ restart_edit = 0; // don't want insert mode in help file
+
+ // Restore KeyTyped, setting 'filetype=help' may reset it.
+ // It is needed for do_tag top open folds under the cursor.
+ KeyTyped = old_KeyTyped;
+
+ do_tag((char_u *)tag, DT_HELP, 1, false, true);
+
+ // Delete the empty buffer if we're not using it. Careful: autocommands
+ // may have jumped to another window, check that the buffer is not in a
+ // window.
+ if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
+ buf = buflist_findnr(empty_fnum);
+ if (buf != NULL && buf->b_nwindows == 0) {
+ wipe_buffer(buf, true);
+ }
+ }
+
+ // keep the previous alternate file
+ if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum
+ && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
+ curwin->w_alt_fnum = alt_fnum;
+ }
+
+erret:
+ xfree(tag);
+}
+
+/// ":helpclose": Close one help window
+void ex_helpclose(exarg_T *eap)
+{
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (bt_help(win->w_buffer)) {
+ win_close(win, false, eap->forceit);
+ return;
+ }
+ }
+}
+
+/// In an argument search for a language specifiers in the form "@xx".
+/// Changes the "@" to NUL if found, and returns a pointer to "xx".
+///
+/// @return NULL if not found.
+char *check_help_lang(char *arg)
+{
+ int len = (int)STRLEN(arg);
+
+ if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
+ && ASCII_ISALPHA(arg[len - 1])) {
+ arg[len - 3] = NUL; // remove the '@'
+ return arg + len - 2;
+ }
+ return NULL;
+}
+
+/// Return a heuristic indicating how well the given string matches. The
+/// smaller the number, the better the match. This is the order of priorities,
+/// from best match to worst match:
+/// - Match with least alphanumeric characters is better.
+/// - Match with least total characters is better.
+/// - Match towards the start is better.
+/// - Match starting with "+" is worse (feature instead of command)
+/// Assumption is made that the matched_string passed has already been found to
+/// match some string for which help is requested. webb.
+///
+/// @param offset offset for match
+/// @param wrong_case no matching case
+///
+/// @return a heuristic indicating how well the given string matches.
+int help_heuristic(char *matched_string, int offset, int wrong_case)
+ FUNC_ATTR_PURE
+{
+ int num_letters;
+ char *p;
+
+ num_letters = 0;
+ for (p = matched_string; *p; p++) {
+ if (ASCII_ISALNUM(*p)) {
+ num_letters++;
+ }
+ }
+
+ // Multiply the number of letters by 100 to give it a much bigger
+ // weighting than the number of characters.
+ // If there only is a match while ignoring case, add 5000.
+ // If the match starts in the middle of a word, add 10000 to put it
+ // somewhere in the last half.
+ // If the match is more than 2 chars from the start, multiply by 200 to
+ // put it after matches at the start.
+ if (offset > 0
+ && ASCII_ISALNUM(matched_string[offset])
+ && ASCII_ISALNUM(matched_string[offset - 1])) {
+ offset += 10000;
+ } else if (offset > 2) {
+ offset *= 200;
+ }
+ if (wrong_case) {
+ offset += 5000;
+ }
+ // Features are less interesting than the subjects themselves, but "+"
+ // alone is not a feature.
+ if (matched_string[0] == '+' && matched_string[1] != NUL) {
+ offset += 100;
+ }
+ return 100 * num_letters + (int)STRLEN(matched_string) + offset;
+}
+
+/// Compare functions for qsort() below, that checks the help heuristics number
+/// that has been put after the tagname by find_tags().
+static int help_compare(const void *s1, const void *s2)
+{
+ char *p1;
+ char *p2;
+
+ p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
+ p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
+
+ // Compare by help heuristic number first.
+ int cmp = strcmp(p1, p2);
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ // Compare by strings as tie-breaker when same heuristic number.
+ return strcmp(*(char **)s1, *(char **)s2);
+}
+
+/// Find all help tags matching "arg", sort them and return in matches[], with
+/// the number of matches in num_matches.
+/// The matches will be sorted with a "best" match algorithm.
+/// When "keep_lang" is true try keeping the language of the current buffer.
+int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep_lang)
+{
+ int i;
+
+ // Specific tags that either have a specific replacement or won't go
+ // through the generic rules.
+ static char *(except_tbl[][2]) = {
+ { "*", "star" },
+ { "g*", "gstar" },
+ { "[*", "[star" },
+ { "]*", "]star" },
+ { ":*", ":star" },
+ { "/*", "/star" }, // NOLINT
+ { "/\\*", "/\\\\star" },
+ { "\"*", "quotestar" },
+ { "**", "starstar" },
+ { "cpo-*", "cpo-star" },
+ { "/\\(\\)", "/\\\\(\\\\)" },
+ { "/\\%(\\)", "/\\\\%(\\\\)" },
+ { "?", "?" },
+ { "??", "??" },
+ { ":?", ":?" },
+ { "?<CR>", "?<CR>" },
+ { "g?", "g?" },
+ { "g?g?", "g?g?" },
+ { "g??", "g??" },
+ { "-?", "-?" },
+ { "q?", "q?" },
+ { "v_g?", "v_g?" },
+ { "/\\?", "/\\\\?" },
+ { "/\\z(\\)", "/\\\\z(\\\\)" },
+ { "\\=", "\\\\=" },
+ { ":s\\=", ":s\\\\=" },
+ { "[count]", "\\[count]" },
+ { "[quotex]", "\\[quotex]" },
+ { "[range]", "\\[range]" },
+ { ":[range]", ":\\[range]" },
+ { "[pattern]", "\\[pattern]" },
+ { "\\|", "\\\\bar" },
+ { "\\%$", "/\\\\%\\$" },
+ { "s/\\~", "s/\\\\\\~" },
+ { "s/\\U", "s/\\\\U" },
+ { "s/\\L", "s/\\\\L" },
+ { "s/\\1", "s/\\\\1" },
+ { "s/\\2", "s/\\\\2" },
+ { "s/\\3", "s/\\\\3" },
+ { "s/\\9", "s/\\\\9" },
+ { NULL, NULL }
+ };
+
+ static const char *(expr_table[]) = {
+ "!=?", "!~?", "<=?", "<?", "==?", "=~?",
+ ">=?", ">?", "is?", "isnot?"
+ };
+ char *d = (char *)IObuff; // assume IObuff is long enough!
+ d[0] = NUL;
+
+ if (STRNICMP(arg, "expr-", 5) == 0) {
+ // When the string starting with "expr-" and containing '?' and matches
+ // the table, it is taken literally (but ~ is escaped). Otherwise '?'
+ // is recognized as a wildcard.
+ for (i = (int)ARRAY_SIZE(expr_table); --i >= 0;) {
+ if (STRCMP(arg + 5, expr_table[i]) == 0) {
+ for (int si = 0, di = 0;; si++) {
+ if (arg[si] == '~') {
+ d[di++] = '\\';
+ }
+ d[di++] = arg[si];
+ if (arg[si] == NUL) {
+ break;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ // Recognize a few exceptions to the rule. Some strings that contain
+ // '*'are changed to "star", otherwise '*' is recognized as a wildcard.
+ for (i = 0; except_tbl[i][0] != NULL; i++) {
+ if (STRCMP(arg, except_tbl[i][0]) == 0) {
+ STRCPY(d, except_tbl[i][1]);
+ break;
+ }
+ }
+ }
+
+ if (d[0] == NUL) { // no match in table
+ // Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
+ // Also replace "\%^" and "\%(", they match every tag too.
+ // Also "\zs", "\z1", etc.
+ // Also "\@<", "\@=", "\@<=", etc.
+ // And also "\_$" and "\_^".
+ if (arg[0] == '\\'
+ && ((arg[1] != NUL && arg[2] == NUL)
+ || (vim_strchr("%_z@", arg[1]) != NULL
+ && arg[2] != NUL))) {
+ vim_snprintf(d, IOSIZE, "/\\\\%s", arg + 1);
+ // Check for "/\\_$", should be "/\\_\$"
+ if (d[3] == '_' && d[4] == '$') {
+ STRCPY(d + 4, "\\$");
+ }
+ } else {
+ // Replace:
+ // "[:...:]" with "\[:...:]"
+ // "[++...]" with "\[++...]"
+ // "\{" with "\\{" -- matching "} \}"
+ if ((arg[0] == '[' && (arg[1] == ':'
+ || (arg[1] == '+' && arg[2] == '+')))
+ || (arg[0] == '\\' && arg[1] == '{')) {
+ *d++ = '\\';
+ }
+
+ // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
+ if (*arg == '(' && arg[1] == '\'') {
+ arg++;
+ }
+ for (const char *s = arg; *s; s++) {
+ // Replace "|" with "bar" and '"' with "quote" to match the name of
+ // the tags for these commands.
+ // Replace "*" with ".*" and "?" with "." to match command line
+ // completion.
+ // Insert a backslash before '~', '$' and '.' to avoid their
+ // special meaning.
+ if ((char_u *)d - IObuff > IOSIZE - 10) { // getting too long!?
+ break;
+ }
+ switch (*s) {
+ case '|':
+ STRCPY(d, "bar");
+ d += 3;
+ continue;
+ case '"':
+ STRCPY(d, "quote");
+ d += 5;
+ continue;
+ case '*':
+ *d++ = '.';
+ break;
+ case '?':
+ *d++ = '.';
+ continue;
+ case '$':
+ case '.':
+ case '~':
+ *d++ = '\\';
+ break;
+ }
+
+ // Replace "^x" by "CTRL-X". Don't do this for "^_" to make
+ // ":help i_^_CTRL-D" work.
+ // Insert '-' before and after "CTRL-X" when applicable.
+ if (*s < ' '
+ || (*s == '^' && s[1]
+ && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
+ if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
+ *d++ = '_'; // prepend a '_' to make x_CTRL-x
+ }
+ STRCPY(d, "CTRL-");
+ d += 5;
+ if (*s < ' ') {
+ *d++ = (char)(*s + '@');
+ if (d[-1] == '\\') {
+ *d++ = '\\'; // double a backslash
+ }
+ } else {
+ *d++ = *++s;
+ }
+ if (s[1] != NUL && s[1] != '_') {
+ *d++ = '_'; // append a '_'
+ }
+ continue;
+ } else if (*s == '^') { // "^" or "CTRL-^" or "^_"
+ *d++ = '\\';
+ } else if (s[0] == '\\' && s[1] != '\\' && *arg == '/' && s == arg + 1) {
+ // Insert a backslash before a backslash after a slash, for search
+ // pattern tags: "/\|" --> "/\\|".
+ *d++ = '\\';
+ }
+
+ // "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
+ // "CTRL-\_CTRL-N"
+ if (STRNICMP(s, "CTRL-\\_", 7) == 0) {
+ STRCPY(d, "CTRL-\\\\");
+ d += 7;
+ s += 6;
+ }
+
+ *d++ = *s;
+
+ // If tag contains "({" or "([", tag terminates at the "(".
+ // This is for help on functions, e.g.: abs({expr}).
+ if (*s == '(' && (s[1] == '{' || s[1] == '[')) {
+ break;
+ }
+
+ // If tag starts with ', toss everything after a second '. Fixes
+ // CTRL-] on 'option'. (would include the trailing '.').
+ if (*s == '\'' && s > arg && *arg == '\'') {
+ break;
+ }
+ // Also '{' and '}'. Fixes CTRL-] on '{address}'.
+ if (*s == '}' && s > arg && *arg == '{') {
+ break;
+ }
+ }
+ *d = NUL;
+
+ if (*IObuff == '`') {
+ if ((char_u *)d > IObuff + 2 && d[-1] == '`') {
+ // remove the backticks from `command`
+ memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ d[-2] = NUL;
+ } else if ((char_u *)d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
+ // remove the backticks and comma from `command`,
+ memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ d[-3] = NUL;
+ } else if ((char_u *)d > IObuff + 4 && d[-3] == '`'
+ && d[-2] == '\\' && d[-1] == '.') {
+ // remove the backticks and dot from `command`\.
+ memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ d[-4] = NUL;
+ }
+ }
+ }
+ }
+
+ *matches = NULL;
+ *num_matches = 0;
+ int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC;
+ if (keep_lang) {
+ flags |= TAG_KEEP_LANG;
+ }
+ if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
+ && *num_matches > 0) {
+ // Sort the matches found on the heuristic number that is after the
+ // tag name.
+ qsort((void *)(*matches), (size_t)(*num_matches),
+ sizeof(char_u *), help_compare);
+ // Delete more than TAG_MANY to reduce the size of the listing.
+ while (*num_matches > TAG_MANY) {
+ xfree((*matches)[--*num_matches]);
+ }
+ }
+ return OK;
+}
+
+/// Cleanup matches for help tags:
+/// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
+/// tag matches it. Otherwise remove "@en" if "en" is the only language.
+void cleanup_help_tags(int num_file, char **file)
+{
+ char_u buf[4];
+ char_u *p = buf;
+
+ if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
+ *p++ = '@';
+ *p++ = p_hlg[0];
+ *p++ = p_hlg[1];
+ }
+ *p = NUL;
+
+ for (int i = 0; i < num_file; i++) {
+ int len = (int)STRLEN(file[i]) - 3;
+ if (len <= 0) {
+ continue;
+ }
+ if (STRCMP(file[i] + len, "@en") == 0) {
+ // Sorting on priority means the same item in another language may
+ // be anywhere. Search all items for a match up to the "@en".
+ int j;
+ for (j = 0; j < num_file; j++) {
+ if (j != i
+ && (int)STRLEN(file[j]) == len + 3
+ && STRNCMP(file[i], file[j], len + 1) == 0) {
+ break;
+ }
+ }
+ if (j == num_file) {
+ // item only exists with @en, remove it
+ file[i][len] = NUL;
+ }
+ }
+ }
+
+ if (*buf != NUL) {
+ for (int i = 0; i < num_file; i++) {
+ int len = (int)STRLEN(file[i]) - 3;
+ if (len <= 0) {
+ continue;
+ }
+ if (STRCMP(file[i] + len, buf) == 0) {
+ // remove the default language
+ file[i][len] = NUL;
+ }
+ }
+ }
+}
+
+/// Called when starting to edit a buffer for a help file.
+void prepare_help_buffer(void)
+{
+ curbuf->b_help = true;
+ set_string_option_direct("buftype", -1, "help", OPT_FREE|OPT_LOCAL, 0);
+
+ // Always set these options after jumping to a help tag, because the
+ // user may have an autocommand that gets in the way.
+ // Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
+ // latin1 word characters (for translated help files).
+ // Only set it when needed, buf_init_chartab() is some work.
+ char *p = "!-~,^*,^|,^\",192-255";
+ if (STRCMP(curbuf->b_p_isk, p) != 0) {
+ set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
+ check_buf_options(curbuf);
+ (void)buf_init_chartab(curbuf, false);
+ }
+
+ // Don't use the global foldmethod.
+ set_string_option_direct("fdm", -1, "manual", OPT_FREE|OPT_LOCAL, 0);
+
+ curbuf->b_p_ts = 8; // 'tabstop' is 8.
+ curwin->w_p_list = false; // No list mode.
+
+ curbuf->b_p_ma = false; // Not modifiable.
+ curbuf->b_p_bin = false; // Reset 'bin' before reading file.
+ curwin->w_p_nu = 0; // No line numbers.
+ curwin->w_p_rnu = 0; // No relative line numbers.
+ RESET_BINDING(curwin); // No scroll or cursor binding.
+ curwin->w_p_arab = false; // No arabic mode.
+ curwin->w_p_rl = false; // Help window is left-to-right.
+ curwin->w_p_fen = false; // No folding in the help window.
+ curwin->w_p_diff = false; // No 'diff'.
+ curwin->w_p_spell = false; // No spell checking.
+
+ set_buflisted(false);
+}
+
+/// After reading a help file: May cleanup a help buffer when syntax
+/// highlighting is not used.
+void fix_help_buffer(void)
+{
+ linenr_T lnum;
+ char *line;
+ bool in_example = false;
+
+ // Set filetype to "help".
+ if (STRCMP(curbuf->b_p_ft, "help") != 0) {
+ curbuf->b_ro_locked++;
+ set_option_value("ft", 0L, "help", OPT_LOCAL);
+ curbuf->b_ro_locked--;
+ }
+
+ if (!syntax_present(curwin)) {
+ for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
+ line = (char *)ml_get_buf(curbuf, lnum, false);
+ const size_t len = STRLEN(line);
+ if (in_example && len > 0 && !ascii_iswhite(line[0])) {
+ // End of example: non-white or '<' in first column.
+ if (line[0] == '<') {
+ // blank-out a '<' in the first column
+ line = (char *)ml_get_buf(curbuf, lnum, true);
+ line[0] = ' ';
+ }
+ in_example = false;
+ }
+ if (!in_example && len > 0) {
+ if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) {
+ // blank-out a '>' in the last column (start of example)
+ line = (char *)ml_get_buf(curbuf, lnum, true);
+ line[len - 1] = ' ';
+ in_example = true;
+ } else if (line[len - 1] == '~') {
+ // blank-out a '~' at the end of line (header marker)
+ line = (char *)ml_get_buf(curbuf, lnum, true);
+ line[len - 1] = ' ';
+ }
+ }
+ }
+ }
+
+ // In the "help.txt" and "help.abx" file, add the locally added help
+ // files. This uses the very first line in the help file.
+ char *const fname = path_tail(curbuf->b_fname);
+ if (FNAMECMP(fname, "help.txt") == 0
+ || (FNAMENCMP(fname, "help.", 5) == 0
+ && ASCII_ISALPHA(fname[5])
+ && ASCII_ISALPHA(fname[6])
+ && TOLOWER_ASC(fname[7]) == 'x'
+ && fname[8] == NUL)) {
+ for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) {
+ line = (char *)ml_get_buf(curbuf, lnum, false);
+ if (strstr(line, "*local-additions*") == NULL) {
+ continue;
+ }
+
+ // Go through all directories in 'runtimepath', skipping
+ // $VIMRUNTIME.
+ char *p = (char *)p_rtp;
+ while (*p != NUL) {
+ copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
+ char *const rt = vim_getenv("VIMRUNTIME");
+ if (rt != NULL
+ && path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
+ int fcount;
+ char **fnames;
+ char *s;
+ vimconv_T vc;
+ char *cp;
+
+ // Find all "doc/ *.txt" files in this directory.
+ if (!add_pathsep((char *)NameBuff)
+ || STRLCAT(NameBuff, "doc/*.??[tx]", // NOLINT
+ sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ continue;
+ }
+
+ // Note: We cannot just do `&NameBuff` because it is a statically sized array
+ // so `NameBuff == &NameBuff` according to C semantics.
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, buff_list, &fcount,
+ &fnames, EW_FILE|EW_SILENT) == OK
+ && fcount > 0) {
+ // If foo.abx is found use it instead of foo.txt in
+ // the same directory.
+ for (int i1 = 0; i1 < fcount; i1++) {
+ for (int i2 = 0; i2 < fcount; i2++) {
+ if (i1 == i2) {
+ continue;
+ }
+ if (fnames[i1] == NULL || fnames[i2] == NULL) {
+ continue;
+ }
+ const char *const f1 = fnames[i1];
+ const char *const f2 = fnames[i2];
+ const char *const t1 = path_tail(f1);
+ const char *const t2 = path_tail(f2);
+ const char *const e1 = (char *)STRRCHR(t1, '.');
+ const char *const e2 = (char *)STRRCHR(t2, '.');
+ if (e1 == NULL || e2 == NULL) {
+ continue;
+ }
+ if (FNAMECMP(e1, ".txt") != 0
+ && FNAMECMP(e1, fname + 4) != 0) {
+ // Not .txt and not .abx, remove it.
+ XFREE_CLEAR(fnames[i1]);
+ continue;
+ }
+ if (e1 - f1 != e2 - f2
+ || FNAMENCMP(f1, f2, e1 - f1) != 0) {
+ continue;
+ }
+ if (FNAMECMP(e1, ".txt") == 0
+ && FNAMECMP(e2, fname + 4) == 0) {
+ // use .abx instead of .txt
+ XFREE_CLEAR(fnames[i1]);
+ }
+ }
+ }
+ for (int fi = 0; fi < fcount; fi++) {
+ if (fnames[fi] == NULL) {
+ continue;
+ }
+
+ FILE *const fd = os_fopen(fnames[fi], "r");
+ if (fd == NULL) {
+ continue;
+ }
+ vim_fgets(IObuff, IOSIZE, fd);
+ if (IObuff[0] == '*'
+ && (s = vim_strchr((char *)IObuff + 1, '*'))
+ != NULL) {
+ TriState this_utf = kNone;
+ // Change tag definition to a
+ // reference and remove <CR>/<NL>.
+ IObuff[0] = '|';
+ *s = '|';
+ while (*s != NUL) {
+ if (*s == '\r' || *s == '\n') {
+ *s = NUL;
+ }
+ // The text is utf-8 when a byte
+ // above 127 is found and no
+ // illegal byte sequence is found.
+ if ((char_u)(*s) >= 0x80 && this_utf != kFalse) {
+ this_utf = kTrue;
+ const int l = utf_ptr2len(s);
+ if (l == 1) {
+ this_utf = kFalse;
+ }
+ s += l - 1;
+ }
+ s++;
+ }
+ // The help file is latin1 or utf-8;
+ // conversion to the current
+ // 'encoding' may be required.
+ vc.vc_type = CONV_NONE;
+ convert_setup(&vc,
+ (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"),
+ p_enc);
+ if (vc.vc_type == CONV_NONE) {
+ // No conversion needed.
+ cp = (char *)IObuff;
+ } else {
+ // Do the conversion. If it fails
+ // use the unconverted text.
+ cp = (char *)string_convert(&vc, IObuff, NULL);
+ if (cp == NULL) {
+ cp = (char *)IObuff;
+ }
+ }
+ convert_setup(&vc, NULL, NULL);
+
+ ml_append(lnum, cp, (colnr_T)0, false);
+ if ((char_u *)cp != IObuff) {
+ xfree(cp);
+ }
+ lnum++;
+ }
+ fclose(fd);
+ }
+ FreeWild(fcount, fnames);
+ }
+ }
+ xfree(rt);
+ }
+ break;
+ }
+ }
+}
+
+/// ":exusage"
+void ex_exusage(exarg_T *eap)
+{
+ do_cmdline_cmd("help ex-cmd-index");
+}
+
+/// ":viusage"
+void ex_viusage(exarg_T *eap)
+{
+ do_cmdline_cmd("help normal-index");
+}
+
+/// Generate tags in one help directory
+///
+/// @param dir Path to the doc directory
+/// @param ext Suffix of the help files (".txt", ".itx", ".frx", etc.)
+/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for
+/// French)
+/// @param add_help_tags Whether to add the "help-tags" tag
+/// @param ignore_writeerr ignore write error
+static void helptags_one(char *dir, const char *ext, const char *tagfname, bool add_help_tags,
+ bool ignore_writeerr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T ga;
+ int filecount;
+ char **files;
+ char *p1, *p2;
+ char *s;
+ TriState utf8 = kNone;
+ bool mix = false; // detected mixed encodings
+
+ // Find all *.txt files.
+ size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
+ if (dirlen >= MAXPATHL
+ || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
+ || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ return;
+ }
+
+ // Note: We cannot just do `&NameBuff` because it is a statically sized array
+ // so `NameBuff == &NameBuff` according to C semantics.
+ char *buff_list[1] = { (char *)NameBuff };
+ const int res = gen_expand_wildcards(1, buff_list, &filecount, &files,
+ EW_FILE|EW_SILENT);
+ if (res == FAIL || filecount == 0) {
+ if (!got_int) {
+ semsg(_("E151: No match: %s"), NameBuff);
+ }
+ if (res != FAIL) {
+ FreeWild(filecount, files);
+ }
+ return;
+ }
+
+ // Open the tags file for writing.
+ // Do this before scanning through all the files.
+ memcpy(NameBuff, dir, dirlen + 1);
+ if (!add_pathsep((char *)NameBuff)
+ || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ return;
+ }
+
+ FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
+ if (fd_tags == NULL) {
+ if (!ignore_writeerr) {
+ semsg(_("E152: Cannot open %s for writing"), NameBuff);
+ }
+ FreeWild(filecount, files);
+ return;
+ }
+
+ // If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
+ // add the "help-tags" tag.
+ ga_init(&ga, (int)sizeof(char_u *), 100);
+ if (add_help_tags
+ || path_full_compare("$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) {
+ size_t s_len = 18 + STRLEN(tagfname);
+ s = xmalloc(s_len);
+ snprintf(s, s_len, "help-tags\t%s\t1\n", tagfname);
+ GA_APPEND(char *, &ga, s);
+ }
+
+ // Go over all the files and extract the tags.
+ for (int fi = 0; fi < filecount && !got_int; fi++) {
+ FILE *const fd = os_fopen(files[fi], "r");
+ if (fd == NULL) {
+ semsg(_("E153: Unable to open %s for reading"), files[fi]);
+ continue;
+ }
+ const char *const fname = files[fi] + dirlen + 1;
+
+ bool firstline = true;
+ while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
+ if (firstline) {
+ // Detect utf-8 file by a non-ASCII char in the first line.
+ TriState this_utf8 = kNone;
+ for (s = (char *)IObuff; *s != NUL; s++) {
+ if ((char_u)(*s) >= 0x80) {
+ this_utf8 = kTrue;
+ const int l = utf_ptr2len(s);
+ if (l == 1) {
+ // Illegal UTF-8 byte sequence.
+ this_utf8 = kFalse;
+ break;
+ }
+ s += l - 1;
+ }
+ }
+ if (this_utf8 == kNone) { // only ASCII characters found
+ this_utf8 = kFalse;
+ }
+ if (utf8 == kNone) { // first file
+ utf8 = this_utf8;
+ } else if (utf8 != this_utf8) {
+ semsg(_("E670: Mix of help file encodings within a language: %s"),
+ files[fi]);
+ mix = !got_int;
+ got_int = true;
+ }
+ firstline = false;
+ }
+ p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
+ while (p1 != NULL) {
+ p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
+ if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
+ for (s = p1 + 1; s < p2; s++) {
+ if (*s == ' ' || *s == '\t' || *s == '|') {
+ break;
+ }
+ }
+
+ // Only accept a *tag* when it consists of valid
+ // characters, there is white space before it and is
+ // followed by a white character or end-of-line.
+ if (s == p2
+ && ((char_u *)p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
+ && (vim_strchr(" \t\n\r", s[1]) != NULL
+ || s[1] == '\0')) {
+ *p2 = '\0';
+ p1++;
+ size_t s_len= (size_t)(p2 - p1) + STRLEN(fname) + 2;
+ s = xmalloc(s_len);
+ GA_APPEND(char *, &ga, s);
+ snprintf(s, s_len, "%s\t%s", p1, fname);
+
+ // find next '*'
+ p2 = vim_strchr(p2 + 1, '*');
+ }
+ }
+ p1 = p2;
+ }
+ line_breakcheck();
+ }
+
+ fclose(fd);
+ }
+
+ FreeWild(filecount, files);
+
+ if (!got_int && ga.ga_data != NULL) {
+ // Sort the tags.
+ sort_strings(ga.ga_data, ga.ga_len);
+
+ // Check for duplicates.
+ for (int i = 1; i < ga.ga_len; i++) {
+ p1 = ((char **)ga.ga_data)[i - 1];
+ p2 = ((char **)ga.ga_data)[i];
+ while (*p1 == *p2) {
+ if (*p2 == '\t') {
+ *p2 = NUL;
+ vim_snprintf((char *)NameBuff, MAXPATHL,
+ _("E154: Duplicate tag \"%s\" in file %s/%s"),
+ ((char_u **)ga.ga_data)[i], dir, p2 + 1);
+ emsg((char *)NameBuff);
+ *p2 = '\t';
+ break;
+ }
+ p1++;
+ p2++;
+ }
+ }
+
+ if (utf8 == kTrue) {
+ fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
+ }
+
+ // Write the tags into the file.
+ for (int i = 0; i < ga.ga_len; i++) {
+ s = ((char **)ga.ga_data)[i];
+ if (STRNCMP(s, "help-tags\t", 10) == 0) {
+ // help-tags entry was added in formatted form
+ fputs(s, fd_tags);
+ } else {
+ fprintf(fd_tags, "%s\t/" "*", s);
+ for (p1 = s; *p1 != '\t'; p1++) {
+ // insert backslash before '\\' and '/'
+ if (*p1 == '\\' || *p1 == '/') {
+ putc('\\', fd_tags);
+ }
+ putc(*p1, fd_tags);
+ }
+ fprintf(fd_tags, "*\n");
+ }
+ }
+ }
+ if (mix) {
+ got_int = false; // continue with other languages
+ }
+
+ GA_DEEP_CLEAR_PTR(&ga);
+ fclose(fd_tags); // there is no check for an error...
+}
+
+/// Generate tags in one help directory, taking care of translations.
+static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int len;
+ garray_T ga;
+ char lang[2];
+ char ext[5];
+ char fname[8];
+ int filecount;
+ char **files;
+
+ // Get a list of all files in the help directory and in subdirectories.
+ STRLCPY(NameBuff, dirname, sizeof(NameBuff));
+ if (!add_pathsep((char *)NameBuff)
+ || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ return;
+ }
+
+ // Note: We cannot just do `&NameBuff` because it is a statically sized array
+ // so `NameBuff == &NameBuff` according to C semantics.
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, buff_list, &filecount, &files,
+ EW_FILE|EW_SILENT) == FAIL
+ || filecount == 0) {
+ semsg(_("E151: No match: %s"), NameBuff);
+ return;
+ }
+
+ // Go over all files in the directory to find out what languages are
+ // present.
+ int j;
+ ga_init(&ga, 1, 10);
+ for (int i = 0; i < filecount; i++) {
+ len = (int)STRLEN(files[i]);
+ if (len <= 4) {
+ continue;
+ }
+
+ if (STRICMP(files[i] + len - 4, ".txt") == 0) {
+ // ".txt" -> language "en"
+ lang[0] = 'e';
+ lang[1] = 'n';
+ } else if (files[i][len - 4] == '.'
+ && ASCII_ISALPHA(files[i][len - 3])
+ && ASCII_ISALPHA(files[i][len - 2])
+ && TOLOWER_ASC(files[i][len - 1]) == 'x') {
+ // ".abx" -> language "ab"
+ lang[0] = (char)TOLOWER_ASC(files[i][len - 3]);
+ lang[1] = (char)TOLOWER_ASC(files[i][len - 2]);
+ } else {
+ continue;
+ }
+
+ // Did we find this language already?
+ for (j = 0; j < ga.ga_len; j += 2) {
+ if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) {
+ break;
+ }
+ }
+ if (j == ga.ga_len) {
+ // New language, add it.
+ ga_grow(&ga, 2);
+ ((char *)ga.ga_data)[ga.ga_len++] = lang[0];
+ ((char *)ga.ga_data)[ga.ga_len++] = lang[1];
+ }
+ }
+
+ // Loop over the found languages to generate a tags file for each one.
+ for (j = 0; j < ga.ga_len; j += 2) {
+ STRCPY(fname, "tags-xx");
+ fname[5] = ((char *)ga.ga_data)[j];
+ fname[6] = ((char *)ga.ga_data)[j + 1];
+ if (fname[5] == 'e' && fname[6] == 'n') {
+ // English is an exception: use ".txt" and "tags".
+ fname[4] = NUL;
+ STRCPY(ext, ".txt");
+ } else {
+ // Language "ab" uses ".abx" and "tags-ab".
+ STRCPY(ext, ".xxx");
+ ext[1] = fname[5];
+ ext[2] = fname[6];
+ }
+ helptags_one(dirname, (char *)ext, (char *)fname, add_help_tags, ignore_writeerr);
+ }
+
+ ga_clear(&ga);
+ FreeWild(filecount, files);
+}
+
+static void helptags_cb(char *fname, void *cookie)
+ FUNC_ATTR_NONNULL_ALL
+{
+ do_helptags(fname, *(bool *)cookie, true);
+}
+
+/// ":helptags"
+void ex_helptags(exarg_T *eap)
+{
+ expand_T xpc;
+ char *dirname;
+ bool add_help_tags = false;
+
+ // Check for ":helptags ++t {dir}".
+ if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
+ add_help_tags = true;
+ eap->arg = skipwhite(eap->arg + 3);
+ }
+
+ if (STRCMP(eap->arg, "ALL") == 0) {
+ do_in_path(p_rtp, "doc", DIP_ALL + DIP_DIR, helptags_cb, &add_help_tags);
+ } else {
+ ExpandInit(&xpc);
+ xpc.xp_context = EXPAND_DIRECTORIES;
+ dirname = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL,
+ WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
+ if (dirname == NULL || !os_isdir((char_u *)dirname)) {
+ semsg(_("E150: Not a directory: %s"), eap->arg);
+ } else {
+ do_helptags(dirname, add_help_tags, false);
+ }
+ xfree(dirname);
+ }
+}
diff --git a/src/nvim/help.h b/src/nvim/help.h
new file mode 100644
index 0000000000..21e11392ee
--- /dev/null
+++ b/src/nvim/help.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_HELP_H
+#define NVIM_HELP_H
+
+#include <stdbool.h>
+
+#include "nvim/ex_cmds_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "help.h.generated.h"
+#endif
+#endif // NVIM_HELP_H
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index dfd077840e..c26b00df79 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -6,6 +6,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -13,8 +14,7 @@
#include "nvim/map.h"
#include "nvim/message.h"
#include "nvim/option.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -32,7 +32,9 @@ static Map(int, int) blend_attr_entries = MAP_INIT;
static Map(int, int) blendthrough_attr_entries = MAP_INIT;
/// highlight entries private to a namespace
-static Map(ColorKey, ColorItem) ns_hl;
+static Map(ColorKey, ColorItem) ns_hls;
+typedef int NSHlAttr[HLF_COUNT + 1];
+static PMap(handle_T) ns_hl_attr;
void highlight_init(void)
{
@@ -147,42 +149,46 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
{
- if ((attrs.rgb_ae_attr & HL_DEFAULT)
- && map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) {
- return;
- }
if (ns_id == 0) {
assert(dict);
// set in global (':highlight') namespace
set_hl_group(hl_id, attrs, dict, link_id);
return;
}
+ if ((attrs.rgb_ae_attr & HL_DEFAULT)
+ && map_has(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id))) {
+ return;
+ }
DecorProvider *p = get_decor_provider(ns_id, true);
int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
ColorItem it = { .attr_id = attr_id,
.link_id = link_id,
.version = p->hl_valid,
- .is_default = (attrs.rgb_ae_attr & HL_DEFAULT) };
- map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
+ .is_default = (attrs.rgb_ae_attr & HL_DEFAULT),
+ .link_global = (attrs.rgb_ae_attr & HL_GLOBAL) };
+ map_put(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id), it);
+ p->hl_cached = false;
}
-int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
+int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
{
static int recursive = 0;
- if (ns_id < 0) {
+ if (*ns_hl < 0) {
if (ns_hl_active <= 0) {
return -1;
}
- ns_id = ns_hl_active;
+ *ns_hl = ns_hl_active;
}
+ int ns_id = *ns_hl;
+
DecorProvider *p = get_decor_provider(ns_id, true);
- ColorItem it = map_get(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id));
+ ColorItem it = map_get(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id));
// TODO(bfredl): map_ref true even this?
- bool valid_cache = it.version >= p->hl_valid;
+ bool valid_item = it.version >= p->hl_valid;
- if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) {
+ if (!valid_item && p->hl_def != LUA_NOREF && !recursive) {
MAXSIZE_TEMP_ARRAY(args, 3);
ADD_C(args, INTEGER_OBJ((Integer)ns_id));
ADD_C(args, STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))));
@@ -215,44 +221,76 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
it.version = p->hl_valid - tmp;
it.is_default = attrs.rgb_ae_attr & HL_DEFAULT;
- map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
+ it.link_global = attrs.rgb_ae_attr & HL_GLOBAL;
+ map_put(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id), it);
+ valid_item = true;
}
- if (it.is_default && nodefault) {
+ if ((it.is_default && nodefault) || !valid_item) {
return -1;
}
if (link) {
- return it.attr_id >= 0 ? 0 : it.link_id;
+ if (it.attr_id >= 0) {
+ return 0;
+ } else {
+ if (it.link_global) {
+ *ns_hl = 0;
+ }
+ return it.link_id;
+ }
} else {
return it.attr_id;
}
}
-bool win_check_ns_hl(win_T *wp)
+bool hl_check_ns(void)
{
- if (ns_hl_changed) {
- highlight_changed();
- if (wp) {
- update_window_hl(wp, true);
+ int ns = 0;
+ if (ns_hl_fast > 0) {
+ ns = ns_hl_fast;
+ } else if (ns_hl_win >= 0) {
+ ns = ns_hl_win;
+ } else {
+ ns = ns_hl_global;
+ }
+ if (ns_hl_active == ns) {
+ return false;
+ }
+
+ ns_hl_active = ns;
+ hl_attr_active = highlight_attr;
+ if (ns > 0) {
+ update_ns_hl(ns);
+ NSHlAttr *hl_def = (NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns);
+ if (hl_def) {
+ hl_attr_active = *hl_def;
}
- ns_hl_changed = false;
- return true;
}
- return false;
+ need_highlight_changed = true;
+ return true;
+}
+
+/// prepare for drawing window `wp` or global elements if NULL
+///
+/// Note: pum should be drawn in the context of the current window!
+bool win_check_ns_hl(win_T *wp)
+{
+ ns_hl_win = wp ? wp->w_ns_hl : -1;
+ return hl_check_ns();
}
/// Get attribute code for a builtin highlight group.
///
/// The final syntax group could be modified by hi-link or 'winhighlight'.
-int hl_get_ui_attr(int idx, int final_id, bool optional)
+int hl_get_ui_attr(int ns_id, int idx, int final_id, bool optional)
{
HlAttrs attrs = HLATTRS_INIT;
bool available = false;
if (final_id > 0) {
- int syn_attr = syn_id2attr(final_id);
- if (syn_attr != 0) {
+ int syn_attr = syn_ns_id2attr(ns_id, final_id, optional);
+ if (syn_attr > 0) {
attrs = syn_attr2entry(syn_attr);
available = true;
}
@@ -265,8 +303,6 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
if (pum_drawn()) {
must_redraw_pum = true;
}
- } else if (idx == HLF_MSG) {
- msg_grid.blending = attrs.hl_blend > -1;
}
if (optional && !available) {
@@ -278,6 +314,21 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
void update_window_hl(win_T *wp, bool invalid)
{
+ int ns_id = wp->w_ns_hl;
+
+ update_ns_hl(ns_id);
+ if (ns_id != wp->w_ns_hl_active || wp->w_ns_hl_attr == NULL) {
+ wp->w_ns_hl_active = ns_id;
+
+ wp->w_ns_hl_attr = *(NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns_id);
+ if (!wp->w_ns_hl_attr) {
+ // No specific highlights, use the defaults.
+ wp->w_ns_hl_attr = highlight_attr;
+ }
+ }
+
+ int *hl_def = wp->w_ns_hl_attr;
+
if (!wp->w_hl_needs_update && !invalid) {
return;
}
@@ -285,34 +336,17 @@ void update_window_hl(win_T *wp, bool invalid)
// If a floating window is blending it always have a named
// wp->w_hl_attr_normal group. HL_ATTR(HLF_NFLOAT) is always named.
- bool has_blend = wp->w_floating && wp->w_p_winbl != 0;
// determine window specific background set in 'winhighlight'
bool float_win = wp->w_floating && !wp->w_float_config.external;
- if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] != 0) {
- wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
- wp->w_hl_ids[HLF_INACTIVE],
- !has_blend);
- } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] != 0) {
- wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
- wp->w_hl_ids[HLF_NFLOAT], !has_blend);
- } else if (wp->w_hl_id_normal != 0) {
- wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, !has_blend);
+ if (float_win && hl_def[HLF_NFLOAT] != 0) {
+ wp->w_hl_attr_normal = hl_def[HLF_NFLOAT];
+ } else if (hl_def[HLF_COUNT] > 0) {
+ wp->w_hl_attr_normal = hl_def[HLF_COUNT];
} else {
wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
- // NOOOO! You cannot just pretend that "Normal" is just like any other
- // syntax group! It needs at least 10 layers of special casing! Noooooo!
- //
- // haha, theme engine go brrr
- int normality = syn_check_group(S_LEN("Normal"));
- int ns_attr = ns_get_hl(-1, normality, false, false);
- if (ns_attr > 0) {
- // TODO(bfredl): hantera NormalNC and so on
- wp->w_hl_attr_normal = ns_attr;
- }
-
// if blend= attribute is not set, 'winblend' value overrides it.
if (wp->w_floating && wp->w_p_winbl > 0) {
HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normal);
@@ -322,28 +356,13 @@ void update_window_hl(win_T *wp, bool invalid)
}
}
- if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] == 0) {
- wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
- wp->w_hl_attr_normal);
- }
-
- for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
- int attr;
- if (wp->w_hl_ids[hlf] != 0) {
- attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
- } else {
- attr = HL_ATTR(hlf);
- }
- wp->w_hl_attrs[hlf] = attr;
- }
-
wp->w_float_config.shadow = false;
if (wp->w_floating && wp->w_float_config.border) {
for (int i = 0; i < 8; i++) {
- int attr = wp->w_hl_attrs[HLF_BORDER];
+ int attr = hl_def[HLF_BORDER];
if (wp->w_float_config.border_hl_ids[i]) {
- attr = hl_get_ui_attr(HLF_BORDER, wp->w_float_config.border_hl_ids[i],
- false);
+ attr = hl_get_ui_attr(ns_id, HLF_BORDER,
+ wp->w_float_config.border_hl_ids[i], false);
HlAttrs a = syn_attr2entry(attr);
if (a.hl_blend) {
wp->w_float_config.shadow = true;
@@ -355,6 +374,65 @@ void update_window_hl(win_T *wp, bool invalid)
// shadow might cause blending
check_blending(wp);
+
+ // TODO(bfredl): this a bit ad-hoc. move it from highlight ns logic to 'winhl'
+ // implementation?
+ if (hl_def[HLF_INACTIVE] == 0) {
+ wp->w_hl_attr_normalnc = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
+ wp->w_hl_attr_normal);
+ } else {
+ wp->w_hl_attr_normalnc = hl_def[HLF_INACTIVE];
+ }
+}
+
+void update_ns_hl(int ns_id)
+{
+ if (ns_id <= 0) {
+ return;
+ }
+ DecorProvider *p = get_decor_provider(ns_id, true);
+ if (p->hl_cached) {
+ return;
+ }
+
+ NSHlAttr **alloc = (NSHlAttr **)pmap_ref(handle_T)(&ns_hl_attr, ns_id, true);
+ if (*alloc == NULL) {
+ *alloc = xmalloc(sizeof(**alloc));
+ }
+ int *hl_attrs = **alloc;
+
+ for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
+ int id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf]));
+ bool optional = (hlf == HLF_INACTIVE || hlf == HLF_NFLOAT);
+ hl_attrs[hlf] = hl_get_ui_attr(ns_id, hlf, id, optional);
+ }
+
+ // NOOOO! You cannot just pretend that "Normal" is just like any other
+ // syntax group! It needs at least 10 layers of special casing! Noooooo!
+ //
+ // haha, tema engine go brrr
+ int normality = syn_check_group(S_LEN("Normal"));
+ hl_attrs[HLF_COUNT] = hl_get_ui_attr(ns_id, -1, normality, true);
+
+ // hl_get_ui_attr might have invalidated the decor provider
+ p = get_decor_provider(ns_id, true);
+ p->hl_cached = true;
+}
+
+int win_bg_attr(win_T *wp)
+{
+ if (ns_hl_fast < 0) {
+ int local = (wp == curwin) ? wp->w_hl_attr_normal : wp->w_hl_attr_normalnc;
+ if (local) {
+ return local;
+ }
+ }
+
+ if (wp == curwin || hl_attr_active[HLF_INACTIVE] == 0) {
+ return hl_attr_active[HLF_COUNT];
+ } else {
+ return hl_attr_active[HLF_INACTIVE];
+ }
}
/// Gets HL_UNDERLINE highlight.
@@ -403,7 +481,7 @@ void clear_hl_tables(bool reinit)
map_destroy(int, int)(&combine_attr_entries);
map_destroy(int, int)(&blend_attr_entries);
map_destroy(int, int)(&blendthrough_attr_entries);
- map_destroy(ColorKey, ColorItem)(&ns_hl);
+ map_destroy(ColorKey, ColorItem)(&ns_hls);
}
}
@@ -852,7 +930,6 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
- CHECK_FLAG(dict, mask, global, , HL_GLOBAL);
if (HAS_KEY(dict->fg)) {
fg = object_to_color(dict->fg, "fg", true, err);
@@ -895,14 +972,21 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
return hlattrs;
}
- if (HAS_KEY(dict->link)) {
+ if (HAS_KEY(dict->link) || HAS_KEY(dict->global_link)) {
if (link_id) {
- *link_id = object_to_hl_id(dict->link, "link", err);
+ if (HAS_KEY(dict->global_link)) {
+ *link_id = object_to_hl_id(dict->global_link, "link", err);
+ mask |= HL_GLOBAL;
+ } else {
+ *link_id = object_to_hl_id(dict->link, "link", err);
+ }
+
if (ERROR_SET(err)) {
return hlattrs;
}
} else {
- api_set_error(err, kErrorTypeValidation, "Invalid Key: 'link'");
+ api_set_error(err, kErrorTypeValidation, "Invalid Key: '%s'",
+ HAS_KEY(dict->global_link) ? "global_link" : "link");
}
}
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index ae63f83d65..50299bb91c 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/api/private/defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/ui.h"
@@ -11,6 +12,13 @@
# include "highlight.h.generated.h"
#endif
+static inline int win_hl_attr(win_T *wp, int hlf)
+{
+ // wp->w_ns_hl_attr might be null if we check highlights
+ // prior to entering redraw
+ return ((wp->w_ns_hl_attr && ns_hl_fast < 0) ? wp->w_ns_hl_attr : hl_attr_active)[hlf];
+}
+
#define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \
do { \
bool dark_ = (*p_bg == 'd'); \
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index f41f980054..ffcb0f3f22 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -180,7 +180,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_CU] = "Cursor",
});
-EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.
+EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context.
EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups
EXTERN int highlight_user[9]; // User[1-9] attributes
EXTERN int highlight_stlnc[9]; // On top of user
@@ -190,6 +190,13 @@ EXTERN RgbValue normal_fg INIT(= -1);
EXTERN RgbValue normal_bg INIT(= -1);
EXTERN RgbValue normal_sp INIT(= -1);
+EXTERN NS ns_hl_global INIT(= 0); // global highlight namespace
+EXTERN NS ns_hl_win INIT(= -1); // highlight namespace for the current window
+EXTERN NS ns_hl_fast INIT(= -1); // highlight namespace specified in a fast callback
+EXTERN NS ns_hl_active INIT(= 0); // currently active/cached namespace
+
+EXTERN int *hl_attr_active INIT(= highlight_attr);
+
typedef enum {
kHlUnknown,
kHlUI,
@@ -219,8 +226,15 @@ typedef struct {
int link_id;
int version;
bool is_default;
+ bool link_global;
} ColorItem;
-#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, \
- .version = -1, .is_default = false }
+#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1, \
+ .is_default = false, .link_global = false }
+
+/// highlight attributes with associated priorities
+typedef struct {
+ int attr_id;
+ int priority;
+} HlPriAttr;
#endif // NVIM_HIGHLIGHT_DEFS_H
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index d79283d3e3..77424de3b8 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -9,6 +9,7 @@
#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_docmd.h"
@@ -19,7 +20,6 @@
#include "nvim/match.h"
#include "nvim/option.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
/// \addtogroup SG_SET
/// @{
@@ -698,7 +698,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_deflink_sctx = current_sctx;
g->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
}
- return;
+ goto update;
}
g->sg_cleared = false;
@@ -753,6 +753,12 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
ui_mode_info_set();
}
}
+
+update:
+ if (!updating_screen) {
+ redraw_all_later(NOT_VALID);
+ }
+ need_highlight_changed = true;
}
/// Handle ":highlight" command
@@ -1790,11 +1796,18 @@ static int syn_add_group(const char *name, size_t len)
/// @see syn_attr2entry
int syn_id2attr(int hl_id)
{
- hl_id = syn_get_final_id(hl_id);
+ return syn_ns_id2attr(-1, hl_id, false);
+}
+
+int syn_ns_id2attr(int ns_id, int hl_id, bool optional)
+{
+ hl_id = syn_ns_get_final_id(&ns_id, hl_id);
HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one
- int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set);
- if (attr >= 0) {
+ int attr = ns_get_hl(&ns_id, hl_id, false, sgp->sg_set);
+
+ // if a highlight group is optional, don't use the global value
+ if (attr >= 0 || (optional && ns_id > 0)) {
return attr;
}
return sgp->sg_attr;
@@ -1803,10 +1816,16 @@ int syn_id2attr(int hl_id)
/// Translate a group ID to the final group ID (following links).
int syn_get_final_id(int hl_id)
{
+ int id = curwin->w_ns_hl_active;
+ return syn_ns_get_final_id(&id, hl_id);
+}
+
+int syn_ns_get_final_id(int *ns_id, int hl_id)
+{
int count;
if (hl_id > highlight_ga.ga_len || hl_id < 1) {
- return 0; // Can be called from eval!!
+ return 0; // Can be called from eval!!
}
// Follow links until there is no more.
@@ -1814,10 +1833,10 @@ int syn_get_final_id(int hl_id)
for (count = 100; --count >= 0;) {
HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one
- // ACHTUNG: when using "tmp" attribute (no link) the function might be
+ // TODO(bfredl): when using "tmp" attribute (no link) the function might be
// called twice. it needs be smart enough to remember attr only to
// syn_id2attr time
- int check = ns_get_hl(-1, hl_id, true, sgp->sg_set);
+ int check = ns_get_hl(ns_id, hl_id, true, sgp->sg_set);
if (check == 0) {
return hl_id; // how dare! it broke the link!
} else if (check > 0) {
@@ -1915,19 +1934,22 @@ void highlight_changed(void)
if (id == 0) {
abort();
}
- int final_id = syn_get_final_id(id);
+ int ns_id = -1;
+ int final_id = syn_ns_get_final_id(&ns_id, id);
if (hlf == HLF_SNC) {
id_SNC = final_id;
} else if (hlf == HLF_S) {
id_S = final_id;
}
- highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id,
+ highlight_attr[hlf] = hl_get_ui_attr(ns_id, hlf, final_id,
(hlf == HLF_INACTIVE || hlf == HLF_LC));
if (highlight_attr[hlf] != highlight_attr_last[hlf]) {
if (hlf == HLF_MSG) {
clear_cmdline = true;
+ HlAttrs attrs = syn_attr2entry(highlight_attr[hlf]);
+ msg_grid.blending = attrs.hl_blend > -1;
}
ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]),
highlight_attr[hlf]);
@@ -1935,6 +1957,9 @@ void highlight_changed(void)
}
}
+ // sentinel value. used when no hightlight namespace is active
+ highlight_attr[HLF_COUNT] = 0;
+
//
// Setup the user highlights
//
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index cdd746d17d..689d1fce0d 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -663,7 +663,7 @@ static char *cs_create_cmd(char *csoption, char *pattern)
pat = pattern;
if (search != 4 && search != 6) {
while (ascii_iswhite(*pat)) {
- ++pat;
+ pat++;
}
}
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 684c00b7e7..f18a6d7b32 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -11,6 +11,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/extmark.h"
#include "nvim/indent.h"
@@ -30,6 +31,313 @@
# include "indent.c.generated.h"
#endif
+/// Set the integer values corresponding to the string setting of 'vartabstop'.
+/// "array" will be set, caller must free it if needed.
+/// Return false for an error.
+bool tabstop_set(char_u *var, long **array)
+{
+ long valcount = 1;
+ int t;
+ char_u *cp;
+
+ if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
+ *array = NULL;
+ return true;
+ }
+
+ for (cp = var; *cp != NUL; cp++) {
+ if (cp == var || cp[-1] == ',') {
+ char *end;
+
+ if (strtol((char *)cp, &end, 10) <= 0) {
+ if (cp != (char_u *)end) {
+ emsg(_(e_positive));
+ } else {
+ semsg(_(e_invarg2), cp);
+ }
+ return false;
+ }
+ }
+
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
+ valcount++;
+ continue;
+ }
+ semsg(_(e_invarg2), var);
+ return false;
+ }
+
+ *array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
+ (*array)[0] = valcount;
+
+ t = 1;
+ for (cp = var; *cp != NUL;) {
+ int n = atoi((char *)cp);
+
+ // Catch negative values, overflow and ridiculous big values.
+ if (n <= 0 || n > TABSTOP_MAX) {
+ semsg(_(e_invarg2), cp);
+ XFREE_CLEAR(*array);
+ return false;
+ }
+ (*array)[t++] = n;
+ while (*cp != NUL && *cp != ',') {
+ cp++;
+ }
+ if (*cp != NUL) {
+ cp++;
+ }
+ }
+
+ return true;
+}
+
+/// Calculate the number of screen spaces a tab will occupy.
+/// If "vts" is set then the tab widths are taken from that array,
+/// otherwise the value of ts is used.
+int tabstop_padding(colnr_T col, long ts_arg, long *vts)
+{
+ long ts = ts_arg == 0 ? 8 : ts_arg;
+ colnr_T tabcol = 0;
+ int t;
+ long padding = 0;
+
+ if (vts == NULL || vts[0] == 0) {
+ return (int)(ts - (col % ts));
+ }
+
+ const long tabcount = vts[0];
+
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > col) {
+ padding = tabcol - col;
+ break;
+ }
+ }
+ if (t > tabcount) {
+ padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
+ }
+
+ return (int)padding;
+}
+
+/// Find the size of the tab that covers a particular column.
+int tabstop_at(colnr_T col, long ts, long *vts)
+{
+ colnr_T tabcol = 0;
+ int t;
+ long tab_size = 0;
+
+ if (vts == NULL || vts[0] == 0) {
+ return (int)ts;
+ }
+
+ const long tabcount = vts[0];
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > col) {
+ tab_size = vts[t];
+ break;
+ }
+ }
+ if (t > tabcount) {
+ tab_size = vts[tabcount];
+ }
+
+ return (int)tab_size;
+}
+
+/// Find the column on which a tab starts.
+colnr_T tabstop_start(colnr_T col, long ts, long *vts)
+{
+ colnr_T tabcol = 0;
+ int t;
+
+ if (vts == NULL || vts[0] == 0) {
+ return (int)((col / ts) * ts);
+ }
+
+ const long tabcount = vts[0];
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > col) {
+ return (int)(tabcol - vts[t]);
+ }
+ }
+
+ const int excess = (int)(tabcol % vts[tabcount]);
+ return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
+}
+
+/// Find the number of tabs and spaces necessary to get from one column
+/// to another.
+void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
+ int *nspcs)
+{
+ int spaces = end_col - start_col;
+ colnr_T tabcol = 0;
+ long padding = 0;
+ int t;
+ long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
+
+ if (vts == NULL || vts[0] == 0) {
+ int tabs = 0;
+
+ const int initspc = (int)(ts - (start_col % ts));
+ if (spaces >= initspc) {
+ spaces -= initspc;
+ tabs++;
+ }
+ tabs += (int)(spaces / ts);
+ spaces -= (int)((spaces / ts) * ts);
+
+ *ntabs = tabs;
+ *nspcs = spaces;
+ return;
+ }
+
+ // Find the padding needed to reach the next tabstop.
+ const long tabcount = vts[0];
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > start_col) {
+ padding = tabcol - start_col;
+ break;
+ }
+ }
+ if (t > tabcount) {
+ padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
+ }
+
+ // If the space needed is less than the padding no tabs can be used.
+ if (spaces < padding) {
+ *ntabs = 0;
+ *nspcs = spaces;
+ return;
+ }
+
+ *ntabs = 1;
+ spaces -= (int)padding;
+
+ // At least one tab has been used. See if any more will fit.
+ while (spaces != 0 && ++t <= tabcount) {
+ padding = vts[t];
+ if (spaces < padding) {
+ *nspcs = spaces;
+ return;
+ }
+ *ntabs += 1;
+ spaces -= (int)padding;
+ }
+
+ *ntabs += spaces / (int)vts[tabcount];
+ *nspcs = spaces % (int)vts[tabcount];
+}
+
+/// See if two tabstop arrays contain the same values.
+bool tabstop_eq(long *ts1, long *ts2)
+{
+ int t;
+
+ if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
+ return false;
+ }
+ if (ts1 == ts2) {
+ return true;
+ }
+ if (ts1[0] != ts2[0]) {
+ return false;
+ }
+
+ for (t = 1; t <= ts1[0]; t++) {
+ if (ts1[t] != ts2[t]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/// Copy a tabstop array, allocating space for the new array.
+int *tabstop_copy(long *oldts)
+{
+ long *newts;
+ int t;
+
+ if (oldts == 0) {
+ return 0;
+ }
+
+ newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
+ for (t = 0; t <= oldts[0]; t++) {
+ newts[t] = oldts[t];
+ }
+
+ return (int *)newts;
+}
+
+/// Return a count of the number of tabstops.
+int tabstop_count(long *ts)
+{
+ return ts != NULL ? (int)ts[0] : 0;
+}
+
+/// Return the first tabstop, or 8 if there are no tabstops defined.
+int tabstop_first(long *ts)
+{
+ return ts != NULL ? (int)ts[1] : 8;
+}
+
+/// Return the effective shiftwidth value for current buffer, using the
+/// 'tabstop' value when 'shiftwidth' is zero.
+int get_sw_value(buf_T *buf)
+{
+ long result = get_sw_value_col(buf, 0);
+ assert(result >= 0 && result <= INT_MAX);
+ return (int)result;
+}
+
+/// Idem, using "pos".
+long get_sw_value_pos(buf_T *buf, pos_T *pos)
+{
+ pos_T save_cursor = curwin->w_cursor;
+ long sw_value;
+
+ curwin->w_cursor = *pos;
+ sw_value = get_sw_value_col(buf, get_nolist_virtcol());
+ curwin->w_cursor = save_cursor;
+ return sw_value;
+}
+
+/// Idem, using the first non-black in the current line.
+long get_sw_value_indent(buf_T *buf)
+{
+ pos_T pos = curwin->w_cursor;
+
+ pos.col = (colnr_T)getwhitecols_curline();
+ return get_sw_value_pos(buf, &pos);
+}
+
+/// Idem, using virtual column "col".
+long get_sw_value_col(buf_T *buf, colnr_T col)
+{
+ return buf->b_p_sw ? buf->b_p_sw
+ : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
+}
+
+/// Return the effective softtabstop value for the current buffer,
+/// using the shiftwidth value when 'softtabstop' is negative.
+int get_sts_value(void)
+{
+ long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
+ assert(result >= 0 && result <= INT_MAX);
+ return (int)result;
+}
+
// Count the size (in window cells) of the indent in the current line.
int get_indent(void)
{
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 8edff55821..34a3de4f78 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -317,17 +317,17 @@ static bool cin_has_js_key(const char_u *text)
if (*s == '\'' || *s == '"') {
// can be 'key': or "key":
quote = *s;
- ++s;
+ s++;
}
if (!vim_isIDc(*s)) { // need at least one ID character
return false;
}
while (vim_isIDc(*s)) {
- ++s;
+ s++;
}
if (*s && *s == quote) {
- ++s;
+ s++;
}
s = cin_skipcomment(s);
@@ -1768,7 +1768,7 @@ void parse_cino(buf_T *buf)
n += (sw * fraction + divider / 2) / divider;
}
}
- ++p;
+ p++;
}
if (l[1] == '-') {
n = -n;
@@ -3334,7 +3334,7 @@ int get_c_indent(void)
amount += curbuf->b_ind_open_extra;
}
}
- ++whilelevel;
+ whilelevel++;
}
/*
* We are after a "normal" statement.
@@ -3848,7 +3848,7 @@ static int find_match(int lookfor, linenr_T ourscope)
* another "do", so increment whilelevel. XXX
*/
if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) {
- ++whilelevel;
+ whilelevel++;
continue;
}
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 53c238a380..c525a49bc3 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -13,6 +13,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -34,9 +35,8 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/state.h"
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index a86f23db8e..5d97f90bb1 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -15,6 +15,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
@@ -38,7 +39,6 @@
#include "nvim/os/os.h"
#include "nvim/profile.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/undo.h"
#include "nvim/usercmd.h"
#include "nvim/version.h"
@@ -1318,7 +1318,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
- estack_push(ETYPE_SCRIPT, NULL, 0);
+ estack_push(ETYPE_SCRIPT, name, 0);
garray_T ga;
char_u *line = NULL;
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 2afbbebfe7..78346fd81f 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -24,6 +24,8 @@ typedef struct {
#endif
} nlua_ref_state_t;
+#define NLUA_EXEC_STATIC(cstr, arg, err) nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, err)
+
#define NLUA_CLEAR_REF(x) \
do { \
/* Take the address to avoid double evaluation. #1375 */ \
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 4dacf6948f..fd31ba6c66 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
@@ -16,6 +17,7 @@
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
@@ -57,11 +59,10 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
#include "nvim/state.h"
@@ -1244,8 +1245,8 @@ static void command_line_scan(mparm_T *parmp)
} else if (argv[0][0] == '-') {
// "-S" followed by another option: use default session file.
a = SESSION_FILE;
- ++argc;
- --argv;
+ argc++;
+ argv--;
} else {
a = argv[0];
}
@@ -1615,9 +1616,9 @@ static void create_windows(mparm_T *parmp)
// Watch out for autocommands that delete a window.
//
// Don't execute Win/Buf Enter/Leave autocommands here
- ++autocmd_no_enter;
- ++autocmd_no_leave;
- dorewind = TRUE;
+ autocmd_no_enter++;
+ autocmd_no_leave++;
+ dorewind = true;
while (done++ < 1000) {
if (dorewind) {
if (parmp->window_layout == WIN_TABS) {
@@ -1679,8 +1680,8 @@ static void create_windows(mparm_T *parmp)
curwin = firstwin;
}
curbuf = curwin->w_buffer;
- --autocmd_no_enter;
- --autocmd_no_leave;
+ autocmd_no_enter--;
+ autocmd_no_leave--;
}
}
@@ -1697,8 +1698,8 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
/*
* Don't execute Win/Buf Enter/Leave autocommands here
*/
- ++autocmd_no_enter;
- ++autocmd_no_leave;
+ autocmd_no_enter++;
+ autocmd_no_leave++;
// When w_arg_idx is -1 remove the window (see create_windows()).
if (curwin->w_arg_idx == -1) {
@@ -1784,7 +1785,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
if (parmp->window_layout == WIN_TABS) {
goto_tabpage(1);
}
- --autocmd_no_enter;
+ autocmd_no_enter--;
// make the first window the current window
win = firstwin;
@@ -1798,7 +1799,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
win_enter(win, false);
- --autocmd_no_leave;
+ autocmd_no_leave--;
TIME_MSG("editing files in windows");
if (parmp->window_count > 1 && parmp->window_layout != WIN_TABS) {
win_equal(curwin, false, 'b'); // adjust heights
diff --git a/src/nvim/match.c b/src/nvim/match.c
index f085d7cdb4..1c34c9f004 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -7,17 +7,18 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/match.h"
#include "nvim/memline.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -696,7 +697,7 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
}
// Highlight the match were the cursor is using the CurSearch
// group.
- if (shl == search_hl && shl->has_cursor && (HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC])) {
+ if (shl == search_hl && shl->has_cursor && (HL_ATTR(HLF_LC) || win_hl_attr(wp, HLF_LC))) {
shl->attr_cur = win_hl_attr(wp, HLF_LC) ? win_hl_attr(wp, HLF_LC) : HL_ATTR(HLF_LC);
} else {
shl->attr_cur = shl->attr;
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 53bbaab694..af9e214d92 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -39,6 +39,7 @@
#include "nvim/arabic.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
@@ -49,7 +50,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/screen.h"
@@ -890,9 +890,9 @@ int utf_ptr2len_len(const char_u *p, int size)
return len;
}
-/// Return the number of bytes occupied by a UTF-8 character in a string
-///
+/// Return the number of bytes occupied by a UTF-8 character in a string.
/// This includes following composing characters.
+/// Returns zero for NUL.
int utfc_ptr2len(const char *const p_in)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
@@ -1006,8 +1006,9 @@ int utf_char2len(const int c)
/// Convert Unicode character to UTF-8 string
///
-/// @param c character to convert to \p buf
-/// @param[out] buf UTF-8 string generated from \p c, does not add \0
+/// @param c character to convert to UTF-8 string in \p buf
+/// @param[out] buf UTF-8 string generated from \p c, does not add \0
+/// must have room for at least 6 bytes
/// @return Number of bytes (1-6).
int utf_char2bytes(const int c, char *const buf)
{
@@ -1605,7 +1606,7 @@ void show_utf8(void)
}
sprintf((char *)IObuff + rlen, "%02x ",
(line[i] == NL) ? NUL : line[i]); // NUL is stored as NL
- --clen;
+ clen--;
rlen += (int)STRLEN(IObuff + rlen);
if (rlen > IOSIZE - 20) {
break;
@@ -1638,7 +1639,7 @@ int utf_head_off(const char_u *base, const char_u *p)
// Move q to the first byte of this char.
while (q > base && (*q & 0xc0) == 0x80) {
- --q;
+ q--;
}
// Check for illegal sequence. Do allow an illegal byte after where we
// started.
@@ -1659,10 +1660,10 @@ int utf_head_off(const char_u *base, const char_u *p)
if (arabic_maycombine(c)) {
// Advance to get a sneak-peak at the next char
const char_u *j = q;
- --j;
+ j--;
// Move j to the first byte of this char.
while (j > base && (*j & 0xc0) == 0x80) {
- --j;
+ j--;
}
if (arabic_combine(utf_ptr2char((char *)j), c)) {
continue;
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 8b8f709396..23bc5d59c8 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -48,6 +48,7 @@
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
@@ -66,7 +67,6 @@
#include "nvim/os/process.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/screen.h"
#include "nvim/sha256.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
@@ -1136,7 +1136,7 @@ void ml_recover(bool checkext)
if (txt_start <= (int)HEADER_SIZE
|| txt_start >= (int)dp->db_txt_end) {
p = (char_u *)"???";
- ++error;
+ error++;
} else {
p = (char_u *)dp + txt_start;
}
@@ -1205,10 +1205,10 @@ void ml_recover(bool checkext)
if (got_int) {
emsg(_("E311: Recovery Interrupted"));
} else if (error) {
- ++no_wait_return;
+ no_wait_return++;
msg(">>>>>>>>>>>>>");
emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
- --no_wait_return;
+ no_wait_return--;
msg(_("See \":help E312\" for more information."));
msg(">>>>>>>>>>>>>");
} else {
@@ -1655,12 +1655,12 @@ static int recov_file_names(char **names, char_u *path, int prepend_dot)
p += i; // file name has been expanded to full path
}
if (STRCMP(p, names[num_names]) != 0) {
- ++num_names;
+ num_names++;
} else {
xfree(names[num_names]);
}
} else {
- ++num_names;
+ num_names++;
}
return num_names;
@@ -2179,7 +2179,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
memmove((char *)dp_right + dp_right->db_txt_start,
line, (size_t)len);
- ++line_count_right;
+ line_count_right++;
}
/*
* may move lines from the left/old block to the right/new one.
@@ -2219,7 +2219,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
}
memmove((char *)dp_left + dp_left->db_txt_start,
line, (size_t)len);
- ++line_count_left;
+ line_count_left++;
}
if (db_idx < 0) { // left block is new
@@ -2993,9 +2993,9 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
* update high for insert/delete
*/
if (action == ML_INSERT) {
- ++high;
+ high++;
} else if (action == ML_DELETE) {
- --high;
+ high--;
}
dp = hp->bh_data;
@@ -3299,7 +3299,7 @@ static void attention_message(buf_T *buf, char_u *fname)
{
assert(buf->b_fname != NULL);
- ++no_wait_return;
+ no_wait_return++;
(void)emsg(_("E325: ATTENTION"));
msg_puts(_("\nFound a swap file by the name \""));
msg_home_replace(fname);
@@ -3334,7 +3334,7 @@ static void attention_message(buf_T *buf, char_u *fname)
msg_outtrans((char *)fname);
msg_puts(_("\"\n to avoid this message.\n"));
cmdline_row = msg_row;
- --no_wait_return;
+ no_wait_return--;
}
/// Trigger the SwapExists autocommands.
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 2d473aa66b..fb36d4ccf4 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -9,6 +9,7 @@
#include <string.h>
#include "nvim/api/extmark.h"
+#include "nvim/arglist.h"
#include "nvim/context.h"
#include "nvim/decoration_provider.h"
#include "nvim/eval.h"
@@ -630,6 +631,7 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/file_search.h"
# include "nvim/fold.h"
# include "nvim/getchar.h"
+# include "nvim/grid.h"
# include "nvim/mark.h"
# include "nvim/mbyte.h"
# include "nvim/memline.h"
@@ -641,7 +643,6 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/path.h"
# include "nvim/quickfix.h"
# include "nvim/regexp.h"
-# include "nvim/screen.h"
# include "nvim/search.h"
# include "nvim/spell.h"
# include "nvim/syntax.h"
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 1aa1fb5f5a..c3cf4457fc 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -23,7 +23,7 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -952,7 +952,7 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
}
while (*p != NUL && ascii_iswhite(*p)) {
- ++p;
+ p++;
}
arg = after_dot = p;
@@ -1807,9 +1807,9 @@ static char *menu_skip_part(char *p)
{
while (*p != NUL && *p != '.' && !ascii_iswhite(*p)) {
if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL) {
- ++p;
+ p++;
}
- ++p;
+ p++;
}
return p;
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 6910fc16ae..684cf7207c 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -15,6 +15,7 @@
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
@@ -23,7 +24,9 @@
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/indent.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
#include "nvim/main.h"
@@ -39,7 +42,6 @@
#include "nvim/os/time.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -319,7 +321,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
if (entered >= 3) {
return TRUE;
}
- ++entered;
+ entered++;
// Add message to history (unless it's a repeated kept message or a
// truncated message)
@@ -356,7 +358,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
need_fileinfo = false;
xfree(buf);
- --entered;
+ entered--;
return retval;
}
@@ -828,8 +830,15 @@ static bool semsgv(const char *fmt, va_list ap)
/// detected when fuzzing vim.
void iemsg(const char *s)
{
+ if (emsg_not_now()) {
+ return;
+ }
+
emsg(s);
#ifdef ABORT_ON_INTERNAL_ERROR
+ set_vim_var_string(VV_ERRMSG, s, -1);
+ msg_putchar('\n'); // avoid overwriting the error message
+ ui_flush();
abort();
#endif
}
@@ -839,11 +848,17 @@ void iemsg(const char *s)
/// detected when fuzzing vim.
void siemsg(const char *s, ...)
{
+ if (emsg_not_now()) {
+ return;
+ }
+
va_list ap;
va_start(ap, s);
(void)semsgv(s, ap);
va_end(ap);
#ifdef ABORT_ON_INTERNAL_ERROR
+ msg_putchar('\n'); // avoid overwriting the error message
+ ui_flush();
abort();
#endif
}
@@ -1015,7 +1030,7 @@ int delete_first_msg(void)
xfree(p->msg);
hl_msg_free(p->multiattr);
xfree(p);
- --msg_hist_len;
+ msg_hist_len--;
return OK;
}
@@ -1980,13 +1995,13 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
msg_col -= cw;
if (msg_col == 0) {
msg_col = Columns;
- ++msg_row;
+ msg_row++;
}
} else {
msg_col += cw;
if (msg_col >= Columns) {
msg_col = 0;
- ++msg_row;
+ msg_row++;
}
}
return s + l;
@@ -2239,7 +2254,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
* for a character.
*/
if (lines_left > 0) {
- --lines_left;
+ lines_left--;
}
if (p_more && lines_left == 0 && State != MODE_HITRETURN
&& !msg_no_more && !exmode_active) {
@@ -2288,7 +2303,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
msg_col = 0;
} else if (*s == '\b') { // go to previous char
if (msg_col) {
- --msg_col;
+ msg_col--;
}
} else if (*s == TAB) { // translate Tab into spaces
do {
@@ -2322,7 +2337,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
s += l - 1;
}
}
- ++s;
+ s++;
}
// Output any postponed text.
@@ -2670,7 +2685,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
msg_col = mp->sb_msg_col;
p = mp->sb_text;
if (*p == '\n') { // don't display the line break
- ++p;
+ p++;
}
msg_puts_display(p, -1, mp->sb_attr, TRUE);
if (mp->sb_eol || mp->sb_next == NULL) {
@@ -2699,7 +2714,7 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
}
if (msg_col >= Columns) {
msg_col = 0;
- ++msg_row;
+ msg_row++;
}
}
@@ -2954,7 +2969,7 @@ static int do_more_prompt(int typed_char)
HL_ATTR(HLF_MSG));
for (i = 0; mp != NULL && i < Rows - 1; i++) {
mp = disp_sb_line(i, mp);
- ++msg_scrolled;
+ msg_scrolled++;
}
to_redraw = false;
}
@@ -3053,12 +3068,12 @@ static void msg_screen_putchar(int c, int attr)
if (cmdmsg_rl) {
if (--msg_col == 0) {
msg_col = Columns;
- ++msg_row;
+ msg_row++;
}
} else {
if (++msg_col >= Columns) {
msg_col = 0;
- ++msg_row;
+ msg_row++;
}
}
}
@@ -3355,7 +3370,7 @@ int redirecting(void)
void verbose_enter(void)
{
if (*p_vfile != NUL) {
- ++msg_silent;
+ msg_silent++;
}
}
@@ -3374,7 +3389,7 @@ void verbose_leave(void)
void verbose_enter_scroll(void)
{
if (*p_vfile != NUL) {
- ++msg_silent;
+ msg_silent++;
} else {
// always scroll up, don't overwrite
msg_scroll = TRUE;
@@ -3536,7 +3551,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
* Since we wait for a keypress, don't make the
* user press RETURN as well afterwards.
*/
- ++no_wait_return;
+ no_wait_return++;
hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
for (;;) {
@@ -3585,7 +3600,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
msg_silent = save_msg_silent;
State = oldState;
setmouse();
- --no_wait_return;
+ no_wait_return--;
msg_end_prompt();
return retval;
@@ -3739,7 +3754,7 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int def
}
} else if (*r == DLG_HOTKEY_CHAR || first_hotkey) {
if (*r == DLG_HOTKEY_CHAR) {
- ++r;
+ r++;
}
first_hotkey = false;
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index a4a521fa80..a8d0b3b584 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -8,13 +8,14 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/fold.h"
+#include "nvim/grid.h"
#include "nvim/memline.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/os_unix.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -386,7 +387,7 @@ retnomove:
count = 0;
for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count;) {
if (curwin->w_topfill > 0) {
- ++count;
+ count++;
} else {
count += plines_win(curwin, curwin->w_topline, true);
}
@@ -514,7 +515,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
break; // past end of file
}
row -= count;
- ++lnum;
+ lnum++;
}
if (!retval) {
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 6d4eb8ef49..1ed7acd012 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -21,16 +21,18 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/window.h"
@@ -114,7 +116,7 @@ static void redraw_for_cursorcolumn(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) {
- if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC]) && using_hlsearch())) {
+ if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || win_hl_attr(wp, HLF_LC)) && using_hlsearch())) {
// When 'cursorcolumn' is set or "CurSearch" is in use
// need to redraw with SOME_VALID.
redraw_later(wp, SOME_VALID);
@@ -1056,7 +1058,7 @@ bool scrolldown(long line_count, int byfold)
// A sequence of folded lines only counts for one logical line
linenr_T first;
if (hasFolding(curwin->w_topline, &first, NULL)) {
- ++done;
+ done++;
if (!byfold) {
line_count -= curwin->w_topline - first - 1;
}
@@ -1092,7 +1094,7 @@ bool scrolldown(long line_count, int byfold)
while (wrow >= curwin->w_height_inner && curwin->w_cursor.lnum > 1) {
linenr_T first;
if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) {
- --wrow;
+ wrow--;
if (first == 1) {
curwin->w_cursor.lnum = 1;
} else {
@@ -1406,8 +1408,8 @@ void scroll_cursor_top(int min_scroll, int always)
}
if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) {
- --top;
- ++bot;
+ top--;
+ bot++;
} else {
top = curwin->w_cursor.lnum - 1;
bot = curwin->w_cursor.lnum + 1;
@@ -1453,8 +1455,8 @@ void scroll_cursor_top(int min_scroll, int always)
extra += i;
new_topline = top;
- --top;
- ++bot;
+ top--;
+ bot++;
}
/*
@@ -1664,7 +1666,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
for (i = 0; i < scrolled && boff.lnum < curwin->w_botline;) {
botline_forw(curwin, &boff);
i += boff.height;
- ++line_count;
+ line_count++;
}
if (i < scrolled) { // below curwin->w_botline, don't scroll
line_count = 9999;
@@ -1726,7 +1728,7 @@ void scroll_cursor_halfway(int atend)
} else {
++below; // count a "~" line
if (atend) {
- ++used;
+ used++;
}
}
}
@@ -1835,7 +1837,7 @@ void cursor_correct(void)
if (topline < botline) {
above += win_get_fill(curwin, topline + 1);
}
- ++topline;
+ topline++;
}
}
if (topline == botline || botline == 0) {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index c28ffaa22e..c87c0cbb6e 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -22,6 +22,7 @@
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
@@ -34,7 +35,8 @@
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
+#include "nvim/help.h"
#include "nvim/indent.h"
#include "nvim/keycodes.h"
#include "nvim/log.h"
@@ -55,7 +57,6 @@
#include "nvim/plines.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
@@ -1281,10 +1282,10 @@ static void normal_redraw(NormalState *s)
if (VIsual_active) {
redraw_curbuf_later(INVERTED); // update inverted part
- update_screen(INVERTED);
+ update_screen(0);
} else if (must_redraw) {
update_screen(0);
- } else if (redraw_cmdline || clear_cmdline) {
+ } else if (redraw_cmdline || clear_cmdline || redraw_mode) {
showmode();
}
@@ -1837,7 +1838,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
if (jump_flags) {
jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
- update_curbuf(VIsual_active ? INVERTED : VALID);
+ redraw_curbuf_later(VIsual_active ? INVERTED : VALID);
+ update_screen(0);
setcursor();
ui_flush(); // Update before showing popup menu
}
@@ -6930,6 +6932,10 @@ static void nv_esc(cmdarg_T *cap)
}
}
+ if (restart_edit != 0) {
+ redraw_mode = true; // remove "-- (insert) --"
+ }
+
restart_edit = 0;
if (cmdwin_type != 0) {
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 55bffe6fc5..c3edc5b315 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -17,6 +17,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -46,7 +47,6 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -1927,8 +1927,8 @@ static int op_replace(oparg_T *oap, int c)
// times.
if (utf_char2cells(c) > 1) {
if ((numc & 1) && !bd.is_short) {
- ++bd.endspaces;
- ++n;
+ bd.endspaces++;
+ n++;
}
numc = numc / 2;
}
@@ -3104,7 +3104,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_array != NULL) {
*ptr = NUL;
}
- ++ptr;
+ ptr++;
// A trailing '\n' makes the register linewise.
if (*ptr == NUL) {
y_type = kMTLineWise;
@@ -3442,12 +3442,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
}
curbuf->b_op_start = curwin->w_cursor;
- }
- /*
- * Line mode: BACKWARD is the same as FORWARD on the previous line
- */
- else if (dir == BACKWARD) {
- --lnum;
+ } else if (dir == BACKWARD) {
+ // Line mode: BACKWARD is the same as FORWARD on the previous line
+ lnum--;
}
new_cursor = curwin->w_cursor;
@@ -3586,7 +3583,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
new_lnum++;
}
lnum++;
- ++nr_lines;
+ nr_lines++;
if (flags & PUT_FIXINDENT) {
old_pos = curwin->w_cursor;
curwin->w_cursor.lnum = lnum;
@@ -4001,7 +3998,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
|| *comment_flags == ':') {
break;
}
- ++comment_flags;
+ comment_flags++;
}
// If we found a colon, it means that we are not processing a line
@@ -4285,7 +4282,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in
}
} else {
while (ascii_iswhite(line1[idx1])) {
- ++idx1;
+ idx1++;
}
}
}
@@ -4698,7 +4695,7 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags,
*/
flags = *leader_flags;
while (*flags && *flags != ':' && *flags != COM_END) {
- ++flags;
+ flags++;
}
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index da29d40b05..09793cbdcf 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -27,13 +27,16 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -49,6 +52,7 @@
#include "nvim/hardcopy.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
@@ -66,7 +70,7 @@
#include "nvim/os/os.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/screen.h"
@@ -83,7 +87,9 @@
#ifdef WIN32
# include "nvim/os/pty_conpty_win.h"
#endif
+#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
@@ -340,9 +346,6 @@ static char_u SHM_ALL[] = {
static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence");
static char e_unbalanced_groups[] = N_("E542: unbalanced groups");
-static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'");
-static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'");
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
#endif
@@ -2313,7 +2316,7 @@ static char *set_string_option(const int opt_idx, const char *const value, const
/// Return true if "val" is a valid name: only consists of alphanumeric ASCII
/// characters or characters in "allowed".
-static bool valid_name(const char_u *val, const char *allowed)
+bool valid_name(const char_u *val, const char *allowed)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
for (const char_u *s = val; *s != NUL; s++) {
@@ -2333,25 +2336,6 @@ static bool valid_filetype(const char_u *val)
return valid_name(val, ".-_");
}
-/// Return true if "val" is a valid 'spelllang' value.
-bool valid_spelllang(const char_u *val)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return valid_name(val, ".-_,@");
-}
-
-/// Return true if "val" is a valid 'spellfile' value.
-static bool valid_spellfile(const char_u *val)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- for (const char_u *s = val; *s != NUL; s++) {
- if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
- return false;
- }
- }
- return true;
-}
-
/// Handle setting 'mousescroll'.
/// @return error message, NULL if it's OK.
static char *check_mousescroll(char *string)
@@ -3428,12 +3412,6 @@ static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, c
return errmsg;
}
-/// Simple int comparison function for use with qsort()
-static int int_cmp(const void *a, const void *b)
-{
- return *(const int *)a - *(const int *)b;
-}
-
/// Handle setting 'signcolumn' for value 'val'
///
/// @return OK when the value is valid, FAIL otherwise
@@ -3464,366 +3442,12 @@ int check_signcolumn(char_u *val)
return FAIL;
}
-/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
-///
-/// @return error message, NULL if it's OK.
-char *check_colorcolumn(win_T *wp)
-{
- char *s;
- int col;
- unsigned int count = 0;
- int color_cols[256];
- int j = 0;
-
- if (wp->w_buffer == NULL) {
- return NULL; // buffer was closed
- }
-
- for (s = (char *)wp->w_p_cc; *s != NUL && count < 255;) {
- if (*s == '-' || *s == '+') {
- // -N and +N: add to 'textwidth'
- col = (*s == '-') ? -1 : 1;
- s++;
- if (!ascii_isdigit(*s)) {
- return e_invarg;
- }
- col = col * getdigits_int(&s, true, 0);
- if (wp->w_buffer->b_p_tw == 0) {
- goto skip; // 'textwidth' not set, skip this item
- }
- assert((col >= 0
- && wp->w_buffer->b_p_tw <= INT_MAX - col
- && wp->w_buffer->b_p_tw + col >= INT_MIN)
- || (col < 0
- && wp->w_buffer->b_p_tw >= INT_MIN - col
- && wp->w_buffer->b_p_tw + col <= INT_MAX));
- col += (int)wp->w_buffer->b_p_tw;
- if (col < 0) {
- goto skip;
- }
- } else if (ascii_isdigit(*s)) {
- col = getdigits_int(&s, true, 0);
- } else {
- return e_invarg;
- }
- color_cols[count++] = col - 1; // 1-based to 0-based
-skip:
- if (*s == NUL) {
- break;
- }
- if (*s != ',') {
- return e_invarg;
- }
- if (*++s == NUL) {
- return e_invarg; // illegal trailing comma as in "set cc=80,"
- }
- }
-
- xfree(wp->w_p_cc_cols);
- if (count == 0) {
- wp->w_p_cc_cols = NULL;
- } else {
- wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
- /* sort the columns for faster usage on screen redraw inside
- * win_line() */
- qsort(color_cols, count, sizeof(int), int_cmp);
-
- for (unsigned int i = 0; i < count; i++) {
- // skip duplicates
- if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
- wp->w_p_cc_cols[j++] = color_cols[i];
- }
- }
- wp->w_p_cc_cols[j] = -1; // end marker
- }
-
- return NULL; // no error
-}
-
void check_blending(win_T *wp)
{
wp->w_grid_alloc.blending =
wp->w_p_winbl > 0 || (wp->w_floating && wp->w_float_config.shadow);
}
-/// Calls mb_cptr2char_adv(p) and returns the character.
-/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
-/// Returns 0 for invalid hex or invalid UTF-8 byte.
-static int get_encoded_char_adv(char_u **p)
-{
- char_u *s = *p;
-
- if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
- int64_t num = 0;
- int bytes;
- int n;
- for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
- *p += 2;
- n = hexhex2nr(*p);
- if (n < 0) {
- return 0;
- }
- num = num * 256 + n;
- }
- *p += 2;
- return (int)num;
- }
-
- // TODO(bfredl): use schar_T representation and utfc_ptr2len
- int clen = utf_ptr2len((char *)s);
- int c = mb_cptr2char_adv((const char_u **)p);
- if (clen == 1 && c > 127) { // Invalid UTF-8 byte
- return 0;
- }
- return c;
-}
-
-/// Handle setting 'listchars' or 'fillchars'.
-/// Assume monocell characters
-///
-/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
-/// @return error message, NULL if it's OK.
-char *set_chars_option(win_T *wp, char_u **varp, bool set)
-{
- int round, i, len, len2, entries;
- char_u *p, *s;
- int c1;
- int c2 = 0;
- int c3 = 0;
- char_u *last_multispace = NULL; // Last occurrence of "multispace:"
- char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
- int multispace_len = 0; // Length of lcs-multispace string
- int lead_multispace_len = 0; // Length of lcs-leadmultispace string
-
- struct chars_tab {
- int *cp; ///< char value
- char *name; ///< char id
- int def; ///< default value
- };
- struct chars_tab *tab;
-
- // XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case.
- struct chars_tab fcs_tab[] = {
- { &wp->w_p_fcs_chars.stl, "stl", ' ' },
- { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
- { &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
- { &wp->w_p_fcs_chars.horiz, "horiz", char2cells(0x2500) == 1 ? 0x2500 : '-' }, // ─
- { &wp->w_p_fcs_chars.horizup, "horizup", char2cells(0x2534) == 1 ? 0x2534 : '-' }, // ┴
- { &wp->w_p_fcs_chars.horizdown, "horizdown", char2cells(0x252c) == 1 ? 0x252c : '-' }, // ┬
- { &wp->w_p_fcs_chars.vert, "vert", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
- { &wp->w_p_fcs_chars.vertleft, "vertleft", char2cells(0x2524) == 1 ? 0x2524 : '|' }, // ┤
- { &wp->w_p_fcs_chars.vertright, "vertright", char2cells(0x251c) == 1 ? 0x251c : '|' }, // ├
- { &wp->w_p_fcs_chars.verthoriz, "verthoriz", char2cells(0x253c) == 1 ? 0x253c : '+' }, // ┼
- { &wp->w_p_fcs_chars.fold, "fold", char2cells(0x00b7) == 1 ? 0x00b7 : '-' }, // ·
- { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
- { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
- { &wp->w_p_fcs_chars.foldsep, "foldsep", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
- { &wp->w_p_fcs_chars.diff, "diff", '-' },
- { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
- { &wp->w_p_fcs_chars.eob, "eob", '~' },
- };
- struct chars_tab lcs_tab[] = {
- { &wp->w_p_lcs_chars.eol, "eol", NUL },
- { &wp->w_p_lcs_chars.ext, "extends", NUL },
- { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
- { &wp->w_p_lcs_chars.prec, "precedes", NUL },
- { &wp->w_p_lcs_chars.space, "space", NUL },
- { &wp->w_p_lcs_chars.tab2, "tab", NUL },
- { &wp->w_p_lcs_chars.lead, "lead", NUL },
- { &wp->w_p_lcs_chars.trail, "trail", NUL },
- { &wp->w_p_lcs_chars.conceal, "conceal", NUL },
- };
-
- if (varp == &p_lcs || varp == &wp->w_p_lcs) {
- tab = lcs_tab;
- entries = ARRAY_SIZE(lcs_tab);
- if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
- varp = &p_lcs;
- }
- } else {
- tab = fcs_tab;
- entries = ARRAY_SIZE(fcs_tab);
- if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
- varp = &p_fcs;
- }
- }
-
- // first round: check for valid value, second round: assign values
- for (round = 0; round <= (set ? 1 : 0); round++) {
- if (round > 0) {
- // After checking that the value is valid: set defaults
- for (i = 0; i < entries; i++) {
- if (tab[i].cp != NULL) {
- *(tab[i].cp) = tab[i].def;
- }
- }
- if (varp == &p_lcs || varp == &wp->w_p_lcs) {
- wp->w_p_lcs_chars.tab1 = NUL;
- wp->w_p_lcs_chars.tab3 = NUL;
-
- xfree(wp->w_p_lcs_chars.multispace);
- if (multispace_len > 0) {
- wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
- wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
- } else {
- wp->w_p_lcs_chars.multispace = NULL;
- }
-
- xfree(wp->w_p_lcs_chars.leadmultispace);
- if (lead_multispace_len > 0) {
- wp->w_p_lcs_chars.leadmultispace
- = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
- wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
- } else {
- wp->w_p_lcs_chars.leadmultispace = NULL;
- }
- }
- }
- p = *varp;
- while (*p) {
- for (i = 0; i < entries; i++) {
- len = (int)STRLEN(tab[i].name);
- if (STRNCMP(p, tab[i].name, len) == 0
- && p[len] == ':'
- && p[len + 1] != NUL) {
- c2 = c3 = 0;
- s = p + len + 1;
- c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
- if (*s == NUL) {
- return e_invarg;
- }
- c2 = get_encoded_char_adv(&s);
- if (c2 == 0 || char2cells(c2) > 1) {
- return e_invarg;
- }
- if (!(*s == ',' || *s == NUL)) {
- c3 = get_encoded_char_adv(&s);
- if (c3 == 0 || char2cells(c3) > 1) {
- return e_invarg;
- }
- }
- }
- if (*s == ',' || *s == NUL) {
- if (round > 0) {
- if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
- wp->w_p_lcs_chars.tab1 = c1;
- wp->w_p_lcs_chars.tab2 = c2;
- wp->w_p_lcs_chars.tab3 = c3;
- } else if (tab[i].cp != NULL) {
- *(tab[i].cp) = c1;
- }
- }
- p = s;
- break;
- }
- }
- }
-
- if (i == entries) {
- len = (int)STRLEN("multispace");
- len2 = (int)STRLEN("leadmultispace");
- if ((varp == &p_lcs || varp == &wp->w_p_lcs)
- && STRNCMP(p, "multispace", len) == 0
- && p[len] == ':'
- && p[len + 1] != NUL) {
- s = p + len + 1;
- if (round == 0) {
- // Get length of lcs-multispace string in the first round
- last_multispace = p;
- multispace_len = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- multispace_len++;
- }
- if (multispace_len == 0) {
- // lcs-multispace cannot be an empty string
- return e_invarg;
- }
- p = s;
- } else {
- int multispace_pos = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (p == last_multispace) {
- wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
- }
- }
- p = s;
- }
- } else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
- && STRNCMP(p, "leadmultispace", len2) == 0
- && p[len2] == ':'
- && p[len2 + 1] != NUL) {
- s = p + len2 + 1;
- if (round == 0) {
- // get length of lcs-leadmultispace string in first round
- last_lmultispace = p;
- lead_multispace_len = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- lead_multispace_len++;
- }
- if (lead_multispace_len == 0) {
- // lcs-leadmultispace cannot be an empty string
- return e_invarg;
- }
- p = s;
- } else {
- int multispace_pos = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (p == last_lmultispace) {
- wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
- }
- }
- p = s;
- }
- } else {
- return e_invarg;
- }
- }
- if (*p == ',') {
- p++;
- }
- }
- }
-
- return NULL; // no error
-}
-
-/// Check all global and local values of 'listchars' and 'fillchars'.
-/// May set different defaults in case character widths change.
-///
-/// @return an untranslated error message if any of them is invalid, NULL otherwise.
-char *check_chars_options(void)
-{
- if (set_chars_option(curwin, &p_lcs, false) != NULL) {
- return e_conflicts_with_value_of_listchars;
- }
- if (set_chars_option(curwin, &p_fcs, false) != NULL) {
- return e_conflicts_with_value_of_fillchars;
- }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
- return e_conflicts_with_value_of_listchars;
- }
- if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
- return e_conflicts_with_value_of_fillchars;
- }
- }
- return NULL;
-}
-
/// Check validity of options with the 'statusline' format.
/// Return an untranslated error message or NULL.
char *check_stl_option(char *s)
@@ -3894,63 +3518,30 @@ char *check_stl_option(char *s)
return NULL;
}
-static char *did_set_spell_option(bool is_spellfile)
+/// Handle setting `winhighlight' in window "wp"
+bool parse_winhl_opt(win_T *wp)
{
- char *errmsg = NULL;
+ const char *p = (const char *)wp->w_p_winhl;
- if (is_spellfile) {
- int l = (int)STRLEN(curwin->w_s->b_p_spf);
- if (l > 0
- && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
- errmsg = e_invarg;
+ if (!*p) {
+ if (wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) {
+ wp->w_ns_hl = 0;
+ wp->w_hl_needs_update = true;
}
- }
- if (errmsg == NULL) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == curbuf && wp->w_p_spell) {
- errmsg = did_set_spelllang(wp);
- break;
- }
- }
+ return true;
}
- return errmsg;
-}
-
-/// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
-/// Return error message when failed, NULL when OK.
-static char *compile_cap_prog(synblock_T *synblock)
- FUNC_ATTR_NONNULL_ALL
-{
- regprog_T *rp = synblock->b_cap_prog;
- char_u *re;
-
- if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
- synblock->b_cap_prog = NULL;
+ if (wp->w_ns_hl_winhl == 0) {
+ wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING);
} else {
- // Prepend a ^ so that we only match at one column
- re = concat_str((char_u *)"^", synblock->b_p_spc);
- synblock->b_cap_prog = vim_regcomp((char *)re, RE_MAGIC);
- xfree(re);
- if (synblock->b_cap_prog == NULL) {
- synblock->b_cap_prog = rp; // restore the previous program
- return e_invarg;
- }
+ // namespace already exist. invalidate existing items
+ DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true);
+ dp->hl_valid++;
}
+ wp->w_ns_hl = wp->w_ns_hl_winhl;
+ int ns_hl = wp->w_ns_hl;
- vim_regfree(rp);
- return NULL;
-}
-
-/// Handle setting `winhighlight' in window "wp"
-static bool parse_winhl_opt(win_T *wp)
-{
- int w_hl_id_normal = 0;
- int w_hl_ids[HLF_COUNT] = { 0 };
- int hlf;
-
- const char *p = (const char *)wp->w_p_winhl;
while (*p) {
char *colon = strchr(p, ':');
if (!colon) {
@@ -3961,27 +3552,15 @@ static bool parse_winhl_opt(win_T *wp)
char *commap = xstrchrnul(hi, ',');
size_t len = (size_t)(commap - hi);
int hl_id = len ? syn_check_group(hi, len) : -1;
+ int hl_id_link = nlen ? syn_check_group(p, nlen) : 0;
- if (strncmp("Normal", p, nlen) == 0) {
- w_hl_id_normal = hl_id;
- } else {
- for (hlf = 0; hlf < HLF_COUNT; hlf++) {
- if (strlen(hlf_names[hlf]) == nlen
- && strncmp(hlf_names[hlf], p, nlen) == 0) {
- w_hl_ids[hlf] = hl_id;
- break;
- }
- }
- if (hlf == HLF_COUNT) {
- return false;
- }
- }
+ HlAttrs attrs = HLATTRS_INIT;
+ attrs.rgb_ae_attr |= HL_GLOBAL;
+ ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL);
p = *commap ? commap + 1 : "";
}
- wp->w_hl_id_normal = w_hl_id_normal;
- memcpy(wp->w_hl_ids, w_hl_ids, sizeof(w_hl_ids));
wp->w_hl_needs_update = true;
return true;
}
@@ -5759,47 +5338,6 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
return OK;
}
-/// Compute columns for ruler and shown command. 'sc_col' is also used to
-/// decide what the maximum length of a message on the status line can be.
-/// If there is a status line for the last window, 'sc_col' is independent
-/// of 'ru_col'.
-
-#define COL_RULER 17 // columns needed by standard ruler
-
-void comp_col(void)
-{
- int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
-
- sc_col = 0;
- ru_col = 0;
- if (p_ru) {
- ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
- // no last status line, adjust sc_col
- if (!last_has_status) {
- sc_col = ru_col;
- }
- }
- if (p_sc) {
- sc_col += SHOWCMD_COLS;
- if (!p_ru || last_has_status) { // no need for separating space
- sc_col++;
- }
- }
- assert(sc_col >= 0
- && INT_MIN + sc_col <= Columns);
- sc_col = Columns - sc_col;
- assert(ru_col >= 0
- && INT_MIN + ru_col <= Columns);
- ru_col = Columns - ru_col;
- if (sc_col <= 0) { // screen too narrow, will become a mess
- sc_col = 1;
- }
- if (ru_col <= 0) {
- ru_col = 1;
- }
- set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
-}
-
// Unset local option value, similar to ":set opt<".
void unset_global_local_option(char *name, void *from)
{
@@ -7554,313 +7092,6 @@ int check_ff_value(char_u *p)
return check_opt_strings(p, p_ff_values, false);
}
-// Set the integer values corresponding to the string setting of 'vartabstop'.
-// "array" will be set, caller must free it if needed.
-// Return false for an error.
-bool tabstop_set(char_u *var, long **array)
-{
- long valcount = 1;
- int t;
- char_u *cp;
-
- if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
- *array = NULL;
- return true;
- }
-
- for (cp = var; *cp != NUL; cp++) {
- if (cp == var || cp[-1] == ',') {
- char *end;
-
- if (strtol((char *)cp, &end, 10) <= 0) {
- if (cp != (char_u *)end) {
- emsg(_(e_positive));
- } else {
- semsg(_(e_invarg2), cp);
- }
- return false;
- }
- }
-
- if (ascii_isdigit(*cp)) {
- continue;
- }
- if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
- valcount++;
- continue;
- }
- semsg(_(e_invarg2), var);
- return false;
- }
-
- *array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
- (*array)[0] = valcount;
-
- t = 1;
- for (cp = var; *cp != NUL;) {
- int n = atoi((char *)cp);
-
- // Catch negative values, overflow and ridiculous big values.
- if (n <= 0 || n > TABSTOP_MAX) {
- semsg(_(e_invarg2), cp);
- XFREE_CLEAR(*array);
- return false;
- }
- (*array)[t++] = n;
- while (*cp != NUL && *cp != ',') {
- cp++;
- }
- if (*cp != NUL) {
- cp++;
- }
- }
-
- return true;
-}
-
-// Calculate the number of screen spaces a tab will occupy.
-// If "vts" is set then the tab widths are taken from that array,
-// otherwise the value of ts is used.
-int tabstop_padding(colnr_T col, long ts_arg, long *vts)
-{
- long ts = ts_arg == 0 ? 8 : ts_arg;
- colnr_T tabcol = 0;
- int t;
- long padding = 0;
-
- if (vts == NULL || vts[0] == 0) {
- return (int)(ts - (col % ts));
- }
-
- const long tabcount = vts[0];
-
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > col) {
- padding = tabcol - col;
- break;
- }
- }
- if (t > tabcount) {
- padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
- }
-
- return (int)padding;
-}
-
-// Find the size of the tab that covers a particular column.
-int tabstop_at(colnr_T col, long ts, long *vts)
-{
- colnr_T tabcol = 0;
- int t;
- long tab_size = 0;
-
- if (vts == NULL || vts[0] == 0) {
- return (int)ts;
- }
-
- const long tabcount = vts[0];
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > col) {
- tab_size = vts[t];
- break;
- }
- }
- if (t > tabcount) {
- tab_size = vts[tabcount];
- }
-
- return (int)tab_size;
-}
-
-// Find the column on which a tab starts.
-colnr_T tabstop_start(colnr_T col, long ts, long *vts)
-{
- colnr_T tabcol = 0;
- int t;
-
- if (vts == NULL || vts[0] == 0) {
- return (int)((col / ts) * ts);
- }
-
- const long tabcount = vts[0];
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > col) {
- return (int)(tabcol - vts[t]);
- }
- }
-
- const int excess = (int)(tabcol % vts[tabcount]);
- return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
-}
-
-// Find the number of tabs and spaces necessary to get from one column
-// to another.
-void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
- int *nspcs)
-{
- int spaces = end_col - start_col;
- colnr_T tabcol = 0;
- long padding = 0;
- int t;
- long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
-
- if (vts == NULL || vts[0] == 0) {
- int tabs = 0;
-
- const int initspc = (int)(ts - (start_col % ts));
- if (spaces >= initspc) {
- spaces -= initspc;
- tabs++;
- }
- tabs += (int)(spaces / ts);
- spaces -= (int)((spaces / ts) * ts);
-
- *ntabs = tabs;
- *nspcs = spaces;
- return;
- }
-
- // Find the padding needed to reach the next tabstop.
- const long tabcount = vts[0];
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > start_col) {
- padding = tabcol - start_col;
- break;
- }
- }
- if (t > tabcount) {
- padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
- }
-
- // If the space needed is less than the padding no tabs can be used.
- if (spaces < padding) {
- *ntabs = 0;
- *nspcs = spaces;
- return;
- }
-
- *ntabs = 1;
- spaces -= (int)padding;
-
- // At least one tab has been used. See if any more will fit.
- while (spaces != 0 && ++t <= tabcount) {
- padding = vts[t];
- if (spaces < padding) {
- *nspcs = spaces;
- return;
- }
- *ntabs += 1;
- spaces -= (int)padding;
- }
-
- *ntabs += spaces / (int)vts[tabcount];
- *nspcs = spaces % (int)vts[tabcount];
-}
-
-// See if two tabstop arrays contain the same values.
-bool tabstop_eq(long *ts1, long *ts2)
-{
- int t;
-
- if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
- return false;
- }
- if (ts1 == ts2) {
- return true;
- }
- if (ts1[0] != ts2[0]) {
- return false;
- }
-
- for (t = 1; t <= ts1[0]; t++) {
- if (ts1[t] != ts2[t]) {
- return false;
- }
- }
-
- return true;
-}
-
-// Copy a tabstop array, allocating space for the new array.
-int *tabstop_copy(long *oldts)
-{
- long *newts;
- int t;
-
- if (oldts == 0) {
- return 0;
- }
-
- newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
- for (t = 0; t <= oldts[0]; t++) {
- newts[t] = oldts[t];
- }
-
- return (int *)newts;
-}
-
-// Return a count of the number of tabstops.
-int tabstop_count(long *ts)
-{
- return ts != NULL ? (int)ts[0] : 0;
-}
-
-// Return the first tabstop, or 8 if there are no tabstops defined.
-int tabstop_first(long *ts)
-{
- return ts != NULL ? (int)ts[1] : 8;
-}
-
-/// Return the effective shiftwidth value for current buffer, using the
-/// 'tabstop' value when 'shiftwidth' is zero.
-int get_sw_value(buf_T *buf)
-{
- long result = get_sw_value_col(buf, 0);
- assert(result >= 0 && result <= INT_MAX);
- return (int)result;
-}
-
-// Idem, using the first non-black in the current line.
-long get_sw_value_indent(buf_T *buf)
-{
- pos_T pos = curwin->w_cursor;
-
- pos.col = (colnr_T)getwhitecols_curline();
- return get_sw_value_pos(buf, &pos);
-}
-
-// Idem, using "pos".
-long get_sw_value_pos(buf_T *buf, pos_T *pos)
-{
- pos_T save_cursor = curwin->w_cursor;
- long sw_value;
-
- curwin->w_cursor = *pos;
- sw_value = get_sw_value_col(buf, get_nolist_virtcol());
- curwin->w_cursor = save_cursor;
- return sw_value;
-}
-
-// Idem, using virtual column "col".
-long get_sw_value_col(buf_T *buf, colnr_T col)
-{
- return buf->b_p_sw ? buf->b_p_sw
- : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
-}
-
-/// Return the effective softtabstop value for the current buffer,
-/// using the shiftwidth value when 'softtabstop' is negative.
-int get_sts_value(void)
-{
- long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
- assert(result >= 0 && result <= INT_MAX);
- return (int)result;
-}
-
/// This is called when 'breakindentopt' is changed and when a window is
/// initialized
static bool briopt_check(win_T *wp)
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index ca6879aea1..0d62a5f5f9 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -144,25 +144,6 @@ bool os_isdir(const char_u *name)
return true;
}
-/// Check if the given path is a directory and is executable.
-/// Gives the same results as `os_isdir()` on Windows.
-///
-/// @return `true` if `name` is a directory and executable.
-bool os_isdir_executable(const char *name)
- FUNC_ATTR_NONNULL_ALL
-{
- int32_t mode = os_getperm(name);
- if (mode < 0) {
- return false;
- }
-
-#ifdef WIN32
- return (S_ISDIR(mode));
-#else
- return (S_ISDIR(mode) && (S_IXUSR & mode));
-#endif
-}
-
/// Check what `name` is:
/// @return NODE_NORMAL: file or directory (or doesn't exist)
/// NODE_WRITABLE: writable device, socket, fifo, etc.
diff --git a/src/nvim/os/pty_conpty_win.h b/src/nvim/os/pty_conpty_win.h
index c243db4fa5..15e7c3da0c 100644
--- a/src/nvim/os/pty_conpty_win.h
+++ b/src/nvim/os/pty_conpty_win.h
@@ -1,6 +1,9 @@
#ifndef NVIM_OS_PTY_CONPTY_WIN_H
#define NVIM_OS_PTY_CONPTY_WIN_H
+#include "nvim/lib/kvec.h"
+#include "nvim/os/input.h"
+
#ifndef HPCON
# define HPCON VOID *
#endif
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 15b67cf35b..caea11debd 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -227,7 +227,7 @@ char_u *get_past_head(const char_u *path)
#endif
while (vim_ispathsep(*retval)) {
- ++retval;
+ retval++;
}
return (char_u *)retval;
@@ -666,8 +666,8 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
for (p = buf + wildoff; p < s; ++p) {
if (rem_backslash(p)) {
STRMOVE(p, p + 1);
- --e;
- --s;
+ e--;
+ s--;
}
}
@@ -695,11 +695,11 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
regmatch.rm_ic = true; // Always ignore case on Windows.
#endif
if (flags & (EW_NOERROR | EW_NOTWILD)) {
- ++emsg_silent;
+ emsg_silent++;
}
regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
if (flags & (EW_NOERROR | EW_NOTWILD)) {
- --emsg_silent;
+ emsg_silent--;
}
xfree(pat);
@@ -742,9 +742,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
* find matches. */
STRCPY(buf + len, "/**");
STRCPY(buf + len + 3, path_end);
- ++stardepth;
+ stardepth++;
(void)do_path_expand(gap, buf, len + 1, flags, true);
- --stardepth;
+ stardepth--;
}
STRCPY(buf + len, path_end);
@@ -1401,7 +1401,7 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
cmd = skipwhite(cmd); // skip over white space
p = cmd;
while (*p != NUL && *p != '\r' && *p != '\n') { // skip over entry
- ++p;
+ p++;
}
// add an entry if it is not empty
if (p > cmd) {
@@ -1409,11 +1409,11 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
*p = NUL;
addfile(gap, (char_u *)cmd, flags);
*p = i;
- ++cnt;
+ cnt++;
}
cmd = p;
while (*cmd != NUL && (*cmd == '\r' || *cmd == '\n')) {
- ++cmd;
+ cmd++;
}
}
@@ -1656,12 +1656,12 @@ void simplify_filename(char_u *filename)
*p = NUL;
} else {
if (p > start && tail[-1] == '.') {
- --p;
+ p--;
}
STRMOVE(p, tail); // strip previous component
}
- --components;
+ components--;
}
} else if (p == start && !relative) { // leading "/.." or "/../"
STRMOVE(p, tail); // strip ".." or "../"
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmenu.c
index 6cab847528..a4afe97ac8 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmenu.c
@@ -1,7 +1,7 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/// @file popupmnu.c
+/// @file popupmenu.c
///
/// Popup menu (PUM)
@@ -13,17 +13,19 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
#include "nvim/insexpand.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -55,7 +57,7 @@ static bool pum_external = false;
static bool pum_invalid = false; // the screen was just cleared
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "popupmnu.c.generated.h"
+# include "popupmenu.c.generated.h"
#endif
#define PUM_DEF_HEIGHT 10
@@ -463,7 +465,7 @@ void pum_redraw(void)
/ (pum_size - pum_height);
}
- for (i = 0; i < pum_height; ++i) {
+ for (i = 0; i < pum_height; i++) {
idx = i + pum_first;
attr = (idx == pum_selected) ? attr_select : attr_norm;
@@ -483,7 +485,7 @@ void pum_redraw(void)
grid_col = col_off;
totwidth = 0;
- for (round = 1; round <= 3; ++round) {
+ for (round = 1; round <= 3; round++) {
width = 0;
s = NULL;
@@ -639,11 +641,11 @@ void pum_redraw(void)
/// @param n
/// @param repeat
///
-/// @returns TRUE when the window was resized and the location of the popup
+/// @returns true when the window was resized and the location of the popup
/// menu must be recomputed.
-static int pum_set_selected(int n, int repeat)
+static bool pum_set_selected(int n, int repeat)
{
- int resized = FALSE;
+ int resized = false;
int context = pum_height / 2;
pum_selected = n;
@@ -776,12 +778,12 @@ static int pum_set_selected(int n, int repeat)
if (curwin->w_height < lnum) {
win_setheight((int)lnum);
- resized = TRUE;
+ resized = true;
}
}
curbuf->b_changed = false;
- curbuf->b_p_ma = FALSE;
+ curbuf->b_p_ma = false;
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
@@ -795,7 +797,7 @@ static int pum_set_selected(int n, int repeat)
// window is not resized, skip the preview window's
// status line redrawing.
if (ins_compl_active() && !resized) {
- curwin->w_redr_status = FALSE;
+ curwin->w_redr_status = false;
}
// Return cursor to where we were
@@ -904,7 +906,7 @@ void pum_recompose(void)
/// Gets the height of the menu.
///
/// @return the height of the popup menu, the number of entries visible.
-/// Only valid when pum_visible() returns TRUE!
+/// Only valid when pum_visible() returns true!
int pum_get_height(void)
{
if (pum_external) {
diff --git a/src/nvim/popupmnu.h b/src/nvim/popupmenu.h
index 7d3f4c6f51..851ad31486 100644
--- a/src/nvim/popupmnu.h
+++ b/src/nvim/popupmenu.h
@@ -1,5 +1,5 @@
-#ifndef NVIM_POPUPMNU_H
-#define NVIM_POPUPMNU_H
+#ifndef NVIM_POPUPMENU_H
+#define NVIM_POPUPMENU_H
#include "nvim/grid_defs.h"
#include "nvim/macros.h"
@@ -17,6 +17,6 @@ typedef struct {
EXTERN ScreenGrid pum_grid INIT(= SCREEN_GRID_INIT);
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "popupmnu.h.generated.h"
+# include "popupmenu.h.generated.h"
#endif
-#endif // NVIM_POPUPMNU_H
+#endif // NVIM_POPUPMENU_H
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index d50388b9cf..1c416a872b 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -9,10 +9,12 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
@@ -22,6 +24,7 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -37,7 +40,6 @@
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -5779,7 +5781,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list)
/// If qf_idx is -1, use the current list. Otherwise, use the specified list.
/// If eidx is not 0, then return only the specified entry. Otherwise return
/// all the entries.
-int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list)
+static int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list)
{
qf_info_T *qi = qi_arg;
@@ -6149,7 +6151,7 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
/// Return quickfix/location list details (title) as a dictionary.
/// 'what' contains the details to return. If 'list_idx' is -1,
/// then current list is used. Otherwise the specified list is used.
-int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
+static int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
{
qf_info_T *qi = &ql_info;
dictitem_T *di = NULL;
@@ -7157,3 +7159,137 @@ void ex_helpgrep(exarg_T *eap)
}
}
}
+
+static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
+{
+ if (what_arg->v_type == VAR_UNKNOWN) {
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ if (is_qf || wp != NULL) {
+ (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
+ }
+ } else {
+ tv_dict_alloc_ret(rettv);
+ if (is_qf || wp != NULL) {
+ if (what_arg->v_type == VAR_DICT) {
+ dict_T *d = what_arg->vval.v_dict;
+
+ if (d != NULL) {
+ qf_get_properties(wp, d, rettv->vval.v_dict);
+ }
+ } else {
+ emsg(_(e_dictreq));
+ }
+ }
+ }
+}
+
+/// "getloclist()" function
+void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ get_qf_loc_list(false, wp, &argvars[1], rettv);
+}
+
+/// "getqflist()" functions
+void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ get_qf_loc_list(true, NULL, &argvars[0], rettv);
+}
+
+/// Create quickfix/location list from VimL values
+///
+/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
+/// args argument in which case errors out, including VAR_UNKNOWN parameters.
+///
+/// @param[in,out] wp Window to create location list for. May be NULL in
+/// which case quickfix list will be created.
+/// @param[in] args [list, action, what]
+/// @param[in] args[0] Quickfix list contents.
+/// @param[in] args[1] Optional. Action to perform:
+/// append to an existing list, replace its content,
+/// or create a new one.
+/// @param[in] args[2] Optional. Quickfix list properties or title.
+/// Defaults to caller function name.
+/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
+static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ARG(2, 3)
+{
+ static char *e_invact = N_("E927: Invalid action: '%s'");
+ const char *title = NULL;
+ char action = ' ';
+ static int recursive = 0;
+ rettv->vval.v_number = -1;
+ dict_T *what = NULL;
+
+ typval_T *list_arg = &args[0];
+ if (list_arg->v_type != VAR_LIST) {
+ emsg(_(e_listreq));
+ return;
+ } else if (recursive != 0) {
+ emsg(_(e_au_recursive));
+ return;
+ }
+
+ typval_T *action_arg = &args[1];
+ if (action_arg->v_type == VAR_UNKNOWN) {
+ // Option argument was not given.
+ goto skip_args;
+ } else if (action_arg->v_type != VAR_STRING) {
+ emsg(_(e_stringreq));
+ return;
+ }
+ const char *const act = tv_get_string_chk(action_arg);
+ if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
+ && act[1] == NUL) {
+ action = *act;
+ } else {
+ semsg(_(e_invact), act);
+ return;
+ }
+
+ typval_T *const what_arg = &args[2];
+ if (what_arg->v_type == VAR_UNKNOWN) {
+ // Option argument was not given.
+ goto skip_args;
+ } else if (what_arg->v_type == VAR_STRING) {
+ title = tv_get_string_chk(what_arg);
+ if (!title) {
+ // Type error. Error already printed by tv_get_string_chk().
+ return;
+ }
+ } else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
+ what = what_arg->vval.v_dict;
+ } else {
+ emsg(_(e_dictreq));
+ return;
+ }
+
+skip_args:
+ if (!title) {
+ title = (wp ? ":setloclist()" : ":setqflist()");
+ }
+
+ recursive++;
+ list_T *const l = list_arg->vval.v_list;
+ if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
+ rettv->vval.v_number = 0;
+ }
+ recursive--;
+}
+
+/// "setloclist()" function
+void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = -1;
+
+ win_T *win = find_win_by_nr_or_id(&argvars[0]);
+ if (win != NULL) {
+ set_qf_ll_list(win, &argvars[1], rettv);
+ }
+}
+
+/// "setqflist()" function
+void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ set_qf_ll_list(NULL, argvars, rettv);
+}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 4d0bf03d8b..b7ec4bf94e 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -821,7 +821,7 @@ static int64_t gethexchrs(int maxinputlen)
}
nr <<= 4;
nr |= hex2nr(c);
- ++regparse;
+ regparse++;
}
if (i == 0) {
@@ -878,7 +878,7 @@ static int64_t getoctchrs(void)
}
nr <<= 3;
nr |= hex2nr(c);
- ++regparse;
+ regparse++;
}
if (i == 0) {
@@ -2095,8 +2095,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
dst++;
}
- ++s;
- --len;
+ s++;
+ len--;
}
}
}
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index f79a772795..769d2ceeef 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -2189,7 +2189,7 @@ collection:
while (*regparse != NUL && *regparse != ']') {
if (*regparse == '-') {
- ++regparse;
+ regparse++;
// The '-' is not used for a range at the end and
// after or before a '\n'.
if (*regparse == ']' || *regparse == NUL
@@ -2619,7 +2619,7 @@ static char_u *regpiece(int *flagp)
regoptail(ret, regnode(BACK));
regoptail(ret, ret);
reginsert_limits(BRACE_LIMITS, minval, maxval, ret);
- ++num_complex_braces;
+ num_complex_braces++;
}
if (minval > 0 && maxval > 0) {
*flagp = (HASWIDTH | (flags & (HASNL | HASLOOKBH)));
@@ -2792,7 +2792,7 @@ static char_u *reg(int paren, int *flagp)
EMSG2_RET_NULL(_("E51: Too many %s("), reg_magic == MAGIC_ALL);
}
parno = regnpar;
- ++regnpar;
+ regnpar++;
ret = regnode(MOPEN + parno);
} else if (paren == REG_NPAREN) {
// Make a NOPEN node.
@@ -3181,7 +3181,7 @@ static int regrepeat(char_u *p, long maxcount)
} else {
break;
}
- ++count;
+ count++;
}
break;
@@ -3299,7 +3299,7 @@ do_class:
} else {
break;
}
- ++count;
+ count++;
}
break;
@@ -3415,7 +3415,7 @@ do_class:
break;
}
scan += len;
- ++count;
+ count++;
}
}
}
@@ -3453,7 +3453,7 @@ do_class:
}
scan++;
}
- ++count;
+ count++;
}
break;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 7f16373280..554def5b8a 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -5488,7 +5488,7 @@ static void nfa_save_listids(nfa_regprog_T *prog, int *list)
for (i = prog->nstate; --i >= 0;) {
list[i] = p->lastlist[1];
p->lastlist[1] = 0;
- ++p;
+ p++;
}
}
@@ -5503,7 +5503,7 @@ static void nfa_restore_listids(nfa_regprog_T *prog, int *list)
p = &prog->state[0];
for (i = prog->nstate; --i >= 0;) {
p->lastlist[1] = list[i];
- ++p;
+ p++;
}
}
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 4eb38c2c9e..edcaa27e2b 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -1941,9 +1941,6 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.level = ex_nesting_level;
- // Keep the sourcing name/lnum, for recursive calls.
- estack_push(ETYPE_SCRIPT, fname_exp, 0);
-
// start measuring script load time if --startuptime was passed and
// time_fd was successfully opened afterwards.
proftime_T rel_time;
@@ -1966,6 +1963,9 @@ int do_source(char *fname, int check_other, int is_vimrc)
const sctx_T save_current_sctx = current_sctx;
si = get_current_script_id(&fname_exp, &current_sctx);
+ // Keep the sourcing name/lnum, for recursive calls.
+ estack_push(ETYPE_SCRIPT, (char *)si->sn_name, 0);
+
if (l_do_profiling == PROF_YES) {
bool forceit = false;
@@ -1983,30 +1983,27 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.conv.vc_type = CONV_NONE; // no conversion
- // Read the first line so we can check for a UTF-8 BOM.
- firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true);
- if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
- && firstline[1] == 0xbb && firstline[2] == 0xbf) {
- // Found BOM; setup conversion, skip over BOM and recode the line.
- convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
- p = (char *)string_convert(&cookie.conv, (char_u *)firstline + 3, NULL);
- if (p == NULL) {
- p = xstrdup((char *)firstline + 3);
- }
- xfree(firstline);
- firstline = (uint8_t *)p;
- }
-
if (path_with_extension((const char *)fname_exp, "lua")) {
const sctx_T current_sctx_backup = current_sctx;
current_sctx.sc_sid = SID_LUA;
current_sctx.sc_lnum = 0;
- estack_push(ETYPE_SCRIPT, NULL, 0);
// Source the file as lua
nlua_exec_file((const char *)fname_exp);
current_sctx = current_sctx_backup;
- estack_pop();
} else {
+ // Read the first line so we can check for a UTF-8 BOM.
+ firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true);
+ if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
+ && firstline[1] == 0xbb && firstline[2] == 0xbf) {
+ // Found BOM; setup conversion, skip over BOM and recode the line.
+ convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
+ p = (char *)string_convert(&cookie.conv, (char_u *)firstline + 3, NULL);
+ if (p == NULL) {
+ p = xstrdup((char *)firstline + 3);
+ }
+ xfree(firstline);
+ firstline = (uint8_t *)p;
+ }
// Call do_cmdline, which will call getsourceline() to get the lines.
do_cmdline((char *)firstline, getsourceline, (void *)&cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 609c2e3017..2419a42a2a 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1,616 +1,52 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-// screen.c: code for displaying on the screen
-//
+// screen.c: Lower level code for displaying on the screen.
+// grid.c contains some other lower-level code.
+
// Output to the screen (console, terminal emulator or GUI window) is minimized
// by remembering what is already on the screen, and only updating the parts
// that changed.
-//
-// The grid_*() functions write to the screen and handle updating grid->lines[].
-//
-// update_screen() is the function that updates all windows and status lines.
-// It is called from the main loop when must_redraw is non-zero. It may be
-// called from other places when an immediate screen update is needed.
-//
-// The part of the buffer that is displayed in a window is set with:
-// - w_topline (first buffer line in window)
-// - w_topfill (filler lines above the first line)
-// - w_leftcol (leftmost window cell in window),
-// - w_skipcol (skipped window cells of first line)
-//
-// Commands that only move the cursor around in a window, do not need to take
-// action to update the display. The main loop will check if w_topline is
-// valid and update it (scroll the window) when needed.
-//
-// Commands that scroll a window change w_topline and must call
-// check_cursor() to move the cursor into the visible part of the window, and
-// call redraw_later(wp, VALID) to have the window displayed by update_screen()
-// later.
-//
-// Commands that change text in the buffer must call changed_bytes() or
-// changed_lines() to mark the area that changed and will require updating
-// later. The main loop will call update_screen(), which will update each
-// window that shows the changed buffer. This assumes text above the change
-// can remain displayed as it is. Text after the change may need updating for
-// scrolling, folding and syntax highlighting.
-//
-// Commands that change how a window is displayed (e.g., setting 'list') or
-// invalidate the contents of a window in another way (e.g., change fold
-// settings), must call redraw_later(wp, NOT_VALID) to have the whole window
-// redisplayed by update_screen() later.
-//
-// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
-// must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
-// buffer redisplayed by update_screen() later.
-//
-// Commands that change highlighting and possibly cause a scroll too must call
-// redraw_later(wp, SOME_VALID) to update the whole window but still use
-// scrolling to avoid redrawing everything. But the length of displayed lines
-// must not change, use NOT_VALID then.
-//
-// Commands that move the window position must call redraw_later(wp, NOT_VALID).
-// TODO(neovim): should minimize redrawing by scrolling when possible.
-//
-// Commands that change everything (e.g., resizing the screen) must call
-// redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
-//
-// Things that are handled indirectly:
-// - When messages scroll the screen up, msg_scrolled will be set and
-// update_screen() called to redraw.
-///
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
-#include "nvim/api/extmark.h"
-#include "nvim/api/private/helpers.h"
-#include "nvim/api/ui.h"
-#include "nvim/api/vim.h"
-#include "nvim/arabic.h"
-#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
-#include "nvim/cursor_shape.h"
-#include "nvim/decoration.h"
-#include "nvim/decoration_provider.h"
-#include "nvim/diff.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
-#include "nvim/indent.h"
-#include "nvim/insexpand.h"
-#include "nvim/lib/kvec.h"
-#include "nvim/log.h"
-#include "nvim/lua/executor.h"
-#include "nvim/main.h"
-#include "nvim/mark.h"
-#include "nvim/match.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
#include "nvim/menu.h"
-#include "nvim/message.h"
#include "nvim/move.h"
-#include "nvim/normal.h"
#include "nvim/option.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
#include "nvim/profile.h"
-#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
-#include "nvim/sign.h"
-#include "nvim/spell.h"
#include "nvim/state.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/terminal.h"
-#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
-#include "nvim/version.h"
-#include "nvim/vim.h"
#include "nvim/window.h"
-#define MB_FILLER_CHAR '<' // character used when a double-width character
- // doesn't fit.
-
-static match_T search_hl; // used for 'hlsearch' highlight matching
-
-// for line_putchar. Contains the state that needs to be remembered from
-// putting one character to the next.
-typedef struct {
- const char *p;
- int prev_c; // previous Arabic character
- int prev_c1; // first composing char for prev_c
-} LineState;
-#define LINE_STATE(p) { p, 0, 0 }
-
-/// Whether to call "ui_call_grid_resize" in win_grid_alloc
-static bool send_grid_resize = false;
-
-static bool conceal_cursor_used = false;
-
-static bool redraw_popupmenu = false;
-static bool msg_grid_invalid = false;
-
-static bool resizing = false;
-
-typedef struct {
- NS ns_id;
- uint64_t mark_id;
- int win_row;
- int win_col;
-} WinExtmark;
-static kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h"
#endif
-static char *provider_err = NULL;
-
-/// Redraw a window later, with update_screen(type).
-///
-/// Set must_redraw only if not already set to a higher value.
-/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
-void redraw_later(win_T *wp, int type)
- FUNC_ATTR_NONNULL_ALL
-{
- if (!exiting && wp->w_redr_type < type) {
- wp->w_redr_type = type;
- if (type >= NOT_VALID) {
- wp->w_lines_valid = 0;
- }
- if (must_redraw < type) { // must_redraw is the maximum of all windows
- must_redraw = type;
- }
- }
-}
-
-/// Mark all windows to be redrawn later.
-void redraw_all_later(int type)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, type);
- }
- // This may be needed when switching tabs.
- if (must_redraw < type) {
- must_redraw = type;
- }
-}
-
-void screen_invalidate_highlights(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, NOT_VALID);
- wp->w_grid_alloc.valid = false;
- }
-}
-
-/// Mark all windows that are editing the current buffer to be updated later.
-void redraw_curbuf_later(int type)
-{
- redraw_buf_later(curbuf, type);
-}
-
-void redraw_buf_later(buf_T *buf, int type)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf) {
- redraw_later(wp, type);
- }
- }
-}
-
-void redraw_buf_line_later(buf_T *buf, linenr_T line)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && line >= wp->w_topline && line < wp->w_botline) {
- redrawWinline(wp, line);
- }
- }
-}
-
-void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && lastline >= wp->w_topline && firstline < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) {
- wp->w_redraw_top = firstline;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
- wp->w_redraw_bot = lastline;
- }
- redraw_later(wp, VALID);
- }
- }
-}
-
-/// Changed something in the current window, at buffer line "lnum", that
-/// requires that line and possibly other lines to be redrawn.
-/// Used when entering/leaving Insert mode with the cursor on a folded line.
-/// Used to remove the "$" from a change command.
-/// Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
-/// may become invalid and the whole window will have to be redrawn.
-void redrawWinline(win_T *wp, linenr_T lnum)
- FUNC_ATTR_NONNULL_ALL
-{
- if (lnum >= wp->w_topline
- && lnum < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
- wp->w_redraw_top = lnum;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
- wp->w_redraw_bot = lnum;
- }
- redraw_later(wp, VALID);
- }
-}
-
-/// called when the status bars for the buffer 'buf' need to be updated
-void redraw_buf_status_later(buf_T *buf)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf && (wp->w_status_height || (wp == curwin && global_stl_height())
- || wp->w_winbar_height)) {
- wp->w_redr_status = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
- }
- }
- }
-}
-
-void redraw_win_signcol(win_T *wp)
-{
- // If we can compute a change in the automatic sizing of the sign column
- // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
- // figuring it out here so we can redraw the entire screen for it.
- int scwidth = wp->w_scwidth;
- wp->w_scwidth = win_signcol_count(wp);
- if (wp->w_scwidth != scwidth) {
- changed_line_abv_curs_win(wp);
- }
-}
-
-/// Update all windows that are editing the current buffer.
-void update_curbuf(int type)
-{
- redraw_curbuf_later(type);
- update_screen(type);
-}
-
-/// Redraw the parts of the screen that is marked for redraw.
-///
-/// Most code shouldn't call this directly, rather use redraw_later() and
-/// and redraw_all_later() to mark parts of the screen as needing a redraw.
-///
-/// @param type set to a NOT_VALID to force redraw of entire screen
-int update_screen(int type)
-{
- static bool did_intro = false;
- bool is_stl_global = global_stl_height() > 0;
-
- // Don't do anything if the screen structures are (not yet) valid.
- // A VimResized autocmd can invoke redrawing in the middle of a resize,
- // which would bypass the checks in screen_resize for popupmenu etc.
- if (!default_grid.chars || resizing) {
- return FAIL;
- }
-
- // May have postponed updating diffs.
- if (need_diff_redraw) {
- diff_redraw(true);
- }
-
- if (must_redraw) {
- if (type < must_redraw) { // use maximal type
- type = must_redraw;
- }
-
- // must_redraw is reset here, so that when we run into some weird
- // reason to redraw while busy redrawing (e.g., asynchronous
- // scrolling), or update_topline() in win_update() will cause a
- // scroll, or a decoration provider requires a redraw, the screen
- // will be redrawn later or in win_update().
- must_redraw = 0;
- }
-
- // Need to update w_lines[].
- if (curwin->w_lines_valid == 0 && type < NOT_VALID) {
- type = NOT_VALID;
- }
-
- // Postpone the redrawing when it's not needed and when being called
- // recursively.
- if (!redrawing() || updating_screen) {
- must_redraw = type;
- if (type > INVERTED_ALL) {
- curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
- }
- return FAIL;
- }
- updating_screen = 1;
-
- display_tick++; // let syntax code know we're in a next round of
- // display updating
-
- // Tricky: vim code can reset msg_scrolled behind our back, so need
- // separate bookkeeping for now.
- if (msg_did_scroll) {
- msg_did_scroll = false;
- msg_scrolled_at_flush = 0;
- }
-
- if (type >= CLEAR || !default_grid.valid) {
- ui_comp_set_screen_valid(false);
- }
+static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'");
+static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'");
- // if the screen was scrolled up when displaying a message, scroll it down
- if (msg_scrolled || msg_grid_invalid) {
- clear_cmdline = true;
- int valid = MAX(Rows - msg_scrollsize(), 0);
- if (msg_grid.chars) {
- // non-displayed part of msg_grid is considered invalid.
- for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
- grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
- }
- }
- if (msg_use_msgsep()) {
- msg_grid.throttled = false;
- // CLEAR is already handled
- if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
- ui_comp_set_screen_valid(false);
- for (int i = valid; i < Rows - p_ch; i++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- Columns, false);
- }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (W_ENDROW(wp) > valid) {
- wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID);
- }
- if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
- wp->w_redr_status = true;
- }
- }
- if (is_stl_global && Rows - p_ch - 1 > valid) {
- curwin->w_redr_status = true;
- }
- }
- msg_grid_set_pos(Rows - (int)p_ch, false);
- msg_grid_invalid = false;
- } else if (msg_scrolled > Rows - 5) { // clearing is faster
- type = CLEAR;
- } else if (type != CLEAR) {
- check_for_delay(false);
- grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (wp->w_winrow < msg_scrolled) {
- if (W_ENDROW(wp) > msg_scrolled
- && wp->w_redr_type < REDRAW_TOP
- && wp->w_lines_valid > 0
- && wp->w_topline == wp->w_lines[0].wl_lnum) {
- wp->w_upd_rows = msg_scrolled - wp->w_winrow;
- wp->w_redr_type = REDRAW_TOP;
- } else {
- wp->w_redr_type = NOT_VALID;
- if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
- wp->w_redr_status = true;
- }
- }
- }
- }
- if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
- curwin->w_redr_status = true;
- }
- redraw_cmdline = true;
- redraw_tabline = true;
- }
- msg_scrolled = 0;
- msg_scrolled_at_flush = 0;
- need_wait_return = false;
- }
-
- win_ui_flush();
- msg_ext_check_clear();
-
- // reset cmdline_row now (may have been changed temporarily)
- compute_cmdrow();
-
- bool hl_changed = false;
- // Check for changed highlighting
- if (need_highlight_changed) {
- highlight_changed();
- hl_changed = true;
- }
-
- if (type == CLEAR) { // first clear screen
- screenclear(); // will reset clear_cmdline
- cmdline_screen_cleared(); // clear external cmdline state
- type = NOT_VALID;
- // must_redraw may be set indirectly, avoid another redraw later
- must_redraw = 0;
- } else if (!default_grid.valid) {
- grid_invalidate(&default_grid);
- default_grid.valid = true;
- }
-
- // After disabling msgsep the grid might not have been deallocated yet,
- // hence we also need to check msg_grid.chars
- if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) {
- grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
- }
-
- ui_comp_set_screen_valid(true);
-
- DecorProviders providers;
- decor_providers_start(&providers, type, &provider_err);
-
- // "start" callback could have changed highlights for global elements
- if (win_check_ns_hl(NULL)) {
- redraw_cmdline = true;
- redraw_tabline = true;
- }
-
- if (clear_cmdline) { // going to clear cmdline (done below)
- check_for_delay(false);
- }
-
- // Force redraw when width of 'number' or 'relativenumber' column
- // changes.
- if (curwin->w_redr_type < NOT_VALID
- && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
- ? number_width(curwin) : 0)) {
- curwin->w_redr_type = NOT_VALID;
- }
-
- // Only start redrawing if there is really something to do.
- if (type == INVERTED) {
- update_curswant();
- }
- if (curwin->w_redr_type < type
- && !((type == VALID
- && curwin->w_lines[0].wl_valid
- && curwin->w_topfill == curwin->w_old_topfill
- && curwin->w_botfill == curwin->w_old_botfill
- && curwin->w_topline == curwin->w_lines[0].wl_lnum)
- || (type == INVERTED
- && VIsual_active
- && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
- && curwin->w_old_visual_mode == VIsual_mode
- && (curwin->w_valid & VALID_VIRTCOL)
- && curwin->w_old_curswant == curwin->w_curswant))) {
- curwin->w_redr_type = type;
- }
-
- // Redraw the tab pages line if needed.
- if (redraw_tabline || type >= NOT_VALID) {
- update_window_hl(curwin, type >= NOT_VALID);
- FOR_ALL_TABS(tp) {
- if (tp != curtab) {
- update_window_hl(tp->tp_curwin, type >= NOT_VALID);
- }
- }
- draw_tabline();
- }
-
- // Correct stored syntax highlighting info for changes in each displayed
- // buffer. Each buffer must only be done once.
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- update_window_hl(wp, type >= NOT_VALID || hl_changed);
-
- buf_T *buf = wp->w_buffer;
- if (buf->b_mod_set) {
- if (buf->b_mod_tick_syn < display_tick
- && syntax_present(wp)) {
- syn_stack_apply_changes(buf);
- buf->b_mod_tick_syn = display_tick;
- }
-
- if (buf->b_mod_tick_decor < display_tick) {
- decor_providers_invoke_buf(buf, &providers, &provider_err);
- buf->b_mod_tick_decor = display_tick;
- }
- }
- }
-
- // Go from top to bottom through the windows, redrawing the ones that need
- // it.
- bool did_one = false;
- search_hl.rm.regprog = NULL;
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
- grid_invalidate(&wp->w_grid_alloc);
- wp->w_redr_type = NOT_VALID;
- }
-
- // reallocate grid if needed.
- win_grid_alloc(wp);
-
- if (wp->w_redr_border || wp->w_redr_type >= NOT_VALID) {
- win_redr_border(wp);
- }
-
- if (wp->w_redr_type != 0) {
- if (!did_one) {
- did_one = true;
- start_search_hl();
- }
- win_update(wp, &providers);
- }
-
- // redraw status line and window bar after the window to minimize cursor movement
- if (wp->w_redr_status) {
- win_redr_winbar(wp);
- win_redr_status(wp);
- }
- }
-
- end_search_hl();
-
- // May need to redraw the popup menu.
- if (pum_drawn() && must_redraw_pum) {
- pum_redraw();
- }
-
- // Reset b_mod_set flags. Going through all windows is probably faster
- // than going through all buffers (there could be many buffers).
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- wp->w_buffer->b_mod_set = false;
- }
-
- updating_screen = 0;
-
- // Clear or redraw the command line. Done last, because scrolling may
- // mess up the command line.
- if (clear_cmdline || redraw_cmdline) {
- showmode();
- }
-
- // May put up an introductory message when not editing a file
- if (!did_intro) {
- maybe_intro_message();
- }
- did_intro = true;
-
- decor_providers_invoke_end(&providers, &provider_err);
- kvi_destroy(providers);
-
- // either cmdline is cleared, not drawn or mode is last drawn
- cmdline_was_last_drawn = false;
- return OK;
-}
-
-// Return true if the cursor line in window "wp" may be concealed, according
-// to the 'concealcursor' option.
+/// Return true if the cursor line in window "wp" may be concealed, according
+/// to the 'concealcursor' option.
bool conceal_cursor_line(const win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
@@ -633,20 +69,6 @@ bool conceal_cursor_line(const win_T *wp)
return vim_strchr((char *)wp->w_p_cocu, c) != NULL;
}
-// Check if the cursor line needs to be redrawn because of 'concealcursor'.
-//
-// When cursor is moved at the same time, both lines will be redrawn regardless.
-void conceal_check_cursor_line(void)
-{
- bool should_conceal = conceal_cursor_line(curwin);
- if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- // Need to recompute cursor column, e.g., when starting Visual mode
- // without concealing.
- curs_columns(curwin, true);
- }
-}
-
/// Whether cursorline is drawn in a special way
///
/// If true, both old and new cursorline will need to be redrawn when moving cursor within windows.
@@ -656,1037 +78,6 @@ bool win_cursorline_standout(const win_T *wp)
return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
}
-/// Update a single window.
-///
-/// This may cause the windows below it also to be redrawn (when clearing the
-/// screen or scrolling lines).
-///
-/// How the window is redrawn depends on wp->w_redr_type. Each type also
-/// implies the one below it.
-/// NOT_VALID redraw the whole window
-/// SOME_VALID redraw the whole window but do scroll when possible
-/// REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID
-/// INVERTED redraw the changed part of the Visual area
-/// INVERTED_ALL redraw the whole Visual area
-/// VALID 1. scroll up/down to adjust for a changed w_topline
-/// 2. update lines at the top when scrolled down
-/// 3. redraw changed text:
-/// - if wp->w_buffer->b_mod_set set, update lines between
-/// b_mod_top and b_mod_bot.
-/// - if wp->w_redraw_top non-zero, redraw lines between
-/// wp->w_redraw_top and wp->w_redr_bot.
-/// - continue redrawing when syntax status is invalid.
-/// 4. if scrolled up, update lines at the bottom.
-/// This results in three areas that may need updating:
-/// top: from first row to top_end (when scrolled down)
-/// mid: from mid_start to mid_end (update inversion or changed text)
-/// bot: from bot_start to last row (when scrolled up)
-static void win_update(win_T *wp, DecorProviders *providers)
-{
- bool called_decor_providers = false;
-win_update_start:
- ;
- buf_T *buf = wp->w_buffer;
- int type;
- int top_end = 0; // Below last row of the top area that needs
- // updating. 0 when no top area updating.
- int mid_start = 999; // first row of the mid area that needs
- // updating. 999 when no mid area updating.
- int mid_end = 0; // Below last row of the mid area that needs
- // updating. 0 when no mid area updating.
- int bot_start = 999; // first row of the bot area that needs
- // updating. 999 when no bot area updating
- bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
- bool top_to_mod = false; // redraw above mod_top
-
- int row; // current window row to display
- linenr_T lnum; // current buffer lnum to display
- int idx; // current index in w_lines[]
- int srow; // starting row of the current line
-
- bool eof = false; // if true, we hit the end of the file
- bool didline = false; // if true, we finished the last line
- int i;
- long j;
- static bool recursive = false; // being called recursively
- const linenr_T old_botline = wp->w_botline;
- // Remember what happened to the previous line.
-#define DID_NONE 1 // didn't update a line
-#define DID_LINE 2 // updated a normal line
-#define DID_FOLD 3 // updated a folded line
- int did_update = DID_NONE;
- linenr_T syntax_last_parsed = 0; // last parsed text line
- linenr_T mod_top = 0;
- linenr_T mod_bot = 0;
- int save_got_int;
-
- type = wp->w_redr_type;
-
- if (type >= NOT_VALID) {
- wp->w_redr_status = true;
- wp->w_lines_valid = 0;
- }
-
- // Window is zero-height: Only need to draw the separator
- if (wp->w_grid.rows == 0) {
- // draw the horizontal separator below this window
- draw_hsep_win(wp);
- draw_sep_connectors_win(wp);
- wp->w_redr_type = 0;
- return;
- }
-
- // Window is zero-width: Only need to draw the separator.
- if (wp->w_grid.cols == 0) {
- // draw the vertical separator right of this window
- draw_vsep_win(wp);
- draw_sep_connectors_win(wp);
- wp->w_redr_type = 0;
- return;
- }
-
- redraw_win_signcol(wp);
-
- init_search_hl(wp, &search_hl);
-
- // Force redraw when width of 'number' or 'relativenumber' column
- // changes.
- i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
- if (wp->w_nrwidth != i) {
- type = NOT_VALID;
- wp->w_nrwidth = i;
-
- if (buf->terminal) {
- terminal_check_size(buf->terminal);
- }
- } else if (buf->b_mod_set
- && buf->b_mod_xlines != 0
- && wp->w_redraw_top != 0) {
- // When there are both inserted/deleted lines and specific lines to be
- // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
- // everything (only happens when redrawing is off for while).
- type = NOT_VALID;
- } else {
- // Set mod_top to the first line that needs displaying because of
- // changes. Set mod_bot to the first line after the changes.
- mod_top = wp->w_redraw_top;
- if (wp->w_redraw_bot != 0) {
- mod_bot = wp->w_redraw_bot + 1;
- } else {
- mod_bot = 0;
- }
- if (buf->b_mod_set) {
- if (mod_top == 0 || mod_top > buf->b_mod_top) {
- mod_top = buf->b_mod_top;
- // Need to redraw lines above the change that may be included
- // in a pattern match.
- if (syntax_present(wp)) {
- mod_top -= buf->b_s.b_syn_sync_linebreaks;
- if (mod_top < 1) {
- mod_top = 1;
- }
- }
- }
- if (mod_bot == 0 || mod_bot < buf->b_mod_bot) {
- mod_bot = buf->b_mod_bot;
- }
-
- // When 'hlsearch' is on and using a multi-line search pattern, a
- // change in one line may make the Search highlighting in a
- // previous line invalid. Simple solution: redraw all visible
- // lines above the change.
- // Same for a match pattern.
- if (search_hl.rm.regprog != NULL
- && re_multiline(search_hl.rm.regprog)) {
- top_to_mod = true;
- } else {
- const matchitem_T *cur = wp->w_match_head;
- while (cur != NULL) {
- if (cur->match.regprog != NULL
- && re_multiline(cur->match.regprog)) {
- top_to_mod = true;
- break;
- }
- cur = cur->next;
- }
- }
- }
- if (mod_top != 0 && hasAnyFolding(wp)) {
- linenr_T lnumt, lnumb;
-
- // A change in a line can cause lines above it to become folded or
- // unfolded. Find the top most buffer line that may be affected.
- // If the line was previously folded and displayed, get the first
- // line of that fold. If the line is folded now, get the first
- // folded line. Use the minimum of these two.
-
- // Find last valid w_lines[] entry above mod_top. Set lnumt to
- // the line below it. If there is no valid entry, use w_topline.
- // Find the first valid w_lines[] entry below mod_bot. Set lnumb
- // to this line. If there is no valid entry, use MAXLNUM.
- lnumt = wp->w_topline;
- lnumb = MAXLNUM;
- for (i = 0; i < wp->w_lines_valid; i++) {
- if (wp->w_lines[i].wl_valid) {
- if (wp->w_lines[i].wl_lastlnum < mod_top) {
- lnumt = wp->w_lines[i].wl_lastlnum + 1;
- }
- if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) {
- lnumb = wp->w_lines[i].wl_lnum;
- // When there is a fold column it might need updating
- // in the next line ("J" just above an open fold).
- if (compute_foldcolumn(wp, 0) > 0) {
- lnumb++;
- }
- }
- }
- }
-
- (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL);
- if (mod_top > lnumt) {
- mod_top = lnumt;
- }
-
- // Now do the same for the bottom line (one above mod_bot).
- mod_bot--;
- (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL);
- mod_bot++;
- if (mod_bot < lnumb) {
- mod_bot = lnumb;
- }
- }
-
- // When a change starts above w_topline and the end is below
- // w_topline, start redrawing at w_topline.
- // If the end of the change is above w_topline: do like no change was
- // made, but redraw the first line to find changes in syntax.
- if (mod_top != 0 && mod_top < wp->w_topline) {
- if (mod_bot > wp->w_topline) {
- mod_top = wp->w_topline;
- } else if (syntax_present(wp)) {
- top_end = 1;
- }
- }
-
- // When line numbers are displayed need to redraw all lines below
- // inserted/deleted lines.
- if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) {
- mod_bot = MAXLNUM;
- }
- }
- wp->w_redraw_top = 0; // reset for next time
- wp->w_redraw_bot = 0;
-
- // When only displaying the lines at the top, set top_end. Used when
- // window has scrolled down for msg_scrolled.
- if (type == REDRAW_TOP) {
- j = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
- j += wp->w_lines[i].wl_size;
- if (j >= wp->w_upd_rows) {
- top_end = (int)j;
- break;
- }
- }
- if (top_end == 0) {
- // not found (cannot happen?): redraw everything
- type = NOT_VALID;
- } else {
- // top area defined, the rest is VALID
- type = VALID;
- }
- }
-
- // If there are no changes on the screen that require a complete redraw,
- // handle three cases:
- // 1: we are off the top of the screen by a few lines: scroll down
- // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
- // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
- // w_lines[] that needs updating.
- if ((type == VALID || type == SOME_VALID
- || type == INVERTED || type == INVERTED_ALL)
- && !wp->w_botfill && !wp->w_old_botfill) {
- if (mod_top != 0
- && wp->w_topline == mod_top
- && (!wp->w_lines[0].wl_valid
- || wp->w_topline == wp->w_lines[0].wl_lnum)) {
- // w_topline is the first changed line and window is not scrolled,
- // the scrolling from changed lines will be done further down.
- } else if (wp->w_lines[0].wl_valid
- && (wp->w_topline < wp->w_lines[0].wl_lnum
- || (wp->w_topline == wp->w_lines[0].wl_lnum
- && wp->w_topfill > wp->w_old_topfill))) {
- // New topline is above old topline: May scroll down.
- if (hasAnyFolding(wp)) {
- linenr_T ln;
-
- // count the number of lines we are off, counting a sequence
- // of folded lines as one
- j = 0;
- for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
- j++;
- if (j >= wp->w_grid.rows - 2) {
- break;
- }
- (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL);
- }
- } else {
- j = wp->w_lines[0].wl_lnum - wp->w_topline;
- }
- if (j < wp->w_grid.rows - 2) { // not too far off
- i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
- // insert extra lines for previously invisible filler lines
- if (wp->w_lines[0].wl_lnum != wp->w_topline) {
- i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
- }
- if (i != 0 && i < wp->w_grid.rows - 2) { // less than a screen off
- // Try to insert the correct number of lines.
- // If not the last window, delete the lines at the bottom.
- // win_ins_lines may fail when the terminal can't do it.
- win_scroll_lines(wp, 0, i);
- if (wp->w_lines_valid != 0) {
- // Need to update rows that are new, stop at the
- // first one that scrolled down.
- top_end = i;
- scrolled_down = true;
-
- // Move the entries that were scrolled, disable
- // the entries for the lines to be redrawn.
- if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) {
- wp->w_lines_valid = wp->w_grid.rows;
- }
- for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
- wp->w_lines[idx] = wp->w_lines[idx - j];
- }
- while (idx >= 0) {
- wp->w_lines[idx--].wl_valid = false;
- }
- }
- } else {
- mid_start = 0; // redraw all lines
- }
- } else {
- mid_start = 0; // redraw all lines
- }
- } else {
- // New topline is at or below old topline: May scroll up.
- // When topline didn't change, find first entry in w_lines[] that
- // needs updating.
-
- // try to find wp->w_topline in wp->w_lines[].wl_lnum
- j = -1;
- row = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lnum == wp->w_topline) {
- j = i;
- break;
- }
- row += wp->w_lines[i].wl_size;
- }
- if (j == -1) {
- // if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all
- // lines
- mid_start = 0;
- } else {
- // Try to delete the correct number of lines.
- // wp->w_topline is at wp->w_lines[i].wl_lnum.
-
- // If the topline didn't change, delete old filler lines,
- // otherwise delete filler lines of the new topline...
- if (wp->w_lines[0].wl_lnum == wp->w_topline) {
- row += wp->w_old_topfill;
- } else {
- row += win_get_fill(wp, wp->w_topline);
- }
- // ... but don't delete new filler lines.
- row -= wp->w_topfill;
- if (row > 0) {
- win_scroll_lines(wp, 0, -row);
- bot_start = wp->w_grid.rows - row;
- }
- if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
- // Skip the lines (below the deleted lines) that are still
- // valid and don't need redrawing. Copy their info
- // upwards, to compensate for the deleted lines. Set
- // bot_start to the first row that needs redrawing.
- bot_start = 0;
- idx = 0;
- for (;;) {
- wp->w_lines[idx] = wp->w_lines[j];
- // stop at line that didn't fit, unless it is still
- // valid (no lines deleted)
- if (row > 0 && bot_start + row
- + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) {
- wp->w_lines_valid = idx + 1;
- break;
- }
- bot_start += wp->w_lines[idx++].wl_size;
-
- // stop at the last valid entry in w_lines[].wl_size
- if (++j >= wp->w_lines_valid) {
- wp->w_lines_valid = idx;
- break;
- }
- }
-
- // Correct the first entry for filler lines at the top
- // when it won't get updated below.
- if (win_may_fill(wp) && bot_start > 0) {
- wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true)
- + wp->w_topfill);
- }
- }
- }
- }
-
- // When starting redraw in the first line, redraw all lines.
- if (mid_start == 0) {
- mid_end = wp->w_grid.rows;
- }
- } else {
- // Not VALID or INVERTED: redraw all lines.
- mid_start = 0;
- mid_end = wp->w_grid.rows;
- }
-
- if (type == SOME_VALID) {
- // SOME_VALID: redraw all lines.
- mid_start = 0;
- mid_end = wp->w_grid.rows;
- type = NOT_VALID;
- }
-
- // check if we are updating or removing the inverted part
- if ((VIsual_active && buf == curwin->w_buffer)
- || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) {
- linenr_T from, to;
-
- if (VIsual_active) {
- if (VIsual_mode != wp->w_old_visual_mode || type == INVERTED_ALL) {
- // If the type of Visual selection changed, redraw the whole
- // selection. Also when the ownership of the X selection is
- // gained or lost.
- if (curwin->w_cursor.lnum < VIsual.lnum) {
- from = curwin->w_cursor.lnum;
- to = VIsual.lnum;
- } else {
- from = VIsual.lnum;
- to = curwin->w_cursor.lnum;
- }
- // redraw more when the cursor moved as well
- if (wp->w_old_cursor_lnum < from) {
- from = wp->w_old_cursor_lnum;
- }
- if (wp->w_old_cursor_lnum > to) {
- to = wp->w_old_cursor_lnum;
- }
- if (wp->w_old_visual_lnum < from) {
- from = wp->w_old_visual_lnum;
- }
- if (wp->w_old_visual_lnum > to) {
- to = wp->w_old_visual_lnum;
- }
- } else {
- // Find the line numbers that need to be updated: The lines
- // between the old cursor position and the current cursor
- // position. Also check if the Visual position changed.
- if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum) {
- from = curwin->w_cursor.lnum;
- to = wp->w_old_cursor_lnum;
- } else {
- from = wp->w_old_cursor_lnum;
- to = curwin->w_cursor.lnum;
- if (from == 0) { // Visual mode just started
- from = to;
- }
- }
-
- if (VIsual.lnum != wp->w_old_visual_lnum
- || VIsual.col != wp->w_old_visual_col) {
- if (wp->w_old_visual_lnum < from
- && wp->w_old_visual_lnum != 0) {
- from = wp->w_old_visual_lnum;
- }
- if (wp->w_old_visual_lnum > to) {
- to = wp->w_old_visual_lnum;
- }
- if (VIsual.lnum < from) {
- from = VIsual.lnum;
- }
- if (VIsual.lnum > to) {
- to = VIsual.lnum;
- }
- }
- }
-
- // If in block mode and changed column or curwin->w_curswant:
- // update all lines.
- // First compute the actual start and end column.
- if (VIsual_mode == Ctrl_V) {
- colnr_T fromc, toc;
- unsigned int save_ve_flags = curwin->w_ve_flags;
-
- if (curwin->w_p_lbr) {
- curwin->w_ve_flags = VE_ALL;
- }
-
- getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
- toc++;
- curwin->w_ve_flags = save_ve_flags;
- // Highlight to the end of the line, unless 'virtualedit' has
- // "block".
- if (curwin->w_curswant == MAXCOL) {
- if (get_ve_flags() & VE_BLOCK) {
- pos_T pos;
- int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
-
- // Need to find the longest line.
- toc = 0;
- pos.coladd = 0;
- for (pos.lnum = curwin->w_cursor.lnum;
- cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum;
- pos.lnum += cursor_above ? 1 : -1) {
- colnr_T t;
-
- pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
- getvvcol(wp, &pos, NULL, NULL, &t);
- if (toc < t) {
- toc = t;
- }
- }
- toc++;
- } else {
- toc = MAXCOL;
- }
- }
-
- if (fromc != wp->w_old_cursor_fcol
- || toc != wp->w_old_cursor_lcol) {
- if (from > VIsual.lnum) {
- from = VIsual.lnum;
- }
- if (to < VIsual.lnum) {
- to = VIsual.lnum;
- }
- }
- wp->w_old_cursor_fcol = fromc;
- wp->w_old_cursor_lcol = toc;
- }
- } else {
- // Use the line numbers of the old Visual area.
- if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) {
- from = wp->w_old_cursor_lnum;
- to = wp->w_old_visual_lnum;
- } else {
- from = wp->w_old_visual_lnum;
- to = wp->w_old_cursor_lnum;
- }
- }
-
- // There is no need to update lines above the top of the window.
- if (from < wp->w_topline) {
- from = wp->w_topline;
- }
-
- // If we know the value of w_botline, use it to restrict the update to
- // the lines that are visible in the window.
- if (wp->w_valid & VALID_BOTLINE) {
- if (from >= wp->w_botline) {
- from = wp->w_botline - 1;
- }
- if (to >= wp->w_botline) {
- to = wp->w_botline - 1;
- }
- }
-
- // Find the minimal part to be updated.
- // Watch out for scrolling that made entries in w_lines[] invalid.
- // E.g., CTRL-U makes the first half of w_lines[] invalid and sets
- // top_end; need to redraw from top_end to the "to" line.
- // A middle mouse click with a Visual selection may change the text
- // above the Visual area and reset wl_valid, do count these for
- // mid_end (in srow).
- if (mid_start > 0) {
- lnum = wp->w_topline;
- idx = 0;
- srow = 0;
- if (scrolled_down) {
- mid_start = top_end;
- } else {
- mid_start = 0;
- }
- while (lnum < from && idx < wp->w_lines_valid) { // find start
- if (wp->w_lines[idx].wl_valid) {
- mid_start += wp->w_lines[idx].wl_size;
- } else if (!scrolled_down) {
- srow += wp->w_lines[idx].wl_size;
- }
- idx++;
- if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) {
- lnum = wp->w_lines[idx].wl_lnum;
- } else {
- lnum++;
- }
- }
- srow += mid_start;
- mid_end = wp->w_grid.rows;
- for (; idx < wp->w_lines_valid; idx++) { // find end
- if (wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum >= to + 1) {
- // Only update until first row of this line
- mid_end = srow;
- break;
- }
- srow += wp->w_lines[idx].wl_size;
- }
- }
- }
-
- if (VIsual_active && buf == curwin->w_buffer) {
- wp->w_old_visual_mode = (char)VIsual_mode;
- wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
- wp->w_old_visual_lnum = VIsual.lnum;
- wp->w_old_visual_col = VIsual.col;
- wp->w_old_curswant = curwin->w_curswant;
- } else {
- wp->w_old_visual_mode = 0;
- wp->w_old_cursor_lnum = 0;
- wp->w_old_visual_lnum = 0;
- wp->w_old_visual_col = 0;
- }
-
- // reset got_int, otherwise regexp won't work
- save_got_int = got_int;
- got_int = 0;
- // Set the time limit to 'redrawtime'.
- proftime_T syntax_tm = profile_setlimit(p_rdt);
- syn_set_timeout(&syntax_tm);
-
- // Update all the window rows.
- idx = 0; // first entry in w_lines[].wl_size
- row = 0;
- srow = 0;
- lnum = wp->w_topline; // first line shown in window
-
- win_extmark_arr.size = 0;
-
- decor_redraw_reset(buf, &decor_state);
-
- DecorProviders line_providers;
- decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
- (void)win_signcol_count(wp); // check if provider changed signcol width
- if (must_redraw != 0) {
- must_redraw = 0;
- if (!called_decor_providers) {
- called_decor_providers = true;
- goto win_update_start;
- }
- }
-
- bool cursorline_standout = win_cursorline_standout(wp);
-
- for (;;) {
- // stop updating when reached the end of the window (check for _past_
- // the end of the window is at the end of the loop)
- if (row == wp->w_grid.rows) {
- didline = true;
- break;
- }
-
- // stop updating when hit the end of the file
- if (lnum > buf->b_ml.ml_line_count) {
- eof = true;
- break;
- }
-
- // Remember the starting row of the line that is going to be dealt
- // with. It is used further down when the line doesn't fit.
- srow = row;
-
- // Update a line when it is in an area that needs updating, when it
- // has changes or w_lines[idx] is invalid.
- // "bot_start" may be halfway a wrapped line after using
- // win_scroll_lines(), check if the current line includes it.
- // When syntax folding is being used, the saved syntax states will
- // already have been updated, we can't see where the syntax state is
- // the same again, just update until the end of the window.
- if (row < top_end
- || (row >= mid_start && row < mid_end)
- || top_to_mod
- || idx >= wp->w_lines_valid
- || (row + wp->w_lines[idx].wl_size > bot_start)
- || (mod_top != 0
- && (lnum == mod_top
- || (lnum >= mod_top
- && (lnum < mod_bot
- || did_update == DID_FOLD
- || (did_update == DID_LINE
- && syntax_present(wp)
- && ((foldmethodIsSyntax(wp)
- && hasAnyFolding(wp))
- || syntax_check_changed(lnum)))
- // match in fixed position might need redraw
- // if lines were inserted or deleted
- || (wp->w_match_head != NULL
- && buf->b_mod_xlines != 0)))))
- || (cursorline_standout && lnum == wp->w_cursor.lnum)
- || lnum == wp->w_last_cursorline) {
- if (lnum == mod_top) {
- top_to_mod = false;
- }
-
- // When at start of changed lines: May scroll following lines
- // up or down to minimize redrawing.
- // Don't do this when the change continues until the end.
- // Don't scroll when dollar_vcol >= 0, keep the "$".
- // Don't scroll when redrawing the top, scrolled already above.
- if (lnum == mod_top
- && mod_bot != MAXLNUM
- && !(dollar_vcol >= 0 && mod_bot == mod_top + 1)
- && row >= top_end) {
- int old_rows = 0;
- int new_rows = 0;
- int xtra_rows;
- linenr_T l;
-
- // Count the old number of window rows, using w_lines[], which
- // should still contain the sizes for the lines as they are
- // currently displayed.
- for (i = idx; i < wp->w_lines_valid; i++) {
- // Only valid lines have a meaningful wl_lnum. Invalid
- // lines are part of the changed area.
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lnum == mod_bot) {
- break;
- }
- old_rows += wp->w_lines[i].wl_size;
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) {
- // Must have found the last valid entry above mod_bot.
- // Add following invalid entries.
- i++;
- while (i < wp->w_lines_valid
- && !wp->w_lines[i].wl_valid) {
- old_rows += wp->w_lines[i++].wl_size;
- }
- break;
- }
- }
-
- if (i >= wp->w_lines_valid) {
- // We can't find a valid line below the changed lines,
- // need to redraw until the end of the window.
- // Inserting/deleting lines has no use.
- bot_start = 0;
- } else {
- // Able to count old number of rows: Count new window
- // rows, and may insert/delete lines
- j = idx;
- for (l = lnum; l < mod_bot; l++) {
- if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
- new_rows++;
- } else if (l == wp->w_topline) {
- new_rows += plines_win_nofill(wp, l, true) + wp->w_topfill;
- } else {
- new_rows += plines_win(wp, l, true);
- }
- j++;
- if (new_rows > wp->w_grid.rows - row - 2) {
- // it's getting too much, must redraw the rest
- new_rows = 9999;
- break;
- }
- }
- xtra_rows = new_rows - old_rows;
- if (xtra_rows < 0) {
- // May scroll text up. If there is not enough
- // remaining text or scrolling fails, must redraw the
- // rest. If scrolling works, must redraw the text
- // below the scrolled text.
- if (row - xtra_rows >= wp->w_grid.rows - 2) {
- mod_bot = MAXLNUM;
- } else {
- win_scroll_lines(wp, row, xtra_rows);
- bot_start = wp->w_grid.rows + xtra_rows;
- }
- } else if (xtra_rows > 0) {
- // May scroll text down. If there is not enough
- // remaining text of scrolling fails, must redraw the
- // rest.
- if (row + xtra_rows >= wp->w_grid.rows - 2) {
- mod_bot = MAXLNUM;
- } else {
- win_scroll_lines(wp, row + old_rows, xtra_rows);
- if (top_end > row + old_rows) {
- // Scrolled the part at the top that requires
- // updating down.
- top_end += xtra_rows;
- }
- }
- }
-
- // When not updating the rest, may need to move w_lines[]
- // entries.
- if (mod_bot != MAXLNUM && i != j) {
- if (j < i) {
- int x = row + new_rows;
-
- // move entries in w_lines[] upwards
- for (;;) {
- // stop at last valid entry in w_lines[]
- if (i >= wp->w_lines_valid) {
- wp->w_lines_valid = (int)j;
- break;
- }
- wp->w_lines[j] = wp->w_lines[i];
- // stop at a line that won't fit
- if (x + (int)wp->w_lines[j].wl_size
- > wp->w_grid.rows) {
- wp->w_lines_valid = (int)j + 1;
- break;
- }
- x += wp->w_lines[j++].wl_size;
- i++;
- }
- if (bot_start > x) {
- bot_start = x;
- }
- } else { // j > i
- // move entries in w_lines[] downwards
- j -= i;
- wp->w_lines_valid += (linenr_T)j;
- if (wp->w_lines_valid > wp->w_grid.rows) {
- wp->w_lines_valid = wp->w_grid.rows;
- }
- for (i = wp->w_lines_valid; i - j >= idx; i--) {
- wp->w_lines[i] = wp->w_lines[i - j];
- }
-
- // The w_lines[] entries for inserted lines are
- // now invalid, but wl_size may be used above.
- // Reset to zero.
- while (i >= idx) {
- wp->w_lines[i].wl_size = 0;
- wp->w_lines[i--].wl_valid = false;
- }
- }
- }
- }
- }
-
- // When lines are folded, display one line for all of them.
- // Otherwise, display normally (can be several display lines when
- // 'wrap' is on).
- foldinfo_T foldinfo = fold_info(wp, lnum);
-
- if (foldinfo.fi_lines == 0
- && idx < wp->w_lines_valid
- && wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum == lnum
- && lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
- && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows
- && win_get_fill(wp, lnum) == 0) {
- // This line is not going to fit. Don't draw anything here,
- // will draw "@ " lines below.
- row = wp->w_grid.rows + 1;
- } else {
- prepare_search_hl(wp, &search_hl, lnum);
- // Let the syntax stuff know we skipped a few lines.
- if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
- && syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
- }
-
- // Display one line
- row = win_line(wp, lnum, srow,
- foldinfo.fi_lines ? srow : wp->w_grid.rows,
- mod_top == 0, false, foldinfo, &line_providers);
-
- if (foldinfo.fi_lines == 0) {
- wp->w_lines[idx].wl_folded = false;
- wp->w_lines[idx].wl_lastlnum = lnum;
- did_update = DID_LINE;
- syntax_last_parsed = lnum;
- } else {
- foldinfo.fi_lines--;
- wp->w_lines[idx].wl_folded = true;
- wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
- did_update = DID_FOLD;
- }
- }
-
- wp->w_lines[idx].wl_lnum = lnum;
- wp->w_lines[idx].wl_valid = true;
-
- if (row > wp->w_grid.rows) { // past end of grid
- // we may need the size of that too long line later on
- if (dollar_vcol == -1) {
- wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true);
- }
- idx++;
- break;
- }
- if (dollar_vcol == -1) {
- wp->w_lines[idx].wl_size = (uint16_t)(row - srow);
- }
- idx++;
- lnum += foldinfo.fi_lines + 1;
- } else {
- if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) {
- // 'relativenumber' set and cursor moved vertically: The
- // text doesn't need to be drawn, but the number column does.
- foldinfo_T info = fold_info(wp, lnum);
- (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true,
- info, &line_providers);
- }
-
- // This line does not need to be drawn, advance to the next one.
- row += wp->w_lines[idx++].wl_size;
- if (row > wp->w_grid.rows) { // past end of screen
- break;
- }
- lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
- did_update = DID_NONE;
- }
-
- if (lnum > buf->b_ml.ml_line_count) {
- eof = true;
- break;
- }
- }
- // End of loop over all window lines.
-
- // Now that the window has been redrawn with the old and new cursor line,
- // update w_last_cursorline.
- wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0;
-
- wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
-
- if (idx > wp->w_lines_valid) {
- wp->w_lines_valid = idx;
- }
-
- // Let the syntax stuff know we stop parsing here.
- if (syntax_last_parsed != 0 && syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
- }
-
- // If we didn't hit the end of the file, and we didn't finish the last
- // line we were working on, then the line didn't fit.
- wp->w_empty_rows = 0;
- wp->w_filler_rows = 0;
- if (!eof && !didline) {
- int at_attr = hl_combine_attr(wp->w_hl_attr_normal,
- win_hl_attr(wp, HLF_AT));
- if (lnum == wp->w_topline) {
- // Single line that does not fit!
- // Don't overwrite it, it can be edited.
- wp->w_botline = lnum + 1;
- } else if (win_get_fill(wp, lnum) >= wp->w_grid.rows - srow) {
- // Window ends in filler lines.
- wp->w_botline = lnum;
- wp->w_filler_rows = wp->w_grid.rows - srow;
- } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
- int scr_row = wp->w_grid.rows - 1;
-
- // Last line isn't finished: Display "@@@" in the last screen line.
- grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr);
-
- grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols,
- '@', ' ', at_attr);
- set_empty_rows(wp, srow);
- wp->w_botline = lnum;
- } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
- int start_col = wp->w_grid.cols - 3;
-
- // Last line isn't finished: Display "@@@" at the end.
- grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
- MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
- set_empty_rows(wp, srow);
- wp->w_botline = lnum;
- } else {
- win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT);
- wp->w_botline = lnum;
- }
- } else {
- if (eof) { // we hit the end of the file
- wp->w_botline = buf->b_ml.ml_line_count + 1;
- j = win_get_fill(wp, wp->w_botline);
- if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) {
- // Display filler text below last line. win_line() will check
- // for ml_line_count+1 and only draw filler lines
- foldinfo_T info = FOLDINFO_INIT;
- row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
- false, false, info, &line_providers);
- }
- } else if (dollar_vcol == -1) {
- wp->w_botline = lnum;
- }
-
- // make sure the rest of the screen is blank
- // write the 'eob' character to rows that aren't part of the file.
- win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows,
- HLF_EOB);
- }
-
- kvi_destroy(line_providers);
-
- if (wp->w_redr_type >= REDRAW_TOP) {
- draw_vsep_win(wp);
- draw_hsep_win(wp);
- draw_sep_connectors_win(wp);
- }
- syn_set_timeout(NULL);
-
- // Reset the type of redrawing required, the window has been updated.
- wp->w_redr_type = 0;
- wp->w_old_topfill = wp->w_topfill;
- wp->w_old_botfill = wp->w_botfill;
-
- // Send win_extmarks if needed
- for (size_t n = 0; n < kv_size(win_extmark_arr); n++) {
- ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle,
- kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id,
- kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col);
- }
-
- if (dollar_vcol == -1) {
- // There is a trick with w_botline. If we invalidate it on each
- // change that might modify it, this will cause a lot of expensive
- // calls to plines_win() in update_topline() each time. Therefore the
- // value of w_botline is often approximated, and this value is used to
- // compute the value of w_topline. If the value of w_botline was
- // wrong, check that the value of w_topline is correct (cursor is on
- // the visible part of the text). If it's not, we need to redraw
- // again. Mostly this just means scrolling up a few lines, so it
- // doesn't look too bad. Only do this for the current window (where
- // changes are relevant).
- wp->w_valid |= VALID_BOTLINE;
- wp->w_viewport_invalid = true;
- if (wp == curwin && wp->w_botline != old_botline && !recursive) {
- recursive = true;
- curwin->w_valid &= ~VALID_TOPLINE;
- update_topline(curwin); // may invalidate w_botline again
- if (must_redraw != 0) {
- // Don't update for changes in buffer again.
- i = curbuf->b_mod_set;
- curbuf->b_mod_set = false;
- win_update(curwin, providers);
- must_redraw = 0;
- curbuf->b_mod_set = i;
- }
- recursive = false;
- }
- }
-
- // restore got_int, unless CTRL-C was hit while redrawing
- if (!got_int) {
- got_int = save_got_int;
- }
-}
-
/// Returns width of the signcolumn that should be used for the whole window
///
/// @param wp window we want signcolumn width from
@@ -1726,7 +117,7 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
/// Clear lines near the end of the window and mark the unused lines with "c1".
/// Use "c2" as filler character.
/// When "draw_margin" is true, then draw the sign/fold/number columns.
-static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl)
+void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl)
{
assert(hl >= 0 && hl < HLF_COUNT);
int n = 0;
@@ -1751,7 +142,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i
}
}
- int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, (int)hl));
+ int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl));
if (wp->w_p_rl) {
grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n,
@@ -1765,20 +156,9 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i
set_empty_rows(wp, row);
}
-/// Advance **color_cols
-///
-/// @return true when there are columns to draw.
-static bool advance_color_col(int vcol, int **color_cols)
-{
- while (**color_cols >= 0 && vcol > **color_cols) {
- (*color_cols)++;
- }
- return **color_cols >= 0;
-}
-
-// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
-// space is available for window "wp", minus "col".
-static int compute_foldcolumn(win_T *wp, int col)
+/// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
+/// space is available for window "wp", minus "col".
+int compute_foldcolumn(win_T *wp, int col)
{
int fdc = win_fdccol_count(wp);
int wmw = wp == curwin && p_wmw == 0 ? 1 : (int)p_wmw;
@@ -1790,63 +170,6 @@ static int compute_foldcolumn(win_T *wp, int col)
return fdc;
}
-/// Put a single char from an UTF-8 buffer into a line buffer.
-///
-/// Handles composing chars and arabic shaping state.
-static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol)
-{
- const char_u *p = (char_u *)s->p;
- int cells = utf_ptr2cells((char *)p);
- int c_len = utfc_ptr2len((char *)p);
- int u8c, u8cc[MAX_MCO];
- if (cells > maxcells) {
- return -1;
- }
- u8c = utfc_ptr2char(p, u8cc);
- if (*p == TAB) {
- cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
- for (int c = 0; c < cells; c++) {
- schar_from_ascii(dest[c], ' ');
- }
- goto done;
- } else if (*p < 0x80 && u8cc[0] == 0) {
- schar_from_ascii(dest[0], (char)(*p));
- s->prev_c = u8c;
- } else {
- if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
- // Do Arabic shaping.
- int pc, pc1, nc;
- int pcc[MAX_MCO];
- int firstbyte = *p;
-
- // The idea of what is the previous and next
- // character depends on 'rightleft'.
- if (rl) {
- pc = s->prev_c;
- pc1 = s->prev_c1;
- nc = utf_ptr2char((char *)p + c_len);
- s->prev_c1 = u8cc[0];
- } else {
- pc = utfc_ptr2char(p + c_len, pcc);
- nc = s->prev_c;
- pc1 = pcc[0];
- }
- s->prev_c = u8c;
-
- u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc);
- } else {
- s->prev_c = u8c;
- }
- schar_from_cc(dest[0], u8c, u8cc);
- }
- if (cells > 1) {
- dest[1][0] = 0;
- }
-done:
- s->p += c_len;
- return cells;
-}
-
/// Fills the foldcolumn at "p" for window "wp".
/// Only to be called when 'foldcolumn' > 0.
///
@@ -1856,7 +179,7 @@ done:
///
/// Assume monocell characters
/// @return number of chars added to \param p
-static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
+size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
{
int i = 0;
int level;
@@ -1911,2545 +234,6 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_
return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc);
}
-static inline void provider_err_virt_text(linenr_T lnum, char *err)
-{
- Decoration err_decor = DECORATION_INIT;
- int hl_err = syn_check_group(S_LEN("ErrorMsg"));
- kv_push(err_decor.virt_text,
- ((VirtTextChunk){ .text = provider_err,
- .hl_id = hl_err }));
- err_decor.virt_text_width = (int)mb_string2cells(err);
- decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
-}
-
-static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
-{
- long num;
- char *fmt = "%*ld ";
-
- if (wp->w_p_nu && !wp->w_p_rnu) {
- // 'number' + 'norelativenumber'
- num = (long)lnum;
- } else {
- // 'relativenumber', don't use negative numbers
- num = labs((long)get_cursor_rel_lnum(wp, lnum));
- if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
- // 'number' + 'relativenumber'
- num = lnum;
- fmt = "%-*ld ";
- }
- }
-
- snprintf((char *)buf, buf_len, fmt, number_width(wp), num);
-}
-
-/// Display line "lnum" of window 'wp' on the screen.
-/// wp->w_virtcol needs to be valid.
-///
-/// @param lnum line to display
-/// @param startrow first row relative to window grid
-/// @param endrow last grid row to be redrawn
-/// @param nochange not updating for changed text
-/// @param number_only only update the number column
-/// @param foldinfo fold info for this line
-/// @param[in, out] providers decoration providers active this line
-/// items will be disables if they cause errors
-/// or explicitly return `false`.
-///
-/// @return the number of last row the line occupies.
-static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
- bool number_only, foldinfo_T foldinfo, DecorProviders *providers)
-{
- int c = 0; // init for GCC
- long vcol = 0; // virtual column (for tabs)
- long vcol_sbr = -1; // virtual column after showbreak
- long vcol_prev = -1; // "vcol" of previous character
- char_u *line; // current line
- char_u *ptr; // current position in "line"
- int row; // row in the window, excl w_winrow
- ScreenGrid *grid = &wp->w_grid; // grid specific to the window
-
- char_u extra[57]; // sign, line number and 'fdc' must
- // fit in here
- int n_extra = 0; // number of extra chars
- char_u *p_extra = NULL; // string of extra chars, plus NUL
- char_u *p_extra_free = NULL; // p_extra needs to be freed
- int c_extra = NUL; // extra chars, all the same
- int c_final = NUL; // final char, mandatory if set
- int extra_attr = 0; // attributes when n_extra != 0
- static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
- // curwin->w_p_lcs_chars.eol at
- // end-of-line
- int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
- int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
- bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
-
- // saved "extra" items for when draw_state becomes WL_LINE (again)
- int saved_n_extra = 0;
- char_u *saved_p_extra = NULL;
- int saved_c_extra = 0;
- int saved_c_final = 0;
- int saved_char_attr = 0;
-
- int n_attr = 0; // chars with special attr
- int saved_attr2 = 0; // char_attr saved for n_attr
- int n_attr3 = 0; // chars with overruling special attr
- int saved_attr3 = 0; // char_attr saved for n_attr3
-
- int n_skip = 0; // nr of chars to skip for 'nowrap'
-
- int fromcol = -10; // start of inverting
- int tocol = MAXCOL; // end of inverting
- int fromcol_prev = -2; // start of inverting after cursor
- bool noinvcur = false; // don't invert the cursor
- bool lnum_in_visual_area = false;
- pos_T pos;
- long v;
-
- int char_attr = 0; // attributes for next character
- bool attr_pri = false; // char_attr has priority
- bool area_highlighting = false; // Visual or incsearch highlighting in this line
- int attr = 0; // attributes for area highlighting
- int area_attr = 0; // attributes desired by highlighting
- int search_attr = 0; // attributes desired by 'hlsearch'
- int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
- int syntax_attr = 0; // attributes desired by syntax
- bool has_syntax = false; // this buffer has syntax highl.
- int save_did_emsg;
- int eol_hl_off = 0; // 1 if highlighted char after EOL
- bool draw_color_col = false; // highlight colorcolumn
- int *color_cols = NULL; // pointer to according columns array
- bool has_spell = false; // this buffer has spell checking
-#define SPWORDLEN 150
- char_u nextline[SPWORDLEN * 2]; // text with start of the next line
- int nextlinecol = 0; // column where nextline[] starts
- int nextline_idx = 0; // index in nextline[] where next line
- // starts
- int spell_attr = 0; // attributes desired by spelling
- int word_end = 0; // last byte with same spell_attr
- static linenr_T checked_lnum = 0; // line number for "checked_col"
- static int checked_col = 0; // column in "checked_lnum" up to which
- // there are no spell errors
- static int cap_col = -1; // column to check for Cap word
- static linenr_T capcol_lnum = 0; // line number where "cap_col"
- int cur_checked_col = 0; // checked column for current line
- int extra_check = 0; // has syntax or linebreak
- int multi_attr = 0; // attributes desired by multibyte
- int mb_l = 1; // multi-byte byte length
- int mb_c = 0; // decoded multi-byte character
- bool mb_utf8 = false; // screen char is UTF-8 char
- int u8cc[MAX_MCO]; // composing UTF-8 chars
- int filler_lines; // nr of filler lines to be drawn
- int filler_todo; // nr of filler lines still to do + 1
- hlf_T diff_hlf = (hlf_T)0; // type of diff highlighting
- int change_start = MAXCOL; // first col of changed area
- int change_end = -1; // last col of changed area
- colnr_T trailcol = MAXCOL; // start of trailing spaces
- colnr_T leadcol = 0; // start of leading spaces
- bool in_multispace = false; // in multiple consecutive spaces
- int multispace_pos = 0; // position in lcs-multispace string
- bool need_showbreak = false; // overlong line, skip first x chars
- sign_attrs_T sattrs[SIGN_SHOW_MAX]; // attributes for signs
- int num_signs; // number of signs for line
- int line_attr = 0; // attribute for the whole line
- int line_attr_save;
- int line_attr_lowprio = 0; // low-priority attribute for the line
- int line_attr_lowprio_save;
- int prev_c = 0; // previous Arabic character
- int prev_c1 = 0; // first composing char for prev_c
-
- bool search_attr_from_match = false; // if search_attr is from :match
- bool has_decor = false; // this buffer has decoration
- int win_col_offset = 0; // offset for window columns
-
- char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
-
- bool area_active = false;
-
- int cul_attr = 0; // set when 'cursorline' active
- // 'cursorlineopt' has "screenline" and cursor is in this line
- bool cul_screenline = false;
- // margin columns for the screen line, needed for when 'cursorlineopt'
- // contains "screenline"
- int left_curline_col = 0;
- int right_curline_col = 0;
-
- // draw_state: items that are drawn in sequence:
-#define WL_START 0 // nothing done yet
-#define WL_CMDLINE (WL_START + 1) // cmdline window column
-#define WL_FOLD (WL_CMDLINE + 1) // 'foldcolumn'
-#define WL_SIGN (WL_FOLD + 1) // column for signs
-#define WL_NR (WL_SIGN + 1) // line number
-#define WL_BRI (WL_NR + 1) // 'breakindent'
-#define WL_SBR (WL_BRI + 1) // 'showbreak' or 'diff'
-#define WL_LINE (WL_SBR + 1) // text in the line
- int draw_state = WL_START; // what to draw next
-
- int syntax_flags = 0;
- int syntax_seqnr = 0;
- int prev_syntax_id = 0;
- int conceal_attr = win_hl_attr(wp, HLF_CONCEAL);
- bool is_concealing = false;
- int boguscols = 0; ///< nonexistent columns added to
- ///< force wrapping
- int vcol_off = 0; ///< offset for concealed characters
- int did_wcol = false;
- int match_conc = 0; ///< cchar for match functions
- int old_boguscols = 0;
-#define VCOL_HLC (vcol - vcol_off)
-#define FIX_FOR_BOGUSCOLS \
- { \
- n_extra += vcol_off; \
- vcol -= vcol_off; \
- vcol_off = 0; \
- col -= boguscols; \
- old_boguscols = boguscols; \
- boguscols = 0; \
- }
-
- if (startrow > endrow) { // past the end already!
- return startrow;
- }
-
- row = startrow;
-
- buf_T *buf = wp->w_buffer;
- bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
-
- if (!number_only) {
- // To speed up the loop below, set extra_check when there is linebreak,
- // trailing white space and/or syntax processing to be done.
- extra_check = wp->w_p_lbr;
- if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow
- && !has_fold && !end_fill) {
- // Prepare for syntax highlighting in this line. When there is an
- // error, stop syntax highlighting.
- save_did_emsg = did_emsg;
- did_emsg = false;
- syntax_start(wp, lnum);
- if (did_emsg) {
- wp->w_s->b_syn_error = true;
- } else {
- did_emsg = save_did_emsg;
- if (!wp->w_s->b_syn_slow) {
- has_syntax = true;
- extra_check = true;
- }
- }
- }
-
- has_decor = decor_redraw_line(buf, lnum - 1, &decor_state);
-
- providers_invoke_line(wp, providers, lnum - 1, &has_decor, &provider_err);
-
- if (provider_err) {
- provider_err_virt_text(lnum, provider_err);
- has_decor = true;
- provider_err = NULL;
- }
-
- if (has_decor) {
- extra_check = true;
- }
-
- // Check for columns to display for 'colorcolumn'.
- color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
- if (color_cols != NULL) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- if (wp->w_p_spell
- && !has_fold
- && !end_fill
- && *wp->w_s->b_p_spl != NUL
- && !GA_EMPTY(&wp->w_s->b_langp)
- && *(char **)(wp->w_s->b_langp.ga_data) != NULL) {
- // Prepare for spell checking.
- has_spell = true;
- extra_check = true;
-
- // Get the start of the next line, so that words that wrap to the next
- // line are found too: "et<line-break>al.".
- // Trick: skip a few chars for C/shell/Vim comments
- nextline[SPWORDLEN] = NUL;
- if (lnum < wp->w_buffer->b_ml.ml_line_count) {
- line = ml_get_buf(wp->w_buffer, lnum + 1, false);
- spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
- }
-
- // When a word wrapped from the previous line the start of the current
- // line is valid.
- if (lnum == checked_lnum) {
- cur_checked_col = checked_col;
- }
- checked_lnum = 0;
-
- // When there was a sentence end in the previous line may require a
- // word starting with capital in this line. In line 1 always check
- // the first word.
- if (lnum != capcol_lnum) {
- cap_col = -1;
- }
- if (lnum == 1) {
- cap_col = 0;
- }
- capcol_lnum = 0;
- }
-
- // handle Visual active in this window
- if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
- pos_T *top, *bot;
-
- if (ltoreq(curwin->w_cursor, VIsual)) {
- // Visual is after curwin->w_cursor
- top = &curwin->w_cursor;
- bot = &VIsual;
- } else {
- // Visual is before curwin->w_cursor
- top = &VIsual;
- bot = &curwin->w_cursor;
- }
- lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum);
- if (VIsual_mode == Ctrl_V) {
- // block mode
- if (lnum_in_visual_area) {
- fromcol = wp->w_old_cursor_fcol;
- tocol = wp->w_old_cursor_lcol;
- }
- } else {
- // non-block mode
- if (lnum > top->lnum && lnum <= bot->lnum) {
- fromcol = 0;
- } else if (lnum == top->lnum) {
- if (VIsual_mode == 'V') { // linewise
- fromcol = 0;
- } else {
- getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL);
- if (gchar_pos(top) == NUL) {
- tocol = fromcol + 1;
- }
- }
- }
- if (VIsual_mode != 'V' && lnum == bot->lnum) {
- if (*p_sel == 'e' && bot->col == 0
- && bot->coladd == 0) {
- fromcol = -10;
- tocol = MAXCOL;
- } else if (bot->col == MAXCOL) {
- tocol = MAXCOL;
- } else {
- pos = *bot;
- if (*p_sel == 'e') {
- getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL);
- } else {
- getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol);
- tocol++;
- }
- }
- }
- }
-
- // Check if the char under the cursor should be inverted (highlighted).
- if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
- && cursor_is_block_during_visual(*p_sel == 'e')) {
- noinvcur = true;
- }
-
- // if inverting in this line set area_highlighting
- if (fromcol >= 0) {
- area_highlighting = true;
- attr = win_hl_attr(wp, HLF_V);
- }
- // handle 'incsearch' and ":s///c" highlighting
- } else if (highlight_match
- && wp == curwin
- && !has_fold
- && lnum >= curwin->w_cursor.lnum
- && lnum <= curwin->w_cursor.lnum + search_match_lines) {
- if (lnum == curwin->w_cursor.lnum) {
- getvcol(curwin, &(curwin->w_cursor),
- (colnr_T *)&fromcol, NULL, NULL);
- } else {
- fromcol = 0;
- }
- if (lnum == curwin->w_cursor.lnum + search_match_lines) {
- pos.lnum = lnum;
- pos.col = search_match_endcol;
- getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL);
- }
- // do at least one character; happens when past end of line
- if (fromcol == tocol && search_match_endcol) {
- tocol = fromcol + 1;
- }
- area_highlighting = true;
- attr = win_hl_attr(wp, HLF_I);
- }
- }
-
- filler_lines = diff_check(wp, lnum);
- if (filler_lines < 0) {
- if (filler_lines == -1) {
- if (diff_find_change(wp, lnum, &change_start, &change_end)) {
- diff_hlf = HLF_ADD; // added line
- } else if (change_start == 0) {
- diff_hlf = HLF_TXD; // changed text
- } else {
- diff_hlf = HLF_CHD; // changed line
- }
- } else {
- diff_hlf = HLF_ADD; // added line
- }
- filler_lines = 0;
- area_highlighting = true;
- }
- VirtLines virt_lines = KV_INITIAL_VALUE;
- int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines);
- filler_lines += n_virt_lines;
- if (lnum == wp->w_topline) {
- filler_lines = wp->w_topfill;
- n_virt_lines = MIN(n_virt_lines, filler_lines);
- }
- filler_todo = filler_lines;
-
- // Cursor line highlighting for 'cursorline' in the current window.
- if (lnum == wp->w_cursor.lnum) {
- // Do not show the cursor line in the text when Visual mode is active,
- // because it's not clear what is selected then.
- if (wp->w_p_cul && !(wp == curwin && VIsual_active)
- && wp->w_p_culopt_flags != CULOPT_NBR) {
- cul_screenline = (wp->w_p_wrap
- && (wp->w_p_culopt_flags & CULOPT_SCRLINE));
- if (!cul_screenline) {
- cul_attr = win_hl_attr(wp, HLF_CUL);
- HlAttrs ae = syn_attr2entry(cul_attr);
- // We make a compromise here (#7383):
- // * low-priority CursorLine if fg is not set
- // * high-priority ("same as Vim" priority) CursorLine if fg is set
- if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
- line_attr_lowprio = cul_attr;
- } else {
- if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
- && qf_current_entry(wp) == lnum) {
- line_attr = hl_combine_attr(cul_attr, line_attr);
- } else {
- line_attr = cul_attr;
- }
- }
- } else {
- margin_columns_win(wp, &left_curline_col, &right_curline_col);
- }
- area_highlighting = true;
- }
- }
-
- CLEAR_FIELD(sattrs);
- num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
- decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs);
-
- // If this line has a sign with line highlighting set line_attr.
- // TODO(bfredl, vigoux): this should not take priority over decoration!
- sign_attrs_T *sattr = sign_get_attr(SIGN_LINEHL, sattrs, 0, 1);
- if (sattr != NULL) {
- line_attr = sattr->sat_linehl;
- }
-
- // Highlight the current line in the quickfix window.
- if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
- line_attr = win_hl_attr(wp, HLF_QFL);
- }
-
- if (line_attr_lowprio || line_attr) {
- area_highlighting = true;
- }
-
- if (cul_screenline) {
- line_attr_save = line_attr;
- line_attr_lowprio_save = line_attr_lowprio;
- }
-
- line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line;
-
- if (has_spell && !number_only) {
- // For checking first word with a capital skip white space.
- if (cap_col == 0) {
- cap_col = (int)getwhitecols(line);
- }
-
- // To be able to spell-check over line boundaries copy the end of the
- // current line into nextline[]. Above the start of the next line was
- // copied to nextline[SPWORDLEN].
- if (nextline[SPWORDLEN] == NUL) {
- // No next line or it is empty.
- nextlinecol = MAXCOL;
- nextline_idx = 0;
- } else {
- v = (long)STRLEN(line);
- if (v < SPWORDLEN) {
- // Short line, use it completely and append the start of the
- // next line.
- nextlinecol = 0;
- memmove(nextline, line, (size_t)v);
- STRMOVE(nextline + v, nextline + SPWORDLEN);
- nextline_idx = (int)v + 1;
- } else {
- // Long line, use only the last SPWORDLEN bytes.
- nextlinecol = (int)v - SPWORDLEN;
- memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512
- nextline_idx = SPWORDLEN + 1;
- }
- }
- }
-
- if (wp->w_p_list && !has_fold && !end_fill) {
- if (wp->w_p_lcs_chars.space
- || wp->w_p_lcs_chars.multispace != NULL
- || wp->w_p_lcs_chars.leadmultispace != NULL
- || wp->w_p_lcs_chars.trail
- || wp->w_p_lcs_chars.lead
- || wp->w_p_lcs_chars.nbsp) {
- extra_check = true;
- }
- // find start of trailing whitespace
- if (wp->w_p_lcs_chars.trail) {
- trailcol = (colnr_T)STRLEN(ptr);
- while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
- trailcol--;
- }
- trailcol += (colnr_T)(ptr - line);
- }
- // find end of leading whitespace
- if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) {
- leadcol = 0;
- while (ascii_iswhite(ptr[leadcol])) {
- leadcol++;
- }
- if (ptr[leadcol] == NUL) {
- // in a line full of spaces all of them are treated as trailing
- leadcol = (colnr_T)0;
- } else {
- // keep track of the first column not filled with spaces
- leadcol += (colnr_T)(ptr - line) + 1;
- }
- }
- }
-
- // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
- // first character to be displayed.
- if (wp->w_p_wrap) {
- v = wp->w_skipcol;
- } else {
- v = wp->w_leftcol;
- }
- if (v > 0 && !number_only) {
- char_u *prev_ptr = ptr;
- while (vcol < v && *ptr != NUL) {
- c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL);
- vcol += c;
- prev_ptr = ptr;
- MB_PTR_ADV(ptr);
- }
-
- // When:
- // - 'cuc' is set, or
- // - 'colorcolumn' is set, or
- // - 'virtualedit' is set, or
- // - the visual mode is active,
- // the end of the line may be before the start of the displayed part.
- if (vcol < v && (wp->w_p_cuc
- || draw_color_col
- || virtual_active()
- || (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
- vcol = v;
- }
-
- // Handle a character that's not completely on the screen: Put ptr at
- // that character but skip the first few screen characters.
- if (vcol > v) {
- vcol -= c;
- ptr = prev_ptr;
- // If the character fits on the screen, don't need to skip it.
- // Except for a TAB.
- if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) {
- n_skip = (int)(v - vcol);
- }
- }
-
- // Adjust for when the inverted text is before the screen,
- // and when the start of the inverted text is before the screen.
- if (tocol <= vcol) {
- fromcol = 0;
- } else if (fromcol >= 0 && fromcol < vcol) {
- fromcol = (int)vcol;
- }
-
- // When w_skipcol is non-zero, first line needs 'showbreak'
- if (wp->w_p_wrap) {
- need_showbreak = true;
- }
- // When spell checking a word we need to figure out the start of the
- // word and if it's badly spelled or not.
- if (has_spell) {
- size_t len;
- colnr_T linecol = (colnr_T)(ptr - line);
- hlf_T spell_hlf = HLF_COUNT;
-
- pos = wp->w_cursor;
- wp->w_cursor.lnum = lnum;
- wp->w_cursor.col = linecol;
- len = spell_move_to(wp, FORWARD, true, true, &spell_hlf);
-
- // spell_move_to() may call ml_get() and make "line" invalid
- line = ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line + linecol;
-
- if (len == 0 || (int)wp->w_cursor.col > ptr - line) {
- // no bad word found at line start, don't check until end of a
- // word
- spell_hlf = HLF_COUNT;
- word_end = (int)(spell_to_word_end(ptr, wp) - line + 1);
- } else {
- // bad word found, use attributes until end of word
- assert(len <= INT_MAX);
- word_end = wp->w_cursor.col + (int)len + 1;
-
- // Turn index into actual attributes.
- if (spell_hlf != HLF_COUNT) {
- spell_attr = highlight_attr[spell_hlf];
- }
- }
- wp->w_cursor = pos;
-
- // Need to restart syntax highlighting for this line.
- if (has_syntax) {
- syntax_start(wp, lnum);
- }
- }
- }
-
- // Correct highlighting for cursor that can't be disabled.
- // Avoids having to check this for each character.
- if (fromcol >= 0) {
- if (noinvcur) {
- if ((colnr_T)fromcol == wp->w_virtcol) {
- // highlighting starts at cursor, let it start just after the
- // cursor
- fromcol_prev = fromcol;
- fromcol = -1;
- } else if ((colnr_T)fromcol < wp->w_virtcol) {
- // restart highlighting after the cursor
- fromcol_prev = wp->w_virtcol;
- }
- }
- if (fromcol >= tocol) {
- fromcol = -1;
- }
- }
-
- if (!number_only && !has_fold && !end_fill) {
- v = ptr - line;
- area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
- &line, &search_hl, &search_attr,
- &search_attr_from_match);
- ptr = line + v; // "line" may have been updated
- }
-
- int off = 0; // Offset relative start of line
- int col = 0; // Visual column on screen.
- if (wp->w_p_rl) {
- // Rightleft window: process the text in the normal direction, but put
- // it in linebuf_char[off] from right to left. Start at the
- // rightmost column of the window.
- col = grid->cols - 1;
- off += col;
- }
-
- // won't highlight after TERM_ATTRS_MAX columns
- int term_attrs[TERM_ATTRS_MAX] = { 0 };
- if (wp->w_buffer->terminal) {
- terminal_get_line_attributes(wp->w_buffer->terminal, wp, lnum, term_attrs);
- extra_check = true;
- }
-
- int sign_idx = 0;
- // Repeat for the whole displayed line.
- for (;;) {
- int has_match_conc = 0; ///< match wants to conceal
- int decor_conceal = 0;
-
- bool did_decrement_ptr = false;
-
- // Skip this quickly when working on the text.
- if (draw_state != WL_LINE) {
- if (cul_screenline) {
- cul_attr = 0;
- line_attr = line_attr_save;
- line_attr_lowprio = line_attr_lowprio_save;
- }
-
- if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
- draw_state = WL_CMDLINE;
- if (cmdwin_type != 0 && wp == curwin) {
- // Draw the cmdline character.
- n_extra = 1;
- c_extra = cmdwin_type;
- c_final = NUL;
- char_attr = win_hl_attr(wp, HLF_AT);
- }
- }
-
- if (draw_state == WL_FOLD - 1 && n_extra == 0) {
- int fdc = compute_foldcolumn(wp, 0);
-
- draw_state = WL_FOLD;
- if (fdc > 0) {
- // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
- // already be in use.
- xfree(p_extra_free);
- p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1);
- n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
- p_extra_free[n_extra] = NUL;
- p_extra = p_extra_free;
- c_extra = NUL;
- c_final = NUL;
- if (use_cursor_line_sign(wp, lnum)) {
- char_attr = win_hl_attr(wp, HLF_CLF);
- } else {
- char_attr = win_hl_attr(wp, HLF_FC);
- }
- }
- }
-
- // sign column, this is hit until sign_idx reaches count
- if (draw_state == WL_SIGN - 1 && n_extra == 0) {
- draw_state = WL_SIGN;
- // Show the sign column when there are any signs in this
- // buffer or when using Netbeans.
- if (wp->w_scwidth > 0) {
- get_sign_display_info(false, wp, lnum, sattrs, row,
- startrow, filler_lines, filler_todo,
- &c_extra, &c_final, extra, sizeof(extra),
- &p_extra, &n_extra, &char_attr, sign_idx);
- sign_idx++;
- if (sign_idx < wp->w_scwidth) {
- draw_state = WL_SIGN - 1;
- } else {
- sign_idx = 0;
- }
- }
- }
-
- if (draw_state == WL_NR - 1 && n_extra == 0) {
- draw_state = WL_NR;
- // Display the absolute or relative line number. After the
- // first fill with blanks when the 'n' flag isn't in 'cpo'
- if ((wp->w_p_nu || wp->w_p_rnu)
- && (row == startrow + filler_lines
- || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) {
- // If 'signcolumn' is set to 'number' and a sign is present
- // in 'lnum', then display the sign instead of the line
- // number.
- if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'
- && num_signs > 0 && sign_get_attr(SIGN_TEXT, sattrs, 0, 1)) {
- get_sign_display_info(true, wp, lnum, sattrs, row,
- startrow, filler_lines, filler_todo,
- &c_extra, &c_final, extra, sizeof(extra),
- &p_extra, &n_extra, &char_attr, sign_idx);
- } else {
- // Draw the line number (empty space after wrapping).
- if (row == startrow + filler_lines) {
- get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
- if (wp->w_skipcol > 0) {
- for (p_extra = extra; *p_extra == ' '; p_extra++) {
- *p_extra = '-';
- }
- }
- if (wp->w_p_rl) { // reverse line numbers
- // like rl_mirror(), but keep the space at the end
- char_u *p2 = (char_u *)skipwhite((char *)extra);
- p2 = skiptowhite(p2) - 1;
- for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
- const char_u t = *p1;
- *p1 = *p2;
- *p2 = t;
- }
- }
- p_extra = extra;
- c_extra = NUL;
- } else {
- c_extra = ' ';
- }
- c_final = NUL;
- n_extra = number_width(wp) + 1;
- char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines, sattrs);
- }
- }
- }
-
- if (draw_state == WL_NR && n_extra == 0) {
- win_col_offset = off;
- }
-
- if (wp->w_briopt_sbr && draw_state == WL_BRI - 1
- && n_extra == 0 && *get_showbreak_value(wp) != NUL) {
- // draw indent after showbreak value
- draw_state = WL_BRI;
- } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) {
- // after the showbreak, draw the breakindent
- draw_state = WL_BRI - 1;
- }
-
- // draw 'breakindent': indent wrapped text accordingly
- if (draw_state == WL_BRI - 1 && n_extra == 0) {
- draw_state = WL_BRI;
- // if need_showbreak is set, breakindent also applies
- if (wp->w_p_bri && (row != startrow || need_showbreak)
- && filler_lines == 0) {
- char_attr = 0;
-
- if (diff_hlf != (hlf_T)0) {
- char_attr = win_hl_attr(wp, (int)diff_hlf);
- }
- p_extra = NULL;
- c_extra = ' ';
- c_final = NUL;
- n_extra =
- get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false));
- if (row == startrow) {
- n_extra -= win_col_off2(wp);
- if (n_extra < 0) {
- n_extra = 0;
- }
- }
- if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
- need_showbreak = false;
- }
- // Correct end of highlighted area for 'breakindent',
- // required wen 'linebreak' is also set.
- if (tocol == vcol) {
- tocol += n_extra;
- }
- }
- }
-
- if (draw_state == WL_SBR - 1 && n_extra == 0) {
- draw_state = WL_SBR;
- if (filler_todo > filler_lines - n_virt_lines) {
- // TODO(bfredl): check this doesn't inhibit TUI-style
- // clear-to-end-of-line.
- c_extra = ' ';
- c_final = NUL;
- if (wp->w_p_rl) {
- n_extra = col + 1;
- } else {
- n_extra = grid->cols - col;
- }
- char_attr = 0;
- } else if (filler_todo > 0) {
- // draw "deleted" diff line(s)
- if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
- c_extra = '-';
- c_final = NUL;
- } else {
- c_extra = wp->w_p_fcs_chars.diff;
- c_final = NUL;
- }
- if (wp->w_p_rl) {
- n_extra = col + 1;
- } else {
- n_extra = grid->cols - col;
- }
- char_attr = win_hl_attr(wp, HLF_DED);
- }
- char_u *const sbr = get_showbreak_value(wp);
- if (*sbr != NUL && need_showbreak) {
- // Draw 'showbreak' at the start of each broken line.
- p_extra = sbr;
- c_extra = NUL;
- c_final = NUL;
- n_extra = (int)STRLEN(sbr);
- char_attr = win_hl_attr(wp, HLF_AT);
- if (wp->w_skipcol == 0 || !wp->w_p_wrap) {
- need_showbreak = false;
- }
- vcol_sbr = vcol + mb_charlen(sbr);
- // Correct end of highlighted area for 'showbreak',
- // required when 'linebreak' is also set.
- if (tocol == vcol) {
- tocol += n_extra;
- }
- // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
- if (cul_attr) {
- char_attr = hl_combine_attr(cul_attr, char_attr);
- }
- }
- }
-
- if (draw_state == WL_LINE - 1 && n_extra == 0) {
- sign_idx = 0;
- draw_state = WL_LINE;
-
- if (has_decor && row == startrow + filler_lines) {
- // hide virt_text on text hidden by 'nowrap'
- decor_redraw_col(wp->w_buffer, (int)vcol, off, true, &decor_state);
- }
-
- if (saved_n_extra) {
- // Continue item from end of wrapped line.
- n_extra = saved_n_extra;
- c_extra = saved_c_extra;
- c_final = saved_c_final;
- p_extra = saved_p_extra;
- char_attr = saved_char_attr;
- } else {
- char_attr = 0;
- }
- }
- }
-
- if (cul_screenline && draw_state == WL_LINE
- && vcol >= left_curline_col
- && vcol < right_curline_col) {
- cul_attr = win_hl_attr(wp, HLF_CUL);
- HlAttrs ae = syn_attr2entry(cul_attr);
- if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
- line_attr_lowprio = cul_attr;
- } else {
- if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
- && qf_current_entry(wp) == lnum) {
- line_attr = hl_combine_attr(cul_attr, line_attr);
- } else {
- line_attr = cul_attr;
- }
- }
- }
-
- // When still displaying '$' of change command, stop at cursor
- if (((dollar_vcol >= 0
- && wp == curwin
- && lnum == wp->w_cursor.lnum
- && vcol >= (long)wp->w_virtcol)
- || (number_only && draw_state > WL_NR))
- && filler_todo <= 0) {
- draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
- grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp,
- wp->w_hl_attr_normal, false);
- // Pretend we have finished updating the window. Except when
- // 'cursorcolumn' is set.
- if (wp->w_p_cuc) {
- row = wp->w_cline_row + wp->w_cline_height;
- } else {
- row = grid->rows;
- }
- break;
- }
-
- if (draw_state == WL_LINE
- && has_fold
- && col == win_col_offset
- && n_extra == 0
- && row == startrow) {
- char_attr = win_hl_attr(wp, HLF_FL);
-
- linenr_T lnume = lnum + foldinfo.fi_lines - 1;
- memset(buf_fold, ' ', FOLD_TEXT_LEN);
- p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
- n_extra = (int)STRLEN(p_extra);
-
- if (p_extra != buf_fold) {
- xfree(p_extra_free);
- p_extra_free = p_extra;
- }
- c_extra = NUL;
- c_final = NUL;
- p_extra[n_extra] = NUL;
- }
-
- if (draw_state == WL_LINE
- && has_fold
- && col < grid->cols
- && n_extra == 0
- && row == startrow) {
- // fill rest of line with 'fold'
- c_extra = wp->w_p_fcs_chars.fold;
- c_final = NUL;
-
- n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col);
- }
-
- if (draw_state == WL_LINE
- && has_fold
- && col >= grid->cols
- && n_extra != 0
- && row == startrow) {
- // Truncate the folding.
- n_extra = 0;
- }
-
- if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
- // handle Visual or match highlighting in this line
- if (vcol == fromcol
- || (vcol + 1 == fromcol && n_extra == 0
- && utf_ptr2cells((char *)ptr) > 1)
- || ((int)vcol_prev == fromcol_prev
- && vcol_prev < vcol // not at margin
- && vcol < tocol)) {
- area_attr = attr; // start highlighting
- if (area_highlighting) {
- area_active = true;
- }
- } else if (area_attr != 0 && (vcol == tocol
- || (noinvcur
- && (colnr_T)vcol == wp->w_virtcol))) {
- area_attr = 0; // stop highlighting
- area_active = false;
- }
-
- if (!n_extra) {
- // Check for start/end of 'hlsearch' and other matches.
- // After end, check for start/end of next match.
- // When another match, have to check for start again.
- v = (ptr - line);
- search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc,
- &match_conc, lcs_eol_one, &search_attr_from_match);
- ptr = line + v; // "line" may have been changed
-
- // Do not allow a conceal over EOL otherwise EOL will be missed
- // and bad things happen.
- if (*ptr == NUL) {
- has_match_conc = 0;
- }
- }
-
- if (diff_hlf != (hlf_T)0) {
- if (diff_hlf == HLF_CHD && ptr - line >= change_start
- && n_extra == 0) {
- diff_hlf = HLF_TXD; // changed text
- }
- if (diff_hlf == HLF_TXD && ptr - line > change_end
- && n_extra == 0) {
- diff_hlf = HLF_CHD; // changed line
- }
- line_attr = win_hl_attr(wp, (int)diff_hlf);
- // Overlay CursorLine onto diff-mode highlight.
- if (cul_attr) {
- line_attr = 0 != line_attr_lowprio // Low-priority CursorLine
- ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr),
- hl_get_underline())
- : hl_combine_attr(line_attr, cul_attr);
- }
- }
-
- // Decide which of the highlight attributes to use.
- attr_pri = true;
-
- if (area_attr != 0) {
- char_attr = hl_combine_attr(line_attr, area_attr);
- if (!highlight_match) {
- // let search highlight show in Visual area if possible
- char_attr = hl_combine_attr(search_attr, char_attr);
- }
- } else if (search_attr != 0) {
- char_attr = hl_combine_attr(line_attr, search_attr);
- } else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL)
- || vcol < fromcol || vcol_prev < fromcol_prev
- || vcol >= tocol)) {
- // Use line_attr when not in the Visual or 'incsearch' area
- // (area_attr may be 0 when "noinvcur" is set).
- char_attr = line_attr;
- } else {
- attr_pri = false;
- if (has_syntax) {
- char_attr = syntax_attr;
- } else {
- char_attr = 0;
- }
- }
- }
-
- // Get the next character to put on the screen.
- //
- // The "p_extra" points to the extra stuff that is inserted to
- // represent special characters (non-printable stuff) and other
- // things. When all characters are the same, c_extra is used.
- // If c_final is set, it will compulsorily be used at the end.
- // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past
- // "p_extra[n_extra]".
- // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
- if (n_extra > 0) {
- if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
- c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
- mb_c = c; // doesn't handle non-utf-8 multi-byte!
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- } else {
- assert(p_extra != NULL);
- c = *p_extra;
- mb_c = c;
- // If the UTF-8 character is more than one byte:
- // Decode it into "mb_c".
- mb_l = utfc_ptr2len((char *)p_extra);
- mb_utf8 = false;
- if (mb_l > n_extra) {
- mb_l = 1;
- } else if (mb_l > 1) {
- mb_c = utfc_ptr2char(p_extra, u8cc);
- mb_utf8 = true;
- c = 0xc0;
- }
- if (mb_l == 0) { // at the NUL at end-of-line
- mb_l = 1;
- }
-
- // If a double-width char doesn't fit display a '>' in the last column.
- if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1))
- && utf_char2cells(mb_c) == 2) {
- c = '>';
- mb_c = c;
- mb_l = 1;
- (void)mb_l;
- multi_attr = win_hl_attr(wp, HLF_AT);
-
- if (cul_attr) {
- multi_attr = 0 != line_attr_lowprio
- ? hl_combine_attr(cul_attr, multi_attr)
- : hl_combine_attr(multi_attr, cul_attr);
- }
-
- // put the pointer back to output the double-width
- // character at the start of the next line.
- n_extra++;
- p_extra--;
- } else {
- n_extra -= mb_l - 1;
- p_extra += mb_l - 1;
- }
- p_extra++;
- }
- n_extra--;
- } else if (foldinfo.fi_lines > 0) {
- // skip writing the buffer line itself
- c = NUL;
- XFREE_CLEAR(p_extra_free);
- } else {
- int c0;
-
- XFREE_CLEAR(p_extra_free);
-
- // Get a character from the line itself.
- c0 = c = *ptr;
- mb_c = c;
- // If the UTF-8 character is more than one byte: Decode it
- // into "mb_c".
- mb_l = utfc_ptr2len((char *)ptr);
- mb_utf8 = false;
- if (mb_l > 1) {
- mb_c = utfc_ptr2char(ptr, u8cc);
- // Overlong encoded ASCII or ASCII with composing char
- // is displayed normally, except a NUL.
- if (mb_c < 0x80) {
- c0 = c = mb_c;
- }
- mb_utf8 = true;
-
- // At start of the line we can have a composing char.
- // Draw it as a space with a composing char.
- if (utf_iscomposing(mb_c)) {
- int i;
-
- for (i = MAX_MCO - 1; i > 0; i--) {
- u8cc[i] = u8cc[i - 1];
- }
- u8cc[0] = mb_c;
- mb_c = ' ';
- }
- }
-
- if ((mb_l == 1 && c >= 0x80)
- || (mb_l >= 1 && mb_c == 0)
- || (mb_l > 1 && (!vim_isprintc(mb_c)))) {
- // Illegal UTF-8 byte: display as <xx>.
- // Non-BMP character : display as ? or fullwidth ?.
- transchar_hex((char *)extra, mb_c);
- if (wp->w_p_rl) { // reverse
- rl_mirror(extra);
- }
-
- p_extra = extra;
- c = *p_extra;
- mb_c = mb_ptr2char_adv((const char_u **)&p_extra);
- mb_utf8 = (c >= 0x80);
- n_extra = (int)STRLEN(p_extra);
- c_extra = NUL;
- c_final = NUL;
- if (area_attr == 0 && search_attr == 0) {
- n_attr = n_extra + 1;
- extra_attr = win_hl_attr(wp, HLF_8);
- saved_attr2 = char_attr; // save current attr
- }
- } else if (mb_l == 0) { // at the NUL at end-of-line
- mb_l = 1;
- } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) {
- // Do Arabic shaping.
- int pc, pc1, nc;
- int pcc[MAX_MCO];
-
- // The idea of what is the previous and next
- // character depends on 'rightleft'.
- if (wp->w_p_rl) {
- pc = prev_c;
- pc1 = prev_c1;
- nc = utf_ptr2char((char *)ptr + mb_l);
- prev_c1 = u8cc[0];
- } else {
- pc = utfc_ptr2char(ptr + mb_l, pcc);
- nc = prev_c;
- pc1 = pcc[0];
- }
- prev_c = mb_c;
-
- mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc);
- } else {
- prev_c = mb_c;
- }
- // If a double-width char doesn't fit display a '>' in the
- // last column; the character is displayed at the start of the
- // next line.
- if ((wp->w_p_rl ? (col <= 0) :
- (col >= grid->cols - 1))
- && utf_char2cells(mb_c) == 2) {
- c = '>';
- mb_c = c;
- mb_utf8 = false;
- mb_l = 1;
- multi_attr = win_hl_attr(wp, HLF_AT);
- // Put pointer back so that the character will be
- // displayed at the start of the next line.
- ptr--;
- did_decrement_ptr = true;
- } else if (*ptr != NUL) {
- ptr += mb_l - 1;
- }
-
- // If a double-width char doesn't fit at the left side display a '<' in
- // the first column. Don't do this for unprintable characters.
- if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
- n_extra = 1;
- c_extra = MB_FILLER_CHAR;
- c_final = NUL;
- c = ' ';
- if (area_attr == 0 && search_attr == 0) {
- n_attr = n_extra + 1;
- extra_attr = win_hl_attr(wp, HLF_AT);
- saved_attr2 = char_attr; // save current attr
- }
- mb_c = c;
- mb_utf8 = false;
- mb_l = 1;
- }
- ptr++;
-
- if (extra_check) {
- bool can_spell = true;
-
- // Get syntax attribute, unless still at the start of the line
- // (double-wide char that doesn't fit).
- v = (ptr - line);
- if (has_syntax && v > 0) {
- // Get the syntax attribute for the character. If there
- // is an error, disable syntax highlighting.
- save_did_emsg = did_emsg;
- did_emsg = false;
-
- syntax_attr = get_syntax_attr((colnr_T)v - 1,
- has_spell ? &can_spell : NULL, false);
-
- if (did_emsg) {
- wp->w_s->b_syn_error = true;
- has_syntax = false;
- } else {
- did_emsg = save_did_emsg;
- }
-
- if (wp->w_s->b_syn_slow) {
- has_syntax = false;
- }
-
- // Need to get the line again, a multi-line regexp may
- // have made it invalid.
- line = ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line + v;
-
- if (!attr_pri) {
- if (cul_attr) {
- char_attr = 0 != line_attr_lowprio
- ? hl_combine_attr(cul_attr, syntax_attr)
- : hl_combine_attr(syntax_attr, cul_attr);
- } else {
- char_attr = syntax_attr;
- }
- } else {
- char_attr = hl_combine_attr(syntax_attr, char_attr);
- }
- // no concealing past the end of the line, it interferes
- // with line highlighting.
- if (c == NUL) {
- syntax_flags = 0;
- } else {
- syntax_flags = get_syntax_info(&syntax_seqnr);
- }
- } else if (!attr_pri) {
- char_attr = 0;
- }
-
- // Check spelling (unless at the end of the line).
- // Only do this when there is no syntax highlighting, the
- // @Spell cluster is not used or the current syntax item
- // contains the @Spell cluster.
- v = (ptr - line);
- if (has_spell && v >= word_end && v > cur_checked_col) {
- spell_attr = 0;
- if (!attr_pri) {
- char_attr = syntax_attr;
- }
- if (c != 0 && (!has_syntax || can_spell)) {
- char_u *prev_ptr;
- char_u *p;
- int len;
- hlf_T spell_hlf = HLF_COUNT;
- prev_ptr = ptr - mb_l;
- v -= mb_l - 1;
-
- // Use nextline[] if possible, it has the start of the
- // next line concatenated.
- if ((prev_ptr - line) - nextlinecol >= 0) {
- p = nextline + ((prev_ptr - line) - nextlinecol);
- } else {
- p = prev_ptr;
- }
- cap_col -= (int)(prev_ptr - line);
- size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange);
- assert(tmplen <= INT_MAX);
- len = (int)tmplen;
- word_end = (int)v + len;
-
- // In Insert mode only highlight a word that
- // doesn't touch the cursor.
- if (spell_hlf != HLF_COUNT
- && (State & MODE_INSERT)
- && wp->w_cursor.lnum == lnum
- && wp->w_cursor.col >=
- (colnr_T)(prev_ptr - line)
- && wp->w_cursor.col < (colnr_T)word_end) {
- spell_hlf = HLF_COUNT;
- spell_redraw_lnum = lnum;
- }
-
- if (spell_hlf == HLF_COUNT && p != prev_ptr
- && (p - nextline) + len > nextline_idx) {
- // Remember that the good word continues at the
- // start of the next line.
- checked_lnum = lnum + 1;
- checked_col = (int)((p - nextline) + len - nextline_idx);
- }
-
- // Turn index into actual attributes.
- if (spell_hlf != HLF_COUNT) {
- spell_attr = highlight_attr[spell_hlf];
- }
-
- if (cap_col > 0) {
- if (p != prev_ptr
- && (p - nextline) + cap_col >= nextline_idx) {
- // Remember that the word in the next line
- // must start with a capital.
- capcol_lnum = lnum + 1;
- cap_col = (int)((p - nextline) + cap_col
- - nextline_idx);
- } else {
- // Compute the actual column.
- cap_col += (int)(prev_ptr - line);
- }
- }
- }
- }
- if (spell_attr != 0) {
- if (!attr_pri) {
- char_attr = hl_combine_attr(char_attr, spell_attr);
- } else {
- char_attr = hl_combine_attr(spell_attr, char_attr);
- }
- }
-
- if (wp->w_buffer->terminal) {
- char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
- }
-
- if (has_decor && v > 0) {
- bool selected = (area_active || (area_highlighting && noinvcur
- && (colnr_T)vcol == wp->w_virtcol));
- int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off,
- selected, &decor_state);
- if (extmark_attr != 0) {
- if (!attr_pri) {
- char_attr = hl_combine_attr(char_attr, extmark_attr);
- } else {
- char_attr = hl_combine_attr(extmark_attr, char_attr);
- }
- }
-
- decor_conceal = decor_state.conceal;
- if (decor_conceal && decor_state.conceal_char) {
- decor_conceal = 2; // really??
- }
- }
-
- // Found last space before word: check for line break.
- if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
- && !vim_isbreak((int)(*ptr))) {
- int mb_off = utf_head_off(line, ptr - 1);
- char_u *p = ptr - (mb_off + 1);
- // TODO(neovim): is passing p for start of the line OK?
- n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
-
- // We have just drawn the showbreak value, no need to add
- // space for it again.
- if (vcol == vcol_sbr) {
- n_extra -= mb_charlen(get_showbreak_value(wp));
- if (n_extra < 0) {
- n_extra = 0;
- }
- }
-
- if (c == TAB && n_extra + col > grid->cols) {
- n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array) - 1;
- }
- c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
- c_final = NUL;
- if (ascii_iswhite(c)) {
- if (c == TAB) {
- // See "Tab alignment" below.
- FIX_FOR_BOGUSCOLS;
- }
- if (!wp->w_p_list) {
- c = ' ';
- }
- }
- }
-
- in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
- if (!in_multispace) {
- multispace_pos = 0;
- }
-
- // 'list': Change char 160 to 'nbsp' and space to 'space'.
- // But not when the character is followed by a composing
- // character (use mb_l to check that).
- if (wp->w_p_list
- && ((((c == 160 && mb_l == 1)
- || (mb_utf8
- && ((mb_c == 160 && mb_l == 2)
- || (mb_c == 0x202f && mb_l == 3))))
- && wp->w_p_lcs_chars.nbsp)
- || (c == ' '
- && mb_l == 1
- && (wp->w_p_lcs_chars.space
- || (in_multispace && wp->w_p_lcs_chars.multispace != NULL))
- && ptr - line >= leadcol
- && ptr - line <= trailcol))) {
- if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) {
- c = wp->w_p_lcs_chars.multispace[multispace_pos++];
- if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
- multispace_pos = 0;
- }
- } else {
- c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
- }
- n_attr = 1;
- extra_attr = win_hl_attr(wp, HLF_0);
- saved_attr2 = char_attr; // save current attr
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- }
-
- if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol)
- || (leadcol != 0 && ptr < line + leadcol))) {
- if (leadcol != 0 && in_multispace && ptr < line + leadcol
- && wp->w_p_lcs_chars.leadmultispace != NULL) {
- c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
- if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
- multispace_pos = 0;
- }
- } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
- c = wp->w_p_lcs_chars.trail;
- } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
- c = wp->w_p_lcs_chars.lead;
- } else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
- c = wp->w_p_lcs_chars.space;
- }
-
- n_attr = 1;
- extra_attr = win_hl_attr(wp, HLF_0);
- saved_attr2 = char_attr; // save current attr
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- }
- }
-
- // Handling of non-printable characters.
- if (!vim_isprintc(c)) {
- // when getting a character from the file, we may have to
- // turn it into something else on the way to putting it on the screen.
- if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- int tab_len = 0;
- long vcol_adjusted = vcol; // removed showbreak length
- char_u *const sbr = get_showbreak_value(wp);
-
- // Only adjust the tab_len, when at the first column after the
- // showbreak value was drawn.
- if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) {
- vcol_adjusted = vcol - mb_charlen(sbr);
- }
- // tab amount depends on current column
- tab_len = tabstop_padding((colnr_T)vcol_adjusted,
- wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array) - 1;
-
- if (!wp->w_p_lbr || !wp->w_p_list) {
- n_extra = tab_len;
- } else {
- char_u *p;
- int i;
- int saved_nextra = n_extra;
-
- if (vcol_off > 0) {
- // there are characters to conceal
- tab_len += vcol_off;
- }
- // boguscols before FIX_FOR_BOGUSCOLS macro from above.
- if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
- && n_extra > tab_len) {
- tab_len += n_extra - tab_len;
- }
-
- // If n_extra > 0, it gives the number of chars
- // to use for a tab, else we need to calculate the width
- // for a tab.
- int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2));
- if (wp->w_p_lcs_chars.tab3) {
- len += utf_char2len(wp->w_p_lcs_chars.tab3);
- }
- if (n_extra > 0) {
- len += n_extra - tab_len;
- }
- c = wp->w_p_lcs_chars.tab1;
- p = xmalloc((size_t)len + 1);
- memset(p, ' ', (size_t)len);
- p[len] = NUL;
- xfree(p_extra_free);
- p_extra_free = p;
- for (i = 0; i < tab_len; i++) {
- if (*p == NUL) {
- tab_len = i;
- break;
- }
- int lcs = wp->w_p_lcs_chars.tab2;
-
- // if tab3 is given, use it for the last char
- if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
- lcs = wp->w_p_lcs_chars.tab3;
- }
- p += utf_char2bytes(lcs, (char *)p);
- n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
- }
- p_extra = p_extra_free;
-
- // n_extra will be increased by FIX_FOX_BOGUSCOLS
- // macro below, so need to adjust for that here
- if (vcol_off > 0) {
- n_extra -= vcol_off;
- }
- }
-
- {
- int vc_saved = vcol_off;
-
- // Tab alignment should be identical regardless of
- // 'conceallevel' value. So tab compensates of all
- // previous concealed characters, and thus resets
- // vcol_off and boguscols accumulated so far in the
- // line. Note that the tab can be longer than
- // 'tabstop' when there are concealed characters.
- FIX_FOR_BOGUSCOLS;
-
- // Make sure, the highlighting for the tab char will be
- // correctly set further below (effectively reverts the
- // FIX_FOR_BOGSUCOLS macro).
- if (n_extra == tab_len + vc_saved && wp->w_p_list
- && wp->w_p_lcs_chars.tab1) {
- tab_len += vc_saved;
- }
- }
-
- mb_utf8 = false; // don't draw as UTF-8
- if (wp->w_p_list) {
- c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
- ? wp->w_p_lcs_chars.tab3
- : wp->w_p_lcs_chars.tab1;
- if (wp->w_p_lbr) {
- c_extra = NUL; // using p_extra from above
- } else {
- c_extra = wp->w_p_lcs_chars.tab2;
- }
- c_final = wp->w_p_lcs_chars.tab3;
- n_attr = tab_len + 1;
- extra_attr = win_hl_attr(wp, HLF_0);
- saved_attr2 = char_attr; // save current attr
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- }
- } else {
- c_final = NUL;
- c_extra = ' ';
- c = ' ';
- }
- } else if (c == NUL
- && (wp->w_p_list
- || ((fromcol >= 0 || fromcol_prev >= 0)
- && tocol > vcol
- && VIsual_mode != Ctrl_V
- && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))
- && !(noinvcur
- && lnum == wp->w_cursor.lnum
- && (colnr_T)vcol == wp->w_virtcol)))
- && lcs_eol_one > 0) {
- // Display a '$' after the line or highlight an extra
- // character if the line break is included.
- // For a diff line the highlighting continues after the "$".
- if (diff_hlf == (hlf_T)0
- && line_attr == 0
- && line_attr_lowprio == 0) {
- // In virtualedit, visual selections may extend beyond end of line
- if (area_highlighting && virtual_active()
- && tocol != MAXCOL && vcol < tocol) {
- n_extra = 0;
- } else {
- p_extra = at_end_str;
- n_extra = 1;
- c_extra = NUL;
- c_final = NUL;
- }
- }
- if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
- c = wp->w_p_lcs_chars.eol;
- } else {
- c = ' ';
- }
- lcs_eol_one = -1;
- ptr--; // put it back at the NUL
- extra_attr = win_hl_attr(wp, HLF_AT);
- n_attr = 1;
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false; // don't draw as UTF-8
- }
- } else if (c != NUL) {
- p_extra = transchar_buf(wp->w_buffer, c);
- if (n_extra == 0) {
- n_extra = byte2cells(c) - 1;
- }
- if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
- rl_mirror(p_extra); // reverse "<12>"
- }
- c_extra = NUL;
- c_final = NUL;
- if (wp->w_p_lbr) {
- char_u *p;
-
- c = *p_extra;
- p = xmalloc((size_t)n_extra + 1);
- memset(p, ' ', (size_t)n_extra);
- STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf)
- p[n_extra] = NUL;
- xfree(p_extra_free);
- p_extra_free = p_extra = p;
- } else {
- n_extra = byte2cells(c) - 1;
- c = *p_extra++;
- }
- n_attr = n_extra + 1;
- extra_attr = win_hl_attr(wp, HLF_8);
- saved_attr2 = char_attr; // save current attr
- mb_utf8 = false; // don't draw as UTF-8
- } else if (VIsual_active
- && (VIsual_mode == Ctrl_V || VIsual_mode == 'v')
- && virtual_active()
- && tocol != MAXCOL
- && vcol < tocol
- && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) {
- c = ' ';
- ptr--; // put it back at the NUL
- }
- }
-
- if (wp->w_p_cole > 0
- && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp))
- && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)
- && !(lnum_in_visual_area && vim_strchr((char *)wp->w_p_cocu, 'v') == NULL)) {
- char_attr = conceal_attr;
- if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
- || has_match_conc > 1 || decor_conceal > 1)
- && (syn_get_sub_char() != NUL
- || (has_match_conc && match_conc)
- || (decor_conceal && decor_state.conceal_char)
- || wp->w_p_cole == 1)
- && wp->w_p_cole != 3) {
- // First time at this concealed item: display one
- // character.
- if (has_match_conc && match_conc) {
- c = match_conc;
- } else if (decor_conceal && decor_state.conceal_char) {
- c = decor_state.conceal_char;
- if (decor_state.conceal_attr) {
- char_attr = decor_state.conceal_attr;
- }
- } else if (syn_get_sub_char() != NUL) {
- c = syn_get_sub_char();
- } else if (wp->w_p_lcs_chars.conceal != NUL) {
- c = wp->w_p_lcs_chars.conceal;
- } else {
- c = ' ';
- }
-
- prev_syntax_id = syntax_seqnr;
-
- if (n_extra > 0) {
- vcol_off += n_extra;
- }
- vcol += n_extra;
- if (wp->w_p_wrap && n_extra > 0) {
- if (wp->w_p_rl) {
- col -= n_extra;
- boguscols -= n_extra;
- } else {
- boguscols += n_extra;
- col += n_extra;
- }
- }
- n_extra = 0;
- n_attr = 0;
- } else if (n_skip == 0) {
- is_concealing = true;
- n_skip = 1;
- }
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false; // don't draw as UTF-8
- }
- } else {
- prev_syntax_id = 0;
- is_concealing = false;
- }
-
- if (n_skip > 0 && did_decrement_ptr) {
- // not showing the '>', put pointer back to avoid getting stuck
- ptr++;
- }
- } // end of printing from buffer content
-
- // In the cursor line and we may be concealing characters: correct
- // the cursor column when we reach its position.
- if (!did_wcol && draw_state == WL_LINE
- && wp == curwin && lnum == wp->w_cursor.lnum
- && conceal_cursor_line(wp)
- && (int)wp->w_virtcol <= vcol + n_skip) {
- if (wp->w_p_rl) {
- wp->w_wcol = grid->cols - col + boguscols - 1;
- } else {
- wp->w_wcol = col - boguscols;
- }
- wp->w_wrow = row;
- did_wcol = true;
- wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
- }
-
- // Don't override visual selection highlighting.
- if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) {
- char_attr = hl_combine_attr(char_attr, extra_attr);
- }
-
- // Handle the case where we are in column 0 but not on the first
- // character of the line and the user wants us to show us a
- // special character (via 'listchars' option "precedes:<char>".
- if (lcs_prec_todo != NUL
- && wp->w_p_list
- && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0)
- && filler_todo <= 0
- && draw_state > WL_NR
- && c != NUL) {
- c = wp->w_p_lcs_chars.prec;
- lcs_prec_todo = NUL;
- if (utf_char2cells(mb_c) > 1) {
- // Double-width character being overwritten by the "precedes"
- // character, need to fill up half the character.
- c_extra = MB_FILLER_CHAR;
- c_final = NUL;
- n_extra = 1;
- n_attr = 2;
- extra_attr = win_hl_attr(wp, HLF_AT);
- }
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false; // don't draw as UTF-8
- }
- saved_attr3 = char_attr; // save current attr
- char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
- n_attr3 = 1;
- }
-
- // At end of the text line or just after the last character.
- if (c == NUL && eol_hl_off == 0) {
- // flag to indicate whether prevcol equals startcol of search_hl or
- // one of the matches
- bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl,
- (long)(ptr - line) - 1);
-
- // Invert at least one char, used for Visual and empty line or
- // highlight match at end of line. If it's beyond the last
- // char on the screen, just overwrite that one (tricky!) Not
- // needed when a '$' was displayed for 'list'.
- if (wp->w_p_lcs_chars.eol == lcs_eol_one
- && ((area_attr != 0 && vcol == fromcol
- && (VIsual_mode != Ctrl_V
- || lnum == VIsual.lnum
- || lnum == curwin->w_cursor.lnum))
- // highlight 'hlsearch' match at end of line
- || prevcol_hl_flag)) {
- int n = 0;
-
- if (wp->w_p_rl) {
- if (col < 0) {
- n = 1;
- }
- } else {
- if (col >= grid->cols) {
- n = -1;
- }
- }
- if (n != 0) {
- // At the window boundary, highlight the last character
- // instead (better than nothing).
- off += n;
- col += n;
- } else {
- // Add a blank character to highlight.
- schar_from_ascii(linebuf_char[off], ' ');
- }
- if (area_attr == 0 && !has_fold) {
- // Use attributes from match with highest priority among
- // 'search_hl' and the match list.
- get_search_match_hl(wp, &search_hl, (long)(ptr - line), &char_attr);
- }
-
- int eol_attr = char_attr;
- if (cul_attr) {
- eol_attr = hl_combine_attr(cul_attr, eol_attr);
- }
- linebuf_attr[off] = eol_attr;
- if (wp->w_p_rl) {
- col--;
- off--;
- } else {
- col++;
- off++;
- }
- vcol++;
- eol_hl_off = 1;
- }
- // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
- if (wp->w_p_wrap) {
- v = wp->w_skipcol;
- } else {
- v = wp->w_leftcol;
- }
-
- // check if line ends before left margin
- if (vcol < v + col - win_col_off(wp)) {
- vcol = v + col - win_col_off(wp);
- }
- // Get rid of the boguscols now, we want to draw until the right
- // edge for 'cursorcolumn'.
- col -= boguscols;
- // boguscols = 0; // Disabled because value never read after this
-
- if (draw_color_col) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- bool has_virttext = false;
- // Make sure alignment is the same regardless
- // if listchars=eol:X is used or not.
- int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0
- ? 1 : 0);
-
- if (has_decor) {
- has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr,
- col + eol_skip);
- }
-
- if (((wp->w_p_cuc
- && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
- && (int)wp->w_virtcol <
- (long)grid->cols * (row - startrow + 1) + v
- && lnum != wp->w_cursor.lnum)
- || draw_color_col || line_attr_lowprio || line_attr
- || diff_hlf != (hlf_T)0 || has_virttext)) {
- int rightmost_vcol = 0;
- int i;
-
- if (wp->w_p_cuc) {
- rightmost_vcol = wp->w_virtcol;
- }
-
- if (draw_color_col) {
- // determine rightmost colorcolumn to possibly draw
- for (i = 0; color_cols[i] >= 0; i++) {
- if (rightmost_vcol < color_cols[i]) {
- rightmost_vcol = color_cols[i];
- }
- }
- }
-
- int cuc_attr = win_hl_attr(wp, HLF_CUC);
- int mc_attr = win_hl_attr(wp, HLF_MC);
-
- int diff_attr = 0;
- if (diff_hlf == HLF_TXD) {
- diff_hlf = HLF_CHD;
- }
- if (diff_hlf != 0) {
- diff_attr = win_hl_attr(wp, (int)diff_hlf);
- }
-
- int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr);
- if (base_attr || line_attr || has_virttext) {
- rightmost_vcol = INT_MAX;
- }
-
- int col_stride = wp->w_p_rl ? -1 : 1;
-
- while (wp->w_p_rl ? col >= 0 : col < grid->cols) {
- schar_from_ascii(linebuf_char[off], ' ');
- col += col_stride;
- if (draw_color_col) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- int col_attr = base_attr;
-
- if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
- col_attr = cuc_attr;
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
- col_attr = mc_attr;
- }
-
- col_attr = hl_combine_attr(col_attr, line_attr);
-
- linebuf_attr[off] = col_attr;
- off += col_stride;
-
- if (VCOL_HLC >= rightmost_vcol) {
- break;
- }
-
- vcol += 1;
- }
- }
-
- // TODO(bfredl): integrate with the common beyond-the-end-loop
- if (wp->w_buffer->terminal) {
- // terminal buffers may need to highlight beyond the end of the
- // logical line
- int n = wp->w_p_rl ? -1 : 1;
- while (col >= 0 && col < grid->cols) {
- schar_from_ascii(linebuf_char[off], ' ');
- linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol];
- off += n;
- vcol += n;
- col += n;
- }
- }
-
- draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
- grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp,
- wp->w_hl_attr_normal, false);
- row++;
-
- // Update w_cline_height and w_cline_folded if the cursor line was
- // updated (saves a call to plines_win() later).
- if (wp == curwin && lnum == curwin->w_cursor.lnum) {
- curwin->w_cline_row = startrow;
- curwin->w_cline_height = row - startrow;
- curwin->w_cline_folded = foldinfo.fi_lines > 0;
- curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
- conceal_cursor_used = conceal_cursor_line(curwin);
- }
- break;
- }
-
- // Show "extends" character from 'listchars' if beyond the line end and
- // 'list' is set.
- if (wp->w_p_lcs_chars.ext != NUL
- && draw_state == WL_LINE
- && wp->w_p_list
- && !wp->w_p_wrap
- && filler_todo <= 0
- && (wp->w_p_rl ? col == 0 : col == grid->cols - 1)
- && !has_fold
- && (*ptr != NUL
- || lcs_eol_one > 0
- || (n_extra && (c_extra != NUL || *p_extra != NUL)))) {
- c = wp->w_p_lcs_chars.ext;
- char_attr = win_hl_attr(wp, HLF_AT);
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- }
-
- // advance to the next 'colorcolumn'
- if (draw_color_col) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- // Highlight the cursor column if 'cursorcolumn' is set. But don't
- // highlight the cursor position itself.
- // Also highlight the 'colorcolumn' if it is different than
- // 'cursorcolumn'
- // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'
- // options are set
- vcol_save_attr = -1;
- if ((draw_state == WL_LINE
- || draw_state == WL_BRI
- || draw_state == WL_SBR)
- && !lnum_in_visual_area
- && search_attr == 0
- && area_attr == 0
- && filler_todo <= 0) {
- if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
- && lnum != wp->w_cursor.lnum) {
- vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
- vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
- }
- }
-
- // Apply lowest-priority line attr now, so everything can override it.
- if (draw_state == WL_LINE) {
- char_attr = hl_combine_attr(line_attr_lowprio, char_attr);
- }
-
- // Store character to be displayed.
- // Skip characters that are left of the screen for 'nowrap'.
- vcol_prev = vcol;
- if (draw_state < WL_LINE || n_skip <= 0) {
- //
- // Store the character.
- //
- if (wp->w_p_rl && utf_char2cells(mb_c) > 1) {
- // A double-wide character is: put first half in left cell.
- off--;
- col--;
- }
- if (mb_utf8) {
- schar_from_cc(linebuf_char[off], mb_c, u8cc);
- } else {
- schar_from_ascii(linebuf_char[off], (char)c);
- }
- if (multi_attr) {
- linebuf_attr[off] = multi_attr;
- multi_attr = 0;
- } else {
- linebuf_attr[off] = char_attr;
- }
-
- if (utf_char2cells(mb_c) > 1) {
- // Need to fill two screen columns.
- off++;
- col++;
- // UTF-8: Put a 0 in the second screen char.
- linebuf_char[off][0] = 0;
- if (draw_state > WL_NR && filler_todo <= 0) {
- vcol++;
- }
- // When "tocol" is halfway through a character, set it to the end of
- // the character, otherwise highlighting won't stop.
- if (tocol == vcol) {
- tocol++;
- }
- if (wp->w_p_rl) {
- // now it's time to backup one cell
- off--;
- col--;
- }
- }
- if (wp->w_p_rl) {
- off--;
- col--;
- } else {
- off++;
- col++;
- }
- } else if (wp->w_p_cole > 0 && is_concealing) {
- n_skip--;
- vcol_off++;
- if (n_extra > 0) {
- vcol_off += n_extra;
- }
- if (wp->w_p_wrap) {
- // Special voodoo required if 'wrap' is on.
- //
- // Advance the column indicator to force the line
- // drawing to wrap early. This will make the line
- // take up the same screen space when parts are concealed,
- // so that cursor line computations aren't messed up.
- //
- // To avoid the fictitious advance of 'col' causing
- // trailing junk to be written out of the screen line
- // we are building, 'boguscols' keeps track of the number
- // of bad columns we have advanced.
- if (n_extra > 0) {
- vcol += n_extra;
- if (wp->w_p_rl) {
- col -= n_extra;
- boguscols -= n_extra;
- } else {
- col += n_extra;
- boguscols += n_extra;
- }
- n_extra = 0;
- n_attr = 0;
- }
-
- if (utf_char2cells(mb_c) > 1) {
- // Need to fill two screen columns.
- if (wp->w_p_rl) {
- boguscols--;
- col--;
- } else {
- boguscols++;
- col++;
- }
- }
-
- if (wp->w_p_rl) {
- boguscols--;
- col--;
- } else {
- boguscols++;
- col++;
- }
- } else {
- if (n_extra > 0) {
- vcol += n_extra;
- n_extra = 0;
- n_attr = 0;
- }
- }
- } else {
- n_skip--;
- }
-
- // Only advance the "vcol" when after the 'number' or 'relativenumber'
- // column.
- if (draw_state > WL_NR
- && filler_todo <= 0) {
- vcol++;
- }
-
- if (vcol_save_attr >= 0) {
- char_attr = vcol_save_attr;
- }
-
- // restore attributes after "predeces" in 'listchars'
- if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) {
- char_attr = saved_attr3;
- }
-
- // restore attributes after last 'listchars' or 'number' char
- if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) {
- char_attr = saved_attr2;
- }
-
- // At end of screen line and there is more to come: Display the line
- // so far. If there is no more to display it is caught above.
- if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols))
- && foldinfo.fi_lines == 0
- && (draw_state != WL_LINE
- || *ptr != NUL
- || filler_todo > 0
- || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
- && p_extra != at_end_str)
- || (n_extra != 0
- && (c_extra != NUL || *p_extra != NUL)))) {
- bool wrap = wp->w_p_wrap // Wrapping enabled.
- && filler_todo <= 0 // Not drawing diff filler lines.
- && lcs_eol_one != -1 // Haven't printed the lcs_eol character.
- && row != endrow - 1 // Not the last line being displayed.
- && (grid->cols == Columns // Window spans the width of the screen,
- || ui_has(kUIMultigrid)) // or has dedicated grid.
- && !wp->w_p_rl; // Not right-to-left.
-
- int draw_col = col - boguscols;
- if (filler_todo > 0) {
- int index = filler_todo - (filler_lines - n_virt_lines);
- if (index > 0) {
- int i = (int)kv_size(virt_lines) - index;
- assert(i >= 0);
- int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset;
- draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line,
- kHlModeReplace, grid->cols, offset);
- }
- } else {
- draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
- }
-
- grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl,
- wp, wp->w_hl_attr_normal, wrap);
- if (wrap) {
- ScreenGrid *current_grid = grid;
- int current_row = row, dummy_col = 0; // dummy_col unused
- grid_adjust(&current_grid, &current_row, &dummy_col);
-
- // Force a redraw of the first column of the next line.
- current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
-
- // Remember that the line wraps, used for modeless copy.
- current_grid->line_wraps[current_row] = true;
- }
-
- boguscols = 0;
- row++;
-
- // When not wrapping and finished diff lines, or when displayed
- // '$' and highlighting until last column, break here.
- if ((!wp->w_p_wrap && filler_todo <= 0) || lcs_eol_one == -1) {
- break;
- }
-
- // When the window is too narrow draw all "@" lines.
- if (draw_state != WL_LINE && filler_todo <= 0) {
- win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT);
- row = endrow;
- }
-
- // When line got too long for screen break here.
- if (row == endrow) {
- row++;
- break;
- }
-
- col = 0;
- off = 0;
- if (wp->w_p_rl) {
- col = grid->cols - 1; // col is not used if breaking!
- off += col;
- }
-
- // reset the drawing state for the start of a wrapped line
- draw_state = WL_START;
- saved_n_extra = n_extra;
- saved_p_extra = p_extra;
- saved_c_extra = c_extra;
- saved_c_final = c_final;
- saved_char_attr = char_attr;
- n_extra = 0;
- lcs_prec_todo = wp->w_p_lcs_chars.prec;
- if (filler_todo <= 0) {
- need_showbreak = true;
- }
- filler_todo--;
- // When the filler lines are actually below the last line of the
- // file, don't draw the line itself, break here.
- if (filler_todo == 0 && (wp->w_botfill || end_fill)) {
- break;
- }
- }
- } // for every character in the line
-
- // After an empty line check first word for capital.
- if (*skipwhite((char *)line) == NUL) {
- capcol_lnum = lnum + 1;
- cap_col = 0;
- }
-
- kv_destroy(virt_lines);
- xfree(p_extra_free);
- return row;
-}
-
-void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, int win_row)
-{
- DecorState *state = &decor_state;
- int right_pos = max_col;
- bool do_eol = state->eol_col > -1;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
- if (!(item->start_row == state->row
- && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) {
- continue;
- }
- if (item->win_col == -1) {
- if (item->decor.virt_text_pos == kVTRightAlign) {
- right_pos -= item->decor.virt_text_width;
- item->win_col = right_pos;
- } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
- item->win_col = state->eol_col;
- } else if (item->decor.virt_text_pos == kVTWinCol) {
- item->win_col = MAX(item->decor.col + col_off, 0);
- }
- }
- if (item->win_col < 0) {
- continue;
- }
- int col;
- if (item->decor.ui_watched) {
- // send mark position to UI
- col = item->win_col;
- WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col };
- kv_push(win_extmark_arr, m);
- }
- if (kv_size(item->decor.virt_text)) {
- col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
- item->decor.hl_mode, max_col, item->win_col - col_off);
- }
- item->win_col = -2; // deactivate
- if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
- state->eol_col = col + 1;
- }
-
- *end_col = MAX(*end_col, col);
- }
-}
-
-static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
- int vcol)
-{
- LineState s = LINE_STATE("");
- int virt_attr = 0;
- size_t virt_pos = 0;
-
- while (col < max_col) {
- if (!*s.p) {
- if (virt_pos >= kv_size(vt)) {
- break;
- }
- virt_attr = 0;
- do {
- s.p = kv_A(vt, virt_pos).text;
- int hl_id = kv_A(vt, virt_pos).hl_id;
- virt_attr = hl_combine_attr(virt_attr,
- hl_id > 0 ? syn_id2attr(hl_id) : 0);
- virt_pos++;
- } while (!s.p && virt_pos < kv_size(vt));
- if (!s.p) {
- break;
- }
- }
- if (!*s.p) {
- continue;
- }
- int attr;
- bool through = false;
- if (hl_mode == kHlModeCombine) {
- attr = hl_combine_attr(linebuf_attr[col], virt_attr);
- } else if (hl_mode == kHlModeBlend) {
- through = (*s.p == ' ');
- attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
- } else {
- attr = virt_attr;
- }
- schar_T dummy[2];
- int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
- max_col - col, false, vcol);
- // if we failed to emit a char, we still need to advance
- cells = MAX(cells, 1);
-
- for (int c = 0; c < cells; c++) {
- linebuf_attr[col++] = attr;
- }
- vcol += cells;
- }
- return col;
-}
-
-// Return true if CursorLineSign highlight is to be used.
-static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
-{
- return wp->w_p_cul
- && lnum == wp->w_cursor.lnum
- && (wp->w_p_culopt_flags & CULOPT_NBR);
-}
-
-/// Return true if CursorLineNr highlight is to be used for the number column.
-///
-/// - 'cursorline' must be set
-/// - lnum must be the cursor line
-/// - 'cursorlineopt' has "number"
-/// - don't highlight filler lines (when in diff mode)
-/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number
-/// itself on the first screenline of the wrapped line, otherwise highlight the number column of
-/// all screenlines of the wrapped line.
-static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
-{
- return wp->w_p_cul
- && lnum == wp->w_cursor.lnum
- && (wp->w_p_culopt_flags & CULOPT_NBR)
- && (row == startrow + filler_lines
- || (row > startrow + filler_lines
- && (wp->w_p_culopt_flags & CULOPT_LINE)));
-}
-
-static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines,
- sign_attrs_T *sattrs)
-{
- sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1);
- if (num_sattr != NULL) {
- // :sign defined with "numhl" highlight.
- return num_sattr->sat_numhl;
- }
-
- if (wp->w_p_rnu) {
- if (lnum < wp->w_cursor.lnum) {
- // Use LineNrAbove
- return win_hl_attr(wp, HLF_LNA);
- }
- if (lnum > wp->w_cursor.lnum) {
- // Use LineNrBelow
- return win_hl_attr(wp, HLF_LNB);
- }
- }
-
- if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) {
- // TODO(vim): Can we use CursorLine instead of CursorLineNr
- // when CursorLineNr isn't set?
- return win_hl_attr(wp, HLF_CLN);
- }
-
- return win_hl_attr(wp, HLF_N);
-}
-
-// Get information needed to display the sign in line 'lnum' in window 'wp'.
-// If 'nrcol' is TRUE, the sign is going to be displayed in the number column.
-// Otherwise the sign is going to be displayed in the sign column.
-//
-// @param count max number of signs
-// @param[out] n_extrap number of characters from pp_extra to display
-// @param sign_idxp Index of the displayed sign
-static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[],
- int row, int startrow, int filler_lines, int filler_todo,
- int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
- char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx)
-{
- // Draw cells with the sign value or blank.
- *c_extrap = ' ';
- *c_finalp = NUL;
- if (nrcol) {
- *n_extrap = number_width(wp) + 1;
- } else {
- if (use_cursor_line_sign(wp, lnum)) {
- *char_attrp = win_hl_attr(wp, HLF_CLS);
- } else {
- *char_attrp = win_hl_attr(wp, HLF_SC);
- }
- *n_extrap = win_signcol_width(wp);
- }
-
- if (row == startrow + filler_lines && filler_todo <= 0) {
- sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, sign_idx, wp->w_scwidth);
- if (sattr != NULL) {
- *pp_extra = sattr->sat_text;
- if (*pp_extra != NULL) {
- *c_extrap = NUL;
- *c_finalp = NUL;
-
- if (nrcol) {
- int n, width = number_width(wp) - 2;
- for (n = 0; n < width; n++) {
- extra[n] = ' ';
- }
- extra[n] = NUL;
- STRCAT(extra, *pp_extra);
- STRCAT(extra, " ");
- *pp_extra = extra;
- *n_extrap = (int)STRLEN(*pp_extra);
- } else {
- int symbol_blen = (int)STRLEN(*pp_extra);
-
- // TODO(oni-link): Is sign text already extended to
- // full cell width?
- assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra)));
- // symbol(s) bytes + (filling spaces) (one byte each)
- *n_extrap = symbol_blen + win_signcol_width(wp) -
- (int)mb_string2cells((char *)(*pp_extra));
-
- assert(extra_size > (size_t)symbol_blen);
- memset(extra, ' ', extra_size);
- memcpy(extra, *pp_extra, (size_t)symbol_blen);
-
- *pp_extra = extra;
- (*pp_extra)[*n_extrap] = NUL;
- }
- }
-
- if (use_cursor_line_sign(wp, lnum) && sattr->sat_culhl > 0) {
- *char_attrp = sattr->sat_culhl;
- } else {
- *char_attrp = sattr->sat_texthl;
- }
- }
- }
-}
-
/// Mirror text "str" for right-left displaying.
/// Only works for single-byte characters (e.g., numbers).
void rl_mirror(char_u *str)
@@ -4464,52 +248,26 @@ void rl_mirror(char_u *str)
}
}
-/// Mark all status lines and window bars for redraw; used after first :cd
-void status_redraw_all(void)
-{
- bool is_stl_global = global_stl_height() != 0;
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)
- || wp->w_winbar_height) {
- wp->w_redr_status = true;
- redraw_later(wp, VALID);
- }
- }
-}
-
-/// Marks all status lines and window bars of the current buffer for redraw.
-void status_redraw_curbuf(void)
+/// Get the length of an item as it will be shown in the status line.
+static int status_match_len(expand_T *xp, char_u *s)
{
- status_redraw_buf(curbuf);
-}
+ int len = 0;
-/// Marks all status lines and window bars of the given buffer for redraw.
-void status_redraw_buf(buf_T *buf)
-{
- bool is_stl_global = global_stl_height() != 0;
+ int emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height)
- || (is_stl_global && wp == curwin) || wp->w_winbar_height)) {
- wp->w_redr_status = true;
- redraw_later(wp, VALID);
- }
+ // Check for menu separators - replace with '|'.
+ if (emenu && menu_is_separator((char *)s)) {
+ return 1;
}
-}
-/// Redraw all status lines that need to be redrawn.
-void redraw_statuslines(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_redr_status) {
- win_redr_winbar(wp);
- win_redr_status(wp);
- }
- }
- if (redraw_tabline) {
- draw_tabline();
+ while (*s != NUL) {
+ s += skip_status_match_char(xp, s);
+ len += ptr2cells((char *)s);
+ MB_PTR_ADV(s);
}
+
+ return len;
}
/// Redraw all status lines at the bottom of frame "frp".
@@ -4532,127 +290,6 @@ void win_redraw_last_status(const frame_T *frp)
}
}
-/// Draw the vertical separator right of window "wp"
-static void draw_vsep_win(win_T *wp)
-{
- int hl;
- int c;
-
- if (wp->w_vsep_width) {
- // draw the vertical separator right of this window
- c = fillchar_vsep(wp, &hl);
- grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
- W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
- }
-}
-
-/// Draw the horizontal separator below window "wp"
-static void draw_hsep_win(win_T *wp)
-{
- int hl;
- int c;
-
- if (wp->w_hsep_height) {
- // draw the horizontal separator below this window
- c = fillchar_hsep(wp, &hl);
- grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
- wp->w_wincol, W_ENDCOL(wp), c, c, hl);
- }
-}
-
-/// Get the separator connector for specified window corner of window "wp"
-static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
-{
- // It's impossible for windows to be connected neither vertically nor horizontally
- // So if they're not vertically connected, assume they're horizontally connected
- if (vsep_connected(wp, corner)) {
- if (hsep_connected(wp, corner)) {
- return wp->w_p_fcs_chars.verthoriz;
- } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
- return wp->w_p_fcs_chars.vertright;
- } else {
- return wp->w_p_fcs_chars.vertleft;
- }
- } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
- return wp->w_p_fcs_chars.horizdown;
- } else {
- return wp->w_p_fcs_chars.horizup;
- }
-}
-
-/// Draw separator connecting characters on the corners of window "wp"
-static void draw_sep_connectors_win(win_T *wp)
-{
- // Don't draw separator connectors unless global statusline is enabled and the window has
- // either a horizontal or vertical separator
- if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) {
- return;
- }
-
- int hl = win_hl_attr(wp, HLF_C);
-
- // Determine which edges of the screen the window is located on so we can avoid drawing separators
- // on corners contained in those edges
- bool win_at_top;
- bool win_at_bottom = wp->w_hsep_height == 0;
- bool win_at_left;
- bool win_at_right = wp->w_vsep_width == 0;
- frame_T *frp;
-
- for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
- if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
- break;
- }
- }
- win_at_top = frp->fr_parent == NULL;
- for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
- if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
- break;
- }
- }
- win_at_left = frp->fr_parent == NULL;
-
- // Draw the appropriate separator connector in every corner where drawing them is necessary
- if (!(win_at_top || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
- wp->w_winrow - 1, wp->w_wincol - 1, hl);
- }
- if (!(win_at_top || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
- wp->w_winrow - 1, W_ENDCOL(wp), hl);
- }
- if (!(win_at_bottom || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
- W_ENDROW(wp), wp->w_wincol - 1, hl);
- }
- if (!(win_at_bottom || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
- W_ENDROW(wp), W_ENDCOL(wp), hl);
- }
-}
-
-/// Get the length of an item as it will be shown in the status line.
-static int status_match_len(expand_T *xp, char_u *s)
-{
- int len = 0;
-
- int emenu = (xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES);
-
- // Check for menu separators - replace with '|'.
- if (emenu && menu_is_separator((char *)s)) {
- return 1;
- }
-
- while (*s != NUL) {
- s += skip_status_match_char(xp, s);
- len += ptr2cells((char *)s);
- MB_PTR_ADV(s);
- }
-
- return len;
-}
-
/// Return the number of characters that should be skipped in a status match.
/// These are backslashes used for escaping. Do show backslashes in help tags.
static int skip_status_match_char(expand_T *xp, char_u *s)
@@ -4859,186 +496,6 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
xfree(buf);
}
-/// Redraw the status line of window `wp`.
-///
-/// If inversion is possible we use it. Else '=' characters are used.
-static void win_redr_status(win_T *wp)
-{
- int row;
- int col;
- char_u *p;
- int len;
- int fillchar;
- int attr;
- int width;
- int this_ru_col;
- bool is_stl_global = global_stl_height() > 0;
- static bool busy = false;
-
- // May get here recursively when 'statusline' (indirectly)
- // invokes ":redrawstatus". Simply ignore the call then.
- if (busy
- // Also ignore if wildmenu is showing.
- || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) {
- return;
- }
- busy = true;
-
- wp->w_redr_status = false;
- if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
- // no status line, either global statusline is enabled or the window is a last window
- redraw_cmdline = true;
- } else if (!redrawing()) {
- // Don't redraw right now, do it later. Don't update status line when
- // popup menu is visible and may be drawn over it
- wp->w_redr_status = true;
- } else if (*p_stl != NUL || *wp->w_p_stl != NUL) {
- // redraw custom status line
- redraw_custom_statusline(wp);
- } else {
- fillchar = fillchar_status(&attr, wp);
- width = is_stl_global ? Columns : wp->w_width;
-
- get_trans_bufname(wp->w_buffer);
- p = NameBuff;
- len = (int)STRLEN(p);
-
- if (bt_help(wp->w_buffer)
- || wp->w_p_pvw
- || bufIsChanged(wp->w_buffer)
- || wp->w_buffer->b_p_ro) {
- *(p + len++) = ' ';
- }
- if (bt_help(wp->w_buffer)) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
- len += (int)STRLEN(p + len);
- }
- if (wp->w_p_pvw) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
- len += (int)STRLEN(p + len);
- }
- if (bufIsChanged(wp->w_buffer)) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]");
- len += (int)STRLEN(p + len);
- }
- if (wp->w_buffer->b_p_ro) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
- // len += (int)STRLEN(p + len); // dead assignment
- }
-
- this_ru_col = ru_col - (Columns - width);
- if (this_ru_col < (width + 1) / 2) {
- this_ru_col = (width + 1) / 2;
- }
- if (this_ru_col <= 1) {
- p = (char_u *)"<"; // No room for file name!
- len = 1;
- } else {
- int clen = 0, i;
-
- // Count total number of display cells.
- clen = (int)mb_string2cells((char *)p);
-
- // Find first character that will fit.
- // Going from start to end is much faster for DBCS.
- for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
- i += utfc_ptr2len((char *)p + i)) {
- clen -= utf_ptr2cells((char *)p + i);
- }
- len = clen;
- if (i > 0) {
- p = p + i - 1;
- *p = '<';
- len++;
- }
- }
-
- row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
- col = is_stl_global ? 0 : wp->w_wincol;
- grid_puts(&default_grid, p, row, col, attr);
- grid_fill(&default_grid, row, row + 1, len + col,
- this_ru_col + col, fillchar, fillchar, attr);
-
- if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL)
- && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
- grid_puts(&default_grid, NameBuff, row,
- (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr);
- }
-
- win_redr_ruler(wp, true);
- }
-
- // May need to draw the character below the vertical separator.
- if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) {
- if (stl_connected(wp)) {
- fillchar = fillchar_status(&attr, wp);
- } else {
- fillchar = fillchar_vsep(wp, &attr);
- }
- grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr);
- }
- busy = false;
-}
-
-/// Redraw the status line according to 'statusline' and take care of any
-/// errors encountered.
-static void redraw_custom_statusline(win_T *wp)
-{
- static bool entered = false;
- int saved_did_emsg = did_emsg;
-
- // When called recursively return. This can happen when the statusline
- // contains an expression that triggers a redraw.
- if (entered) {
- return;
- }
- entered = true;
-
- did_emsg = false;
- win_redr_custom(wp, false, false);
- if (did_emsg) {
- // When there is an error disable the statusline, otherwise the
- // display is messed up with errors and a redraw triggers the problem
- // again and again.
- set_string_option_direct("statusline", -1, "",
- OPT_FREE | (*wp->w_p_stl != NUL
- ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
- }
- did_emsg |= saved_did_emsg;
- entered = false;
-}
-
-static void win_redr_winbar(win_T *wp)
-{
- static bool entered = false;
-
- // Return when called recursively. This can happen when the winbar contains an expression
- // that triggers a redraw.
- if (entered) {
- return;
- }
- entered = true;
-
- if (wp->w_winbar_height == 0 || !redrawing()) {
- // Do nothing.
- } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
- int saved_did_emsg = did_emsg;
-
- did_emsg = false;
- win_redr_custom(wp, true, false);
- if (did_emsg) {
- // When there is an error disable the winbar, otherwise the
- // display is messed up with errors and a redraw triggers the problem
- // again and again.
- set_string_option_direct("winbar", -1, "",
- OPT_FREE | (*wp->w_p_stl != NUL
- ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
- }
- did_emsg |= saved_did_emsg;
- }
- entered = false;
-}
-
/// Only call if (wp->w_vsep_width != 0).
///
/// @return true if the status line of window "wp" is connected to the status
@@ -5063,77 +520,6 @@ bool stl_connected(win_T *wp)
return false;
}
-/// Check if horizontal separator of window "wp" at specified window corner is connected to the
-/// horizontal separator of another window
-/// Assumes global statusline is enabled
-static bool hsep_connected(win_T *wp, WindowCorner corner)
-{
- bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT);
- int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT)
- ? wp->w_winrow - 1 : W_ENDROW(wp);
- frame_T *fr = wp->w_frame;
-
- while (fr->fr_parent != NULL) {
- if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) {
- fr = before ? fr->fr_prev : fr->fr_next;
- break;
- }
- fr = fr->fr_parent;
- }
- if (fr->fr_parent == NULL) {
- return false;
- }
- while (fr->fr_layout != FR_LEAF) {
- fr = fr->fr_child;
- if (fr->fr_parent->fr_layout == FR_ROW && before) {
- while (fr->fr_next != NULL) {
- fr = fr->fr_next;
- }
- } else {
- while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) {
- fr = fr->fr_next;
- }
- }
- }
-
- return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win));
-}
-
-/// Check if vertical separator of window "wp" at specified window corner is connected to the
-/// vertical separator of another window
-static bool vsep_connected(win_T *wp, WindowCorner corner)
-{
- bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT);
- int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT)
- ? wp->w_wincol - 1 : W_ENDCOL(wp);
- frame_T *fr = wp->w_frame;
-
- while (fr->fr_parent != NULL) {
- if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) {
- fr = before ? fr->fr_prev : fr->fr_next;
- break;
- }
- fr = fr->fr_parent;
- }
- if (fr->fr_parent == NULL) {
- return false;
- }
- while (fr->fr_layout != FR_LEAF) {
- fr = fr->fr_child;
- if (fr->fr_parent->fr_layout == FR_COL && before) {
- while (fr->fr_next != NULL) {
- fr = fr->fr_next;
- }
- } else {
- while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) {
- fr = fr->fr_next;
- }
- }
- }
-
- return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win));
-}
-
/// Get the value to show for the language mappings, active 'keymap'.
///
/// @param fmt format string containing one %s item
@@ -5177,7 +563,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
/// Redraw the status line, window bar or ruler of window "wp".
/// When "wp" is NULL redraw the tab pages line from 'tabline'.
-static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
+void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
{
static bool entered = false;
int attr;
@@ -5392,82 +778,23 @@ theend:
entered = false;
}
-static void win_redr_border(win_T *wp)
-{
- wp->w_redr_border = false;
- if (!(wp->w_floating && wp->w_float_config.border)) {
- return;
- }
-
- ScreenGrid *grid = &wp->w_grid_alloc;
-
- schar_T *chars = wp->w_float_config.border_chars;
- int *attrs = wp->w_float_config.border_attr;
-
- int *adj = wp->w_border_adj;
- int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
-
- if (adj[0]) {
- grid_puts_line_start(grid, 0);
- if (adj[3]) {
- grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
- }
- for (int i = 0; i < icol; i++) {
- grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
- }
- if (adj[1]) {
- grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
- }
- grid_puts_line_flush(false);
- }
-
- for (int i = 0; i < irow; i++) {
- if (adj[3]) {
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
- grid_puts_line_flush(false);
- }
- if (adj[1]) {
- int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
- grid_puts_line_flush(false);
- }
- }
-
- if (adj[2]) {
- grid_puts_line_start(grid, irow + adj[0]);
- if (adj[3]) {
- grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
- }
- for (int i = 0; i < icol; i++) {
- int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
- grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
- }
- if (adj[1]) {
- grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
- }
- grid_puts_line_flush(false);
- }
-}
-
/// Prepare for 'hlsearch' highlighting.
-static void start_search_hl(void)
+void start_search_hl(void)
{
if (p_hls && !no_hlsearch) {
end_search_hl(); // just in case it wasn't called before
- last_pat_prog(&search_hl.rm);
+ last_pat_prog(&screen_search_hl.rm);
// Set the time limit to 'redrawtime'.
- search_hl.tm = profile_setlimit(p_rdt);
+ screen_search_hl.tm = profile_setlimit(p_rdt);
}
}
/// Clean up for 'hlsearch' highlighting.
-static void end_search_hl(void)
+void end_search_hl(void)
{
- if (search_hl.rm.regprog != NULL) {
- vim_regfree(search_hl.rm.regprog);
- search_hl.rm.regprog = NULL;
+ if (screen_search_hl.rm.regprog != NULL) {
+ vim_regfree(screen_search_hl.rm.regprog);
+ screen_search_hl.rm.regprog = NULL;
}
}
@@ -5487,101 +814,6 @@ void check_for_delay(bool check_msg_scroll)
}
}
-/// Resize the screen to Rows and Columns.
-///
-/// Allocate default_grid.chars[] and other grid arrays.
-///
-/// There may be some time between setting Rows and Columns and (re)allocating
-/// default_grid arrays. This happens when starting up and when
-/// (manually) changing the screen size. Always use default_grid.rows and
-/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
-/// and Columns for positioning text etc. where the final size of the screen is
-/// needed.
-void screenalloc(void)
-{
- // It's possible that we produce an out-of-memory message below, which
- // will cause this function to be called again. To break the loop, just
- // return here.
- if (resizing) {
- return;
- }
- resizing = true;
-
- int retry_count = 0;
-
-retry:
- // Allocation of the screen buffers is done only when the size changes and
- // when Rows and Columns have been set and we have started doing full
- // screen stuff.
- if ((default_grid.chars != NULL
- && Rows == default_grid.rows
- && Columns == default_grid.cols
- )
- || Rows == 0
- || Columns == 0
- || (!full_screen && default_grid.chars == NULL)) {
- resizing = false;
- return;
- }
-
- // Note that the window sizes are updated before reallocating the arrays,
- // thus we must not redraw here!
- RedrawingDisabled++;
-
- // win_new_screensize will recompute floats position, but tell the
- // compositor to not redraw them yet
- ui_comp_set_screen_valid(false);
- if (msg_grid.chars) {
- msg_grid_invalid = true;
- }
-
- win_new_screensize(); // fit the windows in the new sized screen
-
- comp_col(); // recompute columns for shown command and ruler
-
- // We're changing the size of the screen.
- // - Allocate new arrays for default_grid
- // - Move lines from the old arrays into the new arrays, clear extra
- // lines (unless the screen is going to be cleared).
- // - Free the old arrays.
- //
- // If anything fails, make grid arrays NULL, so we don't do anything!
- // Continuing with the old arrays may result in a crash, because the
- // size is wrong.
-
- grid_alloc(&default_grid, Rows, Columns, true, true);
- StlClickDefinition *new_tab_page_click_defs =
- xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
-
- stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
- xfree(tab_page_click_defs);
-
- tab_page_click_defs = new_tab_page_click_defs;
- tab_page_click_defs_size = Columns;
-
- default_grid.comp_height = Rows;
- default_grid.comp_width = Columns;
-
- default_grid.row_offset = 0;
- default_grid.col_offset = 0;
- default_grid.handle = DEFAULT_GRID_HANDLE;
-
- must_redraw = CLEAR; // need to clear the screen later
-
- RedrawingDisabled--;
-
- // Do not apply autocommands more than 3 times to avoid an endless loop
- // in case applying autocommands always changes Rows or Columns.
- if (starting == 0 && ++retry_count <= 3) {
- apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
- // In rare cases, autocommands may have altered Rows or Columns,
- // jump back to check if we need to allocate the screen again.
- goto retry;
- }
-
- resizing = false;
-}
-
/// Clear status line, window bar or tab page line click definition table
///
/// @param[out] tpcd Table to clear.
@@ -5598,67 +830,6 @@ void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click
}
}
-void screenclear(void)
-{
- check_for_delay(false);
- screenalloc(); // allocate screen buffers if size changed
-
- int i;
-
- if (starting == NO_SCREEN || default_grid.chars == NULL) {
- return;
- }
-
- // blank out the default grid
- for (i = 0; i < default_grid.rows; i++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- default_grid.cols, true);
- default_grid.line_wraps[i] = false;
- }
-
- ui_call_grid_clear(1); // clear the display
- ui_comp_set_screen_valid(true);
-
- clear_cmdline = false;
- mode_displayed = false;
-
- redraw_all_later(NOT_VALID);
- redraw_cmdline = true;
- redraw_tabline = true;
- redraw_popupmenu = true;
- pum_invalidate();
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- wp->w_redr_type = CLEAR;
- }
- }
- if (must_redraw == CLEAR) {
- must_redraw = NOT_VALID; // no need to clear again
- }
- compute_cmdrow();
- msg_row = cmdline_row; // put cursor on last line for messages
- msg_col = 0;
- msg_scrolled = 0; // can't scroll back
- msg_didany = false;
- msg_didout = false;
- if (HL_ATTR(HLF_MSG) > 0 && msg_use_grid() && msg_grid.chars) {
- grid_invalidate(&msg_grid);
- msg_grid_validate();
- msg_grid_invalid = false;
- clear_cmdline = true;
- }
-}
-
-/// Copy part of a grid line for vertically split window.
-static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
-{
- unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col);
- unsigned off_from = (unsigned)(grid->line_offset[from] + (size_t)col);
-
- memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T));
- memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T));
-}
-
/// Set cursor to its position in the current window.
void setcursor(void)
{
@@ -5688,9 +859,9 @@ void setcursor_mayforce(bool force)
}
}
-/// Scroll 'line_count' lines at 'row' in window 'wp'.
+/// Scroll `line_count` lines at 'row' in window 'wp'.
///
-/// Positive `line_count' means scrolling down, so that more space is available
+/// Positive `line_count` means scrolling down, so that more space is available
/// at 'row'. Negative `line_count` implies deleting lines at `row`.
void win_scroll_lines(win_T *wp, int row, int line_count)
{
@@ -5712,121 +883,26 @@ void win_scroll_lines(win_T *wp, int row, int line_count)
}
}
-// The rest of the routines in this file perform screen manipulations. The
-// given operation is performed physically on the screen. The corresponding
-// change is also made to the internal screen image. In this way, the editor
-// anticipates the effect of editing changes on the appearance of the screen.
-// That way, when we call screenupdate a complete redraw isn't usually
-// necessary. Another advantage is that we can keep adding code to anticipate
-// screen changes, and in the meantime, everything still works.
-
-/// insert lines on the screen and move the existing lines down
-/// 'line_count' is the number of lines to be inserted.
-/// 'end' is the line after the scrolled part. Normally it is Rows.
-/// 'col' is the column from with we start inserting.
-//
-/// 'row', 'col' and 'end' are relative to the start of the region.
-void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
+/// @return true when postponing displaying the mode message: when not redrawing
+/// or inside a mapping.
+bool skip_showmode(void)
{
- int i;
- int j;
- unsigned temp;
-
- int row_off = 0;
- grid_adjust(&grid, &row_off, &col);
- row += row_off;
- end += row_off;
-
- if (line_count <= 0) {
- return;
- }
-
- // Shift line_offset[] line_count down to reflect the inserted lines.
- // Clear the inserted lines.
- for (i = 0; i < line_count; i++) {
- if (width != grid->cols) {
- // need to copy part of a line
- j = end - 1 - i;
- while ((j -= line_count) >= row) {
- linecopy(grid, j + line_count, j, col, width);
- }
- j += line_count;
- grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
- } else {
- j = end - 1 - i;
- temp = (unsigned)grid->line_offset[j];
- while ((j -= line_count) >= row) {
- grid->line_offset[j + line_count] = grid->line_offset[j];
- grid->line_wraps[j + line_count] = grid->line_wraps[j];
- }
- grid->line_offset[j + line_count] = temp;
- grid->line_wraps[j + line_count] = false;
- grid_clear_line(grid, temp, grid->cols, false);
- }
- }
-
- if (!grid->throttled) {
- ui_call_grid_scroll(grid->handle, row, end, col, col + width, -line_count, 0);
- }
-}
-
-/// delete lines on the screen and move lines up.
-/// 'end' is the line after the scrolled part. Normally it is Rows.
-/// When scrolling region used 'off' is the offset from the top for the region.
-/// 'row' and 'end' are relative to the start of the region.
-void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
-{
- int j;
- int i;
- unsigned temp;
-
- int row_off = 0;
- grid_adjust(&grid, &row_off, &col);
- row += row_off;
- end += row_off;
-
- if (line_count <= 0) {
- return;
- }
-
- // Now shift line_offset[] line_count up to reflect the deleted lines.
- // Clear the inserted lines.
- for (i = 0; i < line_count; i++) {
- if (width != grid->cols) {
- // need to copy part of a line
- j = row + i;
- while ((j += line_count) <= end - 1) {
- linecopy(grid, j - line_count, j, col, width);
- }
- j -= line_count;
- grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
- } else {
- // whole width, moving the line pointers is faster
- j = row + i;
- temp = (unsigned)grid->line_offset[j];
- while ((j += line_count) <= end - 1) {
- grid->line_offset[j - line_count] = grid->line_offset[j];
- grid->line_wraps[j - line_count] = grid->line_wraps[j];
- }
- grid->line_offset[j - line_count] = temp;
- grid->line_wraps[j - line_count] = false;
- grid_clear_line(grid, temp, grid->cols, false);
- }
- }
-
- if (!grid->throttled) {
- ui_call_grid_scroll(grid->handle, row, end, col, col + width, line_count, 0);
+ // Call char_avail() only when we are going to show something, because it
+ // takes a bit of time. redrawing() may also call char_avail().
+ if (global_busy || msg_silent != 0 || !redrawing() || (char_avail() && !KeyTyped)) {
+ redraw_mode = true; // show mode later
+ return true;
}
+ return false;
}
-// Show the current mode and ruler.
-//
-// If clear_cmdline is true, clear the rest of the cmdline.
-// If clear_cmdline is false there may be a message there that needs to be
-// cleared only if a mode is shown.
-// Return the length of the message (0 if no message).
+/// Show the current mode and ruler.
+///
+/// If clear_cmdline is true, clear the rest of the cmdline.
+/// If clear_cmdline is false there may be a message there that needs to be
+/// cleared only if a mode is shown.
+/// If redraw_mode is true show or clear the mode.
+/// @return the length of the message (0 if no message).
int showmode(void)
{
bool need_clear;
@@ -5850,12 +926,8 @@ int showmode(void)
|| restart_edit != NUL
|| VIsual_active));
if (do_mode || reg_recording != 0) {
- // Don't show mode right now, when not redrawing or inside a mapping.
- // Call char_avail() only when we are going to show something, because
- // it takes a bit of time.
- if (!redrawing() || (char_avail() && !KeyTyped) || msg_silent != 0) {
- redraw_cmdline = true; // show mode later
- return 0;
+ if (skip_showmode()) {
+ return 0; // show mode later
}
bool nwr_save = need_wait_return;
@@ -5989,7 +1061,7 @@ int showmode(void)
}
mode_displayed = true;
- if (need_clear || clear_cmdline) {
+ if (need_clear || clear_cmdline || redraw_mode) {
msg_clr_eos();
}
msg_didout = false; // overwrite this message
@@ -6001,6 +1073,9 @@ int showmode(void)
} else if (clear_cmdline && msg_silent == 0) {
// Clear the whole command line. Will reset "clear_cmdline".
msg_clr_cmdline();
+ } else if (redraw_mode) {
+ msg_pos_mode();
+ msg_clr_eos();
}
// NB: also handles clearing the showmode if it was empty or disabled
@@ -6018,6 +1093,7 @@ int showmode(void)
win_redr_ruler(last, true);
}
redraw_cmdline = false;
+ redraw_mode = false;
clear_cmdline = false;
return length;
@@ -6245,7 +1321,7 @@ void draw_tabline(void)
redraw_tabline = false;
}
-void ui_ext_tabline_update(void)
+static void ui_ext_tabline_update(void)
{
Arena arena = ARENA_EMPTY;
arena_start(&arena, &ui_ext_fixblk);
@@ -6292,8 +1368,6 @@ void ui_ext_tabline_update(void)
arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
}
-/// Get buffer name for "buf" into NameBuff[].
-/// Takes care of special buffer names and translates special characters.
void get_trans_bufname(buf_T *buf)
{
if (buf_spname(buf) != NULL) {
@@ -6332,7 +1406,7 @@ int fillchar_status(int *attr, win_T *wp)
/// Get the character to use in a separator between vertically split windows.
/// Get its attributes in "*attr".
-static int fillchar_vsep(win_T *wp, int *attr)
+int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.vert;
@@ -6340,7 +1414,7 @@ static int fillchar_vsep(win_T *wp, int *attr)
/// Get the character to use in a separator between horizontally split windows.
/// Get its attributes in "*attr".
-static int fillchar_hsep(win_T *wp, int *attr)
+int fillchar_hsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.horiz;
@@ -6359,36 +1433,7 @@ bool messaging(void)
return !(p_lz && char_avail() && !KeyTyped) && ui_has_messages();
}
-/// Show current status info in ruler and various other places
-///
-/// @param always if false, only show ruler if position has changed.
-void showruler(bool always)
-{
- if (!always && !redrawing()) {
- return;
- }
- if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
- && (curwin->w_status_height || global_stl_height())) {
- redraw_custom_statusline(curwin);
- } else {
- win_redr_ruler(curwin, always);
- }
- if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
- win_redr_winbar(curwin);
- }
-
- if (need_maketitle
- || (p_icon && (stl_syntax & STL_IN_ICON))
- || (p_title && (stl_syntax & STL_IN_TITLE))) {
- maketitle();
- }
- // Redraw the tab pages line if needed.
- if (redraw_tabline) {
- draw_tabline();
- }
-}
-
-static void win_redr_ruler(win_T *wp, bool always)
+void win_redr_ruler(win_T *wp, bool always)
{
bool is_stl_global = global_stl_height() > 0;
static bool did_show_ext_ruler = false;
@@ -6557,6 +1602,46 @@ static void win_redr_ruler(win_T *wp, bool always)
}
}
+#define COL_RULER 17 // columns needed by standard ruler
+
+/// Compute columns for ruler and shown command. 'sc_col' is also used to
+/// decide what the maximum length of a message on the status line can be.
+/// If there is a status line for the last window, 'sc_col' is independent
+/// of 'ru_col'.
+void comp_col(void)
+{
+ int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
+
+ sc_col = 0;
+ ru_col = 0;
+ if (p_ru) {
+ ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
+ // no last status line, adjust sc_col
+ if (!last_has_status) {
+ sc_col = ru_col;
+ }
+ }
+ if (p_sc) {
+ sc_col += SHOWCMD_COLS;
+ if (!p_ru || last_has_status) { // no need for separating space
+ sc_col++;
+ }
+ }
+ assert(sc_col >= 0
+ && INT_MIN + sc_col <= Columns);
+ sc_col = Columns - sc_col;
+ assert(ru_col >= 0
+ && INT_MIN + ru_col <= Columns);
+ ru_col = Columns - ru_col;
+ if (sc_col <= 0) { // screen too narrow, will become a mess
+ sc_col = 1;
+ }
+ if (ru_col <= 0) {
+ ru_col = 1;
+ }
+ set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
+}
+
/// Return the width of the 'number' and 'relativenumber' column.
/// Caller may need to check if 'number' or 'relativenumber' is set.
/// Otherwise it depends on 'numberwidth' and the line count.
@@ -6600,152 +1685,282 @@ int number_width(win_T *wp)
return n;
}
-/// Used when 'cursorlineopt' contains "screenline": compute the margins between
-/// which the highlighting is used.
-static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
+/// Calls mb_cptr2char_adv(p) and returns the character.
+/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
+/// Returns 0 for invalid hex or invalid UTF-8 byte.
+static int get_encoded_char_adv(char_u **p)
{
- // cache previous calculations depending on w_virtcol
- static int saved_w_virtcol;
- static win_T *prev_wp;
- static int prev_left_col;
- static int prev_right_col;
- static int prev_col_off;
-
- int cur_col_off = win_col_off(wp);
- int width1;
- int width2;
-
- if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp
- && prev_col_off == cur_col_off) {
- *right_col = prev_right_col;
- *left_col = prev_left_col;
- return;
- }
-
- width1 = wp->w_width - cur_col_off;
- width2 = width1 + win_col_off2(wp);
-
- *left_col = 0;
- *right_col = width1;
+ char_u *s = *p;
- if (wp->w_virtcol >= (colnr_T)width1) {
- *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2;
- }
- if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
- *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1;
+ if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
+ int64_t num = 0;
+ int bytes;
+ int n;
+ for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
+ *p += 2;
+ n = hexhex2nr(*p);
+ if (n < 0) {
+ return 0;
+ }
+ num = num * 256 + n;
+ }
+ *p += 2;
+ return (int)num;
}
- // cache values
- prev_left_col = *left_col;
- prev_right_col = *right_col;
- prev_wp = wp;
- saved_w_virtcol = wp->w_virtcol;
- prev_col_off = cur_col_off;
+ // TODO(bfredl): use schar_T representation and utfc_ptr2len
+ int clen = utf_ptr2len((char *)s);
+ int c = mb_cptr2char_adv((const char_u **)p);
+ if (clen == 1 && c > 127) { // Invalid UTF-8 byte
+ return 0;
+ }
+ return c;
}
-/// Set dimensions of the Nvim application "screen".
-void screen_resize(int width, int height)
-{
- // Avoid recursiveness, can happen when setting the window size causes
- // another window-changed signal.
- if (updating_screen || resizing_screen) {
- return;
- }
+/// Handle setting 'listchars' or 'fillchars'.
+/// Assume monocell characters
+///
+/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
+/// @return error message, NULL if it's OK.
+char *set_chars_option(win_T *wp, char_u **varp, bool set)
+{
+ int round, i, len, len2, entries;
+ char_u *p, *s;
+ int c1;
+ int c2 = 0;
+ int c3 = 0;
+ char_u *last_multispace = NULL; // Last occurrence of "multispace:"
+ char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
+ int multispace_len = 0; // Length of lcs-multispace string
+ int lead_multispace_len = 0; // Length of lcs-leadmultispace string
+
+ struct chars_tab {
+ int *cp; ///< char value
+ char *name; ///< char id
+ int def; ///< default value
+ };
+ struct chars_tab *tab;
+
+ // XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case.
+ struct chars_tab fcs_tab[] = {
+ { &wp->w_p_fcs_chars.stl, "stl", ' ' },
+ { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
+ { &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
+ { &wp->w_p_fcs_chars.horiz, "horiz", char2cells(0x2500) == 1 ? 0x2500 : '-' }, // ─
+ { &wp->w_p_fcs_chars.horizup, "horizup", char2cells(0x2534) == 1 ? 0x2534 : '-' }, // ┴
+ { &wp->w_p_fcs_chars.horizdown, "horizdown", char2cells(0x252c) == 1 ? 0x252c : '-' }, // ┬
+ { &wp->w_p_fcs_chars.vert, "vert", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
+ { &wp->w_p_fcs_chars.vertleft, "vertleft", char2cells(0x2524) == 1 ? 0x2524 : '|' }, // ┤
+ { &wp->w_p_fcs_chars.vertright, "vertright", char2cells(0x251c) == 1 ? 0x251c : '|' }, // ├
+ { &wp->w_p_fcs_chars.verthoriz, "verthoriz", char2cells(0x253c) == 1 ? 0x253c : '+' }, // ┼
+ { &wp->w_p_fcs_chars.fold, "fold", char2cells(0x00b7) == 1 ? 0x00b7 : '-' }, // ·
+ { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
+ { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
+ { &wp->w_p_fcs_chars.foldsep, "foldsep", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
+ { &wp->w_p_fcs_chars.diff, "diff", '-' },
+ { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
+ { &wp->w_p_fcs_chars.eob, "eob", '~' },
+ };
+ struct chars_tab lcs_tab[] = {
+ { &wp->w_p_lcs_chars.eol, "eol", NUL },
+ { &wp->w_p_lcs_chars.ext, "extends", NUL },
+ { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
+ { &wp->w_p_lcs_chars.prec, "precedes", NUL },
+ { &wp->w_p_lcs_chars.space, "space", NUL },
+ { &wp->w_p_lcs_chars.tab2, "tab", NUL },
+ { &wp->w_p_lcs_chars.lead, "lead", NUL },
+ { &wp->w_p_lcs_chars.trail, "trail", NUL },
+ { &wp->w_p_lcs_chars.conceal, "conceal", NUL },
+ };
- if (width < 0 || height < 0) { // just checking...
- return;
+ if (varp == &p_lcs || varp == &wp->w_p_lcs) {
+ tab = lcs_tab;
+ entries = ARRAY_SIZE(lcs_tab);
+ if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
+ varp = &p_lcs;
+ }
+ } else {
+ tab = fcs_tab;
+ entries = ARRAY_SIZE(fcs_tab);
+ if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
+ varp = &p_fcs;
+ }
}
- if (State == MODE_HITRETURN || State == MODE_SETWSIZE) {
- // postpone the resizing
- State = MODE_SETWSIZE;
- return;
- }
+ // first round: check for valid value, second round: assign values
+ for (round = 0; round <= (set ? 1 : 0); round++) {
+ if (round > 0) {
+ // After checking that the value is valid: set defaults
+ for (i = 0; i < entries; i++) {
+ if (tab[i].cp != NULL) {
+ *(tab[i].cp) = tab[i].def;
+ }
+ }
+ if (varp == &p_lcs || varp == &wp->w_p_lcs) {
+ wp->w_p_lcs_chars.tab1 = NUL;
+ wp->w_p_lcs_chars.tab3 = NUL;
- // curwin->w_buffer can be NULL when we are closing a window and the
- // buffer has already been closed and removing a scrollbar causes a resize
- // event. Don't resize then, it will happen after entering another buffer.
- if (curwin->w_buffer == NULL) {
- return;
- }
+ xfree(wp->w_p_lcs_chars.multispace);
+ if (multispace_len > 0) {
+ wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
+ wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
+ } else {
+ wp->w_p_lcs_chars.multispace = NULL;
+ }
- resizing_screen = true;
+ xfree(wp->w_p_lcs_chars.leadmultispace);
+ if (lead_multispace_len > 0) {
+ wp->w_p_lcs_chars.leadmultispace
+ = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
+ wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
+ } else {
+ wp->w_p_lcs_chars.leadmultispace = NULL;
+ }
+ }
+ }
+ p = *varp;
+ while (*p) {
+ for (i = 0; i < entries; i++) {
+ len = (int)STRLEN(tab[i].name);
+ if (STRNCMP(p, tab[i].name, len) == 0
+ && p[len] == ':'
+ && p[len + 1] != NUL) {
+ c2 = c3 = 0;
+ s = p + len + 1;
+ c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
+ if (*s == NUL) {
+ return e_invarg;
+ }
+ c2 = get_encoded_char_adv(&s);
+ if (c2 == 0 || char2cells(c2) > 1) {
+ return e_invarg;
+ }
+ if (!(*s == ',' || *s == NUL)) {
+ c3 = get_encoded_char_adv(&s);
+ if (c3 == 0 || char2cells(c3) > 1) {
+ return e_invarg;
+ }
+ }
+ }
+ if (*s == ',' || *s == NUL) {
+ if (round > 0) {
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
+ wp->w_p_lcs_chars.tab1 = c1;
+ wp->w_p_lcs_chars.tab2 = c2;
+ wp->w_p_lcs_chars.tab3 = c3;
+ } else if (tab[i].cp != NULL) {
+ *(tab[i].cp) = c1;
+ }
+ }
+ p = s;
+ break;
+ }
+ }
+ }
- 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 (i == entries) {
+ len = (int)STRLEN("multispace");
+ len2 = (int)STRLEN("leadmultispace");
+ if ((varp == &p_lcs || varp == &wp->w_p_lcs)
+ && STRNCMP(p, "multispace", len) == 0
+ && p[len] == ':'
+ && p[len + 1] != NUL) {
+ s = p + len + 1;
+ if (round == 0) {
+ // Get length of lcs-multispace string in the first round
+ last_multispace = p;
+ multispace_len = 0;
+ while (*s != NUL && *s != ',') {
+ c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ multispace_len++;
+ }
+ if (multispace_len == 0) {
+ // lcs-multispace cannot be an empty string
+ return e_invarg;
+ }
+ p = s;
+ } else {
+ int multispace_pos = 0;
+ while (*s != NUL && *s != ',') {
+ c1 = get_encoded_char_adv(&s);
+ if (p == last_multispace) {
+ wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
+ }
+ }
+ p = s;
+ }
+ } else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
+ && STRNCMP(p, "leadmultispace", len2) == 0
+ && p[len2] == ':'
+ && p[len2 + 1] != NUL) {
+ s = p + len2 + 1;
+ if (round == 0) {
+ // get length of lcs-leadmultispace string in first round
+ last_lmultispace = p;
+ lead_multispace_len = 0;
+ while (*s != NUL && *s != ',') {
+ c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ lead_multispace_len++;
+ }
+ if (lead_multispace_len == 0) {
+ // lcs-leadmultispace cannot be an empty string
+ return e_invarg;
+ }
+ p = s;
+ } else {
+ int multispace_pos = 0;
+ while (*s != NUL && *s != ',') {
+ c1 = get_encoded_char_adv(&s);
+ if (p == last_lmultispace) {
+ wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
+ }
+ }
+ p = s;
+ }
+ } else {
+ return e_invarg;
+ }
+ }
+ if (*p == ',') {
+ p++;
+ }
+ }
}
- height = Rows;
- width = Columns;
- p_lines = Rows;
- p_columns = Columns;
- ui_call_grid_resize(1, width, height);
-
- send_grid_resize = true;
- /// The window layout used to be adjusted here, but it now happens in
- /// screenalloc() (also invoked from screenclear()). That is because the
- /// recursize "resizing_screen" check above may skip this, but not screenalloc().
+ return NULL; // no error
+}
- if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
- screenclear();
+/// Check all global and local values of 'listchars' and 'fillchars'.
+/// May set different defaults in case character widths change.
+///
+/// @return an untranslated error message if any of them is invalid, NULL otherwise.
+char *check_chars_options(void)
+{
+ if (set_chars_option(curwin, &p_lcs, false) != NULL) {
+ return e_conflicts_with_value_of_listchars;
}
-
- if (starting != NO_SCREEN) {
- maketitle();
- changed_line_abv_curs();
- invalidate_botline();
-
- // We only redraw when it's needed:
- // - While at the more prompt or executing an external command, don't
- // redraw, but position the cursor.
- // - While editing the command line, only redraw that.
- // - in Ex mode, don't redraw anything.
- // - Otherwise, redraw right now, and position the cursor.
- // Always need to call update_screen() or screenalloc(), to make
- // sure Rows/Columns and the size of the screen is correct!
- if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
- || exmode_active) {
- screenalloc();
- if (msg_grid.chars) {
- msg_grid_validate();
- }
- // TODO(bfredl): sometimes messes up the output. Implement clear+redraw
- // also for the pager? (or: what if the pager was just a modal window?)
- ui_comp_set_screen_valid(true);
- repeat_message();
- } else {
- if (curwin->w_p_scb) {
- do_check_scrollbind(true);
- }
- if (State & MODE_CMDLINE) {
- redraw_popupmenu = false;
- update_screen(NOT_VALID);
- redrawcmdline();
- if (pum_drawn()) {
- cmdline_pum_display(false);
- }
- } else {
- update_topline(curwin);
- if (pum_drawn()) {
- // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
- // For now make sure the nested update_screen(0) won't redraw the
- // pum at the old position. Try to untangle this later.
- redraw_popupmenu = false;
- ins_compl_show_pum();
- }
- update_screen(NOT_VALID);
- if (redrawing()) {
- setcursor();
- }
- }
+ if (set_chars_option(curwin, &p_fcs, false) != NULL) {
+ return e_conflicts_with_value_of_fillchars;
+ }
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
+ return e_conflicts_with_value_of_listchars;
+ }
+ if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
+ return e_conflicts_with_value_of_fillchars;
}
- ui_flush();
}
- resizing_screen = false;
+ return NULL;
}
/// Check if the new Nvim application "screen" dimensions are valid.
@@ -6766,13 +1981,3 @@ void check_screensize(void)
Columns = 10000;
}
}
-
-win_T *get_win_by_grid_handle(handle_T handle)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_grid_alloc.handle == handle) {
- return wp;
- }
- }
- return NULL;
-}
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index 9eda5223f1..ea1c58cd80 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -4,31 +4,10 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
-#include "nvim/grid.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
+#include "nvim/fold.h"
+#include "nvim/grid_defs.h"
-// flags for update_screen()
-// The higher the value, the higher the priority
-#define VALID 10 // buffer not changed, or changes marked
- // with b_mod_*
-#define INVERTED 20 // redisplay inverted part that changed
-#define INVERTED_ALL 25 // redisplay whole inverted part
-#define REDRAW_TOP 30 // display first w_upd_rows screen lines
-#define SOME_VALID 35 // like NOT_VALID but may scroll
-#define NOT_VALID 40 // buffer needs complete redraw
-#define CLEAR 50 // screen messed up, clear it
-
-/// corner value flags for hsep_connected and vsep_connected
-typedef enum {
- WC_TOP_LEFT = 0,
- WC_TOP_RIGHT,
- WC_BOTTOM_LEFT,
- WC_BOTTOM_RIGHT,
-} WindowCorner;
-
-// Maximum columns for terminal highlight attributes
-#define TERM_ATTRS_MAX 1024
+EXTERN match_T screen_search_hl; // used for 'hlsearch' highlight matching
/// Array defining what should be done when tabline is clicked
EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
@@ -39,13 +18,6 @@ EXTERN long tab_page_click_defs_size INIT(= 0);
#define W_ENDCOL(wp) ((wp)->w_wincol + (wp)->w_width)
#define W_ENDROW(wp) ((wp)->w_winrow + (wp)->w_height)
-// While redrawing the screen this flag is set. It means the screen size
-// ('lines' and 'rows') must not be changed.
-EXTERN bool updating_screen INIT(= 0);
-
-// While resizing the screen this flag is set.
-EXTERN bool resizing_screen INIT(= 0);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.h.generated.h"
#endif
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 94ec26e709..c820817a71 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -17,6 +17,7 @@
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
@@ -45,7 +46,6 @@
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -515,7 +515,7 @@ void last_pat_prog(regmmatch_T *regmatch)
}
++emsg_off; // So it doesn't beep if bad expr
(void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
- --emsg_off;
+ emsg_off--;
}
/// Lowest level search function.
@@ -1162,9 +1162,9 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
} else { // single '+'
spats[0].off.off = 1;
}
- ++p;
+ p++;
while (ascii_isdigit(*p)) { // skip number
- ++p;
+ p++;
}
}
@@ -1428,7 +1428,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
emsg(_("E386: Expected '?' or '/' after ';'"));
goto end_do_search;
}
- ++pat;
+ pat++;
}
if (options & SEARCH_MARK) {
@@ -2138,10 +2138,10 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
if (*ptr == '"'
&& (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) {
- ++do_quotes;
+ do_quotes++;
}
if (*ptr == '\\' && ptr[1] != NUL) {
- ++ptr;
+ ptr++;
}
}
do_quotes &= 1; // result is 1 with even number of quotes
@@ -2344,7 +2344,7 @@ int check_linecomment(const char_u *line)
&& !is_pos_in_string(line, (colnr_T)(p - line))) {
break;
}
- ++p;
+ p++;
}
}
@@ -2631,7 +2631,7 @@ bool findpar(bool *pincl, int dir, long count, int what, int both)
}
setpcmark();
if (both && *ml_get(curr) == '}') { // include line with '}'
- ++curr;
+ curr++;
}
curwin->w_cursor.lnum = curr;
if (curr == curbuf->b_ml.ml_line_count && what != '}') {
@@ -2669,7 +2669,7 @@ static int inmacro(char_u *opt, char_u *s)
&& (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) {
break;
}
- ++macro;
+ macro++;
if (macro[0] == NUL) {
break;
}
@@ -3112,7 +3112,7 @@ int current_word(oparg_T *oap, long count, int include, int bigword)
oap->start = start_pos;
oap->motion_type = kMTCharWise;
}
- --count;
+ count--;
}
/*
@@ -3162,7 +3162,7 @@ int current_word(oparg_T *oap, long count, int include, int bigword)
}
}
}
- --count;
+ count--;
}
if (include_white && (cls() != 0
@@ -3325,7 +3325,7 @@ extend:
} else {
ncount = count;
if (start_blank) {
- --ncount;
+ ncount--;
}
}
if (ncount > 0) {
@@ -3853,7 +3853,7 @@ extend:
break;
}
}
- --start_lnum;
+ start_lnum--;
}
/*
@@ -3861,13 +3861,13 @@ extend:
*/
end_lnum = start_lnum;
while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) {
- ++end_lnum;
+ end_lnum++;
}
end_lnum--;
i = (int)count;
if (!include && white_in_front) {
- --i;
+ i--;
}
while (i--) {
if (end_lnum == curbuf->b_ml.ml_line_count) {
@@ -3879,14 +3879,12 @@ extend:
}
if (include || !do_white) {
- ++end_lnum;
- /*
- * skip to end of paragraph
- */
+ end_lnum++;
+ // skip to end of paragraph
while (end_lnum < curbuf->b_ml.ml_line_count
&& !linewhite(end_lnum + 1)
&& !startPS(end_lnum + 1, 0, 0)) {
- ++end_lnum;
+ end_lnum++;
}
}
@@ -3900,7 +3898,7 @@ extend:
if (include || do_white) {
while (end_lnum < curbuf->b_ml.ml_line_count
&& linewhite(end_lnum + 1)) {
- ++end_lnum;
+ end_lnum++;
}
}
}
@@ -3911,7 +3909,7 @@ extend:
*/
if (!white_in_front && !linewhite(end_lnum) && include) {
while (start_lnum > 1 && linewhite(start_lnum - 1)) {
- --start_lnum;
+ start_lnum--;
}
}
@@ -3985,7 +3983,7 @@ static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *e
if (escape != NULL) {
while (col_start - n > 0 && vim_strchr((char *)escape,
line[col_start - n - 1]) != NULL) {
- ++n;
+ n++;
}
}
if (n & 1) {
@@ -4171,11 +4169,11 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
if (include) {
if (ascii_iswhite(line[col_end + 1])) {
while (ascii_iswhite(line[col_end + 1])) {
- ++col_end;
+ col_end++;
}
} else {
while (col_start > 0 && ascii_iswhite(line[col_start - 1])) {
- --col_start;
+ col_start--;
}
}
}
@@ -5469,7 +5467,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
did_show = true;
while (depth_displayed < depth && !got_int) {
- ++depth_displayed;
+ depth_displayed++;
for (i = 0; i < depth_displayed; i++) {
msg_puts(" ");
}
@@ -5511,11 +5509,11 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
// Avoid checking before the start of the line, can
// happen if \zs appears in the regexp.
if (p[-1] == '"' || p[-1] == '<') {
- --p;
- ++i;
+ p--;
+ i++;
}
if (p[i] == '"' || p[i] == '>') {
- ++i;
+ i++;
}
}
save_char = p[i];
@@ -5563,7 +5561,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
// Something wrong. We will forget one of our already visited files
// now.
xfree(files[old_files].name);
- ++old_files;
+ old_files++;
}
files[depth].name = curr_fname = new_fname;
files[depth].lnum = 0;
@@ -5862,7 +5860,7 @@ exit_matched:
while (depth >= 0 && !already
&& vim_fgets(line = file_line, LSIZE, files[depth].fp)) {
fclose(files[depth].fp);
- --old_files;
+ old_files--;
files[old_files].name = files[depth].name;
files[old_files].matched = files[depth].matched;
depth--;
@@ -5950,10 +5948,10 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
if (fp != NULL) {
// We used fgets(), so get rid of newline at end
if (p >= line && *p == '\n') {
- --p;
+ p--;
}
if (p >= line && *p == '\r') {
- --p;
+ p--;
}
*(p + 1) = NUL;
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index a1e61a4d8c..f1ddbfd147 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -9,6 +9,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval/funcs.h"
#include "nvim/ex_docmd.h"
@@ -16,7 +17,6 @@
#include "nvim/highlight_group.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
@@ -442,27 +442,24 @@ static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group
/// @param max_signs the number of signs, with priority for the ones
/// with the highest Ids.
/// @return Attrs of the matching sign, or NULL
-sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int max_signs)
+SignTextAttrs *sign_get_attr(int idx, SignTextAttrs sattrs[], int max_signs)
{
- sign_attrs_T *matches[SIGN_SHOW_MAX];
- int nr_matches = 0;
+ SignTextAttrs *matches[SIGN_SHOW_MAX];
+ int sattr_matches = 0;
for (int i = 0; i < SIGN_SHOW_MAX; i++) {
- if ((type == SIGN_TEXT && sattrs[i].sat_text != NULL)
- || (type == SIGN_LINEHL && sattrs[i].sat_linehl != 0)
- || (type == SIGN_NUMHL && sattrs[i].sat_numhl != 0)) {
- matches[nr_matches] = &sattrs[i];
- nr_matches++;
+ if (sattrs[i].text != NULL) {
+ matches[sattr_matches++] = &sattrs[i];
// attr list is sorted with most important (priority, id), thus we
// may stop as soon as we have max_signs matches
- if (nr_matches >= max_signs) {
+ if (sattr_matches >= max_signs) {
break;
}
}
}
- if (nr_matches > idx) {
- return matches[nr_matches - idx - 1];
+ if (sattr_matches > idx) {
+ return matches[sattr_matches - idx - 1];
}
return NULL;
@@ -474,12 +471,12 @@ sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int m
/// @param lnum Line in which to search
/// @param sattrs Output array for attrs
/// @return Number of signs of which attrs were found
-int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
+int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriAttr *num_attrs,
+ HlPriAttr *line_attrs, HlPriAttr *cul_attrs)
{
sign_entry_T *sign;
- sign_T *sp;
- int nr_matches = 0;
+ int sattr_matches = 0;
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->se_lnum > lnum) {
@@ -488,37 +485,39 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
break;
}
- if (sign->se_lnum == lnum) {
- sign_attrs_T sattr;
- CLEAR_FIELD(sattr);
- sattr.sat_typenr = sign->se_typenr;
- sp = find_sign_by_typenr(sign->se_typenr);
- if (sp != NULL) {
- sattr.sat_text = sp->sn_text;
- if (sattr.sat_text != NULL && sp->sn_text_hl != 0) {
- sattr.sat_texthl = syn_id2attr(sp->sn_text_hl);
- }
- if (sp->sn_line_hl != 0) {
- sattr.sat_linehl = syn_id2attr(sp->sn_line_hl);
- }
- if (sp->sn_cul_hl != 0) {
- sattr.sat_culhl = syn_id2attr(sp->sn_cul_hl);
- }
- if (sp->sn_num_hl != 0) {
- sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
- }
- // Store the priority so we can mesh in extmark signs later
- sattr.sat_prio = sign->se_priority;
- }
+ if (sign->se_lnum < lnum) {
+ continue;
+ }
- sattrs[nr_matches] = sattr;
- nr_matches++;
- if (nr_matches == SIGN_SHOW_MAX) {
- break;
+ sign_T *sp = find_sign_by_typenr(sign->se_typenr);
+ if (sp == NULL) {
+ continue;
+ }
+
+ if (sp->sn_text != NULL && sattr_matches < SIGN_SHOW_MAX) {
+ sattrs[sattr_matches++] = (SignTextAttrs) {
+ .text = sp->sn_text,
+ .hl_attr_id = sp->sn_text_hl == 0 ? 0 : syn_id2attr(sp->sn_text_hl),
+ .priority = sign->se_priority
+ };
+ }
+
+ struct { HlPriAttr *dest; int hl; } cattrs[] = {
+ { line_attrs, sp->sn_line_hl },
+ { num_attrs, sp->sn_num_hl },
+ { cul_attrs, sp->sn_cul_hl },
+ { NULL, -1 },
+ };
+ for (int i = 0; cattrs[i].dest; i++) {
+ if (cattrs[i].hl != 0 && sign->se_priority >= cattrs[i].dest->priority) {
+ *cattrs[i].dest = (HlPriAttr) {
+ .attr_id = syn_id2attr(cattrs[i].hl),
+ .priority = sign->se_priority
+ };
}
}
}
- return nr_matches;
+ return sattr_matches;
}
/// Delete sign 'id' in group 'group' from buffer 'buf'.
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index e4ece71846..a4fb325ec8 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -33,15 +33,11 @@ struct sign_entry {
};
/// Sign attributes. Used by the screen refresh routines.
-typedef struct sign_attrs_S {
- int sat_typenr;
- char_u *sat_text;
- int sat_texthl;
- int sat_linehl;
- int sat_culhl;
- int sat_numhl;
- int sat_prio; // Used for inserting extmark signs
-} sign_attrs_T;
+typedef struct {
+ char_u *text;
+ int hl_attr_id;
+ int priority;
+} SignTextAttrs;
#define SIGN_SHOW_MAX 9
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index ed2b8bbdb5..1e44d328b3 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -72,6 +72,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
@@ -491,13 +492,13 @@ static void find_word(matchinf_T *mip, int mode)
}
endlen[endidxcnt] = wlen;
endidx[endidxcnt++] = arridx++;
- --len;
+ len--;
// Skip over the zeros, there can be several flag/region
// combinations.
while (len > 0 && byts[arridx] == 0) {
- ++arridx;
- --len;
+ arridx++;
+ len--;
}
if (len == 0) {
break; // no children, word must end here
@@ -535,8 +536,8 @@ static void find_word(matchinf_T *mip, int mode)
// Continue at the child (if there is one).
arridx = idxs[lo];
- ++wlen;
- --flen;
+ wlen++;
+ flen--;
// One space in the good word may stand for several spaces in the
// checked word.
@@ -548,8 +549,8 @@ static void find_word(matchinf_T *mip, int mode)
if (ptr[wlen] != ' ' && ptr[wlen] != TAB) {
break;
}
- ++wlen;
- --flen;
+ wlen++;
+ flen--;
}
}
}
@@ -560,7 +561,7 @@ static void find_word(matchinf_T *mip, int mode)
// Verify that one of the possible endings is valid. Try the longest
// first.
while (endidxcnt > 0) {
- --endidxcnt;
+ endidxcnt--;
arridx = endidx[endidxcnt];
wlen = endlen[endidxcnt];
@@ -980,7 +981,7 @@ bool match_compoundrule(slang_T *slang, char_u *compflags)
bool match = false;
// compare against all the flags in []
- ++p;
+ p++;
while (*p != ']' && *p != NUL) {
if (*p++ == c) {
match = true;
@@ -992,7 +993,7 @@ bool match_compoundrule(slang_T *slang, char_u *compflags)
} else if (*p != c) {
break; // flag of word doesn't match flag in pattern
}
- ++p;
+ p++;
}
// Skip to the next "/", where the next pattern starts.
@@ -1108,8 +1109,8 @@ static void find_prefix(matchinf_T *mip, int mode)
mip->mi_prefarridx = arridx;
mip->mi_prefcnt = len;
while (len > 0 && byts[arridx] == 0) {
- ++arridx;
- --len;
+ arridx++;
+ len--;
}
mip->mi_prefcnt -= len;
@@ -1158,8 +1159,8 @@ static void find_prefix(matchinf_T *mip, int mode)
// Continue at the child (if there is one).
arridx = idxs[lo];
- ++wlen;
- --flen;
+ wlen++;
+ flen--;
}
}
@@ -1407,7 +1408,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
capcol = -1;
} else {
if (lnum < wp->w_buffer->b_ml.ml_line_count) {
- ++lnum;
+ lnum++;
} else if (!p_ws) {
break; // at first line and 'nowrapscan'
} else {
@@ -1435,7 +1436,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
}
// Capcol skips over the inserted space.
- --capcol;
+ capcol--;
// But after empty line check first word in next line
if (empty_line) {
@@ -1810,7 +1811,7 @@ static int count_syllables(slang_T *slang, const char_u *word)
}
}
if (len != 0) { // found a match, count syllable
- ++cnt;
+ cnt++;
skip = false;
} else {
// No recognized syllable item, at least a syllable char then?
@@ -2606,10 +2607,10 @@ void ex_spellrepall(exarg_T *eap)
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
if (curwin->w_cursor.lnum != prev_lnum) {
- ++sub_nlines;
+ sub_nlines++;
prev_lnum = curwin->w_cursor.lnum;
}
- ++sub_nsubs;
+ sub_nsubs++;
}
curwin->w_cursor.col += (colnr_T)STRLEN(repl_to);
}
@@ -2900,12 +2901,12 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
if ((pf = smp[n].sm_oneof_w) != NULL) {
// Check for match with one of the chars in "sm_oneof".
while (*pf != NUL && *pf != word[i + k]) {
- ++pf;
+ pf++;
}
if (*pf == NUL) {
continue;
}
- ++k;
+ k++;
}
char_u *s = smp[n].sm_rules;
pri = 5; // default priority
@@ -2975,12 +2976,12 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// Check for match with one of the chars in
// "sm_oneof".
while (*pf != NUL && *pf != word[i + k0]) {
- ++pf;
+ pf++;
}
if (*pf == NUL) {
continue;
}
- ++k0;
+ k0++;
}
p0 = 5;
@@ -3284,7 +3285,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
&& (pat == NULL || !ins_compl_interrupted())) {
if (curi[depth] > byts[arridx[depth]]) {
// Done all bytes at this node, go up one level.
- --depth;
+ depth--;
line_breakcheck();
ins_compl_check_keys(50, false);
} else {
@@ -3317,7 +3318,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
dump_word(slang, word, pat, dir,
dumpflags, flags, lnum);
if (pat == NULL) {
- ++lnum;
+ lnum++;
}
}
@@ -3342,7 +3343,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
assert(depth >= 0);
if (depth <= patlen
&& mb_strnicmp(word, pat, (size_t)depth) != 0) {
- --depth;
+ depth--;
}
}
}
@@ -3480,7 +3481,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
len = byts[n];
if (curi[depth] > len) {
// Done all bytes at this node, go up one level.
- --depth;
+ depth--;
line_breakcheck();
} else {
// Do one more byte at this node.
@@ -3503,7 +3504,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
(c & WF_RAREPFX) ? (flags | WF_RARE)
: flags, lnum);
if (lnum != 0) {
- ++lnum;
+ lnum++;
}
}
@@ -3519,7 +3520,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
(c & WF_RAREPFX) ? (flags | WF_RARE)
: flags, lnum);
if (lnum != 0) {
- ++lnum;
+ lnum++;
}
}
}
@@ -3606,3 +3607,71 @@ int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp)
*matchp = ga.ga_data;
return ga.ga_len;
}
+
+/// Return true if "val" is a valid 'spelllang' value.
+bool valid_spelllang(const char_u *val)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return valid_name(val, ".-_,@");
+}
+
+/// Return true if "val" is a valid 'spellfile' value.
+bool valid_spellfile(const char_u *val)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ for (const char_u *s = val; *s != NUL; s++) {
+ if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
+ return false;
+ }
+ }
+ return true;
+}
+
+char *did_set_spell_option(bool is_spellfile)
+{
+ char *errmsg = NULL;
+
+ if (is_spellfile) {
+ int l = (int)STRLEN(curwin->w_s->b_p_spf);
+ if (l > 0
+ && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
+ errmsg = e_invarg;
+ }
+ }
+
+ if (errmsg == NULL) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == curbuf && wp->w_p_spell) {
+ errmsg = did_set_spelllang(wp);
+ break;
+ }
+ }
+ }
+
+ return errmsg;
+}
+
+/// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
+/// Return error message when failed, NULL when OK.
+char *compile_cap_prog(synblock_T *synblock)
+ FUNC_ATTR_NONNULL_ALL
+{
+ regprog_T *rp = synblock->b_cap_prog;
+ char_u *re;
+
+ if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
+ synblock->b_cap_prog = NULL;
+ } else {
+ // Prepend a ^ so that we only match at one column
+ re = concat_str((char_u *)"^", synblock->b_p_spc);
+ synblock->b_cap_prog = vim_regcomp((char *)re, RE_MAGIC);
+ xfree(re);
+ if (synblock->b_cap_prog == NULL) {
+ synblock->b_cap_prog = rp; // restore the previous program
+ return e_invarg;
+ }
+ }
+
+ vim_regfree(rp);
+ return NULL;
+}
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 58a66f0635..be1373f617 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -230,9 +230,11 @@
#include <stdio.h>
#include <wctype.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/ex_cmds2.h"
#include "nvim/fileio.h"
#include "nvim/memline.h"
@@ -243,7 +245,6 @@
#include "nvim/path.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/spell_defs.h"
#include "nvim/spellfile.h"
@@ -839,7 +840,7 @@ static void tree_count_words(char_u *byts, idx_T *idxs)
wordcount[depth - 1] += wordcount[depth];
}
- --depth;
+ depth--;
fast_breakcheck();
} else {
// Do one more byte at this node.
@@ -854,12 +855,12 @@ static void tree_count_words(char_u *byts, idx_T *idxs)
// Skip over any other NUL bytes (same word with different
// flags).
while (byts[n + 1] == 0) {
- ++n;
- ++curi[depth];
+ n++;
+ curi[depth]++;
}
} else {
// Normal char, go one level deeper to count the words.
- ++depth;
+ depth++;
arridx[depth] = idxs[n];
curi[depth] = 1;
wordcount[depth] = 0;
@@ -1370,21 +1371,21 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
if (todo < 2) {
return SP_FORMERROR; // need at least two bytes
}
- --todo;
+ todo--;
c = getc(fd); // <compmax>
if (c < 2) {
c = MAXWLEN;
}
slang->sl_compmax = c;
- --todo;
+ todo--;
c = getc(fd); // <compminlen>
if (c < 1) {
c = 0;
}
slang->sl_compminlen = c;
- --todo;
+ todo--;
c = getc(fd); // <compsylmax>
if (c < 1) {
c = MAXWLEN;
@@ -1395,9 +1396,9 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
if (c != 0) {
ungetc(c, fd); // be backwards compatible with Vim 7.0b
} else {
- --todo;
+ todo--;
c = getc(fd); // only use the lower byte for now
- --todo;
+ todo--;
slang->sl_compoptions = c;
gap = &slang->sl_comppat;
@@ -2064,7 +2065,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Read all the lines in the file one by one.
while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
- ++lnum;
+ lnum++;
// Skip comment lines.
if (*rline == '#') {
@@ -2091,7 +2092,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
itemcnt = 0;
for (p = line;;) {
while (*p != NUL && *p <= ' ') { // skip white space and CR/NL
- ++p;
+ p++;
}
if (*p == NUL) {
break;
@@ -2103,11 +2104,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// A few items have arbitrary text argument, don't split them.
if (itemcnt == 2 && spell_info_item(items[0])) {
while (*p >= ' ' || *p == TAB) { // skip until CR/NL
- ++p;
+ p++;
}
} else {
while (*p > ' ') { // skip until white space or CR/NL
- ++p;
+ p++;
}
}
if (*p == NUL) {
@@ -2384,7 +2385,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Check for the "S" flag, which apparently means that another
// block with the same affix name is following.
if (itemcnt > lasti && STRCMP(items[lasti], "S") == 0) {
- ++lasti;
+ lasti++;
cur_aff->ah_follows = true;
} else {
cur_aff->ah_follows = false;
@@ -2806,7 +2807,7 @@ static void aff_process_flags(afffile_T *affile, affentry_T *entry)
}
}
if (affile->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
if (*entry->ae_flags == NUL) {
@@ -2942,7 +2943,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
*tp++ = (char_u)id;
}
if (aff->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
}
@@ -3069,7 +3070,7 @@ static void spell_free_aff(afffile_T *aff)
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
ah = HI2AH(hi);
for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) {
vim_regfree(ae->ae_prog);
@@ -3139,7 +3140,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// the hashtable.
while (!vim_fgets(line, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
- ++lnum;
+ lnum++;
if (line[0] == '#' || line[0] == '/') {
continue; // comment line
}
@@ -3147,7 +3148,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// the word is kept to allow multi-word terms like "et al.".
l = (int)STRLEN(line);
while (l > 0 && line[l - 1] <= ' ') {
- --l;
+ l--;
}
if (l == 0) {
continue; // empty line
@@ -3183,7 +3184,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// Skip non-ASCII words when "spin->si_ascii" is true.
if (spin->si_ascii && has_non_ascii(w)) {
- ++non_ascii;
+ non_ascii++;
xfree(pc);
continue;
}
@@ -3224,7 +3225,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
smsg(_("First duplicate word in %s line %d: %s"),
fname, lnum, dw);
}
- ++duplicate;
+ duplicate++;
} else {
hash_add_item(&ht, hi, dw, hash);
}
@@ -3359,7 +3360,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
}
}
if (affile->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
@@ -3389,7 +3390,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl
}
}
if (affile->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
@@ -3435,7 +3436,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0 && retval == OK; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
ah = HI2AH(hi);
// Check that the affix combines, if required, and that the word
@@ -3683,7 +3684,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
// Read all the lines in the file one by one.
while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
- ++lnum;
+ lnum++;
// Skip comment lines.
if (*rline == '#') {
@@ -3693,7 +3694,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
// Remove CR, LF and white space from the end.
l = (int)STRLEN(rline);
while (l > 0 && rline[l - 1] <= ' ') {
- --l;
+ l--;
}
if (l == 0) {
continue; // empty or blank line
@@ -3716,7 +3717,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
}
if (*line == '/') {
- ++line;
+ line++;
if (STRNCMP(line, "encoding=", 9) == 0) {
if (spin->si_conv.vc_type != CONV_NONE) {
smsg(_("Duplicate /encoding= line ignored in %s line %ld: %s"),
@@ -3799,13 +3800,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
fname, lnum, p);
break;
}
- ++p;
+ p++;
}
}
// Skip non-ASCII words when "spin->si_ascii" is true.
if (spin->si_ascii && has_non_ascii(line)) {
- ++non_ascii;
+ non_ascii++;
continue;
}
@@ -4170,7 +4171,7 @@ static int deref_wordnode(spellinfo_T *spin, wordnode_T *node)
cnt += deref_wordnode(spin, np->wn_child);
}
free_wordnode(spin, np);
- ++cnt;
+ cnt++;
}
++cnt; // length field
}
@@ -4245,7 +4246,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
// Note that with "child" we mean not just the node that is pointed to,
// but the whole list of siblings of which the child node is the first.
for (np = node; np != NULL && !got_int; np = np->wn_sibling) {
- ++len;
+ len++;
if ((child = np->wn_child) != NULL) {
// Compress the child first. This fills hashkey.
compressed += node_compress(spin, child, ht, tot);
@@ -4578,7 +4579,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
if (round == 2) { // <word>
fwv &= fwrite(hi->hi_key, l, 1, fd);
}
- --todo;
+ todo--;
}
}
if (round == 1) {
@@ -4779,7 +4780,7 @@ static int put_node(FILE *fd, wordnode_T *node, int idx, int regionmask, bool pr
// Count the number of siblings.
int siblingcount = 0;
for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) {
- ++siblingcount;
+ siblingcount++;
}
// Write the sibling count.
@@ -5009,7 +5010,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
wordcount[depth - 1] += wordcount[depth];
}
- --depth;
+ depth--;
line_breakcheck();
} else {
// Do one more byte at this node.
@@ -5030,8 +5031,8 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
return FAIL;
}
- ++words_done;
- ++wordcount[depth];
+ words_done++;
+ wordcount[depth]++;
// Reset the block count each time to avoid compression
// kicking in.
diff --git a/src/nvim/state.c b/src/nvim/state.c
index d6cca71ad8..61740800a1 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -5,6 +5,7 @@
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/getchar.h"
@@ -15,7 +16,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/os/input.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index dbd413c2d5..78312c738c 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -264,7 +264,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
*d++ = '\\';
*d++ = '\'';
*d++ = '\'';
- ++p;
+ p++;
continue;
}
if ((*p == '\n' && (csh_like || do_newline))
@@ -431,8 +431,8 @@ int vim_stricmp(const char *s1, const char *s2)
if (*s1 == NUL) {
break; // strings match until NUL
}
- ++s1;
- ++s2;
+ s1++;
+ s2++;
}
return 0; // strings match
}
@@ -457,9 +457,9 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len)
if (*s1 == NUL) {
break; // strings match until NUL
}
- ++s1;
- ++s2;
- --len;
+ s1++;
+ s2++;
+ len--;
}
return 0; // strings match
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 1079533df2..47b5647a08 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -18,6 +18,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_docmd.h"
@@ -43,7 +44,6 @@
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -379,7 +379,7 @@ void syntax_start(win_T *wp, linenr_T lnum)
&& current_lnum < syn_buf->b_ml.ml_line_count) {
(void)syn_finish_line(false);
if (!current_state_stored) {
- ++current_lnum;
+ current_lnum++;
(void)store_current_state();
}
@@ -724,7 +724,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
} else if (found_m_endpos.col > current_col) {
current_col = found_m_endpos.col;
} else {
- ++current_col;
+ current_col++;
}
// syn_current_attr() will have skipped the check for
@@ -732,7 +732,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
// careful not to go past the NUL.
prev_current_col = current_col;
if (syn_getcurline()[current_col] != NUL) {
- ++current_col;
+ current_col++;
}
check_state_ends();
current_col = prev_current_col;
@@ -1030,7 +1030,7 @@ static void syn_stack_alloc(void)
// Move the states from the old array to the new one.
for (from = syn_block->b_sst_first; from != NULL;
from = from->sst_next) {
- ++to;
+ to++;
*to = *from;
to->sst_next = to + 1;
}
@@ -1501,7 +1501,7 @@ bool syntax_check_changed(linenr_T lnum)
/*
* Store the current state in b_sst_array[] for later use.
*/
- ++current_lnum;
+ current_lnum++;
(void)store_current_state();
}
}
@@ -2096,9 +2096,9 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
check_state_ends();
if (!GA_EMPTY(&current_state)
&& syn_getcurline()[current_col] != NUL) {
- ++current_col;
+ current_col++;
check_state_ends();
- --current_col;
+ current_col--;
}
}
} else if (can_spell != NULL) {
@@ -2583,7 +2583,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
if (spp->sp_type != SPTYPE_START) {
break;
}
- ++idx;
+ idx++;
}
/*
@@ -2591,7 +2591,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
*/
if (spp->sp_type == SPTYPE_SKIP) {
spp_skip = spp;
- ++idx;
+ idx++;
} else {
spp_skip = NULL;
}
@@ -3654,7 +3654,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
&& SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) {
put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
}
- --idx;
+ idx--;
msg_putchar(' ');
}
syn_list_flags(namelist1, spp->sp_flags, attr);
@@ -3928,7 +3928,7 @@ static void syn_clear_keyword(int id, hashtab_T *ht)
if (HASHITEM_EMPTY(hi)) {
continue;
}
- --todo;
+ todo--;
kp_prev = NULL;
for (kp = HI2KE(hi); kp != NULL;) {
if (kp->k_syn.id == id) {
@@ -3968,7 +3968,7 @@ static void clear_keywtab(hashtab_T *ht)
todo = (int)ht->ht_used;
for (hi = ht->ht_array; todo > 0; ++hi) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
for (kp = HI2KE(hi); kp != NULL; kp = kp_next) {
kp_next = kp->ke_next;
xfree(kp->next_list);
@@ -4258,7 +4258,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
}
if (arg[0] == '@') {
- ++arg;
+ arg++;
rest = get_group_name(arg, &group_name_end);
if (rest == NULL) {
emsg(_("E397: Filename required"));
@@ -4584,7 +4584,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
// must be a pattern or matchgroup then
key_end = rest;
while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') {
- ++key_end;
+ key_end++;
}
xfree(key);
key = vim_strnsave_up(rest, (size_t)(key_end - rest));
@@ -4709,8 +4709,8 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
syn_opt_arg.next_list;
}
- ++curwin->w_s->b_syn_patterns.ga_len;
- ++idx;
+ curwin->w_s->b_syn_patterns.ga_len++;
+ idx++;
if (syn_opt_arg.flags & HL_FOLD) {
++curwin->w_s->b_syn_folditems;
}
@@ -5082,7 +5082,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
/*
* Check for a match, highlight or region offset.
*/
- ++end;
+ end++;
do {
for (idx = SPO_COUNT; --idx >= 0;) {
if (STRNCMP(end, spo_name_tab[idx], 3) == 0) {
@@ -5127,7 +5127,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
if (*end != ',') {
break;
}
- ++end;
+ end++;
}
}
} while (idx >= 0);
@@ -5402,7 +5402,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
retval[count] = (int16_t)id;
}
}
- ++count;
+ count++;
}
p = skipwhite(end);
if (*p != ',') {
@@ -5478,7 +5478,7 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
// that we don't go back past the first one.
while ((cur_si->si_flags & HL_TRANS_CONT)
&& cur_si > (stateitem_T *)(current_state.ga_data)) {
- --cur_si;
+ cur_si--;
}
// cur_si->si_idx is -1 for keywords, these never contain anything.
if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
@@ -5542,9 +5542,9 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
// restrict recursiveness to 30 to avoid an endless loop for a
// cluster that includes itself (indirectly)
if (scl_list != NULL && depth < 30) {
- ++depth;
+ depth++;
r = in_id_list(NULL, scl_list, ssp, contained);
- --depth;
+ depth--;
if (r) {
return retval;
}
@@ -5615,7 +5615,7 @@ void ex_syntax(exarg_T *eap)
}
xfree(subcmd_name);
if (eap->skip) {
- --emsg_skip;
+ emsg_skip--;
}
}
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index aa68cbf0b0..f212aefbfc 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -14,6 +14,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
@@ -23,6 +24,7 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/help.h"
#include "nvim/if_cscope.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
@@ -40,7 +42,6 @@
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
@@ -465,7 +466,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// when the argument starts with '/', use it as a regexp
if (!no_regexp && *name == '/') {
flags = TAG_REGEXP;
- ++name;
+ name++;
} else {
flags = TAG_NOIC;
}
@@ -653,13 +654,13 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
|| cur_match < num_matches - 1))) {
error_cur_match = cur_match;
if (use_tagstack) {
- --tagstackidx;
+ tagstackidx--;
}
if (type == DT_PREV) {
- --cur_match;
+ cur_match--;
} else {
type = DT_NEXT;
- ++cur_match;
+ cur_match++;
}
continue;
}
@@ -1076,9 +1077,9 @@ static int tag_strnicmp(char_u *s1, char_u *s2, size_t len)
if (*s1 == NUL) {
break; // strings match until NUL
}
- ++s1;
- ++s2;
- --len;
+ s1++;
+ s2++;
+ len--;
}
return 0; // strings match
}
@@ -1612,7 +1613,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
// unless found already.
help_pri++;
if (STRICMP(help_lang, "en") != 0) {
- ++help_pri;
+ help_pri++;
}
}
}
@@ -2475,7 +2476,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
// Isolate file name, from first to second white space
if (*p != NUL) {
- ++p;
+ p++;
}
tagp->fname = p;
p = (char_u *)vim_strchr((char *)p, TAB);
@@ -2486,7 +2487,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
// find start of search command, after second white space
if (*p != NUL) {
- ++p;
+ p++;
}
if (*p == NUL) {
return FAIL;
@@ -2717,7 +2718,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
goto erret;
}
- ++RedrawingDisabled;
+ RedrawingDisabled++;
if (l_g_do_tagpreview != 0) {
postponed_split = 0; // don't split again below
@@ -3168,7 +3169,7 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta
if (end == NULL) {
end = start + STRLEN(start);
while (end > start && (end[-1] == '\r' || end[-1] == '\n')) {
- --end;
+ end--;
}
}
len = (int)(end - start);
@@ -3247,13 +3248,13 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
// separated by Tabs.
n = p;
while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') {
- ++p;
+ p++;
}
len = (int)(p - n);
if (*p == ':' && len > 0) {
s = ++p;
while (*p != NUL && *p >= ' ') {
- ++p;
+ p++;
}
n[len] = NUL;
if (add_tag_field(dict, (char *)n, s, p) == FAIL) {
@@ -3263,7 +3264,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
} else {
// Skip field without colon.
while (*p != NUL && *p >= ' ') {
- ++p;
+ p++;
}
}
if (*p == NUL) {
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index dffcf89f2c..844a79b33d 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -48,6 +48,7 @@
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/event/loop.h"
#include "nvim/event/time.h"
@@ -69,7 +70,6 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/terminal.h"
#include "nvim/ui.h"
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 6b16e888a9..fcd3d5724c 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -257,6 +257,7 @@ endfunc
func EarlyExit(test)
" It's OK for the test we use to test the quit detection.
if a:test != 'Test_zz_quit_detected()'
+ call add(v:errors, v:errmsg)
call add(v:errors, 'Test caused Vim to exit: ' . a:test)
endif
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index ca7c8574cb..521c3fcd57 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -87,6 +87,10 @@ func Test_argadd()
new
arga
call assert_equal(0, len(argv()))
+
+ if has('unix')
+ call assert_fails('argadd `Xdoes_not_exist`', 'E479:')
+ endif
endfunc
func Test_argadd_empty_curbuf()
@@ -408,6 +412,35 @@ func Test_argedit()
bw! x
endfunc
+" Test for the :argdedupe command
+func Test_argdedupe()
+ call Reset_arglist()
+ argdedupe
+ call assert_equal([], argv())
+ args a a a aa b b a b aa
+ argdedupe
+ call assert_equal(['a', 'aa', 'b'], argv())
+ args a b c
+ argdedupe
+ call assert_equal(['a', 'b', 'c'], argv())
+ args a
+ argdedupe
+ call assert_equal(['a'], argv())
+ args a A b B
+ argdedupe
+ if has('fname_case')
+ call assert_equal(['a', 'A', 'b', 'B'], argv())
+ else
+ call assert_equal(['a', 'b'], argv())
+ endif
+ args a b a c a b
+ last
+ argdedupe
+ next
+ call assert_equal('c', expand('%:t'))
+ %argd
+endfunc
+
" Test for the :argdelete command
func Test_argdelete()
call Reset_arglist()
@@ -420,6 +453,8 @@ func Test_argdelete()
call assert_equal(['b'], argv())
call assert_fails('argdelete', 'E610:')
call assert_fails('1,100argdelete', 'E16:')
+ call assert_fails('argdel /\)/', 'E55:')
+ call assert_fails('1argdel 1', 'E474:')
call Reset_arglist()
args a b c d
@@ -427,6 +462,8 @@ func Test_argdelete()
argdel
call Assert_argc(['a', 'c', 'd'])
%argdel
+
+ call assert_fails('argdel does_not_exist', 'E480:')
endfunc
func Test_argdelete_completion()
@@ -472,13 +509,16 @@ func Test_arglist_autocmd()
new
" redefine arglist; go to Xxx1
next! Xxx1 Xxx2 Xxx3
- " open window for all args
+ " open window for all args; Reading Xxx2 will change the arglist and the
+ " third window will get Xxx1:
+ " win 1: Xxx1
+ " win 2: Xxx2
+ " win 3: Xxx1
all
call assert_equal('test file Xxx1', getline(1))
wincmd w
wincmd w
call assert_equal('test file Xxx1', getline(1))
- " should now be in Xxx2
rewind
call assert_equal('test file Xxx2', getline(1))
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 89e3c9927b..716511210d 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2737,6 +2737,59 @@ func Test_autocmd_sigusr1()
unlet g:sigusr1_passed
endfunc
+" Test for BufReadPre autocmd deleting the file
+func Test_BufReadPre_delfile()
+ augroup TestAuCmd
+ au!
+ autocmd BufReadPre Xfile call delete('Xfile')
+ augroup END
+ call writefile([], 'Xfile')
+ call assert_fails('new Xfile', 'E200:')
+ call assert_equal('Xfile', @%)
+ call assert_equal(1, &readonly)
+ call delete('Xfile')
+ augroup TestAuCmd
+ au!
+ augroup END
+ close!
+endfunc
+
+" Test for BufReadPre autocmd changing the current buffer
+func Test_BufReadPre_changebuf()
+ augroup TestAuCmd
+ au!
+ autocmd BufReadPre Xfile edit Xsomeotherfile
+ augroup END
+ call writefile([], 'Xfile')
+ call assert_fails('new Xfile', 'E201:')
+ call assert_equal('Xsomeotherfile', @%)
+ call assert_equal(1, &readonly)
+ call delete('Xfile')
+ augroup TestAuCmd
+ au!
+ augroup END
+ close!
+endfunc
+
+" Test for BufWipeouti autocmd changing the current buffer when reading a file
+" in an empty buffer with 'f' flag in 'cpo'
+func Test_BufDelete_changebuf()
+ new
+ augroup TestAuCmd
+ au!
+ autocmd BufWipeout * let bufnr = bufadd('somefile') | exe "b " .. bufnr
+ augroup END
+ let save_cpo = &cpo
+ set cpo+=f
+ call assert_fails('r Xfile', 'E484:')
+ call assert_equal('somefile', @%)
+ let &cpo = save_cpo
+ augroup TestAuCmd
+ au!
+ augroup END
+ close!
+endfunc
+
" Test for the temporary internal window used to execute autocmds
func Test_autocmd_window()
%bw!
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index ffb8e3facd..579d3a5eb5 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -19,7 +19,7 @@ func Test_setbufline_getbufline()
let b = bufnr('%')
wincmd w
call assert_equal(1, setbufline(b, 5, ['x']))
- call assert_equal(1, setbufline(1234, 1, ['x']))
+ call assert_equal(1, setbufline(bufnr('$') + 1, 1, ['x']))
call assert_equal(0, setbufline(b, 4, ['d', 'e']))
call assert_equal(['c'], b->getbufline(3))
call assert_equal(['d'], getbufline(b, 4))
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 7aac731709..b9f027afb2 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -126,6 +126,40 @@ func Test_wildmenu_screendump()
call delete('XTest_wildmenu')
endfunc
+func Test_changing_cmdheight()
+ CheckScreendump
+
+ let lines =<< trim END
+ set cmdheight=1 laststatus=2
+ END
+ call writefile(lines, 'XTest_cmdheight')
+
+ let buf = RunVimInTerminal('-S XTest_cmdheight', {'rows': 8})
+ call term_sendkeys(buf, ":resize -3\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_1', {})
+
+ " using the space available doesn't change the status line
+ call term_sendkeys(buf, ":set cmdheight+=3\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_2', {})
+
+ " using more space moves the status line up
+ call term_sendkeys(buf, ":set cmdheight+=1\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_3', {})
+
+ " reducing cmdheight moves status line down
+ call term_sendkeys(buf, ":set cmdheight-=2\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_4', {})
+
+ " reducing window size and then setting cmdheight
+ call term_sendkeys(buf, ":resize -1\<CR>")
+ call term_sendkeys(buf, ":set cmdheight=1\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_5', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_cmdheight')
+endfunc
+
func Test_map_completion()
if !has('cmdline_compl')
return
@@ -912,12 +946,26 @@ func Test_cmdline_complete_various()
call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:)
+ " completion of autocmd group after comma
+ call feedkeys(":doautocmd BufNew,BufEn\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"doautocmd BufNew,BufEnter", @:)
+
+ " completion of file name in :doautocmd
+ call writefile([], 'Xfile1')
+ call writefile([], 'Xfile2')
+ call feedkeys(":doautocmd BufEnter Xfi\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"doautocmd BufEnter Xfile1 Xfile2", @:)
+ call delete('Xfile1')
+ call delete('Xfile2')
+
" completion for the :augroup command
- augroup XTest
+ augroup XTest.test
augroup END
call feedkeys(":augroup X\<C-A>\<C-B>\"\<CR>", 'xt')
- call assert_equal("\"augroup XTest", @:)
- augroup! XTest
+ call assert_equal("\"augroup XTest.test", @:)
+ call feedkeys(":au X\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"au XTest.test", @:)
+ augroup! XTest.test
" completion for the :unlet command
call feedkeys(":unlet one two\<C-A>\<C-B>\"\<CR>", 'xt')
@@ -1392,14 +1440,6 @@ func Test_cmdwin_jump_to_win()
call assert_equal(1, winnr('$'))
endfunc
-" Test for backtick expression in the command line
-func Test_cmd_backtick()
- %argd
- argadd `=['a', 'b', 'c']`
- call assert_equal(['a', 'b', 'c'], argv())
- %argd
-endfunc
-
func Test_cmdwin_tabpage()
tabedit
" v8.2.1919 isn't ported yet, so E492 is thrown after E11 here.
@@ -1412,11 +1452,22 @@ func Test_cmdwin_tabpage()
tabclose!
endfunc
+" Test for backtick expression in the command line
+func Test_cmd_backtick()
+ CheckNotMSWindows " FIXME: see #19297
+ %argd
+ argadd `=['a', 'b', 'c']`
+ call assert_equal(['a', 'b', 'c'], argv())
+ %argd
+
+ argadd `echo abc def`
+ call assert_equal(['abc def'], argv())
+ %argd
+endfunc
+
" Test for the :! command
func Test_cmd_bang()
- if !has('unix')
- return
- endif
+ CheckUnix
let lines =<< trim [SCRIPT]
" Test for no previous command
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 1dbbe578c5..ea453b7174 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -137,7 +137,7 @@ func Common_vert_split()
" Test diffoff
diffoff!
- 1wincmd 2
+ 1wincmd w
let &diff = 1
let &fdm = diff_fdm
let &fdc = diff_fdc
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index a09346a595..e26bbdc5be 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -262,22 +262,6 @@ func Test_edit_09()
bw!
endfunc
-func Test_edit_10()
- " Test for starting selectmode
- new
- set selectmode=key keymodel=startsel
- call setline(1, ['abc', 'def', 'ghi'])
- call cursor(1, 4)
- call feedkeys("A\<s-home>start\<esc>", 'txin')
- call assert_equal(['startdef', 'ghi'], getline(1, '$'))
- " start select mode again with gv
- set selectmode=cmd
- call feedkeys('gvabc', 'xt')
- call assert_equal('abctdef', getline(1))
- set selectmode= keymodel=
- bw!
-endfunc
-
func Test_edit_11()
" Test that indenting kicks in
new
@@ -1620,6 +1604,7 @@ func Test_edit_InsertLeave_undo()
bwipe!
au! InsertLeave
call delete('XtestUndo')
+ call delete(undofile('XtestUndo'))
set undofile&
endfunc
@@ -1687,11 +1672,11 @@ func Test_edit_noesckeys()
endfunc
" Test for running an invalid ex command in insert mode using CTRL-O
-" Note that vim has a hard-coded sleep of 3 seconds. So this test will take
-" more than 3 seconds to complete.
func Test_edit_ctrl_o_invalid_cmd()
new
set showmode showcmd
+ " Avoid a sleep of 3 seconds. Zero might have side effects.
+ " call test_override('ui_delay', 50)
let caught_e492 = 0
try
call feedkeys("i\<C-O>:invalid\<CR>abc\<Esc>", "xt")
@@ -1701,6 +1686,18 @@ func Test_edit_ctrl_o_invalid_cmd()
call assert_equal(1, caught_e492)
call assert_equal('abc', getline(1))
set showmode& showcmd&
+ " call test_override('ui_delay', 0)
+ close!
+endfunc
+
+" Test for editing a file with a very long name
+func Test_edit_illegal_filename()
+ CheckEnglish
+ new
+ redir => msg
+ exe 'edit ' . repeat('f', 5000)
+ redir END
+ call assert_match("Illegal file name$", split(msg, "\n")[0])
close!
endfunc
@@ -1763,6 +1760,102 @@ func Test_edit_is_a_directory()
call delete(dirname, 'rf')
endfunc
+" Test for editing a file using invalid file encoding
+func Test_edit_invalid_encoding()
+ CheckEnglish
+ call writefile([], 'Xfile')
+ redir => msg
+ new ++enc=axbyc Xfile
+ redir END
+ call assert_match('\[NOT converted\]', msg)
+ call delete('Xfile')
+ close!
+endfunc
+
+" Test for the "charconvert" option
+func Test_edit_charconvert()
+ CheckEnglish
+ call writefile(['one', 'two'], 'Xfile')
+
+ " set 'charconvert' to a non-existing function
+ set charconvert=NonExitingFunc()
+ new
+ let caught_e117 = v:false
+ try
+ redir => msg
+ edit ++enc=axbyc Xfile
+ catch /E117:/
+ let caught_e117 = v:true
+ finally
+ redir END
+ endtry
+ call assert_true(caught_e117)
+ call assert_equal(['one', 'two'], getline(1, '$'))
+ call assert_match("Conversion with 'charconvert' failed", msg)
+ close!
+ set charconvert&
+
+ " 'charconvert' function doesn't create a output file
+ func Cconv1()
+ endfunc
+ set charconvert=Cconv1()
+ new
+ redir => msg
+ edit ++enc=axbyc Xfile
+ redir END
+ call assert_equal(['one', 'two'], getline(1, '$'))
+ call assert_match("can't read output of 'charconvert'", msg)
+ close!
+ delfunc Cconv1
+ set charconvert&
+
+ " 'charconvert' function to convert to upper case
+ func Cconv2()
+ let data = readfile(v:fname_in)
+ call map(data, 'toupper(v:val)')
+ call writefile(data, v:fname_out)
+ endfunc
+ set charconvert=Cconv2()
+ new Xfile
+ write ++enc=ucase Xfile1
+ call assert_equal(['ONE', 'TWO'], readfile('Xfile1'))
+ call delete('Xfile1')
+ close!
+ delfunc Cconv2
+ set charconvert&
+
+ " 'charconvert' function removes the input file
+ func Cconv3()
+ call delete(v:fname_in)
+ endfunc
+ set charconvert=Cconv3()
+ new
+ call assert_fails('edit ++enc=lcase Xfile', 'E202:')
+ call assert_equal([''], getline(1, '$'))
+ close!
+ delfunc Cconv3
+ set charconvert&
+
+ call delete('Xfile')
+endfunc
+
+" Test for editing a file without read permission
+func Test_edit_file_no_read_perm()
+ CheckUnix
+ CheckNotBSD
+ call writefile(['one', 'two'], 'Xfile')
+ call setfperm('Xfile', '-w-------')
+ new
+ redir => msg
+ edit Xfile
+ redir END
+ call assert_equal(1, &readonly)
+ call assert_equal([''], getline(1, '$'))
+ call assert_match('\[Permission Denied\]', msg)
+ close!
+ call delete('Xfile')
+endfunc
+
" Using :edit without leaving 'insertmode' should not cause Insert mode to be
" re-entered immediately after <C-L>
func Test_edit_insertmode_ex_edit()
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 811c6c946d..eff1376d3c 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -75,6 +75,18 @@ func Test_for_invalid()
redraw
endfunc
+func Test_for_over_null_string()
+ let save_enc = &enc
+ " set enc=iso8859
+ let cnt = 0
+ for c in v:_null_string
+ let cnt += 1
+ endfor
+ call assert_equal(0, cnt)
+
+ let &enc = save_enc
+endfunc
+
func Test_readfile_binary()
new
call setline(1, ['one', 'two', 'three'])
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index fc0f7619c4..df01d84f19 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -41,7 +41,7 @@ func Test_expand_sfile_and_stack()
call assert_match('test_expand_func\.vim$', s:sfile)
let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack'
call assert_match(expected .. '$', expand('<sfile>'))
- call assert_match(expected .. '\[4\]' , expand('<stack>'))
+ call assert_match(expected .. '\[4\]$' , expand('<stack>'))
" Call in script-local function
call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
@@ -59,7 +59,32 @@ func Test_expand_sfile_and_stack()
call writefile(lines, 'Xstack')
source Xstack
call assert_match('\<Xstack\[2\]$', g:stack_value)
+ unlet g:stack_value
call delete('Xstack')
+
+ if exists('+shellslash')
+ call mkdir('Xshellslash')
+ let lines =<< trim END
+ let g:stack1 = expand('<stack>')
+ set noshellslash
+ let g:stack2 = expand('<stack>')
+ set shellslash
+ let g:stack3 = expand('<stack>')
+ END
+ call writefile(lines, 'Xshellslash/Xstack')
+ " Test that changing 'shellslash' always affects the result of expand()
+ " when sourcing a script multiple times.
+ for i in range(2)
+ source Xshellslash/Xstack
+ call assert_match('\<Xshellslash/Xstack\[1\]$', g:stack1)
+ call assert_match('\<Xshellslash\\Xstack\[3\]$', g:stack2)
+ call assert_match('\<Xshellslash/Xstack\[5\]$', g:stack3)
+ unlet g:stack1
+ unlet g:stack2
+ unlet g:stack3
+ endfor
+ call delete('Xshellslash', 'rf')
+ endif
endfunc
func Test_expand_slnum()
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
index c6e781a1ef..b77f02afd1 100644
--- a/src/nvim/testdir/test_filechanged.vim
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -242,6 +242,15 @@ func Test_file_changed_dialog()
call assert_equal(1, line('$'))
call assert_equal('new line', getline(1))
+ " File created after starting to edit it
+ call delete('Xchanged_d')
+ new Xchanged_d
+ call writefile(['one'], 'Xchanged_d')
+ call feedkeys('L', 'L')
+ checktime Xchanged_d
+ call assert_equal(['one'], getline(1, '$'))
+ close!
+
bwipe!
call delete('Xchanged_d')
endfunc
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index feae44e5ee..b2bb189688 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -191,6 +191,22 @@ func Test_gf_error()
au! InsertCharPre
bwipe!
+
+ " gf is not allowed when buffer is locked
+ new
+ augroup Test_gf
+ au!
+ au OptionSet diff norm! gf
+ augroup END
+ call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])
+ " Nvim does not support test_override()
+ " call test_override('starting', 1)
+ " call assert_fails('diffthis', 'E788:')
+ " call test_override('starting', 0)
+ augroup Test_gf
+ au!
+ augroup END
+ bw!
endfunc
" If a file is not found by 'gf', then 'includeexpr' should be used to locate
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index 947f7efc7c..cb6851250c 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -9,7 +9,10 @@ func Test_yank_put_clipboard()
set clipboard=unnamed
g/^/normal yyp
call assert_equal(['a', 'a', 'b', 'b', 'c', 'c'], getline(1, 6))
-
+ set clipboard=unnamed,unnamedplus
+ call setline(1, ['a', 'b', 'c'])
+ g/^/normal yyp
+ call assert_equal(['a', 'a', 'b', 'b', 'c', 'c'], getline(1, 6))
set clipboard&
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index 49095400ef..6d029ffda2 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -122,6 +122,24 @@ func Test_gd()
call XTest_goto_decl('gd', lines, 3, 14)
endfunc
+" Using gd to jump to a declaration in a fold
+func Test_gd_with_fold()
+ new
+ let lines =<< trim END
+ #define ONE 1
+ #define TWO 2
+ #define THREE 3
+
+ TWO
+ END
+ call setline(1, lines)
+ 1,3fold
+ call feedkeys('Ggd', 'xt')
+ call assert_equal(2, line('.'))
+ call assert_equal(-1, foldclosedend(2))
+ bw!
+endfunc
+
func Test_gd_not_local()
let lines =<< trim [CODE]
int func1(void)
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index aa66d86af1..2f4e1db4a1 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -649,6 +649,8 @@ func Test_reduce()
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
+ call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:')
+ call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E39:')
let g:lut = [1, 2, 3, 4]
func EvilRemove()
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index a02d23b409..3a607ff533 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -95,6 +95,65 @@ func Test_echoerr()
call test_ignore_error('RESET')
endfunc
+func Test_mode_message_at_leaving_insert_by_ctrl_c()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+
+ " Set custom statusline built by user-defined function.
+ let testfile = 'Xtest.vim'
+ call writefile([
+ \ 'func StatusLine() abort',
+ \ ' return ""',
+ \ 'endfunc',
+ \ 'set statusline=%!StatusLine()',
+ \ 'set laststatus=2',
+ \ ], testfile)
+
+ let rows = 10
+ let buf = term_start([GetVimProg(), '--clean', '-S', testfile], {'term_rows': rows})
+ call term_wait(buf, 200)
+ call assert_equal('run', job_status(term_getjob(buf)))
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, rows))})
+ call term_sendkeys(buf, "\<C-C>")
+ call WaitForAssert({-> assert_match('^\s*$', term_getline(buf, rows))})
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe!'
+ call delete(testfile)
+endfunc
+
+func Test_mode_message_at_leaving_insert_with_esc_mapped()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+
+ " Set custom statusline built by user-defined function.
+ let testfile = 'Xtest.vim'
+ call writefile([
+ \ 'set laststatus=2',
+ \ 'inoremap <Esc> <Esc>00',
+ \ ], testfile)
+
+ let rows = 10
+ let buf = term_start([GetVimProg(), '--clean', '-S', testfile], {'term_rows': rows})
+ call term_wait(buf, 200)
+ call assert_equal('run', job_status(term_getjob(buf)))
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, rows))})
+ call term_sendkeys(buf, "\<Esc>")
+ call WaitForAssert({-> assert_match('^\s*$', term_getline(buf, rows))})
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe!'
+ call delete(testfile)
+endfunc
+
func Test_echospace()
set noruler noshowcmd laststatus=1
call assert_equal(&columns - 1, v:echospace)
@@ -317,6 +376,7 @@ func Test_fileinfo_after_echo()
endfunc
func Test_cmdheight_zero()
+ enew
set cmdheight=0
set showcmd
redraw!
@@ -366,10 +426,13 @@ func Test_cmdheight_zero()
7
call feedkeys(":\"\<C-R>=line('w0')\<CR>\<CR>", "xt")
call assert_equal('"1', @:)
- bwipe!
+ bwipe!
+ bwipe!
set cmdheight&
set showcmd&
+ tabnew
+ tabonly
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 7cb70aa2af..347404a579 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3,6 +3,7 @@
source shared.vim
source check.vim
source view_util.vim
+source screendump.vim
func Setup_NewWindow()
10new
@@ -123,31 +124,6 @@ func Test_normal01_keymodel()
bw!
endfunc
-" Test for select mode
-func Test_normal02_selectmode()
- call Setup_NewWindow()
- 50
- norm! gHy
- call assert_equal('y51', getline('.'))
- call setline(1, range(1,100))
- 50
- exe ":norm! V9jo\<c-g>y"
- call assert_equal('y60', getline('.'))
- " clean up
- bw!
-endfunc
-
-func Test_normal02_selectmode2()
- " some basic select mode tests
- call Setup_NewWindow()
- 50
- " call feedkeys(":set im\n\<c-o>gHc\<c-o>:set noim\n", 'tx')
- call feedkeys("i\<c-o>gHc\<esc>", 'tx')
- call assert_equal('c51', getline('.'))
- " clean up
- bw!
-endfunc
-
func Test_normal03_join()
" basic join test
call Setup_NewWindow()
@@ -491,6 +467,18 @@ func Test_normal11_showcmd()
call assert_equal(3, line('$'))
exe "norm! 0d3\<del>2l"
call assert_equal('obar2foobar3', getline('.'))
+ " test for the visual block size displayed in the status line
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc'])
+ call feedkeys("ggl\<C-V>lljj", 'xt')
+ redraw!
+ call assert_match('3x3$', Screenline(&lines))
+ call feedkeys("\<C-V>", 'xt')
+ " test for visually selecting a multi-byte character
+ call setline(1, ["\U2206"])
+ call feedkeys("ggv", 'xt')
+ redraw!
+ call assert_match('1-3$', Screenline(&lines))
+ call feedkeys("v", 'xt')
bw!
endfunc
@@ -654,6 +642,19 @@ func Test_normal15_z_scroll_vert()
call assert_equal(21, winsaveview()['topline'])
call assert_equal([0, 21, 2, 0, 9], getcurpos())
+ " Test for z+ with [count] greater than buffer size
+ 1
+ norm! 1000z+
+ call assert_equal(' 100', getline('.'))
+ call assert_equal(100, winsaveview()['topline'])
+ call assert_equal([0, 100, 2, 0, 9], getcurpos())
+
+ " Test for z+ from the last buffer line
+ norm! Gz.z+
+ call assert_equal(' 100', getline('.'))
+ call assert_equal(100, winsaveview()['topline'])
+ call assert_equal([0, 100, 2, 0, 9], getcurpos())
+
" Test for z^
norm! 22z+0
norm! z^
@@ -661,6 +662,12 @@ func Test_normal15_z_scroll_vert()
call assert_equal(12, winsaveview()['topline'])
call assert_equal([0, 21, 2, 0, 9], getcurpos())
+ " Test for z^ from first buffer line
+ norm! ggz^
+ call assert_equal('1', getline('.'))
+ call assert_equal(1, winsaveview()['topline'])
+ call assert_equal([0, 1, 1, 0, 1], getcurpos())
+
" Test for [count]z^
1
norm! 30z^
@@ -740,6 +747,19 @@ func Test_normal16_z_scroll_hor()
norm! yl
call assert_equal('z', @0)
+ " Test for zs and ze with folds
+ %fold
+ norm! $zs
+ call assert_equal(26, col('.'))
+ call assert_equal(0, winsaveview()['leftcol'])
+ norm! yl
+ call assert_equal('z', @0)
+ norm! ze
+ call assert_equal(26, col('.'))
+ call assert_equal(0, winsaveview()['leftcol'])
+ norm! yl
+ call assert_equal('z', @0)
+
" cleanup
set wrap listchars=eol:$
bw!
@@ -833,6 +853,19 @@ func Test_vert_scroll_cmds()
normal! 4H
call assert_equal(33, line('.'))
+ " Test for using a large count value
+ %d
+ call setline(1, range(1, 4))
+ norm! 6H
+ call assert_equal(4, line('.'))
+
+ " Test for 'M' with folded lines
+ %d
+ call setline(1, range(1, 20))
+ 1,5fold
+ norm! LM
+ call assert_equal(12, line('.'))
+
" Test for the CTRL-E and CTRL-Y commands with folds
%d
call setline(1, range(1, 10))
@@ -851,6 +884,18 @@ func Test_vert_scroll_cmds()
exe "normal \<C-Y>\<C-Y>"
call assert_equal(h + 1, line('w$'))
+ " Test for CTRL-Y from the first line and CTRL-E from the last line
+ %d
+ set scrolloff=2
+ call setline(1, range(1, 4))
+ exe "normal gg\<C-Y>"
+ call assert_equal(1, line('w0'))
+ call assert_equal(1, line('.'))
+ exe "normal G4\<C-E>\<C-E>"
+ call assert_equal(4, line('w$'))
+ call assert_equal(4, line('.'))
+ set scrolloff&
+
" Using <PageUp> and <PageDown> in an empty buffer should beep
%d
call assert_beeps('exe "normal \<PageUp>"')
@@ -899,6 +944,18 @@ func Test_vert_scroll_cmds()
exe "normal \<C-D>"
call assert_equal(50, line('w0'))
+ " Test for <S-CR>. Page down.
+ %d
+ call setline(1, range(1, 100))
+ call feedkeys("\<S-CR>", 'xt')
+ call assert_equal(14, line('w0'))
+ call assert_equal(28, line('w$'))
+
+ " Test for <S-->. Page up.
+ call feedkeys("\<S-->", 'xt')
+ call assert_equal(1, line('w0'))
+ call assert_equal(15, line('w$'))
+
set foldenable&
close!
endfunc
@@ -1213,6 +1270,13 @@ func Test_normal18_z_fold()
norm! j
call assert_equal('55', getline('.'))
+ " Test for zm with a count
+ 50
+ set foldlevel=2
+ norm! 3zm
+ call assert_equal(0, &foldlevel)
+ call assert_equal(49, foldclosed(line('.')))
+
" Test for zM
48
set nofoldenable foldlevel=99
@@ -1420,6 +1484,15 @@ func Test_normal23_K()
set iskeyword-=%
set iskeyword-=\|
+ " Currently doesn't work in Nvim, see #19436
+ " Test for specifying a count to K
+ " 1
+ " com! -nargs=* Kprog let g:Kprog_Args = <q-args>
+ " set keywordprg=:Kprog
+ " norm! 3K
+ " call assert_equal('3 version8', g:Kprog_Args)
+ " delcom Kprog
+
" Only expect "man" to work on Unix
if !has("unix") || has('nvim') " Nvim K uses :terminal. #15398
let &keywordprg = k
@@ -1867,7 +1940,31 @@ func Test_normal29_brace()
bw!
endfunc
-" Test for ~ command
+" Test for section movements
+func Test_normal_section()
+ new
+ let lines =<< trim [END]
+ int foo()
+ {
+ if (1)
+ {
+ a = 1;
+ }
+ }
+ [END]
+ call setline(1, lines)
+
+ " jumping to a folded line using [[ should open the fold
+ 2,3fold
+ call cursor(5, 1)
+ call feedkeys("[[", 'xt')
+ call assert_equal(2, line('.'))
+ call assert_equal(-1, foldclosedend(line('.')))
+
+ close!
+endfunc
+
+" Test for changing case using u, U, gu, gU and ~ (tilde) commands
func Test_normal30_changecase()
new
call append(0, 'This is a simple test: äüöß')
@@ -1887,6 +1984,9 @@ func Test_normal30_changecase()
call assert_equal('this is a SIMPLE TEST: ÄÜÖSS', getline('.'))
norm! V~
call assert_equal('THIS IS A simple test: äüöss', getline('.'))
+ call assert_beeps('norm! c~')
+ %d
+ call assert_beeps('norm! ~')
" Test for changing case across lines using 'whichwrap'
call setline(1, ['aaaaaa', 'aaaaaa'])
@@ -2038,9 +2138,9 @@ func Test_normal33_g_cmd2()
call assert_equal(2, line('.'))
call assert_fails(':norm! g;', 'E662')
call assert_fails(':norm! g,', 'E663')
- let &ul=&ul
+ let &ul = &ul
call append('$', ['a', 'b', 'c', 'd'])
- let &ul=&ul
+ let &ul = &ul
call append('$', ['Z', 'Y', 'X', 'W'])
let a = execute(':changes')
call assert_match('2\s\+0\s\+2', a)
@@ -2889,6 +2989,20 @@ func Test_message_when_using_ctrl_c()
bwipe!
endfunc
+func Test_mode_updated_after_ctrl_c()
+ CheckScreendump
+
+ let buf = RunVimInTerminal('', {'rows': 5})
+ call term_sendkeys(buf, "i")
+ call term_sendkeys(buf, "\<C-O>")
+ " wait a moment so that the "-- (insert) --" message is displayed
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "\<C-C>")
+ call VerifyScreenDump(buf, 'Test_mode_updated_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test for '[m', ']m', '[M' and ']M'
" Jumping to beginning and end of methods in Java-like languages
func Test_java_motion()
@@ -2897,25 +3011,26 @@ func Test_java_motion()
call assert_beeps('normal! ]m')
call assert_beeps('normal! [M')
call assert_beeps('normal! ]M')
- a
-Piece of Java
-{
- tt m1 {
- t1;
- } e1
-
- tt m2 {
- t2;
- } e2
-
- tt m3 {
- if (x)
- {
- t3;
- }
- } e3
-}
-.
+ let lines =<< trim [CODE]
+ Piece of Java
+ {
+ tt m1 {
+ t1;
+ } e1
+
+ tt m2 {
+ t2;
+ } e2
+
+ tt m3 {
+ if (x)
+ {
+ t3;
+ }
+ } e3
+ }
+ [CODE]
+ call setline(1, lines)
normal gg
@@ -2968,14 +3083,21 @@ Piece of Java
call assert_equal("{LF", getline('.'))
call assert_equal([2, 2, 2], [line('.'), col('.'), virtcol('.')])
+ call cursor(2, 1)
+ call assert_beeps('norm! 5]m')
+
+ " jumping to a method in a fold should open the fold
+ 6,10fold
+ call feedkeys("gg3]m", 'xt')
+ call assert_equal([7, 8, 15], [line('.'), col('.'), virtcol('.')])
+ call assert_equal(-1, foldclosedend(7))
+
close!
endfunc
+" Tests for g cmds
func Test_normal_gdollar_cmd()
- if !has("jumplist")
- return
- endif
- " Tests for g cmds
+ CheckFeature jumplist
call Setup_NewWindow()
" Make long lines that will wrap
%s/$/\=repeat(' foobar', 10)/
@@ -3183,6 +3305,27 @@ func Test_normal_colon_op()
close!
endfunc
+" Test for deleting or changing characters across lines with 'whichwrap'
+" containing 's'. Should count <EOL> as one character.
+func Test_normal_op_across_lines()
+ new
+ set whichwrap&
+ call setline(1, ['one two', 'three four'])
+ exe "norm! $3d\<Space>"
+ call assert_equal(['one twhree four'], getline(1, '$'))
+
+ call setline(1, ['one two', 'three four'])
+ exe "norm! $3c\<Space>x"
+ call assert_equal(['one twxhree four'], getline(1, '$'))
+
+ set whichwrap+=l
+ call setline(1, ['one two', 'three four'])
+ exe "norm! $3x"
+ call assert_equal(['one twhree four'], getline(1, '$'))
+ close!
+ set whichwrap&
+endfunc
+
" Test for 'w' and 'b' commands
func Test_normal_word_move()
new
@@ -3256,6 +3399,19 @@ func Test_normal_vert_scroll_longline()
close!
endfunc
+" Test for jumping in a file using %
+func Test_normal_percent_jump()
+ new
+ call setline(1, range(1, 100))
+
+ " jumping to a folded line should open the fold
+ 25,75fold
+ call feedkeys('50%', 'xt')
+ call assert_equal(50, line('.'))
+ call assert_equal(-1, foldclosedend(50))
+ close!
+endfunc
+
" Some commands like yy, cc, dd, >>, << and !! accept a count after
" typing the first letter of the command.
func Test_normal_count_after_operator()
diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim
index 191cd948ac..14b9724d67 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -522,8 +522,8 @@ endfunc
func Test_search_with_end_offset()
new
call setline(1, ['', 'dog(a', 'cat('])
- exe "normal /(/e+" .. "\<CR>"
- normal "ayn
+ exe "normal /(/e+\<CR>"
+ normal n"ayn
call assert_equal("a\ncat(", @a)
close!
endfunc
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 52e745438d..11dd3badb6 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -279,7 +279,12 @@ func Test_get_register()
call feedkeys(":\<C-R>r\<Esc>", 'xt')
call assert_equal("a\rb", histget(':', -1)) " Modified because of #6137
+ call assert_fails('let r = getreg("=", [])', 'E745:')
+ call assert_fails('let r = getreg("=", 1, [])', 'E745:')
enew!
+
+ " Using a register in operator-pending mode should fail
+ call assert_beeps('norm! c"')
endfunc
func Test_set_register()
@@ -426,6 +431,12 @@ func Test_execute_register()
@
call assert_equal(3, i)
+ " try to execute expression register and use a backspace to cancel it
+ new
+ call feedkeys("@=\<BS>ax\<CR>y", 'xt')
+ call assert_equal(['x', 'y'], getline(1, '$'))
+ close!
+
" cannot execute a register in operator pending mode
call assert_beeps('normal! c@r')
endfunc
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
index b483841060..f2cab45450 100644
--- a/src/nvim/testdir/test_selectmode.vim
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -2,6 +2,156 @@
source shared.vim
+" Test for select mode
+func Test_selectmode_basic()
+ new
+ call setline(1, range(1,100))
+ 50
+ norm! gHy
+ call assert_equal('y51', getline('.'))
+ call setline(1, range(1,100))
+ 50
+ exe ":norm! V9jo\<c-g>y"
+ call assert_equal('y60', getline('.'))
+ call setline(1, range(1,100))
+ 50
+ " call feedkeys(":set im\n\<c-o>gHc\<c-o>:set noim\n", 'tx')
+ call feedkeys("i\<c-o>gHc\<esc>", 'tx')
+ call assert_equal('c51', getline('.'))
+ " clean up
+ bw!
+endfunc
+
+" Test for starting selectmode
+func Test_selectmode_start()
+ new
+ set selectmode=key keymodel=startsel
+ call setline(1, ['abc', 'def', 'ghi'])
+ call cursor(1, 4)
+ call feedkeys("A\<s-home>start\<esc>", 'txin')
+ call assert_equal(['startdef', 'ghi'], getline(1, '$'))
+ " start select mode again with gv
+ set selectmode=cmd
+ call feedkeys('gvabc', 'xt')
+ call assert_equal('abctdef', getline(1))
+ set selectmode= keymodel=
+ bw!
+endfunc
+
+" Test for characterwise select mode
+func Test_characterwise_select_mode()
+ new
+
+ " Select mode maps
+ snoremap <lt>End> <End>
+ snoremap <lt>Down> <Down>
+ snoremap <lt>Del> <Del>
+
+ " characterwise select mode: delete middle line
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Gkkgh\<End>\<Del>"
+ call assert_equal(['', 'b', 'c'], getline(1, '$'))
+
+ " characterwise select mode: delete middle two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Gkkgh\<Down>\<End>\<Del>"
+ call assert_equal(['', 'c'], getline(1, '$'))
+
+ " characterwise select mode: delete last line
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Ggh\<End>\<Del>"
+ call assert_equal(['', 'a', 'b', ''], getline(1, '$'))
+
+ " characterwise select mode: delete last two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Gkgh\<Down>\<End>\<Del>"
+ call assert_equal(['', 'a', ''], getline(1, '$'))
+
+ " CTRL-H in select mode behaves like 'x'
+ call setline(1, 'abcdef')
+ exe "normal! gggh\<Right>\<Right>\<Right>\<C-H>"
+ call assert_equal('ef', getline(1))
+
+ " CTRL-O in select mode switches to visual mode for one command
+ call setline(1, 'abcdef')
+ exe "normal! gggh\<C-O>3lm"
+ call assert_equal('mef', getline(1))
+
+ sunmap <lt>End>
+ sunmap <lt>Down>
+ sunmap <lt>Del>
+ bwipe!
+endfunc
+
+" Test for linewise select mode
+func Test_linewise_select_mode()
+ new
+
+ " linewise select mode: delete middle line
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GkkgH\<Del>"
+ call assert_equal(['', 'b', 'c'], getline(1, '$'))
+
+ " linewise select mode: delete middle two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GkkgH\<Down>\<Del>"
+ call assert_equal(['', 'c'], getline(1, '$'))
+
+ " linewise select mode: delete last line
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GgH\<Del>"
+ call assert_equal(['', 'a', 'b'], getline(1, '$'))
+
+ " linewise select mode: delete last two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GkgH\<Down>\<Del>"
+ call assert_equal(['', 'a'], getline(1, '$'))
+
+ bwipe!
+endfunc
+
+" Test for blockwise select mode (g CTRL-H)
+func Test_blockwise_select_mode()
+ new
+ call setline(1, ['foo', 'bar'])
+ call feedkeys("g\<BS>\<Right>\<Down>mm", 'xt')
+ call assert_equal(['mmo', 'mmr'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for using visual mode maps in select mode
+func Test_select_mode_map()
+ new
+ vmap <buffer> <F2> 3l
+ call setline(1, 'Test line')
+ call feedkeys("gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+
+ vmap <buffer> <F2> ygV
+ call feedkeys("0gh\<Right>\<Right>\<F2>cwabc", 'xt')
+ call assert_equal('abc line', getline(1))
+
+ vmap <buffer> <F2> :<C-U>let v=100<CR>
+ call feedkeys("gggh\<Right>\<Right>\<F2>foo", 'xt')
+ call assert_equal('foo line', getline(1))
+
+ " reselect the select mode using gv from a visual mode map
+ vmap <buffer> <F2> gv
+ set selectmode=cmd
+ call feedkeys("0gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+ set selectmode&
+
+ close!
+endfunc
+
" Test for selecting a register with CTRL-R
func Test_selectmode_register()
new
diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim
index b028e9d969..dbffbafed9 100644
--- a/src/nvim/testdir/test_spellfile.vim
+++ b/src/nvim/testdir/test_spellfile.vim
@@ -25,6 +25,18 @@ func Test_spell_normal()
let cnt=readfile('./Xspellfile.add')
call assert_equal('goood', cnt[0])
+ " zg should fail in operator-pending mode
+ call assert_beeps('norm! czg')
+
+ " zg fails in visual mode when not able to get the visual text
+ call assert_beeps('norm! ggVjzg')
+ norm! V
+
+ " zg fails for a non-identifier word
+ call append(line('$'), '###')
+ call assert_fails('norm! Gzg', 'E349:')
+ $d
+
" Test for zw
2
norm! $zw
@@ -907,6 +919,33 @@ func Test_spellfile_COMMON()
call delete('XtestCOMMON-utf8.spl')
endfunc
+" Test NOSUGGEST (see :help spell-COMMON)
+func Test_spellfile_NOSUGGEST()
+ call writefile(['2', 'foo/X', 'fog'], 'XtestNOSUGGEST.dic')
+ call writefile(['NOSUGGEST X'], 'XtestNOSUGGEST.aff')
+
+ mkspell! XtestNOSUGGEST-utf8.spl XtestNOSUGGEST
+ set spell spelllang=XtestNOSUGGEST-utf8.spl
+
+ for goodword in ['foo', 'Foo', 'FOO', 'fog', 'Fog', 'FOG']
+ call assert_equal(['', ''], spellbadword(goodword), goodword)
+ endfor
+ for badword in ['foO', 'fOO', 'fooo', 'foog', 'foofog', 'fogfoo']
+ call assert_equal([badword, 'bad'], spellbadword(badword))
+ endfor
+
+ call assert_equal(['fog'], spellsuggest('fooo', 1))
+ call assert_equal(['fog'], spellsuggest('fOo', 1))
+ call assert_equal(['fog'], spellsuggest('foG', 1))
+ call assert_equal(['fog'], spellsuggest('fogg', 1))
+
+ set spell& spelllang&
+ call delete('XtestNOSUGGEST.dic')
+ call delete('XtestNOSUGGEST.aff')
+ call delete('XtestNOSUGGEST-utf8.spl')
+endfunc
+
+
" Test CIRCUMFIX (see: :help spell-CIRCUMFIX)
func Test_spellfile_CIRCUMFIX()
" Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index f795d1c0cf..b3a80072d9 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -858,6 +858,44 @@ func Test_substitute_skipped_range()
bwipe!
endfunc
+" Test using the 'gdefault' option (when on, flag 'g' is default on).
+func Test_substitute_gdefault()
+ new
+
+ " First check without 'gdefault'
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/
+ call assert_equal('FOO bar foo', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/g
+ call assert_equal('FOO bar FOO', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/gg
+ call assert_equal('FOO bar foo', getline(1))
+
+ " Then check with 'gdefault'
+ set gdefault
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/
+ call assert_equal('FOO bar FOO', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/g
+ call assert_equal('FOO bar foo', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/gg
+ call assert_equal('FOO bar FOO', getline(1))
+
+ " Setting 'compatible' should reset 'gdefault'
+ call assert_equal(1, &gdefault)
+ " set compatible
+ set nogdefault
+ call assert_equal(0, &gdefault)
+ set nocompatible
+ call assert_equal(0, &gdefault)
+
+ bw!
+endfunc
+
" This was using "old_sub" after it was freed.
func Test_using_old_sub()
" set compatible maxfuncdepth=10
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 07f35ddb4f..6d468ec9de 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -670,15 +670,19 @@ func Test_tabline_tabmenu()
call assert_equal(3, tabpagenr('$'))
" go to tab page 2 in operator-pending mode (should beep)
- call assert_beeps('call feedkeys("f" .. TabLineSelectPageCode(2), "Lx!")')
+ call assert_beeps('call feedkeys("c" .. TabLineSelectPageCode(2), "Lx!")')
+ call assert_equal(2, tabpagenr())
+ call assert_equal(3, tabpagenr('$'))
" open new tab page before tab page 1 in operator-pending mode (should beep)
- call assert_beeps('call feedkeys("f" .. TabMenuNewItemCode(1), "Lx!")')
+ call assert_beeps('call feedkeys("c" .. TabMenuNewItemCode(1), "Lx!")')
+ call assert_equal(1, tabpagenr())
+ call assert_equal(4, tabpagenr('$'))
" open new tab page after tab page 3 in normal mode
call feedkeys(TabMenuNewItemCode(4), "Lx!")
call assert_equal(4, tabpagenr())
- call assert_equal(4, tabpagenr('$'))
+ call assert_equal(5, tabpagenr('$'))
" go to tab page 2 in insert mode
call feedkeys("i" .. TabLineSelectPageCode(2) .. "\<C-C>", "Lx!")
@@ -686,17 +690,17 @@ func Test_tabline_tabmenu()
" close tab page 2 in insert mode
call feedkeys("i" .. TabMenuCloseItemCode(2) .. "\<C-C>", "Lx!")
- call assert_equal(3, tabpagenr('$'))
+ call assert_equal(4, tabpagenr('$'))
" open new tab page before tab page 3 in insert mode
call feedkeys("i" .. TabMenuNewItemCode(3) .. "\<C-C>", "Lx!")
call assert_equal(3, tabpagenr())
- call assert_equal(4, tabpagenr('$'))
+ call assert_equal(5, tabpagenr('$'))
" open new tab page after tab page 4 in insert mode
call feedkeys("i" .. TabMenuNewItemCode(5) .. "\<C-C>", "Lx!")
call assert_equal(5, tabpagenr())
- call assert_equal(5, tabpagenr('$'))
+ call assert_equal(6, tabpagenr('$'))
%bw!
endfunc
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 1dd656ece5..04c0218f74 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -1133,7 +1133,7 @@ endfunc
" Test for [i, ]i, [I, ]I, [ CTRL-I, ] CTRL-I and CTRL-W i commands
func Test_inc_search()
new
- call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo'])
+ call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo', '==='])
call cursor(3, 1)
" Test for [i and ]i
@@ -1143,6 +1143,9 @@ func Test_inc_search()
call assert_equal('3:foo', execute('normal ]i'))
call assert_equal('4:foo', execute('normal 2]i'))
call assert_fails('normal 3]i', 'E389:')
+ call assert_fails('normal G]i', 'E349:')
+ call assert_fails('normal [i', 'E349:')
+ call cursor(3, 1)
" Test for :isearch
call assert_equal('1:foo', execute('isearch foo'))
@@ -1163,6 +1166,9 @@ func Test_inc_search()
call assert_equal([
\ ' 1: 4 3:foo',
\ ' 2: 5 4:foo'], split(execute('normal ]I'), "\n"))
+ call assert_fails('normal G]I', 'E349:')
+ call assert_fails('normal [I', 'E349:')
+ call cursor(3, 1)
" Test for :ilist
call assert_equal([
@@ -1188,6 +1194,9 @@ func Test_inc_search()
exe "normal k2]\t"
call assert_equal([5, 3], [line('.'), col('.')])
call assert_fails("normal 2k3]\t", 'E389:')
+ call assert_fails("normal G[\t", 'E349:')
+ call assert_fails("normal ]\t", 'E349:')
+ call cursor(3, 1)
" Test for :ijump
call cursor(3, 1)
@@ -1212,6 +1221,8 @@ func Test_inc_search()
close
call assert_fails('3wincmd i', 'E387:')
call assert_fails('6wincmd i', 'E389:')
+ call assert_fails("normal G\<C-W>i", 'E349:')
+ call cursor(3, 1)
" Test for :isplit
isplit foo
diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim
index eeb2946a8b..f21d6fcb99 100644
--- a/src/nvim/testdir/test_textobjects.vim
+++ b/src/nvim/testdir/test_textobjects.vim
@@ -1,7 +1,6 @@
" Test for textobjects
source check.vim
-CheckFeature textobjects
func CpoM(line, useM, expected)
new
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index a9ec405aa4..eb47af08d7 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -336,7 +336,7 @@ func Test_undofile_earlier()
" create undofile with timestamps older than Vim startup time.
let t0 = localtime() - 43200
call test_settime(t0)
- new Xfile
+ new XfileEarlier
call feedkeys("ione\<Esc>", 'xt')
set ul=100
call test_settime(t0 + 1)
@@ -350,12 +350,12 @@ func Test_undofile_earlier()
bwipe!
" restore normal timestamps.
call test_settime(0)
- new Xfile
+ new XfileEarlier
rundo Xundofile
earlier 1d
call assert_equal('', getline(1))
bwipe!
- call delete('Xfile')
+ call delete('XfileEarlier')
call delete('Xundofile')
endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index b6a8fb24a4..9c1ad0c099 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -378,14 +378,17 @@ endfunc
func Test_Visual_paragraph_textobject()
new
- call setline(1, ['First line.',
- \ '',
- \ 'Second line.',
- \ 'Third line.',
- \ 'Fourth line.',
- \ 'Fifth line.',
- \ '',
- \ 'Sixth line.'])
+ let lines =<< trim [END]
+ First line.
+
+ Second line.
+ Third line.
+ Fourth line.
+ Fifth line.
+
+ Sixth line.
+ [END]
+ call setline(1, lines)
" When start and end of visual area are identical, 'ap' or 'ip' select
" the whole paragraph.
@@ -639,6 +642,14 @@ func Test_characterwise_visual_mode()
normal Gkvj$d
call assert_equal(['', 'a', ''], getline(1, '$'))
+ " characterwise visual mode: use a count with the visual mode from the last
+ " line in the buffer
+ %d _
+ call setline(1, ['one', 'two', 'three', 'four'])
+ norm! vj$y
+ norm! G1vy
+ call assert_equal('four', @")
+
" characterwise visual mode: replace a single character line and the eol
%d _
call setline(1, "a")
@@ -653,92 +664,6 @@ func Test_characterwise_visual_mode()
bwipe!
endfunc
-func Test_characterwise_select_mode()
- new
-
- " Select mode maps
- snoremap <lt>End> <End>
- snoremap <lt>Down> <Down>
- snoremap <lt>Del> <Del>
-
- " characterwise select mode: delete middle line
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Gkkgh\<End>\<Del>"
- call assert_equal(['', 'b', 'c'], getline(1, '$'))
-
- " characterwise select mode: delete middle two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Gkkgh\<Down>\<End>\<Del>"
- call assert_equal(['', 'c'], getline(1, '$'))
-
- " characterwise select mode: delete last line
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Ggh\<End>\<Del>"
- call assert_equal(['', 'a', 'b', ''], getline(1, '$'))
-
- " characterwise select mode: delete last two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Gkgh\<Down>\<End>\<Del>"
- call assert_equal(['', 'a', ''], getline(1, '$'))
-
- " CTRL-H in select mode behaves like 'x'
- call setline(1, 'abcdef')
- exe "normal! gggh\<Right>\<Right>\<Right>\<C-H>"
- call assert_equal('ef', getline(1))
-
- " CTRL-O in select mode switches to visual mode for one command
- call setline(1, 'abcdef')
- exe "normal! gggh\<C-O>3lm"
- call assert_equal('mef', getline(1))
-
- sunmap <lt>End>
- sunmap <lt>Down>
- sunmap <lt>Del>
- bwipe!
-endfunc
-
-func Test_linewise_select_mode()
- new
-
- " linewise select mode: delete middle line
- call append('$', ['a', 'b', 'c'])
- exe "normal GkkgH\<Del>"
- call assert_equal(['', 'b', 'c'], getline(1, '$'))
-
- " linewise select mode: delete middle two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal GkkgH\<Down>\<Del>"
- call assert_equal(['', 'c'], getline(1, '$'))
-
- " linewise select mode: delete last line
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal GgH\<Del>"
- call assert_equal(['', 'a', 'b'], getline(1, '$'))
-
- " linewise select mode: delete last two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal GkgH\<Down>\<Del>"
- call assert_equal(['', 'a'], getline(1, '$'))
-
- bwipe!
-endfunc
-
-" Test for blockwise select mode (g CTRL-H)
-func Test_blockwise_select_mode()
- new
- call setline(1, ['foo', 'bar'])
- call feedkeys("g\<BS>\<Right>\<Down>mm", 'xt')
- call assert_equal(['mmo', 'mmr'], getline(1, '$'))
- close!
-endfunc
-
func Test_visual_mode_put()
new
@@ -778,16 +703,16 @@ func Test_visual_mode_put()
bwipe!
endfunc
-func Test_select_mode_gv()
+func Test_gv_with_exclusive_selection()
new
- " gv in exclusive select mode after operation
+ " gv with exclusive selection after an operation
call append('$', ['zzz ', 'äà '])
set selection=exclusive
normal Gkv3lyjv3lpgvcxxx
call assert_equal(['', 'zzz ', 'xxx '], getline(1, '$'))
- " gv in exclusive select mode without operation
+ " gv with exclusive selection without an operation
call deletebufline('', 1, '$')
call append('$', 'zzz ')
set selection=exclusive
@@ -1136,32 +1061,6 @@ func Test_star_register()
close!
endfunc
-" Test for using visual mode maps in select mode
-func Test_select_mode_map()
- new
- vmap <buffer> <F2> 3l
- call setline(1, 'Test line')
- call feedkeys("gh\<F2>map", 'xt')
- call assert_equal('map line', getline(1))
-
- vmap <buffer> <F2> ygV
- call feedkeys("0gh\<Right>\<Right>\<F2>cwabc", 'xt')
- call assert_equal('abc line', getline(1))
-
- vmap <buffer> <F2> :<C-U>let v=100<CR>
- call feedkeys("gggh\<Right>\<Right>\<F2>foo", 'xt')
- call assert_equal('foo line', getline(1))
-
- " reselect the select mode using gv from a visual mode map
- vmap <buffer> <F2> gv
- set selectmode=cmd
- call feedkeys("0gh\<F2>map", 'xt')
- call assert_equal('map line', getline(1))
- set selectmode&
-
- close!
-endfunc
-
" Test for changing text in visual mode with 'exclusive' selection
func Test_exclusive_selection()
new
@@ -1178,15 +1077,38 @@ func Test_exclusive_selection()
close!
endfunc
-" Test for starting visual mode with a count.
-" This test should be run without any previous visual modes. So this should be
-" run as a first test.
-func Test_AAA_start_visual_mode_with_count()
- new
- call setline(1, ['aaaaaaa', 'aaaaaaa', 'aaaaaaa', 'aaaaaaa'])
- normal! gg2Vy
- call assert_equal("aaaaaaa\naaaaaaa\n", @")
- close!
+" Test for starting linewise visual with a count.
+" This test needs to be run without any previous visual mode. Otherwise the
+" count will use the count from the previous visual mode.
+func Test_linewise_visual_with_count()
+ let after =<< trim [CODE]
+ call setline(1, ['one', 'two', 'three', 'four'])
+ norm! 3Vy
+ call assert_equal("one\ntwo\nthree\n", @")
+ call writefile(v:errors, 'Xtestout')
+ qall!
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal([], readfile('Xtestout'))
+ call delete('Xtestout')
+ endif
+endfunc
+
+" Test for starting characterwise visual with a count.
+" This test needs to be run without any previous visual mode. Otherwise the
+" count will use the count from the previous visual mode.
+func Test_characterwise_visual_with_count()
+ let after =<< trim [CODE]
+ call setline(1, ['one two', 'three'])
+ norm! l5vy
+ call assert_equal("ne tw", @")
+ call writefile(v:errors, 'Xtestout')
+ qall!
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal([], readfile('Xtestout'))
+ call delete('Xtestout')
+ endif
endfunc
" Test for visually selecting an inner block (iB)
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index bfbba1f793..a8735bcaf1 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -128,6 +128,25 @@ func Test_nowrite_quit_split()
bwipe Xfile
endfunc
+func Test_writefile_sync_arg()
+ " This doesn't check if fsync() works, only that the argument is accepted.
+ call writefile(['one'], 'Xtest', 's')
+ call writefile(['two'], 'Xtest', 'S')
+ call delete('Xtest')
+endfunc
+
+func Test_writefile_sync_dev_stdout()
+ if !has('unix')
+ return
+ endif
+ if filewritable('/dev/stdout')
+ " Just check that this doesn't cause an error.
+ call writefile(['one'], '/dev/stdout', 's')
+ else
+ throw 'Skipped: /dev/stdout is not writable'
+ endif
+endfunc
+
func Test_writefile_autowrite()
set autowrite
new
@@ -237,29 +256,18 @@ func Test_write_errors()
call delete('Xfile')
endfunc
-func Test_writefile_sync_dev_stdout()
- if !has('unix')
- return
- endif
- if filewritable('/dev/stdout')
- " Just check that this doesn't cause an error.
- call writefile(['one'], '/dev/stdout', 's')
- else
- throw 'Skipped: /dev/stdout is not writable'
- endif
-endfunc
-
-func Test_writefile_sync_arg()
- " This doesn't check if fsync() works, only that the argument is accepted.
- call writefile(['one'], 'Xtest', 's')
- call writefile(['two'], 'Xtest', 'S')
- call delete('Xtest')
+" Test for writing a file using invalid file encoding
+func Test_write_invalid_encoding()
+ new
+ call setline(1, 'abc')
+ call assert_fails('write ++enc=axbyc Xfile', 'E213:')
+ close!
endfunc
" Tests for reading and writing files with conversion for Win32.
func Test_write_file_encoding()
- CheckMSWindows
throw 'skipped: Nvim does not support :w ++enc=cp1251'
+ CheckMSWindows
let save_encoding = &encoding
let save_fileencodings = &fileencodings
set encoding& fileencodings&
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index be0dda0a2f..da671a3ad1 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -13,10 +13,12 @@
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/event/loop.h"
#include "nvim/ex_getln.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/log.h"
#include "nvim/main.h"
@@ -30,8 +32,7 @@
#include "nvim/os/signal.h"
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 5df70d0d8e..2216e25db9 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -13,6 +13,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lib/kvec.h"
@@ -22,8 +23,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/os.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ugrid.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 1874958964..75a09b244c 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -1,73 +1,71 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * undo.c: multi level undo facility
- *
- * The saved lines are stored in a list of lists (one for each buffer):
- *
- * b_u_oldhead------------------------------------------------+
- * |
- * V
- * +--------------+ +--------------+ +--------------+
- * b_u_newhead--->| u_header | | u_header | | u_header |
- * | uh_next------>| uh_next------>| uh_next---->NULL
- * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
- * | uh_entry | | uh_entry | | uh_entry |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ +--------------+ +--------------+
- * | u_entry | | u_entry | | u_entry |
- * | ue_next | | ue_next | | ue_next |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ NULL NULL
- * | u_entry |
- * | ue_next |
- * +--------|-----+
- * |
- * V
- * etc.
- *
- * Each u_entry list contains the information for one undo or redo.
- * curbuf->b_u_curhead points to the header of the last undo (the next redo),
- * or is NULL if nothing has been undone (end of the branch).
- *
- * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
- * each point in the list a branch may appear for an alternate to redo. The
- * uh_seq field is numbered sequentially to be able to find a newer or older
- * branch.
- *
- * +---------------+ +---------------+
- * b_u_oldhead --->| u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next ----> NULL
- * NULL <----- uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next | | uh_alt_next |
- * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * NULL +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next |
- * | uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * etc. etc.
- *
- *
- * All data is allocated and will all be freed when the buffer is unloaded.
- */
+// undo.c: multi level undo facility
+
+// The saved lines are stored in a list of lists (one for each buffer):
+//
+// b_u_oldhead------------------------------------------------+
+// |
+// V
+// +--------------+ +--------------+ +--------------+
+// b_u_newhead--->| u_header | | u_header | | u_header |
+// | uh_next------>| uh_next------>| uh_next---->NULL
+// NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
+// | uh_entry | | uh_entry | | uh_entry |
+// +--------|-----+ +--------|-----+ +--------|-----+
+// | | |
+// V V V
+// +--------------+ +--------------+ +--------------+
+// | u_entry | | u_entry | | u_entry |
+// | ue_next | | ue_next | | ue_next |
+// +--------|-----+ +--------|-----+ +--------|-----+
+// | | |
+// V V V
+// +--------------+ NULL NULL
+// | u_entry |
+// | ue_next |
+// +--------|-----+
+// |
+// V
+// etc.
+//
+// Each u_entry list contains the information for one undo or redo.
+// curbuf->b_u_curhead points to the header of the last undo (the next redo),
+// or is NULL if nothing has been undone (end of the branch).
+//
+// For keeping alternate undo/redo branches the uh_alt field is used. Thus at
+// each point in the list a branch may appear for an alternate to redo. The
+// uh_seq field is numbered sequentially to be able to find a newer or older
+// branch.
+//
+// +---------------+ +---------------+
+// b_u_oldhead --->| u_header | | u_header |
+// | uh_alt_next ---->| uh_alt_next ----> NULL
+// NULL <----- uh_alt_prev |<------ uh_alt_prev |
+// | uh_prev | | uh_prev |
+// +-----|---------+ +-----|---------+
+// | |
+// V V
+// +---------------+ +---------------+
+// | u_header | | u_header |
+// | uh_alt_next | | uh_alt_next |
+// b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
+// | uh_prev | | uh_prev |
+// +-----|---------+ +-----|---------+
+// | |
+// V V
+// NULL +---------------+ +---------------+
+// | u_header | | u_header |
+// | uh_alt_next ---->| uh_alt_next |
+// | uh_alt_prev |<------ uh_alt_prev |
+// | uh_prev | | uh_prev |
+// +-----|---------+ +-----|---------+
+// | |
+// etc. etc.
+//
+//
+// All data is allocated and will all be freed when the buffer is unloaded.
// Uncomment the next line for including the u_check() function. This warns
// for errors in the debug information.
@@ -88,6 +86,7 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
@@ -126,31 +125,25 @@ typedef struct {
// used in undo_end() to report number of added and deleted lines
static long u_newcount, u_oldcount;
-/*
- * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
- * the action that "u" should do.
- */
+// When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
+// the action that "u" should do.
static bool undo_undoes = false;
static int lastmark = 0;
#if defined(U_DEBUG)
-/*
- * Check the undo structures for being valid. Print a warning when something
- * looks wrong.
- */
+// Check the undo structures for being valid. Print a warning when something
+// looks wrong.
static int seen_b_u_curhead;
static int seen_b_u_newhead;
static int header_count;
static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *exp_uh_alt_prev)
{
- u_entry_T *uep;
-
if (uhp == NULL) {
return;
}
- ++header_count;
+ header_count++;
if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1) {
emsg("b_u_curhead found twice (looping?)");
return;
@@ -176,7 +169,7 @@ static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *e
}
// Check the undo tree at this header.
- for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) {
+ for (u_entry_T *uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) {
if (uep->ue_magic != UE_MAGIC) {
emsg("ue_magic wrong (may be using freed memory)");
break;
@@ -215,11 +208,9 @@ static void u_check(int newhead_may_be_NULL)
#endif
-/*
- * Save the current line for both the "u" and "U" command.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns OK or FAIL.
- */
+/// Save the current line for both the "u" and "U" command.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns OK or FAIL.
int u_save_cursor(void)
{
linenr_T cur = curwin->w_cursor.lnum;
@@ -229,12 +220,10 @@ int u_save_cursor(void)
return u_save(top, bot);
}
-/*
- * Save the lines between "top" and "bot" for both the "u" and "U" command.
- * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Save the lines between "top" and "bot" for both the "u" and "U" command.
+/// "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_save(linenr_T top, linenr_T bot)
{
if (top >= bot || bot > (curbuf->b_ml.ml_line_count + 1)) {
@@ -248,35 +237,29 @@ int u_save(linenr_T top, linenr_T bot)
return u_savecommon(curbuf, top, bot, (linenr_T)0, false);
}
-/*
- * Save the line "lnum" (used by ":s" and "~" command).
- * The line is replaced, so the new bottom line is lnum + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Save the line "lnum" (used by ":s" and "~" command).
+/// The line is replaced, so the new bottom line is lnum + 1.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_savesub(linenr_T lnum)
{
return u_savecommon(curbuf, lnum - 1, lnum + 1, lnum + 1, false);
}
-/*
- * A new line is inserted before line "lnum" (used by :s command).
- * The line is inserted, so the new bottom line is lnum + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// A new line is inserted before line "lnum" (used by :s command).
+/// The line is inserted, so the new bottom line is lnum + 1.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_inssub(linenr_T lnum)
{
return u_savecommon(curbuf, lnum - 1, lnum, lnum + 1, false);
}
-/*
- * Save the lines "lnum" - "lnum" + nlines (used by delete command).
- * The lines are deleted, so the new bottom line is lnum, unless the buffer
- * becomes empty.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Save the lines "lnum" - "lnum" + nlines (used by delete command).
+/// The lines are deleted, so the new bottom line is lnum, unless the buffer
+/// becomes empty.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_savedel(linenr_T lnum, long nlines)
{
return u_savecommon(curbuf, lnum - 1, lnum + (linenr_T)nlines,
@@ -328,25 +311,15 @@ static inline void zero_fmark_additional_data(fmark_T *fmarks)
}
}
-/*
- * Common code for various ways to save text before a change.
- * "top" is the line above the first changed line.
- * "bot" is the line below the last changed line.
- * "newbot" is the new bottom line. Use zero when not known.
- * "reload" is TRUE when saving for a buffer reload.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Common code for various ways to save text before a change.
+/// "top" is the line above the first changed line.
+/// "bot" is the line below the last changed line.
+/// "newbot" is the new bottom line. Use zero when not known.
+/// "reload" is true when saving for a buffer reload.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int reload)
{
- linenr_T lnum;
- long i;
- u_header_T *uhp;
- u_header_T *old_curhead;
- u_entry_T *uep;
- u_entry_T *prev_uep;
- long size;
-
if (!reload) {
// When making changes is not allowed return FAIL. It's a crude way
// to make all change commands fail.
@@ -371,16 +344,19 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
}
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
- size = bot - top - 1;
+ u_entry_T *uep;
+ u_entry_T *prev_uep;
+ long size = bot - top - 1;
// If curbuf->b_u_synced == true make a new header.
if (buf->b_u_synced) {
// Need to create new entry in b_changelist.
buf->b_new_change = true;
+ u_header_T *uhp;
if (get_undolevel(buf) >= 0) {
// Make a new header entry. Do this first so that we don't mess
// up the undo info when out of memory.
@@ -393,19 +369,15 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
uhp = NULL;
}
- /*
- * If we undid more than we redid, move the entry lists before and
- * including curbuf->b_u_curhead to an alternate branch.
- */
- old_curhead = buf->b_u_curhead;
+ // If we undid more than we redid, move the entry lists before and
+ // including curbuf->b_u_curhead to an alternate branch.
+ u_header_T *old_curhead = buf->b_u_curhead;
if (old_curhead != NULL) {
buf->b_u_newhead = old_curhead->uh_next.ptr;
buf->b_u_curhead = NULL;
}
- /*
- * free headers to keep the size right
- */
+ // free headers to keep the size right
while (buf->b_u_numhead > get_undolevel(buf)
&& buf->b_u_oldhead != NULL) {
u_header_T *uhfree = buf->b_u_oldhead;
@@ -424,7 +396,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
u_freebranch(buf, uhfree, &old_curhead);
}
#ifdef U_DEBUG
- u_check(TRUE);
+ u_check(true);
#endif
}
@@ -496,19 +468,17 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
return OK;
}
- /*
- * When saving a single line, and it has been saved just before, it
- * doesn't make sense saving it again. Saves a lot of memory when
- * making lots of changes inside the same line.
- * This is only possible if the previous change didn't increase or
- * decrease the number of lines.
- * Check the ten last changes. More doesn't make sense and takes too
- * long.
- */
+ // When saving a single line, and it has been saved just before, it
+ // doesn't make sense saving it again. Saves a lot of memory when
+ // making lots of changes inside the same line.
+ // This is only possible if the previous change didn't increase or
+ // decrease the number of lines.
+ // Check the ten last changes. More doesn't make sense and takes too
+ // long.
if (size == 1) {
uep = u_get_headentry(buf);
prev_uep = NULL;
- for (i = 0; i < 10; ++i) {
+ for (long i = 0; i < 10; i++) {
if (uep == NULL) {
break;
}
@@ -520,7 +490,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
!= (uep->ue_bot == 0
? buf->b_ml.ml_line_count + 1
: uep->ue_bot))
- : uep->ue_lcount != buf->b_ml.ml_line_count)
+ : uep->ue_lcount != buf->b_ml.ml_line_count)
|| (uep->ue_size > 1
&& top >= uep->ue_top
&& top + 2 <= uep->ue_top + uep->ue_size + 1)) {
@@ -567,9 +537,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
u_getbot(buf);
}
- /*
- * add lines in front of entry list
- */
+ // add lines in front of entry list
uep = xmalloc(sizeof(u_entry_T));
CLEAR_POINTER(uep);
#ifdef U_DEBUG
@@ -591,7 +559,9 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
if (size > 0) {
uep->ue_array = xmalloc(sizeof(char_u *) * (size_t)size);
- for (i = 0, lnum = top + 1; i < size; ++i) {
+ linenr_T lnum;
+ long i;
+ for (i = 0, lnum = top + 1; i < size; i++) {
fast_breakcheck();
if (got_int) {
u_freeentry(uep, i);
@@ -613,7 +583,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
undo_undoes = false;
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
return OK;
}
@@ -649,12 +619,9 @@ static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
void u_compute_hash(buf_T *buf, char_u *hash)
{
context_sha256_T ctx;
- linenr_T lnum;
- char_u *p;
-
sha256_start(&ctx);
- for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- p = ml_get_buf(buf, lnum, false);
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ char_u *p = ml_get_buf(buf, lnum, false);
sha256_update(&ctx, p, (uint32_t)(STRLEN(p) + 1));
}
sha256_finish(&ctx, hash);
@@ -674,21 +641,14 @@ void u_compute_hash(buf_T *buf, char_u *hash)
char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
FUNC_ATTR_WARN_UNUSED_RESULT
{
- char *dirp;
- char dir_name[MAXPATHL + 1];
- char *munged_name = NULL;
- char *undo_file_name = NULL;
const char *ffname = buf_ffname;
-#ifdef HAVE_READLINK
- char fname_buf[MAXPATHL];
-#endif
- char *p;
if (ffname == NULL) {
return NULL;
}
#ifdef HAVE_READLINK
+ char fname_buf[MAXPATHL];
// Expand symlink in the file name, so that we put the undo file with the
// actual file instead of with the symlink.
if (resolve_symlink((const char_u *)ffname, (char_u *)fname_buf) == OK) {
@@ -696,9 +656,13 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
}
#endif
+ char dir_name[MAXPATHL + 1];
+ char *munged_name = NULL;
+ char *undo_file_name = NULL;
+
// Loop over 'undodir'. When reading find the first file that exists.
// When not reading use the first directory that exists or ".".
- dirp = (char *)p_udir;
+ char *dirp = (char *)p_udir;
while (*dirp != NUL) {
size_t dir_len = copy_option_part(&dirp, dir_name, MAXPATHL, ",");
if (dir_len == 1 && dir_name[0] == '.') {
@@ -716,7 +680,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
dir_name[dir_len] = NUL;
// Remove trailing pathseps from directory name
- p = &dir_name[dir_len - 1];
+ char *p = &dir_name[dir_len - 1];
while (vim_ispathsep(*p)) {
*p-- = NUL;
}
@@ -737,9 +701,9 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
if (has_directory) {
if (munged_name == NULL) {
munged_name = xstrdup(ffname);
- for (p = munged_name; *p != NUL; MB_PTR_ADV(p)) {
- if (vim_ispathsep(*p)) {
- *p = '%';
+ for (char *c = munged_name; *c != NUL; MB_PTR_ADV(c)) {
+ if (vim_ispathsep(*c)) {
+ *c = '%';
}
}
}
@@ -771,12 +735,9 @@ static void corruption_error(const char *const mesg, const char *const file_name
static void u_free_uhp(u_header_T *uhp)
{
- u_entry_T *nuep;
- u_entry_T *uep;
-
- uep = uhp->uh_entry;
+ u_entry_T *uep = uhp->uh_entry;
while (uep != NULL) {
- nuep = uep->ue_next;
+ u_entry_T *nuep = uep->ue_next;
u_freeentry(uep, uep->ue_size);
uep = nuep;
}
@@ -1022,23 +983,21 @@ static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup)
static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, const char *filename)
{
- UndoObjectType type;
uint8_t *buf = NULL;
- size_t n_elems;
ExtmarkUndoObject *extup = xmalloc(sizeof(ExtmarkUndoObject));
- type = (UndoObjectType)undo_read_4c(bi);
+ UndoObjectType type = (UndoObjectType)undo_read_4c(bi);
extup->type = type;
if (type == kExtmarkSplice) {
- n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t);
+ size_t n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t);
buf = xcalloc(n_elems, sizeof(uint8_t));
if (!undo_read(bi, buf, n_elems)) {
goto error;
}
extup->data.splice = *(ExtmarkSplice *)buf;
} else if (type == kExtmarkMove) {
- n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t);
+ size_t n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t);
buf = xcalloc(n_elems, sizeof(uint8_t));
if (!undo_read(bi, buf, n_elems)) {
goto error;
@@ -1179,15 +1138,11 @@ static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, char_u *const hash)
FUNC_ATTR_NONNULL_ARG(3, 4)
{
- u_header_T *uhp;
char *file_name;
- int mark;
#ifdef U_DEBUG
int headers_written = 0;
#endif
- int fd;
FILE *fp = NULL;
- int perm;
bool write_ok = false;
if (name == NULL) {
@@ -1204,12 +1159,10 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
file_name = (char *)name;
}
- /*
- * Decide about the permission to use for the undo file. If the buffer
- * has a name use the permission of the original file. Otherwise only
- * allow the user to access the undo file.
- */
- perm = 0600;
+ // Decide about the permission to use for the undo file. If the buffer
+ // has a name use the permission of the original file. Otherwise only
+ // allow the user to access the undo file.
+ int perm = 0600;
if (buf->b_ffname != NULL) {
perm = os_getperm((const char *)buf->b_ffname);
if (perm < 0) {
@@ -1220,6 +1173,8 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
// Strip any sticky and executable bits.
perm = perm & 0666;
+ int fd;
+
// If the undo file already exists, verify that it actually is an undo
// file, and delete it.
if (os_path_exists((char_u *)file_name)) {
@@ -1284,15 +1239,13 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
#ifdef U_DEBUG
// Check there is no problem in undo info before writing.
- u_check(FALSE);
+ u_check(false);
#endif
#ifdef UNIX
- /*
- * Try to set the group of the undo file same as the original file. If
- * this fails, set the protection bits for the group same as the
- * protection bits for others.
- */
+ // Try to set the group of the undo file same as the original file. If
+ // this fails, set the protection bits for the group same as the
+ // protection bits for others.
FileInfo file_info_old;
FileInfo file_info_new;
if (buf->b_ffname != NULL
@@ -1324,17 +1277,15 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
goto write_error;
}
- /*
- * Iteratively serialize UHPs and their UEPs from the top down.
- */
- mark = ++lastmark;
- uhp = buf->b_u_oldhead;
+ // Iteratively serialize UHPs and their UEPs from the top down.
+ int mark = ++lastmark;
+ u_header_T *uhp = buf->b_u_oldhead;
while (uhp != NULL) {
// Serialize current UHP if we haven't seen it
if (uhp->uh_walk != mark) {
uhp->uh_walk = mark;
#ifdef U_DEBUG
- ++headers_written;
+ headers_written++;
#endif
if (!serialize_uhp(&bi, uhp)) {
goto write_error;
@@ -1574,7 +1525,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
// We have put all of the headers into a table. Now we iterate through the
// table and swizzle each sequence number we have stored in uh_*_seq into
// a pointer corresponding to the header with that sequence number.
- short old_idx = -1, new_idx = -1, cur_idx = -1;
+ int16_t old_idx = -1, new_idx = -1, cur_idx = -1;
for (int i = 0; i < num_head; i++) {
u_header_T *uhp = uhp_table[i];
if (uhp == NULL) {
@@ -1620,18 +1571,18 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
}
}
if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq) {
- assert(i <= SHRT_MAX);
- old_idx = (short)i;
+ assert(i <= INT16_MAX);
+ old_idx = (int16_t)i;
SET_FLAG(i);
}
if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq) {
- assert(i <= SHRT_MAX);
- new_idx = (short)i;
+ assert(i <= INT16_MAX);
+ new_idx = (int16_t)i;
SET_FLAG(i);
}
if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq) {
- assert(i <= SHRT_MAX);
- cur_idx = (short)i;
+ assert(i <= INT16_MAX);
+ cur_idx = (int16_t)i;
SET_FLAG(i);
}
}
@@ -1662,7 +1613,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
}
}
xfree(uhp_table_used);
- u_check(TRUE);
+ u_check(true);
#endif
if (name != NULL) {
@@ -1787,17 +1738,13 @@ static uint8_t *undo_read_string(bufinfo_T *bi, size_t len)
return ptr;
}
-/*
- * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
- * If 'cpoptions' does not contain 'u': Always undo.
- */
+/// If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
+/// If 'cpoptions' does not contain 'u': Always undo.
void u_undo(int count)
{
- /*
- * If we get an undo command while executing a macro, we behave like the
- * original vi. If this happens twice in one macro the result will not
- * be compatible.
- */
+ // If we get an undo command while executing a macro, we behave like the
+ // original vi. If this happens twice in one macro the result will not
+ // be compatible.
if (curbuf->b_u_synced == false) {
u_sync(true);
count = 1;
@@ -1811,10 +1758,8 @@ void u_undo(int count)
u_doit(count, false, true);
}
-/*
- * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
- * If 'cpoptions' does not contain 'u': Always redo.
- */
+/// If 'cpoptions' contains 'u': Repeat the previous undo or redo.
+/// If 'cpoptions' does not contain 'u': Always redo.
void u_redo(int count)
{
if (vim_strchr(p_cpo, CPO_UNDO) == NULL) {
@@ -1876,8 +1821,6 @@ bool u_undo_and_forget(int count)
/// @param do_buf_event If `true`, send the changedtick with the buffer updates
static void u_doit(int startcount, bool quiet, bool do_buf_event)
{
- int count = startcount;
-
if (!undo_allowed(curbuf)) {
return;
}
@@ -1887,6 +1830,8 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
u_oldcount = -1;
}
+
+ int count = startcount;
while (count--) {
// Do the change warning now, so that it triggers FileChangedRO when
// needed. This may cause the file to be reloaded, that must happen
@@ -1946,21 +1891,6 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
// "sec" must be false then.
void undo_time(long step, bool sec, bool file, bool absolute)
{
- long target;
- long closest;
- long closest_start;
- long closest_seq = 0;
- long val;
- u_header_T *uhp = NULL;
- u_header_T *last;
- int mark;
- int nomark = 0; // shut up compiler
- int round;
- bool dosec = sec;
- bool dofile = file;
- bool above = false;
- bool did_undo = true;
-
if (text_locked()) {
text_locked_msg();
return;
@@ -1977,6 +1907,14 @@ void undo_time(long step, bool sec, bool file, bool absolute)
u_oldcount = -1;
}
+ long target;
+ long closest;
+ u_header_T *uhp = NULL;
+ bool dosec = sec;
+ bool dofile = file;
+ bool above = false;
+ bool did_undo = true;
+
// "target" is the node below which we want to be.
// Init "closest" to a value we can't reach.
if (absolute) {
@@ -2040,8 +1978,10 @@ void undo_time(long step, bool sec, bool file, bool absolute)
}
}
}
- closest_start = closest;
- closest_seq = curbuf->b_u_seq_cur;
+ long closest_start = closest;
+ long closest_seq = curbuf->b_u_seq_cur;
+ int mark;
+ int nomark = 0; // shut up compiler
// When "target" is 0; Back to origin.
if (target == 0) {
@@ -2049,15 +1989,13 @@ void undo_time(long step, bool sec, bool file, bool absolute)
goto target_zero;
}
- /*
- * May do this twice:
- * 1. Search for "target", update "closest" to the best match found.
- * 2. If "target" not found search for "closest".
- *
- * When using the closest time we use the sequence number in the second
- * round, because there may be several entries with the same time.
- */
- for (round = 1; round <= 2; round++) {
+ // May do this twice:
+ // 1. Search for "target", update "closest" to the best match found.
+ // 2. If "target" not found search for "closest".
+ //
+ // When using the closest time we use the sequence number in the second
+ // round, because there may be several entries with the same time.
+ for (int round = 1; round <= 2; round++) {
// Find the path from the current state to where we want to go. The
// desired state can be anywhere in the undo tree, need to go all over
// it. We put "nomark" in uh_walk where we have been without success,
@@ -2073,13 +2011,9 @@ void undo_time(long step, bool sec, bool file, bool absolute)
while (uhp != NULL) {
uhp->uh_walk = mark;
- if (dosec) {
- val = (long)(uhp->uh_time);
- } else if (dofile) {
- val = uhp->uh_save_nr;
- } else {
- val = uhp->uh_seq;
- }
+ long val = dosec ? (long)(uhp->uh_time) :
+ dofile ? uhp->uh_save_nr
+ : uhp->uh_seq;
if (round == 1 && !(dofile && val == 0)) {
// Remember the header that is closest to the target.
@@ -2087,17 +2021,17 @@ void undo_time(long step, bool sec, bool file, bool absolute)
// "b_u_seq_cur"). When the timestamp is equal find the
// highest/lowest sequence number.
if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
- : uhp->uh_seq > curbuf->b_u_seq_cur)
+ : uhp->uh_seq > curbuf->b_u_seq_cur)
&& ((dosec && val == closest)
? (step < 0
? uhp->uh_seq < closest_seq
: uhp->uh_seq > closest_seq)
- : closest == closest_start
+ : closest == closest_start
|| (val > target
? (closest > target
? val - target <= closest - target
: val - target <= target - closest)
- : (closest > target
+ : (closest > target
? target - val <= closest - target
: target - val <= target - closest)))) {
closest = val;
@@ -2116,11 +2050,10 @@ void undo_time(long step, bool sec, bool file, bool absolute)
if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
&& uhp->uh_prev.ptr->uh_walk != mark) {
uhp = uhp->uh_prev.ptr;
- }
- // go to alternate branch if we haven't been there
- else if (uhp->uh_alt_next.ptr != NULL
- && uhp->uh_alt_next.ptr->uh_walk != nomark
- && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ } else if (uhp->uh_alt_next.ptr != NULL
+ && uhp->uh_alt_next.ptr->uh_walk != nomark
+ && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ // go to alternate branch if we haven't been there
uhp = uhp->uh_alt_next.ptr;
} else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
// go up in the tree if we haven't been there and we are at the
@@ -2214,7 +2147,7 @@ target_zero:
}
// Find the last branch with a mark, that's the one.
- last = uhp;
+ u_header_T *last = uhp;
while (last->uh_alt_next.ptr != NULL
&& last->uh_alt_next.ptr->uh_walk == mark) {
last = last->uh_alt_next.ptr;
@@ -2291,19 +2224,10 @@ target_zero:
static void u_undoredo(int undo, bool do_buf_event)
{
char_u **newarray = NULL;
- linenr_T oldsize;
- linenr_T newsize;
- linenr_T top, bot;
- linenr_T lnum;
linenr_T newlnum = MAXLNUM;
- long i;
- u_entry_T *uep, *nuep;
+ u_entry_T *nuep;
u_entry_T *newlist = NULL;
- int old_flags;
- int new_flags;
fmark_T namedm[NMARKS];
- visualinfo_T visualinfo;
- bool empty_buffer; // buffer became empty
u_header_T *curhead = curbuf->b_u_curhead;
// Don't want autocommands using the undo structures here, they are
@@ -2311,28 +2235,26 @@ static void u_undoredo(int undo, bool do_buf_event)
block_autocmds();
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
- old_flags = curhead->uh_flags;
- new_flags = (curbuf->b_changed ? UH_CHANGED : 0)
- | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0)
- | (old_flags & UH_RELOAD);
+ int old_flags = curhead->uh_flags;
+ int new_flags = (curbuf->b_changed ? UH_CHANGED : 0)
+ | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0)
+ | (old_flags & UH_RELOAD);
setpcmark();
- /*
- * save marks before undo/redo
- */
+ // save marks before undo/redo
zero_fmark_additional_data(curbuf->b_namedm);
memmove(namedm, curbuf->b_namedm, sizeof(curbuf->b_namedm[0]) * NMARKS);
- visualinfo = curbuf->b_visual;
+ visualinfo_T visualinfo = curbuf->b_visual;
curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
curbuf->b_op_start.col = 0;
curbuf->b_op_end.lnum = 0;
curbuf->b_op_end.col = 0;
- for (uep = curhead->uh_entry; uep != NULL; uep = nuep) {
- top = uep->ue_top;
- bot = uep->ue_bot;
+ for (u_entry_T *uep = curhead->uh_entry; uep != NULL; uep = nuep) {
+ linenr_T top = uep->ue_top;
+ linenr_T bot = uep->ue_bot;
if (bot == 0) {
bot = curbuf->b_ml.ml_line_count + 1;
}
@@ -2344,21 +2266,22 @@ static void u_undoredo(int undo, bool do_buf_event)
return;
}
- oldsize = bot - top - 1; // number of lines before undo
- newsize = (linenr_T)uep->ue_size; // number of lines after undo
+ linenr_T oldsize = bot - top - 1; // number of lines before undo
+ linenr_T newsize = (linenr_T)uep->ue_size; // number of lines after undo
if (top < newlnum) {
- /* If the saved cursor is somewhere in this undo block, move it to
- * the remembered position. Makes "gwap" put the cursor back
- * where it was. */
- lnum = curhead->uh_cursor.lnum;
+ // If the saved cursor is somewhere in this undo block, move it to
+ // the remembered position. Makes "gwap" put the cursor back
+ // where it was.
+ linenr_T lnum = curhead->uh_cursor.lnum;
if (lnum >= top && lnum <= top + newsize + 1) {
curwin->w_cursor = curhead->uh_cursor;
newlnum = curwin->w_cursor.lnum - 1;
} else {
- /* Use the first line that actually changed. Avoids that
- * undoing auto-formatting puts the cursor in the previous
- * line. */
+ // Use the first line that actually changed. Avoids that
+ // undoing auto-formatting puts the cursor in the previous
+ // line.
+ long i;
for (i = 0; i < newsize && i < oldsize; i++) {
if (STRCMP(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) {
break;
@@ -2374,17 +2297,19 @@ static void u_undoredo(int undo, bool do_buf_event)
}
}
- empty_buffer = false;
+ bool empty_buffer = false;
// delete the lines between top and bot and save them in newarray
if (oldsize > 0) {
newarray = xmalloc(sizeof(char_u *) * (size_t)oldsize);
// delete backwards, it goes faster in most cases
+ long i;
+ linenr_T lnum;
for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum) {
// what can we do when we run out of memory?
newarray[i] = u_save_line(lnum);
- /* remember we deleted the last line in the buffer, and a
- * dummy empty line will be inserted */
+ // remember we deleted the last line in the buffer, and a
+ // dummy empty line will be inserted
if (curbuf->b_ml.ml_line_count == 1) {
empty_buffer = true;
}
@@ -2396,11 +2321,11 @@ static void u_undoredo(int undo, bool do_buf_event)
// insert the lines in u_array between top and bot
if (newsize) {
- for (lnum = top, i = 0; i < newsize; ++i, ++lnum) {
- /*
- * If the file is empty, there is an empty line 1 that we
- * should get rid of, by replacing it with the new line
- */
+ long i;
+ linenr_T lnum;
+ for (lnum = top, i = 0; i < newsize; i++, lnum++) {
+ // If the file is empty, there is an empty line 1 that we
+ // should get rid of, by replacing it with the new line
if (empty_buffer && lnum == 0) {
ml_replace((linenr_T)1, (char *)uep->ue_array[i], true);
} else {
@@ -2441,9 +2366,7 @@ static void u_undoredo(int undo, bool do_buf_event)
uep->ue_array = newarray;
uep->ue_bot = top + newsize + 1;
- /*
- * insert this entry in front of the new entry list
- */
+ // insert this entry in front of the new entry list
nuep = uep->ue_next;
uep->ue_next = newlist;
newlist = uep;
@@ -2460,13 +2383,13 @@ static void u_undoredo(int undo, bool do_buf_event)
// Adjust Extmarks
ExtmarkUndoObject undo_info;
if (undo) {
- for (i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) {
+ for (long i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) {
undo_info = kv_A(curhead->uh_extmark, i);
extmark_apply_undo(undo_info, undo);
}
// redo
} else {
- for (i = 0; i < (int)kv_size(curhead->uh_extmark); i++) {
+ for (long i = 0; i < (int)kv_size(curhead->uh_extmark); i++) {
undo_info = kv_A(curhead->uh_extmark, i);
extmark_apply_undo(undo_info, undo);
}
@@ -2496,10 +2419,8 @@ static void u_undoredo(int undo, bool do_buf_event)
buf_updates_changedtick(curbuf);
}
- /*
- * restore marks from before undo/redo
- */
- for (i = 0; i < NMARKS; ++i) {
+ // restore marks from before undo/redo
+ for (long i = 0; i < NMARKS; i++) {
if (curhead->uh_namedm[i].mark.lnum != 0) {
free_fmark(curbuf->b_namedm[i]);
curbuf->b_namedm[i] = curhead->uh_namedm[i];
@@ -2515,14 +2436,12 @@ static void u_undoredo(int undo, bool do_buf_event)
curhead->uh_visual = visualinfo;
}
- /*
- * If the cursor is only off by one line, put it at the same position as
- * before starting the change (for the "o" command).
- * Otherwise the cursor should go to the first undone line.
- */
+ // If the cursor is only off by one line, put it at the same position as
+ // before starting the change (for the "o" command).
+ // Otherwise the cursor should go to the first undone line.
if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
&& curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
}
if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) {
if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) {
@@ -2571,7 +2490,7 @@ static void u_undoredo(int undo, bool do_buf_event)
unblock_autocmds();
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
}
@@ -2583,10 +2502,6 @@ static void u_undoredo(int undo, bool do_buf_event)
/// @param absolute used ":undo N"
static void u_undo_end(bool did_undo, bool absolute, bool quiet)
{
- char *msgstr;
- u_header_T *uhp;
- char_u msgbuf[80];
-
if ((fdo_flags & FDO_UNDO) && KeyTyped) {
foldOpenCursor();
}
@@ -2598,10 +2513,11 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
- --u_newcount;
+ u_newcount--;
}
u_oldcount -= u_newcount;
+ char *msgstr;
if (u_oldcount == -1) {
msgstr = N_("more line");
} else if (u_oldcount < 0) {
@@ -2619,6 +2535,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
}
+ u_header_T *uhp;
if (curbuf->b_u_curhead != NULL) {
// For ":undo N" we prefer a "after #N" message.
if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL) {
@@ -2633,6 +2550,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
uhp = curbuf->b_u_newhead;
}
+ char_u msgbuf[80];
if (uhp == NULL) {
*msgbuf = NUL;
} else {
@@ -2663,9 +2581,8 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
void undo_fmt_time(char_u *buf, size_t buflen, time_t tt)
{
- struct tm curtime;
-
if (time(NULL) - tt >= 100) {
+ struct tm curtime;
os_localtime_r(&tt, &curtime);
if (time(NULL) - tt < (60L * 60L * 12L)) {
// within 12 hours
@@ -2701,27 +2618,20 @@ void u_sync(bool force)
}
}
-/*
- * ":undolist": List the leafs of the undo tree
- */
+/// ":undolist": List the leafs of the undo tree
void ex_undolist(exarg_T *eap)
{
- garray_T ga;
- u_header_T *uhp;
- int mark;
- int nomark;
int changes = 1;
- /*
- * 1: walk the tree to find all leafs, put the info in "ga".
- * 2: sort the lines
- * 3: display the list
- */
- mark = ++lastmark;
- nomark = ++lastmark;
+ // 1: walk the tree to find all leafs, put the info in "ga".
+ // 2: sort the lines
+ // 3: display the list
+ int mark = ++lastmark;
+ int nomark = ++lastmark;
+ garray_T ga;
ga_init(&ga, (int)sizeof(char *), 20);
- uhp = curbuf->b_u_oldhead;
+ u_header_T *uhp = curbuf->b_u_oldhead;
while (uhp != NULL) {
if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
&& uhp->uh_walk != mark) {
@@ -2742,12 +2652,11 @@ void ex_undolist(exarg_T *eap)
if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
&& uhp->uh_prev.ptr->uh_walk != mark) {
uhp = uhp->uh_prev.ptr;
- ++changes;
- }
- // go to alternate branch if we haven't been there
- else if (uhp->uh_alt_next.ptr != NULL
- && uhp->uh_alt_next.ptr->uh_walk != nomark
- && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ changes++;
+ } else if (uhp->uh_alt_next.ptr != NULL
+ && uhp->uh_alt_next.ptr->uh_walk != nomark
+ && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ // go to alternate branch if we haven't been there
uhp = uhp->uh_alt_next.ptr;
} else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
// go up in the tree if we haven't been there and we are at the
@@ -2755,7 +2664,7 @@ void ex_undolist(exarg_T *eap)
&& uhp->uh_next.ptr->uh_walk != nomark
&& uhp->uh_next.ptr->uh_walk != mark) {
uhp = uhp->uh_next.ptr;
- --changes;
+ changes--;
} else {
// need to backtrack; mark this node as done
uhp->uh_walk = nomark;
@@ -2763,7 +2672,7 @@ void ex_undolist(exarg_T *eap)
uhp = uhp->uh_alt_prev.ptr;
} else {
uhp = uhp->uh_next.ptr;
- --changes;
+ changes--;
}
}
}
@@ -2789,9 +2698,7 @@ void ex_undolist(exarg_T *eap)
}
}
-/*
- * ":undojoin": continue adding to the last entry list
- */
+/// ":undojoin": continue adding to the last entry list
void ex_undojoin(exarg_T *eap)
{
if (curbuf->b_u_newhead == NULL) {
@@ -2811,35 +2718,30 @@ void ex_undojoin(exarg_T *eap)
}
}
-/*
- * Called after writing or reloading the file and setting b_changed to FALSE.
- * Now an undo means that the buffer is modified.
- */
+/// Called after writing or reloading the file and setting b_changed to false.
+/// Now an undo means that the buffer is modified.
void u_unchanged(buf_T *buf)
{
u_unch_branch(buf->b_u_oldhead);
buf->b_did_warn = false;
}
-/*
- * After reloading a buffer which was saved for 'undoreload': Find the first
- * line that was changed and set the cursor there.
- */
+/// After reloading a buffer which was saved for 'undoreload': Find the first
+/// line that was changed and set the cursor there.
void u_find_first_changed(void)
{
u_header_T *uhp = curbuf->b_u_newhead;
- u_entry_T *uep;
- linenr_T lnum;
if (curbuf->b_u_curhead != NULL || uhp == NULL) {
return; // undid something in an autocmd?
}
// Check that the last undo block was for the whole file.
- uep = uhp->uh_entry;
+ u_entry_T *uep = uhp->uh_entry;
if (uep->ue_top != 0 || uep->ue_bot != 0) {
return;
}
+ linenr_T lnum;
for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
&& lnum <= uep->ue_size; lnum++) {
if (STRCMP(ml_get_buf(curbuf, lnum, false), uep->ue_array[lnum - 1]) != 0) {
@@ -2855,17 +2757,13 @@ void u_find_first_changed(void)
}
}
-/*
- * Increase the write count, store it in the last undo header, what would be
- * used for "u".
- */
+/// Increase the write count, store it in the last undo header, what would be
+/// used for "u".
void u_update_save_nr(buf_T *buf)
{
- u_header_T *uhp;
-
- ++buf->b_u_save_nr_last;
+ buf->b_u_save_nr_last++;
buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
- uhp = buf->b_u_curhead;
+ u_header_T *uhp = buf->b_u_curhead;
if (uhp != NULL) {
uhp = uhp->uh_next.ptr;
} else {
@@ -2878,9 +2776,7 @@ void u_update_save_nr(buf_T *buf)
static void u_unch_branch(u_header_T *uhp)
{
- u_header_T *uh;
-
- for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr) {
+ for (u_header_T *uh = uhp; uh != NULL; uh = uh->uh_prev.ptr) {
uh->uh_flags |= UH_CHANGED;
if (uh->uh_alt_next.ptr != NULL) {
u_unch_branch(uh->uh_alt_next.ptr); // recursive
@@ -2888,10 +2784,8 @@ static void u_unch_branch(u_header_T *uhp)
}
}
-/*
- * Get pointer to last added entry.
- * If it's not valid, give an error message and return NULL.
- */
+/// Get pointer to last added entry.
+/// If it's not valid, give an error message and return NULL.
static u_entry_T *u_get_headentry(buf_T *buf)
{
if (buf->b_u_newhead == NULL || buf->b_u_newhead->uh_entry == NULL) {
@@ -2901,28 +2795,21 @@ static u_entry_T *u_get_headentry(buf_T *buf)
return buf->b_u_newhead->uh_entry;
}
-/*
- * u_getbot(): compute the line number of the previous u_save
- * It is called only when b_u_synced is false.
- */
+/// u_getbot(): compute the line number of the previous u_save
+/// It is called only when b_u_synced is false.
static void u_getbot(buf_T *buf)
{
- u_entry_T *uep;
- linenr_T extra;
-
- uep = u_get_headentry(buf); // check for corrupt undo list
+ u_entry_T *uep = u_get_headentry(buf); // check for corrupt undo list
if (uep == NULL) {
return;
}
uep = buf->b_u_newhead->uh_getbot_entry;
if (uep != NULL) {
- /*
- * the new ue_bot is computed from the number of lines that has been
- * inserted (0 - deleted) since calling u_save. This is equal to the
- * old line count subtracted from the current line count.
- */
- extra = buf->b_ml.ml_line_count - uep->ue_lcount;
+ // the new ue_bot is computed from the number of lines that has been
+ // inserted (0 - deleted) since calling u_save. This is equal to the
+ // old line count subtracted from the current line count.
+ linenr_T extra = buf->b_ml.ml_line_count - uep->ue_lcount;
uep->ue_bot = uep->ue_top + (linenr_T)uep->ue_size + 1 + extra;
if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) {
iemsg(_("E440: undo line missing"));
@@ -2943,8 +2830,6 @@ static void u_getbot(buf_T *buf)
/// @param uhpp if not NULL reset when freeing this header
static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
{
- u_header_T *uhap;
-
// When there is an alternate redo list free that branch completely,
// because we can never go there.
if (uhp->uh_alt_next.ptr != NULL) {
@@ -2965,7 +2850,7 @@ static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
if (uhp->uh_prev.ptr == NULL) {
buf->b_u_newhead = uhp->uh_next.ptr;
} else {
- for (uhap = uhp->uh_prev.ptr; uhap != NULL;
+ for (u_header_T *uhap = uhp->uh_prev.ptr; uhap != NULL;
uhap = uhap->uh_alt_next.ptr) {
uhap->uh_next.ptr = uhp->uh_next.ptr;
}
@@ -2979,8 +2864,6 @@ static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
/// @param uhpp if not NULL reset when freeing this header
static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
{
- u_header_T *tofree, *next;
-
// If this is the top branch we may need to use u_freeheader() to update
// all the pointers.
if (uhp == buf->b_u_oldhead) {
@@ -2994,9 +2877,9 @@ static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
}
- next = uhp;
+ u_header_T *next = uhp;
while (next != NULL) {
- tofree = next;
+ u_header_T *tofree = next;
if (tofree->uh_alt_next.ptr != NULL) {
u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive
}
@@ -3035,12 +2918,10 @@ static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
uhp->uh_magic = 0;
#endif
xfree((char_u *)uhp);
- --buf->b_u_numhead;
+ buf->b_u_numhead--;
}
-/*
- * free entry 'uep' and 'n' lines in uep->ue_array[]
- */
+/// free entry 'uep' and 'n' lines in uep->ue_array[]
static void u_freeentry(u_entry_T *uep, long n)
{
while (n > 0) {
@@ -3053,9 +2934,7 @@ static void u_freeentry(u_entry_T *uep, long n)
xfree((char_u *)uep);
}
-/*
- * invalidate the undo buffer; called when storage has already been released
- */
+/// invalidate the undo buffer; called when storage has already been released
void u_clearall(buf_T *buf)
{
buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
@@ -3065,9 +2944,7 @@ void u_clearall(buf_T *buf)
buf->b_u_line_lnum = 0;
}
-/*
- * save the line "lnum" for the "U" command
- */
+/// save the line "lnum" for the "U" command
void u_saveline(linenr_T lnum)
{
if (lnum == curbuf->b_u_line_lnum) { // line is already saved
@@ -3086,10 +2963,8 @@ void u_saveline(linenr_T lnum)
curbuf->b_u_line_ptr = u_save_line(lnum);
}
-/*
- * clear the line saved for the "U" command
- * (this is used externally for crossing a line while in insert mode)
- */
+/// clear the line saved for the "U" command
+/// (this is used externally for crossing a line while in insert mode)
void u_clearline(void)
{
if (curbuf->b_u_line_ptr != NULL) {
@@ -3098,17 +2973,12 @@ void u_clearline(void)
}
}
-/*
- * Implementation of the "U" command.
- * Differentiation from vi: "U" can be undone with the next "U".
- * We also allow the cursor to be in another line.
- * Careful: may trigger autocommands that reload the buffer.
- */
+/// Implementation of the "U" command.
+/// Differentiation from vi: "U" can be undone with the next "U".
+/// We also allow the cursor to be in another line.
+/// Careful: may trigger autocommands that reload the buffer.
void u_undoline(void)
{
- colnr_T t;
- char_u *oldp;
-
if (curbuf->b_u_line_ptr == NULL
|| curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count) {
beep_flush();
@@ -3121,7 +2991,7 @@ void u_undoline(void)
return;
}
- oldp = u_save_line(curbuf->b_u_line_lnum);
+ char_u *oldp = u_save_line(curbuf->b_u_line_lnum);
ml_replace(curbuf->b_u_line_lnum, (char *)curbuf->b_u_line_ptr, true);
changed_bytes(curbuf->b_u_line_lnum, 0);
extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum - 1, 0, (colnr_T)STRLEN(oldp),
@@ -3129,7 +2999,7 @@ void u_undoline(void)
xfree(curbuf->b_u_line_ptr);
curbuf->b_u_line_ptr = oldp;
- t = curbuf->b_u_line_colnr;
+ colnr_T t = curbuf->b_u_line_colnr;
if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) {
curbuf->b_u_line_colnr = curwin->w_cursor.col;
}
@@ -3138,9 +3008,7 @@ void u_undoline(void)
check_cursor_col();
}
-/*
- * Free all allocated memory blocks for the buffer 'buf'.
- */
+/// Free all allocated memory blocks for the buffer 'buf'.
void u_blockfree(buf_T *buf)
{
while (buf->b_u_oldhead != NULL) {
diff --git a/src/nvim/version.c b/src/nvim/version.c
index b03cee432d..0667243bc3 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -14,12 +14,13 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
+#include "nvim/grid.h"
#include "nvim/iconv.h"
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/version.h"
#include "nvim/vim.h"
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index b2c7df4cbb..09b949bb20 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -275,8 +275,8 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
(const char *)(y), \
(size_t)(n))
-// Enums need a typecast to be used as array index (for Ultrix).
-#define HL_ATTR(n) highlight_attr[(int)(n)]
+// Enums need a typecast to be used as array index.
+#define HL_ATTR(n) hl_attr_active[(int)(n)]
/// Maximum number of bytes in a multi-byte character. It can be one 32-bit
/// character of up to 6 bytes, or one 16-bit character of up to three bytes
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9d2432ea3b..564b5c4c51 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6,11 +6,14 @@
#include <stdbool.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
@@ -25,7 +28,9 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/hashtab.h"
+#include "nvim/highlight.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
@@ -43,7 +48,6 @@
#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -698,7 +702,7 @@ win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
win_remove(wp, NULL);
win_append(lastwin_nofloating(), wp);
}
- wp->w_floating = 1;
+ wp->w_floating = true;
wp->w_status_height = 0;
wp->w_winbar_height = 0;
wp->w_hsep_height = 0;
@@ -728,13 +732,15 @@ void win_set_minimal_style(win_T *wp)
: concat_str(old, (char_u *)",eob: "));
free_string_option(old);
}
- if (wp->w_hl_ids[HLF_EOB] != -1) {
- char_u *old = wp->w_p_winhl;
- wp->w_p_winhl = ((*old == NUL)
- ? (char_u *)xstrdup("EndOfBuffer:")
- : concat_str(old, (char_u *)",EndOfBuffer:"));
- free_string_option(old);
- }
+
+ // TODO(bfredl): this could use a highlight namespace directly,
+ // and avoid pecularities around window options
+ char_u *old = wp->w_p_winhl;
+ wp->w_p_winhl = ((*old == NUL)
+ ? (char_u *)xstrdup("EndOfBuffer:")
+ : concat_str(old, (char_u *)",EndOfBuffer:"));
+ free_string_option(old);
+ parse_winhl_opt(wp);
// signcolumn: use 'auto'
if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) {
@@ -1670,7 +1676,7 @@ int win_count(void)
int count = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- ++count;
+ count++;
}
return count;
}
@@ -2459,7 +2465,7 @@ void close_windows(buf_T *buf, bool keep_curwin)
tabpage_T *tp, *nexttp;
int h = tabline_height();
- ++RedrawingDisabled;
+ RedrawingDisabled++;
// Start from lastwin to close floating windows with the same buffer first.
// When the autocommand window is involved win_close() may need to print an error message.
@@ -2496,7 +2502,7 @@ void close_windows(buf_T *buf, bool keep_curwin)
}
}
- --RedrawingDisabled;
+ RedrawingDisabled--;
redraw_tabline = true;
if (h != tabline_height()) {
@@ -3820,7 +3826,7 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
m = (int)p_wmw + topfrp->fr_win->w_vsep_width;
// Current window is minimal one column wide
if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) {
- ++m;
+ m++;
}
}
} else if (topfrp->fr_layout == FR_COL) {
@@ -4093,7 +4099,7 @@ int win_new_tabpage(int after, char_u *filename)
n = 2;
for (tp = first_tabpage; tp->tp_next != NULL
&& n < after; tp = tp->tp_next) {
- ++n;
+ n++;
}
}
newtp->tp_next = tp->tp_next;
@@ -4239,7 +4245,7 @@ tabpage_T *find_tabpage(int n)
int i = 1;
for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next) {
- ++i;
+ i++;
}
return tp;
}
@@ -4254,7 +4260,7 @@ int tabpage_index(tabpage_T *ftp)
tabpage_T *tp;
for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next) {
- ++i;
+ i++;
}
return i;
}
@@ -4516,7 +4522,7 @@ void tabpage_move(int nr)
}
for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) {
- ++n;
+ n++;
}
if (tp == curtab || (nr > 0 && tp->tp_next != NULL
@@ -4863,10 +4869,17 @@ static void win_enter_ext(win_T *const wp, const int flags)
redraw_later(curwin, VALID); // causes status line redraw
}
- if (HL_ATTR(HLF_INACTIVE)
- || (prevwin && prevwin->w_hl_ids[HLF_INACTIVE])
- || curwin->w_hl_ids[HLF_INACTIVE]) {
- redraw_all_later(NOT_VALID);
+ // change background color according to NormalNC,
+ // but only if actually defined (otherwise no extra redraw)
+ if (curwin->w_hl_attr_normal != curwin->w_hl_attr_normalnc) {
+ // TODO(bfredl): eventually we should be smart enough
+ // to only recompose the window, not redraw it.
+ redraw_later(curwin, NOT_VALID);
+ }
+ if (prevwin) {
+ if (prevwin->w_hl_attr_normal != prevwin->w_hl_attr_normalnc) {
+ redraw_later(prevwin, NOT_VALID);
+ }
}
// set window height to desired minimal value
@@ -5035,6 +5048,8 @@ static win_T *win_alloc(win_T *after, bool hidden)
new_wp->w_float_config = FLOAT_CONFIG_INIT;
new_wp->w_viewport_invalid = true;
+ new_wp->w_ns_hl = -1;
+
// use global option for global-local options
new_wp->w_p_so = -1;
new_wp->w_p_siso = -1;
@@ -5551,7 +5566,7 @@ static void frame_setheight(frame_T *curfrp, int height)
}
if (curfrp->fr_parent == NULL) {
- // topframe: can only change the command line
+ // topframe: can only change the command line height
if (height > ROWS_AVAIL) {
// If height is greater than the available space, try to create space for
// the frame by reducing 'cmdheight' if possible, while making sure
@@ -5914,6 +5929,13 @@ void win_drag_status_line(win_T *dragwin, int offset)
int row;
bool up; // if true, drag status line up, otherwise down
int n;
+ static bool p_ch_was_zero = false;
+
+ // If the user explicitly set 'cmdheight' to zero, then allow for dragging
+ // the status line making it zero again.
+ if (p_ch == 0) {
+ p_ch_was_zero = true;
+ }
fr = dragwin->w_frame;
curfr = fr;
@@ -5964,6 +5986,8 @@ void win_drag_status_line(win_T *dragwin, int offset)
room = Rows - cmdline_row;
if (curfr->fr_next != NULL) {
room -= (int)p_ch + global_stl_height();
+ } else if (!p_ch_was_zero) {
+ room--;
}
if (room < 0) {
room = 0;
@@ -6019,7 +6043,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
clear_cmdline = true;
}
cmdline_row = row;
- p_ch = MAX(Rows - cmdline_row, 0);
+ p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1);
curtab->tp_ch_used = p_ch;
redraw_all_later(SOME_VALID);
showmode();
@@ -6225,7 +6249,7 @@ void scroll_to_fraction(win_T *wp, int prev_height)
if (lnum == 1) {
// first line in buffer is folded
line_size = 1;
- --sline;
+ sline--;
break;
}
lnum--;
@@ -6383,6 +6407,19 @@ void command_height(void)
// p_ch was changed in another tab page.
curtab->tp_ch_used = p_ch;
+ // If the space for the command line is already more than 'cmdheight' there
+ // is nothing to do (window size must have decreased).
+ if (p_ch > old_p_ch && cmdline_row <= Rows - p_ch) {
+ return;
+ }
+
+ // If cmdline_row is smaller than what it is supposed to be for 'cmdheight'
+ // then set old_p_ch to what it would be, so that the windows get resized
+ // properly for the new value.
+ if (cmdline_row < Rows - p_ch) {
+ old_p_ch = Rows - cmdline_row;
+ }
+
// Find bottom frame with width of screen.
frp = lastwin_nofloating()->w_frame;
while (frp->fr_width != Columns && frp->fr_parent != NULL) {
@@ -6563,7 +6600,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
if (ptr[len] == '\\' && ptr[len + 1] == ' ') {
// Skip over the "\" in "\ ".
- ++len;
+ len++;
}
len += (size_t)(utfc_ptr2len(ptr + len));
}
@@ -6574,7 +6611,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
*/
if (len > 2 && vim_strchr(".,:;!", ptr[len - 1]) != NULL
&& ptr[len - 2] != '.') {
- --len;
+ len--;
}
if (file_lnum != NULL) {
@@ -7232,6 +7269,88 @@ static bool frame_check_width(const frame_T *topfrp, int width)
return true;
}
+/// Simple int comparison function for use with qsort()
+static int int_cmp(const void *a, const void *b)
+{
+ return *(const int *)a - *(const int *)b;
+}
+
+/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
+///
+/// @return error message, NULL if it's OK.
+char *check_colorcolumn(win_T *wp)
+{
+ char *s;
+ int col;
+ unsigned int count = 0;
+ int color_cols[256];
+ int j = 0;
+
+ if (wp->w_buffer == NULL) {
+ return NULL; // buffer was closed
+ }
+
+ for (s = (char *)wp->w_p_cc; *s != NUL && count < 255;) {
+ if (*s == '-' || *s == '+') {
+ // -N and +N: add to 'textwidth'
+ col = (*s == '-') ? -1 : 1;
+ s++;
+ if (!ascii_isdigit(*s)) {
+ return e_invarg;
+ }
+ col = col * getdigits_int(&s, true, 0);
+ if (wp->w_buffer->b_p_tw == 0) {
+ goto skip; // 'textwidth' not set, skip this item
+ }
+ assert((col >= 0
+ && wp->w_buffer->b_p_tw <= INT_MAX - col
+ && wp->w_buffer->b_p_tw + col >= INT_MIN)
+ || (col < 0
+ && wp->w_buffer->b_p_tw >= INT_MIN - col
+ && wp->w_buffer->b_p_tw + col <= INT_MAX));
+ col += (int)wp->w_buffer->b_p_tw;
+ if (col < 0) {
+ goto skip;
+ }
+ } else if (ascii_isdigit(*s)) {
+ col = getdigits_int(&s, true, 0);
+ } else {
+ return e_invarg;
+ }
+ color_cols[count++] = col - 1; // 1-based to 0-based
+skip:
+ if (*s == NUL) {
+ break;
+ }
+ if (*s != ',') {
+ return e_invarg;
+ }
+ if (*++s == NUL) {
+ return e_invarg; // illegal trailing comma as in "set cc=80,"
+ }
+ }
+
+ xfree(wp->w_p_cc_cols);
+ if (count == 0) {
+ wp->w_p_cc_cols = NULL;
+ } else {
+ wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
+ // sort the columns for faster usage on screen redraw inside
+ // win_line()
+ qsort(color_cols, count, sizeof(int), int_cmp);
+
+ for (unsigned int i = 0; i < count; i++) {
+ // skip duplicates
+ if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
+ wp->w_p_cc_cols[j++] = color_cols[i];
+ }
+ }
+ wp->w_p_cc_cols[j] = -1; // end marker
+ }
+
+ return NULL; // no error
+}
+
int win_getid(typval_T *argvars)
{
if (argvars[0].v_type == VAR_UNKNOWN) {
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index c4197f0b3e..2730f7e23d 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -243,7 +243,7 @@ describe("API: set highlight", function()
local function get_ns()
local ns = meths.create_namespace('Test_set_hl')
- meths._set_hl_ns(ns)
+ meths.set_hl_ns(ns)
return ns
end
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 3338fc6538..72a03c409a 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -281,8 +281,8 @@ describe('API', function()
]]}
end)
- it('does\'t display messages when output=true', function()
- local screen = Screen.new(40, 8)
+ it('doesn\'t display messages when output=true', function()
+ local screen = Screen.new(40, 6)
screen:attach()
screen:set_default_attr_ids({
[0] = {bold=true, foreground=Screen.colors.Blue},
@@ -294,9 +294,21 @@ describe('API', function()
{0:~ }|
{0:~ }|
{0:~ }|
+ |
+ ]]}
+ exec([[
+ func Print()
+ call nvim_exec('echo "hello"', v:true)
+ endfunc
+ ]])
+ feed([[:echon 1 | call Print() | echon 5<CR>]])
+ screen:expect{grid=[[
+ ^ |
{0:~ }|
{0:~ }|
- |
+ {0:~ }|
+ {0:~ }|
+ 15 |
]]}
end)
end)
@@ -3836,5 +3848,35 @@ describe('API', function()
meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, {})
assert_alive()
end)
+ it('doesn\'t display messages when output=true', function()
+ local screen = Screen.new(40, 6)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ })
+ meths.cmd({cmd = 'echo', args = {[['hello']]}}, {output = true})
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ exec([[
+ func Print()
+ call nvim_cmd(#{cmd: 'echo', args: ['"hello"']}, #{output: v:true})
+ endfunc
+ ]])
+ feed([[:echon 1 | call Print() | echon 5<CR>]])
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ 15 |
+ ]]}
+ end)
end)
end)
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index 8abfa465ab..02ff18bdda 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -21,6 +21,7 @@ local nvim_set = helpers.nvim_set
local expect_twostreams = helpers.expect_twostreams
local expect_msg_seq = helpers.expect_msg_seq
local pcall_err = helpers.pcall_err
+local matches = helpers.matches
local Screen = require('test.functional.ui.screen')
describe('jobs', function()
@@ -229,8 +230,8 @@ describe('jobs', function()
local dir = 'Xtest_not_executable_dir'
mkdir(dir)
funcs.setfperm(dir, 'rw-------')
- eq('Vim(call):E475: Invalid argument: expected valid directory',
- pcall_err(nvim, 'command', "call jobstart('pwd', {'cwd': '"..dir.."'})"))
+ matches('^Vim%(call%):E903: Process failed to start: permission denied: .*',
+ pcall_err(nvim, 'command', "call jobstart(['pwd'], {'cwd': '"..dir.."'})"))
rmdir(dir)
end)
diff --git a/test/functional/editor/ctrl_c_spec.lua b/test/functional/editor/ctrl_c_spec.lua
index 60131bf2a4..4548e1aa34 100644
--- a/test/functional/editor/ctrl_c_spec.lua
+++ b/test/functional/editor/ctrl_c_spec.lua
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, source = helpers.clear, helpers.feed, helpers.source
local command = helpers.command
+local poke_eventloop = helpers.poke_eventloop
local sleep = helpers.sleep
describe("CTRL-C (mapped)", function()
@@ -57,11 +58,9 @@ describe("CTRL-C (mapped)", function()
it('interrupts :sleep', function()
command('nnoremap <C-C> <Nop>')
feed(':sleep 100<CR>')
- -- wait for :sleep to start
- sleep(10)
+ poke_eventloop() -- wait for :sleep to start
feed('foo<C-C>')
- -- wait for input buffer to be flushed
- sleep(10)
+ poke_eventloop() -- wait for input buffer to be flushed
feed('i')
screen:expect([[
^ |
@@ -77,10 +76,9 @@ describe("CTRL-C (mapped)", function()
command('nnoremap <C-C> <Nop>')
command('nmap <F2> <Ignore><F2>')
feed('<F2>')
- sleep(10)
+ sleep(10) -- wait for the key to enter typeahead
feed('foo<C-C>')
- -- wait for input buffer to be flushed
- sleep(10)
+ poke_eventloop() -- wait for input buffer to be flushed
feed('i')
screen:expect([[
^ |
diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua
index 003ab64dd4..5f87c3cdd9 100644
--- a/test/functional/ex_cmds/oldfiles_spec.lua
+++ b/test/functional/ex_cmds/oldfiles_spec.lua
@@ -29,7 +29,6 @@ describe(':oldfiles', function()
it('shows most recently used files', function()
local screen = Screen.new(100, 5)
screen:attach()
- feed_command("set display-=msgsep")
feed_command('edit testfile1')
feed_command('edit testfile2')
feed_command('wshada')
@@ -37,7 +36,7 @@ describe(':oldfiles', function()
local oldfiles = helpers.meths.get_vvar('oldfiles')
feed_command('oldfiles')
screen:expect([[
- testfile2 |
+ |
1: ]].. add_padding(oldfiles[1]) ..[[ |
2: ]].. add_padding(oldfiles[2]) ..[[ |
|
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index 4bc3355e9e..163ded43f9 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -48,21 +48,38 @@ describe(':source', function()
pending("'shellslash' only works on Windows")
return
end
+ meths.set_option('shellslash', false)
mkdir('Xshellslash')
- local script = [[
- let g:result1 = expand('<stack>')
+
+ write_file([[Xshellslash/Xstack.vim]], [[
+ let g:stack1 = expand('<stack>')
set shellslash
- let g:result2 = expand('<stack>')
+ let g:stack2 = expand('<stack>')
set noshellslash
- let g:result3 = expand('<stack>')
- ]]
- write_file([[Xshellslash/Xexpand.vim]], script)
+ let g:stack3 = expand('<stack>')
+ ]])
- meths.set_option('shellslash', false)
- command([[source Xshellslash/Xexpand.vim]])
- matches([[Xshellslash\Xexpand%.vim]], meths.get_var('result1'))
- matches([[Xshellslash/Xexpand%.vim]], meths.get_var('result2'))
- matches([[Xshellslash\Xexpand%.vim]], meths.get_var('result3'))
+ for _ = 1, 2 do
+ command([[source Xshellslash/Xstack.vim]])
+ matches([[Xshellslash\Xstack%.vim]], meths.get_var('stack1'))
+ matches([[Xshellslash/Xstack%.vim]], meths.get_var('stack2'))
+ matches([[Xshellslash\Xstack%.vim]], meths.get_var('stack3'))
+ end
+
+ write_file([[Xshellslash/Xstack.lua]], [[
+ vim.g.stack1 = vim.fn.expand('<stack>')
+ vim.o.shellslash = true
+ vim.g.stack2 = vim.fn.expand('<stack>')
+ vim.o.shellslash = false
+ vim.g.stack3 = vim.fn.expand('<stack>')
+ ]])
+
+ for _ = 1, 2 do
+ command([[source Xshellslash/Xstack.lua]])
+ matches([[Xshellslash\Xstack%.lua]], meths.get_var('stack1'))
+ matches([[Xshellslash/Xstack%.lua]], meths.get_var('stack2'))
+ matches([[Xshellslash\Xstack%.lua]], meths.get_var('stack3'))
+ end
rmdir('Xshellslash')
end)
@@ -145,11 +162,18 @@ describe(':source', function()
it('can source lua files', function()
local test_file = 'test.lua'
- write_file (test_file, [[vim.g.sourced_lua = 1]])
-
- exec('source ' .. test_file)
+ write_file(test_file, [[
+ vim.g.sourced_lua = 1
+ vim.g.sfile_value = vim.fn.expand('<sfile>')
+ vim.g.stack_value = vim.fn.expand('<stack>')
+ ]])
+ command('set shellslash')
+ command('source ' .. test_file)
eq(1, eval('g:sourced_lua'))
+ matches([[/test%.lua$]], meths.get_var('sfile_value'))
+ matches([[/test%.lua$]], meths.get_var('stack_value'))
+
os.remove(test_file)
end)
@@ -181,13 +205,15 @@ describe(':source', function()
it('can source current lua buffer without argument', function()
local test_file = 'test.lua'
- write_file (test_file, [[
+ write_file(test_file, [[
vim.g.c = 10
vim.g.c = 11
vim.g.c = 12
a = [=[
\ 1
"\ 2]=]
+ vim.g.sfile_value = vim.fn.expand('<sfile>')
+ vim.g.stack_value = vim.fn.expand('<stack>')
]])
command('edit '..test_file)
@@ -195,6 +221,9 @@ describe(':source', function()
eq(12, eval('g:c'))
eq(' \\ 1\n "\\ 2', exec_lua('return _G.a'))
+ eq(':source (no file)', meths.get_var('sfile_value'))
+ eq(':source (no file)', meths.get_var('stack_value'))
+
os.remove(test_file)
end)
diff --git a/test/functional/ex_cmds/wincmd_spec.lua b/test/functional/ex_cmds/wincmd_spec.lua
new file mode 100644
index 0000000000..b1f174f445
--- /dev/null
+++ b/test/functional/ex_cmds/wincmd_spec.lua
@@ -0,0 +1,13 @@
+local helpers = require("test.functional.helpers")(after_each)
+local clear = helpers.clear
+local eq = helpers.eq
+local funcs = helpers.funcs
+local command = helpers.command
+
+it(':wincmd accepts a count', function()
+ clear()
+ command('vsplit')
+ eq(1, funcs.winnr())
+ command('wincmd 2 w')
+ eq(2, funcs.winnr())
+end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 8c5a60657a..981cfc306e 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -244,10 +244,12 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
last_error = nil
error(err)
end
+
+ return session.eof_err
end
function module.run(request_cb, notification_cb, setup_cb, timeout)
- module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
+ return module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
end
function module.stop()
diff --git a/test/functional/legacy/ex_mode_spec.lua b/test/functional/legacy/ex_mode_spec.lua
index a8f54c6939..f21c47e175 100644
--- a/test/functional/legacy/ex_mode_spec.lua
+++ b/test/functional/legacy/ex_mode_spec.lua
@@ -6,7 +6,7 @@ local eq = helpers.eq
local eval = helpers.eval
local feed = helpers.feed
local meths = helpers.meths
-local sleep = helpers.sleep
+local poke_eventloop = helpers.poke_eventloop
before_each(clear)
@@ -143,7 +143,7 @@ describe('Ex mode', function()
^ |
]])
feed('<C-C>')
- sleep(10) -- Wait for input to be flushed
+ poke_eventloop() -- Wait for input to be flushed
feed('foo<CR>')
screen:expect([[
Entering Ex mode. Type "visual" to go to Normal mode. |
diff --git a/test/functional/legacy/filechanged_spec.lua b/test/functional/legacy/filechanged_spec.lua
index ecb861098c..1f23528d61 100644
--- a/test/functional/legacy/filechanged_spec.lua
+++ b/test/functional/legacy/filechanged_spec.lua
@@ -67,6 +67,15 @@ describe('file changed dialog', function()
call assert_equal(1, line('$'))
call assert_equal('new line', getline(1))
+ " File created after starting to edit it
+ call delete('Xchanged_d')
+ new Xchanged_d
+ call writefile(['one'], 'Xchanged_d')
+ call nvim_input('L')
+ checktime Xchanged_d
+ call assert_equal(['one'], getline(1, '$'))
+ close!
+
bwipe!
call delete('Xchanged_d')
endfunc
diff --git a/test/functional/legacy/gf_spec.lua b/test/functional/legacy/gf_spec.lua
new file mode 100644
index 0000000000..f1b1790ba1
--- /dev/null
+++ b/test/functional/legacy/gf_spec.lua
@@ -0,0 +1,15 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local pcall_err = helpers.pcall_err
+
+describe('gf', function()
+ before_each(clear)
+
+ it('is not allowed when buffer is locked', function()
+ command('au OptionSet diff norm! gf')
+ command([[call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])]])
+ eq('Vim(normal):E788: Not allowed to edit another buffer now', pcall_err(command, 'diffthis'))
+ end)
+end)
diff --git a/test/functional/legacy/global_spec.lua b/test/functional/legacy/global_spec.lua
index 9f4528530c..ff02c41e6c 100644
--- a/test/functional/legacy/global_spec.lua
+++ b/test/functional/legacy/global_spec.lua
@@ -3,7 +3,7 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local exec = helpers.exec
local feed = helpers.feed
-local sleep = helpers.sleep
+local poke_eventloop = helpers.poke_eventloop
before_each(clear)
@@ -24,7 +24,7 @@ describe(':global', function()
]])
feed(':g/foo/norm :<C-V>;<CR>')
- sleep(10) -- Wait for :sleep to start
+ poke_eventloop() -- Wait for :sleep to start
feed('<C-C>')
screen:expect([[
^foo |
@@ -37,7 +37,7 @@ describe(':global', function()
-- Also test in Ex mode
feed('gQg/foo/norm :<C-V>;<CR>')
- sleep(10) -- Wait for :sleep to start
+ poke_eventloop() -- Wait for :sleep to start
feed('<C-C>')
screen:expect([[
{0: }|
diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua
index 456acc12b5..c1f23ab0a6 100644
--- a/test/functional/legacy/mapping_spec.lua
+++ b/test/functional/legacy/mapping_spec.lua
@@ -131,11 +131,11 @@ describe('mapping', function()
command('set selectmode=mouse')
command('nnoremap <LeftDrag> <LeftDrag><Cmd><CR>')
- sleep(10)
+ poke_eventloop()
meths.input_mouse('left', 'press', '', 0, 0, 0)
- sleep(10)
+ poke_eventloop()
meths.input_mouse('left', 'drag', '', 0, 0, 1)
- sleep(10)
+ poke_eventloop()
eq('s', eval('mode()'))
end)
@@ -144,22 +144,22 @@ describe('mapping', function()
command('inoremap <LeftDrag> <LeftDrag><Cmd>let g:dragged = 1<CR>')
feed('i')
- sleep(10)
+ poke_eventloop()
meths.input_mouse('left', 'press', '', 0, 0, 0)
- sleep(10)
+ poke_eventloop()
meths.input_mouse('left', 'drag', '', 0, 0, 1)
- sleep(10)
+ poke_eventloop()
eq(1, eval('g:dragged'))
eq('v', eval('mode()'))
feed([[<C-\><C-N>]])
command([[inoremap <LeftDrag> <LeftDrag><C-\><C-N>]])
feed('i')
- sleep(10)
+ poke_eventloop()
meths.input_mouse('left', 'press', '', 0, 0, 0)
- sleep(10)
+ poke_eventloop()
meths.input_mouse('left', 'drag', '', 0, 0, 1)
- sleep(10)
+ poke_eventloop()
eq('n', eval('mode()'))
end)
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
index 51c2406933..159cf7a551 100644
--- a/test/functional/legacy/messages_spec.lua
+++ b/test/functional/legacy/messages_spec.lua
@@ -337,6 +337,95 @@ describe('messages', function()
end)
end)
+ describe('mode is cleared when', function()
+ before_each(function()
+ screen = Screen.new(40, 6)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true}, -- ModeMsg
+ [3] = {bold = true, reverse=true}, -- StatusLine
+ })
+ screen:attach()
+ end)
+
+ -- oldtest: Test_mode_message_at_leaving_insert_by_ctrl_c()
+ it('leaving Insert mode with Ctrl-C vim-patch:8.1.1189', function()
+ exec([[
+ func StatusLine() abort
+ return ""
+ endfunc
+ set statusline=%!StatusLine()
+ set laststatus=2
+ ]])
+ feed('i')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3: }|
+ {2:-- INSERT --} |
+ ]])
+ feed('<C-C>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3: }|
+ |
+ ]])
+ end)
+
+ -- oldtest: Test_mode_message_at_leaving_insert_with_esc_mapped()
+ it('leaving Insert mode with ESC in the middle of a mapping vim-patch:8.1.1192', function()
+ exec([[
+ set laststatus=2
+ inoremap <Esc> <Esc>00
+ ]])
+ feed('i')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] }|
+ {2:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+ end)
+
+ -- oldtest: Test_mode_updated_after_ctrl_c()
+ it('pressing Ctrl-C in i_CTRL-O', function()
+ feed('i<C-O>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- (insert) --} |
+ ]])
+ feed('<C-C>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+ end)
+
-- oldtest: Test_ask_yesno()
it('y/n prompt works', function()
screen = Screen.new(75, 6)
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 9b51af1eec..32c1615a45 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -144,13 +144,14 @@ describe('debug.debug', function()
before_each(function()
screen = Screen.new()
screen:attach()
- screen:set_default_attr_ids({
- [0] = {bold=true, foreground=255},
- E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
- cr = {bold = true, foreground = Screen.colors.SeaGreen4},
- })
- command("set display-=msgsep")
+ screen:set_default_attr_ids {
+ [0] = {bold=true, foreground=255};
+ [1] = {bold = true, reverse = true};
+ E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
+ cr = {bold = true, foreground = Screen.colors.SeaGreen4};
+ }
end)
+
it('works', function()
command([[lua
function Test(a)
@@ -160,9 +161,8 @@ describe('debug.debug', function()
end
]])
feed(':lua Test()\n')
- screen:expect([[
- {0:~ }|
- {0:~ }|
+ screen:expect{grid=[[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -173,11 +173,13 @@ describe('debug.debug', function()
{0:~ }|
{0:~ }|
{0:~ }|
+ {1: }|
nil |
lua_debug> ^ |
- ]])
+ ]]}
feed('print("TEST")\n')
screen:expect([[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -186,8 +188,7 @@ describe('debug.debug', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
nil |
lua_debug> print("TEST") |
TEST |
@@ -195,10 +196,10 @@ describe('debug.debug', function()
]])
feed('<C-c>')
screen:expect{grid=[[
+ |
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
nil |
lua_debug> print("TEST") |
TEST |
@@ -212,6 +213,7 @@ describe('debug.debug', function()
]]}
feed('<C-l>:lua Test()\n')
screen:expect([[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -222,19 +224,18 @@ describe('debug.debug', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
nil |
lua_debug> ^ |
]])
feed('\n')
screen:expect{grid=[[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
nil |
lua_debug> |
{E:E5108: Error executing lua [string ":lua"]:5: attempt}|
@@ -268,6 +269,7 @@ describe('debug.debug', function()
feed("conttt<cr>") -- misspelled cont; invalid syntax
screen:expect{grid=[[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -276,8 +278,7 @@ describe('debug.debug', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
lua_debug> conttt |
{E:E5115: Error while loading debug string: (debug comma}|
{E:nd):1: '=' expected near '<eof>'} |
@@ -286,14 +287,14 @@ describe('debug.debug', function()
feed("cont<cr>") -- exactly "cont", exit now
screen:expect{grid=[[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
lua_debug> conttt |
{E:E5115: Error while loading debug string: (debug comma}|
{E:nd):1: '=' expected near '<eof>'} |
diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua
index 5bdfec574e..fbaef3ae00 100644
--- a/test/functional/provider/clipboard_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -96,9 +96,9 @@ describe('clipboard', function()
[0] = {bold = true, foreground = Screen.colors.Blue},
[1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
[2] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [3] = {bold = true, reverse = true};
})
screen:attach()
- command("set display-=msgsep")
end)
it('unnamed register works without provider', function()
@@ -123,10 +123,10 @@ describe('clipboard', function()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | bogus_cmd | redir END')
screen:expect{grid=[[
- {0:~ }|
- clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
- {1:E492: Not an editor command: bogus_cmd | redir END} |
- {2:Press ENTER or type command to continue}^ |
+ {3: }|
+ clipboard: No provider. Try ":checkhealth" or ":h clipboard". |
+ {1:E492: Not an editor command: bogus_cmd | redir END} |
+ {2:Press ENTER or type command to continue}^ |
]]}
end)
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index 384761ab17..fa5771a8b3 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -24,7 +24,6 @@ before_each(function()
clear()
screen = Screen.new(40, 8)
screen:attach()
- command("set display-=msgsep")
source([[
highlight RBP1 guibg=Red
highlight RBP2 guibg=Yellow
@@ -152,6 +151,7 @@ before_each(function()
SB={foreground = Screen.colors.Blue4},
E={foreground = Screen.colors.Red, background = Screen.colors.Blue},
M={bold = true},
+ MSEP={bold = true, reverse = true};
})
end)
@@ -298,22 +298,22 @@ describe('Command-line coloring', function()
function()
set_color_cb('SplittedMultibyteStart')
start_prompt('echo "«')
- screen:expect([[
- {EOB:~ }|
- {EOB:~ }|
+ screen:expect{grid=[[
+ |
{EOB:~ }|
{EOB:~ }|
+ {MSEP: }|
:echo " |
{ERR:E5405: Chunk 0 start 7 splits multibyte }|
{ERR:character} |
:echo "«^ |
- ]])
+ ]]}
feed('»')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:echo " |
{ERR:E5405: Chunk 0 start 7 splits multibyte }|
{ERR:character} |
@@ -325,10 +325,10 @@ describe('Command-line coloring', function()
set_color_cb('SplittedMultibyteEnd')
start_prompt('echo "«')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:echo " |
{ERR:E5406: Chunk 0 end 7 splits multibyte ch}|
{ERR:aracter} |
@@ -339,10 +339,10 @@ describe('Command-line coloring', function()
set_color_cb('Echoerring')
start_prompt('e')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
: |
{ERR:E5407: Callback has thrown an exception:}|
{ERR: Vim(echoerr):HERE} |
@@ -398,10 +398,10 @@ describe('Command-line coloring', function()
set_color_cb('Throwing')
start_prompt('e')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
: |
{ERR:E5407: Callback has thrown an exception:}|
{ERR: ABC} |
@@ -412,10 +412,10 @@ describe('Command-line coloring', function()
set_color_cb('SplittedMultibyteStart')
start_prompt('let x = "«»«»«»«»«»"')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:let x = " |
{ERR:E5405: Chunk 0 start 10 splits multibyte}|
{ERR: character} |
@@ -453,10 +453,10 @@ describe('Command-line coloring', function()
screen:sleep(500)
feed('<C-c>')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
: |
{ERR:E5407: Callback has thrown an exception:}|
{ERR: Keyboard interrupt} |
@@ -517,11 +517,11 @@ describe('Command-line coloring', function()
set_color_cb('ReturningGlobal', '')
start_prompt('#')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
: |
{ERR:E5400: Callback should return list} |
:#^ |
@@ -531,11 +531,11 @@ describe('Command-line coloring', function()
set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}, 42})
start_prompt('#')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
: |
{ERR:E5401: List item 1 is not a List} |
:#^ |
@@ -545,10 +545,10 @@ describe('Command-line coloring', function()
set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1}})
start_prompt('+')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:+ |
{ERR:E5402: List item 1 has incorrect length:}|
{ERR: 1 /= 3} |
@@ -559,10 +559,10 @@ describe('Command-line coloring', function()
set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {2, 3, 'Normal'}})
start_prompt('+')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:+ |
{ERR:E5403: Chunk 1 start 2 not in range [1, }|
{ERR:2)} |
@@ -573,10 +573,10 @@ describe('Command-line coloring', function()
set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1, 3, 'Normal'}})
start_prompt('+')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:+ |
{ERR:E5404: Chunk 1 end 3 not in range (1, 2]}|
|
@@ -800,10 +800,10 @@ describe('Ex commands coloring', function()
it('does not crash when using `n` in debug mode', function()
feed(':debug execute "echo 1"\n')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
Entering Debug mode. Type "cont" to con|
tinue. |
cmd: execute "echo 1" |
@@ -811,8 +811,8 @@ describe('Ex commands coloring', function()
]])
feed('n\n')
screen:expect([[
- {EOB:~ }|
- {EOB:~ }|
+ |
+ {MSEP: }|
Entering Debug mode. Type "cont" to con|
tinue. |
cmd: execute "echo 1" |
@@ -836,10 +836,10 @@ describe('Ex commands coloring', function()
command("cnoremap <expr> x execute('throw 42')[-1]")
feed(':#x')
screen:expect([[
+ |
{EOB:~ }|
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:# |
{ERR:Error detected while processing :} |
{ERR:E605: Exception not caught: 42} |
@@ -847,9 +847,9 @@ describe('Ex commands coloring', function()
]])
feed('<CR>')
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
:# |
{ERR:Error detected while processing :} |
{ERR:E605: Exception not caught: 42} |
@@ -864,9 +864,9 @@ describe('Ex commands coloring', function()
meths.set_var('Nvim_color_cmdline', 42)
feed(':#')
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {MSEP: }|
: |
{ERR:E5408: Unable to get g:Nvim_color_cmdlin}|
{ERR:e callback: Vim:E6000: Argument is not a}|
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 68eebaf3a2..789f1c6487 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -193,7 +193,7 @@ describe('decorations providers', function()
|
]]}
- meths._set_hl_ns(ns1)
+ meths.set_hl_ns(ns1)
screen:expect{grid=[[
{10: 1 }{11:// just to see if there was an accid}|
{10: }{11:ent} |
@@ -219,7 +219,7 @@ describe('decorations providers', function()
local ns2 = a.nvim_create_namespace 'ns2'
a.nvim_set_decoration_provider (ns2, {
on_win = function (_, win, buf)
- a.nvim__set_hl_ns(win == thewin and _G.ns1 or ns2)
+ a.nvim_set_hl_ns_fast(win == thewin and _G.ns1 or ns2)
end;
})
]]
@@ -266,7 +266,7 @@ describe('decorations providers', function()
]]}
meths.set_hl(ns1, 'LinkGroup', {fg = 'Blue'})
- meths._set_hl_ns(ns1)
+ meths.set_hl_ns(ns1)
screen:expect{grid=[[
// just to see if there was an accident |
@@ -302,7 +302,7 @@ describe('decorations providers', function()
]]}
meths.set_hl(ns1, 'LinkGroup', {fg = 'Blue', default=true})
- meths._set_hl_ns(ns1)
+ meths.set_hl_ns(ns1)
feed 'k'
screen:expect{grid=[[
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index e065a727f3..4e3d62509c 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -3,9 +3,10 @@ local Screen = require('test.functional.ui.screen')
local os = require('os')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local command, exec = helpers.command, helpers.exec
-local eval, exc_exec = helpers.eval, helpers.exc_exec
+local eval = helpers.eval
local feed_command, eq = helpers.feed_command, helpers.eq
local curbufmeths = helpers.curbufmeths
+local meths = helpers.meths
describe('colorscheme compatibility', function()
before_each(function()
@@ -92,16 +93,22 @@ describe('highlight defaults', function()
before_each(function()
clear()
screen = Screen.new()
+ screen:set_default_attr_ids {
+ [0] = {bold=true, foreground=Screen.colors.Blue};
+ [1] = {reverse = true, bold = true};
+ [2] = {reverse = true};
+ [3] = {bold = true};
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen};
+ [5] = {foreground = Screen.colors.Red1, background = Screen.colors.WebGreen};
+ [6] = {background = Screen.colors.Red1, foreground = Screen.colors.Grey100};
+ [7] = {foreground = Screen.colors.Red};
+ [8] = {foreground = Screen.colors.Blue};
+ [9] = {italic = true};
+ }
screen:attach()
- command("set display-=msgsep")
end)
it('window status bar', function()
- screen:set_default_attr_ids({
- [0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {reverse = true, bold = true}, -- StatusLine
- [2] = {reverse = true} -- StatusLineNC
- })
feed_command('sp', 'vsp', 'vsp')
screen:expect([[
^ │ │ |
@@ -200,31 +207,29 @@ describe('highlight defaults', function()
^ |
{0:~ }|
{0:~ }|
- {1:-- INSERT --} |
- ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {bold = true}})
+ {3:-- INSERT --} |
+ ]])
end)
it('end of file markers', function()
screen:try_resize(53, 4)
screen:expect([[
^ |
- {1:~ }|
- {1:~ }|
+ {0:~ }|
+ {0:~ }|
|
- ]], {[1] = {bold = true, foreground = Screen.colors.Blue}})
+ ]])
end)
it('"wait return" text', function()
screen:try_resize(53, 4)
feed(':ls<cr>')
screen:expect([[
- {0:~ }|
+ {1: }|
:ls |
1 %a "[No Name]" line 1 |
- {1:Press ENTER or type command to continue}^ |
- ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {bold = true, foreground = Screen.colors.SeaGreen}})
+ {4:Press ENTER or type command to continue}^ |
+ ]])
feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
end)
@@ -237,8 +242,7 @@ describe('highlight defaults', function()
{0:~ }|
{0:~ }|
-- INSERT -- |
- ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {bold=true}})
+ ]])
feed('<esc>')
feed_command('highlight CustomHLGroup guifg=red guibg=green')
feed_command('highlight link ModeMsg CustomHLGroup')
@@ -247,9 +251,8 @@ describe('highlight defaults', function()
^ |
{0:~ }|
{0:~ }|
- {1:-- INSERT --} |
- ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}})
+ {5:-- INSERT --} |
+ ]])
end)
it('can be cleared by assigning NONE', function()
@@ -258,14 +261,11 @@ describe('highlight defaults', function()
feed_command('hi link TmpKeyword ErrorMsg')
insert('neovim')
screen:expect([[
- {1:neovi^m} |
+ {6:neovi^m} |
{0:~ }|
{0:~ }|
|
- ]], {
- [0] = {bold=true, foreground=Screen.colors.Blue},
- [1] = {foreground = Screen.colors.White, background = Screen.colors.Red}
- })
+ ]])
feed_command("hi ErrorMsg term=NONE cterm=NONE ctermfg=NONE ctermbg=NONE"
.. " gui=NONE guifg=NONE guibg=NONE guisp=NONE")
screen:expect([[
@@ -273,7 +273,7 @@ describe('highlight defaults', function()
{0:~ }|
{0:~ }|
|
- ]], {[0] = {bold=true, foreground=Screen.colors.Blue}})
+ ]])
end)
it('linking updates window highlight immediately #16552', function()
@@ -283,7 +283,7 @@ describe('highlight defaults', function()
{0:~ }|
{0:~ }|
|
- ]], {[0] = {bold=true, foreground=Screen.colors.Blue}})
+ ]])
feed_command("hi NonTextAlt guifg=Red")
feed_command("hi! link NonText NonTextAlt")
screen:expect([[
@@ -305,56 +305,44 @@ describe('highlight defaults', function()
feed_command('set listchars=space:.,tab:>-,trail:*,eol:¬ list')
insert(' ne \t o\tv im ')
screen:expect([[
- ne{0:.>----.}o{0:>-----}v{0:..}im{0:*^*¬} |
- {0:~ }|
- {0:~ }|
+ ne{7:.>----.}o{7:>-----}v{7:..}im{7:*^*¬} |
+ {7:~ }|
+ {7:~ }|
|
- ]], {
- [0] = {foreground=Screen.colors.Red},
- [1] = {foreground=Screen.colors.Blue},
- })
+ ]])
feed_command('highlight Whitespace gui=NONE guifg=#0000FF')
screen:expect([[
- ne{1:.>----.}o{1:>-----}v{1:..}im{1:*^*}{0:¬} |
- {0:~ }|
- {0:~ }|
+ ne{8:.>----.}o{8:>-----}v{8:..}im{8:*^*}{7:¬} |
+ {7:~ }|
+ {7:~ }|
:highlight Whitespace gui=NONE guifg=#0000FF |
- ]], {
- [0] = {foreground=Screen.colors.Red},
- [1] = {foreground=Screen.colors.Blue},
- })
+ ]])
end)
it('are sent to UIs', function()
screen:try_resize(53, 4)
- screen:set_default_attr_ids({
- [0] = {},
- [1] = {bold = true, foreground = Screen.colors.Blue1},
- [2] = {bold = true, reverse = true},
- [3] = {italic=true}
- })
screen:expect{grid=[[
^ |
- {1:~ }|
- {1:~ }|
+ {0:~ }|
+ {0:~ }|
|
- ]], hl_groups={EndOfBuffer=1, MsgSeparator=2}}
+ ]], hl_groups={EndOfBuffer=0, MsgSeparator=1}}
command('highlight EndOfBuffer gui=italic')
screen:expect{grid=[[
^ |
- {3:~ }|
- {3:~ }|
+ {9:~ }|
+ {9:~ }|
|
- ]], hl_groups={EndOfBuffer=3, MsgSeparator=2}}
+ ]], hl_groups={EndOfBuffer=9, MsgSeparator=1}}
command('highlight clear EndOfBuffer')
screen:expect{grid=[[
^ |
- {1:~ }|
- {1:~ }|
+ {0:~ }|
+ {0:~ }|
|
- ]], hl_groups={EndOfBuffer=1, MsgSeparator=2}}
+ ]], hl_groups={EndOfBuffer=0, MsgSeparator=1}}
end)
end)
@@ -1787,6 +1775,7 @@ describe("'winhighlight' highlight", function()
[26] = {background = Screen.colors.Red},
[27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1},
[28] = {bold = true, foreground = Screen.colors.Brown},
+ [29] = {foreground = Screen.colors.Blue1, background = Screen.colors.Red, bold = true};
})
command("hi Background1 guibg=DarkBlue")
command("hi Background2 guibg=DarkGreen")
@@ -1820,7 +1809,7 @@ describe("'winhighlight' highlight", function()
]])
end)
- it('handles invalid values', function()
+ it('handles undefined groups', function()
command("set winhl=Normal:Background1")
screen:expect([[
{1:^ }|
@@ -1833,19 +1822,44 @@ describe("'winhighlight' highlight", function()
|
]])
- eq('Vim(set):E474: Invalid argument: winhl=xxx:yyy',
- exc_exec("set winhl=xxx:yyy"))
- eq('Normal:Background1', eval('&winhl'))
+ command("set winhl=xxx:yyy")
+ eq('xxx:yyy', eval('&winhl'))
screen:expect{grid=[[
- {1:^ }|
- {2:~ }|
- {2:~ }|
- {2:~ }|
- {2:~ }|
- {2:~ }|
- {2:~ }|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
- ]], unchanged=true}
+ ]]}
+ end)
+
+ it('can be changed to define different groups', function()
+ command("set winhl=EndOfBuffer:Background1")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ command("set winhl=Normal:ErrorMsg")
+ screen:expect{grid=[[
+ {15:^ }|
+ {29:~ }|
+ {29:~ }|
+ {29:~ }|
+ {29:~ }|
+ {29:~ }|
+ {29:~ }|
+ |
+ ]]}
end)
it('works local to the window', function()
@@ -2270,4 +2284,191 @@ describe("'winhighlight' highlight", function()
|
]])
end)
+
+
+ it("can override syntax groups", function()
+ command('syntax on')
+ command('syntax keyword Foobar foobar')
+ command('syntax keyword Article the')
+ command('hi Foobar guibg=#FF0000')
+ command('hi Article guifg=#00FF00 gui=bold')
+ insert('the foobar was foobar')
+ screen:expect([[
+ {25:the} {26:foobar} was {26:fooba}|
+ {26:^r} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ command('split')
+ command('set winhl=Foobar:Background1,Article:ErrorMsg')
+ screen:expect{grid=[[
+ {15:the} {1:foobar} was {1:fooba}|
+ {1:^r} |
+ {0:~ }|
+ {3:[No Name] [+] }|
+ {25:the} {26:foobar} was {26:fooba}|
+ {26:r} |
+ {4:[No Name] [+] }|
+ |
+ ]]}
+ end)
+
+ it('can be disabled in newly opened window #19823', function()
+ command('split | set winhl=Normal:ErrorMsg | set winhl=')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ |
+ {0:~ }|
+ {4:[No Name] }|
+ |
+ ]]}
+
+ helpers.assert_alive()
+ end)
+end)
+
+describe('highlight namespaces', function()
+ local screen
+ local ns1, ns2
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25,10)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {foreground = Screen.colors.Blue, bold = true};
+ [2] = {background = Screen.colors.DarkGrey};
+ [3] = {italic = true, foreground = Screen.colors.DarkCyan, background = Screen.colors.DarkOrange4};
+ [4] = {background = Screen.colors.Magenta4};
+ [5] = {background = Screen.colors.Magenta4, foreground = Screen.colors.Crimson};
+ [6] = {bold = true, reverse = true};
+ [7] = {reverse = true};
+ [8] = {foreground = Screen.colors.Gray20};
+ }
+
+ ns1 = meths.create_namespace 'grungy'
+ ns2 = meths.create_namespace 'ultrared'
+
+ meths.set_hl(ns1, 'Normal', {bg='DarkGrey'})
+ meths.set_hl(ns1, 'NonText', {bg='DarkOrange4', fg='DarkCyan', italic=true})
+ meths.set_hl(ns2, 'Normal', {bg='DarkMagenta'})
+ meths.set_hl(ns2, 'NonText', {fg='Crimson'})
+ end)
+
+ it('can be used globally', function()
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ meths.set_hl_ns(ns1)
+ screen:expect{grid=[[
+ {2:^ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]]}
+
+ meths.set_hl_ns(ns2)
+ screen:expect{grid=[[
+ {4:^ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ |
+ ]]}
+
+ meths.set_hl_ns(0)
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it('can be used per window', function()
+ local win1 = meths.get_current_win()
+ command 'split'
+ local win2 = meths.get_current_win()
+ command 'split'
+
+ meths.win_set_hl_ns(win1, ns1)
+ meths.win_set_hl_ns(win2, ns2)
+
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {6:[No Name] }|
+ {4: }|
+ {5:~ }|
+ {7:[No Name] }|
+ {2: }|
+ {3:~ }|
+ {7:[No Name] }|
+ |
+ ]]}
+ end)
+
+ it('redraws correctly when ns=0', function()
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ meths.set_hl(0, 'EndOfBuffer', {fg='#333333'})
+ screen:expect{grid=[[
+ ^ |
+ {8:~ }|
+ {8:~ }|
+ {8:~ }|
+ {8:~ }|
+ {8:~ }|
+ {8:~ }|
+ {8:~ }|
+ {8:~ }|
+ |
+ ]]}
+ end)
end)
diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua
index df7f34aa7f..dc74d6d401 100644
--- a/test/functional/ui/hlstate_spec.lua
+++ b/test/functional/ui/hlstate_spec.lua
@@ -119,13 +119,15 @@ describe('ext_hlstate detailed highlights', function()
[3] = {{bold = true, reverse = true}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}},
[4] = {{reverse = true}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}},
[5] = {{background = Screen.colors.Red, foreground = Screen.colors.Grey100}, {{hi_name = "ErrorMsg", ui_name = "LineNr", kind = "ui"}}},
- [6] = {{bold = true, reverse = true}, {{hi_name = "MsgSeparator", ui_name = "Normal", kind = "ui"}}},
+ [6] = {{bold = true, reverse = true}, {{hi_name = "Normal", ui_name = "Normal", kind = "ui"}}},
[7] = {{foreground = Screen.colors.Brown, bold = true, reverse = true}, {6, 1}},
- [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 2}},
- [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "Statement", ui_name = "NormalNC", kind = "ui"}}},
+ [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 14}},
+ [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "NormalNC", ui_name = "NormalNC", kind = "ui"}}},
[10] = {{bold = true, foreground = Screen.colors.Brown}, {9, 1}},
- [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 2}},
+ [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 14}},
[12] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}},
+ [13] = {{background = Screen.colors.Red1, foreground = Screen.colors.Gray100}, {{ui_name = "LineNr", kind = "ui", hi_name = "LineNr"}}};
+ [14] = {{bold = true, foreground = Screen.colors.Blue}, {{ui_name = "EndOfBuffer", kind = "ui", hi_name = "EndOfBuffer"}}};
})
command("set number")
@@ -143,16 +145,16 @@ describe('ext_hlstate detailed highlights', function()
]])
command("set winhl=LineNr:ErrorMsg")
- screen:expect([[
- {5: 1 }^ |
- {2:~ }|
- {2:~ }|
+ screen:expect{grid=[[
+ {13: 1 }^ |
+ {14:~ }|
+ {14:~ }|
{3:[No Name] }|
{1: 1 } |
{2:~ }|
{4:[No Name] }|
{12: }|
- ]])
+ ]]}
command("set winhl=Normal:MsgSeparator,NormalNC:Statement")
screen:expect([[
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index d8dd546a8d..9ca4673efe 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -66,7 +66,6 @@ local function common_setup(screen, inccommand, text)
command("syntax on")
command("set nohlsearch")
command("hi Substitute guifg=red guibg=yellow")
- command("set display-=msgsep")
screen:attach()
screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.Fuchsia},
@@ -142,11 +141,11 @@ describe(":substitute, 'inccommand' preserves", function()
feed_command("ls")
screen:expect([[
+ BAC |
{15:~ }|
{15:~ }|
{15:~ }|
- {15:~ }|
- {15:~ }|
+ {11: }|
:ls |
1 %a + "[No Name]" |
line 1 |
@@ -1469,14 +1468,14 @@ describe("inccommand=nosplit", function()
-- non-modifier prefix
feed(':silent tabedit %s/tw/to')
screen:expect([[
+ Inc substitution on |
two lines |
Inc substitution on |
two lines |
|
{15:~ }|
{15:~ }|
- {15:~ }|
- {15:~ }|
+ {11: }|
:silent tabedit %s/t|
w/to^ |
]])
@@ -2656,6 +2655,7 @@ describe(":substitute", function()
feed("\\rѫ ab \\rXXXX")
screen:expect([[
+ 7 8 9 |
K L M |
{12:JLKR £} |
{12:ѫ ab } |
@@ -2667,8 +2667,7 @@ describe(":substitute", function()
{12:ѫ ab } |
{11:[No Name] [+] }|
| 7| {12:JLKR £} |
- | 8|{12: ѫ ab } |
- {10:[Preview] }|
+ {11: }|
:%s/[a-z]/JLKR £\rѫ ab \rXXX|
X^ |
]])
@@ -3001,8 +3000,8 @@ it('long :%s/ with inccommand does not collapse cmdline', function()
feed(':%s/AAAAAAA', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A')
screen:expect([[
- {15:~ }|
- {15:~ }|
+ |
+ {11: }|
:%s/AAAAAAAA|
AAAAAAAAAAAA|
AAAAAAA^ |
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 0f4e97088c..05d55b94fb 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -321,13 +321,14 @@ describe('input non-printable chars', function()
it("doesn't crash when echoing them back", function()
write_file("Xtest-overwrite", [[foobar]])
local screen = Screen.new(60,8)
- screen:set_default_attr_ids({
- [1] = {bold = true, foreground = Screen.colors.Blue1},
- [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
- [3] = {bold = true, foreground = Screen.colors.SeaGreen4}
- })
+ screen:set_default_attr_ids {
+ [1] = {bold = true, foreground = Screen.colors.Blue1};
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4};
+ [4] = {bold = true, reverse = true};
+ }
screen:attach()
- command("set display-=msgsep shortmess-=F")
+ command("set shortmess-=F")
feed_command("e Xtest-overwrite")
screen:expect([[
@@ -346,11 +347,11 @@ describe('input non-printable chars', function()
write_file("Xtest-overwrite", [[smurf]])
feed_command("w")
screen:expect([[
+ foobar |
{1:~ }|
{1:~ }|
{1:~ }|
- {1:~ }|
- {1:~ }|
+ {4: }|
"Xtest-overwrite" |
{2:WARNING: The file has been changed since reading it!!!} |
{3:Do you really want to write to it (y/n)?}^ |
@@ -358,10 +359,10 @@ describe('input non-printable chars', function()
feed("u")
screen:expect([[
+ foobar |
{1:~ }|
{1:~ }|
- {1:~ }|
- {1:~ }|
+ {4: }|
"Xtest-overwrite" |
{2:WARNING: The file has been changed since reading it!!!} |
{3:Do you really want to write to it (y/n)?}u |
@@ -370,9 +371,9 @@ describe('input non-printable chars', function()
feed("\005")
screen:expect([[
+ foobar |
{1:~ }|
- {1:~ }|
- {1:~ }|
+ {4: }|
"Xtest-overwrite" |
{2:WARNING: The file has been changed since reading it!!!} |
{3:Do you really want to write to it (y/n)?}u |
@@ -382,8 +383,8 @@ describe('input non-printable chars', function()
feed("n")
screen:expect([[
- {1:~ }|
- {1:~ }|
+ foobar |
+ {4: }|
"Xtest-overwrite" |
{2:WARNING: The file has been changed since reading it!!!} |
{3:Do you really want to write to it (y/n)?}u |
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index e7eaedba2d..2cff7c1cf4 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -1243,6 +1243,19 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
|
]])
end)
+
+ it('echo messages are shown correctly when getchar() immediately follows', function()
+ feed([[:echo 'foo' | echo 'bar' | call getchar()<CR>]])
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3: }|
+ foo |
+ bar^ |
+ ]])
+ end)
end)
describe('ui/ext_messages', function()
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index e389b7ab89..9896b11218 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -32,7 +32,7 @@ describe('ui/mouse/input', function()
[6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
[7] = {bold = true, foreground = Screen.colors.SeaGreen4},
})
- command("set display-=msgsep mousemodel=extend")
+ command("set mousemodel=extend")
feed('itesting<cr>mouse<cr>support and selection<esc>')
screen:expect([[
testing |
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
index 71c6410013..9bb067ed8e 100644
--- a/test/functional/ui/output_spec.lua
+++ b/test/functional/ui/output_spec.lua
@@ -100,45 +100,52 @@ describe("shell command :!", function()
pending('missing printf')
end
local screen = Screen.new(50, 4)
+ screen:set_default_attr_ids {
+ [1] = {bold = true, reverse = true};
+ [2] = {bold = true, foreground = Screen.colors.SeaGreen};
+ [3] = {foreground = Screen.colors.Blue};
+ }
screen:attach()
- command("set display-=msgsep")
-- Print TAB chars. #2958
feed([[:!printf '1\t2\t3'<CR>]])
- screen:expect([[
- ~ |
+ screen:expect{grid=[[
+ {1: }|
:!printf '1\t2\t3' |
1 2 3 |
- Press ENTER or type command to continue^ |
- ]])
+ {2:Press ENTER or type command to continue}^ |
+ ]]}
feed([[<CR>]])
+
-- Print BELL control code. #4338
screen.bell = false
feed([[:!printf '\007\007\007\007text'<CR>]])
screen:expect{grid=[[
- ~ |
+ {1: }|
:!printf '\007\007\007\007text' |
text |
- Press ENTER or type command to continue^ |
+ {2:Press ENTER or type command to continue}^ |
]], condition=function()
eq(true, screen.bell)
end}
feed([[<CR>]])
+
-- Print BS control code.
feed([[:echo system('printf ''\010\n''')<CR>]])
screen:expect([[
- ~ |
- ^H |
+ {1: }|
+ {3:^H} |
|
- Press ENTER or type command to continue^ |
+ {2:Press ENTER or type command to continue}^ |
]])
feed([[<CR>]])
+
-- Print LF control code.
feed([[:!printf '\n'<CR>]])
screen:expect([[
:!printf '\n' |
|
|
- Press ENTER or type command to continue^ |
+ {2:Press ENTER or type command to continue}^ |
]])
feed([[<CR>]])
end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index ea98705394..6ee9e7b393 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -546,7 +546,7 @@ function Screen:_wait(check, flags)
return true
end
- run_session(self._session, flags.request_cb, notification_cb, nil, minimal_timeout)
+ local eof = run_session(self._session, flags.request_cb, notification_cb, nil, minimal_timeout)
if not did_flush then
err = "no flush received"
elseif not checked then
@@ -557,9 +557,9 @@ function Screen:_wait(check, flags)
end
end
- if not success_seen then
+ if not success_seen and not eof then
did_miminal_timeout = true
- run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout)
+ eof = run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout)
end
local did_warn = false
@@ -600,8 +600,10 @@ between asynchronous (feed(), nvim_input()) and synchronous API calls.
if err then
+ if eof then err = err..'\n\n'..eof[2] end
busted.fail(err, 3)
elseif did_warn then
+ if eof then print(eof[2]) end
local tb = debug.traceback()
local index = string.find(tb, '\n%s*%[C]')
print(string.sub(tb,1,index))
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
index f3735c8e4c..add5144e1b 100644
--- a/test/functional/ui/statusline_spec.lua
+++ b/test/functional/ui/statusline_spec.lua
@@ -394,7 +394,7 @@ describe('global statusline', function()
meths.input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 15, 10)
- eq(0, meths.get_option('cmdheight'))
+ eq(1, meths.get_option('cmdheight'))
meths.input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, meths.get_option('cmdheight'))
end)
diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua
index f23f00bcc5..2451da983e 100644
--- a/test/functional/vimscript/null_spec.lua
+++ b/test/functional/vimscript/null_spec.lua
@@ -135,7 +135,7 @@ describe('NULL', function()
null_test('does not make Neovim crash when v:oldfiles gets assigned to that', ':let v:oldfiles = L|oldfiles', 0)
null_expr_test('does not make complete() crash or error out',
'execute(":normal i\\<C-r>=complete(1, L)[-1]\\n")',
- '', '\n', function()
+ 0, '', function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0)
diff --git a/test/unit/indent_spec.lua b/test/unit/indent_spec.lua
new file mode 100644
index 0000000000..ec86822b55
--- /dev/null
+++ b/test/unit/indent_spec.lua
@@ -0,0 +1,30 @@
+local helpers = require("test.unit.helpers")(after_each)
+local itp = helpers.gen_itp(it)
+
+local eq = helpers.eq
+
+local indent = helpers.cimport("./src/nvim/indent.h")
+local globals = helpers.cimport("./src/nvim/globals.h")
+
+describe('get_sts_value', function()
+ itp([[returns 'softtabstop' when it is non-negative]], function()
+ globals.curbuf.b_p_sts = 5
+ eq(5, indent.get_sts_value())
+
+ globals.curbuf.b_p_sts = 0
+ eq(0, indent.get_sts_value())
+ end)
+
+ itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function()
+ local shiftwidth = 2
+ globals.curbuf.b_p_sw = shiftwidth
+ local tabstop = 5
+ globals.curbuf.b_p_ts = tabstop
+ globals.curbuf.b_p_sts = -2
+ eq(shiftwidth, indent.get_sts_value())
+
+ shiftwidth = 0
+ globals.curbuf.b_p_sw = shiftwidth
+ eq(tabstop, indent.get_sts_value())
+ end)
+end)
diff --git a/test/unit/option_spec.lua b/test/unit/option_spec.lua
index b8b8a435bc..b3c3718035 100644
--- a/test/unit/option_spec.lua
+++ b/test/unit/option_spec.lua
@@ -5,7 +5,6 @@ local to_cstr = helpers.to_cstr
local eq = helpers.eq
local option = helpers.cimport("./src/nvim/option.h")
-local globals = helpers.cimport("./src/nvim/globals.h")
local check_ff_value = function(ff)
return option.check_ff_value(to_cstr(ff))
@@ -27,26 +26,3 @@ describe('check_ff_value', function()
eq(0, check_ff_value("foo"))
end)
end)
-
-describe('get_sts_value', function()
- itp([[returns 'softtabstop' when it is non-negative]], function()
- globals.curbuf.b_p_sts = 5
- eq(5, option.get_sts_value())
-
- globals.curbuf.b_p_sts = 0
- eq(0, option.get_sts_value())
- end)
-
- itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function()
- local shiftwidth = 2
- globals.curbuf.b_p_sw = shiftwidth
- local tabstop = 5
- globals.curbuf.b_p_ts = tabstop
- globals.curbuf.b_p_sts = -2
- eq(shiftwidth, option.get_sts_value())
-
- shiftwidth = 0
- globals.curbuf.b_p_sw = shiftwidth
- eq(tabstop, option.get_sts_value())
- end)
-end)