aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/nightly.yaml49
-rw-r--r--.github/workflows/release.yml6
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt11
-rw-r--r--cmake/UninstallHelper.cmake.in21
-rw-r--r--config/CMakeLists.txt1
-rw-r--r--config/config.h.in1
-rw-r--r--contrib/flake.nix16
-rw-r--r--runtime/autoload/haskellcomplete.vim5
-rw-r--r--runtime/autoload/provider/clipboard.vim6
-rw-r--r--runtime/doc/api.txt103
-rw-r--r--runtime/doc/change.txt10
-rw-r--r--runtime/doc/editing.txt4
-rw-r--r--runtime/doc/eval.txt68
-rw-r--r--runtime/doc/insert.txt6
-rw-r--r--runtime/doc/lsp.txt45
-rw-r--r--runtime/doc/lua.txt12
-rw-r--r--runtime/doc/map.txt18
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt8
-rw-r--r--runtime/doc/options.txt48
-rw-r--r--runtime/doc/starting.txt2
-rw-r--r--runtime/doc/syntax.txt3
-rw-r--r--runtime/doc/tagsrch.txt27
-rw-r--r--runtime/doc/treesitter.txt6
-rw-r--r--runtime/doc/usr_41.txt1
-rw-r--r--runtime/doc/vim_diff.txt2
-rw-r--r--runtime/filetype.vim14
-rw-r--r--runtime/indent/tcl.vim4
-rw-r--r--runtime/lua/vim/lsp.lua123
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua10
-rw-r--r--runtime/lua/vim/lsp/log.lua4
-rw-r--r--runtime/lua/vim/lsp/protocol.lua13
-rw-r--r--runtime/lua/vim/lsp/rpc.lua9
-rw-r--r--runtime/lua/vim/lsp/util.lua264
-rw-r--r--runtime/lua/vim/shared.lua14
-rw-r--r--runtime/lua/vim/treesitter.lua8
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua30
-rw-r--r--runtime/lua/vim/treesitter/query.lua54
-rw-r--r--runtime/menu.vim4
-rw-r--r--runtime/optwin.vim8
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim168
-rw-r--r--runtime/queries/c/highlights.scm17
-rw-r--r--runtime/syntax/cabal.vim30
-rw-r--r--runtime/syntax/cabalconfig.vim30
-rw-r--r--runtime/syntax/cabalproject.vim28
-rw-r--r--runtime/syntax/haskell.vim89
-rw-r--r--runtime/syntax/lsp_markdown.vim15
-rw-r--r--runtime/syntax/vim.vim2
-rwxr-xr-xscripts/gen_vimdoc.py28
-rwxr-xr-xscripts/lua2dox_filter2
-rw-r--r--src/nvim/CMakeLists.txt39
-rw-r--r--src/nvim/api/buffer.c91
-rw-r--r--src/nvim/api/private/helpers.c352
-rw-r--r--src/nvim/api/vim.c126
-rw-r--r--src/nvim/api/window.c29
-rw-r--r--src/nvim/autocmd.c28
-rw-r--r--src/nvim/buffer.c16
-rw-r--r--src/nvim/buffer_defs.h19
-rw-r--r--src/nvim/change.c40
-rw-r--r--src/nvim/channel.c17
-rw-r--r--src/nvim/charset.c11
-rw-r--r--src/nvim/decoration.c31
-rw-r--r--src/nvim/decoration.h17
-rw-r--r--src/nvim/edit.c106
-rw-r--r--src/nvim/eval.c96
-rw-r--r--src/nvim/eval.lua4
-rw-r--r--src/nvim/eval/funcs.c163
-rw-r--r--src/nvim/eval/typval.c32
-rw-r--r--src/nvim/eval/typval.h6
-rw-r--r--src/nvim/event/multiqueue.c6
-rw-r--r--src/nvim/ex_cmds.c88
-rw-r--r--src/nvim/ex_cmds2.c8
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/ex_getln.c26
-rw-r--r--src/nvim/ex_session.c3
-rw-r--r--src/nvim/fileio.c2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua14
-rw-r--r--src/nvim/generators/gen_eval.lua2
-rw-r--r--src/nvim/getchar.c502
-rw-r--r--src/nvim/globals.h18
-rw-r--r--src/nvim/grid_defs.h23
-rw-r--r--src/nvim/hardcopy.c7
-rw-r--r--src/nvim/highlight.c15
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/if_cscope.c2
-rw-r--r--src/nvim/indent.c102
-rw-r--r--src/nvim/indent_c.c628
-rw-r--r--src/nvim/lib/queue.h16
-rw-r--r--src/nvim/lua/executor.c11
-rw-r--r--src/nvim/lua/treesitter.c12
-rw-r--r--src/nvim/lua/vim.lua11
-rw-r--r--src/nvim/mbyte.c3
-rw-r--r--src/nvim/memline.c4
-rw-r--r--src/nvim/message.c37
-rw-r--r--src/nvim/mouse.c82
-rw-r--r--src/nvim/normal.c11
-rw-r--r--src/nvim/ops.c77
-rw-r--r--src/nvim/option.c527
-rw-r--r--src/nvim/option_defs.h6
-rw-r--r--src/nvim/options.lua18
-rw-r--r--src/nvim/os/input.c20
-rw-r--r--src/nvim/os/pty_process_win.c18
-rw-r--r--src/nvim/os/shell.c8
-rw-r--r--src/nvim/os/time.c16
-rw-r--r--src/nvim/popupmnu.c20
-rw-r--r--src/nvim/quickfix.c9
-rw-r--r--src/nvim/regexp.c3
-rw-r--r--src/nvim/regexp_nfa.c4
-rw-r--r--src/nvim/screen.c251
-rw-r--r--src/nvim/state.c28
-rw-r--r--src/nvim/syntax.c58
-rw-r--r--src/nvim/tag.c19
-rw-r--r--src/nvim/terminal.c40
-rw-r--r--src/nvim/testdir/Makefile11
-rw-r--r--src/nvim/testdir/check.vim27
-rw-r--r--src/nvim/testdir/runtest.vim8
-rw-r--r--src/nvim/testdir/script_util.vim69
-rw-r--r--src/nvim/testdir/test_alot.vim3
-rw-r--r--src/nvim/testdir/test_alot_latin.vim3
-rw-r--r--src/nvim/testdir/test_assert.vim31
-rw-r--r--src/nvim/testdir/test_autochdir.vim8
-rw-r--r--src/nvim/testdir/test_autocmd.vim38
-rw-r--r--src/nvim/testdir/test_breakindent.vim609
-rw-r--r--src/nvim/testdir/test_cmdline.vim15
-rw-r--r--src/nvim/testdir/test_cursor_func.vim5
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim2
-rw-r--r--src/nvim/testdir/test_filetype.vim5
-rw-r--r--src/nvim/testdir/test_functions.vim34
-rw-r--r--src/nvim/testdir/test_gn.vim4
-rw-r--r--src/nvim/testdir/test_highlight.vim100
-rw-r--r--src/nvim/testdir/test_ins_complete.vim28
-rw-r--r--src/nvim/testdir/test_listchars.vim29
-rw-r--r--src/nvim/testdir/test_listlbr.vim6
-rw-r--r--src/nvim/testdir/test_mapping.vim24
-rw-r--r--src/nvim/testdir/test_messages.vim3
-rw-r--r--src/nvim/testdir/test_mksession.vim14
-rw-r--r--src/nvim/testdir/test_options.vim103
-rw-r--r--src/nvim/testdir/test_popup.vim2
-rw-r--r--src/nvim/testdir/test_quickfix.vim2
-rw-r--r--src/nvim/testdir/test_quotestar.vim2
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim16
-rw-r--r--src/nvim/testdir/test_registers.vim2
-rw-r--r--src/nvim/testdir/test_rename.vim119
-rw-r--r--src/nvim/testdir/test_search.vim73
-rw-r--r--src/nvim/testdir/test_signs.vim7
-rw-r--r--src/nvim/testdir/test_startup.vim250
-rw-r--r--src/nvim/testdir/test_statusline.vim37
-rw-r--r--src/nvim/testdir/test_substitute.vim9
-rw-r--r--src/nvim/testdir/test_syntax.vim4
-rw-r--r--src/nvim/testdir/test_system.vim4
-rw-r--r--src/nvim/testdir/test_tab.vim45
-rw-r--r--src/nvim/testdir/test_tabpage.vim3
-rw-r--r--src/nvim/testdir/test_tagfunc.vim38
-rw-r--r--src/nvim/testdir/test_tagjump.vim289
-rw-r--r--src/nvim/testdir/test_taglist.vim96
-rw-r--r--src/nvim/testdir/test_timers.vim4
-rw-r--r--src/nvim/testdir/test_undo.vim6
-rw-r--r--src/nvim/testdir/test_vartabs.vim381
-rw-r--r--src/nvim/testdir/test_visual.vim2
-rw-r--r--src/nvim/testdir/test_window_cmd.vim66
-rw-r--r--src/nvim/ui.c1
-rw-r--r--src/nvim/ui_compositor.c32
-rw-r--r--src/nvim/undo.c114
-rw-r--r--src/nvim/window.c346
-rw-r--r--test/functional/api/vim_spec.lua63
-rw-r--r--test/functional/api/window_spec.lua40
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua75
-rw-r--r--test/functional/ex_cmds/excmd_spec.lua2
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua9
-rw-r--r--test/functional/fixtures/smile2.cat32
-rw-r--r--test/functional/legacy/memory_usage_spec.lua4
-rw-r--r--test/functional/legacy/options_spec.lua61
-rw-r--r--test/functional/lua/buffer_updates_spec.lua138
-rw-r--r--test/functional/lua/vim_spec.lua5
-rw-r--r--test/functional/plugin/lsp_spec.lua181
-rw-r--r--test/functional/treesitter/highlight_spec.lua98
-rw-r--r--test/functional/ui/cursor_spec.lua4
-rw-r--r--test/functional/ui/decorations_spec.lua280
-rw-r--r--test/functional/ui/diff_spec.lua20
-rw-r--r--test/functional/ui/float_spec.lua457
-rw-r--r--test/functional/ui/fold_spec.lua567
-rw-r--r--test/functional/ui/highlight_spec.lua6
-rw-r--r--test/functional/ui/input_spec.lua38
-rw-r--r--test/functional/ui/messages_spec.lua2
-rw-r--r--test/functional/ui/screen_basic_spec.lua44
-rw-r--r--test/functional/viml/errorlist_spec.lua13
-rw-r--r--third-party/CMakeLists.txt13
189 files changed, 8751 insertions, 1969 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 44a911b21b..bd90aeb932 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,6 +28,7 @@ jobs:
runner: macos-10.15
os: osx
runs-on: ${{ matrix.runner }}
+ if: github.event.pull_request.draft == false
env:
CC: ${{ matrix.cc }}
CI_OS_NAME: ${{ matrix.os }}
@@ -88,6 +89,7 @@ jobs:
windows:
runs-on: windows-2016
+ if: github.event.pull_request.draft == false
env:
DEPS_BUILD_DIR: "C:/projects/nvim-deps"
DEPS_PREFIX: "C:/projects/nvim-deps/usr"
diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml
new file mode 100644
index 0000000000..431ccd8b61
--- /dev/null
+++ b/.github/workflows/nightly.yaml
@@ -0,0 +1,49 @@
+name: Nightly
+on:
+ schedule:
+ - cron: '3 3 * * *'
+
+jobs:
+ update-vim-patches:
+ runs-on: ubuntu-20.04
+ env:
+ VIM_SOURCE_DIR: ${{ format('{0}/vim-src', github.workspace) }}
+ VERSION_BRANCH: marvim/ci-version-update
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - uses: actions/checkout@v2
+ with:
+ repository: vim/vim
+ path: ${{ env.VIM_SOURCE_DIR }}
+ fetch-depth: 0
+
+ - run: |
+ gh release download -R neovim/neovim -p nvim.appimage
+ chmod a+x nvim.appimage
+ mkdir -p $HOME/.local/bin
+ mv nvim.appimage $HOME/.local/bin/nvim
+ printf '%s\n' "$HOME/.local/bin" >> $GITHUB_PATH
+
+ - name: Setup git config
+ run: |
+ git config --global user.name 'marvim'
+ git config --global user.email 'marvim@users.noreply.github.com'
+
+ - name: Update src/version.c
+ id: update-version
+ run: |
+ git checkout -b ${VERSION_BRANCH}
+ nvim -i NONE -u NONE --headless +'luafile scripts/vimpatch.lua' +q
+ printf '::set-output name=NEW_PATCHES::%s\n' $([ -z "$(git diff)" ]; echo $?)
+
+ - name: Automatic PR
+ if: ${{ steps.update-version.outputs.NEW_PATCHES != 0 }}
+ run: |
+ git add -u
+ git commit -m 'version.c: update [skip ci]'
+ git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} ${VERSION_BRANCH}
+ gh pr create --fill --label vim-patch --base master --head ${VERSION_BRANCH} || true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 753142e555..43fe1d5101 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -26,7 +26,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential cmake gcc-multilib gettext gperf libtool-bin locales ninja-build pkg-config unzip
+ sudo apt-get install -y autoconf automake build-essential cmake gettext gperf libtool-bin locales ninja-build pkg-config unzip
- name: Build release
id: build
run: |
@@ -51,7 +51,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
- sudo apt-get install -y autoconf automake build-essential cmake gcc-multilib gettext gperf libtool-bin locales ninja-build pkg-config unzip
+ sudo apt-get install -y autoconf automake build-essential cmake gettext gperf libtool-bin locales ninja-build pkg-config unzip
- if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly')
run: make appimage-latest
- if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly')
@@ -156,7 +156,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- delete_release: ''
+ delete_release: true
tag_name: nightly
- uses: meeDamian/github-release@2.0
with:
diff --git a/.gitignore b/.gitignore
index 6004101cce..670340a519 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,7 +35,7 @@ compile_commands.json
/src/nvim/testdir/del
/src/nvim/testdir/test*.out
/src/nvim/testdir/test*.res
-/src/nvim/testdir/test.log
+/src/nvim/testdir/test*.log
/src/nvim/testdir/messages
/src/nvim/testdir/viminfo
/src/nvim/testdir/test.ok
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4e427eea26..c22ab8dbae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -679,3 +679,14 @@ set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set(CPACK_NSIS_MODIFY_PATH ON)
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
include(CPack)
+
+#add uninstall target
+if(NOT TARGET uninstall)
+ configure_file(
+ "cmake/UninstallHelper.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/UninstallHelper.cmake"
+ IMMEDIATE @ONLY)
+
+ add_custom_target(uninstall
+ COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/UninstallHelper.cmake)
+endif()
diff --git a/cmake/UninstallHelper.cmake.in b/cmake/UninstallHelper.cmake.in
new file mode 100644
index 0000000000..c2d34d4796
--- /dev/null
+++ b/cmake/UninstallHelper.cmake.in
@@ -0,0 +1,21 @@
+if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+ message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+ message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+ if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+ exec_program(
+ "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+ OUTPUT_VARIABLE rm_out
+ RETURN_VALUE rm_retval
+ )
+ if(NOT "${rm_retval}" STREQUAL 0)
+ message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+ endif()
+ else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+ message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+ endif()
+endforeach()
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt
index 8a70d864c4..6b88c92cf0 100644
--- a/config/CMakeLists.txt
+++ b/config/CMakeLists.txt
@@ -52,6 +52,7 @@ check_function_exists(setsid HAVE_SETSID)
check_function_exists(sigaction HAVE_SIGACTION)
check_function_exists(strcasecmp HAVE_STRCASECMP)
check_function_exists(strncasecmp HAVE_STRNCASECMP)
+check_function_exists(strptime HAVE_STRPTIME)
# Symbols
check_symbol_exists(FD_CLOEXEC "fcntl.h" HAVE_FD_CLOEXEC)
diff --git a/config/config.h.in b/config/config.h.in
index 275bff79a0..502f84bbcf 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -33,6 +33,7 @@
#cmakedefine HAVE_STRCASECMP
#cmakedefine HAVE_STRINGS_H
#cmakedefine HAVE_STRNCASECMP
+#cmakedefine HAVE_STRPTIME
#cmakedefine HAVE_SYS_SDT_H
#cmakedefine HAVE_SYS_UTSNAME_H
#cmakedefine HAVE_SYS_WAIT_H
diff --git a/contrib/flake.nix b/contrib/flake.nix
index 08126a48e9..c86bba6809 100644
--- a/contrib/flake.nix
+++ b/contrib/flake.nix
@@ -42,12 +42,16 @@
disallowedReferences = [];
}));
- # for neovim developers, very slow
+ # for neovim developers, builds a slow binary
+ # huge closure size but aims at covering all scripts
# brings development tools as well
neovim-developer =
let
lib = nixpkgs.lib;
- pythonEnv = pkgs.python3;
+ pythonEnv = pkgs.python3.withPackages(ps: [
+ ps.msgpack
+ ps.flake8 # for 'make pylint'
+ ]);
luacheck = pkgs.luaPackages.luacheck;
in
(neovim-debug.override ({ doCheck = pkgs.stdenv.isLinux; })).overrideAttrs (oa: {
@@ -56,7 +60,7 @@
"-DMIN_LOG_LEVEL=0"
"-DENABLE_LTO=OFF"
"-DUSE_BUNDLED=OFF"
- ] ++ pkgs.stdenv.lib.optionals pkgs.stdenv.isLinux [
+ ] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
# https://github.com/google/sanitizers/wiki/AddressSanitizerFlags
# https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports
"-DCLANG_ASAN_UBSAN=ON"
@@ -66,7 +70,8 @@
pythonEnv
include-what-you-use # for scripts/check-includes.py
jq # jq for scripts/vim-patch.sh -r
- doxygen
+ shellcheck # for `make shlint`
+ doxygen # for script/gen_vimdoc.py
]);
shellHook = oa.shellHook + ''
@@ -102,6 +107,5 @@
defaultApp = apps.nvim;
devShell = pkgs.neovim-developer;
- }
- );
+ });
}
diff --git a/runtime/autoload/haskellcomplete.vim b/runtime/autoload/haskellcomplete.vim
index 520ab93700..48fbac7f9f 100644
--- a/runtime/autoload/haskellcomplete.vim
+++ b/runtime/autoload/haskellcomplete.vim
@@ -2,7 +2,7 @@
" Language: Haskell
" Maintainer: Daniel Campoverde <alx@sillybytes.net>
" URL: https://github.com/alx741/haskellcomplete.vim
-" Last Change: 2018 Aug 26
+" Last Change: 2019 May 14
" Usage: setlocal omnifunc=haskellcomplete#Complete
@@ -63,6 +63,7 @@ function! haskellcomplete#Complete(findstart, base)
call add(l:matches, extension)
endif
endfor
+ let b:completingLangExtension = 0
return l:matches
endif
@@ -78,6 +79,7 @@ function! haskellcomplete#Complete(findstart, base)
call add(l:matches, flag)
endif
endfor
+ let b:completingOptionsGHC = 0
return l:matches
endif
@@ -93,6 +95,7 @@ function! haskellcomplete#Complete(findstart, base)
call add(l:matches, module)
endif
endfor
+ let b:completingModule = 0
return l:matches
endif
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index c2195fa02d..07f37d604f 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -132,6 +132,12 @@ function! provider#clipboard#Executable() abort
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'win32yank'
+ elseif executable('termux-clipboard-set')
+ let s:copy['+'] = ['termux-clipboard-set']
+ let s:paste['+'] = ['termux-clipboard-get']
+ let s:copy['*'] = s:copy['+']
+ let s:paste['*'] = s:paste['+']
+ return 'termux-clipboard'
elseif !empty($TMUX) && executable('tmux')
let s:copy['+'] = ['tmux', 'load-buffer', '-']
let s:paste['+'] = ['tmux', 'save-buffer', '-']
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index fdc41af1d5..1e287281cf 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -538,6 +538,21 @@ nvim__screenshot({path}) *nvim__screenshot()*
Attributes: ~
{fast}
+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: ~
+ {fast}
+
+ Parameters: ~
+ {ns_id} the namespace to activate
+
nvim__stats() *nvim__stats()*
Gets internal stats.
@@ -599,6 +614,22 @@ nvim_call_function({fn}, {args}) *nvim_call_function()*
Return: ~
Result of the function call
+nvim_chan_send({chan}, {data}) *nvim_chan_send()*
+ Send data to channel `id` . For a job, it writes it to the
+ stdin of the process. For the stdio channel |channel-stdio|,
+ it writes to Nvim's stdout. For an internal terminal instance
+ (|nvim_open_term()|) it writes directly to terimal output. See
+ |channel-bytes| for more information.
+
+ This function writes raw data, not RPC messages. If the
+ channel was created with `rpc=true` then the channel expects
+ RPC messages, use |vim.rpcnotify()| and |vim.rpcrequest()|
+ instead.
+
+ Parameters: ~
+ {chan} id of the channel
+ {data} data to write. 8-bit clean: can contain NUL bytes.
+
nvim_command({command}) *nvim_command()*
Executes an ex-command.
@@ -1160,6 +1191,27 @@ nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()*
{log_level} The log level
{opts} Reserved for future use.
+nvim_open_term({buffer}, {opts}) *nvim_open_term()*
+ Open a terminal instance in a buffer
+
+ By default (and currently the only option) the terminal will
+ not be connected to an external process. Instead, input send
+ on the channel will be echoed directly by the terminal. This
+ is useful to disply ANSI terminal sequences returned as part
+ of a rpc message, or similar.
+
+ Note: to directly initiate the terminal using the right size,
+ display the buffer in a configured window before calling this.
+ For instance, for a floating display, first create an empty
+ buffer using |nvim_create_buf()|, then display it using
+ |nvim_open_win()|, and then call this function. Then
+ |nvim_chan_send()| cal be called immediately to process
+ sequences in a virtual terminal having the intended size.
+
+ Parameters: ~
+ {buffer} the buffer to use (expected to be empty)
+ {opts} Optional parameters. Reserved for future use.
+
nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
Open a new window.
@@ -1415,9 +1467,9 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()*
• "c" |charwise| mode
• "l" |linewise| mode
• "" guess by contents, see |setreg()|
- {after} Insert after cursor (like |p|), or before (like
- |P|).
- {follow} Place cursor at end of inserted text.
+ {after} If true insert after cursor (like |p|), or
+ before (like |P|).
+ {follow} If true place cursor at end of inserted text.
*nvim_replace_termcodes()*
nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
@@ -1623,21 +1675,6 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
keys are also recognized: `default` : don't
override existing definition, like `hi default`
-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: ~
- {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.
@@ -1737,7 +1774,7 @@ nvim__buf_stats({buffer}) *nvim__buf_stats()*
TODO: Documentation
*nvim_buf_add_highlight()*
-nvim_buf_add_highlight({buffer}, {src_id}, {hl_group}, {line}, {col_start},
+nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start},
{col_end})
Adds a highlight to buffer.
@@ -2202,6 +2239,19 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts})
column, without shifting the underlying
text.
+ • virt_text_hide : hide the virtual text when
+ the background text is selected or hidden due
+ to horizontal scroll 'nowrap'
+ • hl_mode : control how highlights are combined
+ with the highlights of the text. Currently
+ only affects virt_text highlights, but might
+ affect`hl_group`in later versions.
+ • "replace": only show the virt_text color.
+ This is the default
+ • "combine": combine with background text
+ color
+ • "blend": blend with background text color.
+
• ephemeral : for use with
|nvim_set_decoration_provider| callbacks. The
mark will only be used for the current redraw
@@ -2468,6 +2518,21 @@ nvim_win_get_width({window}) *nvim_win_get_width()*
Return: ~
Width as a count of columns
+nvim_win_hide({window}) *nvim_win_hide()*
+ Closes the window and hide the buffer it contains (like
+ |:hide| with a |window-ID|).
+
+ Like |:hide| the buffer becomes hidden unless another window
+ is editing it, or 'bufhidden' is `unload` , `delete` or `wipe`
+ as opposed to |:close| or |nvim_win_close|, which will close
+ the buffer.
+
+ Attributes: ~
+ not allowed when |textlock| is active
+
+ Parameters: ~
+ {window} Window handle, or 0 for current window
+
nvim_win_is_valid({window}) *nvim_win_is_valid()*
Checks if a window is valid
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index f3ed086933..310d244fbc 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -445,6 +445,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right*
*<*
<{motion} Shift {motion} lines one 'shiftwidth' leftwards.
+ If the 'shiftwidth' option is set to zero, the amount
+ of indent is calculated at the first non-blank
+ character in the line.
*<<*
<< Shift [count] lines one 'shiftwidth' leftwards.
@@ -455,6 +458,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right*
*>*
>{motion} Shift {motion} lines one 'shiftwidth' rightwards.
+ If the 'shiftwidth' option is set to zero, the amount
+ of indent is calculated at the first non-blank
+ character in the line.
*>>*
>> Shift [count] lines one 'shiftwidth' rightwards.
@@ -934,6 +940,10 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|.
this (that's a good habit anyway).
`:retab!` may also change a sequence of spaces by
<Tab> characters, which can mess up a printf().
+ A list of tab widths separated by commas may be used
+ in place of a single tabstop. Each value in the list
+ represents the width of one tabstop, except the final
+ value which applies to all following tabstops.
*retab-example*
Example for using autocommands and ":retab" to edit a file which is stored
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index ac398ec494..aa964a521f 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -90,7 +90,7 @@ g CTRL-G Prints the current position of the cursor in five
:buffers
:files
:ls List all the currently known file names. See
- 'windows.txt' |:files| |:buffers| |:ls|.
+ |windows.txt| |:files| |:buffers| |:ls|.
Vim will remember the full path name of a file name that you enter. In most
cases when the file name is displayed only the name you typed is shown, but
@@ -1190,7 +1190,7 @@ The syntax is best shown via some examples: >
< Open the browser in the C:/bar directory, with the current
buffer filename as default, and save the buffer under the
filename chosen.
-Also see the |'browsedir'| option.
+Also see the 'browsedir' option.
For versions of Vim where browsing is not supported, the command is executed
unmodified.
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index be7c026f5a..2911224de5 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -444,7 +444,7 @@ as a key.
To avoid having to put quotes around every key the #{} form can be used. This
does require the key to consist only of ASCII letters, digits, '-' and '_'.
Example: >
- let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3}
+ :let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3}
Note that 333 here is the string "333". Empty keys are not possible with #{}.
A value can be any expression. Using a Dictionary for a value creates a
@@ -2308,7 +2308,6 @@ perleval({expr}) any evaluate |perl| expression
pow({x}, {y}) Float {x} to the power of {y}
prevnonblank({lnum}) Number line nr of non-blank line <= {lnum}
printf({fmt}, {expr1}...) String format text
-prompt_addtext({buf}, {expr}) none add text to a prompt buffer
prompt_setcallback({buf}, {expr}) none set prompt callback function
prompt_setinterrupt({buf}, {text}) none set prompt interrupt function
prompt_setprompt({buf}, {text}) none set prompt text
@@ -2393,7 +2392,7 @@ sha256({string}) String SHA256 checksum of {string}
shellescape({string} [, {special}])
String escape {string} for use as shell
command argument
-shiftwidth() Number effective value of 'shiftwidth'
+shiftwidth([{col}]) Number effective value of 'shiftwidth'
sign_define({name} [, {dict}]) Number define or update a sign
sign_getdefined([{name}]) List get a list of defined signs
sign_getplaced([{expr} [, {dict}]])
@@ -2430,7 +2429,7 @@ strcharpart({str}, {start} [, {len}])
String {len} characters of {str} at
character {start}
strdisplaywidth({expr} [, {col}]) Number display length of the String {expr}
-strftime({format} [, {time}]) String time in specified format
+strftime({format} [, {time}]) String format time with a specified format
strgetchar({str}, {index}) Number get char {index} from {str}
stridx({haystack}, {needle} [, {start}])
Number index of {needle} in {haystack}
@@ -2439,6 +2438,8 @@ strlen({expr}) Number length of the String {expr}
strpart({str}, {start} [, {len} [, {chars}]])
String {len} bytes/chars of {str} at
byte {start}
+strptime({format}, {timestring})
+ Number Convert {timestring} to unix timestamp
strridx({haystack}, {needle} [, {start}])
Number last index of {needle} in {haystack}
strtrans({expr}) String translate string to make it printable
@@ -2498,6 +2499,8 @@ win_gotoid({expr}) Number go to |window-ID| {expr}
win_id2tabwin({expr}) List get tab and window nr from |window-ID|
win_id2win({expr}) Number get window nr from |window-ID|
win_screenpos({nr}) List get screen position of window {nr}
+win_splitmove({nr}, {target} [, {options}])
+ none move window {nr} to split of {target}
winbufnr({nr}) Number buffer number of window {nr}
wincol() Number window column of the cursor
winheight({nr}) Number height of window {nr}
@@ -4981,7 +4984,7 @@ getwininfo([{winid}]) *getwininfo()*
getwinpos([{timeout}]) *getwinpos()*
The result is a list with two numbers, the result of
- getwinposx() and getwinposy() combined:
+ |getwinposx()| and |getwinposy()| combined:
[x-pos, y-pos]
{timeout} can be used to specify how long to wait in msec for
a response from the terminal. When omitted 100 msec is used.
@@ -5851,7 +5854,7 @@ list2str({list} [, {utf8}]) *list2str()*
<
localtime() *localtime()*
Return the current time, measured as seconds since 1st Jan
- 1970. See also |strftime()| and |getftime()|.
+ 1970. See also |strftime()|, |strptime()| and |getftime()|.
log({expr}) *log()*
@@ -7895,7 +7898,7 @@ shellescape({string} [, {special}]) *shellescape()*
< See also |::S|.
-shiftwidth() *shiftwidth()*
+shiftwidth([{col}]) *shiftwidth()*
Returns the effective value of 'shiftwidth'. This is the
'shiftwidth' value unless it is zero, in which case it is the
'tabstop' value. To be backwards compatible in indent
@@ -7911,6 +7914,11 @@ shiftwidth() *shiftwidth()*
endif
< And then use s:sw() instead of &sw.
+ When there is one argument {col} this is used as column number
+ for which to return the 'shiftwidth' value. This matters for the
+ 'vartabstop' feature. If no {col} argument is given, column 1
+ will be assumed.
+
sign_define({name} [, {dict}]) *sign_define()*
Define a new sign named {name} or modify the attributes of an
existing sign. This is similar to the |:sign-define| command.
@@ -8486,7 +8494,7 @@ strftime({format} [, {time}]) *strftime()*
{format} depends on your system, thus this is not portable!
See the manual page of the C function strftime() for the
format. The maximum length of the result is 80 characters.
- See also |localtime()| and |getftime()|.
+ See also |localtime()|, |getftime()| and |strptime()|.
The language can be changed with the |:language| command.
Examples: >
:echo strftime("%c") Sun Apr 27 11:49:23 1997
@@ -8576,6 +8584,31 @@ strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
example, to get the character under the cursor: >
strpart(getline("."), col(".") - 1, 1, v:true)
<
+strptime({format}, {timestring}) *strptime()*
+ The result is a Number, which is a unix timestamp representing
+ the date and time in {timestring}, which is expected to match
+ the format specified in {format}.
+
+ The accepted {format} depends on your system, thus this is not
+ portable! See the manual page of the C function strptime()
+ for the format. Especially avoid "%c". The value of $TZ also
+ matters.
+
+ If the {timestring} cannot be parsed with {format} zero is
+ returned. If you do not know the format of {timestring} you
+ can try different {format} values until you get a non-zero
+ result.
+
+ See also |strftime()|.
+ Examples: >
+ :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23")
+< 862156163 >
+ :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55"))
+< Sun Apr 27 11:53:55 1997 >
+ :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600)
+< Sun Apr 27 12:53:55 1997
+
+
strridx({haystack}, {needle} [, {start}]) *strridx()*
The result is a Number, which gives the byte index in
{haystack} of the last occurrence of the String {needle}.
@@ -9370,6 +9403,25 @@ win_screenpos({nr}) *win_screenpos()*
Return [0, 0] if the window cannot be found in the current
tabpage.
+win_splitmove({nr}, {target} [, {options}]) *win_splitmove()*
+ Move the window {nr} to a new split of the window {target}.
+ This is similar to moving to {target}, creating a new window
+ using |:split| but having the same contents as window {nr}, and
+ then closing {nr}.
+
+ Both {nr} and {target} can be window numbers or |window-ID|s.
+
+ Returns zero for success, non-zero for failure.
+
+ {options} is a Dictionary with the following optional entries:
+ "vertical" When TRUE, the split is created vertically,
+ like with |:vsplit|.
+ "rightbelow" When TRUE, the split is made below or to the
+ right (if vertical). When FALSE, it is done
+ above or to the left (if vertical). When not
+ present, the values of 'splitbelow' and
+ 'splitright' are used.
+
*winbufnr()*
winbufnr({nr}) The result is a Number, which is the number of the buffer
associated with window {nr}. {nr} can be the window number or
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index c4b93a2a27..6902ed5fd4 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -287,8 +287,7 @@ character is written at the end of each line. Thus if you want to insert a
*i_CTRL-X* *insert_expand*
CTRL-X enters a sub-mode where several commands can be used. Most of these
-commands do keyword completion; see |ins-completion|. These are not available
-when Vim was compiled without the |+insert_expand| feature.
+commands do keyword completion; see |ins-completion|.
Two commands can be used to scroll the window up or down, without exiting
insert mode:
@@ -592,9 +591,6 @@ In Insert and Replace mode, there are several commands to complete part of a
keyword or line that has been typed. This is useful if you are using
complicated keywords (e.g., function names with capitals and underscores).
-These commands are not available when the |+insert_expand| feature was
-disabled at compile time.
-
Completion can be done for:
1. Whole lines |i_CTRL-X_CTRL-L|
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index d2d88fb9ba..67a10c7efb 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -749,15 +749,6 @@ start_client({config}) *vim.lsp.start_client()*
The following parameters describe fields in the {config}
table.
->
-
- -- In attach function for the client, you can do:
- local custom_attach = function(client)
- if client.config.flags then
- client.config.flags.allow_incremental_sync = true
- end
- end
-<
Parameters: ~
{root_dir} (required, string) Directory where the
@@ -799,6 +790,8 @@ start_client({config}) *vim.lsp.start_client()*
See `initialize` in the LSP spec.
{name} (string, default=client-id) Name in log
messages.
+ {get_language_id} function(bufnr, filetype) -> language
+ ID as string. Defaults to the filetype.
{offset_encoding} (default="utf-16") One of "utf-8",
"utf-16", or "utf-32" which is the
encoding that the LSP server expects.
@@ -854,8 +847,8 @@ start_client({config}) *vim.lsp.start_client()*
{flags} A table with flags for the client. The
current (experimental) flags are:
• allow_incremental_sync (bool, default
- false): Allow using on_line callbacks
- for lsp
+ true): Allow using incremental sync
+ for buffer edits
Return: ~
Client id. |vim.lsp.get_client_by_id()| Note: client may
@@ -1469,8 +1462,8 @@ show_line_diagnostics({opts}, {bufnr}, {line_nr}, {client_id})
==============================================================================
Lua module: vim.lsp.handlers *lsp-handlers*
- *vim.lsp.handlers.progress_callback()*
-progress_callback({_}, {_}, {params}, {client_id})
+ *vim.lsp.handlers.progress_handler()*
+progress_handler({_}, {_}, {params}, {client_id})
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand
@@ -1547,6 +1540,18 @@ close_preview_autocmd({events}, {winnr})
See also: ~
|autocmd-events|
+ *vim.lsp.util.compute_diff()*
+compute_diff({old_lines}, {new_lines}, {start_line_idx}, {end_line_idx})
+ Returns the range table for the difference between old and new
+ lines
+
+ Parameters: ~
+ {old_lines} table list of lines
+ {new_lines} table list of lines
+
+ Return: ~
+ table start_line_idx and start_col_idx of range
+
*vim.lsp.util.convert_input_to_markdown_lines()*
convert_input_to_markdown_lines({input}, {contents})
Converts any of `MarkedString` | `MarkedString[]` |
@@ -1581,6 +1586,12 @@ convert_signature_help_to_markdown_lines({signature_help})
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
+create_file({change}) *vim.lsp.util.create_file()*
+ TODO: Documentation
+
+delete_file({change}) *vim.lsp.util.delete_file()*
+ TODO: Documentation
+
*vim.lsp.util.extract_completion_items()*
extract_completion_items({result})
Can be used to extract the completion items from a `textDocument/completion` request, which may return one of `CompletionItem[]` , `CompletionList` or null.
@@ -1788,12 +1799,12 @@ make_workspace_params({added}, {removed})
{removed}
*vim.lsp.util.open_floating_preview()*
-open_floating_preview({contents}, {filetype}, {opts})
+open_floating_preview({contents}, {syntax}, {opts})
Shows contents in a floating window.
Parameters: ~
{contents} table of lines to show in window
- {filetype} string of filetype to set for opened buffer
+ {syntax} string of syntax to set for opened buffer
{opts} dictionary with optional fields
Return: ~
@@ -1824,6 +1835,10 @@ preview_location({location}) *vim.lsp.util.preview_location()*
(bufnr,winnr) buffer and window number of floating window
or nil
+rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()*
+ Parameters: ~
+ {opts} (table)
+
set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()*
Replaces text in a range with new text.
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 0bbed56662..c2fc25431c 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1055,6 +1055,18 @@ list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()*
See also: ~
|vim.tbl_extend()|
+list_slice({list}, {start}, {finish}) *vim.list_slice()*
+ Creates a copy of a table containing only elements from start
+ to end (inclusive)
+
+ Parameters: ~
+ {list} table table
+ {start} integer Start range of slice
+ {finish} integer End range of slice
+
+ Return: ~
+ Copy of table sliced from start to finish (inclusive)
+
pesc({s}) *vim.pesc()*
Escapes magic chars in a Lua pattern.
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index edec4a8de7..73ac3763d0 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -224,6 +224,20 @@ text before the cursor and start omni completion when some condition is met.
For abbreviations |v:char| is set to the character that was typed to trigger
the abbreviation. You can use this to decide how to expand the {lhs}. You
should not either insert or change the v:char.
+
+Also, keep in mind that the expression may be evaluated when looking for
+typeahead, before the previous command has been executed. For example: >
+ func StoreColumn()
+ let g:column = col('.')
+ return 'x'
+ endfunc
+ nnoremap <expr> x StoreColumn()
+ nmap ! f!x
+You will notice that g:column has the value from before executing "fx",
+because "z" is evaluated before "fx" is executed.
+This can be solved by inserting <Ignore> before the character that is
+expression-mapped: >
+ nmap ! f!<Ignore>x
Be very careful about side effects! The expression is evaluated while
obtaining characters, you may very well make the command dysfunctional.
@@ -276,7 +290,7 @@ as a special key.
*<Cmd>* *:map-cmd*
The <Cmd> pseudokey begins a "command mapping", which executes the command
directly (without changing modes). Where you might use ":...<CR>" in the
-{lhs} of a mapping, you can instead use "<Cmd>...<CR>".
+{rhs} of a mapping, you can instead use "<Cmd>...<CR>".
Example: >
noremap x <Cmd>echo mode(1)<cr>
<
@@ -300,7 +314,7 @@ Note:
*E5520*
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
-{lhs} of the mapping definition. |Command-line| mode is never entered.
+{rhs} of the mapping definition. |Command-line| mode is never entered.
1.3 MAPPING AND MODES *:map-modes*
diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt
index bec2b362ea..5885b20ab7 100644
--- a/runtime/doc/nvim_terminal_emulator.txt
+++ b/runtime/doc/nvim_terminal_emulator.txt
@@ -312,6 +312,8 @@ Other commands ~
*:Program* jump to the window with the running program
*:Source* jump to the window with the source code, create it if there
isn't one
+ *:Asm* jump to the window with the disassembly, create it if there
+ isn't one
Prompt mode ~
@@ -330,6 +332,12 @@ This works slightly differently:
Prompt mode can be used even when the |+terminal| feature is present with: >
let g:termdebug_use_prompt = 1
+<
+ *termdebug_disasm_window*
+If you want the Asm window shown by default, set this to 1. Setting to
+any value greater than 1 will set the Asm window height to that value: >
+ let g:termdebug_disasm_window = 15
+<
Communication ~
*termdebug-communication*
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index ce3788e0fc..04310ca8d4 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -3716,6 +3716,10 @@ A jump table for the options with a short description can be found at |Q_op|.
*lcs-space*
space:c Character to show for a space. When omitted, spaces
are left blank.
+ *lcs-lead*
+ lead:c Character to show for leading spaces. When omitted,
+ leading spaces are blank. Overrides the "space"
+ setting for leading spaces.
*lcs-trail*
trail:c Character to show for trailing spaces. When omitted,
trailing spaces are blank. Overrides the "space"
@@ -4090,8 +4094,6 @@ A jump table for the options with a short description can be found at |Q_op|.
In the "popup" model the right mouse button produces a pop-up menu.
You need to define this first, see |popup-menu|.
- In a terminal the popup menu works if Vim is compiled with the
- |+insert_expand| option.
Note that you can further refine the meaning of buttons with mappings.
See |mouse-overview|. But mappings are NOT used for modeless selection.
@@ -4858,7 +4860,9 @@ A jump table for the options with a short description can be found at |Q_op|.
local to window
Number of lines to scroll with CTRL-U and CTRL-D commands. Will be
set to half the number of lines in the window when the window size
- changes. If you give a count to the CTRL-U or CTRL-D command it will
+ changes. This may happen when enabling the |status-line| or
+ 'tabline' option after setting the 'scroll' option.
+ If you give a count to the CTRL-U or CTRL-D command it will
be used as the new value for 'scroll'. Reset to half the window
height with ":set scroll=0".
@@ -5621,6 +5625,9 @@ A jump table for the options with a short description can be found at |Q_op|.
The 'L' flag in 'cpoptions' changes how tabs are used when 'list' is
set.
+ The value of 'softtabstop' will be ignored if |'varsofttabstop'| is set
+ to anything other than an empty string.
+
*'spell'* *'nospell'*
'spell' boolean (default off)
local to window
@@ -6158,6 +6165,9 @@ A jump table for the options with a short description can be found at |Q_op|.
though. Otherwise aligned comments will be wrong when 'tabstop' is
changed.
+ The value of 'tabstop' will be ignored if |'vartabstop'| is set to
+ anything other than an empty string.
+
*'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'*
'tagbsearch' 'tbs' boolean (default on)
global
@@ -6542,6 +6552,38 @@ A jump table for the options with a short description can be found at |Q_op|.
written to disk (see |crash-recovery|). Also used for the
|CursorHold| autocommand event.
+ *'varsofttabstop'* *'vsts'*
+'varsofttabstop' 'vsts' string (default "")
+ local to buffer
+ A list of the number of spaces that a <Tab> counts for while editing,
+ such as inserting a <Tab> or using <BS>. It "feels" like variable-
+ width <Tab>s are being inserted, while in fact a mixture of spaces
+ and <Tab>s is used. Tab widths are separated with commas, with the
+ final value applying to all subsequent tabs.
+
+ For example, when editing assembly language files where statements
+ start in the 8th column and comments in the 40th, it may be useful
+ to use the following: >
+ :set varsofttabstop=8,32,8
+< This will set soft tabstops at the 8th and 40th columns, and at every
+ 8th column thereafter.
+
+ Note that the value of |'softtabstop'| will be ignored while
+ 'varsofttabstop' is set.
+
+ *'vartabstop'* *'vts'*
+'vartabstop' 'vts' string (default "")
+ local to buffer
+ A list of the number of spaces that a <Tab> in the file counts for,
+ separated by commas. Each value corresponds to one tab, with the
+ final value applying to all subsequent tabs. For example: >
+ :set vartabstop=4,20,10,8
+< This will make the first tab 4 spaces wide, the second 20 spaces,
+ the third 10 spaces, and all following tabs 8 spaces.
+
+ Note that the value of |'tabstop'| will be ignored while 'vartabstop'
+ is set.
+
*'verbose'* *'vbs'*
'verbose' 'vbs' number (default 0)
global
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 4a99aa47bf..d3647246fa 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -106,8 +106,6 @@ argument.
This can be used to find out where time is spent while loading
your |config|, plugins and opening the first file.
When {fname} already exists new messages are appended.
- (Only available when compiled with the |+startuptime|
- feature).
*-+*
+[num] The cursor will be positioned on line "num" for the first
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 7da886dabd..aeee02a1e0 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -1715,7 +1715,7 @@ There are several html preprocessor languages out there. html.vim has been
written such that it should be trivial to include it. To do so add the
following two lines to the syntax coloring file for that language
(the example comes from the asp.vim file):
-
+>
runtime! syntax/html.vim
syn cluster htmlPreproc add=asp
@@ -4689,6 +4689,7 @@ in their own color.
highlighting for groups added by the user!
Uses the current value of 'background' to decide which
default colors to use.
+ If there was a default link, restore it. |:hi-link|
:hi[ghlight] clear {group-name}
:hi[ghlight] {group-name} NONE
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index 23db809543..7d09ca86ac 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -337,11 +337,11 @@ the same as above, with a "p" prepended.
A static tag is a tag that is defined for a specific file. In a C program
this could be a static function.
-In Vi jumping to a tag sets the current search pattern. This means that
-the "n" command after jumping to a tag does not search for the same pattern
-that it did before jumping to the tag. Vim does not do this as we consider it
-to be a bug. You can still find the tag search pattern in the search history.
-If you really want the old Vi behavior, set the 't' flag in 'cpoptions'.
+In Vi jumping to a tag sets the current search pattern. This means that the
+"n" command after jumping to a tag does not search for the same pattern that
+it did before jumping to the tag. Vim does not do this as we consider it to
+be a bug. If you really want the old Vi behavior, set the 't' flag in
+'cpoptions'.
*tag-binary-search*
Vim uses binary searching in the tags file to find the desired tag quickly
@@ -419,8 +419,7 @@ would otherwise go unnoticed. Example: >
In Vi the ":tag" command sets the last search pattern when the tag is searched
for. In Vim this is not done, the previous search pattern is still remembered,
-unless the 't' flag is present in 'cpoptions'. The search pattern is always
-put in the search history, so you can modify it if searching fails.
+unless the 't' flag is present in 'cpoptions'.
*tags-option*
The 'tags' option is a list of file names. Each of these files is searched
@@ -847,19 +846,25 @@ like |CTRL-]|.
The function used for generating the taglist is specified by setting the
'tagfunc' option. The function will be called with three arguments:
- a:pattern The tag identifier used during the tag search.
- a:flags List of flags to control the function behavior.
+ a:pattern The tag identifier or pattern used during the tag search.
+ a:flags String containing flags to control the function behavior.
a:info Dict containing the following entries:
buf_ffname Full filename which can be used for priority.
user_data Custom data String, if stored in the tag
stack previously by tagfunc.
-Currently two flags may be passed to the tag function:
+Currently up to three flags may be passed to the tag function:
'c' The function was invoked by a normal command being processed
(mnemonic: the tag function may use the context around the
cursor to perform a better job of generating the tag list.)
'i' In Insert mode, the user was completing a tag (with
- |i_CTRL-X_CTRL-]|).
+ |i_CTRL-X_CTRL-]| or 'completeopt' contains `t`).
+ 'r' The first argument to tagfunc should be interpreted as a
+ |pattern| (see |tag-regexp|), such as when using: >
+ :tag /pat
+< It is also given when completing in insert mode.
+ If this flag is not present, the argument is usually taken
+ literally as the full tag name.
Note that when 'tagfunc' is set, the priority of the tags described in
|tag-priority| does not apply. Instead, the priority is exactly as the
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 1696d3b9ba..343f4a62c2 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -32,6 +32,12 @@ retained for the lifetime of a buffer but this is subject to change. A plugin
should keep a reference to the parser object as long as it wants incremental
updates.
+ *vim.treesitter.language_version*
+To check which language version is compiled with neovim, the number is stored
+within `vim.treesitter.language_version`. This number is not too helpful
+unless you are wondering about compatibility between different versions of
+compiled grammars.
+
Parser files *treesitter-parsers*
Parsers are the heart of tree-sitter. They are libraries that tree-sitter will
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 63c899da0c..97aacc1403 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -788,6 +788,7 @@ Date and Time: *date-functions* *time-functions*
getftime() get last modification time of a file
localtime() get current time in seconds
strftime() convert time to a string
+ strptime() convert a date/time string to time
reltime() get the current or elapsed time accurately
reltimestr() convert reltime() result to a string
reltimefloat() convert reltime() result to a Float
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 0c1e216164..eadc1c04a0 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -70,6 +70,8 @@ the differences.
- |matchit| plugin is enabled. To disable it in your config: >
:let loaded_matchit = 1
+- |g:vimsyn_embed| defaults to "l" to enable Lua highlighting
+
==============================================================================
3. New Features *nvim-features*
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 6a13e67ac5..52cd2dcbfd 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -164,6 +164,9 @@ au BufNewFile,BufRead *.mar setf vmasm
" Atlas
au BufNewFile,BufRead *.atl,*.as setf atlas
+" Atom is based on XML
+au BufNewFile,BufRead *.atom setf xml
+
" Autoit v3
au BufNewFile,BufRead *.au3 setf autoit
@@ -1272,6 +1275,11 @@ au BufNewFile,BufRead .povrayrc setf povini
" Povray, Pascal, PHP or assembly
au BufNewFile,BufRead *.inc call dist#ft#FTinc()
+" PowerShell
+au BufNewFile,BufRead *.ps1,*.psd1,*.psm1,*.pssc setf ps1
+au BufNewFile,BufRead *.ps1xml setf ps1xml
+au BufNewFile,BufRead *.cdxml,*.psc1 setf xml
+
" Printcap and Termcap
au BufNewFile,BufRead *printcap
\ let b:ptcap_type = "print" | setf ptcap
@@ -1330,6 +1338,9 @@ au BufNewFile,BufRead *.pml setf promela
au BufNewFile,BufRead *.proto setf proto
au BufNewFile,BufRead *.pbtxt setf pbtxt
+" Poke
+au BufNewFile,BufRead *.pk setf poke
+
" Protocols
au BufNewFile,BufRead */etc/protocols setf protocols
@@ -1394,6 +1405,9 @@ else
au BufNewFile,BufRead *.rmd,*.smd setf rmd
endif
+" RSS looks like XML
+au BufNewFile,BufRead *.rss setf xml
+
" R reStructuredText file
if has("fname_case")
au BufNewFile,BufRead *.Rrst,*.rrst,*.Srst,*.srst setf rrst
diff --git a/runtime/indent/tcl.vim b/runtime/indent/tcl.vim
index d77081841d..eafb8dd568 100644
--- a/runtime/indent/tcl.vim
+++ b/runtime/indent/tcl.vim
@@ -1,8 +1,8 @@
" Vim indent file
" Language: Tcl
-" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
" Latest Update: Chris Heithoff <chrisheithoff@gmail.com>
-" Latest Revision: 2018-12-05
+" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
+" Latest Revision: 2018-12-05
if exists("b:did_indent")
finish
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 143909fe41..563ffc479e 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -38,11 +38,13 @@ lsp._request_name_to_capability = {
['textDocument/declaration'] = 'declaration';
['textDocument/typeDefinition'] = 'type_definition';
['textDocument/documentSymbol'] = 'document_symbol';
- ['textDocument/workspaceSymbol'] = 'workspace_symbol';
['textDocument/prepareCallHierarchy'] = 'call_hierarchy';
['textDocument/rename'] = 'rename';
['textDocument/codeAction'] = 'code_action';
+ ['textDocument/codeLens'] = 'code_lens';
+ ['codeLens/resolve'] = 'code_lens_resolve';
['workspace/executeCommand'] = 'execute_command';
+ ['workspace/symbol'] = 'workspace_symbol';
['textDocument/references'] = 'find_references';
['textDocument/rangeFormatting'] = 'document_range_formatting';
['textDocument/formatting'] = 'document_formatting';
@@ -228,6 +230,7 @@ local function validate_client_config(config)
before_init = { config.before_init, "f", true };
offset_encoding = { config.offset_encoding, "s", true };
flags = { config.flags, "t", true };
+ get_language_id = { config.get_language_id, "f", true };
}
local cmd, cmd_args = lsp._cmd_parts(config.cmd)
@@ -262,23 +265,47 @@ end
--@param bufnr (Number) Number of the buffer, or 0 for current
--@param client Client object
local function text_document_did_open_handler(bufnr, client)
+ local use_incremental_sync = (
+ if_nil(client.config.flags.allow_incremental_sync, true)
+ and client.resolved_capabilities.text_document_did_change == protocol.TextDocumentSyncKind.Incremental
+ )
+ if use_incremental_sync then
+ if not client._cached_buffers then
+ client._cached_buffers = {}
+ end
+ client._cached_buffers[bufnr] = nvim_buf_get_lines(bufnr, 0, -1, true)
+ end
if not client.resolved_capabilities.text_document_open_close then
return
end
if not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
+ local filetype = nvim_buf_get_option(bufnr, 'filetype')
+
local params = {
textDocument = {
version = 0;
uri = vim.uri_from_bufnr(bufnr);
- -- TODO make sure our filetypes are compatible with languageId names.
- languageId = nvim_buf_get_option(bufnr, 'filetype');
+ languageId = client.config.get_language_id(bufnr, filetype);
text = buf_get_full_text(bufnr);
}
}
client.notify('textDocument/didOpen', params)
util.buf_versions[bufnr] = params.textDocument.version
+
+ -- Next chance we get, we should re-do the diagnostics
+ vim.schedule(function()
+ vim.lsp.handlers["textDocument/publishDiagnostics"](
+ nil,
+ "textDocument/publishDiagnostics",
+ {
+ diagnostics = vim.lsp.diagnostic.get(bufnr, client.id),
+ uri = vim.uri_from_bufnr(bufnr),
+ },
+ client.id
+ )
+ end)
end
-- FIXME: DOC: Shouldn't need to use a dummy function
@@ -400,6 +427,9 @@ end
---
--@param name (string, default=client-id) Name in log messages.
---
+--@param get_language_id function(bufnr, filetype) -> language ID as string.
+--- Defaults to the filetype.
+---
--@param offset_encoding (default="utf-16") One of "utf-8", "utf-16",
--- or "utf-32" which is the encoding that the LSP server expects. Client does
--- not verify this is correct.
@@ -438,16 +468,7 @@ end
--@param trace: "off" | "messages" | "verbose" | nil passed directly to the language
--- server in the initialize request. Invalid/empty values will default to "off"
--@param flags: A table with flags for the client. The current (experimental) flags are:
---- - allow_incremental_sync (bool, default false): Allow using on_line callbacks for lsp
----
---- <pre>
---- -- In attach function for the client, you can do:
---- local custom_attach = function(client)
---- if client.config.flags then
---- client.config.flags.allow_incremental_sync = true
---- end
---- end
---- </pre>
+--- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits
---
--@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be
--- fully initialized. Use `on_init` to do any actions once
@@ -459,6 +480,11 @@ function lsp.start_client(config)
config.flags = config.flags or {}
config.settings = config.settings or {}
+ -- By default, get_language_id just returns the exact filetype it is passed.
+ -- It is possible to pass in something that will calculate a different filetype,
+ -- to be sent by the client.
+ config.get_language_id = config.get_language_id or function(_, filetype) return filetype end
+
local client_id = next_client_id()
local handlers = config.handlers or {}
@@ -542,6 +568,13 @@ function lsp.start_client(config)
client_ids[client_id] = nil
end
+ if code ~= 0 or (signal ~= 0 and signal ~= 15) then
+ local msg = string.format("Client %s quit with exit code %s and signal %s", client_id, code, signal)
+ vim.schedule(function()
+ vim.notify(msg, vim.log.levels.WARN)
+ end)
+ end
+
if config.on_exit then
pcall(config.on_exit, code, signal, client_id)
end
@@ -801,7 +834,6 @@ end
--- Notify all attached clients that a buffer has changed.
local text_document_did_change_handler
do
- local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; }
text_document_did_change_handler = function(_, bufnr, changedtick,
firstline, lastline, new_lastline, old_byte_size, old_utf32_size,
old_utf16_size)
@@ -818,45 +850,30 @@ do
end
util.buf_versions[bufnr] = changedtick
- -- Lazy initialize these because clients may not even need them.
- local incremental_changes = once(function(client)
- local size_index = encoding_index[client.offset_encoding]
- local length = select(size_index, old_byte_size, old_utf16_size, old_utf32_size)
- local lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true)
-
- -- This is necessary because we are specifying the full line including the
- -- newline in range. Therefore, we must replace the newline as well.
- if #lines > 0 then
- table.insert(lines, '')
- end
- return {
- range = {
- start = { line = firstline, character = 0 };
- ["end"] = { line = lastline, character = 0 };
- };
- rangeLength = length;
- text = table.concat(lines, '\n');
- };
- end)
+
+ local incremental_changes = function(client)
+ local lines = nvim_buf_get_lines(bufnr, 0, -1, true)
+ local startline = math.min(firstline + 1, math.min(#client._cached_buffers[bufnr], #lines))
+ local endline = math.min(-(#lines - new_lastline), -1)
+ local incremental_change = vim.lsp.util.compute_diff(
+ client._cached_buffers[bufnr], lines, startline, endline, client.offset_encoding or "utf-16")
+ client._cached_buffers[bufnr] = lines
+ return incremental_change
+ end
+
local full_changes = once(function()
return {
text = buf_get_full_text(bufnr);
};
end)
- local uri = vim.uri_from_bufnr(bufnr)
- for_each_buffer_client(bufnr, function(client, _client_id)
- local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, false)
+ local uri = vim.uri_from_bufnr(bufnr)
+ for_each_buffer_client(bufnr, function(client)
+ local allow_incremental_sync = if_nil(client.config.flags.allow_incremental_sync, true)
local text_document_did_change = client.resolved_capabilities.text_document_did_change
local changes
if text_document_did_change == protocol.TextDocumentSyncKind.None then
return
- --[=[ TODO(ashkan) there seem to be problems with the byte_sizes sent by
- -- neovim right now so only send the full content for now. In general, we
- -- can assume that servers *will* support both versions anyway, as there
- -- is no way to specify the sync capability by the client.
- -- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both.
- --]=]
elseif not allow_incremental_sync or text_document_did_change == protocol.TextDocumentSyncKind.Full then
changes = full_changes(client)
elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then
@@ -914,16 +931,34 @@ function lsp.buf_attach_client(bufnr, client_id)
all_buffer_active_clients[bufnr] = buffer_client_ids
local uri = vim.uri_from_bufnr(bufnr)
- nvim_command(string.format("autocmd BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0)", bufnr))
+ local buf_did_save_autocommand = [=[
+ augroup lsp_c_%d_b_%d_did_save
+ au!
+ au BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0)
+ augroup END
+ ]=]
+ vim.api.nvim_exec(string.format(buf_did_save_autocommand, client_id, bufnr, bufnr), false)
-- First time, so attach and set up stuff.
vim.api.nvim_buf_attach(bufnr, false, {
on_lines = text_document_did_change_handler;
+ on_reload = function()
+ local params = { textDocument = { uri = uri; } }
+ for_each_buffer_client(bufnr, function(client, _)
+ if client.resolved_capabilities.text_document_open_close then
+ client.notify('textDocument/didClose', params)
+ end
+ text_document_did_open_handler(bufnr, client)
+ end)
+ end;
on_detach = function()
local params = { textDocument = { uri = uri; } }
for_each_buffer_client(bufnr, function(client, _)
if client.resolved_capabilities.text_document_open_close then
client.notify('textDocument/didClose', params)
end
+ if client._cached_buffers then
+ client._cached_buffers[bufnr] = nil
+ end
end)
util.buf_versions[bufnr] = nil
all_buffer_active_clients[bufnr] = nil
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index df25943ecd..4e82c46fef 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -264,10 +264,16 @@ local function set_diagnostic_cache(diagnostics, bufnr, client_id)
-- The diagnostic's severity. Can be omitted. If omitted it is up to the
-- client to interpret diagnostics as error, warning, info or hint.
-- TODO: Replace this with server-specific heuristics to infer severity.
+ local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
for _, diagnostic in ipairs(diagnostics) do
if diagnostic.severity == nil then
diagnostic.severity = DiagnosticSeverity.Error
end
+ -- Account for servers that place diagnostics on terminating newline
+ if buf_line_count > 0 then
+ local start = diagnostic.range.start
+ start.line = math.min(start.line, buf_line_count - 1)
+ end
end
diagnostic_cache[bufnr][client_id] = diagnostics
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 7819bddcb9..eacbd90077 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -26,7 +26,7 @@ end
-- @msg of type ProgressParams
-- Basically a token of type number/string
-local function progress_callback(_, _, params, client_id)
+local function progress_handler(_, _, params, client_id)
local client = vim.lsp.get_client_by_id(client_id)
local client_name = client and client.name or string.format("id=%d", client_id)
if not client then
@@ -62,7 +62,7 @@ local function progress_callback(_, _, params, client_id)
end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
-M['$/progress'] = progress_callback
+M['$/progress'] = progress_handler
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create
M['window/workDoneProgress/create'] = function(_, _, params, client_id)
@@ -304,7 +304,7 @@ M['textDocument/typeDefinition'] = location_handler
M['textDocument/implementation'] = location_handler
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
-M['textDocument/signatureHelp'] = function(_, method, result)
+M['textDocument/signatureHelp'] = function(_, method, result, _, bufnr)
-- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler
-- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore
if not (result and result.signatures and result.signatures[1]) then
@@ -317,9 +317,11 @@ M['textDocument/signatureHelp'] = function(_, method, result)
print('No signature help available')
return
end
- util.focusable_preview(method, function()
+ local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
+ local p_bufnr, _ = util.focusable_preview(method, function()
return lines, util.try_trim_markdown_code_blocks(lines)
end)
+ api.nvim_buf_set_option(p_bufnr, 'syntax', syntax)
end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index 4e8d2d4202..331e980e67 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -32,6 +32,8 @@ do
vim.fn.mkdir(vim.fn.stdpath('cache'), "p")
local logfile = assert(io.open(logfilename, "a+"))
+ -- Start message for logging
+ logfile:write(string.format("[ START ] %s ] LSP logging initiated\n", os.date(log_date_format)))
for level, levelnr in pairs(log.levels) do
-- Also export the log level on the root object.
log[level] = levelnr
@@ -68,8 +70,6 @@ do
logfile:flush()
end
end
- -- Add some space to make it easier to distinguish different neovim runs.
- logfile:write("\n")
end
-- This is put here on purpose after the loop above so that it doesn't
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 388f65c180..7e43eb84de 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -749,6 +749,9 @@ function protocol.make_client_capabilities()
};
workspaceFolders = true;
applyEdit = true;
+ workspaceEdit = {
+ resourceOperations = {'rename', 'create', 'delete',},
+ };
};
callHierarchy = {
dynamicRegistration = false;
@@ -975,6 +978,16 @@ function protocol.resolve_capabilities(server_capabilities)
general_properties.rename = true
end
+ if server_capabilities.codeLensProvider == nil then
+ general_properties.code_lens = false
+ general_properties.code_lens_resolve = false
+ elseif type(server_capabilities.codeLensProvider) == 'table' then
+ general_properties.code_lens = true
+ general_properties.code_lens_resolve = server_capabilities.codeLensProvider.resolveProvider or false
+ else
+ error("The server sent invalid codeLensProvider")
+ end
+
if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false
elseif type(server_capabilities.codeActionProvider) == 'boolean'
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 4e2dd7c8e8..1aa8326514 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -315,8 +315,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
dispatchers = { dispatchers, 't', true };
}
- if not (vim.fn.executable(cmd) == 1) then
- error(string.format("The given command %q is not executable.", cmd))
+ if extra_spawn_params and extra_spawn_params.cwd then
+ assert(is_dir(extra_spawn_params.cwd), "cwd must be a directory")
+ elseif not (vim.fn.executable(cmd) == 1) then
+ error(string.format("The given command %q is not executable.", cmd))
end
if dispatchers then
local user_dispatchers = dispatchers
@@ -370,9 +372,6 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
}
if extra_spawn_params then
spawn_params.cwd = extra_spawn_params.cwd
- if spawn_params.cwd then
- assert(is_dir(spawn_params.cwd), "cwd must be a directory")
- end
spawn_params.env = env_merge(extra_spawn_params.env)
end
handle, pid = uv.spawn(cmd, spawn_params, onexit)
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 6fb9f09c99..ec1131ae1f 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -226,6 +226,177 @@ end
-- function M.glob_to_regex(glob)
-- end
+--@private
+--- Finds the first line and column of the difference between old and new lines
+--@param old_lines table list of lines
+--@param new_lines table list of lines
+--@returns (int, int) start_line_idx and start_col_idx of range
+local function first_difference(old_lines, new_lines, start_line_idx)
+ local line_count = math.min(#old_lines, #new_lines)
+ if line_count == 0 then return 1, 1 end
+ if not start_line_idx then
+ for i = 1, line_count do
+ start_line_idx = i
+ if old_lines[start_line_idx] ~= new_lines[start_line_idx] then
+ break
+ end
+ end
+ end
+ local old_line = old_lines[start_line_idx]
+ local new_line = new_lines[start_line_idx]
+ local length = math.min(#old_line, #new_line)
+ local start_col_idx = 1
+ while start_col_idx <= length do
+ if string.sub(old_line, start_col_idx, start_col_idx) ~= string.sub(new_line, start_col_idx, start_col_idx) then
+ break
+ end
+ start_col_idx = start_col_idx + 1
+ end
+ return start_line_idx, start_col_idx
+end
+
+
+--@private
+--- Finds the last line and column of the differences between old and new lines
+--@param old_lines table list of lines
+--@param new_lines table list of lines
+--@param start_char integer First different character idx of range
+--@returns (int, int) end_line_idx and end_col_idx of range
+local function last_difference(old_lines, new_lines, start_char, end_line_idx)
+ local line_count = math.min(#old_lines, #new_lines)
+ if line_count == 0 then return 0,0 end
+ if not end_line_idx then
+ end_line_idx = -1
+ end
+ for i = end_line_idx, -line_count, -1 do
+ if old_lines[#old_lines + i + 1] ~= new_lines[#new_lines + i + 1] then
+ end_line_idx = i
+ break
+ end
+ end
+ local old_line
+ local new_line
+ if end_line_idx <= -line_count then
+ end_line_idx = -line_count
+ old_line = string.sub(old_lines[#old_lines + end_line_idx + 1], start_char)
+ new_line = string.sub(new_lines[#new_lines + end_line_idx + 1], start_char)
+ else
+ old_line = old_lines[#old_lines + end_line_idx + 1]
+ new_line = new_lines[#new_lines + end_line_idx + 1]
+ end
+ local old_line_length = #old_line
+ local new_line_length = #new_line
+ local length = math.min(old_line_length, new_line_length)
+ local end_col_idx = -1
+ while end_col_idx >= -length do
+ local old_char = string.sub(old_line, old_line_length + end_col_idx + 1, old_line_length + end_col_idx + 1)
+ local new_char = string.sub(new_line, new_line_length + end_col_idx + 1, new_line_length + end_col_idx + 1)
+ if old_char ~= new_char then
+ break
+ end
+ end_col_idx = end_col_idx - 1
+ end
+ return end_line_idx, end_col_idx
+
+end
+
+--@private
+--- Get the text of the range defined by start and end line/column
+--@param lines table list of lines
+--@param start_char integer First different character idx of range
+--@param end_char integer Last different character idx of range
+--@param start_line integer First different line idx of range
+--@param end_line integer Last different line idx of range
+--@returns string text extracted from defined region
+local function extract_text(lines, start_line, start_char, end_line, end_char)
+ if start_line == #lines + end_line + 1 then
+ if end_line == 0 then return '' end
+ local line = lines[start_line]
+ local length = #line + end_char - start_char
+ return string.sub(line, start_char, start_char + length + 1)
+ end
+ local result = string.sub(lines[start_line], start_char) .. '\n'
+ for line_idx = start_line + 1, #lines + end_line do
+ result = result .. lines[line_idx] .. '\n'
+ end
+ if end_line ~= 0 then
+ local line = lines[#lines + end_line + 1]
+ local length = #line + end_char + 1
+ result = result .. string.sub(line, 1, length)
+ end
+ return result
+end
+
+--@private
+--- Compute the length of the substituted range
+--@param lines table list of lines
+--@param start_char integer First different character idx of range
+--@param end_char integer Last different character idx of range
+--@param start_line integer First different line idx of range
+--@param end_line integer Last different line idx of range
+--@returns (int, int) end_line_idx and end_col_idx of range
+local function compute_length(lines, start_line, start_char, end_line, end_char)
+ local adj_end_line = #lines + end_line + 1
+ local adj_end_char
+ if adj_end_line > #lines then
+ adj_end_char = end_char - 1
+ else
+ adj_end_char = #lines[adj_end_line] + end_char
+ end
+ if start_line == adj_end_line then
+ return adj_end_char - start_char + 1
+ end
+ local result = #lines[start_line] - start_char + 1
+ for line = start_line + 1, adj_end_line -1 do
+ result = result + #lines[line] + 1
+ end
+ result = result + adj_end_char + 1
+ return result
+end
+
+--- Returns the range table for the difference between old and new lines
+--@param old_lines table list of lines
+--@param new_lines table list of lines
+--@param start_line_idx int line to begin search for first difference
+--@param end_line_idx int line to begin search for last difference
+--@param offset_encoding string encoding requested by language server
+--@returns table start_line_idx and start_col_idx of range
+function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx, offset_encoding)
+ local start_line, start_char = first_difference(old_lines, new_lines, start_line_idx)
+ local end_line, end_char = last_difference(vim.list_slice(old_lines, start_line, #old_lines),
+ vim.list_slice(new_lines, start_line, #new_lines), start_char, end_line_idx)
+ local text = extract_text(new_lines, start_line, start_char, end_line, end_char)
+ local length = compute_length(old_lines, start_line, start_char, end_line, end_char)
+
+ local adj_end_line = #old_lines + end_line
+ local adj_end_char
+ if end_line == 0 then
+ adj_end_char = 0
+ else
+ adj_end_char = #old_lines[#old_lines + end_line + 1] + end_char + 1
+ end
+
+ local _
+ if offset_encoding == "utf-16" then
+ _, start_char = vim.str_utfindex(old_lines[start_line], start_char - 1)
+ _, end_char = vim.str_utfindex(old_lines[#old_lines + end_line + 1], adj_end_char)
+ else
+ start_char = start_char - 1
+ end_char = adj_end_char
+ end
+
+ local result = {
+ range = {
+ start = { line = start_line - 1, character = start_char},
+ ["end"] = { line = adj_end_line, character = end_char}
+ },
+ text = text,
+ rangeLength = length + 1,
+ }
+
+ return result
+end
+
--- Can be used to extract the completion items from a
--- `textDocument/completion` request, which may return one of
--- `CompletionItem[]`, `CompletionList` or null.
@@ -359,13 +530,13 @@ end
--- precedence is as follows: textEdit.newText > insertText > label
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
local function get_completion_word(item)
- if item.textEdit ~= nil and item.textEdit.newText ~= nil then
+ if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
return item.textEdit.newText
else
return M.parse_snippet(item.textEdit.newText)
end
- elseif item.insertText ~= nil then
+ elseif item.insertText ~= nil and item.insertText ~= "" then
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
return item.insertText
else
@@ -453,6 +624,62 @@ function M.text_document_completion_list_to_complete_items(result, prefix)
return matches
end
+
+--- Rename old_fname to new_fname
+--
+--@param opts (table)
+-- overwrite? bool
+-- ignoreIfExists? bool
+function M.rename(old_fname, new_fname, opts)
+ opts = opts or {}
+ local bufnr = vim.fn.bufadd(old_fname)
+ vim.fn.bufload(bufnr)
+ local target_exists = vim.loop.fs_stat(new_fname) ~= nil
+ if target_exists and not opts.overwrite or opts.ignoreIfExists then
+ vim.notify('Rename target already exists. Skipping rename.')
+ return
+ end
+ local ok, err = os.rename(old_fname, new_fname)
+ assert(ok, err)
+ api.nvim_buf_call(bufnr, function()
+ vim.cmd('saveas! ' .. vim.fn.fnameescape(new_fname))
+ end)
+end
+
+
+local function create_file(change)
+ local opts = change.options or {}
+ -- from spec: Overwrite wins over `ignoreIfExists`
+ local fname = vim.uri_to_fname(change.uri)
+ if not opts.ignoreIfExists or opts.overwrite then
+ local file = io.open(fname, 'w')
+ file:close()
+ end
+ vim.fn.bufadd(fname)
+end
+
+
+local function delete_file(change)
+ local opts = change.options or {}
+ local fname = vim.uri_to_fname(change.uri)
+ local stat = vim.loop.fs_stat(fname)
+ if opts.ignoreIfNotExists and not stat then
+ return
+ end
+ assert(stat, "Cannot delete not existing file or folder " .. fname)
+ local flags
+ if stat and stat.type == 'directory' then
+ flags = opts.recursive and 'rf' or 'd'
+ else
+ flags = ''
+ end
+ local bufnr = vim.fn.bufadd(fname)
+ local result = tonumber(vim.fn.delete(fname, flags))
+ assert(result == 0, 'Could not delete file: ' .. fname .. ', stat: ' .. vim.inspect(stat))
+ api.nvim_buf_delete(bufnr, { force = true })
+end
+
+
--- Applies a `WorkspaceEdit`.
---
--@param workspace_edit (table) `WorkspaceEdit`
@@ -460,8 +687,17 @@ end
function M.apply_workspace_edit(workspace_edit)
if workspace_edit.documentChanges then
for idx, change in ipairs(workspace_edit.documentChanges) do
- if change.kind then
- -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile
+ if change.kind == "rename" then
+ M.rename(
+ vim.uri_to_fname(change.oldUri),
+ vim.uri_to_fname(change.newUri),
+ change.options
+ )
+ elseif change.kind == 'create' then
+ create_file(change)
+ elseif change.kind == 'delete' then
+ delete_file(change)
+ elseif change.kind then
error(string.format("Unsupported change: %q", vim.inspect(change)))
else
M.apply_text_document_edit(change, idx)
@@ -687,8 +923,8 @@ function M.preview_location(location)
end
local range = location.targetRange or location.range
local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false)
- local filetype = api.nvim_buf_get_option(bufnr, 'filetype')
- return M.open_floating_preview(contents, filetype)
+ local syntax = api.nvim_buf_get_option(bufnr, 'syntax')
+ return M.open_floating_preview(contents, syntax)
end
--@private
@@ -873,8 +1109,10 @@ function M.fancy_floating_markdown(contents, opts)
-- This is because the syntax command doesn't accept a target.
local cwin = vim.api.nvim_get_current_win()
vim.api.nvim_set_current_win(winnr)
+ api.nvim_win_set_option(winnr, 'conceallevel', 2)
+ api.nvim_win_set_option(winnr, 'concealcursor', 'n')
- vim.cmd("ownsyntax markdown")
+ vim.cmd("ownsyntax lsp_markdown")
local idx = 1
--@private
local function apply_syntax_to_region(ft, start, finish)
@@ -975,7 +1213,7 @@ end
--- Shows contents in a floating window.
---
--@param contents table of lines to show in window
---@param filetype string of filetype to set for opened buffer
+--@param syntax string of syntax to set for opened buffer
--@param opts dictionary with optional fields
-- - height of floating window
-- - width of floating window
@@ -988,10 +1226,10 @@ end
-- - pad_bottom number of lines to pad contents at bottom
--@returns bufnr,winnr buffer and window number of the newly created floating
---preview window
-function M.open_floating_preview(contents, filetype, opts)
+function M.open_floating_preview(contents, syntax, opts)
validate {
contents = { contents, 't' };
- filetype = { filetype, 's', true };
+ syntax = { syntax, 's', true };
opts = { opts, 't', true };
}
opts = opts or {}
@@ -1004,12 +1242,12 @@ function M.open_floating_preview(contents, filetype, opts)
local width, height = M._make_floating_popup_size(contents, opts)
local floating_bufnr = api.nvim_create_buf(false, true)
- if filetype then
- api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype)
+ if syntax then
+ api.nvim_buf_set_option(floating_bufnr, 'syntax', syntax)
end
local float_option = M.make_floating_popup_options(width, height, opts)
local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
- if filetype == 'markdown' then
+ if syntax == 'markdown' then
api.nvim_win_set_option(floating_winnr, 'conceallevel', 2)
end
api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents)
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 998e04f568..0a663628a5 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -400,6 +400,20 @@ function vim.tbl_count(t)
return count
end
+--- Creates a copy of a table containing only elements from start to end (inclusive)
+---
+--@param list table table
+--@param start integer Start range of slice
+--@param finish integer End range of slice
+--@returns Copy of table sliced from start to finish (inclusive)
+function vim.list_slice(list, start, finish)
+ local new_list = {}
+ for i = start or 1, finish or #list do
+ new_list[#new_list+1] = list[i]
+ end
+ return new_list
+end
+
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
---
--@see https://www.lua.org/pil/20.2.html
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index 3af66b134c..cac0ab864b 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -10,13 +10,11 @@ local parsers = {}
local M = vim.tbl_extend("error", query, language)
+M.language_version = vim._ts_get_language_version()
+
setmetatable(M, {
__index = function (t, k)
- if k == "TSHighlighter" then
- a.nvim_err_writeln("vim.TSHighlighter is deprecated, please use vim.treesitter.highlighter")
- t[k] = require'vim.treesitter.highlighter'
- return t[k]
- elseif k == "highlighter" then
+ if k == "highlighter" then
t[k] = require'vim.treesitter.highlighter'
return t[k]
end
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 6b833c6d35..fe7e1052c9 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -12,6 +12,16 @@ TSHighlighterQuery.__index = TSHighlighterQuery
local ns = a.nvim_create_namespace("treesitter/highlighter")
+local _default_highlights = {}
+local _link_default_highlight_once = function(from, to)
+ if not _default_highlights[from] then
+ _default_highlights[from] = true
+ vim.cmd(string.format("highlight default link %s %s", from, to))
+ end
+
+ return from
+end
+
-- These are conventions defined by nvim-treesitter, though it
-- needs to be user extensible also.
TSHighlighter.hl_map = {
@@ -70,9 +80,12 @@ function TSHighlighterQuery.new(lang, query_string)
self.hl_cache = setmetatable({}, {
__index = function(table, capture)
- local hl = self:get_hl_from_capture(capture)
- rawset(table, capture, hl)
+ local hl, is_vim_highlight = self:_get_hl_from_capture(capture)
+ if not is_vim_highlight then
+ hl = _link_default_highlight_once(lang .. hl, hl)
+ end
+ rawset(table, capture, hl)
return hl
end
})
@@ -90,16 +103,16 @@ function TSHighlighterQuery:query()
return self._query
end
-function TSHighlighterQuery:get_hl_from_capture(capture)
+--- Get the hl from capture.
+--- Returns a tuple { highlight_name: string, is_builtin: bool }
+function TSHighlighterQuery:_get_hl_from_capture(capture)
local name = self._query.captures[capture]
if is_highlight_name(name) then
-- From "Normal.left" only keep "Normal"
- return vim.split(name, '.', true)[1]
+ return vim.split(name, '.', true)[1], true
else
- -- Default to false to avoid recomputing
- local hl = TSHighlighter.hl_map[name]
- return hl and a.nvim_get_hl_id_by_name(hl) or 0
+ return TSHighlighter.hl_map[name] or name, false
end
end
@@ -208,6 +221,9 @@ local function on_line_impl(self, buf, line)
local state = self:get_highlight_state(tstree)
local highlighter_query = self:get_query(tree:lang())
+ -- Some injected languages may not have highlight queries.
+ if not highlighter_query:query() then return end
+
if state.iter == nil then
state.iter = highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
end
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 8b94348994..f40e1d5294 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -8,10 +8,33 @@ Query.__index = Query
local M = {}
+local function dedupe_files(files)
+ local result = {}
+ local seen = {}
+
+ for _, path in ipairs(files) do
+ if not seen[path] then
+ table.insert(result, path)
+ seen[path] = true
+ end
+ end
+
+ return result
+end
+
+local function safe_read(filename, read_quantifier)
+ local file, err = io.open(filename, 'r')
+ if not file then
+ error(err)
+ end
+ local content = file:read(read_quantifier)
+ io.close(file)
+ return content
+end
function M.get_query_files(lang, query_name, is_included)
local query_path = string.format('queries/%s/%s.scm', lang, query_name)
- local lang_files = a.nvim_get_runtime_file(query_path, true)
+ local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
if #lang_files == 0 then return {} end
@@ -25,7 +48,7 @@ function M.get_query_files(lang, query_name, is_included)
local MODELINE_FORMAT = "^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$"
for _, file in ipairs(lang_files) do
- local modeline = io.open(file, 'r'):read('*l')
+ local modeline = safe_read(file, '*l')
if modeline then
local langlist = modeline:match(MODELINE_FORMAT)
@@ -60,7 +83,7 @@ local function read_query_files(filenames)
local contents = {}
for _,filename in ipairs(filenames) do
- table.insert(contents, io.open(filename, 'r'):read('*a'))
+ table.insert(contents, safe_read(filename, '*a'))
end
return table.concat(contents, '')
@@ -77,6 +100,27 @@ local function new_match_metadata()
return setmetatable({}, match_metatable)
end
+--- The explicitly set queries from |vim.treesitter.query.set_query()|
+local explicit_queries = setmetatable({}, {
+ __index = function(t, k)
+ local lang_queries = {}
+ rawset(t, k, lang_queries)
+
+ return lang_queries
+ end,
+})
+
+--- Sets the runtime query {query_name} for {lang}
+---
+--- This allows users to override any runtime files and/or configuration
+--- set by plugins.
+---@param lang string: The language to use for the query
+---@param query_name string: The name of the query (i.e. "highlights")
+---@param text string: The query text (unparsed).
+function M.set_query(lang, query_name, text)
+ explicit_queries[lang][query_name] = M.parse_query(lang, text)
+end
+
--- Returns the runtime query {query_name} for {lang}.
--
-- @param lang The language to use for the query
@@ -84,6 +128,10 @@ end
--
-- @return The corresponding query, parsed.
function M.get_query(lang, query_name)
+ if explicit_queries[lang][query_name] then
+ return explicit_queries[lang][query_name]
+ end
+
local query_files = M.get_query_files(lang, query_name)
local query_string = read_query_files(query_files)
diff --git a/runtime/menu.vim b/runtime/menu.vim
index 3756787e7f..cd56eb5583 100644
--- a/runtime/menu.vim
+++ b/runtime/menu.vim
@@ -356,8 +356,8 @@ func! s:SetupColorSchemes() abort
let s:did_setup_color_schemes = 1
let n = globpath(&runtimepath, "colors/*.vim", 1, 1)
- let n += globpath(&runtimepath, "pack/*/start/*/colors/*.vim", 1, 1)
- let n += globpath(&runtimepath, "pack/*/opt/*/colors/*.vim", 1, 1)
+ let n += globpath(&packpath, "pack/*/start/*/colors/*.vim", 1, 1)
+ let n += globpath(&packpath, "pack/*/opt/*/colors/*.vim", 1, 1)
" Ignore case for VMS and windows, sort on name
let names = sort(map(n, 'substitute(v:val, "\\c.*[/\\\\:\\]]\\([^/\\\\:]*\\)\\.vim", "\\1", "")'), 1)
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 6b3328a5d4..60dc6fdd31 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -810,6 +810,14 @@ call <SID>OptionL("ts")
call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent")
call append("$", "\t(local to buffer)")
call <SID>OptionL("sw")
+if has("vartabs")
+ call append("$", "vartabstop\tlist of number of spaces a tab counts for")
+ call append("$", "\t(local to buffer)")
+ call <SID>OptionL("vts")
+ call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for")
+ call append("$", "\t(local to buffer)")
+ call <SID>OptionL("vsts")
+endif
call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces")
call <SID>BinOptionG("sta", &sta)
call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>")
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 6870bcec75..fa5d064048 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -43,7 +43,7 @@
" balloon -> nvim floating window
"
" The code for opening the floating window was taken from the beautiful
-" implementation of LanguageClient-Neovim:
+" implementation of LanguageClient-Neovim:
" https://github.com/autozimu/LanguageClient-neovim/blob/0ed9b69dca49c415390a8317b19149f97ae093fa/autoload/LanguageClient.vim#L304
"
" Neovim terminal also works seamlessly on windows, which is why the ability
@@ -76,9 +76,14 @@ if !exists('g:termdebugger')
endif
let s:pc_id = 12
-let s:break_id = 13 " breakpoint number is added to this
+let s:asm_id = 13
+let s:break_id = 14 " breakpoint number is added to this
let s:stopped = 1
+let s:parsing_disasm_msg = 0
+let s:asm_lines = []
+let s:asm_addr = ''
+
" Take a breakpoint number as used by GDB and turn it into an integer.
" The breakpoint may contain a dot: 123.4 -> 123004
" The main breakpoint has a zero subid.
@@ -120,6 +125,7 @@ func s:StartDebug_internal(dict)
let s:ptywin = 0
let s:pid = 0
+ let s:asmwin = 0
" Uncomment this line to write logging in "debuglog".
" call ch_logfile('debuglog', 'w')
@@ -155,6 +161,14 @@ func s:StartDebug_internal(dict)
else
call s:StartDebug_term(a:dict)
endif
+
+ if exists('g:termdebug_disasm_window')
+ if g:termdebug_disasm_window
+ let curwinid = win_getid(winnr())
+ call s:GotoAsmwinOrCreateIt()
+ call win_gotoid(curwinid)
+ endif
+ endif
endfunc
" Use when debugger didn't start or ended.
@@ -321,9 +335,9 @@ func s:StartDebug_prompt(dict)
"call ch_log('executing "' . join(cmd) . '"')
let s:gdbjob = jobstart(cmd, {
- \ 'on_exit': function('s:EndPromptDebug'),
- \ 'on_stdout': function('s:GdbOutCallback'),
- \ })
+ \ 'on_exit': function('s:EndPromptDebug'),
+ \ 'on_stdout': function('s:GdbOutCallback'),
+ \ })
if s:gdbjob == 0
echoerr 'invalid argument (or job table is full) while starting gdb job'
exe 'bwipe! ' . s:ptybuf
@@ -562,6 +576,15 @@ func s:GetFullname(msg)
return name
endfunc
+" Extract the "addr" value from a gdb message with addr="0x0001234".
+func s:GetAsmAddr(msg)
+ if a:msg !~ 'addr='
+ return ''
+ endif
+ let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', ''))
+ return addr
+endfunc
+
function s:EndTermDebug(job_id, exit_code, event)
unlet s:gdbwin
@@ -601,6 +624,66 @@ func s:EndPromptDebug(job_id, exit_code, event)
"call ch_log("Returning from EndPromptDebug()")
endfunc
+" - CommOutput: disassemble $pc
+" - CommOutput: &"disassemble $pc\n"
+" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n"
+" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n"
+" ...
+" - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n"
+" - CommOutput: ~" 0x0000555556467cd1:\tret \n"
+" - CommOutput: ~"End of assembler dump.\n"
+" - CommOutput: ^done
+
+" - CommOutput: disassemble $pc
+" - CommOutput: &"disassemble $pc\n"
+" - CommOutput: &"No function contains specified address.\n"
+" - CommOutput: ^error,msg="No function contains specified address."
+func s:HandleDisasmMsg(msg)
+ if a:msg =~ '^\^done'
+ let curwinid = win_getid(winnr())
+ if win_gotoid(s:asmwin)
+ silent normal! gg0"_dG
+ call setline(1, s:asm_lines)
+ set nomodified
+ set filetype=asm
+
+ let lnum = search('^' . s:asm_addr)
+ if lnum != 0
+ exe 'sign unplace ' . s:asm_id
+ exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
+ endif
+
+ call win_gotoid(curwinid)
+ endif
+
+ let s:parsing_disasm_msg = 0
+ let s:asm_lines = []
+ elseif a:msg =~ '^\^error,msg='
+ if s:parsing_disasm_msg == 1
+ " Disassemble call ran into an error. This can happen when gdb can't
+ " find the function frame address, so let's try to disassemble starting
+ " at current PC
+ call s:SendCommand('disassemble $pc,+100')
+ endif
+ let s:parsing_disasm_msg = 0
+ elseif a:msg =~ '\&\"disassemble \$pc'
+ if a:msg =~ '+100'
+ " This is our second disasm attempt
+ let s:parsing_disasm_msg = 2
+ endif
+ else
+ let value = substitute(a:msg, '^\~\"[ ]*', '', '')
+ let value = substitute(value, '^=>[ ]*', '', '')
+ let value = substitute(value, '\\n\" $', '', '')
+ let value = substitute(value, ' ', '', '')
+ let value = substitute(value, '\\t', ' ', 'g')
+
+ if value != '' || !empty(s:asm_lines)
+ call add(s:asm_lines, value)
+ endif
+ endif
+endfunc
+
func s:CommOutput(job_id, msgs, event)
for msg in a:msgs
@@ -608,7 +691,10 @@ func s:CommOutput(job_id, msgs, event)
if msg[0] == "\n"
let msg = msg[1:]
endif
- if msg != ''
+
+ if s:parsing_disasm_msg
+ call s:HandleDisasmMsg(msg)
+ elseif msg != ''
if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
call s:HandleCursor(msg)
elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
@@ -621,6 +707,9 @@ func s:CommOutput(job_id, msgs, event)
call s:HandleEvaluate(msg)
elseif msg =~ '^\^error,msg='
call s:HandleError(msg)
+ elseif msg =~ '^disassemble'
+ let s:parsing_disasm_msg = 1
+ let s:asm_lines = []
endif
endif
endfor
@@ -651,6 +740,7 @@ func s:InstallCommands()
command Gdb call win_gotoid(s:gdbwin)
command Program call win_gotoid(s:ptywin)
command Source call s:GotoSourcewinOrCreateIt()
+ command Asm call s:GotoAsmwinOrCreateIt()
command Winbar call s:InstallWinbar()
" TODO: can the K mapping be restored?
@@ -689,6 +779,7 @@ func s:DeleteCommands()
delcommand Gdb
delcommand Program
delcommand Source
+ delcommand Asm
delcommand Winbar
nunmap K
@@ -963,6 +1054,48 @@ func s:GotoSourcewinOrCreateIt()
endif
endfunc
+func s:GotoAsmwinOrCreateIt()
+ if !win_gotoid(s:asmwin)
+ if win_gotoid(s:sourcewin)
+ exe 'rightbelow new'
+ else
+ exe 'new'
+ endif
+
+ let s:asmwin = win_getid(winnr())
+
+ setlocal nowrap
+ setlocal number
+ setlocal noswapfile
+ setlocal buftype=nofile
+
+ let asmbuf = bufnr('Termdebug-asm-listing')
+ if asmbuf > 0
+ exe 'buffer' . asmbuf
+ else
+ exe 'file Termdebug-asm-listing'
+ endif
+
+ if exists('g:termdebug_disasm_window')
+ if g:termdebug_disasm_window > 1
+ exe 'resize ' . g:termdebug_disasm_window
+ endif
+ endif
+ endif
+
+ if s:asm_addr != ''
+ let lnum = search('^' . s:asm_addr)
+ if lnum == 0
+ if s:stopped
+ call s:SendCommand('disassemble $pc')
+ endif
+ else
+ exe 'sign unplace ' . s:asm_id
+ exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
+ endif
+ endif
+endfunc
+
" Handle stopping and running message from gdb.
" Will update the sign that shows the current position.
func s:HandleCursor(msg)
@@ -981,10 +1114,31 @@ func s:HandleCursor(msg)
else
let fname = ''
endif
+
+ if a:msg =~ 'addr='
+ let asm_addr = s:GetAsmAddr(a:msg)
+ if asm_addr != ''
+ let s:asm_addr = asm_addr
+
+ let curwinid = win_getid(winnr())
+ if win_gotoid(s:asmwin)
+ let lnum = search('^' . s:asm_addr)
+ if lnum == 0
+ call s:SendCommand('disassemble $pc')
+ else
+ exe 'sign unplace ' . s:asm_id
+ exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
+ endif
+
+ call win_gotoid(curwinid)
+ endif
+ endif
+ endif
+
if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
if lnum =~ '^[0-9]*$'
- call s:GotoSourcewinOrCreateIt()
+ call s:GotoSourcewinOrCreateIt()
if expand('%:p') != fnamemodify(fname, ':p')
if &modified
" TODO: find existing window
diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm
index 96b43cf0d0..260750a85b 100644
--- a/runtime/queries/c/highlights.scm
+++ b/runtime/queries/c/highlights.scm
@@ -14,6 +14,7 @@
"union"
"volatile"
"goto"
+ "register"
] @keyword
[
@@ -81,6 +82,8 @@
"|="
"&="
"^="
+ ">>="
+ "<<="
"--"
"++"
] @operator
@@ -117,7 +120,6 @@
(preproc_arg)
(preproc_defined)
] @function.macro
-; TODO (preproc_arg) @embedded
(field_identifier) @property
(statement_identifier) @label
@@ -129,13 +131,22 @@
(type_descriptor)
] @type
-(declaration type: [(identifier) (type_identifier)] @type)
-(cast_expression type: [(identifier) (type_identifier)] @type)
+(declaration (type_qualifier) @type)
+(cast_expression type: (type_descriptor) @type)
(sizeof_expression value: (parenthesized_expression (identifier) @type))
((identifier) @constant
(#match? @constant "^[A-Z][A-Z0-9_]+$"))
+;; Preproc def / undef
+(preproc_def
+ name: (_) @constant)
+(preproc_call
+ directive: (preproc_directive) @_u
+ argument: (_) @constant
+ (#eq? @_u "#undef"))
+
+
(comment) @comment
;; Parameters
diff --git a/runtime/syntax/cabal.vim b/runtime/syntax/cabal.vim
index 8af47d4042..92e6b8331e 100644
--- a/runtime/syntax/cabal.vim
+++ b/runtime/syntax/cabal.vim
@@ -4,7 +4,7 @@
" Maintainer: Marcin Szamotulski <profunctor@pm.me>
" Previous Maintainer: Vincent Berthoux <twinside@gmail.com>
" File Types: .cabal
-" Last Change: 15 May 2018
+" Last Change: 21 Nov 2020
" v1.5: Incorporated changes from
" https://github.com/sdiehl/haskell-vim-proto/blob/master/vim/syntax/cabal.vim
" Use `syn keyword` instead of `syn match`.
@@ -62,11 +62,12 @@ syn keyword cabalCategory contained
\ source-repository
\ flag
\ custom-setup
+ \ common
syn match cabalCategoryTitle contained /[^{]*\ze{\?/
syn match cabalCategoryRegion
\ contains=cabalCategory,cabalCategoryTitle
\ nextgroup=cabalCategory skipwhite
- \ /^\c\s*\(contained\|executable\|library\|benchmark\|test-suite\|source-repository\|flag\|custom-setup\)\+\s*\%(.*$\|$\)/
+ \ /^\c\s*\(contained\|executable\|library\|benchmark\|test-suite\|source-repository\|flag\|custom-setup\|common\)\+\s*\%(.*$\|$\)/
syn keyword cabalTruth true false
" cabalStatementRegion which limits the scope of cabalStatement keywords, this
@@ -76,10 +77,14 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ default-language
\ default-extensions
\ author
+ \ autogen-modules
+ \ asm-sources
+ \ asm-options
\ branch
\ bug-reports
\ build-depends
\ build-tools
+ \ build-tools-depends
\ build-type
\ buildable
\ c-sources
@@ -87,32 +92,46 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ category
\ cc-options
\ copyright
+ \ cmm-sources
+ \ cmm-options
\ cpp-options
+ \ cxx-sources
\ data-dir
\ data-files
\ default
+ \ default-extensions
\ description
\ executable
\ exposed-modules
\ exposed
\ extensions
- \ extra-tmp-files
+ \ extra-bundled-libraries
\ extra-doc-files
+ \ extra-dynamic-library-flavours
+ \ extra-framework-dirs
+ \ extra-ghci-libraries
\ extra-lib-dirs
\ extra-libraries
+ \ extra-library-flavours
\ extra-source-files
- \ exta-tmp-files
+ \ extra-tmp-files
\ for example
\ frameworks
\ ghc-options
\ ghc-prof-options
\ ghc-shared-options
+ \ ghcjs-options
+ \ ghcjs-prof-options
+ \ ghcjs-shared-options
\ homepage
+ \ hs-source-dir
\ hs-source-dirs
\ hugs-options
+ \ import
\ include-dirs
\ includes
\ install-includes
+ \ js-sources
\ ld-options
\ license
\ license-file
@@ -120,10 +139,13 @@ syn keyword cabalStatement contained containedin=cabalStatementRegion
\ main-is
\ maintainer
\ manual
+ \ mixins
\ module
\ name
\ nhc98-options
\ other-extensions
+ \ other-language
+ \ other-languages
\ other-modules
\ package-url
\ pkgconfig-depends
diff --git a/runtime/syntax/cabalconfig.vim b/runtime/syntax/cabalconfig.vim
new file mode 100644
index 0000000000..0165725c06
--- /dev/null
+++ b/runtime/syntax/cabalconfig.vim
@@ -0,0 +1,30 @@
+" Vim syntax file
+" Language: Cabal Config
+" Maintainer: profunctor@pm.me
+" Last Change: Marcin Szamotulski
+" Original Author: Marcin Szamotulski
+
+if exists("b:current_syntax")
+ finish
+endif
+
+syn match CabalConfigSection /^\S[[:alpha:]]\+\%(-[[:alpha:]]\+\)*[^:]*$/
+syn region CabalConfigRegion matchgroup=CabalConfigKey start=/^\s*[[:alpha:]]\+\%(-[[:alpha:]]\+\)*:/ matchgroup=NONE end=/$/ contains=CabalConfigSeparator,CabalConfigKeyword,CabalConfigPath keepend
+syn match CabalConfigComment /^\s*--.*$/
+syn match CabalConfigValue /.*$/ contained
+syn match CabalConfigKey /[[:alpha:]]\+\%(-[[:alpha:]]\+\)*\ze:/
+syn keyword CabalConfigSeparator : contained
+syn match CabalConfigVariable /\$[[:alpha:]]\+/
+syn keyword CabalConfigKeyword True False ghc
+syn match CabalConfigPath /\%([[:alpha:]]\+:\)\?\%(\/[[:print:]]\+\)\+/
+
+hi def link CabalConfigComment Comment
+hi def link CabalConfigSection Title
+hi def link CabalConfigKey Statement
+hi def link CabalConfigSeparator NonText
+hi def link CabalConfigValue Normal
+hi def link CabalConfigVariable Identifier
+hi def link CabalConfigKeyword Keyword
+hi def link CabalConfigPath Directory
+
+let b:current_syntax = "cabal.config"
diff --git a/runtime/syntax/cabalproject.vim b/runtime/syntax/cabalproject.vim
new file mode 100644
index 0000000000..12143b9ee9
--- /dev/null
+++ b/runtime/syntax/cabalproject.vim
@@ -0,0 +1,28 @@
+" Vim syntax file
+" Language: Cabal Project
+" Maintainer: profunctor@pm.me
+" Last Change: Marcin Szamotulski
+" Original Author: Marcin Szamotulski
+
+if exists("b:current_syntax")
+ finish
+endif
+
+syn match CabalProjectComment /^\s*--.*/ contains=@Spell
+syn match CabalProjectField /^\w\%(\w\|-\)\+/ contains=@NoSpell
+
+syn keyword CabalProjectBoolean true false True False
+syn keyword CabalProjectCompiler ghc ghcjs jhc lhc uhc haskell-suite
+syn match CabalProjectNat /\<\d\+\>/
+syn keyword CabalProjectJobs $ncpus
+syn keyword CabalProjectProfilingLevel default none exported-functions toplevel-functions all-functions
+
+hi def link CabalProjectComment Comment
+hi def link CabalProjectField Statement
+hi def link CabalProjectBoolean Boolean
+hi def link CabalProjectCompiler Identifier
+hi def link CabalProjectNat Number
+hi def link CabalProjectJobs Number
+hi def link CabalProjectProfilingLevel Statement
+
+let b:current_syntax = "cabal.project"
diff --git a/runtime/syntax/haskell.vim b/runtime/syntax/haskell.vim
index e5128a12ab..1b70b9344a 100644
--- a/runtime/syntax/haskell.vim
+++ b/runtime/syntax/haskell.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Haskell
" Maintainer: Haskell Cafe mailinglist <haskell-cafe@haskell.org>
-" Last Change: 2018 Mar 29 by Marcin Szamotulski
+" Last Change: 2020 Oct 4 by Marcin Szamotulski <profunctor@pm.me>
" Original Author: John Williams <jrw@pobox.com>
"
" Thanks to Ryan Crumley for suggestions and John Meacham for
@@ -38,8 +38,8 @@ if exists("b:current_syntax")
endif
" (Qualified) identifiers (no default highlighting)
-syn match ConId "\(\<[A-Z][a-zA-Z0-9_']*\.\)\=\<[A-Z][a-zA-Z0-9_']*\>" contains=@NoSpell
-syn match VarId "\(\<[A-Z][a-zA-Z0-9_']*\.\)\=\<[a-z][a-zA-Z0-9_']*\>" contains=@NoSpell
+syn match ConId "\(\<[A-Z][a-zA-Z0-9_']*\.\)*\<[A-Z][a-zA-Z0-9_']*\>" contains=@NoSpell
+syn match VarId "\(\<[A-Z][a-zA-Z0-9_']*\.\)*\<[a-z][a-zA-Z0-9_']*\>" contains=@NoSpell
" Infix operators--most punctuation characters and any (qualified) identifier
" enclosed in `backquotes`. An operator starting with : is a constructor,
@@ -49,8 +49,11 @@ syn match hsConSym "\(\<[A-Z][a-zA-Z0-9_']*\.\)\=:[-!#$%&\*\+./<=>\?@\\^|~:]*"
syn match hsVarSym "`\(\<[A-Z][a-zA-Z0-9_']*\.\)\=[a-z][a-zA-Z0-9_']*`"
syn match hsConSym "`\(\<[A-Z][a-zA-Z0-9_']*\.\)\=[A-Z][a-zA-Z0-9_']*`"
+" (Non-qualified) identifiers which start with # are labels
+syn match hsLabel "#[a-z][a-zA-Z0-9_']*\>"
+
" Reserved symbols--cannot be overloaded.
-syn match hsDelimiter "(\|)\|\[\|\]\|,\|;\|_\|{\|}"
+syn match hsDelimiter "(\|)\|\[\|\]\|,\|;\|{\|}"
" Strings and constants
syn match hsSpecialChar contained "\\\([0-9]\+\|o[0-7]\+\|x[0-9a-fA-F]\+\|[\"\\'&\\abfnrtv]\|^[A-Z^_\[\\\]]\)"
@@ -62,37 +65,41 @@ syn match hsCharacter "^'\([^\\]\|\\[^']\+\|\\'\)'" contains=hsSpecialChar,hs
syn match hsNumber "\v<[0-9]%(_*[0-9])*>|<0[xX]_*[0-9a-fA-F]%(_*[0-9a-fA-F])*>|<0[oO]_*%(_*[0-7])*>|<0[bB]_*[01]%(_*[01])*>"
syn match hsFloat "\v<[0-9]%(_*[0-9])*\.[0-9]%(_*[0-9])*%(_*[eE][-+]?[0-9]%(_*[0-9])*)?>|<[0-9]%(_*[0-9])*_*[eE][-+]?[0-9]%(_*[0-9])*>|<0[xX]_*[0-9a-fA-F]%(_*[0-9a-fA-F])*\.[0-9a-fA-F]%(_*[0-9a-fA-F])*%(_*[pP][-+]?[0-9]%(_*[0-9])*)?>|<0[xX]_*[0-9a-fA-F]%(_*[0-9a-fA-F])*_*[pP][-+]?[0-9]%(_*[0-9])*>"
-" Keyword definitions. These must be patterns instead of keywords
-" because otherwise they would match as keywords at the start of a
-" "literate" comment (see lhs.vim).
-syn match hsModule "\<module\>"
-syn match hsImport "\<import\>.*"he=s+6 contains=hsImportMod,hsLineComment,hsBlockComment,@NoSpell
-syn match hsImportMod contained "\<\(as\|qualified\|hiding\)\>" contains=@NoSpell
-syn match hsInfix "\<\(infix\|infixl\|infixr\)\>"
-syn match hsStructure "\<\(class\|data\|deriving\|instance\|default\|where\)\>"
-syn match hsTypedef "\<\(type\|newtype\)\>"
-syn match hsStatement "\<\(do\|case\|of\|let\|in\)\>"
-syn match hsConditional "\<\(if\|then\|else\)\>"
+" Keyword definitions.
+syn keyword hsModule module
+syn match hsImportGroup "\<import\>.*" contains=hsImport,hsImportModuleName,hsImportMod,hsLineComment,hsBlockComment,hsImportList,@NoSpell nextgroup=hsImport
+syn keyword hsImport import contained nextgroup=hsImportModuleName
+syn match hsImportModuleName '\<[A-Z][A-Za-z.]*' contained
+syn region hsImportList start='(' skip='([^)]\{-})' end=')' keepend contained contains=ConId,VarId,hsDelimiter,hsBlockComment,hsTypedef,@NoSpell
+
+syn keyword hsImportMod contained as qualified hiding
+syn keyword hsInfix infix infixl infixr
+syn keyword hsStructure class data deriving instance default where
+syn keyword hsTypedef type
+syn keyword hsNewtypedef newtype
+syn keyword hsTypeFam family
+syn keyword hsStatement mdo do case of let in
+syn keyword hsConditional if then else
" Not real keywords, but close.
if exists("hs_highlight_boolean")
" Boolean constants from the standard prelude.
- syn match hsBoolean "\<\(True\|False\)\>"
+ syn keyword hsBoolean True False
endif
if exists("hs_highlight_types")
" Primitive types from the standard prelude and libraries.
- syn match hsType "\<\(Int\|Integer\|Char\|Bool\|Float\|Double\|IO\|Void\|Addr\|Array\|String\)\>"
+ syn keyword hsType Int Integer Char Bool Float Double IO Void Addr Array String
endif
if exists("hs_highlight_more_types")
" Types from the standard prelude libraries.
- syn match hsType "\<\(Maybe\|Either\|Ratio\|Complex\|Ordering\|IOError\|IOResult\|ExitCode\)\>"
- syn match hsMaybe "\<Nothing\>"
- syn match hsExitCode "\<\(ExitSuccess\)\>"
- syn match hsOrdering "\<\(GT\|LT\|EQ\)\>"
+ syn keyword hsType Maybe Either Ratio Complex Ordering IOError IOResult ExitCode
+ syn keyword hsMaybe Nothing
+ syn keyword hsExitCode ExitSuccess
+ syn keyword hsOrdering GT LT EQ
endif
if exists("hs_highlight_debug")
" Debugging functions from the standard prelude.
- syn match hsDebug "\<\(undefined\|error\|trace\)\>"
+ syn keyword hsDebug undefined error trace
endif
@@ -133,12 +140,14 @@ hi def link hsImportMod hsImport
hi def link hsInfix PreProc
hi def link hsStructure Structure
hi def link hsStatement Statement
-hi def link hsConditional Conditional
-hi def link hsSpecialChar SpecialChar
+hi def link hsConditional Conditional
+hi def link hsSpecialChar SpecialChar
hi def link hsTypedef Typedef
+hi def link hsNewtypedef Typedef
hi def link hsVarSym hsOperator
hi def link hsConSym hsOperator
hi def link hsOperator Operator
+hi def link hsTypeFam Structure
if exists("hs_highlight_delimiters")
" Some people find this highlighting distracting.
hi def link hsDelimiter Delimiter
@@ -160,22 +169,22 @@ hi def link hsMaybe hsEnumConst
hi def link hsOrdering hsEnumConst
hi def link hsEnumConst Constant
hi def link hsDebug Debug
-
-hi def link cCppString hsString
-hi def link cCommentStart hsComment
-hi def link cCommentError hsError
-hi def link cCommentStartError hsError
-hi def link cInclude Include
-hi def link cPreProc PreProc
-hi def link cDefine Macro
-hi def link cIncluded hsString
-hi def link cError Error
-hi def link cPreCondit PreCondit
-hi def link cComment Comment
-hi def link cCppSkip cCppOut
-hi def link cCppOut2 cCppOut
-hi def link cCppOut Comment
-
+hi def link hsLabel Special
+
+hi def link cCppString hsString
+hi def link cCommentStart hsComment
+hi def link cCommentError hsError
+hi def link cCommentStartError hsError
+hi def link cInclude Include
+hi def link cPreProc PreProc
+hi def link cDefine Macro
+hi def link cIncluded hsString
+hi def link cError Error
+hi def link cPreCondit PreCondit
+hi def link cComment Comment
+hi def link cCppSkip cCppOut
+hi def link cCppOut2 cCppOut
+hi def link cCppOut Comment
let b:current_syntax = "haskell"
diff --git a/runtime/syntax/lsp_markdown.vim b/runtime/syntax/lsp_markdown.vim
new file mode 100644
index 0000000000..d5c1414f01
--- /dev/null
+++ b/runtime/syntax/lsp_markdown.vim
@@ -0,0 +1,15 @@
+" Vim syntax file
+" Language: lsp_markdown
+" Maintainer: Michael Lingelbach <m.j.lbach@gmail.com
+" URL: http://neovim.io
+" Remark: Uses markdown syntax file
+
+runtime! syntax/markdown.vim
+
+syn cluster mkdNonListItem add=mkdEscape,mkdNbsp
+
+syntax region mkdEscape matchgroup=mkdEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/.\zs/ keepend contains=mkdEscapeCh oneline concealends
+syntax match mkdEscapeCh /./ contained
+syntax match mkdNbsp /&nbsp;/ conceal cchar=
+
+hi def link mkdEscape special
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 1a37af1c8a..92348d57ec 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -616,7 +616,7 @@ syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' e
" g:vimsyn_embed =~# 'r' : embed ruby
" g:vimsyn_embed =~# 't' : embed tcl
if !exists("g:vimsyn_embed")
- let g:vimsyn_embed= 0
+ let g:vimsyn_embed = 'l'
endif
" [-- lua --] {{{3
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
index 0507e4b7b6..b4d896fecc 100755
--- a/scripts/gen_vimdoc.py
+++ b/scripts/gen_vimdoc.py
@@ -48,6 +48,7 @@ import textwrap
import subprocess
import collections
import msgpack
+import logging
from xml.dom import minidom
@@ -57,10 +58,18 @@ if sys.version_info < MIN_PYTHON_VERSION:
print("requires Python {}.{}+".format(*MIN_PYTHON_VERSION))
sys.exit(1)
-DEBUG = ('DEBUG' in os.environ)
+# DEBUG = ('DEBUG' in os.environ)
INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
+log = logging.getLogger(__name__)
+
+LOG_LEVELS = {
+ logging.getLevelName(level): level for level in [
+ logging.DEBUG, logging.INFO, logging.ERROR
+ ]
+}
+
fmt_vimhelp = False # HACK
text_width = 78
script_path = os.path.abspath(__file__)
@@ -157,7 +166,7 @@ CONFIG = {
]),
'file_patterns': '*.lua',
'fn_name_prefix': '',
- 'section_name': {},
+ 'section_name': {'lsp.lua': 'lsp'},
'section_fmt': lambda name: (
'Lua module: vim.lsp'
if name.lower() == 'lsp'
@@ -726,8 +735,8 @@ def extract_from_xml(filename, target, width):
if desc:
for child in desc.childNodes:
paras.append(para_as_map(child))
- if DEBUG:
- print(textwrap.indent(
+ log.debug(
+ textwrap.indent(
re.sub(r'\n\s*\n+', '\n',
desc.toprettyxml(indent=' ', newl='\n')), ' ' * 16))
@@ -885,12 +894,13 @@ def main(config, args):
os.remove(mpack_file)
output_dir = out_dir.format(target=target)
+ debug = args.log_level >= logging.DEBUG
p = subprocess.Popen(
['doxygen', '-'],
stdin=subprocess.PIPE,
# silence warnings
# runtime/lua/vim/lsp.lua:209: warning: argument 'foo' not found
- stderr=(subprocess.STDOUT if DEBUG else subprocess.DEVNULL))
+ stderr=(subprocess.STDOUT if debug else subprocess.DEVNULL))
p.communicate(
config.format(
input=CONFIG[target]['files'],
@@ -1039,6 +1049,10 @@ def filter_source(filename):
def parse_args():
targets = ', '.join(CONFIG.keys())
ap = argparse.ArgumentParser()
+ ap.add_argument(
+ "--log-level", "-l", choices=LOG_LEVELS.keys(),
+ default=logging.getLevelName(logging.ERROR), help="Set log verbosity"
+ )
ap.add_argument('source_filter', nargs='*',
help="Filter source file(s)")
ap.add_argument('-k', '--keep-tmpfiles', action='store_true',
@@ -1085,6 +1099,10 @@ Doxyfile = textwrap.dedent('''
if __name__ == "__main__":
args = parse_args()
+ print("Setting log level to %s" % args.log_level)
+ args.log_level = LOG_LEVELS[args.log_level]
+ log.setLevel(args.log_level)
+
if len(args.source_filter) > 0:
filter_source(args.source_filter[0])
else:
diff --git a/scripts/lua2dox_filter b/scripts/lua2dox_filter
index 61577527c4..8760f12176 100755
--- a/scripts/lua2dox_filter
+++ b/scripts/lua2dox_filter
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
###########################################################################
# Copyright (C) 2012 by Simon Dales #
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 1a1a178620..2c9d655a15 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -513,8 +513,44 @@ if(WIN32)
tidy.exe
win32yank.exe
winpty-agent.exe
+ winpty.dll
xxd.exe
+ # Dependencies for neovim-qt
+ bearer/qgenericbearer.dll
+ iconengines/qsvgicon.dll
+ imageformats/qgif.dll
+ imageformats/qicns.dll
+ imageformats/qico.dll
+ imageformats/qjpeg.dll
+ imageformats/qsvg.dll
+ imageformats/qtga.dll
+ imageformats/qtiff.dll
+ imageformats/qwbmp.dll
+ imageformats/qwebp.dll
+ platforms/qwindows.dll
+ styles/qwindowsvistastyle.dll
+ translations/qt_ar.qm
+ translations/qt_bg.qm
+ translations/qt_ca.qm
+ translations/qt_cs.qm
+ translations/qt_da.qm
+ translations/qt_de.qm
+ translations/qt_en.qm
+ translations/qt_es.qm
+ translations/qt_fi.qm
+ translations/qt_fr.qm
+ translations/qt_gd.qm
+ translations/qt_he.qm
+ translations/qt_hu.qm
+ translations/qt_it.qm
+ translations/qt_ja.qm
+ translations/qt_ko.qm
+ translations/qt_lv.qm
+ translations/qt_pl.qm
+ translations/qt_ru.qm
+ translations/qt_sk.qm
+ translations/qt_uk.qm
D3Dcompiler_47.dll
libEGL.dll
libgcc_s_dw2-1.dll
@@ -522,14 +558,13 @@ if(WIN32)
libstdc++-6.dll
libwinpthread-1.dll
nvim-qt.exe
+ opengl32sw.dll
Qt5Core.dll
Qt5Gui.dll
Qt5Network.dll
Qt5Svg.dll
Qt5Widgets.dll
- winpty.dll
- platforms/qwindows.dll
)
get_filename_component(DEP_FILE_DIR ${DEP_FILE} DIRECTORY)
set(EXTERNAL_BLOBS_SCRIPT "${EXTERNAL_BLOBS_SCRIPT}\n"
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index c1f2842b52..66c4454f7b 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1430,6 +1430,18 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// - "eol": right after eol character (default)
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
+/// - virt_text_hide : hide the virtual text when the background
+/// text is selected or hidden due to
+/// horizontal scroll 'nowrap'
+/// - hl_mode : control how highlights are combined with the
+/// highlights of the text. Currently only affects
+/// virt_text highlights, but might affect `hl_group`
+/// in later versions.
+/// - "replace": only show the virt_text color. This is the
+/// default
+/// - "combine": combine with background text color
+/// - "blend": blend with background text color.
+///
/// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the
@@ -1477,11 +1489,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
bool ephemeral = false;
uint64_t id = 0;
- int line2 = -1, hl_id = 0;
- DecorPriority priority = DECOR_PRIORITY_BASE;
+ int line2 = -1;
+ Decoration decor = DECORATION_INIT;
colnr_T col2 = -1;
- VirtText virt_text = KV_INITIAL_VALUE;
- VirtTextPos virt_text_pos = kVTEndOfLine;
+
bool right_gravity = true;
bool end_right_gravity = false;
bool end_gravity_set = false;
@@ -1528,12 +1539,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
switch (v->type) {
case kObjectTypeString:
hl_group = v->data.string;
- hl_id = syn_check_group(
+ decor.hl_id = syn_check_group(
(char_u *)(hl_group.data),
(int)hl_group.size);
break;
case kObjectTypeInteger:
- hl_id = (int)v->data.integer;
+ decor.hl_id = (int)v->data.integer;
break;
default:
api_set_error(err, kErrorTypeValidation,
@@ -1546,7 +1557,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
"virt_text is not an Array");
goto error;
}
- virt_text = parse_virt_text(v->data.array, err);
+ decor.virt_text = parse_virt_text(v->data.array, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -1558,9 +1569,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
String str = v->data.string;
if (strequal("eol", str.data)) {
- virt_text_pos = kVTEndOfLine;
+ decor.virt_text_pos = kVTEndOfLine;
} else if (strequal("overlay", str.data)) {
- virt_text_pos = kVTOverlay;
+ decor.virt_text_pos = kVTOverlay;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos: invalid value");
+ goto error;
+ }
+ } else if (strequal("virt_text_hide", k.data)) {
+ decor.virt_text_hide = api_object_to_bool(*v,
+ "virt_text_hide", false, err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ } else if (strequal("hl_mode", k.data)) {
+ if (v->type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "hl_mode is not a String");
+ goto error;
+ }
+ String str = v->data.string;
+ if (strequal("replace", str.data)) {
+ decor.hl_mode = kHlModeReplace;
+ } else if (strequal("combine", str.data)) {
+ decor.hl_mode = kHlModeCombine;
+ } else if (strequal("blend", str.data)) {
+ decor.hl_mode = kHlModeBlend;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
@@ -1583,7 +1618,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
"priority is not a valid value");
goto error;
}
- priority = (DecorPriority)v->data.integer;
+ decor.priority = (DecorPriority)v->data.integer;
} else if (strequal("right_gravity", k.data)) {
if (v->type != kObjectTypeBoolean) {
api_set_error(err, kErrorTypeValidation,
@@ -1631,23 +1666,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
col2 = 0;
}
- Decoration *decor = NULL, tmp = { 0 };
+ Decoration *d = NULL;
- if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) {
+ if (ephemeral) {
+ d = &decor;
+ } else if (kv_size(decor.virt_text)
+ || decor.priority != DECOR_PRIORITY_BASE) {
// TODO(bfredl): this is a bit sketchy. eventually we should
// have predefined decorations for both marks/ephemerals
- decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor));
- decor->hl_id = hl_id;
- decor->virt_text = virt_text;
- decor->priority = priority;
- decor->virt_text_pos = virt_text_pos;
- } else if (hl_id) {
- decor = decor_hl(hl_id);
+ d = xcalloc(1, sizeof(*d));
+ *d = decor;
+ } else if (decor.hl_id) {
+ d = decor_hl(decor.hl_id);
}
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
- decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0);
+ decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, 0);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
@@ -1655,14 +1690,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col,
- line2, col2, decor, right_gravity,
+ line2, col2, d, right_gravity,
end_right_gravity, kExtmarkNoUndo);
}
return (Integer)id;
error:
- clear_virttext(&virt_text);
+ clear_virttext(&decor.virt_text);
return 0;
}
@@ -1723,7 +1758,7 @@ Boolean nvim_buf_del_extmark(Buffer buffer,
/// @param[out] err Error details, if any
/// @return The ns_id that was used
Integer nvim_buf_add_highlight(Buffer buffer,
- Integer src_id,
+ Integer ns_id,
String hl_group,
Integer line,
Integer col_start,
@@ -1748,18 +1783,18 @@ Integer nvim_buf_add_highlight(Buffer buffer,
col_end = MAXCOL;
}
- uint64_t ns_id = src2ns(&src_id);
+ uint64_t ns = src2ns(&ns_id);
if (!(line < buf->b_ml.ml_line_count)) {
// safety check, we can't add marks outside the range
- return src_id;
+ return ns_id;
}
int hl_id = 0;
if (hl_group.size > 0) {
hl_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size);
} else {
- return src_id;
+ return ns_id;
}
int end_line = (int)line;
@@ -1768,11 +1803,11 @@ Integer nvim_buf_add_highlight(Buffer buffer,
end_line++;
}
- extmark_set(buf, ns_id, 0,
+ extmark_set(buf, ns, 0,
(int)line, (colnr_T)col_start,
end_line, (colnr_T)col_end,
decor_hl(hl_id), true, false, kExtmarkNoUndo);
- return src_id;
+ return ns_id;
}
/// Clears namespaced objects (highlights, extmarks, virtual text) from
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 7cee569989..d2b787a6f5 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1645,6 +1645,20 @@ bool api_object_to_bool(Object obj, const char *what,
}
}
+int object_to_hl_id(Object obj, const char *what, Error *err)
+{
+ if (obj.type == kObjectTypeString) {
+ String str = obj.data.string;
+ return str.size ? syn_check_group((char_u *)str.data, (int)str.size) : 0;
+ } else if (obj.type == kObjectTypeInteger) {
+ return (int)obj.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "%s is not a valid highlight", what);
+ return 0;
+ }
+}
+
HlMessage parse_hl_msg(Array chunks, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
@@ -1720,3 +1734,341 @@ DecorProvider *get_provider(NS ns_id, bool force)
return item;
}
+
+static bool parse_float_anchor(String anchor, FloatAnchor *out)
+{
+ if (anchor.size == 0) {
+ *out = (FloatAnchor)0;
+ }
+ char *str = anchor.data;
+ if (striequal(str, "NW")) {
+ *out = 0; // NW is the default
+ } else if (striequal(str, "NE")) {
+ *out = kFloatAnchorEast;
+ } else if (striequal(str, "SW")) {
+ *out = kFloatAnchorSouth;
+ } else if (striequal(str, "SE")) {
+ *out = kFloatAnchorSouth | kFloatAnchorEast;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool parse_float_relative(String relative, FloatRelative *out)
+{
+ char *str = relative.data;
+ if (striequal(str, "editor")) {
+ *out = kFloatRelativeEditor;
+ } else if (striequal(str, "win")) {
+ *out = kFloatRelativeWindow;
+ } else if (striequal(str, "cursor")) {
+ *out = kFloatRelativeCursor;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool parse_float_bufpos(Array bufpos, lpos_T *out)
+{
+ if (bufpos.size != 2
+ || bufpos.items[0].type != kObjectTypeInteger
+ || bufpos.items[1].type != kObjectTypeInteger) {
+ return false;
+ }
+ out->lnum = bufpos.items[0].data.integer;
+ out->col = (colnr_T)bufpos.items[1].data.integer;
+ return true;
+}
+
+static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
+{
+ struct {
+ const char *name;
+ schar_T chars[8];
+ } defaults[] = {
+ { "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" } },
+ { "single", { "┌", "─", "┐", "│", "┘", "─", "└", "│" } },
+ { NULL, { { NUL } } },
+ };
+
+ schar_T *chars = fconfig->border_chars;
+ int *hl_ids = fconfig->border_hl_ids;
+
+ fconfig->border = true;
+
+ if (style.type == kObjectTypeArray) {
+ Array arr = style.data.array;
+ size_t size = arr.size;
+ if (!size || size > 8 || (size & (size-1))) {
+ api_set_error(err, kErrorTypeValidation,
+ "invalid number of border chars");
+ return;
+ }
+ for (size_t i = 0; i < size; i++) {
+ Object iytem = arr.items[i];
+ String string = NULL_STRING;
+ int hl_id = 0;
+ if (iytem.type == kObjectTypeArray) {
+ Array iarr = iytem.data.array;
+ if (!iarr.size || iarr.size > 2) {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ if (iarr.items[0].type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ string = iarr.items[0].data.string;
+ if (iarr.size == 2) {
+ hl_id = object_to_hl_id(iarr.items[1], "border char highlight", err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+ }
+
+ } else if (iytem.type == kObjectTypeString) {
+ string = iytem.data.string;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ if (!string.size
+ || mb_string2cells_len((char_u *)string.data, string.size) != 1) {
+ api_set_error(err, kErrorTypeValidation,
+ "border chars must be one cell");
+ }
+ size_t len = MIN(string.size, sizeof(*chars)-1);
+ memcpy(chars[i], string.data, len);
+ chars[i][len] = NUL;
+ hl_ids[i] = hl_id;
+ }
+ while (size < 8) {
+ memcpy(chars+size, chars, sizeof(*chars) * size);
+ memcpy(hl_ids+size, hl_ids, sizeof(*hl_ids) * size);
+ size <<= 1;
+ }
+ } else if (style.type == kObjectTypeString) {
+ String str = style.data.string;
+ if (str.size == 0 || strequal(str.data, "none")) {
+ fconfig->border = false;
+ return;
+ }
+ for (size_t i = 0; defaults[i].name; i++) {
+ if (strequal(str.data, defaults[i].name)) {
+ memcpy(chars, defaults[i].chars, sizeof(defaults[i].chars));
+ memset(hl_ids, 0, 8 * sizeof(*hl_ids));
+ return;
+ }
+ }
+ api_set_error(err, kErrorTypeValidation,
+ "invalid border style \"%s\"", str.data);
+ }
+}
+
+bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
+ Error *err)
+{
+ // TODO(bfredl): use a get/has_key interface instead and get rid of extra
+ // flags
+ bool has_row = false, has_col = false, has_relative = false;
+ bool has_external = false, has_window = false;
+ bool has_width = false, has_height = false;
+ bool has_bufpos = false;
+
+ for (size_t i = 0; i < config.size; i++) {
+ char *key = config.items[i].key.data;
+ Object val = config.items[i].value;
+ if (!strcmp(key, "row")) {
+ has_row = true;
+ if (val.type == kObjectTypeInteger) {
+ fconfig->row = (double)val.data.integer;
+ } else if (val.type == kObjectTypeFloat) {
+ fconfig->row = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'row' key must be Integer or Float");
+ return false;
+ }
+ } else if (!strcmp(key, "col")) {
+ has_col = true;
+ if (val.type == kObjectTypeInteger) {
+ fconfig->col = (double)val.data.integer;
+ } else if (val.type == kObjectTypeFloat) {
+ fconfig->col = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'col' key must be Integer or Float");
+ return false;
+ }
+ } else if (strequal(key, "width")) {
+ has_width = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->width = (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'width' key must be a positive Integer");
+ return false;
+ }
+ } else if (strequal(key, "height")) {
+ has_height = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->height= (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'height' key must be a positive Integer");
+ return false;
+ }
+ } else if (!strcmp(key, "anchor")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'anchor' key must be String");
+ return false;
+ }
+ if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'anchor' key");
+ return false;
+ }
+ } else if (!strcmp(key, "relative")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'relative' key must be String");
+ return false;
+ }
+ // ignore empty string, to match nvim_win_get_config
+ if (val.data.string.size > 0) {
+ has_relative = true;
+ if (!parse_float_relative(val.data.string, &fconfig->relative)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'relative' key");
+ return false;
+ }
+ }
+ } else if (!strcmp(key, "win")) {
+ has_window = true;
+ if (val.type != kObjectTypeInteger
+ && val.type != kObjectTypeWindow) {
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key must be Integer or Window");
+ return false;
+ }
+ fconfig->window = (Window)val.data.integer;
+ } else if (!strcmp(key, "bufpos")) {
+ if (val.type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation,
+ "'bufpos' key must be Array");
+ return false;
+ }
+ if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'bufpos' key");
+ return false;
+ }
+ has_bufpos = true;
+ } else if (!strcmp(key, "external")) {
+ if (val.type == kObjectTypeInteger) {
+ fconfig->external = val.data.integer;
+ } else if (val.type == kObjectTypeBoolean) {
+ fconfig->external = val.data.boolean;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'external' key must be Boolean");
+ return false;
+ }
+ has_external = fconfig->external;
+ } else if (!strcmp(key, "focusable")) {
+ if (val.type == kObjectTypeInteger) {
+ fconfig->focusable = val.data.integer;
+ } else if (val.type == kObjectTypeBoolean) {
+ fconfig->focusable = val.data.boolean;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'focusable' key must be Boolean");
+ return false;
+ }
+ } else if (!strcmp(key, "border")) {
+ parse_border_style(val, fconfig, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ } else if (!strcmp(key, "style")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'style' key must be String");
+ return false;
+ }
+ if (val.data.string.data[0] == NUL) {
+ fconfig->style = kWinStyleUnused;
+ } else if (striequal(val.data.string.data, "minimal")) {
+ fconfig->style = kWinStyleMinimal;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'style' key");
+ }
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid key '%s'", key);
+ return false;
+ }
+ }
+
+ if (has_window && !(has_relative
+ && fconfig->relative == kFloatRelativeWindow)) {
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key is only valid with relative='win'");
+ return false;
+ }
+
+ if ((has_relative && fconfig->relative == kFloatRelativeWindow)
+ && (!has_window || fconfig->window == 0)) {
+ fconfig->window = curwin->handle;
+ }
+
+ if (has_window && !has_bufpos) {
+ fconfig->bufpos.lnum = -1;
+ }
+
+ if (has_bufpos) {
+ if (!has_row) {
+ fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
+ has_row = true;
+ }
+ if (!has_col) {
+ fconfig->col = 0;
+ has_col = true;
+ }
+ }
+
+ if (has_relative && has_external) {
+ api_set_error(err, kErrorTypeValidation,
+ "Only one of 'relative' and 'external' must be used");
+ return false;
+ } else if (!reconf && !has_relative && !has_external) {
+ api_set_error(err, kErrorTypeValidation,
+ "One of 'relative' and 'external' must be used");
+ return false;
+ } else if (has_relative) {
+ fconfig->external = false;
+ }
+
+ if (!reconf && !(has_height && has_width)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Must specify 'width' and 'height'");
+ return false;
+ }
+
+ if (fconfig->external && !ui_has(kUIMultigrid)) {
+ api_set_error(err, kErrorTypeValidation,
+ "UI doesn't support external windows");
+ return false;
+ }
+
+ if (has_relative != has_row || has_row != has_col) {
+ api_set_error(err, kErrorTypeValidation,
+ "'relative' requires 'row'/'col' or 'bufpos'");
+ return false;
+ }
+ return true;
+}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index b94c99dc5e..787b6addc9 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -39,6 +39,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/fileio.h"
+#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/state.h"
@@ -241,8 +242,7 @@ void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err)
///
/// @param ns_id the namespace to activate
/// @param[out] err Error details, if any
-void nvim_set_hl_ns(Integer ns_id, Error *err)
- FUNC_API_SINCE(7)
+void nvim__set_hl_ns(Integer ns_id, Error *err)
FUNC_API_FAST
{
if (ns_id >= 0) {
@@ -1246,6 +1246,99 @@ fail:
return 0;
}
+/// Open a terminal instance in a buffer
+///
+/// By default (and currently the only option) the terminal will not be
+/// connected to an external process. Instead, input send on the channel
+/// will be echoed directly by the terminal. This is useful to disply
+/// ANSI terminal sequences returned as part of a rpc message, or similar.
+///
+/// Note: to directly initiate the terminal using the right size, display the
+/// buffer in a configured window before calling this. For instance, for a
+/// floating display, first create an empty buffer using |nvim_create_buf()|,
+/// then display it using |nvim_open_win()|, and then call this function.
+/// Then |nvim_chan_send()| cal be called immediately to process sequences
+/// in a virtual terminal having the intended size.
+///
+/// @param buffer the buffer to use (expected to be empty)
+/// @param opts Optional parameters. Reserved for future use.
+/// @param[out] err Error details, if any
+Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err)
+ FUNC_API_SINCE(7)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return 0;
+ }
+
+ TerminalOptions topts;
+ Channel *chan = channel_alloc(kChannelStreamInternal);
+ topts.data = chan;
+ // NB: overriden in terminal_check_size if a window is already
+ // displaying the buffer
+ topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0);
+ topts.height = (uint16_t)curwin->w_height_inner;
+ topts.write_cb = term_write;
+ topts.resize_cb = term_resize;
+ topts.close_cb = term_close;
+ Terminal *term = terminal_open(buf, topts);
+ terminal_check_size(term);
+ chan->term = term;
+ channel_incref(chan);
+ return (Integer)chan->id;
+}
+
+static void term_write(char *buf, size_t size, void *data)
+{
+ // TODO(bfredl): lua callback
+}
+
+static void term_resize(uint16_t width, uint16_t height, void *data)
+{
+ // TODO(bfredl): lua callback
+}
+
+static void term_close(void *data)
+{
+ Channel *chan = data;
+ terminal_destroy(chan->term);
+ chan->term = NULL;
+ channel_decref(chan);
+}
+
+
+/// Send data to channel `id`. For a job, it writes it to the
+/// stdin of the process. For the stdio channel |channel-stdio|,
+/// it writes to Nvim's stdout. For an internal terminal instance
+/// (|nvim_open_term()|) it writes directly to terimal output.
+/// See |channel-bytes| for more information.
+///
+/// This function writes raw data, not RPC messages. If the channel
+/// was created with `rpc=true` then the channel expects RPC
+/// messages, use |vim.rpcnotify()| and |vim.rpcrequest()| instead.
+///
+/// @param chan id of the channel
+/// @param data data to write. 8-bit clean: can contain NUL bytes.
+/// @param[out] err Error details, if any
+void nvim_chan_send(Integer chan, String data, Error *err)
+ FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY FUNC_API_LUA_ONLY
+{
+ const char *error = NULL;
+ if (!data.size) {
+ return;
+ }
+
+ channel_send((uint64_t)chan, data.data, data.size, &error);
+ if (error) {
+ api_set_error(err, kErrorTypeValidation, "%s", error);
+ }
+}
+
/// Open a new window.
///
/// Currently this is used to open floating and external windows.
@@ -1323,6 +1416,25 @@ fail:
/// end-of-buffer region is hidden by setting `eob` flag of
/// 'fillchars' to a space char, and clearing the
/// |EndOfBuffer| region in 'winhighlight'.
+/// - `border`: style of (optional) window border. This can either be a string
+/// or an array. the string values are:
+/// - "none" No border. This is the default
+/// - "single" a single line box
+/// - "double" a double line box
+/// If it is an array it should be an array of eight items or any divisor of
+/// eight. The array will specifify the eight chars building up the border
+/// in a clockwise fashion starting with the top-left corner. As, an
+/// example, the double box style could be specified as:
+/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]
+/// if the number of chars are less than eight, they will be repeated. Thus
+/// an ASCII border could be specified as:
+/// [ "/", "-", "\\", "|" ]
+/// or all chars the same as:
+/// [ "x" ]
+/// By default `FloatBorder` highlight is used which links to `VertSplit`
+/// when not defined. It could also be specified by character:
+/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]
+///
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
@@ -1546,8 +1658,8 @@ theend:
/// - "c" |charwise| mode
/// - "l" |linewise| mode
/// - "" guess by contents, see |setreg()|
-/// @param after Insert after cursor (like |p|), or before (like |P|).
-/// @param follow Place cursor at end of inserted text.
+/// @param after If true insert after cursor (like |p|), or before (like |P|).
+/// @param follow If true place cursor at end of inserted text.
/// @param[out] err Error details, if any
void nvim_put(ArrayOf(String) lines, String type, Boolean after,
Boolean follow, Error *err)
@@ -2738,8 +2850,8 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
g = &pum_grid;
} else if (grid > 1) {
win_T *wp = get_win_by_grid_handle((handle_T)grid);
- if (wp != NULL && wp->w_grid.chars != NULL) {
- g = &wp->w_grid;
+ if (wp != NULL && wp->w_grid_alloc.chars != NULL) {
+ g = &wp->w_grid_alloc;
} else {
api_set_error(err, kErrorTypeValidation,
"No grid with the given handle");
@@ -2848,7 +2960,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts,
String k = opts.items[i].key;
Object *v = &opts.items[i].value;
size_t j;
- for (j = 0; cbs[j].name; j++) {
+ for (j = 0; cbs[j].name && cbs[j].dest; j++) {
if (strequal(cbs[j].name, k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation,
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index f4af1632ec..89fa2f86fb 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -492,6 +492,35 @@ Dictionary nvim_win_get_config(Window window, Error *err)
return rv;
}
+/// Closes the window and hide the buffer it contains (like |:hide| with a
+/// |window-ID|).
+///
+/// Like |:hide| the buffer becomes hidden unless another window is editing it,
+/// or 'bufhidden' is `unload`, `delete` or `wipe` as opposed to |:close| or
+/// |nvim_win_close|, which will close the buffer.
+///
+/// @param window Window handle, or 0 for current window
+/// @param[out] err Error details, if any
+void nvim_win_hide(Window window, Error *err)
+ FUNC_API_SINCE(7)
+ FUNC_API_CHECK_TEXTLOCK
+{
+ win_T *win = find_window_by_handle(window, err);
+ if (!win) {
+ return;
+ }
+
+ tabpage_T *tabpage = win_find_tabpage(win);
+ TryState tstate;
+ try_enter(&tstate);
+ if (tabpage == curtab) {
+ win_close(win, false);
+ } else {
+ win_close_othertab(win, false, tabpage);
+ }
+ vim_ignored = try_leave(&tstate, err);
+}
+
/// Closes the window (like |:close| with a |window-ID|).
///
/// @param window Window handle, or 0 for current window
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 3de2e0f342..f71075ae74 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -968,7 +968,7 @@ static int do_autocmd_event(event_T event,
// Implementation of ":doautocmd [group] event [fname]".
// Return OK for success, FAIL for failure;
int do_doautocmd(char_u *arg,
- int do_msg, // give message for no matching autocmds?
+ bool do_msg, // give message for no matching autocmds?
bool *did_something)
{
char_u *fname;
@@ -1017,11 +1017,12 @@ int do_doautocmd(char_u *arg,
// ":doautoall": execute autocommands for each loaded buffer.
void ex_doautoall(exarg_T *eap)
{
- int retval;
+ int retval = OK;
aco_save_T aco;
char_u *arg = eap->arg;
int call_do_modelines = check_nomodeline(&arg);
bufref_T bufref;
+ bool did_aucmd;
// This is a bit tricky: For some commands curwin->w_buffer needs to be
// equal to curbuf, but for some buffers there may not be a window.
@@ -1029,14 +1030,14 @@ void ex_doautoall(exarg_T *eap)
// gives problems when the autocommands make changes to the list of
// buffers or windows...
FOR_ALL_BUFFERS(buf) {
- if (buf->b_ml.ml_mfp == NULL) {
+ // Only do loaded buffers and skip the current buffer, it's done last.
+ if (buf->b_ml.ml_mfp == NULL || buf == curbuf) {
continue;
}
// Find a window for this buffer and save some values.
aucmd_prepbuf(&aco, buf);
set_bufref(&bufref, buf);
- bool did_aucmd;
// execute the autocommands for this buffer
retval = do_doautocmd(arg, false, &did_aucmd);
@@ -1052,10 +1053,19 @@ void ex_doautoall(exarg_T *eap)
// Stop if there is some error or buffer was deleted.
if (retval == FAIL || !bufref_valid(&bufref)) {
+ retval = FAIL;
break;
}
}
+ // Execute autocommands for the current buffer last.
+ if (retval == OK) {
+ (void)do_doautocmd(arg, false, &did_aucmd);
+ if (call_do_modelines && did_aucmd) {
+ do_modelines(0);
+ }
+ }
+
check_cursor(); // just in case lines got deleted
}
@@ -1182,10 +1192,10 @@ void aucmd_restbuf(aco_save_T *aco)
win_remove(curwin, NULL);
handle_unregister_window(curwin);
- if (curwin->w_grid.chars != NULL) {
- ui_comp_remove_grid(&curwin->w_grid);
- ui_call_win_hide(curwin->w_grid.handle);
- grid_free(&curwin->w_grid);
+ if (curwin->w_grid_alloc.chars != NULL) {
+ ui_comp_remove_grid(&curwin->w_grid_alloc);
+ ui_call_win_hide(curwin->w_grid_alloc.handle);
+ grid_free(&curwin->w_grid_alloc);
}
aucmd_win_used = false;
@@ -1661,11 +1671,13 @@ static bool apply_autocmds_group(event_T event,
did_filetype = false;
while (au_pending_free_buf != NULL) {
buf_T *b = au_pending_free_buf->b_next;
+
xfree(au_pending_free_buf);
au_pending_free_buf = b;
}
while (au_pending_free_win != NULL) {
win_T *w = au_pending_free_win->w_next;
+
xfree(au_pending_free_win);
au_pending_free_win = w;
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index c42a0e2dad..c7ec3a456c 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -282,7 +282,7 @@ int open_buffer(
// Set/reset the Changed flag first, autocmds may change the buffer.
// Apply the automatic commands, before processing the modelines.
- // So the modelines have priority over auto commands.
+ // So the modelines have priority over autocommands.
// When reading stdin, the buffer contents always needs writing, so set
// the changed flag. Unless in readonly mode: "ls | nvim -R -".
@@ -1946,6 +1946,13 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_fo);
clear_string_option(&buf->b_p_flp);
clear_string_option(&buf->b_p_isk);
+ clear_string_option(&buf->b_p_vsts);
+ xfree(buf->b_p_vsts_nopaste);
+ buf->b_p_vsts_nopaste = NULL;
+ xfree(buf->b_p_vsts_array);
+ buf->b_p_vsts_array = NULL;
+ clear_string_option(&buf->b_p_vts);
+ XFREE_CLEAR(buf->b_p_vts_array);
clear_string_option(&buf->b_p_keymap);
keymap_ga_clear(&buf->b_kmap_ga);
ga_clear(&buf->b_kmap_ga);
@@ -5375,8 +5382,8 @@ bool bt_terminal(const buf_T *const buf)
return buf != NULL && buf->b_p_bt[0] == 't';
}
-// Return true if "buf" is a "nofile", "acwrite" or "terminal" buffer.
-// This means the buffer name is not a file name.
+// Return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
+// buffer. This means the buffer name is not a file name.
bool bt_nofile(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5386,7 +5393,8 @@ bool bt_nofile(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p');
}
-// Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer.
+// Return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
+// buffer.
bool bt_dontwrite(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index a05bd6fcc7..b36b7beab8 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -743,6 +743,11 @@ struct file_buffer {
long b_p_wm; ///< 'wrapmargin'
long b_p_wm_nobin; ///< b_p_wm saved for binary mode
long b_p_wm_nopaste; ///< b_p_wm saved for paste mode
+ char_u *b_p_vsts; ///< 'varsofttabstop'
+ long *b_p_vsts_array; ///< 'varsofttabstop' in internal format
+ char_u *b_p_vsts_nopaste; ///< b_p_vsts saved for paste mode
+ char_u *b_p_vts; ///< 'vartabstop'
+ long *b_p_vts_array; ///< 'vartabstop' in internal format
char_u *b_p_keymap; ///< 'keymap'
// local values for options which are normally global
@@ -1079,6 +1084,10 @@ typedef struct {
bool external;
bool focusable;
WinStyle style;
+ bool border;
+ schar_T border_chars[8];
+ int border_hl_ids[8];
+ int border_attr[8];
} FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
@@ -1192,6 +1201,7 @@ struct window_S {
int tab1; ///< first tab character
int tab2; ///< second tab character
int tab3; ///< third tab character
+ int lead;
int trail;
int conceal;
} w_p_lcs_chars;
@@ -1256,6 +1266,11 @@ struct window_S {
int w_height_request;
int w_width_request;
+ int w_border_adj;
+ // outer size of window grid, including border
+ int w_height_outer;
+ int w_width_outer;
+
/*
* === start of cached values ====
*/
@@ -1331,7 +1346,8 @@ struct window_S {
// w_redr_type is REDRAW_TOP
linenr_T w_redraw_top; // when != 0: first line needing redraw
linenr_T w_redraw_bot; // when != 0: last line needing redraw
- int w_redr_status; // if TRUE status line must be redrawn
+ bool w_redr_status; // if true status line must be redrawn
+ bool w_redr_border; // if true border must be redrawn
// remember what is shown in the ruler for this window (if 'ruler' set)
pos_T w_ru_cursor; // cursor position shown in ruler
@@ -1409,6 +1425,7 @@ struct window_S {
int w_tagstacklen; // number of tags on stack
ScreenGrid w_grid; // the grid specific to the window
+ ScreenGrid w_grid_alloc; // the grid specific to the window
bool w_pos_changed; // true if window position changed
bool w_floating; ///< whether the window is floating
FloatConfig w_float_config;
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 0f5081c94c..38bd591eca 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -828,6 +828,7 @@ int copy_indent(int size, char_u *src)
int tab_pad;
int ind_done;
int round;
+ int ind_col;
// Round 1: compute the number of characters needed for the indent
// Round 2: copy the characters.
@@ -835,13 +836,15 @@ int copy_indent(int size, char_u *src)
todo = size;
ind_len = 0;
ind_done = 0;
+ ind_col = 0;
s = src;
// Count/copy the usable portion of the source line.
while (todo > 0 && ascii_iswhite(*s)) {
if (*s == TAB) {
- tab_pad = (int)curbuf->b_p_ts
- - (ind_done % (int)curbuf->b_p_ts);
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target.
if (todo < tab_pad) {
@@ -849,9 +852,11 @@ int copy_indent(int size, char_u *src)
}
todo -= tab_pad;
ind_done += tab_pad;
+ ind_col += tab_pad;
} else {
todo--;
ind_done++;
+ ind_col++;
}
ind_len++;
@@ -862,11 +867,12 @@ int copy_indent(int size, char_u *src)
}
// Fill to next tabstop with a tab, if possible.
- tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+ tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_array);
if ((todo >= tab_pad) && !curbuf->b_p_et) {
todo -= tab_pad;
ind_len++;
+ ind_col += tab_pad;
if (p != NULL) {
*p++ = TAB;
@@ -874,12 +880,20 @@ int copy_indent(int size, char_u *src)
}
// Add tabs required for indent.
- while (todo >= (int)curbuf->b_p_ts && !curbuf->b_p_et) {
- todo -= (int)curbuf->b_p_ts;
- ind_len++;
-
- if (p != NULL) {
- *p++ = TAB;
+ if (!curbuf->b_p_et) {
+ for (;;) {
+ tab_pad = tabstop_padding(ind_col,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+ if (todo < tab_pad) {
+ break;
+ }
+ todo -= tab_pad;
+ ind_len++;
+ ind_col += tab_pad;
+ if (p != NULL) {
+ *p++ = TAB;
+ }
}
}
@@ -1029,7 +1043,9 @@ int open_line(
|| do_si
) {
// count white space on current line
- newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts, false);
+ newindent = get_indent_str_vtab(saved_line,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array, false);
if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) {
newindent = second_line_indent; // for ^^D command in insert mode
}
@@ -1453,7 +1469,9 @@ int open_line(
if (curbuf->b_p_ai
|| do_si
) {
- newindent = get_indent_str(leader, (int)curbuf->b_p_ts, false);
+ newindent = get_indent_str_vtab(leader,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array, false);
}
// Add the indent offset
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 09a34ca9fe..7a08ba58d0 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -161,7 +161,7 @@ void channel_init(void)
///
/// Channel is allocated with refcount 1, which should be decreased
/// when the underlying stream closes.
-static Channel *channel_alloc(ChannelStreamType type)
+Channel *channel_alloc(ChannelStreamType type)
{
Channel *chan = xcalloc(1, sizeof(*chan));
if (type == kChannelStreamStdio) {
@@ -503,7 +503,7 @@ size_t channel_send(uint64_t id, char *data, size_t len, const char **error)
{
Channel *chan = find_channel(id);
if (!chan) {
- EMSG(_(e_invchan));
+ *error = _(e_invchan);
goto err;
}
@@ -518,6 +518,11 @@ size_t channel_send(uint64_t id, char *data, size_t len, const char **error)
return len * written;
}
+ if (chan->streamtype == kChannelStreamInternal && chan->term) {
+ terminal_receive(chan->term, data, len);
+ return len;
+ }
+
Stream *in = channel_instream(chan);
if (in->closed) {
@@ -724,8 +729,8 @@ static void channel_callback_call(Channel *chan, CallbackReader *reader)
/// Open terminal for channel
///
/// Channel `chan` is assumed to be an open pty channel,
-/// and curbuf is assumed to be a new, unmodified buffer.
-void channel_terminal_open(Channel *chan)
+/// and `buf` is assumed to be a new, unmodified buffer.
+void channel_terminal_open(buf_T *buf, Channel *chan)
{
TerminalOptions topts;
topts.data = chan;
@@ -734,8 +739,8 @@ void channel_terminal_open(Channel *chan)
topts.write_cb = term_write;
topts.resize_cb = term_resize;
topts.close_cb = term_close;
- curbuf->b_p_channel = (long)chan->id; // 'channel' option
- Terminal *term = terminal_open(topts);
+ buf->b_p_channel = (long)chan->id; // 'channel' option
+ Terminal *term = terminal_open(buf, topts);
chan->term = term;
channel_incref(chan);
}
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 5ad1fe0dfd..e2d844a351 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -744,8 +744,7 @@ int vim_strnsize(char_u *s, int len)
/// @return Number of characters.
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \
- const int ts = (int)(buf)->b_p_ts; \
- return (ts - (int)(col % ts)); \
+ return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
} else { \
return ptr2cells(p); \
}
@@ -1143,8 +1142,9 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
int n;
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- n = (int)wp->w_buffer->b_p_ts;
- return n - (col % n);
+ return tabstop_padding(col,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array);
}
n = ptr2cells(s);
@@ -1211,6 +1211,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
char_u *line; // start of the line
int incr;
int head;
+ long *vts = wp->w_buffer->b_p_vts_array;
int ts = (int)wp->w_buffer->b_p_ts;
int c;
@@ -1251,7 +1252,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
// A tab gets expanded, depending on the current column
if (c == TAB) {
- incr = ts - (vcol % ts);
+ incr = tabstop_padding(vcol, ts, vts);
} else {
// For utf-8, if the byte is >= 0x80, need to look at
// further bytes to find the cell width.
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index a1289f202a..e16598e7d2 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -145,8 +145,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i);
if (item.virt_text_owned) {
- clear_virttext(item.virt_text);
- xfree(item.virt_text);
+ clear_virttext(&item.virt_text);
}
}
kv_size(state->active) = 0;
@@ -229,8 +228,8 @@ static void decor_add(DecorState *state, int start_row, int start_col,
HlRange range = { start_row, start_col, end_row, end_col,
attr_id, MAX(priority, decor->priority),
- kv_size(decor->virt_text) ? &decor->virt_text : NULL,
- decor->virt_text_pos,
+ decor->virt_text,
+ decor->virt_text_pos, decor->virt_text_hide, decor->hl_mode,
kv_size(decor->virt_text) && owned, -1 };
kv_pushp(state->active);
@@ -245,7 +244,8 @@ static void decor_add(DecorState *state, int start_row, int start_col,
kv_A(state->active, index) = range;
}
-int decor_redraw_col(buf_T *buf, int col, int virt_col, DecorState *state)
+int decor_redraw_col(buf_T *buf, int col, int virt_col, bool hidden,
+ DecorState *state)
{
if (col <= state->col_until) {
return state->current;
@@ -303,7 +303,7 @@ next_mark:
bool active = false, keep = true;
if (item.end_row < state->row
|| (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && item.virt_text)) {
+ if (!(item.start_row >= state->row && kv_size(item.virt_text))) {
keep = false;
}
} else {
@@ -323,14 +323,13 @@ next_mark:
attr = hl_combine_attr(attr, item.attr_id);
}
if ((item.start_row == state->row && item.start_col <= col)
- && item.virt_text && item.virt_col == -1) {
- item.virt_col = virt_col;
+ && kv_size(item.virt_text) && item.virt_col == -1) {
+ item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col;
}
if (keep) {
kv_A(state->active, j++) = item;
} else if (item.virt_text_owned) {
- clear_virttext(item.virt_text);
- xfree(item.virt_text);
+ clear_virttext(&item.virt_text);
}
}
kv_size(state->active) = j;
@@ -343,22 +342,26 @@ void decor_redraw_end(DecorState *state)
state->buf = NULL;
}
-VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state)
+VirtText decor_redraw_virt_text(buf_T *buf, DecorState *state)
{
- decor_redraw_col(buf, MAXCOL, MAXCOL, state);
+ decor_redraw_col(buf, MAXCOL, MAXCOL, false, state);
for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i);
- if (item.start_row == state->row && item.virt_text
+ if (item.start_row == state->row && kv_size(item.virt_text)
&& item.virt_text_pos == kVTEndOfLine) {
return item.virt_text;
}
}
- return NULL;
+ return VIRTTEXT_EMPTY;
}
void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
Decoration *decor, DecorPriority priority)
{
+ if (end_row == -1) {
+ end_row = start_row;
+ end_col = start_col;
+ }
decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true,
priority);
}
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 47bd9abbc3..c5424a1642 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -23,15 +23,26 @@ typedef enum {
kVTOverlay,
} VirtTextPos;
+typedef enum {
+ kHlModeUnknown,
+ kHlModeReplace,
+ kHlModeCombine,
+ kHlModeBlend,
+} HlMode;
+
struct Decoration
{
int hl_id; // highlight group
VirtText virt_text;
VirtTextPos virt_text_pos;
+ bool virt_text_hide;
+ HlMode hl_mode;
// TODO(bfredl): style, signs, etc
DecorPriority priority;
bool shared; // shared decoration, don't free
};
+#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \
+ kHlModeUnknown, DECOR_PRIORITY_BASE, false }
typedef struct {
int start_row;
@@ -39,9 +50,13 @@ typedef struct {
int end_row;
int end_col;
int attr_id;
+ // TODO(bfredl): embed decoration instead, perhaps using an arena
+ // for ephemerals?
DecorPriority priority;
- VirtText *virt_text;
+ VirtText virt_text;
VirtTextPos virt_text_pos;
+ bool virt_text_hide;
+ HlMode hl_mode;
bool virt_text_owned;
int virt_col;
} HlRange;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 53717229f6..49bd170bcd 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -597,7 +597,10 @@ static int insert_check(VimState *state)
s->mincol = curwin->w_wcol;
validate_cursor_col();
- if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
+ if (
+ curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(),
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array)
&& curwin->w_wrow == curwin->w_winrow
+ curwin->w_height_inner - 1 - get_scrolloff_value(curwin)
&& (curwin->w_cursor.lnum != curwin->w_topline
@@ -1024,7 +1027,7 @@ static int insert_handle_key(InsertState *s)
break;
case K_EVENT: // some event
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
goto check_pum;
case K_COMMAND: // some command
@@ -1565,7 +1568,7 @@ void edit_putchar(int c, bool highlight)
{
int attr;
- if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) {
+ if (curwin->w_grid_alloc.chars != NULL || default_grid.chars != NULL) {
update_topline(curwin); // just in case w_topline isn't valid
validate_cursor();
if (highlight) {
@@ -7266,7 +7269,6 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
char_u *p;
char_u *line;
int icase;
- int i;
if (keytyped == NUL) {
// Can happen with CTRL-Y and CTRL-E on a short line.
@@ -7351,8 +7353,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
&& p[curwin->w_cursor.col - 1] == ':'
&& p[curwin->w_cursor.col - 2] == ':') {
p[curwin->w_cursor.col - 1] = ' ';
- i = (cin_iscase(p, FALSE) || cin_isscopedecl(p)
- || cin_islabel());
+ const bool i = cin_iscase(p, false)
+ || cin_isscopedecl(p)
+ || cin_islabel();
p = get_cursor_line_ptr();
p[curwin->w_cursor.col - 1] = ':';
if (i) {
@@ -8178,24 +8181,20 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
/*
* Handle deleting one 'shiftwidth' or 'softtabstop'.
*/
- if ( mode == BACKSPACE_CHAR
- && ((p_sta && in_indent)
- || (get_sts_value() != 0
- && curwin->w_cursor.col > 0
- && (*(get_cursor_pos_ptr() - 1) == TAB
- || (*(get_cursor_pos_ptr() - 1) == ' '
- && (!*inserted_space_p
- || arrow_used)))))) {
+ if (mode == BACKSPACE_CHAR
+ && ((p_sta && in_indent)
+ || ((get_sts_value() != 0
+ || tabstop_count(curbuf->b_p_vsts_array))
+ && curwin->w_cursor.col > 0
+ && (*(get_cursor_pos_ptr() - 1) == TAB
+ || (*(get_cursor_pos_ptr() - 1) == ' '
+ && (!*inserted_space_p || arrow_used)))))) {
int ts;
colnr_T vcol;
colnr_T want_vcol;
colnr_T start_vcol;
- *inserted_space_p = FALSE;
- if (p_sta && in_indent)
- ts = get_sw_value(curbuf);
- else
- ts = get_sts_value();
+ *inserted_space_p = false;
// Compute the virtual column where we want to be. Since
// 'showbreak' may get in the way, need to get the last column of
// the previous character.
@@ -8204,7 +8203,14 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
dec_cursor();
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol);
inc_cursor();
- want_vcol = (want_vcol / ts) * ts;
+ if (p_sta && in_indent) {
+ ts = (int)get_sw_value(curbuf);
+ want_vcol = (want_vcol / ts) * ts;
+ } else {
+ want_vcol = tabstop_start(want_vcol,
+ get_sts_value(),
+ curbuf->b_p_vsts_array);
+ }
// delete characters until we are at or before want_vcol
while (vcol > want_vcol
@@ -8669,10 +8675,19 @@ static bool ins_tab(void)
can_cindent = false;
}
- // When nothing special, insert TAB like a normal character
+ // When nothing special, insert TAB like a normal character.
if (!curbuf->b_p_et
- && !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf))
- && get_sts_value() == 0) {
+ && !(
+ p_sta
+ && ind
+ // These five lines mean 'tabstop' != 'shiftwidth'
+ && ((tabstop_count(curbuf->b_p_vts_array) > 1)
+ || (tabstop_count(curbuf->b_p_vts_array) == 1
+ && tabstop_first(curbuf->b_p_vts_array)
+ != get_sw_value(curbuf))
+ || (tabstop_count(curbuf->b_p_vts_array) == 0
+ && curbuf->b_p_ts != get_sw_value(curbuf))))
+ && tabstop_count(curbuf->b_p_vsts_array) == 0 && get_sts_value() == 0) {
return true;
}
@@ -8686,16 +8701,22 @@ static bool ins_tab(void)
can_si_back = false;
AppendToRedobuff("\t");
- if (p_sta && ind) { // insert tab in indent, use "shiftwidth"
- temp = get_sw_value(curbuf);
- } else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set
- temp = get_sts_value();
- } else { // otherwise use "tabstop"
- temp = (int)curbuf->b_p_ts;
+ if (p_sta && ind) { // insert tab in indent, use 'shiftwidth'
+ temp = (int)get_sw_value(curbuf);
+ temp -= get_nolist_virtcol() % temp;
+ } else if (tabstop_count(curbuf->b_p_vsts_array) > 0
+ || curbuf->b_p_sts != 0) {
+ // use 'softtabstop' when set
+ temp = tabstop_padding(get_nolist_virtcol(),
+ get_sts_value(),
+ curbuf->b_p_vsts_array);
+ } else {
+ // otherwise use 'tabstop'
+ temp = tabstop_padding(get_nolist_virtcol(),
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
}
- temp -= get_nolist_virtcol() % temp;
-
/*
* Insert the first space with ins_char(). It will delete one char in
* replace mode. Insert the rest with ins_str(); it will not delete any
@@ -8716,7 +8737,9 @@ static bool ins_tab(void)
/*
* When 'expandtab' not set: Replace spaces by TABs where possible.
*/
- if (!curbuf->b_p_et && (get_sts_value() || (p_sta && ind))) {
+ if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0
+ || get_sts_value() > 0
+ || (p_sta && ind))) {
char_u *ptr;
char_u *saved_line = NULL; // init for GCC
pos_T pos;
@@ -8764,6 +8787,10 @@ static bool ins_tab(void)
getvcol(curwin, &fpos, &vcol, NULL, NULL);
getvcol(curwin, cursor, &want_vcol, NULL, NULL);
+ // save start of changed region for extmark_splice
+ int start_row = fpos.lnum;
+ colnr_T start_col = fpos.col;
+
// Use as many TABs as possible. Beware of 'breakindent', 'showbreak'
// and 'linebreak' adding extra virtual columns.
while (ascii_iswhite(*ptr)) {
@@ -8813,6 +8840,11 @@ static bool ins_tab(void)
replace_join(repl_off);
}
}
+ if (!(State & VREPLACE_FLAG)) {
+ extmark_splice_cols(curbuf, start_row - 1, start_col,
+ cursor->col - start_col, fpos.col - start_col,
+ kExtmarkUndo);
+ }
}
cursor->col -= i;
@@ -9124,10 +9156,16 @@ static void ins_try_si(int c)
* Get the value that w_virtcol would have when 'list' is off.
* Unless 'cpo' contains the 'L' flag.
*/
-static colnr_T get_nolist_virtcol(void)
+colnr_T get_nolist_virtcol(void)
{
- if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL)
+ // check validity of cursor in current buffer
+ if (curwin->w_buffer == NULL || curwin->w_buffer->b_ml.ml_mfp == NULL
+ || curwin->w_cursor.lnum > curwin->w_buffer->b_ml.ml_line_count) {
+ return 0;
+ }
+ if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
return getvcol_nolist(&curwin->w_cursor);
+ }
validate_virtcol();
return curwin->w_virtcol;
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6d97310c1c..9c3941b0fd 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3007,7 +3007,8 @@ static size_t varnamebuflen = 0;
/*
* Function to concatenate a prefix and a variable name.
*/
-static char_u *cat_prefix_varname(int prefix, char_u *name)
+char_u *cat_prefix_varname(int prefix, const char_u *name)
+ FUNC_ATTR_NONNULL_ALL
{
size_t len = STRLEN(name) + 3;
@@ -5280,22 +5281,18 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
if (ht_stack == NULL) {
abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
} else {
- ht_stack_T *newitem = try_malloc(sizeof(ht_stack_T));
- if (newitem == NULL) {
- abort = true;
- } else {
- newitem->ht = &dd->dv_hashtab;
- newitem->prev = *ht_stack;
- *ht_stack = newitem;
- }
+ ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T));
+ newitem->ht = &dd->dv_hashtab;
+ newitem->prev = *ht_stack;
+ *ht_stack = newitem;
}
QUEUE *w = NULL;
DictWatcher *watcher = NULL;
- QUEUE_FOREACH(w, &dd->watchers) {
+ QUEUE_FOREACH(w, &dd->watchers, {
watcher = tv_dict_watcher_node_data(w);
set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack);
- }
+ })
}
break;
}
@@ -5308,14 +5305,10 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
if (list_stack == NULL) {
abort = set_ref_in_list(ll, copyID, ht_stack);
} else {
- list_stack_T *newitem = try_malloc(sizeof(list_stack_T));
- if (newitem == NULL) {
- abort = true;
- } else {
- newitem->list = ll;
- newitem->prev = *list_stack;
- *list_stack = newitem;
- }
+ list_stack_T *const newitem = xmalloc(sizeof(list_stack_T));
+ newitem->list = ll;
+ newitem->prev = *list_stack;
+ *list_stack = newitem;
}
}
break;
@@ -5874,26 +5867,53 @@ int assert_inrange(typval_T *argvars)
FUNC_ATTR_NONNULL_ALL
{
bool error = false;
- const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
- const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
- const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
- if (error) {
- return 0;
- }
- if (actual < lower || actual > upper) {
- garray_T ga;
- prepare_assert_error(&ga);
+ if (argvars[0].v_type == VAR_FLOAT
+ || argvars[1].v_type == VAR_FLOAT
+ || argvars[2].v_type == VAR_FLOAT) {
+ const float_T flower = tv_get_float(&argvars[0]);
+ const float_T fupper = tv_get_float(&argvars[1]);
+ const float_T factual = tv_get_float(&argvars[2]);
- char msg[55];
- vim_snprintf(msg, sizeof(msg),
- "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
- lower, upper);
- fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
- ASSERT_INRANGE);
- assert_error(&ga);
- ga_clear(&ga);
- return 1;
+ if (factual < flower || factual > fupper) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+ if (argvars[3].v_type != VAR_UNKNOWN) {
+ char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL);
+ ga_concat(&ga, tofree);
+ xfree(tofree);
+ } else {
+ char msg[80];
+ vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g",
+ flower, fupper, factual);
+ ga_concat(&ga, (char_u *)msg);
+ }
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
+ } else {
+ const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
+ const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
+ const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
+
+ if (error) {
+ return 0;
+ }
+ if (actual < lower || actual > upper) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+
+ char msg[55];
+ vim_snprintf(msg, sizeof(msg),
+ "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
+ lower, upper);
+ fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
+ ASSERT_INRANGE);
+ assert_error(&ga);
+ ga_clear(&ga);
+ return 1;
+ }
}
return 0;
}
@@ -8508,7 +8528,7 @@ static bool tv_is_luafunc(typval_T *tv)
int check_luafunc_name(const char *str, bool paren)
{
const char *p = str;
- while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.') {
+ while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') {
p++;
}
if (*p != (paren ? '(' : NUL)) {
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index eac0feafcf..72168060cc 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -309,7 +309,7 @@ return {
setwinvar={args=3},
sha256={args=1},
shellescape={args={1, 2}},
- shiftwidth={},
+ shiftwidth={args={0, 1}},
sign_define={args={1, 2}},
sign_getdefined={args={0, 1}},
sign_getplaced={args={0, 2}},
@@ -341,6 +341,7 @@ return {
string={args=1},
strlen={args=1},
strpart={args={2, 4}},
+ strptime={args=2},
strridx={args={2, 3}},
strtrans={args=1},
strwidth={args=1},
@@ -392,6 +393,7 @@ return {
win_id2tabwin={args=1},
win_id2win={args=1},
win_screenpos={args=1},
+ win_splitmove={args={2, 3}},
winbufnr={args=1},
wincol={},
windowsversion={},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 8c8e0d568b..deeda28571 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -117,8 +117,12 @@ char_u *get_function_name(expand_T *xp, int idx)
intidx = -1;
if (intidx < 0) {
name = get_user_func_name(xp, idx);
- if (name != NULL)
+ if (name != NULL) {
+ if (*name != '<' && STRNCMP("g:", xp->xp_pattern, 2) == 0) {
+ return cat_prefix_varname('g', name);
+ }
return name;
+ }
}
while ((size_t)++intidx < ARRAY_SIZE(functions)
&& functions[intidx].name[0] == '\0') {
@@ -3029,10 +3033,11 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_UNKNOWN) {
// getchar(): blocking wait.
+ // TODO(bfredl): deduplicate shared logic with state_enter ?
if (!(char_avail() || using_script() || input_available())) {
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
if (!multiqueue_empty(main_loop.events)) {
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
continue;
}
}
@@ -3975,6 +3980,87 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
}
+//
+// Move the window wp into a new split of targetwin in a given direction
+//
+static void win_move_into_split(win_T *wp, win_T *targetwin,
+ int size, int flags)
+{
+ int dir;
+ int height = wp->w_height;
+ win_T *oldwin = curwin;
+
+ if (wp == targetwin) {
+ return;
+ }
+
+ // Jump to the target window
+ if (curwin != targetwin) {
+ win_goto(targetwin);
+ }
+
+ // Remove the old window and frame from the tree of frames
+ (void)winframe_remove(wp, &dir, NULL);
+ win_remove(wp, NULL);
+ last_status(false); // may need to remove last status line
+ (void)win_comp_pos(); // recompute window positions
+
+ // Split a window on the desired side and put the old window there
+ (void)win_split_ins(size, flags, wp, dir);
+
+ // If splitting horizontally, try to preserve height
+ if (size == 0 && !(flags & WSP_VERT)) {
+ win_setheight_win(height, wp);
+ if (p_ea) {
+ win_equal(wp, true, 'v');
+ }
+ }
+
+ if (oldwin != curwin) {
+ win_goto(oldwin);
+ }
+}
+
+// "win_splitmove()" function
+static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp;
+ win_T *targetwin;
+ int flags = 0, size = 0;
+
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ targetwin = find_win_by_nr_or_id(&argvars[1]);
+
+ if (wp == NULL || targetwin == NULL || wp == targetwin
+ || !win_valid(wp) || !win_valid(targetwin)
+ || win_valid_floating(wp) || win_valid_floating(targetwin)) {
+ EMSG(_(e_invalwindow));
+ rettv->vval.v_number = -1;
+ return;
+ }
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ dict_T *d;
+ dictitem_T *di;
+
+ if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ d = argvars[2].vval.v_dict;
+ if (tv_dict_get_number(d, "vertical")) {
+ flags |= WSP_VERT;
+ }
+ if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) {
+ flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
+ }
+ size = tv_dict_get_number(d, "size");
+ }
+
+ win_move_into_split(wp, targetwin, size, flags);
+}
+
// "getwinpos({timeout})" function
static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -4226,6 +4312,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"title",
"user-commands", // was accidentally included in 5.4
"user_commands",
+ "vartabs",
"vertsplit",
"virtualedit",
"visual",
@@ -8723,8 +8810,6 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (set_tagstack(wp, d, action) == OK) {
rettv->vval.v_number = 0;
- } else {
- EMSG(_(e_listreq));
}
}
@@ -8764,6 +8849,18 @@ static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ rettv->vval.v_number = 0;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ long col;
+
+ col = (long)tv_get_number_chk(argvars, NULL);
+ if (col < 0) {
+ return; // type error; errmsg already given
+ }
+ rettv->vval.v_number = get_sw_value_col(curbuf, col);
+ return;
+ }
rettv->vval.v_number = get_sw_value(curbuf);
}
@@ -8905,7 +9002,7 @@ static void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
- // Sign identifer
+ // Sign identifier
sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
if (notanum) {
return;
@@ -8953,7 +9050,7 @@ static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
- // Sign identifer
+ // Sign identifier
sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
if (notanum) {
return;
@@ -10105,6 +10202,38 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len);
}
+// "strptime({format}, {timestring})" function
+static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ char fmt_buf[NUMBUFLEN];
+ char str_buf[NUMBUFLEN];
+
+ struct tm tmval = {
+ .tm_isdst = -1,
+ };
+ char *fmt = (char *)tv_get_string_buf(&argvars[0], fmt_buf);
+ char *str = (char *)tv_get_string_buf(&argvars[1], str_buf);
+
+ vimconv_T conv = {
+ .vc_type = CONV_NONE,
+ };
+ char_u *enc = enc_locale();
+ convert_setup(&conv, p_enc, enc);
+ if (conv.vc_type != CONV_NONE) {
+ fmt = (char *)string_convert(&conv, (char_u *)fmt, NULL);
+ }
+ if (fmt == NULL
+ || os_strptime(str, fmt, &tmval) == NULL
+ || (rettv->vval.v_number = mktime(&tmval)) == -1) {
+ rettv->vval.v_number = 0;
+ }
+ if (conv.vc_type != CONV_NONE) {
+ xfree(fmt);
+ }
+ convert_setup(&conv, NULL, NULL);
+ xfree(enc);
+}
+
/*
* "strridx()" function
*/
@@ -10730,7 +10859,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
INTEGER_OBJ(pid), false, false, &err);
api_clear_error(&err);
- channel_terminal_open(chan);
+ channel_terminal_open(curbuf, chan);
channel_create_event(chan, NULL);
}
@@ -11292,17 +11421,23 @@ static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int winnr = 1;
garray_T ga;
char_u buf[50];
ga_init(&ga, (int)sizeof(char), 70);
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height);
- ga_concat(&ga, buf);
- sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width);
- ga_concat(&ga, buf);
- ++winnr;
+
+ // Do this twice to handle some window layouts properly.
+ for (int i = 0; i < 2; i++) {
+ int winnr = 1;
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ snprintf((char *)buf, sizeof(buf), "%dresize %d|", winnr,
+ wp->w_height);
+ ga_concat(&ga, buf);
+ snprintf((char *)buf, sizeof(buf), "vert %dresize %d|", winnr,
+ wp->w_width);
+ ga_concat(&ga, buf);
+ winnr++;
+ }
}
ga_append(&ga, NUL);
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 9be487f4fd..fe3d147040 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1109,6 +1109,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern,
watcher->key_pattern_len = key_pattern_len;
watcher->callback = callback;
watcher->busy = false;
+ watcher->needs_free = false;
QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node);
}
@@ -1182,22 +1183,30 @@ bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern,
QUEUE *w = NULL;
DictWatcher *watcher = NULL;
bool matched = false;
- QUEUE_FOREACH(w, &dict->watchers) {
+ bool queue_is_busy = false;
+ QUEUE_FOREACH(w, &dict->watchers, {
watcher = tv_dict_watcher_node_data(w);
+ if (watcher->busy) {
+ queue_is_busy = true;
+ }
if (tv_callback_equal(&watcher->callback, &callback)
&& watcher->key_pattern_len == key_pattern_len
&& memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) {
matched = true;
break;
}
- }
+ })
if (!matched) {
return false;
}
- QUEUE_REMOVE(w);
- tv_dict_watcher_free(watcher);
+ if (queue_is_busy) {
+ watcher->needs_free = true;
+ } else {
+ QUEUE_REMOVE(w);
+ tv_dict_watcher_free(watcher);
+ }
return true;
}
@@ -1258,9 +1267,10 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
typval_T rettv;
+ bool any_needs_free = false;
dict->dv_refcount++;
QUEUE *w;
- QUEUE_FOREACH(w, &dict->watchers) {
+ QUEUE_FOREACH(w, &dict->watchers, {
DictWatcher *watcher = tv_dict_watcher_node_data(w);
if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) {
rettv = TV_INITIAL_VALUE;
@@ -1268,7 +1278,19 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
callback_call(&watcher->callback, 3, argv, &rettv);
watcher->busy = false;
tv_clear(&rettv);
+ if (watcher->needs_free) {
+ any_needs_free = true;
+ }
}
+ })
+ if (any_needs_free) {
+ QUEUE_FOREACH(w, &dict->watchers, {
+ DictWatcher *watcher = tv_dict_watcher_node_data(w);
+ if (watcher->needs_free) {
+ QUEUE_REMOVE(w);
+ tv_dict_watcher_free(watcher);
+ }
+ })
}
tv_dict_unref(dict);
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 6fcb01aace..2b4612016b 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -89,6 +89,7 @@ typedef struct dict_watcher {
size_t key_pattern_len;
QUEUE node;
bool busy; // prevent recursion if the dict is changed in the callback
+ bool needs_free;
} DictWatcher;
/// Bool variable values
@@ -341,8 +342,9 @@ struct ufunc {
///< used for s: variables
int uf_refcount; ///< reference count, see func_name_refcount()
funccall_T *uf_scoped; ///< l: local variables for closure
- char_u uf_name[]; ///< Name of function; can start with <SNR>123_
- ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
+ char_u uf_name[]; ///< Name of function (actual size equals name);
+ ///< can start with <SNR>123_
+ ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
};
struct partial_S {
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index c9aa3acc4d..1e6d62135c 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -119,8 +119,8 @@ static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb,
void multiqueue_free(MultiQueue *this)
{
assert(this);
- while (!QUEUE_EMPTY(&this->headtail)) {
- QUEUE *q = QUEUE_HEAD(&this->headtail);
+ QUEUE *q;
+ QUEUE_FOREACH(q, &this->headtail, {
MultiQueueItem *item = multiqueue_node_data(q);
if (this->parent) {
QUEUE_REMOVE(&item->data.item.parent_item->node);
@@ -128,7 +128,7 @@ void multiqueue_free(MultiQueue *this)
}
QUEUE_REMOVE(q);
xfree(item);
- }
+ })
xfree(this);
}
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 103c081143..d34282419a 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -712,14 +712,15 @@ void ex_retab(exarg_T *eap)
long len;
long col;
long vcol;
- long start_col = 0; /* For start of white-space string */
- long start_vcol = 0; /* For start of white-space string */
- int temp;
+ long start_col = 0; // For start of white-space string
+ long start_vcol = 0; // For start of white-space string
long old_len;
char_u *ptr;
- char_u *new_line = (char_u *)1; /* init to non-NULL */
- int did_undo; /* called u_save for current line */
- int new_ts;
+ char_u *new_line = (char_u *)1; // init to non-NULL
+ int did_undo; // called u_save for current line
+ long *new_vts_array = NULL;
+ char_u *new_ts_str; // string value of tab argument
+
int save_list;
linenr_T first_line = 0; /* first changed line */
linenr_T last_line = 0; /* last changed line */
@@ -727,14 +728,24 @@ void ex_retab(exarg_T *eap)
save_list = curwin->w_p_list;
curwin->w_p_list = 0; /* don't want list mode here */
- new_ts = getdigits_int(&(eap->arg), false, -1);
- if (new_ts < 0) {
- EMSG(_(e_positive));
+ new_ts_str = eap->arg;
+ if (!tabstop_set(eap->arg, &new_vts_array)) {
return;
}
- if (new_ts == 0)
- new_ts = curbuf->b_p_ts;
- for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) {
+ while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
+ (eap->arg)++;
+ }
+
+ // This ensures that either new_vts_array and new_ts_str are freshly
+ // allocated, or new_vts_array points to an existing array and new_ts_str
+ // is null.
+ if (new_vts_array == NULL) {
+ new_vts_array = curbuf->b_p_vts_array;
+ new_ts_str = NULL;
+ } else {
+ new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
+ }
+ for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) {
ptr = ml_get(lnum);
col = 0;
vcol = 0;
@@ -758,13 +769,12 @@ void ex_retab(exarg_T *eap)
len = num_spaces = vcol - start_vcol;
num_tabs = 0;
if (!curbuf->b_p_et) {
- temp = new_ts - (start_vcol % new_ts);
- if (num_spaces >= temp) {
- num_spaces -= temp;
- num_tabs++;
- }
- num_tabs += num_spaces / new_ts;
- num_spaces -= (num_spaces / new_ts) * new_ts;
+ int t, s;
+
+ tabstop_fromto(start_vcol, vcol,
+ curbuf->b_p_ts, new_vts_array, &t, &s);
+ num_tabs = t;
+ num_spaces = s;
}
if (curbuf->b_p_et || got_tab
|| (num_spaces + num_tabs < len)) {
@@ -817,15 +827,42 @@ void ex_retab(exarg_T *eap)
if (got_int)
EMSG(_(e_interr));
- if (curbuf->b_p_ts != new_ts)
+ // If a single value was given then it can be considered equal to
+ // either the value of 'tabstop' or the value of 'vartabstop'.
+ if (tabstop_count(curbuf->b_p_vts_array) == 0
+ && tabstop_count(new_vts_array) == 1
+ && curbuf->b_p_ts == tabstop_first(new_vts_array)) {
+ // not changed
+ } else if (tabstop_count(curbuf->b_p_vts_array) > 0
+ && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
+ // not changed
+ } else {
redraw_curbuf_later(NOT_VALID);
+ }
if (first_line != 0) {
changed_lines(first_line, 0, last_line + 1, 0L, true);
}
curwin->w_p_list = save_list; /* restore 'list' */
- curbuf->b_p_ts = new_ts;
+ if (new_ts_str != NULL) { // set the new tabstop
+ // If 'vartabstop' is in use or if the value given to retab has more
+ // than one tabstop then update 'vartabstop'.
+ long *old_vts_ary = curbuf->b_p_vts_array;
+
+ if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) {
+ set_string_option_direct("vts", -1, new_ts_str,
+ OPT_FREE | OPT_LOCAL, 0);
+ curbuf->b_p_vts_array = new_vts_array;
+ xfree(old_vts_ary);
+ } else {
+ // 'vartabstop' wasn't in use and a single value was given to
+ // retab then update 'tabstop'.
+ curbuf->b_p_ts = tabstop_first(new_vts_array);
+ xfree(new_vts_array);
+ }
+ xfree(new_ts_str);
+ }
coladvance(curwin->w_curswant);
u_clearline();
@@ -1407,19 +1444,20 @@ do_shell(
* For autocommands we want to get the output on the current screen, to
* avoid having to type return below.
*/
- msg_putchar('\r'); /* put cursor at start of line */
- msg_putchar('\n'); /* may shift screen one line up */
+ msg_putchar('\r'); // put cursor at start of line
+ msg_putchar('\n'); // may shift screen one line up
- /* warning message before calling the shell */
+ // warning message before calling the shell
if (p_warn
&& !autocmd_busy
- && msg_silent == 0)
+ && msg_silent == 0) {
FOR_ALL_BUFFERS(buf) {
if (bufIsChanged(buf)) {
MSG_PUTS(_("[No write since last change]\n"));
break;
}
}
+ }
// This ui_cursor_goto is required for when the '\n' resulted in a "delete line
// 1" command to the terminal.
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index c24ecde096..cc0ec71627 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2535,7 +2535,7 @@ void ex_source(exarg_T *eap)
static void cmd_source(char_u *fname, exarg_T *eap)
{
- if (*fname == NUL) {
+ if (eap != NULL && *fname == NUL) {
cmd_source_buffer(eap);
} else if (eap != NULL && eap->forceit) {
// ":source!": read Normal mode commands
@@ -2575,7 +2575,8 @@ static char_u *get_buffer_line(int c, void *cookie, int indent, bool do_concat)
return (char_u *)xstrdup((const char *)curr_line);
}
-static void cmd_source_buffer(exarg_T *eap)
+static void cmd_source_buffer(const exarg_T *eap)
+ FUNC_ATTR_NONNULL_ALL
{
GetBufferLineCookie cookie = {
.curr_lnum = eap->line1,
@@ -3024,6 +3025,7 @@ void scriptnames_slash_adjust(void)
# endif
/// Get a pointer to a script name. Used for ":verbose set".
+/// Message appended to "Last set from "
char_u *get_scriptname(LastSet last_set, bool *should_free)
{
*should_free = false;
@@ -3039,6 +3041,8 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
return (char_u *)_("environment variable");
case SID_ERROR:
return (char_u *)_("error handler");
+ case SID_WINLAYOUT:
+ return (char_u *)_("changed window size");
case SID_LUA:
return (char_u *)_("Lua");
case SID_API_CLIENT:
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index bc3d29a03f..3aaf171b2c 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -7494,7 +7494,7 @@ static void ex_syncbind(exarg_T *eap)
ctrl_o[0] = Ctrl_O;
ctrl_o[1] = 0;
- ins_typebuf(ctrl_o, REMAP_NONE, 0, TRUE, FALSE);
+ ins_typebuf(ctrl_o, REMAP_NONE, 0, true, false);
}
}
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index d470bfb418..38385d19b2 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -775,9 +775,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
redrawcmd();
}
- // redraw the statusline for statuslines that display the current mode
- // using the mode() function.
- if (!cmd_silent && msg_scrolled == 0) {
+ // Redraw the statusline in case it uses the current mode using the mode()
+ // function.
+ if (!cmd_silent && msg_scrolled == 0 && *p_stl != NUL) {
curwin->w_redr_status = true;
redraw_statuslines();
}
@@ -935,7 +935,7 @@ static int command_line_execute(VimState *state, int key)
if (s->c == K_EVENT || s->c == K_COMMAND) {
if (s->c == K_EVENT) {
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
} else {
do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
}
@@ -4093,9 +4093,10 @@ ExpandOne (
}
if (mode == WILD_CANCEL) {
- ss = vim_strsave(orig_save);
+ ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
} else if (mode == WILD_APPLY) {
- ss = vim_strsave(findex == -1 ? orig_save : xp->xp_files[findex]);
+ ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"") :
+ xp->xp_files[findex]);
}
/* free old names */
@@ -5117,6 +5118,18 @@ ExpandFromContext (
if (xp->xp_context == EXPAND_PACKADD) {
return ExpandPackAddDir(pat, num_file, file);
}
+
+ // When expanding a function name starting with s:, match the <SNR>nr_
+ // prefix.
+ char_u *tofree = NULL;
+ if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) {
+ const size_t len = STRLEN(pat) + 20;
+
+ tofree = xmalloc(len);
+ snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
+ pat = tofree;
+ }
+
if (xp->xp_context == EXPAND_LUA) {
ILOG("PAT %s", pat);
return nlua_expand_pat(xp, pat, num_file, file);
@@ -5195,6 +5208,7 @@ ExpandFromContext (
}
vim_regfree(regmatch.regprog);
+ xfree(tofree);
return ret;
}
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 63789b3981..09453e100d 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -387,9 +387,10 @@ static int put_view(
if (wp->w_alt_fnum) {
buf_T *const alt = buflist_findnr(wp->w_alt_fnum);
- // Set the alternate file.
+ // Set the alternate file if the buffer is listed.
if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL
&& *alt->b_fname != NUL
+ && alt->b_p_bl
&& (fputs("balt ", fd) < 0
|| ses_fname(fd, alt, flagp, true) == FAIL)) {
return FAIL;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 714bbb5780..65bd809436 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -4280,7 +4280,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
if (fname == NULL || *fname == NUL) {
retval = xmalloc(MAXPATHL + extlen + 3); // +3 for PATHSEP, "_" (Win), NUL
if (os_dirname((char_u *)retval, MAXPATHL) == FAIL
- || (fnamelen = strlen(retval)) == 0) {
+ || strlen(retval) == 0) {
xfree(retval);
return NULL;
}
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 7e78b9e9d6..d2a7c16186 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -104,8 +104,11 @@ for _,f in ipairs(shallowcopy(functions)) do
elseif startswith(f.name, "nvim_tabpage_") then
ismethod = true
end
+ f.remote = f.remote_only or not f.lua_only
+ f.lua = f.lua_only or not f.remote_only
+ f.eval = (not f.lua_only) and (not f.remote_only)
else
- f.remote_only = true
+ f.remote = true
f.since = 0
f.deprecated_since = 1
end
@@ -127,7 +130,8 @@ for _,f in ipairs(shallowcopy(functions)) do
newf.return_type = "Object"
end
newf.impl_name = f.name
- newf.remote_only = true
+ newf.lua = false
+ newf.eval = false
newf.since = 0
newf.deprecated_since = 1
functions[#functions+1] = newf
@@ -192,7 +196,7 @@ end
-- the real API.
for i = 1, #functions do
local fn = functions[i]
- if fn.impl_name == nil and not fn.lua_only then
+ if fn.impl_name == nil and fn.remote then
local args = {}
output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
@@ -323,7 +327,7 @@ void msgpack_rpc_init_method_table(void)
for i = 1, #functions do
local fn = functions[i]
- if not fn.lua_only then
+ if fn.remote then
output:write(' msgpack_rpc_add_method_handler('..
'(String) {.data = "'..fn.name..'", '..
'.size = sizeof("'..fn.name..'") - 1}, '..
@@ -492,7 +496,7 @@ local function process_function(fn)
end
for _, fn in ipairs(functions) do
- if not fn.remote_only or fn.name:sub(1, 4) == '_vim' then
+ if fn.lua or fn.name:sub(1, 4) == '_vim' then
process_function(fn)
end
end
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index d16453530f..679895421a 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -25,7 +25,7 @@ local gperfpipe = io.open(funcsfname .. '.gperf', 'wb')
local funcs = require('eval').funcs
local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all"))
for _,fun in ipairs(metadata) do
- if not (fun.remote_only or fun.lua_only) then
+ if fun.eval then
funcs[fun.name] = {
args=#fun.parameters,
func='api_wrapper',
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 2e2993ed26..9afce6e9d5 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -78,7 +78,7 @@ FileDescriptor *scriptin[NSCRIPT] = { NULL };
* Un-escaping is done by vgetc().
*/
-#define MINIMAL_SIZE 20 /* minimal size for b_str */
+#define MINIMAL_SIZE 20 // minimal size for b_str
static buffheader_T redobuff = { { NULL, { NUL } }, NULL, 0, 0 };
static buffheader_T old_redobuff = { { NULL, { NUL } }, NULL, 0, 0 };
@@ -90,7 +90,7 @@ static buffheader_T readbuf1 = { { NULL, { NUL } }, NULL, 0, 0 };
// Second read ahead buffer. Used for redo.
static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 };
-static int typeahead_char = 0; /* typeahead char that's not flushed */
+static int typeahead_char = 0; // typeahead char that's not flushed
/*
* when block_redo is TRUE redo buffer will not be changed
@@ -116,9 +116,9 @@ static bool maphash_valid = false;
/*
* List used for abbreviations.
*/
-static mapblock_T *first_abbr = NULL; /* first entry in abbrlist */
+static mapblock_T *first_abbr = NULL; // first entry in abbrlist
-static int KeyNoremap = 0; /* remapping flags */
+static int KeyNoremap = 0; // remapping flags
/*
* Variables used by vgetorpeek() and flush_buffers()
@@ -139,18 +139,18 @@ static int KeyNoremap = 0; /* remapping flags */
* typebuf.tb_noremap[typebuf.tb_off] is the first valid flag.
* (typebuf has been put in globals.h, because check_termcode() needs it).
*/
-#define RM_YES 0 /* tb_noremap: remap */
-#define RM_NONE 1 /* tb_noremap: don't remap */
-#define RM_SCRIPT 2 /* tb_noremap: remap local script mappings */
-#define RM_ABBR 4 /* tb_noremap: don't remap, do abbrev. */
+#define RM_YES 0 // tb_noremap: remap
+#define RM_NONE 1 // tb_noremap: don't remap
+#define RM_SCRIPT 2 // tb_noremap: remap local script mappings
+#define RM_ABBR 4 // tb_noremap: don't remap, do abbrev.
/* typebuf.tb_buf has three parts: room in front (for result of mappings), the
* middle for typeahead and room for new characters (which needs to be 3 *
* MAXMAPLEN) for the Amiga).
*/
#define TYPELEN_INIT (5 * (MAXMAPLEN + 3))
-static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */
-static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */
+static char_u typebuf_init[TYPELEN_INIT]; // initial typebuf.tb_buf
+static char_u noremapbuf_init[TYPELEN_INIT]; // initial typebuf.tb_noremap
static size_t last_recorded_len = 0; // number of last recorded chars
@@ -707,8 +707,9 @@ static int read_redo(bool init, bool old_redo)
break;
}
c = *p;
- if (c == NUL) /* cannot happen? */
+ if (c == NUL) { // cannot happen?
break;
+ }
}
return c;
@@ -744,14 +745,15 @@ int start_redo(long count, bool old_redo)
c = read_redo(false, old_redo);
- /* copy the buffer name, if present */
+ // copy the buffer name, if present
if (c == '"') {
add_buff(&readbuf2, "\"", 1L);
c = read_redo(false, old_redo);
- /* if a numbered buffer is used, increment the number */
- if (c >= '1' && c < '9')
- ++c;
+ // if a numbered buffer is used, increment the number
+ if (c >= '1' && c < '9') {
+ c++;
+ }
add_char_buff(&readbuf2, c);
// the expression register should be re-evaluated
@@ -763,7 +765,7 @@ int start_redo(long count, bool old_redo)
c = read_redo(false, old_redo);
}
- if (c == 'v') { /* redo Visual */
+ if (c == 'v') { // redo Visual
VIsual = curwin->w_cursor;
VIsual_active = true;
VIsual_select = false;
@@ -780,7 +782,7 @@ int start_redo(long count, bool old_redo)
add_num_buff(&readbuf2, count);
}
- /* copy from the redo buffer into the stuff buffer */
+ // copy from the redo buffer into the stuff buffer
add_char_buff(&readbuf2, c);
copy_redo(old_redo);
return OK;
@@ -838,26 +840,25 @@ static void init_typebuf(void)
}
}
-/*
- * insert a string in position 'offset' in the typeahead buffer (for "@r"
- * and ":normal" command, vgetorpeek() and check_termcode())
- *
- * If noremap is REMAP_YES, new string can be mapped again.
- * If noremap is REMAP_NONE, new string cannot be mapped again.
- * If noremap is REMAP_SKIP, fist char of new string cannot be mapped again,
- * but abbreviations are allowed.
- * If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for
- * script-local mappings.
- * If noremap is > 0, that many characters of the new string cannot be mapped.
- *
- * If nottyped is TRUE, the string does not return KeyTyped (don't use when
- * offset is non-zero!).
- *
- * If silent is true, cmd_silent is set when the characters are obtained.
- *
- * return FAIL for failure, OK otherwise
- */
-int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
+// Insert a string in position 'offset' in the typeahead buffer (for "@r"
+// and ":normal" command, vgetorpeek() and check_termcode())
+//
+// If noremap is REMAP_YES, new string can be mapped again.
+// If noremap is REMAP_NONE, new string cannot be mapped again.
+// If noremap is REMAP_SKIP, fist char of new string cannot be mapped again,
+// but abbreviations are allowed.
+// If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for
+// script-local mappings.
+// If noremap is > 0, that many characters of the new string cannot be mapped.
+//
+// If nottyped is true, the string does not return KeyTyped (don't use when
+// offset is non-zero!).
+//
+// If silent is true, cmd_silent is set when the characters are obtained.
+//
+// return FAIL for failure, OK otherwise
+int ins_typebuf(char_u *str, int noremap, int offset,
+ bool nottyped, bool silent)
{
char_u *s1, *s2;
int newlen;
@@ -890,8 +891,8 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
// often.
newoff = MAXMAPLEN + 4;
newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4);
- if (newlen < 0) { /* string is getting too long */
- EMSG(_(e_toocompl)); /* also calls flush_buffers */
+ if (newlen < 0) { // string is getting too long
+ EMSG(_(e_toocompl)); // also calls flush_buffers
setcursor();
return FAIL;
}
@@ -927,13 +928,14 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
}
typebuf.tb_len += addlen;
- /* If noremap == REMAP_SCRIPT: do remap script-local mappings. */
- if (noremap == REMAP_SCRIPT)
+ // If noremap == REMAP_SCRIPT: do remap script-local mappings.
+ if (noremap == REMAP_SCRIPT) {
val = RM_SCRIPT;
- else if (noremap == REMAP_SKIP)
+ } else if (noremap == REMAP_SKIP) {
val = RM_ABBR;
- else
+ } else {
val = RM_NONE;
+ }
/*
* Adjust typebuf.tb_noremap[] for the new characters:
@@ -962,8 +964,9 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
typebuf.tb_silent += addlen;
cmd_silent = true;
}
- if (typebuf.tb_no_abbr_cnt && offset == 0) /* and not used for abbrev.s */
+ if (typebuf.tb_no_abbr_cnt && offset == 0) { // and not used for abbrev.s
typebuf.tb_no_abbr_cnt += addlen;
+ }
return OK;
}
@@ -997,9 +1000,8 @@ void ins_char_typebuf(int c)
* Or "typebuf.tb_off" may have been changed and we would overwrite characters
* that was just added.
*/
-int
-typebuf_changed (
- int tb_change_cnt /* old value of typebuf.tb_change_cnt */
+bool typebuf_changed(
+ int tb_change_cnt // old value of typebuf.tb_change_cnt
)
{
return tb_change_cnt != 0 && (typebuf.tb_change_cnt != tb_change_cnt
@@ -1031,8 +1033,9 @@ void del_typebuf(int len, int offset)
{
int i;
- if (len == 0)
- return; /* nothing to do */
+ if (len == 0) {
+ return; // nothing to do
+ }
typebuf.tb_len -= len;
@@ -1068,23 +1071,26 @@ void del_typebuf(int len, int offset)
(size_t)(typebuf.tb_len - offset));
}
- if (typebuf.tb_maplen > offset) { /* adjust tb_maplen */
- if (typebuf.tb_maplen < offset + len)
+ if (typebuf.tb_maplen > offset) { // adjust tb_maplen
+ if (typebuf.tb_maplen < offset + len) {
typebuf.tb_maplen = offset;
- else
+ } else {
typebuf.tb_maplen -= len;
+ }
}
- if (typebuf.tb_silent > offset) { /* adjust tb_silent */
- if (typebuf.tb_silent < offset + len)
+ if (typebuf.tb_silent > offset) { // adjust tb_silent
+ if (typebuf.tb_silent < offset + len) {
typebuf.tb_silent = offset;
- else
+ } else {
typebuf.tb_silent -= len;
+ }
}
- if (typebuf.tb_no_abbr_cnt > offset) { /* adjust tb_no_abbr_cnt */
- if (typebuf.tb_no_abbr_cnt < offset + len)
+ if (typebuf.tb_no_abbr_cnt > offset) { // adjust tb_no_abbr_cnt
+ if (typebuf.tb_no_abbr_cnt < offset + len) {
typebuf.tb_no_abbr_cnt = offset;
- else
+ } else {
typebuf.tb_no_abbr_cnt -= len;
+ }
}
/* Reset the flag that text received from a client or from feedkeys()
@@ -1135,8 +1141,8 @@ static void gotchars(const char_u *chars, size_t len)
may_sync_undo();
- /* output "debug mode" message next time in debug mode */
- debug_did_msg = FALSE;
+ // output "debug mode" message next time in debug mode
+ debug_did_msg = false;
/* Since characters have been typed, consider the following to be in
* another mapping. Search string will be kept in history. */
@@ -1253,10 +1259,9 @@ void restore_typeahead(tasave_T *tp)
/*
* Open a new script file for the ":source!" command.
*/
-void
-openscript (
+void openscript(
char_u *name,
- int directly /* when TRUE execute directly */
+ bool directly // when true execute directly
)
{
if (curscript + 1 == NSCRIPT) {
@@ -1275,9 +1280,10 @@ openscript (
return;
}
- if (scriptin[curscript] != NULL) /* already reading script */
- ++curscript;
- /* use NameBuff for expanded name */
+ if (scriptin[curscript] != NULL) { // already reading script
+ curscript++;
+ }
+ // use NameBuff for expanded name
expand_env(name, NameBuff, MAXPATHL);
int error;
if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff,
@@ -1306,11 +1312,11 @@ openscript (
int save_msg_scroll = msg_scroll;
State = NORMAL;
- msg_scroll = FALSE; /* no msg scrolling in Normal mode */
- restart_edit = 0; /* don't go to Insert mode */
- p_im = FALSE; /* don't use 'insertmode' */
+ msg_scroll = false; // no msg scrolling in Normal mode
+ restart_edit = 0; // don't go to Insert mode
+ p_im = false; // don't use 'insertmode'
clear_oparg(&oa);
- finish_op = FALSE;
+ finish_op = false;
oldcurscript = curscript;
do {
@@ -1626,10 +1632,8 @@ int char_avail(void)
return retval != NUL;
}
-void
-vungetc ( /* unget one character (can only be done once!) */
- int c
-)
+// unget one character (can only be done once!)
+void vungetc(int c)
{
old_char = c;
old_mod_mask = mod_mask;
@@ -1669,10 +1673,10 @@ static int vgetorpeek(bool advance)
mapblock_T *mp2;
mapblock_T *mp_match;
int mp_match_len = 0;
- int timedout = FALSE; /* waited for more than 1 second
- for mapping to complete */
- int mapdepth = 0; /* check for recursive mapping */
- int mode_deleted = FALSE; /* set when mode has been deleted */
+ bool timedout = false; // waited for more than 1 second
+ // for mapping to complete
+ int mapdepth = 0; // check for recursive mapping
+ bool mode_deleted = false; // set when mode has been deleted
int local_State;
int mlen;
int max_mlen;
@@ -1729,8 +1733,9 @@ static int vgetorpeek(bool advance)
// needed for CTRL-W CTRL-] to open a fold, for example.
KeyStuffed = true;
}
- if (typebuf.tb_no_abbr_cnt == 0)
- typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */
+ if (typebuf.tb_no_abbr_cnt == 0) {
+ typebuf.tb_no_abbr_cnt = 1; // no abbreviations now
+ }
} else {
/*
* Loop until we either find a matching mapped key, or we
@@ -1744,10 +1749,11 @@ static int vgetorpeek(bool advance)
* inside a mapping. But call it each time for typed
* characters.
*/
- if (typebuf.tb_maplen)
+ if (typebuf.tb_maplen) {
line_breakcheck();
- else
- os_breakcheck(); /* check for CTRL-C */
+ } else {
+ os_breakcheck(); // check for CTRL-C
+ }
keylen = 0;
if (got_int) {
// flush all input
@@ -1814,11 +1820,11 @@ static int vgetorpeek(bool advance)
&& get_real_state() != SELECTMODE);
nolmaplen = 0;
}
- /* First try buffer-local mappings. */
+ // First try buffer-local mappings.
mp = curbuf->b_maphash[MAP_HASH(local_State, c1)];
mp2 = maphash[MAP_HASH(local_State, c1)];
if (mp == NULL) {
- /* There are no buffer-local mappings. */
+ // There are no buffer-local mappings.
mp = mp2;
mp2 = NULL;
}
@@ -1845,8 +1851,8 @@ static int vgetorpeek(bool advance)
|| typebuf.tb_maplen == 0)) {
int nomap = nolmaplen;
int c2;
- /* find the match length of this mapping */
- for (mlen = 1; mlen < typebuf.tb_len; ++mlen) {
+ // find the match length of this mapping
+ for (mlen = 1; mlen < typebuf.tb_len; mlen++) {
c2 = typebuf.tb_buf[typebuf.tb_off + mlen];
if (nomap > 0)
--nomap;
@@ -1901,7 +1907,7 @@ static int vgetorpeek(bool advance)
if (keylen > typebuf.tb_len) {
if (!timedout && !(mp_match != NULL
&& mp_match->m_nowait)) {
- /* break at a partly match */
+ // break at a partly match
keylen = KEYLEN_PART_MAP;
break;
}
@@ -1953,13 +1959,14 @@ static int vgetorpeek(bool advance)
setcursor();
continue;
}
- /* Need more chars for partly match. */
- if (mlen == typebuf.tb_len)
+ // Need more chars for partly match.
+ if (mlen == typebuf.tb_len) {
keylen = KEYLEN_PART_KEY;
- else if (max_mlen < mlen)
- /* no match, may have to check for termcode at
- * next character */
+ } else if (max_mlen < mlen) {
+ // no match, may have to check for termcode at
+ // next character
max_mlen = mlen + 1;
+ }
}
if ((mp == NULL || max_mlen >= mp_match_len)
@@ -1991,13 +1998,11 @@ static int vgetorpeek(bool advance)
}
}
- /* complete match */
+ // complete match
if (keylen >= 0 && keylen <= typebuf.tb_len) {
int save_m_expr;
int save_m_noremap;
int save_m_silent;
- char_u *save_m_keys;
- char_u *save_m_str;
// Write chars to script file(s)
// Note: :lmap mappings are written *after* being applied. #5658
@@ -2007,7 +2012,7 @@ static int vgetorpeek(bool advance)
}
cmd_silent = (typebuf.tb_silent > 0);
- del_typebuf(keylen, 0); /* remove the mapped keys */
+ del_typebuf(keylen, 0); // remove the mapped keys
/*
* Put the replacement string in front of mapstr.
@@ -2032,9 +2037,8 @@ static int vgetorpeek(bool advance)
*/
if (VIsual_active && VIsual_select
&& (mp->m_mode & VISUAL)) {
- VIsual_select = FALSE;
- (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE,
- 0, TRUE, FALSE);
+ VIsual_select = false;
+ (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false);
}
/* Copy the values from *mp that are used, because
@@ -2044,8 +2048,8 @@ static int vgetorpeek(bool advance)
save_m_expr = mp->m_expr;
save_m_noremap = mp->m_noremap;
save_m_silent = mp->m_silent;
- save_m_keys = NULL; /* only saved when needed */
- save_m_str = NULL; /* only saved when needed */
+ char_u *save_m_keys = NULL; // only saved when needed
+ char_u *save_m_str = NULL; // only saved when needed
/*
* Handle ":map <expr>": evaluate the {rhs} as an
@@ -2141,14 +2145,14 @@ static int vgetorpeek(bool advance)
char_u *ptr;
if (mode_displayed) {
- unshowmode(TRUE);
- mode_deleted = TRUE;
+ unshowmode(true);
+ mode_deleted = true;
}
validate_cursor();
old_wcol = curwin->w_wcol;
old_wrow = curwin->w_wrow;
- /* move cursor left, if possible */
+ // move cursor left, if possible
if (curwin->w_cursor.col != 0) {
if (curwin->w_wcol > 0) {
if (did_ai) {
@@ -2170,7 +2174,7 @@ static int vgetorpeek(bool advance)
+ curwin->w_wcol / curwin->w_width_inner;
curwin->w_wcol %= curwin->w_width_inner;
curwin->w_wcol += curwin_col_off();
- col = 0; /* no correction needed */
+ col = 0; // no correction needed
} else {
--curwin->w_wcol;
col = curwin->w_cursor.col - 1;
@@ -2197,8 +2201,9 @@ static int vgetorpeek(bool advance)
curwin->w_wcol = old_wcol;
curwin->w_wrow = old_wrow;
}
- if (c < 0)
- continue; /* end of input script reached */
+ if (c < 0) {
+ continue; // end of input script reached
+ }
// Allow mapping for just typed characters. When we get here c
// is the number of extra bytes and typebuf.tb_len is 1.
@@ -2207,20 +2212,20 @@ static int vgetorpeek(bool advance)
}
typebuf.tb_len += c;
- /* buffer full, don't map */
+ // buffer full, don't map
if (typebuf.tb_len >= typebuf.tb_maplen + MAXMAPLEN) {
- timedout = TRUE;
+ timedout = true;
continue;
}
if (ex_normal_busy > 0) {
static int tc = 0;
- /* No typeahead left and inside ":normal". Must return
- * something to avoid getting stuck. When an incomplete
- * mapping is present, behave like it timed out. */
+ // No typeahead left and inside ":normal". Must return
+ // something to avoid getting stuck. When an incomplete
+ // mapping is present, behave like it timed out.
if (typebuf.tb_len > 0) {
- timedout = TRUE;
+ timedout = true;
continue;
}
/* When 'insertmode' is set, ESC just beeps in Insert
@@ -2254,7 +2259,7 @@ static int vgetorpeek(bool advance)
if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0
&& advance && must_redraw != 0 && !need_wait_return) {
update_screen(0);
- setcursor(); /* put cursor back where it belongs */
+ setcursor(); // put cursor back where it belongs
}
/*
@@ -2267,16 +2272,16 @@ static int vgetorpeek(bool advance)
if (typebuf.tb_len > 0 && advance && !exmode_active) {
if (((State & (NORMAL | INSERT)) || State == LANGMAP)
&& State != HITRETURN) {
- /* this looks nice when typing a dead character map */
+ // this looks nice when typing a dead character map
if (State & INSERT
&& ptr2cells(typebuf.tb_buf + typebuf.tb_off
- + typebuf.tb_len - 1) == 1) {
- edit_putchar(typebuf.tb_buf[typebuf.tb_off
- + typebuf.tb_len - 1], FALSE);
- setcursor(); /* put cursor back where it belongs */
+ + typebuf.tb_len - 1) == 1) {
+ edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1],
+ false);
+ setcursor(); // put cursor back where it belongs
c1 = 1;
}
- /* need to use the col and row from above here */
+ // need to use the col and row from above here
old_wcol = curwin->w_wcol;
old_wrow = curwin->w_wrow;
curwin->w_wcol = new_wcol;
@@ -2332,31 +2337,34 @@ static int vgetorpeek(bool advance)
if (i != 0)
pop_showcmd();
if (c1 == 1) {
- if (State & INSERT)
+ if (State & INSERT) {
edit_unputchar();
- if (State & CMDLINE)
+ }
+ if (State & CMDLINE) {
unputcmdline();
- else
- setcursor(); /* put cursor back where it belongs */
+ } else {
+ setcursor(); // put cursor back where it belongs
+ }
}
- if (c < 0)
- continue; /* end of input script reached */
- if (c == NUL) { /* no character available */
- if (!advance)
+ if (c < 0) {
+ continue; // end of input script reached
+ }
+ if (c == NUL) { // no character available
+ if (!advance) {
break;
- if (wait_tb_len > 0) { /* timed out */
- timedout = TRUE;
+ }
+ if (wait_tb_len > 0) { // timed out
+ timedout = true;
continue;
}
- } else { /* allow mapping for just typed characters */
- while (typebuf.tb_buf[typebuf.tb_off
- + typebuf.tb_len] != NUL)
- typebuf.tb_noremap[typebuf.tb_off
- + typebuf.tb_len++] = RM_YES;
+ } else { // allow mapping for just typed characters
+ while (typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] != NUL) {
+ typebuf.tb_noremap[typebuf.tb_off + typebuf.tb_len++] = RM_YES;
+ }
}
- } /* for (;;) */
- } /* if (!character from stuffbuf) */
+ } // for (;;)
+ } // if (!character from stuffbuf)
// if advance is false don't loop on NULs
} while (c < 0 || (advance && c == NUL));
@@ -2368,15 +2376,17 @@ static int vgetorpeek(bool advance)
*/
if (advance && p_smd && msg_silent == 0 && (State & INSERT)) {
if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) {
- if (typebuf.tb_len && !KeyTyped)
- redraw_cmdline = TRUE; /* delete mode later */
- else
- unshowmode(FALSE);
+ if (typebuf.tb_len && !KeyTyped) {
+ redraw_cmdline = true; // delete mode later
+ } else {
+ unshowmode(false);
+ }
} else if (c != ESC && mode_deleted) {
- if (typebuf.tb_len && !KeyTyped)
- redraw_cmdline = TRUE; /* show mode later */
- else
+ if (typebuf.tb_len && !KeyTyped) {
+ redraw_cmdline = true; // show mode later
+ } else {
showmode();
+ }
}
}
@@ -2440,8 +2450,8 @@ int inchar(
* on char wait, flush swapfile, write error....).
*/
if (State != HITRETURN) {
- did_outofmem_msg = FALSE; /* display out of memory message (again) */
- did_swapwrite_msg = FALSE; /* display swap file write error again */
+ did_outofmem_msg = false; // display out of memory message (again)
+ did_swapwrite_msg = false; // display swap file write error again
}
// Get a character from a script file if there is one.
@@ -3175,7 +3185,7 @@ static void validate_maphash(void)
/*
* Get the mapping mode from the command name.
*/
-int get_map_mode(char_u **cmdp, int forceit)
+int get_map_mode(char_u **cmdp, bool forceit)
{
char_u *p;
int modec;
@@ -3183,30 +3193,31 @@ int get_map_mode(char_u **cmdp, int forceit)
p = *cmdp;
modec = *p++;
- if (modec == 'i')
- mode = INSERT; /* :imap */
- else if (modec == 'l')
- mode = LANGMAP; /* :lmap */
- else if (modec == 'c')
- mode = CMDLINE; /* :cmap */
- else if (modec == 'n' && *p != 'o') /* avoid :noremap */
- mode = NORMAL; /* :nmap */
- else if (modec == 'v')
- mode = VISUAL + SELECTMODE; /* :vmap */
- else if (modec == 'x')
- mode = VISUAL; /* :xmap */
- else if (modec == 's')
- mode = SELECTMODE; /* :smap */
- else if (modec == 'o')
- mode = OP_PENDING; /* :omap */
- else if (modec == 't')
- mode = TERM_FOCUS; // :tmap
- else {
- --p;
- if (forceit)
- mode = INSERT + CMDLINE; /* :map ! */
- else
- mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING; /* :map */
+ if (modec == 'i') {
+ mode = INSERT; // :imap
+ } else if (modec == 'l') {
+ mode = LANGMAP; // :lmap
+ } else if (modec == 'c') {
+ mode = CMDLINE; // :cmap
+ } else if (modec == 'n' && *p != 'o') { // avoid :noremap
+ mode = NORMAL; // :nmap
+ } else if (modec == 'v') {
+ mode = VISUAL + SELECTMODE; // :vmap
+ } else if (modec == 'x') {
+ mode = VISUAL; // :xmap
+ } else if (modec == 's') {
+ mode = SELECTMODE; // :smap
+ } else if (modec == 'o') {
+ mode = OP_PENDING; // :omap
+ } else if (modec == 't') {
+ mode = TERM_FOCUS; // :tmap
+ } else {
+ p--;
+ if (forceit) {
+ mode = INSERT + CMDLINE; // :map !
+ } else {
+ mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING; // :map
+ }
}
*cmdp = p;
@@ -3237,12 +3248,11 @@ void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr)
/*
* Clear all mappings in "mode".
*/
-void
-map_clear_int (
- buf_T *buf, /* buffer for local mappings */
- int mode, /* mode in which to delete */
- int local, /* TRUE for buffer-local mappings */
- int abbr /* TRUE for abbreviations */
+void map_clear_int(
+ buf_T *buf, // buffer for local mappings
+ int mode, // mode in which to delete
+ bool local, // true for buffer-local mappings
+ bool abbr // true for abbreviations
)
{
mapblock_T *mp, **mpp;
@@ -3253,12 +3263,14 @@ map_clear_int (
for (hash = 0; hash < 256; ++hash) {
if (abbr) {
- if (hash > 0) /* there is only one abbrlist */
+ if (hash > 0) { // there is only one abbrlist
break;
- if (local)
+ }
+ if (local) {
mpp = &buf->b_first_abbr;
- else
+ } else {
mpp = &first_abbr;
+ }
} else {
if (local)
mpp = &buf->b_maphash[hash];
@@ -3286,7 +3298,7 @@ map_clear_int (
mp->m_next = maphash[new_hash];
maphash[new_hash] = mp;
}
- continue; /* continue with *mpp */
+ continue; // continue with *mpp
}
}
mpp = &(mp->m_next);
@@ -3341,10 +3353,9 @@ char *map_mode_to_chars(int mode)
return (char *)mapmode.ga_data;
}
-static void
-showmap (
+static void showmap(
mapblock_T *mp,
- int local /* TRUE for buffer-local map */
+ bool local // true for buffer-local map
)
{
size_t len = 1;
@@ -3355,8 +3366,9 @@ showmap (
if (msg_didout || msg_silent != 0) {
msg_putchar('\n');
- if (got_int) /* 'q' typed at MORE prompt */
+ if (got_int) { // 'q' typed at MORE prompt
return;
+ }
}
{
@@ -3372,8 +3384,8 @@ showmap (
// Display the LHS. Get length of what we write.
len = (size_t)msg_outtrans_special(mp->m_keys, true, 0);
do {
- msg_putchar(' '); /* padd with blanks */
- ++len;
+ msg_putchar(' '); // padd with blanks
+ len++;
} while (len < 12);
if (mp->m_noremap == REMAP_NONE) {
@@ -3506,21 +3518,20 @@ int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
* Used below when expanding mapping/abbreviation names.
*/
static int expand_mapmodes = 0;
-static int expand_isabbrev = 0;
-static int expand_buffer = FALSE;
+static bool expand_isabbrev = false;
+static bool expand_buffer = false;
/*
* Work out what to complete when doing command line completion of mapping
* or abbreviation names.
*/
-char_u *
-set_context_in_map_cmd (
+char_u *set_context_in_map_cmd(
expand_T *xp,
char_u *cmd,
char_u *arg,
- int forceit, /* TRUE if '!' given */
- int isabbrev, /* TRUE if abbreviation */
- int isunmap, /* TRUE if unmap/unabbrev command */
+ bool forceit, // true if '!' given
+ bool isabbrev, // true if abbreviation
+ bool isunmap, // true if unmap/unabbrev command
cmdidx_T cmdidx
)
{
@@ -3536,10 +3547,10 @@ set_context_in_map_cmd (
}
expand_isabbrev = isabbrev;
xp->xp_context = EXPAND_MAPPINGS;
- expand_buffer = FALSE;
+ expand_buffer = false;
for (;; ) {
if (STRNCMP(arg, "<buffer>", 8) == 0) {
- expand_buffer = TRUE;
+ expand_buffer = true;
arg = skipwhite(arg + 8);
continue;
}
@@ -3589,7 +3600,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
validate_maphash();
- *num_file = 0; /* return values in case of FAIL */
+ *num_file = 0; // return values in case of FAIL
*file = NULL;
/*
@@ -3628,8 +3639,9 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
for (hash = 0; hash < 256; ++hash) {
if (expand_isabbrev) {
- if (hash > 0) /* only one abbrev list */
- break; /* for (hash) */
+ if (hash > 0) { // only one abbrev list
+ break; // for (hash)
+ }
mp = first_abbr;
} else if (expand_buffer)
mp = curbuf->b_maphash[hash];
@@ -3648,26 +3660,27 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
}
xfree(p);
}
- } /* for (mp) */
- } /* for (hash) */
+ } // for (mp)
+ } // for (hash)
- if (count == 0) /* no match found */
- break; /* for (round) */
+ if (count == 0) { // no match found
+ break; // for (round)
+ }
if (round == 1) {
*file = (char_u **)xmalloc((size_t)count * sizeof(char_u *));
}
- } /* for (round) */
+ } // for (round)
if (count > 1) {
char_u **ptr1;
char_u **ptr2;
char_u **ptr3;
- /* Sort the matches */
+ // Sort the matches
sort_strings(*file, count);
- /* Remove multiple entries */
+ // Remove multiple entries
ptr1 = *file;
ptr2 = ptr1 + 1;
ptr3 = ptr1 + count;
@@ -3705,7 +3718,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file)
bool check_abbr(int c, char_u *ptr, int col, int mincol)
{
int len;
- int scol; /* starting column of the abbr. */
+ int scol; // starting column of the abbr.
int j;
char_u *s;
char_u tb[MB_MAXBYTES + 4];
@@ -3756,7 +3769,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
if (scol < mincol)
scol = mincol;
- if (scol < col) { /* there is a word in front of the cursor */
+ if (scol < col) { // there is a word in front of the cursor
ptr += scol;
len = col - scol;
mp = curbuf->b_first_abbr;
@@ -3778,7 +3791,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
vim_unescape_csi(q);
qlen = (int)STRLEN(q);
}
- /* find entries with right mode and keys */
+ // find entries with right mode and keys
match = (mp->m_mode & State)
&& qlen == len
&& !STRNCMP(q, ptr, (size_t)len);
@@ -3805,7 +3818,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
*/
j = 0;
if (c != Ctrl_RSB) {
- /* special key code, split up */
+ // special key code, split up
if (IS_SPECIAL(c) || c == K_SPECIAL) {
tb[j++] = K_SPECIAL;
tb[j++] = (char_u)K_SECOND(c);
@@ -3821,17 +3834,17 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
j += utf_char2bytes(c, tb + j);
}
tb[j] = NUL;
- /* insert the last typed char */
- (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
+ // insert the last typed char
+ (void)ins_typebuf(tb, 1, 0, true, mp->m_silent);
}
if (mp->m_expr)
s = eval_map_expr(mp->m_str, c);
else
s = mp->m_str;
if (s != NULL) {
- /* insert the to string */
- (void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent);
- /* no abbrev. for these chars */
+ // insert the to string
+ (void)ins_typebuf(s, mp->m_noremap, 0, true, mp->m_silent);
+ // no abbrev. for these chars
typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
if (mp->m_expr)
xfree(s);
@@ -3856,7 +3869,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
static char_u *
eval_map_expr (
char_u *str,
- int c /* NUL or typed character for abbreviation */
+ int c // NUL or typed character for abbreviation
)
{
char_u *res;
@@ -3874,11 +3887,11 @@ eval_map_expr (
save_cmd = save_cmdline_alloc();
- /* Forbid changing text or using ":normal" to avoid most of the bad side
- * effects. Also restore the cursor position. */
- ++textlock;
- ++ex_normal_lock;
- set_vim_var_char(c); /* set v:char to the typed character */
+ // Forbid changing text or using ":normal" to avoid most of the bad side
+ // effects. Also restore the cursor position.
+ textlock++;
+ ex_normal_lock++;
+ set_vim_var_char(c); // set v:char to the typed character
save_cursor = curwin->w_cursor;
save_msg_col = msg_col;
save_msg_row = msg_row;
@@ -3894,7 +3907,7 @@ eval_map_expr (
if (p == NULL)
return NULL;
- /* Escape CSI in the result to be able to use the string as typeahead. */
+ // Escape CSI in the result to be able to use the string as typeahead.
res = vim_strsave_escape_csi(p);
xfree(p);
@@ -3914,7 +3927,7 @@ char_u *vim_strsave_escape_csi(char_u *p)
char_u *d = res;
for (char_u *s = p; *s != NUL; ) {
if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
- /* Copy special key unmodified. */
+ // Copy special key unmodified.
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
@@ -4213,9 +4226,10 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
c = TO_SPECIAL(str[1], str[2]);
str += 2;
}
- if (IS_SPECIAL(c) || modifiers) { /* special key */
- if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
+ if (IS_SPECIAL(c) || modifiers) { // special key
+ if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0) {
return FAIL;
+ }
continue;
}
}
@@ -4271,35 +4285,36 @@ char_u *
check_map (
char_u *keys,
int mode,
- int exact, /* require exact match */
- int ign_mod, /* ignore preceding modifier */
- int abbr, /* do abbreviations */
- mapblock_T **mp_ptr, /* return: pointer to mapblock or NULL */
- int *local_ptr /* return: buffer-local mapping or NULL */
+ int exact, // require exact match
+ int ign_mod, // ignore preceding modifier
+ int abbr, // do abbreviations
+ mapblock_T **mp_ptr, // return: pointer to mapblock or NULL
+ int *local_ptr // return: buffer-local mapping or NULL
)
{
- int hash;
int len, minlen;
mapblock_T *mp;
- int local;
validate_maphash();
len = (int)STRLEN(keys);
- for (local = 1; local >= 0; --local)
- /* loop over all hash lists */
- for (hash = 0; hash < 256; ++hash) {
+ for (int local = 1; local >= 0; local--) {
+ // loop over all hash lists
+ for (int hash = 0; hash < 256; hash++) {
if (abbr) {
- if (hash > 0) /* there is only one list. */
+ if (hash > 0) { // there is only one list.
break;
- if (local)
+ }
+ if (local) {
mp = curbuf->b_first_abbr;
- else
+ } else {
mp = first_abbr;
- } else if (local)
+ }
+ } else if (local) {
mp = curbuf->b_maphash[hash];
- else
+ } else {
mp = maphash[hash];
+ }
for (; mp != NULL; mp = mp->m_next) {
/* skip entries with wrong mode, wrong length and not matching
* ones */
@@ -4322,6 +4337,7 @@ check_map (
}
}
}
+ }
return NULL;
}
@@ -4336,7 +4352,7 @@ void add_map(char_u *map, int mode)
char_u *s;
char_u *cpo_save = p_cpo;
- p_cpo = (char_u *)""; /* Allow <> notation */
+ p_cpo = (char_u *)""; // Allow <> notation
s = vim_strsave(map);
(void)do_map(0, s, mode, FALSE);
xfree(s);
@@ -4384,7 +4400,7 @@ static char_u * translate_mapping (
}
if (IS_SPECIAL(c) || modifiers) { // special key
ga_concat(&ga, get_special_key_name(c, modifiers));
- continue; /* for (str) */
+ continue; // for (str)
}
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 22f06941aa..ffe0357bd8 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -180,6 +180,11 @@ EXTERN int compl_cont_status INIT(= 0);
# define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
// expansion, (eg use complete=.)
+EXTERN char_u *edit_submode INIT(= NULL); // msg for CTRL-X submode
+EXTERN char_u *edit_submode_pre INIT(= NULL); // prepended to edit_submode
+EXTERN char_u *edit_submode_extra INIT(= NULL); // appended to edit_submode
+EXTERN hlf_T edit_submode_highl; // highl. method for extra info
+
// state for putting characters in the message area
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
EXTERN int msg_col;
@@ -251,7 +256,7 @@ EXTERN linenr_T sourcing_lnum INIT(= 0); // line number of the source file
EXTERN int ex_nesting_level INIT(= 0); // nesting level
EXTERN int debug_break_level INIT(= -1); // break below this level
-EXTERN int debug_did_msg INIT(= false); // did "debug mode" message
+EXTERN bool debug_did_msg INIT(= false); // did "debug mode" message
EXTERN int debug_tick INIT(= 0); // breakpoint change count
EXTERN int debug_backtrace_level INIT(= 0); // breakpoint backtrace level
@@ -328,9 +333,10 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#define SID_ENV -4 // for sourcing environment variable
#define SID_ERROR -5 // option was reset because of an error
#define SID_NONE -6 // don't set scriptID
-#define SID_LUA -7 // for Lua scripts/chunks
-#define SID_API_CLIENT -8 // for API clients
-#define SID_STR -9 // for sourcing a string
+#define SID_WINLAYOUT -7 // changing window size
+#define SID_LUA -8 // for Lua scripts/chunks
+#define SID_API_CLIENT -9 // for API clients
+#define SID_STR -10 // for sourcing a string
// Script CTX being sourced or was sourced to define the current function.
EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
@@ -639,10 +645,6 @@ EXTERN int arrow_used; // Normally false, set to true after
// to call u_sync()
EXTERN bool ins_at_eol INIT(= false); // put cursor after eol when
// restarting edit after CTRL-O
-EXTERN char_u *edit_submode INIT(= NULL); // msg for CTRL-X submode
-EXTERN char_u *edit_submode_pre INIT(= NULL); // prepended to edit_submode
-EXTERN char_u *edit_submode_extra INIT(= NULL); // appended to edit_submode
-EXTERN hlf_T edit_submode_highl; // highl. method for extra info
EXTERN int no_abbr INIT(= true); // true when no abbreviations loaded
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index e14aae73d8..3b34af46e4 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -7,7 +7,7 @@
#include "nvim/types.h"
-#define MAX_MCO 6 // maximum value for 'maxcombine'
+#define MAX_MCO 6 // fixed value for 'maxcombine'
// The characters and attributes drawn on grids.
typedef char_u schar_T[(MAX_MCO+1) * 4 + 1];
@@ -35,7 +35,8 @@ typedef int sattr_T;
/// line_wraps[] is an array of boolean flags indicating if the screen line
/// wraps to the next line. It can only be true if a window occupies the entire
/// screen width.
-typedef struct {
+typedef struct ScreenGrid ScreenGrid;
+struct ScreenGrid {
handle_T handle;
schar_T *chars;
@@ -58,10 +59,13 @@ typedef struct {
// external UI.
bool throttled;
- // offsets for the grid relative to the global screen. Used by screen.c
- // for windows that don't have w_grid->chars etc allocated
+ // TODO(bfredl): maybe physical grids and "views" (i e drawing
+ // specifications) should be two separate types?
+ // offsets for the grid relative to another grid. Used for grids
+ // that are views into another, actually allocated grid 'target'
int row_offset;
int col_offset;
+ ScreenGrid *target;
// whether the compositor should blend the grid with the background grid
bool blending;
@@ -76,6 +80,12 @@ typedef struct {
int comp_row;
int comp_col;
+ // Requested width and height of the grid upon resize. Used by
+ // `ui_compositor` to correctly determine which regions need to
+ // be redrawn.
+ int comp_width;
+ int comp_height;
+
// z-index of the grid. Grids with higher index is draw on top.
// default_grid.comp_index is always zero.
size_t comp_index;
@@ -83,9 +93,10 @@ typedef struct {
// compositor should momentarily ignore the grid. Used internally when
// moving around grids etc.
bool comp_disabled;
-} ScreenGrid;
+};
#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \
- false, 0, 0, false, true, 0, 0, 0, false }
+ false, 0, 0, NULL, false, true, \
+ 0, 0, 0, 0, 0, false }
#endif // NVIM_GRID_DEFS_H
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 4ec949759c..abba5425e7 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -890,8 +890,11 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
* Appropriately expand any tabs to spaces.
*/
if (line[col] == TAB || tab_spaces != 0) {
- if (tab_spaces == 0)
- tab_spaces = (int)(curbuf->b_p_ts - (print_pos % curbuf->b_p_ts));
+ if (tab_spaces == 0) {
+ tab_spaces = tabstop_padding(print_pos,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+ }
while (tab_spaces > 0) {
need_break = mch_print_text_out((char_u *)" ", 1);
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index b01cdde236..f03382bea7 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -341,6 +341,17 @@ void update_window_hl(win_T *wp, bool invalid)
}
wp->w_hl_attrs[hlf] = attr;
}
+
+ if (wp->w_floating && wp->w_float_config.border) {
+ for (int i = 0; i < 8; i++) {
+ int attr = wp->w_hl_attrs[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);
+ }
+ wp->w_float_config.border_attr[i] = attr;
+ }
+ }
}
/// Gets HL_UNDERLINE highlight.
@@ -517,6 +528,10 @@ static HlAttrs get_colors_force(int attr)
/// @return the resulting attributes.
int hl_blend_attrs(int back_attr, int front_attr, bool *through)
{
+ if (front_attr < 0 || back_attr < 0) {
+ return -1;
+ }
+
HlAttrs fattrs = get_colors_force(front_attr);
int ratio = fattrs.hl_blend;
if (ratio <= 0) {
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 2bda094d8e..ed4aefb577 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -101,6 +101,7 @@ typedef enum {
, HLF_MSGSEP // message separator line
, HLF_NFLOAT // Floating window
, HLF_MSG // Message area
+ , HLF_BORDER // Floating window border
, HLF_COUNT // MUST be the last one
} hlf_T;
@@ -155,6 +156,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_MSGSEP] = "MsgSeparator",
[HLF_NFLOAT] = "NormalFloat",
[HLF_MSG] = "MsgArea",
+ [HLF_BORDER] = "FloatBorder",
});
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 2dad8fb781..31615e744a 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -1865,7 +1865,7 @@ static void cs_release_csp(size_t i, bool freefnpp)
alive = false; // cscope process no longer exists
break;
}
- os_delay(50L, false); // sleep 50ms
+ os_delay(50L, false); // sleep 50 ms
}
}
if (alive)
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index fae971b3b3..8fa61515ef 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -34,14 +34,20 @@
// Count the size (in window cells) of the indent in the current line.
int get_indent(void)
{
- return get_indent_str(get_cursor_line_ptr(), (int)curbuf->b_p_ts, false);
+ return get_indent_str_vtab(get_cursor_line_ptr(),
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array,
+ false);
}
// Count the size (in window cells) of the indent in line "lnum".
int get_indent_lnum(linenr_T lnum)
{
- return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, false);
+ return get_indent_str_vtab(ml_get(lnum),
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array,
+ false);
}
@@ -49,7 +55,10 @@ int get_indent_lnum(linenr_T lnum)
// "buf".
int get_indent_buf(buf_T *buf, linenr_T lnum)
{
- return get_indent_str(ml_get_buf(buf, lnum, false), (int)buf->b_p_ts, false);
+ return get_indent_str_vtab(ml_get_buf(buf, lnum, false),
+ curbuf->b_p_ts,
+ buf->b_p_vts_array,
+ false);
}
@@ -82,6 +91,30 @@ int get_indent_str(const char_u *ptr, int ts, int list)
return count;
}
+// Count the size (in window cells) of the indent in line "ptr", using
+// variable tabstops.
+// if "list" is true, count only screen size for tabs.
+int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list)
+{
+ int count = 0;
+
+ for (; *ptr; ptr++) {
+ if (*ptr == TAB) { // count a tab for what it is worth
+ if (!list || curwin->w_p_lcs_chars.tab1) {
+ count += tabstop_padding(count, ts, vts);
+ } else {
+ // In list mode, when tab is not set, count screen char width
+ // for Tab, displays: ^I
+ count += ptr2cells(ptr);
+ }
+ } else if (*ptr == ' ') {
+ count++; // count a space for one
+ } else {
+ break;
+ }
+ }
+ return count;
+}
// Set the indent of the current line.
// Leaves the cursor on the first non-blank in the line.
@@ -104,6 +137,7 @@ int set_indent(int size, int flags)
int line_len;
int doit = false;
int ind_done = 0; // Measured in spaces.
+ int ind_col = 0;
int tab_pad;
int retval = false;
@@ -130,7 +164,9 @@ int set_indent(int size, int flags)
// Count as many characters as we can use.
while (todo > 0 && ascii_iswhite(*p)) {
if (*p == TAB) {
- tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target.
if (todo < tab_pad) {
@@ -147,35 +183,41 @@ int set_indent(int size, int flags)
p++;
}
+ // These diverge from this point.
+ ind_col = ind_done;
// Set initial number of whitespace chars to copy if we are
// preserving indent but expandtab is set.
if (curbuf->b_p_et) {
orig_char_len = ind_len;
}
-
// Fill to next tabstop with a tab, if possible.
- tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
-
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
if ((todo >= tab_pad) && (orig_char_len == -1)) {
doit = true;
todo -= tab_pad;
ind_len++;
// ind_done += tab_pad;
+ ind_col += tab_pad;
}
}
// Count tabs required for indent.
- while (todo >= (int)curbuf->b_p_ts) {
+ for (;;) {
+ tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, curbuf->b_p_vts_array);
+ if (todo < tab_pad) {
+ break;
+ }
if (*p != TAB) {
doit = true;
} else {
p++;
}
- todo -= (int)curbuf->b_p_ts;
+ todo -= tab_pad;
ind_len++;
-
- // ind_done += (int)curbuf->b_p_ts;
+ ind_col += tab_pad;
}
}
@@ -255,7 +297,9 @@ int set_indent(int size, int flags)
while (todo > 0 && ascii_iswhite(*p)) {
if (*p == TAB) {
- tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target.
if (todo < tab_pad) {
@@ -272,18 +316,28 @@ int set_indent(int size, int flags)
}
// Fill to next tabstop with a tab, if possible.
- tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
if (todo >= tab_pad) {
*s++ = TAB;
todo -= tab_pad;
+ ind_done += tab_pad;
}
p = skipwhite(p);
}
- while (todo >= (int)curbuf->b_p_ts) {
+ for (;;) {
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+ if (todo < tab_pad) {
+ break;
+ }
*s++ = TAB;
- todo -= (int)curbuf->b_p_ts;
+ todo -= tab_pad;
+ ind_done += tab_pad;
}
}
@@ -375,11 +429,9 @@ int get_number_indent(linenr_T lnum)
return (int)col;
}
-/*
- * Return appropriate space number for breakindent, taking influencing
- * parameters into account. Window must be specified, since it is not
- * necessarily always the current one.
- */
+// Return appropriate space number for breakindent, taking influencing
+// parameters into account. Window must be specified, since it is not
+// necessarily always the current one.
int get_breakindent_win(win_T *wp, const char_u *line)
FUNC_ATTR_NONNULL_ALL
{
@@ -387,6 +439,7 @@ int get_breakindent_win(win_T *wp, const char_u *line)
static long prev_ts = 0; // Cached tabstop value.
static const char_u *prev_line = NULL; // cached pointer to line.
static varnumber_T prev_tick = 0; // Changedtick of cached value.
+ static long *prev_vts = NULL; // Cached vartabs values.
int bri = 0;
// window width minus window margin space, i.e. what rests for text
const int eff_wwidth = wp->w_width_inner
@@ -396,11 +449,16 @@ int get_breakindent_win(win_T *wp, const char_u *line)
// used cached indent, unless pointer or 'tabstop' changed
if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
- || prev_tick != buf_get_changedtick(wp->w_buffer)) {
+ || prev_tick != buf_get_changedtick(wp->w_buffer)
+ || prev_vts != wp->w_buffer->b_p_vts_array) {
prev_line = line;
prev_ts = wp->w_buffer->b_p_ts;
prev_tick = buf_get_changedtick(wp->w_buffer);
- prev_indent = get_indent_str(line, (int)wp->w_buffer->b_p_ts, wp->w_p_list);
+ prev_vts = wp->w_buffer->b_p_vts_array;
+ prev_indent = get_indent_str_vtab(line,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array,
+ wp->w_p_list);
}
bri = prev_indent + wp->w_briopt_shift;
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 9298e57411..771bf923b2 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -33,15 +33,12 @@ typedef struct {
* Search starts at w_cursor.lnum and goes backwards.
* Return NULL when not inside a comment.
*/
-static pos_T *ind_find_start_comment(void)
-{ /* XXX */
+static pos_T *ind_find_start_comment(void) // XXX
+{
return find_start_comment(curbuf->b_ind_maxcomment);
}
-pos_T *
-find_start_comment ( /* XXX */
- int ind_maxcomment
-)
+pos_T *find_start_comment(int ind_maxcomment) // XXX
{
pos_T *pos;
char_u *line;
@@ -109,8 +106,8 @@ static pos_T *ind_find_start_CORS(linenr_T *is_raw)
* Search starts at w_cursor.lnum and goes backwards.
* Return NULL when not inside a raw string.
*/
-static pos_T *find_start_rawstring(int ind_maxcomment)
-{ /* XXX */
+static pos_T *find_start_rawstring(int ind_maxcomment) // XXX
+{
pos_T *pos;
char_u *line;
char_u *p;
@@ -152,31 +149,35 @@ static char_u *skip_string(char_u *p)
/*
* We loop, because strings may be concatenated: "date""time".
*/
- for (;; ++p) {
- if (p[0] == '\'') { /* 'c' or '\n' or '\000' */
- if (!p[1]) /* ' at end of line */
+ for (;; p++) {
+ if (p[0] == '\'') { // 'c' or '\n' or '\000'
+ if (!p[1]) { // ' at end of line
break;
+ }
i = 2;
- if (p[1] == '\\') { /* '\n' or '\000' */
- ++i;
- while (ascii_isdigit(p[i - 1])) /* '\000' */
- ++i;
+ if (p[1] == '\\') { // '\n' or '\000'
+ i++;
+ while (ascii_isdigit(p[i - 1])) { // '\000'
+ i++;
+ }
}
- if (p[i] == '\'') { /* check for trailing ' */
+ if (p[i] == '\'') { // check for trailing '
p += i;
continue;
}
- } else if (p[0] == '"') { /* start of string */
- for (++p; p[0]; ++p) {
- if (p[0] == '\\' && p[1] != NUL)
- ++p;
- else if (p[0] == '"') /* end of string */
+ } else if (p[0] == '"') { // start of string
+ for (++p; p[0]; p++) {
+ if (p[0] == '\\' && p[1] != NUL) {
+ p++;
+ } else if (p[0] == '"') { // end of string
break;
+ }
+ }
+ if (p[0] == '"') {
+ continue; // continue for another string
}
- if (p[0] == '"')
- continue; /* continue for another string */
} else if (p[0] == 'R' && p[1] == '"') {
- /* Raw string: R"[delim](...)[delim]" */
+ // Raw string: R"[delim](...)[delim]"
char_u *delim = p + 2;
char_u *paren = vim_strchr(delim, '(');
@@ -190,14 +191,16 @@ static char_u *skip_string(char_u *p)
p += delim_len + 1;
break;
}
- if (p[0] == '"')
- continue; /* continue for another string */
+ if (p[0] == '"') {
+ continue; // continue for another string
+ }
}
}
- break; /* no string found */
+ break; // no string found
+ }
+ if (!*p) {
+ p--; // backup from NUL
}
- if (!*p)
- --p; /* backup from NUL */
return p;
}
@@ -255,20 +258,22 @@ static char_u *cin_skipcomment(char_u *s)
s += STRLEN(s);
break;
}
- if (*s != '/')
+ if (*s != '/') {
break;
- ++s;
- if (*s == '/') { /* slash-slash comment continues till eol */
+ }
+ s++;
+ if (*s == '/') { // slash-slash comment continues till eol
s += STRLEN(s);
break;
}
if (*s != '*')
break;
- for (++s; *s; ++s) /* skip slash-star comment */
+ for (++s; *s; s++) { // skip slash-star comment
if (s[0] == '*' && s[1] == '/') {
s += 2;
break;
}
+ }
}
return s;
}
@@ -285,7 +290,7 @@ static int cin_nocode(char_u *s)
/*
* Check previous lines for a "//" line comment, skipping over blank lines.
*/
-static pos_T *find_line_comment(void) /* XXX */
+static pos_T *find_line_comment(void) // XXX
{
static pos_T pos;
char_u *line;
@@ -335,39 +340,38 @@ static bool cin_has_js_key(char_u *text)
/// Checks if string matches "label:"; move to character after ':' if true.
/// "*s" must point to the start of the label, if there is one.
-static int cin_islabel_skip(char_u **s)
+static bool cin_islabel_skip(char_u **s)
+ FUNC_ATTR_NONNULL_ALL
{
- if (!vim_isIDc(**s)) /* need at least one ID character */
- return FALSE;
+ if (!vim_isIDc(**s)) { // need at least one ID character
+ return false;
+ }
while (vim_isIDc(**s))
(*s)++;
*s = cin_skipcomment(*s);
- /* "::" is not a label, it's C++ */
+ // "::" is not a label, it's C++
return **s == ':' && *++*s != ':';
}
-/*
- * Recognize a label: "label:".
- * Note: curwin->w_cursor must be where we are looking for the label.
- */
-int cin_islabel(void)
-{ /* XXX */
+// Recognize a label: "label:".
+// Note: curwin->w_cursor must be where we are looking for the label.
+bool cin_islabel(void) // XXX
+{
char_u *s = cin_skipcomment(get_cursor_line_ptr());
- /*
- * Exclude "default" from labels, since it should be indented
- * like a switch label. Same for C++ scope declarations.
- */
- if (cin_isdefault(s))
- return FALSE;
- if (cin_isscopedecl(s))
- return FALSE;
-
+ // Exclude "default" from labels, since it should be indented
+ // like a switch label. Same for C++ scope declarations.
+ if (cin_isdefault(s)) {
+ return false;
+ }
+ if (cin_isscopedecl(s)) {
+ return false;
+ }
if (!cin_islabel_skip(&s)) {
- return FALSE;
+ return false;
}
/*
@@ -392,21 +396,24 @@ int cin_islabel(void)
}
line = get_cursor_line_ptr();
- if (cin_ispreproc(line)) /* ignore #defines, #if, etc. */
+ if (cin_ispreproc(line)) { // ignore #defines, #if, etc.
continue;
- if (*(line = cin_skipcomment(line)) == NUL)
+ }
+ if (*(line = cin_skipcomment(line)) == NUL) {
continue;
+ }
curwin->w_cursor = cursor_save;
if (cin_isterminated(line, TRUE, FALSE)
|| cin_isscopedecl(line)
- || cin_iscase(line, TRUE)
- || (cin_islabel_skip(&line) && cin_nocode(line)))
- return TRUE;
- return FALSE;
+ || cin_iscase(line, true)
+ || (cin_islabel_skip(&line) && cin_nocode(line))) {
+ return true;
+ }
+ return false;
}
curwin->w_cursor = cursor_save;
- return TRUE; /* label at start of file??? */
+ return true; // label at start of file???
}
/*
@@ -451,10 +458,9 @@ static int cin_isinit(void)
/*
* Recognize a switch label: "case .*:" or "default:".
*/
-int
-cin_iscase (
+bool cin_iscase(
char_u *s,
- int strict /* Allow relaxed check of case statement for JS */
+ bool strict // Allow relaxed check of case statement for JS
)
{
s = cin_skipcomment(s);
@@ -465,29 +471,32 @@ cin_iscase (
break;
}
if (*s == ':') {
- if (s[1] == ':') /* skip over "::" for C++ */
- ++s;
- else
- return TRUE;
+ if (s[1] == ':') { // skip over "::" for C++
+ s++;
+ } else {
+ return true;
+ }
}
- if (*s == '\'' && s[1] && s[2] == '\'')
- s += 2; /* skip over ':' */
- else if (*s == '/' && (s[1] == '*' || s[1] == '/'))
- return FALSE; /* stop at comment */
- else if (*s == '"') {
- /* JS etc. */
- if (strict)
- return FALSE; /* stop at string */
- else
- return TRUE;
+ if (*s == '\'' && s[1] && s[2] == '\'') {
+ s += 2; // skip over ':'
+ } else if (*s == '/' && (s[1] == '*' || s[1] == '/')) {
+ return false; // stop at comment
+ } else if (*s == '"') {
+ // JS etc.
+ if (strict) {
+ return false; // stop at string
+ } else {
+ return true;
+ }
}
}
- return FALSE;
+ return false;
}
- if (cin_isdefault(s))
- return TRUE;
- return FALSE;
+ if (cin_isdefault(s)) {
+ return true;
+ }
+ return false;
}
/*
@@ -503,23 +512,24 @@ static int cin_isdefault(char_u *s)
/*
* Recognize a "public/private/protected" scope declaration label.
*/
-int cin_isscopedecl(char_u *s)
+bool cin_isscopedecl(char_u *s)
{
int i;
s = cin_skipcomment(s);
- if (STRNCMP(s, "public", 6) == 0)
+ if (STRNCMP(s, "public", 6) == 0) {
i = 6;
- else if (STRNCMP(s, "protected", 9) == 0)
+ } else if (STRNCMP(s, "protected", 9) == 0) {
i = 9;
- else if (STRNCMP(s, "private", 7) == 0)
+ } else if (STRNCMP(s, "private", 7) == 0) {
i = 7;
- else
- return FALSE;
+ } else {
+ return false;
+ }
return *(s = cin_skipcomment(s + i)) == ':' && s[1] != ':';
}
-/* Maximum number of lines to search back for a "namespace" line. */
+// Maximum number of lines to search back for a "namespace" line.
#define FIND_NAMESPACE_LIM 20
// Recognize a "namespace" scope declaration.
@@ -569,12 +579,14 @@ static char_u *after_label(char_u *l)
{
for (; *l; ++l) {
if (*l == ':') {
- if (l[1] == ':') /* skip over "::" for C++ */
- ++l;
- else if (!cin_iscase(l + 1, FALSE))
+ if (l[1] == ':') { // skip over "::" for C++
+ l++;
+ } else if (!cin_iscase(l + 1, false)) {
break;
- } else if (*l == '\'' && l[1] && l[2] == '\'')
- l += 2; /* skip over 'x' */
+ }
+ } else if (*l == '\'' && l[1] && l[2] == '\'') {
+ l += 2; // skip over 'x'
+ }
}
if (*l == NUL)
return NULL;
@@ -588,10 +600,7 @@ static char_u *after_label(char_u *l)
* Get indent of line "lnum", skipping a label.
* Return 0 if there is nothing after the label.
*/
-static int
-get_indent_nolabel ( /* XXX */
- linenr_T lnum
-)
+static int get_indent_nolabel(linenr_T lnum) // XXX
{
char_u *l;
pos_T fp;
@@ -624,12 +633,13 @@ static int skip_label(linenr_T lnum, char_u **pp)
cursor_save = curwin->w_cursor;
curwin->w_cursor.lnum = lnum;
l = get_cursor_line_ptr();
- /* XXX */
- if (cin_iscase(l, FALSE) || cin_isscopedecl(l) || cin_islabel()) {
+ // XXX
+ if (cin_iscase(l, false) || cin_isscopedecl(l) || cin_islabel()) {
amount = get_indent_nolabel(lnum);
l = after_label(get_cursor_line_ptr());
- if (l == NULL) /* just in case */
+ if (l == NULL) { // just in case
l = get_cursor_line_ptr();
+ }
} else {
amount = get_indent();
l = get_cursor_line_ptr();
@@ -710,10 +720,11 @@ static int cin_get_equal_amount(linenr_T lnum)
line = s = ml_get(lnum);
while (*s != NUL && vim_strchr((char_u *)"=;{}\"'", *s) == NULL) {
- if (cin_iscomment(s)) /* ignore comments */
+ if (cin_iscomment(s)) { // ignore comments
s = cin_skipcomment(s);
- else
- ++s;
+ } else {
+ s++;
+ }
}
if (*s != '=')
return 0;
@@ -722,8 +733,9 @@ static int cin_get_equal_amount(linenr_T lnum)
if (cin_nocode(s))
return 0;
- if (*s == '"') /* nice alignment for continued strings */
- ++s;
+ if (*s == '"') { // nice alignment for continued strings
+ s++;
+ }
fp.lnum = lnum;
fp.col = (colnr_T)(s - line);
@@ -806,8 +818,8 @@ static int cin_islinecomment(char_u *p)
static char_u
cin_isterminated (
char_u *s,
- int incl_open, /* include '{' at the end as terminator */
- int incl_comma /* recognize a trailing comma */
+ int incl_open, // include '{' at the end as terminator
+ int incl_comma // recognize a trailing comma
)
{
char_u found_start = 0;
@@ -823,7 +835,7 @@ cin_isterminated (
is_else = cin_iselse(s);
while (*s) {
- /* skip over comments, "" strings and 'c'haracters */
+ // skip over comments, "" strings and 'c'haracters
s = skip_string(cin_skipcomment(s));
if (*s == '}' && n_open > 0)
--n_open;
@@ -942,12 +954,12 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
s = skipwhite(s);
if (!just_started && (!comma && *s != ',' && *s != ')'))
break;
- just_started = FALSE;
- } else if (cin_iscomment(s)) /* ignore comments */
+ just_started = false;
+ } else if (cin_iscomment(s)) { // ignore comments
s = cin_skipcomment(s);
- else {
- ++s;
- just_started = FALSE;
+ } else {
+ s++;
+ just_started = false;
}
}
@@ -965,8 +977,9 @@ static int cin_isif(char_u *p)
static int cin_iselse(char_u *p)
{
- if (*p == '}') /* accept "} else" */
+ if (*p == '}') { // accept "} else"
p = cin_skipcomment(p + 1);
+ }
return STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4]);
}
@@ -980,27 +993,24 @@ static int cin_isdo(char_u *p)
* We only accept a "while (condition) ;", with only white space between the
* ')' and ';'. The condition may be spread over several lines.
*/
-static int
-cin_iswhileofdo ( /* XXX */
- char_u *p,
- linenr_T lnum
-)
+static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX
{
pos_T cursor_save;
pos_T *trypos;
int retval = FALSE;
p = cin_skipcomment(p);
- if (*p == '}') /* accept "} while (cond);" */
+ if (*p == '}') { // accept "} while (cond);"
p = cin_skipcomment(p + 1);
+ }
if (cin_starts_with(p, "while")) {
cursor_save = curwin->w_cursor;
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
p = get_cursor_line_ptr();
- while (*p && *p != 'w') { /* skip any '}', until the 'w' of the "while" */
- ++p;
- ++curwin->w_cursor.col;
+ while (*p && *p != 'w') { // skip any '}', until the 'w' of the "while"
+ p++;
+ curwin->w_cursor.col++;
}
if ((trypos = findmatchlimit(NULL, 0, 0,
curbuf->b_ind_maxparen)) != NULL
@@ -1067,8 +1077,9 @@ static int cin_iswhileofdo_end(int terminated)
pos_T *trypos;
int i;
- if (terminated != ';') /* there must be a ';' at the end */
- return FALSE;
+ if (terminated != ';') { // there must be a ';' at the end
+ return false;
+ }
p = line = get_cursor_line_ptr();
while (*p != NUL) {
@@ -1083,15 +1094,16 @@ static int cin_iswhileofdo_end(int terminated)
trypos = find_match_paren(curbuf->b_ind_maxparen);
if (trypos != NULL) {
s = cin_skipcomment(ml_get(trypos->lnum));
- if (*s == '}') /* accept "} while (cond);" */
+ if (*s == '}') { // accept "} while (cond);"
s = cin_skipcomment(s + 1);
+ }
if (cin_starts_with(s, "while")) {
curwin->w_cursor.lnum = trypos->lnum;
return TRUE;
}
}
- /* Searching may have made "line" invalid, get it again. */
+ // Searching may have made "line" invalid, get it again.
line = get_cursor_line_ptr();
p = line + i;
}
@@ -1134,8 +1146,9 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
pos->col = 0;
s = skipwhite(line);
- if (*s == '#') /* skip #define FOO x ? (x) : x */
- return FALSE;
+ if (*s == '#') { // skip #define FOO x ? (x) : x
+ return false;
+ }
s = cin_skipcomment(s);
if (*s == NUL)
return FALSE;
@@ -1230,23 +1243,23 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) {
class_or_struct = FALSE;
lookfor_ctor_init = TRUE;
} else if (s[0] == '?') {
- /* Avoid seeing '() :' after '?' as constructor init. */
- return FALSE;
+ // Avoid seeing '() :' after '?' as constructor init.
+ return false;
} else if (!vim_isIDc(s[0])) {
- /* if it is not an identifier, we are wrong */
+ // if it is not an identifier, we are wrong
class_or_struct = false;
lookfor_ctor_init = false;
} else if (pos->col == 0) {
- /* it can't be a constructor-initialization any more */
- lookfor_ctor_init = FALSE;
+ // it can't be a constructor-initialization any more
+ lookfor_ctor_init = false;
- /* the first statement starts here: lineup with this one... */
+ // the first statement starts here: lineup with this one...
if (cpp_base_class) {
pos->col = (colnr_T)(s - line);
}
}
- /* When the line ends in a comma don't align with it. */
+ // When the line ends in a comma don't align with it.
if (lnum == curwin->w_cursor.lnum && *s == ',' && cin_nocode(s + 1)) {
pos->col = 0;
}
@@ -1271,10 +1284,12 @@ static int get_baseclass_amount(int col)
if (col == 0) {
amount = get_indent();
if (find_last_paren(get_cursor_line_ptr(), '(', ')')
- && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
- amount = get_indent_lnum(trypos->lnum); /* XXX */
- if (!cin_ends_in(get_cursor_line_ptr(), (char_u *)",", NULL))
+ && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
+ amount = get_indent_lnum(trypos->lnum); // XXX
+ }
+ if (!cin_ends_in(get_cursor_line_ptr(), (char_u *)",", NULL)) {
amount += curbuf->b_ind_cpp_baseclass;
+ }
} else {
curwin->w_cursor.col = col;
getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL);
@@ -1389,12 +1404,12 @@ static int cin_skip2pos(pos_T *trypos)
* Return NULL if no match found.
* Ignore a '{' that is in a comment, makes indenting the next three lines
* work. */
-/* foo() */
-/* { */
-/* } */
+// foo()
+// {
+// }
-static pos_T *find_start_brace(void)
-{ /* XXX */
+static pos_T *find_start_brace(void) // XXX
+{
pos_T cursor_save;
pos_T *trypos;
pos_T *pos;
@@ -1402,11 +1417,11 @@ static pos_T *find_start_brace(void)
cursor_save = curwin->w_cursor;
while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) {
- pos_copy = *trypos; /* copy pos_T, next findmatch will change it */
+ pos_copy = *trypos; // copy pos_T, next findmatch will change it
trypos = &pos_copy;
curwin->w_cursor = *trypos;
pos = NULL;
- /* ignore the { if it's in a // or / * * / comment */
+ // ignore the { if it's in a // or / * * / comment
if ((colnr_T)cin_skip2pos(trypos) == trypos->col
&& (pos = ind_find_start_CORS(NULL)) == NULL) { // XXX
break;
@@ -1449,7 +1464,7 @@ retry:
} else {
pos_T *trypos_wk;
- pos_copy = *trypos; /* copy trypos, findmatch will change it */
+ pos_copy = *trypos; // copy trypos, findmatch will change it
trypos = &pos_copy;
curwin->w_cursor = *trypos;
if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) { // XXX
@@ -1515,17 +1530,17 @@ static int find_last_paren(char_u *l, int start, int end)
int retval = FALSE;
int open_count = 0;
- curwin->w_cursor.col = 0; /* default is start of line */
+ curwin->w_cursor.col = 0; // default is start of line
for (i = 0; l[i] != NUL; i++) {
- i = (int)(cin_skipcomment(l + i) - l); /* ignore parens in comments */
- i = (int)(skip_string(l + i) - l); /* ignore parens in quotes */
- if (l[i] == start)
- ++open_count;
- else if (l[i] == end) {
- if (open_count > 0)
- --open_count;
- else {
+ i = (int)(cin_skipcomment(l + i) - l); // ignore parens in comments
+ i = (int)(skip_string(l + i) - l); // ignore parens in quotes
+ if (l[i] == start) {
+ open_count++;
+ } else if (l[i] == end) {
+ if (open_count > 0) {
+ open_count--;
+ } else {
curwin->w_cursor.col = i;
retval = TRUE;
}
@@ -1561,7 +1576,7 @@ void parse_cino(buf_T *buf)
* an opening brace. */
buf->b_ind_no_brace = 0;
- /* Column where the first { of a function should be located }. */
+ // Column where the first { of a function should be located }.
buf->b_ind_first_open = 0;
/* Spaces from the prevailing indent a leftmost open brace should be
@@ -1581,26 +1596,26 @@ void parse_cino(buf_T *buf)
* otherwise the jump label will be put to column 1. */
buf->b_ind_jump_label = -1;
- /* Spaces from the switch() indent a "case xx" label should be located. */
+ // Spaces from the switch() indent a "case xx" label should be located.
buf->b_ind_case = sw;
- /* Spaces from the "case xx:" code after a switch() should be located. */
+ // Spaces from the "case xx:" code after a switch() should be located.
buf->b_ind_case_code = sw;
- /* Lineup break at end of case in switch() with case label. */
+ // Lineup break at end of case in switch() with case label.
buf->b_ind_case_break = 0;
/* Spaces from the class declaration indent a scope declaration label
* should be located. */
buf->b_ind_scopedecl = sw;
- /* Spaces from the scope declaration label code should be located. */
+ // Spaces from the scope declaration label code should be located.
buf->b_ind_scopedecl_code = sw;
- /* Amount K&R-style parameters should be indented. */
+ // Amount K&R-style parameters should be indented.
buf->b_ind_param = sw;
- /* Amount a function type spec should be indented. */
+ // Amount a function type spec should be indented.
buf->b_ind_func_type = sw;
/* Amount a cpp base class declaration or constructor initialization
@@ -1611,7 +1626,7 @@ void parse_cino(buf_T *buf)
* should be located. */
buf->b_ind_continuation = sw;
- /* Spaces from the indent of the line with an unclosed parentheses. */
+ // Spaces from the indent of the line with an unclosed parentheses.
buf->b_ind_unclosed = sw * 2;
/* Spaces from the indent of the line with an unclosed parentheses, which
@@ -1635,35 +1650,35 @@ void parse_cino(buf_T *buf)
* opening parentheses. */
buf->b_ind_matching_paren = 0;
- /* Indent a closing parentheses under the previous line. */
+ // Indent a closing parentheses under the previous line.
buf->b_ind_paren_prev = 0;
- /* Extra indent for comments. */
+ // Extra indent for comments.
buf->b_ind_comment = 0;
- /* Spaces from the comment opener when there is nothing after it. */
+ // Spaces from the comment opener when there is nothing after it.
buf->b_ind_in_comment = 3;
/* Boolean: if non-zero, use b_ind_in_comment even if there is something
* after the comment opener. */
buf->b_ind_in_comment2 = 0;
- /* Max lines to search for an open paren. */
+ // Max lines to search for an open paren.
buf->b_ind_maxparen = 20;
- /* Max lines to search for an open comment. */
+ // Max lines to search for an open comment.
buf->b_ind_maxcomment = 70;
- /* Handle braces for java code. */
+ // Handle braces for java code.
buf->b_ind_java = 0;
- /* Not to confuse JS object properties with labels. */
+ // Not to confuse JS object properties with labels.
buf->b_ind_js = 0;
- /* Handle blocked cases correctly. */
+ // Handle blocked cases correctly.
buf->b_ind_keep_case_label = 0;
- /* Handle C++ namespace. */
+ // Handle C++ namespace.
buf->b_ind_cpp_namespace = 0;
/* Handle continuation lines containing conditions of if(), for() and
@@ -1777,9 +1792,9 @@ int get_c_indent(void)
pos_T our_paren_pos;
char_u *start;
int start_brace;
-#define BRACE_IN_COL0 1 /* '{' is in column 0 */
-#define BRACE_AT_START 2 /* '{' is at start of line */
-#define BRACE_AT_END 3 /* '{' is at end of line */
+#define BRACE_IN_COL0 1 // '{' is in column 0
+#define BRACE_AT_START 2 // '{' is at start of line
+#define BRACE_AT_END 3 // '{' is at end of line
linenr_T ourscope;
char_u *l;
char_u *look;
@@ -1802,24 +1817,24 @@ int get_c_indent(void)
int whilelevel;
linenr_T lnum;
int n;
- int iscase;
int lookfor_break;
- int lookfor_cpp_namespace = FALSE;
- int cont_amount = 0; /* amount for continuation line */
+ bool lookfor_cpp_namespace = false;
+ int cont_amount = 0; // amount for continuation line
int original_line_islabel;
int added_to_amount = 0;
linenr_T raw_string_start = 0;
cpp_baseclass_cache_T cache_cpp_baseclass = { false, { MAXLNUM, 0 } };
- /* make a copy, value is changed below */
+ // make a copy, value is changed below
int ind_continuation = curbuf->b_ind_continuation;
- /* remember where the cursor was when we started */
+ // remember where the cursor was when we started
cur_curpos = curwin->w_cursor;
- /* if we are at line 1 zero indent is fine, right? */
- if (cur_curpos.lnum == 1)
+ // if we are at line 1 zero indent is fine, right?
+ if (cur_curpos.lnum == 1) {
return 0;
+ }
/* Get a copy of the current contents of the line.
* This is required, because only the most recent line obtained with
@@ -1840,11 +1855,11 @@ int get_c_indent(void)
theline = skipwhite(linecopy);
- /* move the cursor to the start of the line */
+ // move the cursor to the start of the line
curwin->w_cursor.col = 0;
- original_line_islabel = cin_islabel(); /* XXX */
+ original_line_islabel = cin_islabel(); // XXX
/*
* If we are inside a raw string don't change the indent.
@@ -1852,7 +1867,7 @@ int get_c_indent(void)
*/
comment_pos = ind_find_start_comment();
if (comment_pos != NULL) {
- /* findmatchlimit() static pos is overwritten, make a copy */
+ // findmatchlimit() static pos is overwritten, make a copy
tryposCopy = *comment_pos;
comment_pos = &tryposCopy;
}
@@ -1887,8 +1902,8 @@ int get_c_indent(void)
* previous line, lineup with that one.
*/
if (cin_islinecomment(theline)
- && (trypos = find_line_comment()) != NULL) { /* XXX */
- /* find how indented the line beginning the comment is */
+ && (trypos = find_line_comment()) != NULL) { // XXX
+ // find how indented the line beginning the comment is
getvcol(curwin, trypos, &col, NULL, NULL);
amount = col;
goto theend;
@@ -1897,18 +1912,18 @@ int get_c_indent(void)
* If we're inside a comment and not looking at the start of the
* comment, try using the 'comments' option.
*/
- if (!cin_iscomment(theline) && comment_pos != NULL) { /* XXX */
+ if (!cin_iscomment(theline) && comment_pos != NULL) { // XXX
int lead_start_len = 2;
int lead_middle_len = 1;
- char_u lead_start[COM_MAX_LEN]; /* start-comment string */
- char_u lead_middle[COM_MAX_LEN]; /* middle-comment string */
- char_u lead_end[COM_MAX_LEN]; /* end-comment string */
+ char_u lead_start[COM_MAX_LEN]; // start-comment string
+ char_u lead_middle[COM_MAX_LEN]; // middle-comment string
+ char_u lead_end[COM_MAX_LEN]; // end-comment string
char_u *p;
int start_align = 0;
int start_off = 0;
int done = FALSE;
- /* find how indented the line beginning the comment is */
+ // find how indented the line beginning the comment is
getvcol(curwin, comment_pos, &col, NULL, NULL);
amount = col;
*lead_start = NUL;
@@ -1981,13 +1996,13 @@ int get_c_indent(void)
if (STRNCMP(theline, lead_middle, lead_middle_len) != 0
&& STRNCMP(theline, lead_end, STRLEN(lead_end)) == 0) {
amount = get_indent_lnum(curwin->w_cursor.lnum - 1);
- /* XXX */
- if (off != 0)
+ // XXX
+ if (off != 0) {
amount += off;
- else if (align == COM_RIGHT)
- amount += vim_strsize(lead_start)
- - vim_strsize(lead_middle);
- done = TRUE;
+ } else if (align == COM_RIGHT) {
+ amount += vim_strsize(lead_start) - vim_strsize(lead_middle);
+ }
+ done = true;
break;
}
}
@@ -2010,18 +2025,20 @@ int get_c_indent(void)
* otherwise, add the amount specified by "c" in 'cino'
*/
amount = -1;
- for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; --lnum) {
- if (linewhite(lnum)) /* skip blank lines */
+ for (lnum = cur_curpos.lnum - 1; lnum > comment_pos->lnum; lnum--) {
+ if (linewhite(lnum)) { // skip blank lines
continue;
- amount = get_indent_lnum(lnum); /* XXX */
+ }
+ amount = get_indent_lnum(lnum); // XXX
break;
}
- if (amount == -1) { /* use the comment opener */
+ if (amount == -1) { // use the comment opener
if (!curbuf->b_ind_in_comment2) {
- start = ml_get(comment_pos->lnum);
- look = start + comment_pos->col + 2; /* skip / and * */
- if (*look != NUL) /* if something after it */
- comment_pos->col = (colnr_T)(skipwhite(look) - start);
+ start = ml_get(comment_pos->lnum);
+ look = start + comment_pos->col + 2; // skip / and *
+ if (*look != NUL) { // if something after it
+ comment_pos->col = (colnr_T)(skipwhite(look) - start);
+ }
}
getvcol(curwin, comment_pos, &col, NULL, NULL);
amount = col;
@@ -2038,9 +2055,8 @@ int get_c_indent(void)
amount = get_indent_lnum(trypos->lnum);
goto theend;
}
- /*
- * Are we inside parentheses or braces?
- */ /* XXX */
+ // Are we inside parentheses or braces?
+ // XXX
if (((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL
&& curbuf->b_ind_java == 0)
|| (tryposBrace = find_start_brace()) != NULL
@@ -2063,8 +2079,8 @@ int get_c_indent(void)
* a previous non-empty line that matches the same paren.
*/
if (theline[0] == ')' && curbuf->b_ind_paren_prev) {
- /* Line up with the start of the matching paren line. */
- amount = get_indent_lnum(curwin->w_cursor.lnum - 1); /* XXX */
+ // Line up with the start of the matching paren line.
+ amount = get_indent_lnum(curwin->w_cursor.lnum - 1); // XXX
} else {
amount = -1;
for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) {
@@ -2083,12 +2099,12 @@ int get_c_indent(void)
continue;
}
- /* XXX */
+ // XXX
if ((trypos = find_match_paren(
corr_ind_maxparen(&cur_curpos))) != NULL
&& trypos->lnum == our_paren_pos.lnum
&& trypos->col == our_paren_pos.col) {
- amount = get_indent_lnum(lnum); /* XXX */
+ amount = get_indent_lnum(lnum); // XXX
if (theline[0] == ')') {
if (our_paren_pos.lnum != lnum
@@ -2200,10 +2216,11 @@ int get_c_indent(void)
col = our_paren_pos.col + 1;
while (ascii_iswhite(l[col]))
col++;
- if (l[col] != NUL) /* In case of trailing space */
+ if (l[col] != NUL) { // In case of trailing space
our_paren_pos.col = col;
- else
+ } else {
our_paren_pos.col++;
+ }
}
}
@@ -2219,7 +2236,7 @@ int get_c_indent(void)
}
if (theline[0] == ')' && curbuf->b_ind_matching_paren) {
- /* Line up with the start of the matching paren line. */
+ // Line up with the start of the matching paren line.
} else if ((curbuf->b_ind_unclosed == 0 && is_if_for_while == 0)
|| (!curbuf->b_ind_unclosed_noignore
&& *look == '(' && ignore_paren_col == 0)) {
@@ -2271,9 +2288,10 @@ int get_c_indent(void)
}
}
- /* add extra indent for a comment */
- if (cin_iscomment(theline))
+ // add extra indent for a comment
+ if (cin_iscomment(theline)) {
amount += curbuf->b_ind_comment;
+ }
} else {
// We are inside braces, there is a { before this line at the position
// stored in tryposBrace.
@@ -2317,7 +2335,7 @@ int get_c_indent(void)
// ldfd) {
// }
if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label)
- && cin_iscase(skipwhite(get_cursor_line_ptr()), FALSE)) {
+ && cin_iscase(skipwhite(get_cursor_line_ptr()), false)) {
amount = get_indent();
} else if (curbuf->b_ind_js) {
amount = get_indent_lnum(lnum);
@@ -2348,14 +2366,15 @@ int get_c_indent(void)
* to match it with.
*/
lookfor = LOOKFOR_INITIAL;
- if (cin_iselse(theline))
+ if (cin_iselse(theline)) {
lookfor = LOOKFOR_IF;
- else if (cin_iswhileofdo(theline, cur_curpos.lnum)) /* XXX */
+ } else if (cin_iswhileofdo(theline, cur_curpos.lnum)) { // XXX
lookfor = LOOKFOR_DO;
+ }
if (lookfor != LOOKFOR_INITIAL) {
curwin->w_cursor.lnum = cur_curpos.lnum;
if (find_match(lookfor, ourscope) == OK) {
- amount = get_indent(); /* XXX */
+ amount = get_indent(); // XXX
goto theend;
}
}
@@ -2390,7 +2409,7 @@ int get_c_indent(void)
amount += curbuf->b_ind_cpp_extern_c;
}
} else {
- /* Compensate for adding b_ind_open_extra later. */
+ // Compensate for adding b_ind_open_extra later.
amount -= curbuf->b_ind_open_extra;
if (amount < 0)
amount = 0;
@@ -2399,19 +2418,20 @@ int get_c_indent(void)
lookfor_break = FALSE;
- if (cin_iscase(theline, FALSE)) { /* it's a switch() label */
- lookfor = LOOKFOR_CASE; /* find a previous switch() label */
+ if (cin_iscase(theline, false)) { // it's a switch() label
+ lookfor = LOOKFOR_CASE; // find a previous switch() label
amount += curbuf->b_ind_case;
- } else if (cin_isscopedecl(theline)) { /* private:, ... */
- lookfor = LOOKFOR_SCOPEDECL; /* class decl is this block */
+ } else if (cin_isscopedecl(theline)) { // private:, ...
+ lookfor = LOOKFOR_SCOPEDECL; // class decl is this block
amount += curbuf->b_ind_scopedecl;
} else {
- if (curbuf->b_ind_case_break && cin_isbreak(theline))
- /* break; ... */
- lookfor_break = TRUE;
+ if (curbuf->b_ind_case_break && cin_isbreak(theline)) {
+ // break; ...
+ lookfor_break = true;
+ }
lookfor = LOOKFOR_INITIAL;
- /* b_ind_level from start of block */
+ // b_ind_level from start of block
amount += curbuf->b_ind_level;
}
scope_amount = amount;
@@ -2503,16 +2523,17 @@ int get_c_indent(void)
if (terminated != ';' && cin_isinit())
break;
- /* nothing useful found */
- if (terminated == 0 || terminated == '{')
+ // nothing useful found
+ if (terminated == 0 || terminated == '{') {
continue;
+ }
}
if (terminated != ';') {
- /* Skip parens and braces. Position the cursor
- * over the rightmost paren, so that matching it
- * will take us back to the start of the line.
- */ /* XXX */
+ // Skip parens and braces. Position the cursor
+ // over the rightmost paren, so that matching it
+ // will take us back to the start of the line.
+ // XXX
trypos = NULL;
if (find_last_paren(l, '(', ')'))
trypos = find_match_paren(
@@ -2582,7 +2603,7 @@ int get_c_indent(void)
continue;
}
- /* Finally the actual check for "namespace". */
+ // Finally the actual check for "namespace".
if (cin_is_cpp_namespace(l)) {
amount += curbuf->b_ind_cpp_namespace
- added_to_amount;
@@ -2614,7 +2635,7 @@ int get_c_indent(void)
* If this is a switch() label, may line up relative to that.
* If this is a C++ scope declaration, do the same.
*/
- iscase = cin_iscase(l, FALSE);
+ bool iscase = cin_iscase(l, false);
if (iscase || cin_isscopedecl(l)) {
/* we are only looking for cpp base class
* declaration/initialization any longer */
@@ -2640,27 +2661,24 @@ int get_c_indent(void)
break;
}
- /*
- * case xx: <- line up with this case
- * x = 333;
- * case yy:
- */
- if ( (iscase && lookfor == LOOKFOR_CASE)
- || (iscase && lookfor_break)
- || (!iscase && lookfor == LOOKFOR_SCOPEDECL)) {
- /*
- * Check that this case label is not for another
- * switch()
- */ /* XXX */
+ // case xx: <- line up with this case
+ // x = 333;
+ // case yy:
+ if ((iscase && lookfor == LOOKFOR_CASE)
+ || (iscase && lookfor_break)
+ || (!iscase && lookfor == LOOKFOR_SCOPEDECL)) {
+ // Check that this case label is not for another
+ // switch()
+ // XXX
if ((trypos = find_start_brace()) == NULL
|| trypos->lnum == ourscope) {
- amount = get_indent(); /* XXX */
+ amount = get_indent(); // XXX
break;
}
continue;
}
- n = get_indent_nolabel(curwin->w_cursor.lnum); /* XXX */
+ n = get_indent_nolabel(curwin->w_cursor.lnum); // XXX
/*
* case xx: if (cond) <- line up with this if
@@ -2708,7 +2726,7 @@ int get_c_indent(void)
* case xx:
* -> y = 1;
*/
- scope_amount = get_indent() + (iscase /* XXX */
+ scope_amount = get_indent() + (iscase // XXX
? curbuf->b_ind_case_code
: curbuf->b_ind_scopedecl_code);
lookfor = curbuf->b_ind_case_break
@@ -2750,11 +2768,10 @@ int get_c_indent(void)
continue;
}
- /*
- * Are we at the start of a cpp base class declaration or
- * constructor initialization?
- */ /* XXX */
- n = FALSE;
+ // Are we at the start of a cpp base class declaration or
+ // constructor initialization?
+ // XXX
+ n = 0;
if (lookfor != LOOKFOR_TERM && curbuf->b_ind_cpp_baseclass > 0) {
n = cin_is_cpp_baseclass(&cache_cpp_baseclass);
l = get_cursor_line_ptr();
@@ -2766,13 +2783,14 @@ int get_c_indent(void)
else
amount += ind_continuation;
} else if (theline[0] == '{') {
- /* Need to find start of the declaration. */
+ // Need to find start of the declaration.
lookfor = LOOKFOR_UNTERM;
ind_continuation = 0;
continue;
- } else
- /* XXX */
+ } else {
+ // XXX
amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col);
+ }
break;
} else if (lookfor == LOOKFOR_CPP_BASECLASS) {
/* only look, whether there is a cpp base class
@@ -2871,8 +2889,8 @@ int get_c_indent(void)
*/
curwin->w_cursor = *trypos;
l = get_cursor_line_ptr();
- if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) {
- ++curwin->w_cursor.lnum;
+ if (cin_iscase(l, false) || cin_isscopedecl(l)) {
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
continue;
}
@@ -3025,9 +3043,10 @@ int get_c_indent(void)
* -> here;
*/
if (lookfor == LOOKFOR_UNTERM) {
- /* When line ends in a comma add extra indent */
- if (terminated == ',')
+ // When line ends in a comma add extra indent
+ if (terminated == ',') {
amount += ind_continuation;
+ }
break;
}
@@ -3144,9 +3163,10 @@ int get_c_indent(void)
if (whilelevel == 0) {
lookfor = LOOKFOR_TERM;
- amount = get_indent(); /* XXX */
- if (theline[0] == '{')
+ amount = get_indent(); // XXX
+ if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
+ }
}
++whilelevel;
}
@@ -3174,8 +3194,8 @@ int get_c_indent(void)
if (whilelevel > 0) {
l = cin_skipcomment(get_cursor_line_ptr());
if (cin_isdo(l)) {
- amount = get_indent(); /* XXX */
- --whilelevel;
+ amount = get_indent(); // XXX
+ whilelevel--;
continue;
}
}
@@ -3240,8 +3260,8 @@ term_again:
*/
curwin->w_cursor = *trypos;
l = get_cursor_line_ptr();
- if (cin_iscase(l, FALSE) || cin_isscopedecl(l)) {
- ++curwin->w_cursor.lnum;
+ if (cin_iscase(l, false) || cin_isscopedecl(l)) {
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
continue;
}
@@ -3256,8 +3276,7 @@ term_again:
* stat;
* }
*/
- iscase = (curbuf->b_ind_keep_case_label
- && cin_iscase(l, FALSE));
+ iscase = curbuf->b_ind_keep_case_label && cin_iscase(l, false);
/*
* Get indent and pointer to text for current line,
@@ -3267,7 +3286,7 @@ term_again:
if (theline[0] == '{')
amount += curbuf->b_ind_open_extra;
- /* See remark above: "Only add b_ind_open_extra.." */
+ // See remark above: "Only add b_ind_open_extra.."
l = skipwhite(l);
if (*l == '{')
amount -= curbuf->b_ind_open_extra;
@@ -3297,11 +3316,11 @@ term_again:
* that block.
*/
l = get_cursor_line_ptr();
- if (find_last_paren(l, '{', '}') /* XXX */
+ if (find_last_paren(l, '{', '}') // XXX
&& (trypos = find_start_brace()) != NULL) {
curwin->w_cursor = *trypos;
- /* if not "else {" check for terminated again */
- /* but skip block for "} else {" */
+ // if not "else {" check for terminated again
+ // but skip block for "} else {"
l = cin_skipcomment(get_cursor_line_ptr());
if (*l == '}' || !cin_iselse(l))
goto term_again;
@@ -3314,13 +3333,14 @@ term_again:
}
}
- /* add extra indent for a comment */
- if (cin_iscomment(theline))
+ // add extra indent for a comment
+ if (cin_iscomment(theline)) {
amount += curbuf->b_ind_comment;
-
- /* subtract extra left-shift for jump labels */
- if (curbuf->b_ind_jump_label > 0 && original_line_islabel)
+ }
+ // subtract extra left-shift for jump labels
+ if (curbuf->b_ind_jump_label > 0 && original_line_islabel) {
amount -= curbuf->b_ind_jump_label;
+ }
goto theend;
}
@@ -3360,7 +3380,7 @@ term_again:
goto theend;
}
- /* search backwards until we find something we recognize */
+ // search backwards until we find something we recognize
amount = 0;
curwin->w_cursor = cur_curpos;
while (curwin->w_cursor.lnum > 1) {
@@ -3386,7 +3406,7 @@ term_again:
l = get_cursor_line_ptr();
}
if (n) {
- /* XXX */
+ // XXX
amount = get_baseclass_amount(cache_cpp_baseclass.lpos.col);
break;
}
@@ -3415,11 +3435,11 @@ term_again:
*/
if (cin_ends_in(l, (char_u *)",", NULL)
|| (*l != NUL && (n = l[STRLEN(l) - 1]) == '\\')) {
- /* take us back to opening paren */
+ // take us back to opening paren
if (find_last_paren(l, '(', ')')
- && (trypos = find_match_paren(
- curbuf->b_ind_maxparen)) != NULL)
+ && (trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL) {
curwin->w_cursor = *trypos;
+ }
/* For a line ending in ',' that is a continuation line go
* back to the first line with a backslash:
@@ -3435,7 +3455,7 @@ term_again:
curwin->w_cursor.col = 0;
}
- amount = get_indent(); /* XXX */
+ amount = get_indent(); // XXX
if (amount == 0)
amount = cin_first_id_amount();
@@ -3448,8 +3468,9 @@ term_again:
* If the line looks like a function declaration, and we're
* not in a comment, put it the left margin.
*/
- if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) /* XXX */
+ if (cin_isfuncdecl(NULL, cur_curpos.lnum, 0)) { // XXX
break;
+ }
l = get_cursor_line_ptr();
/*
@@ -3535,13 +3556,14 @@ term_again:
if ((trypos = find_match_paren(curbuf->b_ind_maxparen)) != NULL)
curwin->w_cursor = *trypos;
- amount = get_indent(); /* XXX */
+ amount = get_indent(); // XXX
break;
}
- /* add extra indent for a comment */
- if (cin_iscomment(theline))
+ // add extra indent for a comment
+ if (cin_iscomment(theline)) {
amount += curbuf->b_ind_comment;
+ }
/* add extra indent if the previous line ended in a backslash:
* "asdfasdf\
@@ -3565,7 +3587,7 @@ theend:
amount = 0;
laterend:
- /* put the cursor back where it belongs */
+ // put the cursor back where it belongs
curwin->w_cursor = cur_curpos;
xfree(linecopy);
@@ -3598,7 +3620,7 @@ static int find_match(int lookfor, linenr_T ourscope)
look = cin_skipcomment(get_cursor_line_ptr());
if (!cin_iselse(look)
&& !cin_isif(look)
- && !cin_isdo(look) /* XXX */
+ && !cin_isdo(look) // XXX
&& !cin_iswhileofdo(look, curwin->w_cursor.lnum)) {
continue;
}
@@ -3607,9 +3629,10 @@ static int find_match(int lookfor, linenr_T ourscope)
* if we've gone outside the braces entirely,
* we must be out of scope...
*/
- theirscope = find_start_brace(); /* XXX */
- if (theirscope == NULL)
+ theirscope = find_start_brace(); // XXX
+ if (theirscope == NULL) {
break;
+ }
/*
* and if the brace enclosing this is further
@@ -3649,7 +3672,7 @@ static int find_match(int lookfor, linenr_T ourscope)
continue;
}
- /* If it's an "if" decrement elselevel */
+ // If it's an "if" decrement elselevel
look = cin_skipcomment(get_cursor_line_ptr());
if (cin_isif(look)) {
elselevel--;
@@ -3661,9 +3684,10 @@ static int find_match(int lookfor, linenr_T ourscope)
whilelevel = 0;
}
- /* If it's a "do" decrement whilelevel */
- if (cin_isdo(look))
+ // If it's a "do" decrement whilelevel
+ if (cin_isdo(look)) {
whilelevel--;
+ }
/*
* if we've used up all the elses, then
diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue.h
index ab9270081e..452998a5a4 100644
--- a/src/nvim/lib/queue.h
+++ b/src/nvim/lib/queue.h
@@ -33,11 +33,17 @@ typedef struct _queue {
#define QUEUE_DATA(ptr, type, field) \
((type *)((char *)(ptr) - offsetof(type, field)))
-// Important note: mutating the list while QUEUE_FOREACH is
-// iterating over its elements results in undefined behavior.
-#define QUEUE_FOREACH(q, h) \
- for ( /* NOLINT(readability/braces) */ \
- (q) = (h)->next; (q) != (h); (q) = (q)->next)
+// Important note: the node currently being processed can be safely deleted.
+// otherwise, mutating the list while QUEUE_FOREACH is iterating over its
+// elements results in undefined behavior.
+#define QUEUE_FOREACH(q, h, code) \
+ (q) = (h)->next; \
+ while((q) != (h)) { \
+ QUEUE *next = q->next; \
+ code \
+ (q) = next; \
+ }
+
// ffi.cdef is unable to swallow `bool` in place of `int` here.
static inline int QUEUE_EMPTY(const QUEUE *const q)
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 310b194c8c..03d178467b 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1272,6 +1272,12 @@ bool nlua_exec_file(const char *path)
return true;
}
+int tslua_get_language_version(lua_State *L)
+{
+ lua_pushnumber(L, TREE_SITTER_LANGUAGE_VERSION);
+ return 1;
+}
+
static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
{
tslua_init(lstate);
@@ -1288,8 +1294,11 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, tslua_inspect_lang);
lua_setfield(lstate, -2, "_ts_inspect_language");
- lua_pushcfunction(lstate, ts_lua_parse_query);
+ lua_pushcfunction(lstate, tslua_parse_query);
lua_setfield(lstate, -2, "_ts_parse_query");
+
+ lua_pushcfunction(lstate, tslua_get_language_version);
+ lua_setfield(lstate, -2, "_ts_get_language_version");
}
int nlua_expand_pat(expand_T *xp,
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index a640b97d3b..38848b0266 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -171,7 +171,7 @@ int tslua_add_language(lua_State *L)
TSLanguage *lang = lang_parser();
if (lang == NULL) {
- return luaL_error(L, "Failed to load parser: internal error");
+ return luaL_error(L, "Failed to load parser %s: internal error", path);
}
uint32_t lang_version = ts_language_version(lang);
@@ -179,7 +179,8 @@ int tslua_add_language(lua_State *L)
|| lang_version > TREE_SITTER_LANGUAGE_VERSION) {
return luaL_error(
L,
- "ABI version mismatch : supported between %d and %d, found %d",
+ "ABI version mismatch for %s: supported between %d and %d, found %d",
+ path,
TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION,
TREE_SITTER_LANGUAGE_VERSION, lang_version);
}
@@ -221,8 +222,9 @@ int tslua_inspect_lang(lua_State *L)
lua_setfield(L, -2, "symbols"); // [retval]
size_t nfields = (size_t)ts_language_field_count(lang);
- lua_createtable(L, nfields-1, 1); // [retval, fields]
- for (size_t i = 0; i < nfields; i++) {
+ lua_createtable(L, nfields, 1); // [retval, fields]
+ // Field IDs go from 1 to nfields inclusive (extra index 0 maps to NULL)
+ for (size_t i = 1; i <= nfields; i++) {
lua_pushstring(L, ts_language_field_name_for_id(lang, i));
lua_rawseti(L, -2, i); // [retval, fields]
}
@@ -1108,7 +1110,7 @@ static int querycursor_gc(lua_State *L)
// Query methods
-int ts_lua_parse_query(lua_State *L)
+int tslua_parse_query(lua_State *L)
{
if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_isstring(L, 2)) {
return luaL_error(L, "string expected");
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index e13b9745a8..eb54ff28ee 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -263,8 +263,15 @@ end
-- vim.fn.{func}(...)
vim.fn = setmetatable({}, {
__index = function(t, key)
- local function _fn(...)
- return vim.call(key, ...)
+ local _fn
+ if vim.api[key] ~= nil then
+ _fn = function()
+ error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key))
+ end
+ else
+ _fn = function(...)
+ return vim.call(key, ...)
+ end
end
t[key] = _fn
return _fn
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index ec4f4cbc21..73e3ba53a5 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -571,11 +571,12 @@ size_t mb_string2cells(const char_u *str)
/// @param size maximum length of string. It will terminate on earlier NUL.
/// @return The number of cells occupied by string `str`
size_t mb_string2cells_len(const char_u *str, size_t size)
+ FUNC_ATTR_NONNULL_ARG(1)
{
size_t clen = 0;
for (const char_u *p = str; *p != NUL && p < str+size;
- p += utf_ptr2len_len(p, size+(p-str))) {
+ p += utfc_ptr2len_len(p, size+(p-str))) {
clen += utf_ptr2cells(p);
}
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 293a4d01db..34d8eb0ffe 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3859,8 +3859,8 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype)
/* May resize here so we don't have to do it in both cases below */
if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks) {
buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
- buf->b_ml.ml_chunksize = (chunksize_T *)
- xrealloc(buf->b_ml.ml_chunksize,
+ buf->b_ml.ml_chunksize = xrealloc(
+ buf->b_ml.ml_chunksize,
sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index ba7a667a60..7c98d3c6b5 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -178,6 +178,7 @@ void msg_grid_validate(void)
msg_grid.throttled = false; // don't throttle in 'cmdheight' area
msg_scrolled_at_flush = msg_scrolled;
msg_grid.focusable = false;
+ msg_grid_adj.target = &msg_grid;
if (!msg_scrolled) {
msg_grid_set_pos(Rows - p_ch, false);
}
@@ -188,6 +189,7 @@ void msg_grid_validate(void)
ui_call_grid_destroy(msg_grid.handle);
msg_grid.throttled = false;
msg_grid_adj.row_offset = 0;
+ msg_grid_adj.target = &default_grid;
redraw_cmdline = true;
} else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != Rows - p_ch) {
msg_grid_set_pos(Rows - p_ch, false);
@@ -867,18 +869,18 @@ char_u *msg_trunc_attr(char_u *s, int force, int attr)
*/
char_u *msg_may_trunc(int force, char_u *s)
{
- int n;
int room;
room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
if ((force || (shortmess(SHM_TRUNC) && !exmode_active))
- && (n = (int)STRLEN(s) - room) > 0) {
+ && (int)STRLEN(s) - room > 0) {
int size = vim_strsize(s);
// There may be room anyway when there are multibyte chars.
if (size <= room) {
return s;
}
+ int n;
for (n = 0; size >= room; ) {
size -= utf_ptr2cells(s + n);
n += utfc_ptr2len(s + n);
@@ -1703,6 +1705,7 @@ void msg_prt_line(char_u *s, int list)
char_u *p_extra = NULL; // init to make SASC shut up
int n;
int attr = 0;
+ char_u *lead = NULL;
char_u *trail = NULL;
int l;
@@ -1710,11 +1713,24 @@ void msg_prt_line(char_u *s, int list)
list = true;
}
- // find start of trailing whitespace
- if (list && curwin->w_p_lcs_chars.trail) {
- trail = s + STRLEN(s);
- while (trail > s && ascii_iswhite(trail[-1])) {
- trail--;
+ if (list) {
+ // find start of trailing whitespace
+ if (curwin->w_p_lcs_chars.trail) {
+ trail = s + STRLEN(s);
+ while (trail > s && ascii_iswhite(trail[-1])) {
+ trail--;
+ }
+ }
+ // find end of leading whitespace
+ if (curwin->w_p_lcs_chars.lead) {
+ lead = s;
+ while (ascii_iswhite(lead[0])) {
+ lead++;
+ }
+ // in a line full of spaces all of them are treated as trailing
+ if (*lead == NUL) {
+ lead = NULL;
+ }
}
}
@@ -1756,7 +1772,9 @@ void msg_prt_line(char_u *s, int list)
c = *s++;
if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
// tab amount depends on current column
- n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
+ n_extra = tabstop_padding(col,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array) - 1;
if (!list) {
c = ' ';
c_extra = ' ';
@@ -1789,6 +1807,9 @@ void msg_prt_line(char_u *s, int list)
/* Use special coloring to be able to distinguish <hex> from
* the same in plain text. */
attr = HL_ATTR(HLF_8);
+ } else if (c == ' ' && lead != NULL && s <= lead) {
+ c = curwin->w_p_lcs_chars.lead;
+ attr = HL_ATTR(HLF_8);
} else if (c == ' ' && trail != NULL && s > trail) {
c = curwin->w_p_lcs_chars.trail;
attr = HL_ATTR(HLF_8);
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index ff471ea978..4c0339e5f4 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -72,9 +72,7 @@ int jump_to_mouse(int flags,
int row = mouse_row;
int col = mouse_col;
int grid = mouse_grid;
- int mouse_char;
int fdc = 0;
- ScreenGrid *gp = &default_grid;
mouse_past_bottom = false;
mouse_past_eol = false;
@@ -303,25 +301,6 @@ retnomove:
}
}
- // Remember the character under the mouse, might be one of foldclose or
- // foldopen fillchars in the fold column.
- if (ui_has(kUIMultigrid)) {
- gp = &curwin->w_grid;
- }
- if (row >= 0 && row < Rows && col >= 0 && col <= Columns
- && gp->chars != NULL) {
- mouse_char = utf_ptr2char(gp->chars[gp->line_offset[row]
- + (unsigned)col]);
- } else {
- mouse_char = ' ';
- }
-
- // Check for position outside of the fold column.
- if (curwin->w_p_rl ? col < curwin->w_width_inner - fdc :
- col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
- mouse_char = ' ';
- }
-
// compute the position in the buffer line from the posn on the screen
if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum)) {
mouse_past_bottom = true;
@@ -362,11 +341,7 @@ retnomove:
count |= CURSOR_MOVED; // Cursor has moved
}
- if (mouse_char == curwin->w_p_fcs_chars.foldclosed) {
- count |= MOUSE_FOLD_OPEN;
- } else if (mouse_char != ' ') {
- count |= MOUSE_FOLD_CLOSE;
- }
+ count |= mouse_check_fold();
return count;
}
@@ -495,21 +470,21 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
*gridp = DEFAULT_GRID_HANDLE;
} else if (*gridp > 1) {
win_T *wp = get_win_by_grid_handle(*gridp);
- if (wp && wp->w_grid.chars
+ if (wp && wp->w_grid_alloc.chars
&& !(wp->w_floating && !wp->w_float_config.focusable)) {
- *rowp = MIN(*rowp, wp->w_grid.Rows-1);
- *colp = MIN(*colp, wp->w_grid.Columns-1);
+ *rowp = MIN(*rowp-wp->w_grid.row_offset, wp->w_grid.Rows-1);
+ *colp = MIN(*colp-wp->w_grid.col_offset, wp->w_grid.Columns-1);
return wp;
}
} else if (*gridp == 0) {
ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (&wp->w_grid != grid) {
+ if (&wp->w_grid_alloc != grid) {
continue;
}
*gridp = grid->handle;
- *rowp -= grid->comp_row;
- *colp -= grid->comp_col;
+ *rowp -= grid->comp_row+wp->w_grid.row_offset;
+ *colp -= grid->comp_col+wp->w_grid.col_offset;
return wp;
}
@@ -738,3 +713,46 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
return col + nudge;
}
+
+// Check clicked cell is foldcolumn
+int mouse_check_fold(void)
+{
+ int click_grid = mouse_grid;
+ int click_row = mouse_row;
+ int click_col = mouse_col;
+ int mouse_char = ' ';
+
+ win_T *wp;
+
+ wp = mouse_find_win(&click_grid, &click_row, &click_col);
+
+ if (wp && mouse_row >= 0 && mouse_row < Rows
+ && mouse_col >= 0 && mouse_col <= Columns) {
+ int multigrid = ui_has(kUIMultigrid);
+ ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
+ int fdc = win_fdccol_count(wp);
+ int row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
+ int col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
+
+ // Remember the character under the mouse, might be one of foldclose or
+ // foldopen fillchars in the fold column.
+ if (gp->chars != NULL) {
+ mouse_char = utf_ptr2char(gp->chars[gp->line_offset[row]
+ + (unsigned)col]);
+ }
+
+ // Check for position outside of the fold column.
+ if (wp->w_p_rl ? click_col < wp->w_width_inner - fdc :
+ click_col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
+ mouse_char = ' ';
+ }
+ }
+
+ if (wp && mouse_char == wp->w_p_fcs_chars.foldclosed) {
+ return MOUSE_FOLD_OPEN;
+ } else if (mouse_char != ' ') {
+ return MOUSE_FOLD_CLOSE;
+ }
+
+ return 0;
+}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index d5c92bc3e6..3587b12277 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2404,8 +2404,8 @@ do_mouse (
start_visual.lnum = 0;
- /* Check for clicking in the tab page line. */
- if (mouse_row == 0 && firstwin->w_winrow > 0) {
+ // Check for clicking in the tab page line.
+ if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) {
if (in_tab_line) {
move_tab_to_mouse();
@@ -6777,9 +6777,10 @@ static void nv_g_cmd(cmdarg_T *cap)
}
coladvance((colnr_T)i);
if (flag) {
- do
+ do {
i = gchar_cursor();
- while (ascii_iswhite(i) && oneright());
+ } while (ascii_iswhite(i) && oneright());
+ curwin->w_valid &= ~VALID_WCOL;
}
curwin->w_set_curswant = true;
break;
@@ -8103,7 +8104,7 @@ static void nv_event(cmdarg_T *cap)
// lists or dicts being used.
may_garbage_collect = false;
bool may_restart = (restart_edit != 0);
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
finish_op = false;
if (may_restart) {
// Tricky: if restart_edit was set before the handler we are in ctrl-o mode,
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 3038fad894..2cd71f2360 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -288,7 +288,7 @@ void shift_line(
{
int count;
int i, j;
- int p_sw = get_sw_value(curbuf);
+ int p_sw = (int)get_sw_value_indent(curbuf);
count = get_indent(); // get current indent
@@ -332,8 +332,9 @@ static void shift_block(oparg_T *oap, int amount)
const int oldstate = State;
char_u *newp;
const int oldcol = curwin->w_cursor.col;
- const int p_sw = get_sw_value(curbuf);
- const int p_ts = (int)curbuf->b_p_ts;
+ int p_sw = (int)get_sw_value_indent(curbuf);
+ long *p_vts = curbuf->b_p_vts_array;
+ const long p_ts = curbuf->b_p_ts;
struct block_def bd;
int incr;
int i = 0, j = 0;
@@ -383,12 +384,11 @@ static void shift_block(oparg_T *oap, int amount)
}
/* OK, now total=all the VWS reqd, and textstart points at the 1st
* non-ws char in the block. */
- if (!curbuf->b_p_et)
- i = ((ws_vcol % p_ts) + total) / p_ts; /* number of tabs */
- if (i)
- j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
- else
+ if (!curbuf->b_p_et) {
+ tabstop_fromto(ws_vcol, ws_vcol + total, p_ts, p_vts, &i, &j);
+ } else {
j = total;
+ }
// if we're splitting a TAB, allow for it
int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0);
@@ -1079,13 +1079,15 @@ do_execreg(
}
}
escaped = vim_strsave_escape_csi(reg->y_array[i]);
- retval = ins_typebuf(escaped, remap, 0, TRUE, silent);
+ retval = ins_typebuf(escaped, remap, 0, true, silent);
xfree(escaped);
- if (retval == FAIL)
+ if (retval == FAIL) {
return FAIL;
- if (colon && ins_typebuf((char_u *)":", remap, 0, TRUE, silent)
- == FAIL)
+ }
+ if (colon
+ && ins_typebuf((char_u *)":", remap, 0, true, silent) == FAIL) {
return FAIL;
+ }
}
reg_executing = regname == 0 ? '"' : regname; // disable the 'q' command
}
@@ -1109,8 +1111,9 @@ static void put_reedit_in_typebuf(int silent)
buf[0] = (char_u)(restart_edit == 'I' ? 'i' : restart_edit);
buf[1] = NUL;
}
- if (ins_typebuf(buf, REMAP_NONE, 0, TRUE, silent) == OK)
+ if (ins_typebuf(buf, REMAP_NONE, 0, true, silent) == OK) {
restart_edit = NUL;
+ }
}
}
@@ -1130,25 +1133,29 @@ static int put_in_typebuf(
int retval = OK;
put_reedit_in_typebuf(silent);
- if (colon)
- retval = ins_typebuf((char_u *)"\n", REMAP_NONE, 0, TRUE, silent);
+ if (colon) {
+ retval = ins_typebuf((char_u *)"\n", REMAP_NONE, 0, true, silent);
+ }
if (retval == OK) {
char_u *p;
- if (esc)
+ if (esc) {
p = vim_strsave_escape_csi(s);
- else
+ } else {
p = s;
- if (p == NULL)
+ }
+ if (p == NULL) {
retval = FAIL;
- else
- retval = ins_typebuf(p, esc ? REMAP_NONE : REMAP_YES,
- 0, TRUE, silent);
- if (esc)
+ } else {
+ retval = ins_typebuf(p, esc ? REMAP_NONE : REMAP_YES, 0, true, silent);
+ }
+ if (esc) {
xfree(p);
+ }
+ }
+ if (colon && retval == OK) {
+ retval = ins_typebuf((char_u *)":", REMAP_NONE, 0, true, silent);
}
- if (colon && retval == OK)
- retval = ins_typebuf((char_u *)":", REMAP_NONE, 0, TRUE, silent);
return retval;
}
@@ -2800,7 +2807,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
size_t y_size;
size_t oldlen;
int y_width = 0;
- colnr_T vcol;
+ colnr_T vcol = 0;
int delcount;
int incr = 0;
struct block_def bd;
@@ -3061,14 +3068,17 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (gchar_cursor() == TAB) {
/* Don't need to insert spaces when "p" on the last position of a
* tab or "P" on the first position. */
+ int viscol = getviscol();
if (dir == FORWARD
- ? (int)curwin->w_cursor.coladd < curbuf->b_p_ts - 1
- : curwin->w_cursor.coladd > 0)
- coladvance_force(getviscol());
- else
+ ? tabstop_padding(viscol, curbuf->b_p_ts, curbuf->b_p_vts_array) != 1
+ : curwin->w_cursor.coladd > 0) {
+ coladvance_force(viscol);
+ } else {
curwin->w_cursor.coladd = 0;
- } else if (curwin->w_cursor.coladd > 0 || gchar_cursor() == NUL)
+ }
+ } else if (curwin->w_cursor.coladd > 0 || gchar_cursor() == NUL) {
coladvance_force(getviscol() + (dir == FORWARD));
+ }
}
lnum = curwin->w_cursor.lnum;
@@ -3177,7 +3187,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// insert the new text
totlen = (size_t)(count * (yanklen + spaces)
+ bd.startspaces + bd.endspaces);
- newp = (char_u *) xmalloc(totlen + oldlen + 1);
+ int addcount = (int)totlen + lines_appended;
+ newp = (char_u *)xmalloc(totlen + oldlen + 1);
// copy part up to cursor to new line
ptr = newp;
memmove(ptr, oldp, (size_t)bd.textcol);
@@ -3194,6 +3205,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if ((j < count - 1 || !shortline) && spaces) {
memset(ptr, ' ', (size_t)spaces);
ptr += spaces;
+ } else {
+ addcount -= spaces;
}
}
// may insert some spaces after the new text
@@ -3205,7 +3218,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns);
ml_replace(curwin->w_cursor.lnum, newp, false);
extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum-1, bd.textcol,
- delcount, (int)totlen + lines_appended, kExtmarkUndo);
+ delcount, addcount, kExtmarkUndo);
++curwin->w_cursor.lnum;
if (i == 0)
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ac25c86b5f..612ecca96a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -180,6 +180,8 @@ static long p_ts;
static long p_tw;
static int p_udf;
static long p_wm;
+static char_u *p_vsts;
+static char_u *p_vts;
static char_u *p_keymap;
// Saved values for when 'bin' is set.
@@ -194,6 +196,7 @@ static int p_et_nopaste;
static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
+static char_u *p_vsts_nopaste;
typedef struct vimoption {
char *fullname; // full option name
@@ -379,8 +382,8 @@ void set_init_1(bool clean_arg)
# else
static char *(names[3]) = {"TMPDIR", "TEMP", "TMP"};
# endif
- int len;
garray_T ga;
+ opt_idx = findoption("backupskip");
ga_init(&ga, 1, 100);
for (size_t n = 0; n < ARRAY_SIZE(names); n++) {
@@ -401,15 +404,23 @@ void set_init_1(bool clean_arg)
}
if (p != NULL && *p != NUL) {
// First time count the NUL, otherwise count the ','.
- len = (int)strlen(p) + 3;
- ga_grow(&ga, len);
- if (!GA_EMPTY(&ga)) {
- STRCAT(ga.ga_data, ",");
+ const size_t len = strlen(p) + 3;
+ char *item = xmalloc(len);
+ xstrlcpy(item, p, len);
+ add_pathsep(item);
+ xstrlcat(item, "*", len);
+ if (find_dup_item(ga.ga_data, (char_u *)item, options[opt_idx].flags)
+ == NULL) {
+ ga_grow(&ga, (int)len);
+ if (!GA_EMPTY(&ga)) {
+ STRCAT(ga.ga_data, ",");
+ }
+ STRCAT(ga.ga_data, p);
+ add_pathsep(ga.ga_data);
+ STRCAT(ga.ga_data, "*");
+ ga.ga_len += (int)len;
}
- STRCAT(ga.ga_data, p);
- add_pathsep(ga.ga_data);
- STRCAT(ga.ga_data, "*");
- ga.ga_len += len;
+ xfree(item);
}
if(mustfree) {
xfree(p);
@@ -713,6 +724,38 @@ static void set_string_default(const char *name, char *val, bool allocated)
}
}
+// For an option value that contains comma separated items, find "newval" in
+// "origval". Return NULL if not found.
+static char_u *find_dup_item(char_u *origval, const char_u *newval,
+ uint32_t flags)
+ FUNC_ATTR_NONNULL_ARG(2)
+{
+ int bs = 0;
+
+ if (origval == NULL) {
+ return NULL;
+ }
+
+ const size_t newlen = STRLEN(newval);
+ for (char_u *s = origval; *s != NUL; s++) {
+ if ((!(flags & P_COMMA) || s == origval || (s[-1] == ',' && !(bs & 1)))
+ && STRNCMP(s, newval, newlen) == 0
+ && (!(flags & P_COMMA) || s[newlen] == ',' || s[newlen] == NUL)) {
+ return s;
+ }
+ // Count backslashes. Only a comma with an even number of backslashes
+ // or a single backslash preceded by a comma before it is recognized as
+ // a separator.
+ if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',')
+ || (s == origval + 1 && s[-1] == '\\')) {
+ bs++;
+ } else {
+ bs = 0;
+ }
+ }
+ return NULL;
+}
+
/// Set the Vi-default value of a number option.
/// Used for 'lines' and 'columns'.
void set_number_default(char *name, long val)
@@ -1285,9 +1328,7 @@ int do_set(
char *saved_newval = NULL;
unsigned newlen;
int comma;
- int bs;
- int new_value_alloced; /* new string option
- was allocated */
+ bool new_value_alloced = false; // new string option was allocated
/* When using ":set opt=val" for a global option
* with a local value the local value will be
@@ -1486,34 +1527,20 @@ int do_set(
i = 0; // init for GCC
if (removing || (flags & P_NODUP)) {
i = (int)STRLEN(newval);
- bs = 0;
- for (s = origval; *s; s++) {
- if ((!(flags & P_COMMA)
- || s == origval
- || (s[-1] == ',' && !(bs & 1)))
- && STRNCMP(s, newval, i) == 0
- && (!(flags & P_COMMA)
- || s[i] == ','
- || s[i] == NUL)) {
- break;
- }
- // Count backslashes. Only a comma with an even number of
- // backslashes or a single backslash preceded by a comma
- // before it is recognized as a separator
- if ((s > origval + 1 && s[-1] == '\\' && s[-2] != ',')
- || (s == origval + 1 && s[-1] == '\\')) {
- bs++;
- } else {
- bs = 0;
- }
- }
+ s = find_dup_item(origval, newval, flags);
// do not add if already there
- if ((adding || prepending) && *s) {
+ if ((adding || prepending) && s != NULL) {
prepending = false;
adding = false;
STRCPY(newval, origval);
}
+
+ // if no duplicate, move pointer to end of
+ // original value
+ if (s == NULL) {
+ s = origval + (int)STRLEN(origval);
+ }
}
/* concatenate the two strings; add a ',' if
@@ -1974,6 +2001,10 @@ static void didset_options2(void)
// Parse default for 'wildmode'.
check_opt_wim();
+ xfree(curbuf->b_p_vsts_array);
+ tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
+ xfree(curbuf->b_p_vts_array);
+ tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array);
}
/// Check for string options that are NULL (normally only termcap options).
@@ -2040,6 +2071,8 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_lw);
check_string_option(&buf->b_p_bkc);
check_string_option(&buf->b_p_menc);
+ check_string_option(&buf->b_p_vsts);
+ check_string_option(&buf->b_p_vts);
}
/// Free the string allocated for an option.
@@ -2086,9 +2119,12 @@ int was_set_insecurely(win_T *const wp, char_u *opt, int opt_flags)
/// Get a pointer to the flags used for the P_INSECURE flag of option
/// "opt_idx". For some local options a local flags field is used.
+/// NOTE: Caller must make sure that "wp" is set to the window from which
+/// the option is used.
static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
{
- if (opt_flags & OPT_LOCAL)
+ if (opt_flags & OPT_LOCAL) {
+ assert(wp != NULL);
switch ((int)options[opt_idx].indir) {
case PV_STL: return &wp->w_p_stl_flags;
case PV_FDE: return &wp->w_p_fde_flags;
@@ -2097,6 +2133,7 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
case PV_FEX: return &wp->w_buffer->b_p_fex_flags;
case PV_INEX: return &wp->w_buffer->b_p_inex_flags;
}
+ }
// Nothing special, return global flags field.
return &options[opt_idx].flags;
@@ -2306,7 +2343,7 @@ static char_u *
did_set_string_option(
int opt_idx, // index in options[] table
char_u **varp, // pointer to the option variable
- int new_value_alloced, // new value was allocated
+ bool new_value_alloced, // new value was allocated
char_u *oldval, // previous value of the option
char_u *errbuf, // buffer for errors, or NULL
size_t errbuflen, // length of errors buffer
@@ -3082,6 +3119,65 @@ ambw_end:
if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
errmsg = e_invarg;
}
+ } else if (varp == &(curbuf->b_p_vsts)) { // 'varsofttabstop'
+ char_u *cp;
+
+ if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
+ if (curbuf->b_p_vsts_array) {
+ xfree(curbuf->b_p_vsts_array);
+ curbuf->b_p_vsts_array = 0;
+ }
+ } else {
+ for (cp = *varp; *cp; cp++) {
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
+ continue;
+ }
+ errmsg = e_invarg;
+ break;
+ }
+ if (errmsg == NULL) {
+ long *oldarray = curbuf->b_p_vsts_array;
+ if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
+ xfree(oldarray);
+ } else {
+ errmsg = e_invarg;
+ }
+ }
+ }
+ } else if (varp == &(curbuf->b_p_vts)) { // 'vartabstop'
+ char_u *cp;
+
+ if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
+ if (curbuf->b_p_vts_array) {
+ xfree(curbuf->b_p_vts_array);
+ curbuf->b_p_vts_array = NULL;
+ }
+ } else {
+ for (cp = *varp; *cp; cp++) {
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
+ continue;
+ }
+ errmsg = e_invarg;
+ break;
+ }
+ if (errmsg == NULL) {
+ long *oldarray = curbuf->b_p_vts_array;
+ if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
+ xfree(oldarray);
+ if (foldmethodIsIndent(curwin)) {
+ foldUpdateAll(curwin);
+ }
+ } else {
+ errmsg = e_invarg;
+ }
+ }
+ }
} else {
// Options that are a list of flags.
p = NULL;
@@ -3381,6 +3477,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp, bool set)
{ &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 },
};
@@ -4283,7 +4380,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
// 'floatblend'
curwin->w_p_winbl = MAX(MIN(curwin->w_p_winbl, 100), 0);
curwin->w_hl_needs_update = true;
- curwin->w_grid.blending = curwin->w_p_winbl > 0;
+ curwin->w_grid_alloc.blending = curwin->w_p_winbl > 0;
}
@@ -5656,6 +5753,8 @@ static char_u *get_varp(vimoption_T *p)
case PV_TW: return (char_u *)&(curbuf->b_p_tw);
case PV_UDF: return (char_u *)&(curbuf->b_p_udf);
case PV_WM: return (char_u *)&(curbuf->b_p_wm);
+ case PV_VSTS: return (char_u *)&(curbuf->b_p_vsts);
+ case PV_VTS: return (char_u *)&(curbuf->b_p_vts);
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
@@ -5796,7 +5895,7 @@ void didset_window_options(win_T *wp)
set_chars_option(wp, &wp->w_p_fcs, true);
set_chars_option(wp, &wp->w_p_lcs, true);
parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl
- wp->w_grid.blending = wp->w_p_winbl > 0;
+ wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
}
@@ -5907,6 +6006,15 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_tfu = vim_strsave(p_tfu);
buf->b_p_sts = p_sts;
buf->b_p_sts_nopaste = p_sts_nopaste;
+ buf->b_p_vsts = vim_strsave(p_vsts);
+ if (p_vsts && p_vsts != empty_option) {
+ tabstop_set(p_vsts, &buf->b_p_vsts_array);
+ } else {
+ buf->b_p_vsts_array = 0;
+ }
+ buf->b_p_vsts_nopaste = p_vsts_nopaste
+ ? vim_strsave(p_vsts_nopaste)
+ : NULL;
buf->b_p_com = vim_strsave(p_com);
buf->b_p_cms = vim_strsave(p_cms);
buf->b_p_fo = vim_strsave(p_fo);
@@ -5978,10 +6086,21 @@ void buf_copy_options(buf_T *buf, int flags)
*/
if (dont_do_help) {
buf->b_p_isk = save_p_isk;
+ if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
+ tabstop_set(p_vts, &buf->b_p_vts_array);
+ } else {
+ buf->b_p_vts_array = NULL;
+ }
} else {
buf->b_p_isk = vim_strsave(p_isk);
did_isk = true;
buf->b_p_ts = p_ts;
+ buf->b_p_vts = vim_strsave(p_vts);
+ if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
+ tabstop_set(p_vts, &buf->b_p_vts_array);
+ } else {
+ buf->b_p_vts_array = NULL;
+ }
buf->b_help = false;
if (buf->b_p_bt[0] == 'h') {
clear_string_option(&buf->b_p_bt);
@@ -6178,6 +6297,8 @@ set_context_in_set_cmd(
xp->xp_backslash = XP_BS_THREE;
else
xp->xp_backslash = XP_BS_ONE;
+ } else if (p == (char_u *)&p_ft) {
+ xp->xp_context = EXPAND_FILETYPE;
} else {
xp->xp_context = EXPAND_FILES;
// for 'tags' need three backslashes for a space
@@ -6594,6 +6715,12 @@ static void paste_option_changed(void)
buf->b_p_sts_nopaste = buf->b_p_sts;
buf->b_p_ai_nopaste = buf->b_p_ai;
buf->b_p_et_nopaste = buf->b_p_et;
+ if (buf->b_p_vsts_nopaste) {
+ xfree(buf->b_p_vsts_nopaste);
+ }
+ buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
+ ? vim_strsave(buf->b_p_vsts)
+ : NULL;
}
// save global options
@@ -6608,6 +6735,12 @@ static void paste_option_changed(void)
p_sts_nopaste = p_sts;
p_tw_nopaste = p_tw;
p_wm_nopaste = p_wm;
+ if (p_vsts_nopaste) {
+ xfree(p_vsts_nopaste);
+ }
+ p_vsts_nopaste = p_vsts && p_vsts != empty_option
+ ? vim_strsave(p_vsts)
+ : NULL;
}
// Always set the option values, also when 'paste' is set when it is
@@ -6619,6 +6752,14 @@ static void paste_option_changed(void)
buf->b_p_sts = 0; // softtabstop is 0
buf->b_p_ai = 0; // no auto-indent
buf->b_p_et = 0; // no expandtab
+ if (buf->b_p_vsts) {
+ free_string_option(buf->b_p_vsts);
+ }
+ buf->b_p_vsts = empty_option;
+ if (buf->b_p_vsts_array) {
+ xfree(buf->b_p_vsts_array);
+ }
+ buf->b_p_vsts_array = 0;
}
// set global options
@@ -6635,6 +6776,10 @@ static void paste_option_changed(void)
p_wm = 0;
p_sts = 0;
p_ai = 0;
+ if (p_vsts) {
+ free_string_option(p_vsts);
+ }
+ p_vsts = empty_option;
} else if (old_p_paste) {
// Paste switched from on to off: Restore saved values.
@@ -6645,6 +6790,20 @@ static void paste_option_changed(void)
buf->b_p_sts = buf->b_p_sts_nopaste;
buf->b_p_ai = buf->b_p_ai_nopaste;
buf->b_p_et = buf->b_p_et_nopaste;
+ if (buf->b_p_vsts) {
+ free_string_option(buf->b_p_vsts);
+ }
+ buf->b_p_vsts = buf->b_p_vsts_nopaste
+ ? vim_strsave(buf->b_p_vsts_nopaste)
+ : empty_option;
+ if (buf->b_p_vsts_array) {
+ xfree(buf->b_p_vsts_array);
+ }
+ if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
+ tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
+ } else {
+ buf->b_p_vsts_array = 0;
+ }
}
// restore global options
@@ -6662,6 +6821,10 @@ static void paste_option_changed(void)
p_sts = p_sts_nopaste;
p_tw = p_tw_nopaste;
p_wm = p_wm_nopaste;
+ if (p_vsts) {
+ free_string_option(p_vsts);
+ }
+ p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
}
old_p_paste = p_paste;
@@ -6911,17 +7074,301 @@ 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.
+bool tabstop_set(char_u *var, long **array)
+{
+ long valcount = 1;
+ int t;
+ char_u *cp;
+
+ if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
+ *array = NULL;
+ return true;
+ }
+
+ for (cp = var; *cp != NUL; cp++) {
+ if (cp == var || cp[-1] == ',') {
+ char_u *end;
+
+ if (strtol((char *)cp, (char **)&end, 10) <= 0) {
+ if (cp != end) {
+ EMSG(_(e_positive));
+ } else {
+ EMSG(_(e_invarg));
+ }
+ return false;
+ }
+ }
+
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
+ valcount++;
+ continue;
+ }
+ EMSG(_(e_invarg));
+ return false;
+ }
+
+ *array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
+ (*array)[0] = valcount;
+
+ t = 1;
+ for (cp = var; *cp != NUL;) {
+ (*array)[t++] = atoi((char *)cp);
+ 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 = buf->b_p_sw ? buf->b_p_sw : buf->b_p_ts;
+ 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 effective shiftwidth value when 'softtabstop' is negative.
+/// 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;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 43b0107800..16749ba86b 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -165,8 +165,8 @@ enum {
SHM_WRI = 'w', ///< "[w]" instead of "written".
SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS.
SHM_WRITE = 'W', ///< Don't use "written" at all.
- SHM_TRUNC = 't', ///< Trunctate file messages.
- SHM_TRUNCALL = 'T', ///< Trunctate all messages.
+ SHM_TRUNC = 't', ///< Truncate file messages.
+ SHM_TRUNCALL = 'T', ///< Truncate all messages.
SHM_OVER = 'o', ///< Overwrite file messages.
SHM_OVERALL = 'O', ///< Overwrite more messages.
SHM_SEARCH = 's', ///< No search hit bottom messages.
@@ -824,6 +824,8 @@ enum {
, BV_UDF
, BV_UL
, BV_WM
+ , BV_VSTS
+ , BV_VTS
, BV_COUNT // must be the last one
};
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index fe108ef1cc..d12b31bcaf 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -900,6 +900,7 @@ return {
normal_fname_chars=true,
vi_def=true,
alloced=true,
+ expand=true,
varname='p_ft',
defaults={if_true={vi=""}}
},
@@ -2998,6 +2999,23 @@ return {
defaults={if_true={vi=4000}}
},
{
+ full_name='varsofttabstop', abbreviation='vsts',
+ short_desc=N_("list of numbers of spaces that <Tab> uses while editing"),
+ type='string', list='comma', scope={'buffer'},
+ vi_def=true,
+ varname='p_vsts',
+ defaults={if_true={vi=""}}
+ },
+ {
+ full_name='vartabstop', abbreviation='vts',
+ short_desc=N_("list of numbers of spaces that <Tab> in file uses"),
+ type='string', list='comma', scope={'buffer'},
+ vi_def=true,
+ varname='p_vts',
+ redraw={'current_buffer'},
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='verbose', abbreviation='vbs',
short_desc=N_("give informative messages"),
type='number', scope={'global'},
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 9d6518841a..eca245650a 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -159,16 +159,28 @@ bool os_char_avail(void)
return inbuf_poll(0, NULL) == kInputAvail;
}
-// Check for CTRL-C typed by reading all available characters.
+/// Poll for fast events. `got_int` will be set to `true` if CTRL-C was typed.
+///
+/// This invokes a full libuv loop iteration which can be quite costly.
+/// Prefer `line_breakcheck()` if called in a busy inner loop.
+///
+/// Caller must at least check `got_int` before calling this function again.
+/// checking for other low-level input state like `input_available()` might
+/// also be relevant (i e to throttle idle processing when user input is
+/// available)
void os_breakcheck(void)
{
+ if (got_int) {
+ return;
+ }
+
int save_us = updating_screen;
// We do not want screen_resize() to redraw here.
+ // TODO(bfredl): we are already special casing redraw events, is this
+ // hack still needed?
updating_screen++;
- if (!got_int) {
- loop_poll_events(&main_loop, 0);
- }
+ loop_poll_events(&main_loop, 0);
updating_screen = save_us;
}
diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c
index 52d2f84ace..94444e4d23 100644
--- a/src/nvim/os/pty_process_win.c
+++ b/src/nvim/os/pty_process_win.c
@@ -343,19 +343,17 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
utf8_cmd_line_len += argc;
char *utf8_cmd_line = xmalloc(utf8_cmd_line_len);
*utf8_cmd_line = NUL;
- while (1) {
- QUEUE *head = QUEUE_HEAD(&args_q);
- QUEUE_REMOVE(head);
- ArgNode *arg_node = QUEUE_DATA(head, ArgNode, node);
+ QUEUE *q;
+ QUEUE_FOREACH(q, &args_q, {
+ ArgNode *arg_node = QUEUE_DATA(q, ArgNode, node);
xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len);
xfree(arg_node->arg);
xfree(arg_node);
- if (QUEUE_EMPTY(&args_q)) {
- break;
- } else {
+ QUEUE_REMOVE(q);
+ if (!QUEUE_EMPTY(&args_q)) {
xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len);
}
- }
+ })
int result = utf8_to_utf16(utf8_cmd_line, -1, cmd_line);
xfree(utf8_cmd_line);
@@ -507,11 +505,11 @@ static int build_env_block(dict_T *denv, wchar_t **env_block)
*env_block = xmalloc(sizeof(**env_block) * env_block_len);
wchar_t *pos = *env_block;
- QUEUE_FOREACH(q, &env_q) {
+ QUEUE_FOREACH(q, &env_q, {
EnvNode *env_node = QUEUE_DATA(q, EnvNode, node);
memcpy(pos, env_node->str, env_node->len * sizeof(*pos));
pos += env_node->len;
- }
+ })
*pos = L'\0';
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index b5d890bf52..2974245857 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -123,7 +123,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file,
int shell_style = STYLE_ECHO;
int check_spaces;
static bool did_find_nul = false;
- bool ampersent = false;
+ bool ampersand = false;
// vimglob() function to define for Posix shell
static char *sh_vimglob_func =
"vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >";
@@ -245,7 +245,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file,
p--;
}
if (*p == '&') { // remove trailing '&'
- ampersent = true;
+ ampersand = true;
*p = ' ';
}
STRCAT(command, ">");
@@ -309,7 +309,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file,
shellopts |= kShellOptHideMess;
}
- if (ampersent) {
+ if (ampersand) {
STRCAT(command, "&"); // put the '&' after the redirection
}
@@ -331,7 +331,7 @@ int os_expand_wildcards(int num_pat, char_u **pat, int *num_file,
// When running in the background, give it some time to create the temp
// file, but don't wait for it to finish.
- if (ampersent) {
+ if (ampersand) {
os_delay(10L, true);
}
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index 5cf628935f..e7e0dc4013 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -196,6 +196,22 @@ char *os_ctime(char *result, size_t result_len)
return os_ctime_r(&rawtime, result, result_len);
}
+/// Portable version of POSIX strptime()
+///
+/// @param str[in] string to convert
+/// @param format[in] format to parse "str"
+/// @param tm[out] time representation of "str"
+/// @return Pointer to first unprocessed character or NULL
+char *os_strptime(const char *str, const char *format, struct tm *tm)
+ FUNC_ATTR_NONNULL_ALL
+{
+#ifdef HAVE_STRPTIME
+ return strptime(str, format, tm);
+#else
+ return NULL;
+#endif
+}
+
/// Obtains the current Unix timestamp.
///
/// @return Seconds since epoch.
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index aef7ffa397..32c9750628 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -139,8 +139,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed,
cursor_col = curwin->w_wcol;
}
- pum_anchor_grid = (int)curwin->w_grid.handle;
- if (!ui_has(kUIMultigrid)) {
+ pum_anchor_grid = (int)curwin->w_grid.target->handle;
+ pum_win_row += curwin->w_grid.row_offset;
+ cursor_col += curwin->w_grid.col_offset;
+ if (!ui_has(kUIMultigrid) && curwin->w_grid.target != &default_grid) {
pum_anchor_grid = (int)default_grid.handle;
pum_win_row += curwin->w_winrow;
cursor_col += curwin->w_wincol;
@@ -396,7 +398,7 @@ void pum_redraw(void)
char_u *p = NULL;
int totwidth, width, w;
int thumb_pos = 0;
- int thumb_heigth = 1;
+ int thumb_height = 1;
int round;
int n;
@@ -447,11 +449,11 @@ void pum_redraw(void)
}
if (pum_scrollbar) {
- thumb_heigth = pum_height * pum_height / pum_size;
- if (thumb_heigth == 0) {
- thumb_heigth = 1;
+ thumb_height = pum_height * pum_height / pum_size;
+ if (thumb_height == 0) {
+ thumb_height = 1;
}
- thumb_pos = (pum_first * (pum_height - thumb_heigth)
+ thumb_pos = (pum_first * (pum_height - thumb_height)
+ (pum_size - pum_height) / 2)
/ (pum_size - pum_height);
}
@@ -614,11 +616,11 @@ void pum_redraw(void)
if (pum_scrollbar > 0) {
if (pum_rl) {
grid_putchar(&pum_grid, ' ', row, col_off - pum_width,
- i >= thumb_pos && i < thumb_pos + thumb_heigth
+ i >= thumb_pos && i < thumb_pos + thumb_height
? attr_thumb : attr_scroll);
} else {
grid_putchar(&pum_grid, ' ', row, col_off + pum_width,
- i >= thumb_pos && i < thumb_pos + thumb_heigth
+ i >= thumb_pos && i < thumb_pos + thumb_height
? attr_thumb : attr_scroll);
}
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index dfd38a6eca..0785fa703d 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3617,6 +3617,15 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height)
if (win_split(height, flags) == FAIL) {
return FAIL; // not enough room for window
}
+
+ // User autocommands may have invalidated the previous window after calling
+ // win_split, so add a check to ensure that the win is still here
+ if (IS_LL_STACK(qi) && !win_valid(win)) {
+ // close the window that was supposed to be for the loclist
+ win_close(curwin, false);
+ return FAIL;
+ }
+
RESET_BINDING(curwin);
if (IS_LL_STACK(qi)) {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index a2589ac431..d7693c7a6f 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -5895,7 +5895,7 @@ static void regdump(char_u *pattern, bt_regprog_T *r)
fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s));
s += 4;
} else if (op == RE_LNUM || op == RE_COL || op == RE_VCOL) {
- /* one int plus comperator */
+ // one int plus comparator
fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s));
s += 5;
}
@@ -7139,6 +7139,7 @@ list_T *reg_submatch_list(int no)
tv_list_append_string(list, s, (const char *)rsm.sm_match->endp[no] - s);
}
+ tv_list_ref(list);
return list;
}
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 8b5ee59d40..b6bcee3fda 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -559,7 +559,9 @@ static char_u *nfa_get_match_text(nfa_state_T *start)
*/
static void realloc_post_list(void)
{
- size_t new_max = (post_end - post_start) + 1000;
+ // For weird patterns the number of states can be very high. Increasing by
+ // 50% seems a reasonable compromise between memory use and speed.
+ const size_t new_max = (post_end - post_start) * 3 / 2;
int *new_start = xrealloc(post_start, new_max * sizeof(int));
post_ptr = new_start + (post_ptr - post_start);
post_end = new_start + new_max;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index dd28c6fb35..5bf5a471c1 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -232,7 +232,7 @@ void screen_invalidate_highlights(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
redraw_later(wp, NOT_VALID);
- wp->w_grid.valid = false;
+ wp->w_grid_alloc.valid = false;
}
}
@@ -582,11 +582,18 @@ int update_screen(int type)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid.chars) {
- grid_invalidate(&wp->w_grid);
+ 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;
@@ -774,8 +781,6 @@ static void win_update(win_T *wp, Providers *providers)
type = wp->w_redr_type;
- win_grid_alloc(wp);
-
if (type >= NOT_VALID) {
wp->w_redr_status = true;
wp->w_lines_valid = 0;
@@ -2077,6 +2082,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
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 need_showbreak = false; // overlong line, skip first x chars
int line_attr = 0; // attribute for the whole line
int line_attr_lowprio = 0; // low-priority attribute for the line
@@ -2096,6 +2102,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext
+ bool area_active = false;
+
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2315,7 +2323,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL);
}
// do at least one character; happens when past end of line
- if (fromcol == tocol) {
+ if (fromcol == tocol && search_match_endcol) {
tocol = fromcol + 1;
}
area_highlighting = true;
@@ -2420,6 +2428,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
if (wp->w_p_list && !has_fold) {
if (wp->w_p_lcs_chars.space
|| wp->w_p_lcs_chars.trail
+ || wp->w_p_lcs_chars.lead
|| wp->w_p_lcs_chars.nbsp) {
extra_check = true;
}
@@ -2431,6 +2440,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
trailcol += (colnr_T) (ptr - line);
}
+ // find end of leading whitespace
+ if (wp->w_p_lcs_chars.lead) {
+ 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;
+ }
+ }
}
/*
@@ -2656,7 +2679,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// already be in use.
xfree(p_extra_free);
p_extra_free = xmalloc(MAX_MCO * fdc + 1);
- n_extra = fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
+ 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;
@@ -2751,7 +2774,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// :sign defined with "numhl" highlight.
char_attr = sign_get_attr(num_sign, SIGN_NUMHL);
} else if ((wp->w_p_cul || wp->w_p_rnu)
- && lnum == wp->w_cursor.lnum) {
+ && lnum == wp->w_cursor.lnum
+ && filler_todo == 0) {
// When 'cursorline' is set highlight the line number of
// the current line differently.
// TODO(vim): Can we use CursorLine instead of CursorLineNr
@@ -2850,6 +2874,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
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, vcol, off, true, &decor_state);
+ }
+
if (saved_n_extra) {
/* Continue item from end of wrapped line. */
n_extra = saved_n_extra;
@@ -2934,10 +2964,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& 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) {
@@ -3120,6 +3154,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
mb_utf8 = false;
}
} else {
+ assert(p_extra != NULL);
c = *p_extra;
mb_c = c;
// If the UTF-8 character is more than one byte:
@@ -3397,9 +3432,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
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,
- &decor_state);
+ selected, &decor_state);
if (extmark_attr != 0) {
if (!attr_pri) {
char_attr = hl_combine_attr(char_attr, extmark_attr);
@@ -3409,10 +3450,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
}
- if (wp->w_buffer->terminal) {
- char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
- }
-
// Found last space before word: check for line break.
if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
&& !vim_isbreak((int)(*ptr))) {
@@ -3421,8 +3458,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// TODO: is passing p for start of the line OK?
n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
if (c == TAB && n_extra + col > grid->Columns) {
- n_extra = (int)wp->w_buffer->b_p_ts
- - vcol % (int)wp->w_buffer->b_p_ts - 1;
+ n_extra = tabstop_padding(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;
@@ -3442,6 +3479,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
|| (mb_utf8 && (mb_c == 160 || mb_c == 0x202f)))
&& curwin->w_p_lcs_chars.nbsp)
|| (c == ' ' && curwin->w_p_lcs_chars.space
+ && ptr - line >= leadcol
&& ptr - line <= trailcol))) {
c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
n_attr = 1;
@@ -3457,8 +3495,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
}
- if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') {
- c = wp->w_p_lcs_chars.trail;
+ if ((trailcol != MAXCOL && ptr > line + trailcol && c == ' ')
+ || (leadcol != 0 && ptr < line + leadcol && c == ' ')) {
+ c = (ptr > line + trailcol) ? wp->w_p_lcs_chars.trail
+ : wp->w_p_lcs_chars.lead;
n_attr = 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3488,8 +3528,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
vcol_adjusted = vcol - MB_CHARLEN(p_sbr);
}
// tab amount depends on current column
- tab_len = (int)wp->w_buffer->b_p_ts
- - vcol_adjusted % (int)wp->w_buffer->b_p_ts - 1;
+ tab_len = tabstop_padding(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;
@@ -3522,6 +3563,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
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, need to change the char
@@ -3902,9 +3947,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
.hl_id = hl_err }));
do_virttext = true;
} else if (has_decor) {
- VirtText *vp = decor_redraw_virt_text(wp->w_buffer, &decor_state);
- if (vp) {
- virt_text = *vp;
+ virt_text = decor_redraw_virt_text(wp->w_buffer, &decor_state);
+ if (kv_size(virt_text)) {
do_virttext = true;
}
}
@@ -4334,10 +4378,10 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
DecorState *state = &decor_state;
for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange *item = &kv_A(state->active, i);
- if (item->start_row == state->row && item->virt_text
+ if (item->start_row == state->row && kv_size(item->virt_text)
&& item->virt_text_pos == kVTOverlay
&& item->virt_col >= 0) {
- VirtText vt = *item->virt_text;
+ VirtText vt = item->virt_text;
LineState s = LINE_STATE("");
int virt_attr = 0;
int col = item->virt_col;
@@ -4355,10 +4399,22 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
virt_pos++;
continue;
}
- int cells = line_putchar(&s, &linebuf_char[col], 2, false);
- linebuf_attr[col++] = virt_attr;
+ int attr;
+ bool through = false;
+ if (item->hl_mode == kHlModeCombine) {
+ attr = hl_combine_attr(linebuf_attr[col], virt_attr);
+ } else if (item->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(&s, through ? dummy : &linebuf_char[col],
+ max_col-col, false);
+ linebuf_attr[col++] = attr;
if (cells > 1) {
- linebuf_attr[col++] = virt_attr;
+ linebuf_attr[col++] = attr;
}
}
*end_col = MAX(*end_col, col);
@@ -4378,14 +4434,10 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
/// screen positions.
void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
{
- if (!(*grid)->chars && *grid != &default_grid) {
- *row_off += (*grid)->row_offset;
- *col_off += (*grid)->col_offset;
- if (*grid == &msg_grid_adj && msg_grid.chars) {
- *grid = &msg_grid;
- } else {
- *grid = &default_grid;
- }
+ if ((*grid)->target) {
+ *row_off += (*grid)->row_offset;
+ *col_off += (*grid)->col_offset;
+ *grid = (*grid)->target;
}
}
@@ -5389,6 +5441,46 @@ 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 endrow = grid->Rows-1, endcol = grid->Columns-1;
+
+ grid_puts_line_start(grid, 0);
+ grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ for (int i = 1; i < endcol; i++) {
+ grid_put_schar(grid, 0, i, chars[1], attrs[1]);
+ }
+ grid_put_schar(grid, 0, endcol, chars[2], attrs[2]);
+ grid_puts_line_flush(false);
+
+ for (int i = 1; i < endrow; i++) {
+ grid_puts_line_start(grid, i);
+ grid_put_schar(grid, i, 0, chars[7], attrs[7]);
+ grid_puts_line_flush(false);
+ grid_puts_line_start(grid, i);
+ grid_put_schar(grid, i, endcol, chars[3], attrs[3]);
+ grid_puts_line_flush(false);
+ }
+
+ grid_puts_line_start(grid, endrow);
+ grid_put_schar(grid, endrow, 0, chars[6], attrs[6]);
+ for (int i = 1; i < endcol; i++) {
+ grid_put_schar(grid, endrow, i, chars[5], attrs[5]);
+ }
+ grid_put_schar(grid, endrow, endcol, chars[4], attrs[4]);
+ grid_puts_line_flush(false);
+}
+
// Low-level functions to manipulate invidual character cells on the
// screen grid.
@@ -5526,6 +5618,20 @@ void grid_puts_line_start(ScreenGrid *grid, int row)
put_dirty_grid = grid;
}
+void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
+{
+ assert(put_dirty_row == row);
+ unsigned int off = grid->line_offset[row] + col;
+ if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) {
+ schar_copy(grid->chars[off], schar);
+ grid->attrs[off] = attr;
+
+ put_dirty_first = MIN(put_dirty_first, col);
+ // TODO(bfredl): Y U NO DOUBLEWIDTH?
+ put_dirty_last = MAX(put_dirty_last, col+1);
+ }
+}
+
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL.
void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
@@ -6117,12 +6223,15 @@ void check_for_delay(int check_msg_scroll)
void win_grid_alloc(win_T *wp)
{
ScreenGrid *grid = &wp->w_grid;
+ ScreenGrid *grid_allocated = &wp->w_grid_alloc;
int rows = wp->w_height_inner;
int cols = wp->w_width_inner;
+ int total_rows = wp->w_height_outer;
+ int total_cols = wp->w_width_outer;
bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating;
- bool has_allocation = (grid->chars != NULL);
+ bool has_allocation = (grid_allocated->chars != NULL);
if (grid->Rows != rows) {
wp->w_lines_valid = 0;
@@ -6131,35 +6240,47 @@ void win_grid_alloc(win_T *wp)
}
int was_resized = false;
- if ((has_allocation != want_allocation)
- || grid->Rows != rows
- || grid->Columns != cols) {
- if (want_allocation) {
- grid_alloc(grid, rows, cols, wp->w_grid.valid, false);
- grid->valid = true;
- } else {
- // Single grid mode, all rendering will be redirected to default_grid.
- // Only keep track of the size and offset of the window.
- grid_free(grid);
- grid->Rows = rows;
- grid->Columns = cols;
- grid->valid = false;
+ if (want_allocation && (!has_allocation
+ || grid_allocated->Rows != total_rows
+ || grid_allocated->Columns != total_cols)) {
+ grid_alloc(grid_allocated, total_rows, total_cols,
+ wp->w_grid_alloc.valid, false);
+ grid_allocated->valid = true;
+ if (wp->w_border_adj) {
+ wp->w_redr_border = true;
}
was_resized = true;
- } else if (want_allocation && has_allocation && !wp->w_grid.valid) {
- grid_invalidate(grid);
- grid->valid = true;
+ } else if (!want_allocation && has_allocation) {
+ // Single grid mode, all rendering will be redirected to default_grid.
+ // Only keep track of the size and offset of the window.
+ grid_free(grid_allocated);
+ grid_allocated->valid = false;
+ was_resized = true;
+ } else if (want_allocation && has_allocation && !wp->w_grid_alloc.valid) {
+ grid_invalidate(grid_allocated);
+ grid_allocated->valid = true;
}
- grid->row_offset = wp->w_winrow;
- grid->col_offset = wp->w_wincol;
+ grid->Rows = rows;
+ grid->Columns = cols;
+
+ if (want_allocation) {
+ grid->target = grid_allocated;
+ grid->row_offset = wp->w_border_adj;
+ grid->col_offset = wp->w_border_adj;
+ } else {
+ grid->target = &default_grid;
+ grid->row_offset = wp->w_winrow;
+ grid->col_offset = wp->w_wincol;
+ }
// send grid resize event if:
// - a grid was just resized
// - screen_resize was called and all grid sizes must be sent
// - the UI wants multigrid event (necessary)
if ((send_grid_resize || was_resized) && want_allocation) {
- ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows);
+ ui_call_grid_resize(grid_allocated->handle,
+ grid_allocated->Columns, grid_allocated->Rows);
}
}
@@ -6248,6 +6369,9 @@ retry:
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;
@@ -7152,11 +7276,13 @@ static void win_redr_ruler(win_T *wp, int always)
if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
return;
- /* Don't draw the ruler while doing insert-completion, it might overwrite
- * the (long) mode message. */
- if (wp == lastwin && lastwin->w_status_height == 0)
- if (edit_submode != NULL)
+ // Don't draw the ruler while doing insert-completion, it might overwrite
+ // the (long) mode message.
+ if (wp == lastwin && lastwin->w_status_height == 0) {
+ if (edit_submode != NULL) {
return;
+ }
+ }
if (*p_ruf) {
int save_called_emsg = called_emsg;
@@ -7484,8 +7610,9 @@ void win_new_shellsize(void)
static long old_Columns = 0;
if (old_Rows != Rows) {
- // if 'window' uses the whole screen, keep it using that */
- if (p_window == old_Rows - 1 || old_Rows == 0) {
+ // If 'window' uses the whole screen, keep it using that.
+ // Don't change it when set with "-w size" on the command line.
+ if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) {
p_window = Rows - 1;
}
old_Rows = Rows;
@@ -7500,7 +7627,7 @@ void win_new_shellsize(void)
win_T *get_win_by_grid_handle(handle_T handle)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_grid.handle == handle) {
+ if (wp->w_grid_alloc.handle == handle) {
return wp;
}
}
diff --git a/src/nvim/state.c b/src/nvim/state.c
index b195c1d96b..a3c74789d1 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -75,6 +75,34 @@ getkey:
}
}
+/// process events on main_loop, but interrupt if input is available
+///
+/// This should be used to handle K_EVENT in states accepting input
+/// otherwise bursts of events can block break checking indefinitely.
+void state_handle_k_event(void)
+{
+ while (true) {
+ Event event = multiqueue_get(main_loop.events);
+ if (event.handler) {
+ event.handler(event.argv);
+ }
+
+ if (multiqueue_empty(main_loop.events)) {
+ // don't breakcheck before return, caller should return to main-loop
+ // and handle input already.
+ return;
+ }
+
+ // TODO(bfredl): as an further micro-optimization, we could check whether
+ // event.handler already checked input.
+ os_breakcheck();
+ if (input_available() || got_int) {
+ return;
+ }
+ }
+}
+
+
/// Return true if in the current mode we need to use virtual.
bool virtual_active(void)
{
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 547d953be9..f1eb7879b0 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -59,7 +59,9 @@ struct hl_group {
bool sg_cleared; ///< "hi clear" was used
int sg_attr; ///< Screen attr @see ATTR_ENTRY
int sg_link; ///< link to this highlight group ID
+ int sg_deflink; ///< default link; restored in highlight_clear()
int sg_set; ///< combination of flags in \ref SG_SET
+ sctx_T sg_deflink_sctx; ///< script where the default link was set
sctx_T sg_script_ctx; ///< script in which the group was last set
// for terminal UIs
int sg_cterm; ///< "cterm=" highlighting attr
@@ -6044,6 +6046,7 @@ static const char *highlight_init_both[] = {
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
"default link NormalFloat Pmenu",
+ "default link FloatBorder VertSplit",
"RedrawDebugNormal cterm=reverse gui=reverse",
"RedrawDebugClear ctermbg=Yellow guibg=Yellow",
"RedrawDebugComposed ctermbg=Green guibg=Green",
@@ -6601,6 +6604,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
const char *to_end;
int from_id;
int to_id;
+ struct hl_group *hlgroup = NULL;
from_end = (const char *)skiptowhite((const char_u *)from_start);
to_start = (const char *)skipwhite((const char_u *)from_end);
@@ -6627,7 +6631,16 @@ void do_highlight(const char *line, const bool forceit, const bool init)
(int)(to_end - to_start));
}
- if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) {
+ if (from_id > 0) {
+ hlgroup = &HL_TABLE()[from_id - 1];
+ if (dodefault && (forceit || hlgroup->sg_deflink == 0)) {
+ hlgroup->sg_deflink = to_id;
+ hlgroup->sg_deflink_sctx = current_sctx;
+ hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ }
+ }
+
+ if (from_id > 0 && (!init || hlgroup->sg_set == 0)) {
// Don't allow a link when there already is some highlighting
// for the group, unless '!' is used
if (to_id > 0 && !forceit && !init
@@ -6635,17 +6648,16 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (sourcing_name == NULL && !dodefault) {
EMSG(_("E414: group has settings, highlight link ignored"));
}
- } else if (HL_TABLE()[from_id - 1].sg_link != to_id
- || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
- != current_sctx.sc_sid
- || HL_TABLE()[from_id - 1].sg_cleared) {
+ } else if (hlgroup->sg_link != to_id
+ || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
+ || hlgroup->sg_cleared) {
if (!init) {
- HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
+ hlgroup->sg_set |= SG_LINK;
}
- HL_TABLE()[from_id - 1].sg_link = to_id;
- HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
- HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
- HL_TABLE()[from_id - 1].sg_cleared = false;
+ hlgroup->sg_link = to_id;
+ hlgroup->sg_script_ctx = current_sctx;
+ hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum;
+ hlgroup->sg_cleared = false;
redraw_all_later(SOME_VALID);
// Only call highlight changed() once after multiple changes
@@ -7076,13 +7088,14 @@ void restore_cterm_colors(void)
*/
static int hl_has_settings(int idx, int check_link)
{
- return HL_TABLE()[idx].sg_attr != 0
- || HL_TABLE()[idx].sg_cterm_fg != 0
- || HL_TABLE()[idx].sg_cterm_bg != 0
- || HL_TABLE()[idx].sg_rgb_fg_name != NULL
- || HL_TABLE()[idx].sg_rgb_bg_name != NULL
- || HL_TABLE()[idx].sg_rgb_sp_name != NULL
- || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK));
+ return HL_TABLE()[idx].sg_cleared == 0
+ && (HL_TABLE()[idx].sg_attr != 0
+ || HL_TABLE()[idx].sg_cterm_fg != 0
+ || HL_TABLE()[idx].sg_cterm_bg != 0
+ || HL_TABLE()[idx].sg_rgb_fg_name != NULL
+ || HL_TABLE()[idx].sg_rgb_bg_name != NULL
+ || HL_TABLE()[idx].sg_rgb_sp_name != NULL
+ || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
}
/*
@@ -7105,12 +7118,11 @@ static void highlight_clear(int idx)
XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name);
XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name);
HL_TABLE()[idx].sg_blend = -1;
- // Clear the script ID only when there is no link, since that is not
- // cleared.
- if (HL_TABLE()[idx].sg_link == 0) {
- HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
- HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
- }
+ // Restore default link and context if they exist. Otherwise clears.
+ HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
+ // Since we set the default link, set the location to where the default
+ // link was set.
+ HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
}
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 4ea298fba9..6b8f393572 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -908,7 +908,7 @@ add_llist_tags(
if (len > 128) {
len = 128;
}
- xstrlcpy((char *)tag_name, (const char *)tagp.tagname, len);
+ xstrlcpy((char *)tag_name, (const char *)tagp.tagname, len + 1);
tag_name[len] = NUL;
// Save the tag file name
@@ -975,7 +975,8 @@ add_llist_tags(
if (cmd_len > (CMDBUFFSIZE - 5)) {
cmd_len = CMDBUFFSIZE - 5;
}
- xstrlcat((char *)cmd, (char *)cmd_start, cmd_len);
+ snprintf((char *)cmd + len, CMDBUFFSIZE + 1 - len,
+ "%.*s", cmd_len, cmd_start);
len += cmd_len;
if (cmd[len - 1] == '$') {
@@ -1141,7 +1142,7 @@ static int find_tagfunc_tags(
int result = FAIL;
typval_T args[4];
typval_T rettv;
- char_u flagString[3];
+ char_u flagString[4];
dict_T *d;
taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx];
@@ -1170,9 +1171,10 @@ static int find_tagfunc_tags(
args[3].v_type = VAR_UNKNOWN;
vim_snprintf((char *)flagString, sizeof(flagString),
- "%s%s",
+ "%s%s%s",
g_tag_at_cursor ? "c": "",
- flags & TAG_INS_COMP ? "i": "");
+ flags & TAG_INS_COMP ? "i": "",
+ flags & TAG_REGEXP ? "r": "");
save_pos = curwin->w_cursor;
result = call_vim_function(curbuf->b_p_tfu, 3, args, &rettv);
@@ -3002,7 +3004,8 @@ static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname,
*/
static int find_extra(char_u **pp)
{
- char_u *str = *pp;
+ char_u *str = *pp;
+ char_u first_char = **pp;
// Repeat for addresses separated with ';'
for (;; ) {
@@ -3010,7 +3013,7 @@ static int find_extra(char_u **pp)
str = skipdigits(str);
} else if (*str == '/' || *str == '?') {
str = skip_regexp(str + 1, *str, false, NULL);
- if (*str != **pp) {
+ if (*str != first_char) {
str = NULL;
} else {
str++;
@@ -3028,6 +3031,7 @@ static int find_extra(char_u **pp)
break;
}
str++; // skip ';'
+ first_char = *str;
}
if (str != NULL && STRNCMP(str, ";\"", 2) == 0) {
@@ -3404,6 +3408,7 @@ int set_tagstack(win_T *wp, const dict_T *d, int action)
if ((di = tv_dict_find(d, "items", -1)) != NULL) {
if (di->di_tv.v_type != VAR_LIST) {
+ EMSG(_(e_listreq));
return FAIL;
}
l = di->di_tv.vval.v_list;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 034de56f9c..afad20f557 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -169,19 +169,20 @@ void terminal_teardown(void)
multiqueue_free(refresh_timer.events);
time_watcher_close(&refresh_timer, NULL);
pmap_free(ptr_t)(invalidated_terminals);
+ invalidated_terminals = NULL;
}
// public API {{{
-Terminal *terminal_open(TerminalOptions opts)
+Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
{
// Create a new terminal instance and configure it
Terminal *rv = xcalloc(1, sizeof(Terminal));
rv->opts = opts;
rv->cursor.visible = true;
// Associate the terminal instance with the new buffer
- rv->buf_handle = curbuf->handle;
- curbuf->terminal = rv;
+ rv->buf_handle = buf->handle;
+ buf->terminal = rv;
// Create VTerm
rv->vt = vterm_new(opts.height, opts.width);
vterm_set_utf8(rv->vt, 1);
@@ -198,28 +199,36 @@ Terminal *terminal_open(TerminalOptions opts)
// have as many lines as screen rows when refresh_scrollback is called
rv->invalid_start = 0;
rv->invalid_end = opts.height;
- refresh_screen(rv, curbuf);
+
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+
+ refresh_screen(rv, buf);
set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666
// Default settings for terminal buffers
- curbuf->b_p_ma = false; // 'nomodifiable'
- curbuf->b_p_ul = -1; // 'undolevels'
- curbuf->b_p_scbk = // 'scrollback' (initialize local from global)
+ buf->b_p_ma = false; // 'nomodifiable'
+ buf->b_p_ul = -1; // 'undolevels'
+ buf->b_p_scbk = // 'scrollback' (initialize local from global)
(p_scbk < 0) ? 10000 : MAX(1, p_scbk);
- curbuf->b_p_tw = 0; // 'textwidth'
+ buf->b_p_tw = 0; // 'textwidth'
set_option_value("wrap", false, NULL, OPT_LOCAL);
set_option_value("list", false, NULL, OPT_LOCAL);
- buf_set_term_title(curbuf, (char *)curbuf->b_ffname);
+ if (buf->b_ffname != NULL) {
+ buf_set_term_title(buf, (char *)buf->b_ffname);
+ }
RESET_BINDING(curwin);
// Reset cursor in current window.
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
- apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf);
// Local 'scrollback' _after_ autocmds.
- curbuf->b_p_scbk = (curbuf->b_p_scbk < 1) ? SB_MAX : curbuf->b_p_scbk;
+ buf->b_p_scbk = (buf->b_p_scbk < 1) ? SB_MAX : buf->b_p_scbk;
+
+ aucmd_restbuf(&aco);
// Configure the scrollback buffer.
- rv->sb_size = (size_t)curbuf->b_p_scbk;
+ rv->sb_size = (size_t)buf->b_p_scbk;
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
// Configure the color palette. Try to get the color from:
@@ -457,7 +466,7 @@ static int terminal_execute(VimState *state, int key)
case K_EVENT:
// We cannot let an event free the terminal yet. It is still needed.
s->term->refcount++;
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
s->term->refcount--;
if (s->term->buf_handle == 0) {
s->close = true;
@@ -511,7 +520,9 @@ void terminal_destroy(Terminal *term)
}
if (!term->refcount) {
- if (pmap_has(ptr_t)(invalidated_terminals, term)) {
+ // might be destroyed after terminal_teardown is invoked
+ if (invalidated_terminals
+ && pmap_has(ptr_t)(invalidated_terminals, term)) {
// flush any pending changes to the buffer
block_autocmds();
refresh_terminal(term);
@@ -1327,6 +1338,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
// focused) of a invalidated terminal
static void refresh_screen(Terminal *term, buf_T *buf)
{
+ assert(buf == curbuf); // TODO(bfredl): remove this condition
int changed = 0;
int added = 0;
int height;
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index e52fd888bd..4641408069 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -31,20 +31,11 @@ endif
SCRIPTS ?= $(SCRIPTS_DEFAULT)
# Tests using runtest.vim.
-NEW_TESTS_ALOT := test_alot_utf8 test_alot
+NEW_TESTS_ALOT := test_alot_utf8 test_alot test_alot_latin
NEW_TESTS_IN_ALOT := $(shell sed -n '/^source/ s/^source //; s/\.vim$$//p' $(addsuffix .vim,$(NEW_TESTS_ALOT)))
-NEW_TESTS_IN_ALOT_LATIN := $(shell sed -n '/^source/ s/^source //; s/\.vim$$//p' test_alot_latin.vim)
# Ignored tests.
-# test_alot_latin: Nvim does not allow setting encoding.
-# test_autochdir: ported to Lua, but kept for easier merging.
-# test_eval_func: used as include in old-style test (test_eval.in).
-# test_listlbr: Nvim does not allow setting encoding.
# test_largefile: uses too much resources to run on CI.
NEW_TESTS_IGNORE := \
- test_alot_latin $(NEW_TESTS_IN_ALOT_LATIN) \
- test_autochdir \
- test_eval_func \
- test_listlbr \
test_largefile \
NEW_TESTS := $(sort $(basename $(notdir $(wildcard test_*.vim))))
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 24d3959f83..7b06e53dd5 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -108,3 +108,30 @@ func CheckNotMSWindows()
throw 'Skipped: does not work on MS-Windows'
endif
endfunc
+
+" Command to check for satisfying any of the conditions.
+" e.g. CheckAnyOf Feature:bsd Feature:sun Linux
+command -nargs=+ CheckAnyOf call CheckAnyOf(<f-args>)
+func CheckAnyOf(...)
+ let excp = []
+ for arg in a:000
+ try
+ exe 'Check' .. substitute(arg, ':', ' ', '')
+ return
+ catch /^Skipped:/
+ let excp += [substitute(v:exception, '^Skipped:\s*', '', '')]
+ endtry
+ endfor
+ throw 'Skipped: ' .. join(excp, '; ')
+endfunc
+
+" Command to check for satisfying all of the conditions.
+" e.g. CheckAllOf Unix Gui Option:ballooneval
+command -nargs=+ CheckAllOf call CheckAllOf(<f-args>)
+func CheckAllOf(...)
+ for arg in a:000
+ exe 'Check' .. substitute(arg, ':', ' ', '')
+ endfor
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 275edece1e..2d94b637e0 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -373,9 +373,6 @@ let s:flaky_tests = [
\ 'Test_with_partial_callback()',
\ ]
-" Pattern indicating a common flaky test failure.
-let s:flaky_errors_re = 'StopVimInTerminal\|VerifyScreenDump'
-
" Locate Test_ functions and execute them.
redir @q
silent function /^Test_
@@ -410,6 +407,9 @@ for s:test in sort(s:tests)
let total_errors = []
let run_nr = 1
+ " A test can set g:test_is_flaky to retry running the test.
+ let g:test_is_flaky = 0
+
call RunTheTest(s:test)
" Repeat a flaky test. Give up when:
@@ -417,7 +417,7 @@ for s:test in sort(s:tests)
" - it fails five times (with a different message)
if len(v:errors) > 0
\ && (index(s:flaky_tests, s:test) >= 0
- \ || v:errors[0] =~ s:flaky_errors_re)
+ \ || g:test_is_flaky)
while 1
call add(s:messages, 'Found errors in ' . s:test . ':')
call extend(s:messages, v:errors)
diff --git a/src/nvim/testdir/script_util.vim b/src/nvim/testdir/script_util.vim
new file mode 100644
index 0000000000..9913b1dfaf
--- /dev/null
+++ b/src/nvim/testdir/script_util.vim
@@ -0,0 +1,69 @@
+" Functions shared by the tests for Vim Script
+
+" Commands to track the execution path of a script
+com! XpathINIT let g:Xpath = ''
+com! -nargs=1 -bar Xpath let g:Xpath ..= <args>
+com! XloopINIT let g:Xloop = 1
+com! -nargs=1 -bar Xloop let g:Xpath ..= <args> .. g:Xloop
+com! XloopNEXT let g:Xloop += 1
+
+" MakeScript() - Make a script file from a function. {{{2
+"
+" Create a script that consists of the body of the function a:funcname.
+" Replace any ":return" by a ":finish", any argument variable by a global
+" variable, and every ":call" by a ":source" for the next following argument
+" in the variable argument list. This function is useful if similar tests are
+" to be made for a ":return" from a function call or a ":finish" in a script
+" file.
+func MakeScript(funcname, ...)
+ let script = tempname()
+ execute "redir! >" . script
+ execute "function" a:funcname
+ redir END
+ execute "edit" script
+ " Delete the "function" and the "endfunction" lines. Do not include the
+ " word "function" in the pattern since it might be translated if LANG is
+ " set. When MakeScript() is being debugged, this deletes also the debugging
+ " output of its line 3 and 4.
+ exec '1,/.*' . a:funcname . '(.*)/d'
+ /^\d*\s*endfunction\>/,$d
+ %s/^\d*//e
+ %s/return/finish/e
+ %s/\<a:\(\h\w*\)/g:\1/ge
+ normal gg0
+ let cnt = 0
+ while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0
+ let cnt = cnt + 1
+ s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/
+ endwhile
+ g/^\s*$/d
+ write
+ bwipeout
+ return script
+endfunc
+
+" ExecAsScript - Source a temporary script made from a function. {{{2
+"
+" Make a temporary script file from the function a:funcname, ":source" it, and
+" delete it afterwards. However, if an exception is thrown the file may remain,
+" the caller should call DeleteTheScript() afterwards.
+let s:script_name = ''
+function! ExecAsScript(funcname)
+ " Make a script from the function passed as argument.
+ let s:script_name = MakeScript(a:funcname)
+
+ " Source and delete the script.
+ exec "source" s:script_name
+ call delete(s:script_name)
+ let s:script_name = ''
+endfunction
+
+function! DeleteTheScript()
+ if s:script_name
+ call delete(s:script_name)
+ let s:script_name = ''
+ endif
+endfunc
+
+com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
+
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 4f056abdc0..71af3eead7 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -33,11 +33,10 @@ source test_move.vim
source test_partial.vim
source test_popup.vim
source test_put.vim
-source test_recover.vim
+source test_rename.vim
source test_scroll_opt.vim
source test_sort.vim
source test_sha256.vim
-source test_statusline.vim
source test_suspend.vim
source test_syn_attr.vim
source test_tabline.vim
diff --git a/src/nvim/testdir/test_alot_latin.vim b/src/nvim/testdir/test_alot_latin.vim
index ebb3bde4ce..23a404cac1 100644
--- a/src/nvim/testdir/test_alot_latin.vim
+++ b/src/nvim/testdir/test_alot_latin.vim
@@ -4,7 +4,4 @@
" These tests use latin1 'encoding'. Setting 'encoding' is in the individual
" files, so that they can be run by themselves.
-" Nvim does not allow setting 'encoding', so skip this test group.
-finish
-
source test_regexp_latin.vim
diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim
index b4f7478807..1d114221dc 100644
--- a/src/nvim/testdir/test_assert.vim
+++ b/src/nvim/testdir/test_assert.vim
@@ -52,6 +52,37 @@ func Test_assert_fails_in_try_block()
endtry
endfunc
+func Test_assert_inrange()
+ call assert_equal(0, assert_inrange(7, 7, 7))
+ call assert_equal(0, assert_inrange(5, 7, 5))
+ call assert_equal(0, assert_inrange(5, 7, 6))
+ call assert_equal(0, assert_inrange(5, 7, 7))
+ call assert_equal(1, assert_inrange(5, 7, 4))
+ call assert_match("Expected range 5 - 7, but got 4", v:errors[0])
+ call remove(v:errors, 0)
+ call assert_equal(1, assert_inrange(5, 7, 8))
+ call assert_match("Expected range 5 - 7, but got 8", v:errors[0])
+ call remove(v:errors, 0)
+
+ call assert_fails('call assert_inrange(1, 1)', 'E119:')
+
+ if has('float')
+ call assert_equal(0, assert_inrange(7.0, 7, 7))
+ call assert_equal(0, assert_inrange(7, 7.0, 7))
+ call assert_equal(0, assert_inrange(7, 7, 7.0))
+ call assert_equal(0, assert_inrange(5, 7, 5.0))
+ call assert_equal(0, assert_inrange(5, 7, 6.0))
+ call assert_equal(0, assert_inrange(5, 7, 7.0))
+
+ call assert_equal(1, assert_inrange(5, 7, 4.0))
+ call assert_match("Expected range 5.0 - 7.0, but got 4.0", v:errors[0])
+ call remove(v:errors, 0)
+ call assert_equal(1, assert_inrange(5, 7, 8.0))
+ call assert_match("Expected range 5.0 - 7.0, but got 8.0", v:errors[0])
+ call remove(v:errors, 0)
+ endif
+endfunc
+
" Must be last.
func Test_zz_quit_detected()
" Verify that if a test function ends Vim the test script detects this.
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 67c537b407..d071f4b325 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -1,10 +1,10 @@
" Test 'autochdir' behavior
-if !exists("+autochdir")
- throw 'Skipped: autochdir feature missing'
-endif
+source check.vim
+CheckOption autochdir
func Test_set_filename()
+ CheckFunction test_autochdir
let cwd = getcwd()
call test_autochdir()
set acd
@@ -17,3 +17,5 @@ func Test_set_filename()
exe 'cd ' . cwd
call delete('samples/Xtest')
endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1f3a45a9ab..5e99edf233 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -76,7 +76,7 @@ if has('timers')
endfunc
func Test_OptionSet_modeline()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
call test_override('starting', 1)
au! OptionSet
augroup set_tabstop
@@ -507,7 +507,7 @@ func s:AutoCommandOptionSet(match)
endfunc
func Test_OptionSet()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !has("eval") || !exists("+autochdir")
return
endif
@@ -648,7 +648,7 @@ func Test_OptionSet()
endfunc
func Test_OptionSet_diffmode()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
call test_override('starting', 1)
" 18: Changing an option when entering diff mode
new
@@ -682,7 +682,7 @@ func Test_OptionSet_diffmode()
endfunc
func Test_OptionSet_diffmode_close()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
call test_override('starting', 1)
" 19: Try to close the current window when entering diff mode
" should not segfault
@@ -1285,9 +1285,9 @@ func Test_autocommand_all_events()
endfunc
" Test TextChangedI and TextChangedP
+" See test/functional/viml/completion_spec.lua'
func Test_ChangedP()
- " Nvim does not support test_override().
- throw 'skipped: see test/functional/viml/completion_spec.lua'
+ CheckFunction test_override
new
call setline(1, ['foo', 'bar', 'foobar'])
call test_override("char_avail", 1)
@@ -1350,7 +1350,7 @@ func SetLineOne()
endfunc
func Test_TextChangedI_with_setline()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
new
call test_override('char_avail', 1)
autocmd TextChangedI <buffer> call SetLineOne()
@@ -1366,9 +1366,11 @@ func Test_TextChangedI_with_setline()
endfunc
func Test_Changed_FirstTime()
- if !has('terminal') || has('gui_running')
- return
- endif
+ CheckFeature terminal
+ CheckNotGui
+ " Starting a terminal to run Vim is always considered flaky.
+ let g:test_is_flaky = 1
+
" Prepare file for TextChanged event.
call writefile([''], 'Xchanged.txt')
let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
@@ -1922,20 +1924,28 @@ func Test_autocmd_window()
%bw!
edit one.txt
tabnew two.txt
+ vnew three.txt
+ tabnew four.txt
+ tabprevious
let g:blist = []
- augroup aucmd_win_test
+ augroup aucmd_win_test1
au!
au BufEnter * call add(g:blist, [expand('<afile>'),
\ win_gettype(bufwinnr(expand('<afile>')))])
augroup END
doautoall BufEnter
- call assert_equal([['one.txt', 'autocmd'], ['two.txt', '']], g:blist)
+ call assert_equal([
+ \ ['one.txt', 'autocmd'],
+ \ ['two.txt', ''],
+ \ ['four.txt', 'autocmd'],
+ \ ['three.txt', ''],
+ \ ], g:blist)
- augroup aucmd_win_test
+ augroup aucmd_win_test1
au!
augroup END
- augroup! aucmd_win_test
+ augroup! aucmd_win_test1
%bw!
endfunc
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index d53acb77d7..ff5029b889 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -12,56 +12,88 @@ source view_util.vim
let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
-function s:screen_lines(lnum, width) abort
+func s:screen_lines(lnum, width) abort
return ScreenLines([a:lnum, a:lnum + 2], a:width)
-endfunction
+endfunc
-function! s:compare_lines(expect, actual)
+func! s:compare_lines(expect, actual)
call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
-endfunction
+endfunc
-function s:test_windows(...)
+func s:test_windows(...)
call NewWindow(10, 20)
setl ts=4 sw=4 sts=4 breakindent
put =s:input
exe get(a:000, 0, '')
-endfunction
+endfunc
-function s:close_windows(...)
+func s:close_windows(...)
call CloseWindow()
exe get(a:000, 0, '')
-endfunction
+endfunc
-function Test_breakindent01()
+func Test_breakindent01()
" simple breakindent test
call s:test_windows('setl briopt=min:0')
- let lines=s:screen_lines(line('.'),8)
- let expect=[
-\ " abcd",
-\ " qrst",
-\ " GHIJ",
-\ ]
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qrst",
+ \ " GHIJ",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows()
-endfunction
+endfunc
-function Test_breakindent02()
+func Test_breakindent01_vartabs()
+ " like 01 but with vartabs feature
+ if !has("vartabs")
+ return
+ endif
+ call s:test_windows('setl briopt=min:0 vts=4')
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qrst",
+ \ " GHIJ",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set vts&')
+endfunc
+
+func Test_breakindent02()
" simple breakindent test with showbreak set
call s:test_windows('setl briopt=min:0 sbr=>>')
- let lines=s:screen_lines(line('.'),8)
- let expect=[
-\ " abcd",
-\ " >>qr",
-\ " >>EF",
-\ ]
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " >>qr",
+ \ " >>EF",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr=')
-endfunction
+endfunc
+
+func Test_breakindent02_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " simple breakindent test with showbreak set
+ call s:test_windows('setl briopt=min:0 sbr=>> vts=4')
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " >>qr",
+ \ " >>EF",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= vts&')
+endfunc
-function Test_breakindent03()
+func Test_breakindent03()
" simple breakindent test with showbreak set and briopt including sbr
call s:test_windows('setl briopt=sbr,min:0 sbr=++')
- let lines=s:screen_lines(line('.'),8)
+ let lines = s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ "++ qrst",
@@ -70,77 +102,177 @@ function Test_breakindent03()
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr=')
-endfunction
+endfunc
-function Test_breakindent04()
+func Test_breakindent03_vartabs()
+ " simple breakindent test with showbreak set and briopt including sbr
+ if !has("vartabs")
+ return
+ endif
+ call s:test_windows('setl briopt=sbr,min:0 sbr=++ vts=4')
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ "++ qrst",
+ \ "++ GHIJ",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr= vts&')
+endfunc
+
+func Test_breakindent04()
" breakindent set with min width 18
call s:test_windows('setl sbr= briopt=min:18')
- let lines=s:screen_lines(line('.'),8)
- let expect=[
-\ " abcd",
-\ " qrstuv",
-\ " IJKLMN",
-\ ]
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qrstuv",
+ \ " IJKLMN",
+ \ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr=')
-endfunction
+endfunc
+
+func Test_breakindent04_vartabs()
+ " breakindent set with min width 18
+ if !has("vartabs")
+ return
+ endif
+ call s:test_windows('setl sbr= briopt=min:18 vts=4')
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qrstuv",
+ \ " IJKLMN",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr= vts&')
+endfunc
-function Test_breakindent05()
+func Test_breakindent05()
" breakindent set and shift by 2
call s:test_windows('setl briopt=shift:2,min:0')
- let lines=s:screen_lines(line('.'),8)
- let expect=[
-\ " abcd",
-\ " qr",
-\ " EF",
-\ ]
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qr",
+ \ " EF",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows()
-endfunction
+endfunc
-function Test_breakindent06()
+func Test_breakindent05_vartabs()
+ " breakindent set and shift by 2
+ if !has("vartabs")
+ return
+ endif
+ call s:test_windows('setl briopt=shift:2,min:0 vts=4')
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qr",
+ \ " EF",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set vts&')
+endfunc
+
+func Test_breakindent06()
" breakindent set and shift by -1
call s:test_windows('setl briopt=shift:-1,min:0')
- let lines=s:screen_lines(line('.'),8)
- let expect=[
-\ " abcd",
-\ " qrstu",
-\ " HIJKL",
-\ ]
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qrstu",
+ \ " HIJKL",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows()
-endfunction
+endfunc
+
+func Test_breakindent06_vartabs()
+ " breakindent set and shift by -1
+ if !has("vartabs")
+ return
+ endif
+ call s:test_windows('setl briopt=shift:-1,min:0 vts=4')
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ " abcd",
+ \ " qrstu",
+ \ " HIJKL",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set vts&')
+endfunc
-function Test_breakindent07()
+func Test_breakindent07()
" breakindent set and shift by 1, Number set sbr=? and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n')
- let lines=s:screen_lines(line('.'),10)
- let expect=[
-\ " 2 ab",
-\ "? m",
-\ "? x",
-\ ]
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ab",
+ \ "? m",
+ \ "? x",
+ \ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr= cpo-=n')
-endfunction
+endfunc
+
+func Test_breakindent07_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " breakindent set and shift by 1, Number set sbr=? and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n vts=4')
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ab",
+ \ "? m",
+ \ "? x",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr= cpo-=n vts&')
+endfunc
-function Test_breakindent07a()
+func Test_breakindent07a()
" breakindent set and shift by 1, Number set sbr=? and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4')
- let lines=s:screen_lines(line('.'),10)
- let expect=[
-\ " 2 ab",
-\ " ? m",
-\ " ? x",
-\ ]
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ab",
+ \ " ? m",
+ \ " ? x",
+ \ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr=')
-endfunction
+endfunc
+
+func Test_breakindent07a_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " breakindent set and shift by 1, Number set sbr=? and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 vts=4')
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ab",
+ \ " ? m",
+ \ " ? x",
+ \ ]
+ call s:compare_lines(expect, lines)
+ " clean up
+ call s:close_windows('set sbr= vts&')
+endfunc
-function Test_breakindent08()
+func Test_breakindent08()
" breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4')
" make sure, cache is invalidated!
@@ -148,43 +280,96 @@ function Test_breakindent08()
redraw!
set ts=4
redraw!
- let lines=s:screen_lines(line('.'),10)
- let expect=[
-\ " 2 ^Iabcd",
-\ "# opq",
-\ "# BCD",
-\ ]
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ^Iabcd",
+ \ "# opq",
+ \ "# BCD",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n')
-endfunction
+endfunc
+
+func Test_breakindent08_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4 vts=4')
+ " make sure, cache is invalidated!
+ set ts=8
+ redraw!
+ set ts=4
+ redraw!
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ^Iabcd",
+ \ "# opq",
+ \ "# BCD",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= cpo-=n vts&')
+endfunc
-function Test_breakindent08a()
+func Test_breakindent08a()
" breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list')
- let lines=s:screen_lines(line('.'),10)
- let expect=[
-\ " 2 ^Iabcd",
-\ " # opq",
-\ " # BCD",
-\ ]
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ^Iabcd",
+ \ " # opq",
+ \ " # BCD",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr=')
-endfunction
+endfunc
-function Test_breakindent09()
+func Test_breakindent08a_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
+ call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list vts=4')
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ^Iabcd",
+ \ " # opq",
+ \ " # BCD",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= vts&')
+endfunc
+
+func Test_breakindent09()
" breakindent set and shift by 1, Number and list set sbr=#
call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list')
- let lines=s:screen_lines(line('.'),10)
- let expect=[
-\ " 2 ^Iabcd",
-\ " #op",
-\ " #AB",
-\ ]
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ^Iabcd",
+ \ " #op",
+ \ " #AB",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr=')
-endfunction
+endfunc
+
+func Test_breakindent09_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " breakindent set and shift by 1, Number and list set sbr=#
+ call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list vts=4')
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ^Iabcd",
+ \ " #op",
+ \ " #AB",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= vts&')
+endfunc
-function Test_breakindent10()
+func Test_breakindent10()
" breakindent set, Number set sbr=~
call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0')
" make sure, cache is invalidated!
@@ -192,41 +377,91 @@ function Test_breakindent10()
redraw!
set ts=4
redraw!
- let lines=s:screen_lines(line('.'),10)
- let expect=[
-\ " 2 ab",
-\ "~ mn",
-\ "~ yz",
-\ ]
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ab",
+ \ "~ mn",
+ \ "~ yz",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n')
-endfunction
+endfunc
+
+func Test_breakindent10_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " breakindent set, Number set sbr=~
+ call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0 vts=4')
+ " make sure, cache is invalidated!
+ set ts=8
+ redraw!
+ set ts=4
+ redraw!
+ let lines = s:screen_lines(line('.'),10)
+ let expect = [
+ \ " 2 ab",
+ \ "~ mn",
+ \ "~ yz",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set sbr= cpo-=n vts&')
+endfunc
-function Test_breakindent11()
+func Test_breakindent11()
" test strdisplaywidth()
call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4')
let text=getline(2)
let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
call assert_equal(width, strdisplaywidth(text))
call s:close_windows('set sbr=')
-endfunction
+endfunc
+
+func Test_breakindent11_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " test strdisplaywidth()
+ call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4 vts=4')
+ let text = getline(2)
+ let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
+ call assert_equal(width, strdisplaywidth(text))
+ call s:close_windows('set sbr= vts&')
+endfunc
-function Test_breakindent12()
+func Test_breakindent12()
" test breakindent with long indent
let s:input="\t\t\t\t\t{"
call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>-')
- let lines=s:screen_lines(2,16)
- let expect=[
-\ " 2 >--->--->--->",
-\ " ---{ ",
-\ "~ ",
-\ ]
+ let lines = s:screen_lines(2,16)
+ let expect = [
+ \ " 2 >--->--->--->",
+ \ " ---{ ",
+ \ "~ ",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows('set nuw=4 listchars=')
-endfunction
+endfunc
+
+func Test_breakindent12_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " test breakindent with long indent
+ let s:input = "\t\t\t\t\t{"
+ call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>- vts=4')
+ let lines = s:screen_lines(2,16)
+ let expect = [
+ \ " 2 >--->--->--->",
+ \ " ---{ ",
+ \ "~ ",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set nuw=4 listchars= vts&')
+endfunc
-function Test_breakindent13()
- let s:input=""
+func Test_breakindent13()
+ let s:input = ""
call s:test_windows('setl breakindent briopt=min:10 ts=8')
vert resize 20
call setline(1, [" a\tb\tc\td\te", " z y x w v"])
@@ -237,65 +472,149 @@ function Test_breakindent13()
call assert_equal('d', @a)
call assert_equal('w', @b)
call s:close_windows()
-endfunction
+endfunc
+
+func Test_breakindent13_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ let s:input = ""
+ call s:test_windows('setl breakindent briopt=min:10 ts=8 vts=8')
+ vert resize 20
+ call setline(1, [" a\tb\tc\td\te", " z y x w v"])
+ 1
+ norm! fbgj"ayl
+ 2
+ norm! fygj"byl
+ call assert_equal('d', @a)
+ call assert_equal('w', @b)
+ call s:close_windows('set vts&')
+endfunc
-function Test_breakindent14()
- let s:input=""
+func Test_breakindent14()
+ let s:input = ""
call s:test_windows('setl breakindent briopt= ts=8')
vert resize 30
norm! 3a1234567890
norm! a abcde
exec "norm! 0\<C-V>tex"
- let lines=s:screen_lines(line('.'),8)
- let expect=[
-\ "e ",
-\ "~ ",
-\ "~ ",
-\ ]
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ "e ",
+ \ "~ ",
+ \ "~ ",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows()
-endfunction
+endfunc
+
+func Test_breakindent14_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ let s:input = ""
+ call s:test_windows('setl breakindent briopt= ts=8 vts=8')
+ vert resize 30
+ norm! 3a1234567890
+ norm! a abcde
+ exec "norm! 0\<C-V>tex"
+ let lines = s:screen_lines(line('.'),8)
+ let expect = [
+ \ "e ",
+ \ "~ ",
+ \ "~ ",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set vts&')
+endfunc
-function Test_breakindent15()
- let s:input=""
+func Test_breakindent15()
+ let s:input = ""
call s:test_windows('setl breakindent briopt= ts=8 sw=8')
vert resize 30
norm! 4a1234567890
exe "normal! >>\<C-V>3f0x"
- let lines=s:screen_lines(line('.'),20)
- let expect=[
-\ " 1234567890 ",
-\ "~ ",
-\ "~ ",
-\ ]
+ let lines = s:screen_lines(line('.'),20)
+ let expect = [
+ \ " 1234567890 ",
+ \ "~ ",
+ \ "~ ",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows()
-endfunction
+endfunc
+
+func Test_breakindent15_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ let s:input = ""
+ call s:test_windows('setl breakindent briopt= ts=8 sw=8 vts=8')
+ vert resize 30
+ norm! 4a1234567890
+ exe "normal! >>\<C-V>3f0x"
+ let lines = s:screen_lines(line('.'),20)
+ let expect = [
+ \ " 1234567890 ",
+ \ "~ ",
+ \ "~ ",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set vts&')
+endfunc
-function Test_breakindent16()
+func Test_breakindent16()
" Check that overlong lines are indented correctly.
- let s:input=""
+ let s:input = ""
call s:test_windows('setl breakindent briopt=min:0 ts=4')
call setline(1, "\t".repeat("1234567890", 10))
resize 6
norm! 1gg$
redraw!
- let lines=s:screen_lines(1,10)
- let expect=[
-\ " 789012",
-\ " 345678",
-\ " 901234",
-\ ]
+ let lines = s:screen_lines(1,10)
+ let expect = [
+ \ " 789012",
+ \ " 345678",
+ \ " 901234",
+ \ ]
call s:compare_lines(expect, lines)
- let lines=s:screen_lines(4,10)
- let expect=[
-\ " 567890",
-\ " 123456",
-\ " 7890 ",
-\ ]
+ let lines = s:screen_lines(4,10)
+ let expect = [
+ \ " 567890",
+ \ " 123456",
+ \ " 7890 ",
+ \ ]
call s:compare_lines(expect, lines)
call s:close_windows()
-endfunction
+endfunc
+
+func Test_breakindent16_vartabs()
+ if !has("vartabs")
+ return
+ endif
+ " Check that overlong lines are indented correctly.
+ let s:input = ""
+ call s:test_windows('setl breakindent briopt=min:0 ts=4 vts=4')
+ call setline(1, "\t".repeat("1234567890", 10))
+ resize 6
+ norm! 1gg$
+ redraw!
+ let lines = s:screen_lines(1,10)
+ let expect = [
+ \ " 789012",
+ \ " 345678",
+ \ " 901234",
+ \ ]
+ call s:compare_lines(expect, lines)
+ let lines = s:screen_lines(4,10)
+ let expect = [
+ \ " 567890",
+ \ " 123456",
+ \ " 7890 ",
+ \ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows('set vts&')
+endfunc
func Test_breakindent17_vartabs()
if !has("vartabs")
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index a66aee5e02..489b2477e6 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -569,6 +569,21 @@ func Test_cmdline_complete_user_cmd()
delcommand Foo
endfunc
+func s:ScriptLocalFunction()
+ echo 'yes'
+endfunc
+
+func Test_cmdline_complete_user_func()
+ call feedkeys(":func Test_cmdline_complete_user\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_match('"func Test_cmdline_complete_user', @:)
+ call feedkeys(":func s:ScriptL\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_match('"func <SNR>\d\+_ScriptLocalFunction', @:)
+
+ " g: prefix also works
+ call feedkeys(":echo g:Test_cmdline_complete_user_f\<Tab>\<Home>\"\<cr>", 'tx')
+ call assert_match('"echo g:Test_cmdline_complete_user_func', @:)
+endfunc
+
func Test_cmdline_complete_user_names()
if has('unix') && executable('whoami')
let whoami = systemlist('whoami')[0]
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index 2e190911b2..53b7da517e 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -92,6 +92,11 @@ func Test_screenpos()
\ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
close
bwipe!
+
+ call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
+ nmenu WinBar.TEST :
+ call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1))
+ nunmenu WinBar.TEST
endfunc
func Test_screenpos_number()
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index ff50d53d86..73b57f302e 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -24,7 +24,7 @@ endfunc
func Test_for_invalid()
call assert_fails("for x in 99", 'E714:')
- call assert_fails("for x in 'asdf'", 'E714:')
+ call assert_fails("for x in function('winnr')", 'E714:')
call assert_fails("for x in {'a': 9}", 'E714:')
if 0
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 6bc5fba5db..fa0bffd96c 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -358,6 +358,7 @@ let s:filename_checks = {
\ 'po': ['file.po', 'file.pot'],
\ 'pod': ['file.pod'],
\ 'pod6': ['file.pod6'],
+ \ 'poke': ['file.pk'],
\ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'],
\ 'pov': ['file.pov'],
\ 'povini': ['.povrayrc'],
@@ -370,6 +371,8 @@ let s:filename_checks = {
\ 'promela': ['file.pml'],
\ 'proto': ['file.proto'],
\ 'protocols': ['/etc/protocols'],
+ \ 'ps1': ['file.ps1', 'file.psd1', 'file.psm1', 'file.pssc'],
+ \ 'ps1xml': ['file.ps1xml'],
\ 'psf': ['file.psf'],
\ 'puppet': ['file.pp'],
\ 'pyrex': ['file.pyx', 'file.pxd'],
@@ -520,7 +523,7 @@ let s:filename_checks = {
\ 'xhtml': ['file.xhtml', 'file.xht'],
\ 'xinetd': ['/etc/xinetd.conf'],
\ 'xmath': ['file.msc', 'file.msf'],
- \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl'],
+ \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1'],
\ 'xmodmap': ['anyXmodmap'],
\ 'xf86conf': ['xorg.conf', 'xorg.conf-4'],
\ 'xpm2': ['file.xpm2'],
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 5dae8d681a..555f549743 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1,5 +1,7 @@
" Tests for various functions.
+
source shared.vim
+source check.vim
" Must be done first, since the alternate buffer must be unset.
func Test_00_bufexists()
@@ -171,9 +173,8 @@ func Test_str2nr()
endfunc
func Test_strftime()
- if !exists('*strftime')
- return
- endif
+ CheckFunction strftime
+
" Format of strftime() depends on system. We assume
" that basic formats tested here are available and
" identical on all systems which support strftime().
@@ -214,6 +215,33 @@ func Test_strftime()
endif
endfunc
+func Test_strptime()
+ CheckFunction strptime
+ CheckNotMSWindows
+
+ if exists('$TZ')
+ let tz = $TZ
+ endif
+ let $TZ = 'UTC'
+
+ call assert_equal(1484653763, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23'))
+
+ " Force DST and check that it's considered
+ let $TZ = 'WINTER0SUMMER,J1,J365'
+ call assert_equal(1484653763 - 3600, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23'))
+
+ call assert_fails('call strptime()', 'E119:')
+ call assert_fails('call strptime("xxx")', 'E119:')
+ call assert_equal(0, strptime("%Y", ''))
+ call assert_equal(0, strptime("%Y", "xxx"))
+
+ if exists('tz')
+ let $TZ = tz
+ else
+ unlet $TZ
+ endif
+endfunc
+
func Test_resolve_unix()
if !has('unix')
return
diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim
index 9acec51913..d09b25b0e7 100644
--- a/src/nvim/testdir/test_gn.vim
+++ b/src/nvim/testdir/test_gn.vim
@@ -1,9 +1,8 @@
" Test for gn command
func Test_gn_command()
- set belloff=all
noautocmd new
- " replace a single char by itsself quoted:
+ " replace a single char by itself quoted:
call setline('.', 'abc x def x ghi x jkl')
let @/ = 'x'
exe "norm! cgn'x'\<esc>.."
@@ -157,7 +156,6 @@ func Test_gn_command()
sil! %d _
set wrapscan&vim
- set belloff&vim
endfunc
func Test_gN_repeat()
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 4cc4d775d1..ce22de09ca 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -3,6 +3,7 @@
source view_util.vim
source screendump.vim
source check.vim
+source script_util.vim
func Test_highlight()
" basic test if ":highlight" doesn't crash
@@ -623,4 +624,103 @@ func Test_xxlast_highlight_RGB_color()
hi clear
endfunc
+func Test_highlight_clear_restores_links()
+ let aaa_id = hlID('aaa')
+ call assert_equal(aaa_id, 0)
+
+ " create default link aaa --> bbb
+ hi def link aaa bbb
+ let id_aaa = hlID('aaa')
+ let hl_aaa_bbb = HighlightArgs('aaa')
+
+ " try to redefine default link aaa --> ccc; check aaa --> bbb
+ hi def link aaa ccc
+ call assert_equal(HighlightArgs('aaa'), hl_aaa_bbb)
+
+ " clear aaa; check aaa --> bbb
+ hi clear aaa
+ call assert_equal(HighlightArgs('aaa'), hl_aaa_bbb)
+
+ " link aaa --> ccc; clear aaa; check aaa --> bbb
+ hi link aaa ccc
+ let id_ccc = hlID('ccc')
+ call assert_equal(synIDtrans(id_aaa), id_ccc)
+ hi clear aaa
+ call assert_equal(HighlightArgs('aaa'), hl_aaa_bbb)
+
+ " forcibly set default link aaa --> ddd
+ hi! def link aaa ddd
+ let id_ddd = hlID('ddd')
+ let hl_aaa_ddd = HighlightArgs('aaa')
+ call assert_equal(synIDtrans(id_aaa), id_ddd)
+
+ " link aaa --> eee; clear aaa; check aaa --> ddd
+ hi link aaa eee
+ let eee_id = hlID('eee')
+ call assert_equal(synIDtrans(id_aaa), eee_id)
+ hi clear aaa
+ call assert_equal(HighlightArgs('aaa'), hl_aaa_ddd)
+endfunc
+
+func Test_highlight_clear_restores_context()
+ func FuncContextDefault()
+ hi def link Context ContextDefault
+ endfun
+
+ func FuncContextRelink()
+ " Dummy line
+ hi link Context ContextRelink
+ endfunc
+
+ let scriptContextDefault = MakeScript("FuncContextDefault")
+ let scriptContextRelink = MakeScript("FuncContextRelink")
+ let patContextDefault = fnamemodify(scriptContextDefault, ':t') .. ' line 1'
+ let patContextRelink = fnamemodify(scriptContextRelink, ':t') .. ' line 2'
+
+ exec "source" scriptContextDefault
+ let hlContextDefault = execute("verbose hi Context")
+ call assert_match(patContextDefault, hlContextDefault)
+
+ exec "source" scriptContextRelink
+ let hlContextRelink = execute("verbose hi Context")
+ call assert_match(patContextRelink, hlContextRelink)
+
+ hi clear
+ let hlContextAfterClear = execute("verbose hi Context")
+ call assert_match(patContextDefault, hlContextAfterClear)
+
+ delfunc FuncContextDefault
+ delfunc FuncContextRelink
+ call delete(scriptContextDefault)
+ call delete(scriptContextRelink)
+endfunc
+
+func Test_highlight_default_colorscheme_restores_links()
+ hi link TestLink Identifier
+ hi TestHi ctermbg=red
+
+ let hlTestLinkPre = HighlightArgs('TestLink')
+ let hlTestHiPre = HighlightArgs('TestHi')
+
+ " Test colorscheme
+ hi clear
+ if exists('syntax_on')
+ syntax reset
+ endif
+ let g:colors_name = 'test'
+ hi link TestLink ErrorMsg
+ hi TestHi ctermbg=green
+
+ " Restore default highlighting
+ colorscheme default
+ " 'default' should work no matter if highlight group was cleared
+ hi def link TestLink Identifier
+ hi def TestHi ctermbg=red
+ let hlTestLinkPost = HighlightArgs('TestLink')
+ let hlTestHiPost = HighlightArgs('TestHi')
+ call assert_equal(hlTestLinkPre, hlTestLinkPost)
+ call assert_equal(hlTestHiPre, hlTestHiPost)
+ hi clear
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 9435931d41..3da3648fec 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -469,6 +469,34 @@ func Test_pum_with_folds_two_tabs()
call delete('Xpumscript')
endfunc
+" Test for inserting the tag search pattern in insert mode
+func Test_ins_compl_tag_sft()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo\t/^int first() {}$/",
+ \ "second\tXfoo\t/^int second() {}$/",
+ \ "third\tXfoo\t/^int third() {}$/"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int second() {}
+ int third() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew
+ set showfulltag
+ exe "normal isec\<C-X>\<C-]>\<C-N>\<CR>"
+ call assert_equal('int second() {}', getline(1))
+ set noshowfulltag
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe!
+endfunc
+
" Test to ensure 'Scanning...' messages are not recorded in messages history
func Test_z1_complete_no_history()
new
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index dcc588120c..4cb609aaf0 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -110,6 +110,35 @@ func Test_listchars()
\ '.....h>-$',
\ 'iii<<<<><<$', '$'], l)
+ " Test lead and trail
+ normal ggdG
+ set listchars=eol:$
+ set listchars+=lead:>,trail:<,space:x
+ set list
+
+ call append(0, [
+ \ ' ffff ',
+ \ ' gg',
+ \ 'h ',
+ \ ' ',
+ \ ' 0 0 ',
+ \ ])
+
+ let expected = [
+ \ '>>>>ffff<<<<$',
+ \ '>>>>>>>>>>gg$',
+ \ 'h<<<<<<<<<<<$',
+ \ '<<<<<<<<<<<<$',
+ \ '>>>>0xx0<<<<$',
+ \ '$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ call assert_equal(expected, split(execute("%list"), "\n"))
" test nbsp
normal ggdG
diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim
index d619ac0eb5..e0518de3c2 100644
--- a/src/nvim/testdir/test_listlbr.vim
+++ b/src/nvim/testdir/test_listlbr.vim
@@ -1,9 +1,5 @@
" Test for linebreak and list option (non-utf8)
-" Nvim does not allow setting 'encoding', so skip this test.
-finish
-
-set encoding=latin1
scriptencoding latin1
if !exists("+linebreak") || !has("conceal")
@@ -46,6 +42,7 @@ func Test_set_linebreak()
endfunc
func Test_linebreak_with_list()
+ throw 'skipped: Nvim does not support enc=latin1'
call s:test_windows('setl ts=4 sbr=+ list listchars=')
call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
let lines = s:screen_lines([1, 4], winwidth(0))
@@ -217,6 +214,7 @@ func Test_norm_after_block_visual()
endfunc
func Test_block_replace_after_wrapping()
+ throw 'skipped: Nvim does not support enc=latin1'
call s:test_windows()
call setline(1, repeat("a", 150))
exe "norm! 0yypk147|\<C-V>jr0"
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index c4807797ff..0191dbf33e 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -427,6 +427,30 @@ func Test_error_in_map_expr()
exe buf .. 'bwipe!'
endfunc
+func Test_expr_map_gets_cursor()
+ new
+ call setline(1, ['one', 'some w!rd'])
+ func StoreColumn()
+ let g:exprLine = line('.')
+ let g:exprCol = col('.')
+ return 'x'
+ endfunc
+ nnoremap <expr> x StoreColumn()
+ 2
+ nmap ! f!<Ignore>x
+ call feedkeys("!", 'xt')
+ call assert_equal('some wrd', getline(2))
+ call assert_equal(2, g:exprLine)
+ call assert_equal(7, g:exprCol)
+
+ bwipe!
+ unlet g:exprLine
+ unlet g:exprCol
+ delfunc StoreColumn
+ nunmap x
+ nunmap !
+endfunc
+
" Test for mapping errors
func Test_map_error()
call assert_fails('unmap', 'E474:')
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 3ebd048f46..08586dffe1 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -1,5 +1,6 @@
" Tests for :messages, :echomsg, :echoerr
+source check.vim
source shared.vim
func Test_messages()
@@ -77,7 +78,7 @@ func Test_echomsg()
endfunc
func Test_echoerr()
- throw 'skipped: Nvim does not support test_ignore_error()'
+ CheckFunction test_ignore_error
call test_ignore_error('IgNoRe')
call assert_equal("\nIgNoRe hello", execute(':echoerr "IgNoRe hello"'))
call assert_equal("\n12345 IgNoRe", execute(':echoerr 12345 "IgNoRe"'))
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 7c7804212b..8486f3ff68 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -331,6 +331,20 @@ func Test_mkview_no_balt()
%bwipe
endfunc
+func Test_mksession_no_balt()
+ edit Xtestfile1
+ edit Xtestfile2
+
+ bdelete Xtestfile1
+ mksession! Xtestview
+
+ source Xtestview
+ call assert_equal(0, buflisted('Xtestfile1'))
+
+ call delete('Xtestview')
+ %bwipe
+endfunc
+
" Test :mkview with a file argument.
func Test_mkview_file()
" Create a view with line number and a fold.
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 5a10c9baa6..5aef33cb09 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -1,5 +1,7 @@
" Test for options
+source check.vim
+
func Test_whichwrap()
set whichwrap=b,s
call assert_equal('b,s', &whichwrap)
@@ -223,11 +225,18 @@ func Test_set_completion()
" Expand files and directories.
call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('./samples/ ./sautest/ ./screendump.vim ./setup.vim ./shared.vim', @:)
+ call assert_match('./samples/ ./sautest/ ./screendump.vim ./script_util.vim ./setup.vim ./shared.vim', @:)
call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)
set tags&
+
+ " Expand values for 'filetype'
+ call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"set filetype=sshdconfig', @:)
+ call feedkeys(":set filetype=a\<C-A>\<C-B>\"\<CR>", 'xt')
+ " call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:)
+ call assert_equal('"set filetype=' .. join(getcompletion('a*', 'filetype')), @:)
endfunc
func Test_set_errors()
@@ -439,6 +448,36 @@ func Test_backupskip()
endif
endfor
+ " Duplicates from environment variables should be filtered out (option has
+ " P_NODUP). Run this in a separate instance and write v:errors in a file,
+ " so that we see what happens on startup.
+ let after =<< trim [CODE]
+ let bsklist = split(&backupskip, ',')
+ call assert_equal(uniq(copy(bsklist)), bsklist)
+ call writefile(['errors:'] + v:errors, 'Xtestout')
+ qall
+ [CODE]
+ call writefile(after, 'Xafter')
+ " let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"'
+ let cmd = GetVimProg() . ' -S Xafter --cmd "set enc=utf8"'
+
+ let saveenv = {}
+ for var in ['TMPDIR', 'TMP', 'TEMP']
+ let saveenv[var] = getenv(var)
+ call setenv(var, '/duplicate/path')
+ endfor
+
+ exe 'silent !' . cmd
+ call assert_equal(['errors:'], readfile('Xtestout'))
+
+ " restore environment variables
+ for var in ['TMPDIR', 'TMP', 'TEMP']
+ call setenv(var, saveenv[var])
+ endfor
+
+ call delete('Xtestout')
+ call delete('Xafter')
+
" Duplicates should be filtered out (option has P_NODUP)
let backupskip = &backupskip
set backupskip=
@@ -604,6 +643,49 @@ func Test_opt_boolean()
set number&
endfunc
+func Test_opt_winminheight_term()
+ " See test/functional/legacy/options_spec.lua
+ CheckRunVimInTerminal
+
+ " The tabline should be taken into account.
+ let lines =<< trim END
+ set wmh=0 stal=2
+ below sp | wincmd _
+ below sp | wincmd _
+ below sp | wincmd _
+ below sp
+ END
+ call writefile(lines, 'Xwinminheight')
+ let buf = RunVimInTerminal('-S Xwinminheight', #{rows: 11})
+ call term_sendkeys(buf, ":set wmh=1\n")
+ call WaitForAssert({-> assert_match('E36: Not enough room', term_getline(buf, 11))})
+
+ call StopVimInTerminal(buf)
+ call delete('Xwinminheight')
+endfunc
+
+func Test_opt_winminheight_term_tabs()
+ " See test/functional/legacy/options_spec.lua
+ CheckRunVimInTerminal
+
+ " The tabline should be taken into account.
+ let lines =<< trim END
+ set wmh=0 stal=2
+ split
+ split
+ split
+ split
+ tabnew
+ END
+ call writefile(lines, 'Xwinminheight')
+ let buf = RunVimInTerminal('-S Xwinminheight', #{rows: 11})
+ call term_sendkeys(buf, ":set wmh=1\n")
+ call WaitForAssert({-> assert_match('E36: Not enough room', term_getline(buf, 11))})
+
+ call StopVimInTerminal(buf)
+ call delete('Xwinminheight')
+endfunc
+
" Test for setting option value containing spaces with isfname+=32
func Test_isfname_with_options()
set isfname+=32
@@ -613,4 +695,23 @@ func Test_isfname_with_options()
setlocal keywordprg&
endfunc
+" Test that resetting laststatus does change scroll option
+func Test_opt_reset_scroll()
+ " See test/functional/legacy/options_spec.lua
+ CheckRunVimInTerminal
+ let vimrc =<< trim [CODE]
+ set scroll=2
+ set laststatus=2
+ [CODE]
+ call writefile(vimrc, 'Xscroll')
+ let buf = RunVimInTerminal('-S Xscroll', {'rows': 16, 'cols': 45})
+ call term_sendkeys(buf, ":verbose set scroll?\n")
+ call WaitForAssert({-> assert_match('Last set.*window size', term_getline(buf, 15))})
+ call assert_match('^\s*scroll=7$', term_getline(buf, 14))
+ call StopVimInTerminal(buf)
+
+ " clean up
+ call delete('Xscroll')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 4ee16558a0..9443958984 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -871,7 +871,7 @@ func Test_popup_complete_backwards_ctrl_p()
endfunc
fun! Test_complete_o_tab()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
let s:o_char_pressed = 0
fun! s:act_on_text_changed()
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 704fdacdcd..da949f5940 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -2660,7 +2660,7 @@ endfunc
" Test for incsearch highlighting of the :vimgrep pattern
" This test used to cause "E315: ml_get: invalid lnum" errors.
func Test_vimgrep_incsearch()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
enew
set incsearch
call test_override("char_avail", 1)
diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim
index 77a5153a81..6e6f91362b 100644
--- a/src/nvim/testdir/test_quotestar.vim
+++ b/src/nvim/testdir/test_quotestar.vim
@@ -97,7 +97,7 @@ func Do_test_quotestar_for_x11()
if has('unix') && has('gui') && !has('gui_running')
let @* = ''
- " Running in a terminal and the GUI is avaiable: Tell the server to open
+ " Running in a terminal and the GUI is available: Tell the server to open
" the GUI and check that the remote command still works.
" Need to wait for the GUI to start up, otherwise the send hangs in trying
" to send to the terminal window.
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index 1bb2ee53de..cacdd68d10 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -1,5 +1,5 @@
" Tests for regexp in latin1 encoding
-set encoding=latin1
+" set encoding=latin1
scriptencoding latin1
func s:equivalence_test()
@@ -22,11 +22,13 @@ func s:equivalence_test()
endfunc
func Test_equivalence_re1()
+ throw 'skipped: Nvim does not support enc=latin1'
set re=1
call s:equivalence_test()
endfunc
func Test_equivalence_re2()
+ throw 'skipped: Nvim does not support enc=latin1'
set re=2
call s:equivalence_test()
endfunc
@@ -39,6 +41,17 @@ func Test_range_with_newline()
bwipe!
endfunc
+func Test_pattern_compile_speed()
+ if !exists('+spellcapcheck') || !has('reltime')
+ return
+ endif
+ let start = reltime()
+ " this used to be very slow, not it should be about a second
+ set spc=\\v(((((Nxxxxxxx&&xxxx){179})+)+)+){179}
+ call assert_inrange(0.01, 10.0, reltimefloat(reltime(start)))
+ set spc=
+endfunc
+
func Test_get_equi_class()
new
" Incomplete equivalence class caused invalid memory access
@@ -87,6 +100,7 @@ func Test_multi_failure()
endfunc
func Test_recursive_addstate()
+ throw 'skipped: TODO: '
" This will call addstate() recursively until it runs into the limit.
let lnum = search('\v((){328}){389}')
call assert_equal(0, lnum)
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 8d2a768ba0..53069b3d31 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -109,6 +109,8 @@ func Test_recording_esc_sequence()
bwipe!
if exists('save_F2')
let &t_F2 = save_F2
+ else
+ set t_F2=
endif
endfunc
diff --git a/src/nvim/testdir/test_rename.vim b/src/nvim/testdir/test_rename.vim
new file mode 100644
index 0000000000..e4228188bd
--- /dev/null
+++ b/src/nvim/testdir/test_rename.vim
@@ -0,0 +1,119 @@
+" Test rename()
+
+func Test_rename_file_to_file()
+ call writefile(['foo'], 'Xrename1')
+
+ call assert_equal(0, rename('Xrename1', 'Xrename2'))
+
+ call assert_equal('', glob('Xrename1'))
+ call assert_equal(['foo'], readfile('Xrename2'))
+
+ " When the destination file already exists, it should be overwritten.
+ call writefile(['foo'], 'Xrename1')
+ call writefile(['bar'], 'Xrename2')
+
+ call assert_equal(0, rename('Xrename1', 'Xrename2'))
+ call assert_equal('', glob('Xrename1'))
+ call assert_equal(['foo'], readfile('Xrename2'))
+
+ call delete('Xrename2')
+endfunc
+
+func Test_rename_file_ignore_case()
+ " With 'fileignorecase', renaming file will go through a temp file
+ " when the source and destination file only differ by case.
+ set fileignorecase
+ call writefile(['foo'], 'Xrename')
+
+ call assert_equal(0, rename('Xrename', 'XRENAME'))
+
+ call assert_equal(['foo'], readfile('XRENAME'))
+
+ set fileignorecase&
+ call delete('XRENAME')
+endfunc
+
+func Test_rename_same_file()
+ call writefile(['foo'], 'Xrename')
+
+ " When the source and destination are the same file, nothing
+ " should be done. The source file should not be deleted.
+ call assert_equal(0, rename('Xrename', 'Xrename'))
+ call assert_equal(['foo'], readfile('Xrename'))
+
+ call assert_equal(0, rename('./Xrename', 'Xrename'))
+ call assert_equal(['foo'], readfile('Xrename'))
+
+ call delete('Xrename')
+endfunc
+
+func Test_rename_dir_to_dir()
+ call mkdir('Xrenamedir1')
+ call writefile(['foo'], 'Xrenamedir1/Xrenamefile')
+
+ call assert_equal(0, rename('Xrenamedir1', 'Xrenamedir2'))
+
+ call assert_equal('', glob('Xrenamedir1'))
+ call assert_equal(['foo'], readfile('Xrenamedir2/Xrenamefile'))
+
+ call delete('Xrenamedir2/Xrenamefile')
+ call delete('Xrenamedir2', 'd')
+endfunc
+
+func Test_rename_same_dir()
+ call mkdir('Xrenamedir')
+ call writefile(['foo'], 'Xrenamedir/Xrenamefile')
+
+ call assert_equal(0, rename('Xrenamedir', 'Xrenamedir'))
+
+ call assert_equal(['foo'], readfile('Xrenamedir/Xrenamefile'))
+
+ call delete('Xrenamedir/Xrenamefile')
+ call delete('Xrenamedir', 'd')
+endfunc
+
+func Test_rename_copy()
+ " Check that when original file can't be deleted, rename()
+ " still succeeds but copies the file.
+ call mkdir('Xrenamedir')
+ call writefile(['foo'], 'Xrenamedir/Xrenamefile')
+ call setfperm('Xrenamedir', 'r-xr-xr-x')
+
+ call assert_equal(0, rename('Xrenamedir/Xrenamefile', 'Xrenamefile'))
+
+ if !has('win32')
+ " On Windows, the source file is removed despite
+ " its directory being made not writable.
+ call assert_equal(['foo'], readfile('Xrenamedir/Xrenamefile'))
+ endif
+ call assert_equal(['foo'], readfile('Xrenamefile'))
+
+ call setfperm('Xrenamedir', 'rwxrwxrwx')
+ call delete('Xrenamedir/Xrenamefile')
+ call delete('Xrenamedir', 'd')
+ call delete('Xrenamefile')
+endfunc
+
+func Test_rename_fails()
+ throw 'skipped: TODO: '
+ call writefile(['foo'], 'Xrenamefile')
+
+ " Can't rename into a non-existing directory.
+ call assert_notequal(0, rename('Xrenamefile', 'Xdoesnotexist/Xrenamefile'))
+
+ " Can't rename a non-existing file.
+ call assert_notequal(0, rename('Xdoesnotexist', 'Xrenamefile2'))
+ call assert_equal('', glob('Xrenamefile2'))
+
+ " When rename() fails, the destination file should not be deleted.
+ call assert_notequal(0, rename('Xdoesnotexist', 'Xrenamefile'))
+ call assert_equal(['foo'], readfile('Xrenamefile'))
+
+ " Can't rename to en empty file name.
+ call assert_notequal(0, rename('Xrenamefile', ''))
+
+ call assert_fails('call rename("Xrenamefile", [])', 'E730')
+ call assert_fails('call rename(0z, "Xrenamefile")', 'E976')
+
+ call delete('Xrenamefile')
+endfunc
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 0703a6b141..d4d529e4b9 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -2,10 +2,11 @@
source shared.vim
source screendump.vim
+source check.vim
+" See test/functional/legacy/search_spec.lua
func Test_search_cmdline()
- " See test/functional/legacy/search_spec.lua
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -202,9 +203,9 @@ func Test_search_cmdline()
bw!
endfunc
+" See test/functional/legacy/search_spec.lua
func Test_search_cmdline2()
- " See test/functional/legacy/search_spec.lua
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -351,7 +352,7 @@ func Test_searchc()
endfunc
func Cmdline3_prep()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
" need to disable char_avail,
" so that expansion of commandline works
call test_override("char_avail", 1)
@@ -361,14 +362,13 @@ func Cmdline3_prep()
endfunc
func Incsearch_cleanup()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
set noincsearch
call test_override("char_avail", 0)
bw!
endfunc
func Test_search_cmdline3()
- throw 'skipped: Nvim does not support test_override()'
if !exists('+incsearch')
return
endif
@@ -382,7 +382,6 @@ func Test_search_cmdline3()
endfunc
func Test_search_cmdline3s()
- throw 'skipped: Nvim does not support test_override()'
if !exists('+incsearch')
return
endif
@@ -409,7 +408,6 @@ func Test_search_cmdline3s()
endfunc
func Test_search_cmdline3g()
- throw 'skipped: Nvim does not support test_override()'
if !exists('+incsearch')
return
endif
@@ -433,7 +431,6 @@ func Test_search_cmdline3g()
endfunc
func Test_search_cmdline3v()
- throw 'skipped: Nvim does not support test_override()'
if !exists('+incsearch')
return
endif
@@ -450,9 +447,9 @@ func Test_search_cmdline3v()
call Incsearch_cleanup()
endfunc
+" See test/functional/legacy/search_spec.lua
func Test_search_cmdline4()
- " See test/functional/legacy/search_spec.lua
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -507,7 +504,7 @@ func Test_search_cmdline5()
endfunc
func Test_search_cmdline7()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
" Test that pressing <c-g> in an empty command line
" does not move the cursor
if !exists('+incsearch')
@@ -798,7 +795,7 @@ func Test_incsearch_vimgrep_dump()
endfunc
func Test_keep_last_search_pattern()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -820,7 +817,7 @@ func Test_keep_last_search_pattern()
endfunc
func Test_word_under_cursor_after_match()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -840,7 +837,7 @@ func Test_word_under_cursor_after_match()
endfunc
func Test_subst_word_under_cursor()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -882,7 +879,7 @@ func Test_incsearch_with_change()
endfunc
func Test_incsearch_cmdline_modifier()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -960,7 +957,7 @@ func Test_incsearch_search_dump()
endfunc
func Test_incsearch_substitute()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -982,7 +979,7 @@ func Test_incsearch_substitute()
endfunc
func Test_incsearch_substitute_long_line()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
new
call test_override("char_avail", 1)
set incsearch
@@ -1104,7 +1101,7 @@ func Test_one_error_msg()
endfunc
func Test_incsearch_add_char_under_cursor()
- throw 'skipped: Nvim does not support test_override()'
+ CheckFunction test_override
if !exists('+incsearch')
return
endif
@@ -1192,4 +1189,40 @@ func Test_search_smartcase_utf8()
close!
endfunc
+func Test_zzzz_incsearch_highlighting_newline()
+ CheckRunVimInTerminal
+ CheckOption incsearch
+ CheckScreendump
+ new
+ call test_override("char_avail", 1)
+
+ let commands =<< trim [CODE]
+ set incsearch nohls
+ call setline(1, ['test', 'xxx'])
+ [CODE]
+ call writefile(commands, 'Xincsearch_nl')
+ let buf = RunVimInTerminal('-S Xincsearch_nl', {'rows': 5, 'cols': 10})
+ " Need to send one key at a time to force a redraw
+ call term_sendkeys(buf, '/test')
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_incsearch_newline1', {})
+ call term_sendkeys(buf, '\n')
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_incsearch_newline2', {})
+ call term_sendkeys(buf, 'x')
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_incsearch_newline3', {})
+ call term_sendkeys(buf, 'x')
+ call VerifyScreenDump(buf, 'Test_incsearch_newline4', {})
+ call term_sendkeys(buf, "\<CR>")
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_incsearch_newline5', {})
+ call StopVimInTerminal(buf)
+
+ " clean up
+ call delete('Xincsearch_nl')
+ call test_override("char_avail", 0)
+ bw
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index 4bbd722fdb..9c3a5636ce 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -1,8 +1,7 @@
" Test for signs
-if !has('signs')
- finish
-endif
+source check.vim
+CheckFeature signs
source screendump.vim
@@ -1541,7 +1540,7 @@ endfunc
" Tests for memory allocation failures in sign functions
func Test_sign_memfailures()
- throw 'skipped: Nvim does not support test_alloc_fail()'
+ CheckFunction test_alloc_fail
call writefile(repeat(["Sun is shining"], 30), "Xsign")
edit Xsign
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index e6ad92f483..e0dc0e0075 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -111,10 +111,9 @@ func Test_pack_in_rtp_when_plugins_run()
endfunc
func Test_help_arg()
- if !has('unix') && has('gui')
- " this doesn't work with gvim on MS-Windows
- return
- endif
+ " This does not work with a GUI-only binary, such as on MS-Windows.
+ CheckAnyOf Unix NotGui
+
if RunVim([], [], '--help >Xtestout')
let lines = readfile('Xtestout')
call assert_true(len(lines) > 20)
@@ -412,6 +411,134 @@ func Test_A_F_H_arg()
call delete('Xtestout')
endfunc
+" Test the --echo-wid argument (for GTK GUI only).
+func Test_echo_wid()
+ CheckCanRunGui
+ CheckFeature gui_gtk
+
+ if RunVim([], [], '-g --echo-wid -cq >Xtest_echo_wid')
+ let lines = readfile('Xtest_echo_wid')
+ call assert_equal(1, len(lines))
+ call assert_match('^WID: \d\+$', lines[0])
+ endif
+
+ call delete('Xtest_echo_wid')
+endfunction
+
+" Test the -reverse and +reverse arguments (for GUI only).
+func Test_reverse()
+ CheckCanRunGui
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+
+ let after =<< trim [CODE]
+ call writefile([&background], "Xtest_reverse")
+ qall
+ [CODE]
+ if RunVim([], after, '-f -g -reverse')
+ let lines = readfile('Xtest_reverse')
+ call assert_equal(['dark'], lines)
+ endif
+ if RunVim([], after, '-f -g +reverse')
+ let lines = readfile('Xtest_reverse')
+ call assert_equal(['light'], lines)
+ endif
+
+ call delete('Xtest_reverse')
+endfunc
+
+" Test the -background and -foreground arguments (for GUI only).
+func Test_background_foreground()
+ CheckCanRunGui
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+
+ " Is there a better way to check the effect of -background & -foreground
+ " other than merely looking at &background (dark or light)?
+ let after =<< trim [CODE]
+ call writefile([&background], "Xtest_fg_bg")
+ qall
+ [CODE]
+ if RunVim([], after, '-f -g -background darkred -foreground yellow')
+ let lines = readfile('Xtest_fg_bg')
+ call assert_equal(['dark'], lines)
+ endif
+ if RunVim([], after, '-f -g -background ivory -foreground darkgreen')
+ let lines = readfile('Xtest_fg_bg')
+ call assert_equal(['light'], lines)
+ endif
+
+ call delete('Xtest_fg_bg')
+endfunc
+
+" Test the -font argument (for GUI only).
+func Test_font()
+ CheckCanRunGui
+ CheckNotMSWindows
+
+ if has('gui_gtk')
+ let font = 'Courier 14'
+ elseif has('gui_motif') || has('gui_athena')
+ let font = '-misc-fixed-bold-*'
+ else
+ throw 'Skipped: test does not set a valid font for this GUI'
+ endif
+
+ let after =<< trim [CODE]
+ call writefile([&guifont], "Xtest_font")
+ qall
+ [CODE]
+
+ if RunVim([], after, '--nofork -g -font "' .. font .. '"')
+ let lines = readfile('Xtest_font')
+ call assert_equal([font], lines)
+ endif
+
+ call delete('Xtest_font')
+endfunc
+
+" Test the -geometry argument (for GUI only).
+func Test_geometry()
+ CheckCanRunGui
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+
+ if has('gui_motif') || has('gui_athena')
+ " FIXME: With GUI Athena or Motif, the value of getwinposx(),
+ " getwinposy() and getwinpos() do not match exactly the
+ " value given in -geometry. Why?
+ " So only check &columns and &lines for those GUIs.
+ let after =<< trim [CODE]
+ call writefile([&columns, &lines], "Xtest_geometry")
+ qall
+ [CODE]
+ if RunVim([], after, '-f -g -geometry 31x13+41+43')
+ let lines = readfile('Xtest_geometry')
+ call assert_equal(['31', '13'], lines)
+ endif
+ else
+ let after =<< trim [CODE]
+ call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry")
+ qall
+ [CODE]
+ if RunVim([], after, '-f -g -geometry 31x13+41+43')
+ let lines = readfile('Xtest_geometry')
+ call assert_equal(['31', '13', '41', '43', '[41, 43]'], lines)
+ endif
+ endif
+
+ call delete('Xtest_geometry')
+endfunc
+
+" Test the -iconic argument (for GUI only).
+func Test_iconic()
+ CheckCanRunGui
+ CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena
+
+ call RunVim([], [], '-f -g -iconic -cq')
+
+ " TODO: currently only start vim iconified, but does not
+ " check that vim is iconified. How could this be checked?
+endfunc
+
+
func Test_invalid_args()
if !has('unix') || has('gui_running')
" can't get output of Vim.
@@ -687,6 +814,34 @@ func Test_v_argv()
call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:])
endfunc
+" Test for the '-t' option to jump to a tag
+func Test_t_arg()
+ let before =<< trim [CODE]
+ set tags=Xtags
+ [CODE]
+ let after =<< trim [CODE]
+ let s = bufname('') .. ':L' .. line('.') .. 'C' .. col('.')
+ call writefile([s], "Xtestout")
+ qall
+ [CODE]
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfile1\t/^ \\zsfirst$/",
+ \ "second\tXfile1\t/^ \\zssecond$/",
+ \ "third\tXfile1\t/^ \\zsthird$/"],
+ \ 'Xtags')
+ call writefile([' first', ' second', ' third'], 'Xfile1')
+
+ for t_arg in ['-t second', '-tsecond']
+ if RunVim(before, after, '-t second')
+ call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'), t_arg)
+ call delete('Xtestout')
+ endif
+ endfor
+
+ call delete('Xtags')
+ call delete('Xfile1')
+endfunc
+
" Test the '-T' argument which sets the 'term' option.
func Test_T_arg()
throw 'skipped: Nvim does not support "-T" argument'
@@ -735,6 +890,66 @@ func Test_x_arg()
call delete('Xtest_x_arg')
endfunc
+" Test for --not-a-term avoiding escape codes.
+func Test_not_a_term()
+ CheckUnix
+ CheckNotGui
+
+ if &shellredir =~ '%s'
+ let redir = printf(&shellredir, 'Xvimout')
+ else
+ let redir = &shellredir .. ' Xvimout'
+ endif
+
+ " Without --not-a-term there are a few escape sequences.
+ " This will take 2 seconds because of the missing --not-a-term
+ let cmd = GetVimProg() .. ' --cmd quit ' .. redir
+ exe "silent !" . cmd
+ " call assert_match("\<Esc>", readfile('Xvimout')->join())
+ call assert_match("\<Esc>", join(readfile('Xvimout')))
+ call delete('Xvimout')
+
+ " With --not-a-term there are no escape sequences.
+ let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir
+ exe "silent !" . cmd
+ " call assert_notmatch("\<Esc>", readfile('Xvimout')->join())
+ call assert_notmatch("\<Esc>", join(readfile('Xvimout')))
+ call delete('Xvimout')
+endfunc
+
+
+" Test for the "-w scriptout" argument
+func Test_w_arg()
+ " Can't catch the output of gvim.
+ CheckNotGui
+
+ call writefile(["iVim Editor\<Esc>:q!\<CR>"], 'Xscriptin', 'b')
+ if RunVim([], [], '-s Xscriptin -w Xscriptout')
+ call assert_equal(["iVim Editor\e:q!\r"], readfile('Xscriptout'))
+ call delete('Xscriptout')
+ endif
+ call delete('Xscriptin')
+
+ " Test for failing to open the script output file. This test works only when
+ " the language is English.
+ if !has('win32') && (v:lang == "C" || v:lang =~ '^[Ee]n')
+ call mkdir("Xdir")
+ let m = system(GetVimCommand() .. " -w Xdir")
+ call assert_equal("Cannot open for script output: \"Xdir\"\n", m)
+ call delete("Xdir", 'rf')
+ endif
+
+ " A number argument sets the 'window' option
+ call writefile(["iwindow \<C-R>=&window\<CR>\<Esc>:wq! Xresult\<CR>"], 'Xscriptin', 'b')
+ for w_arg in ['-w 17', '-w17']
+ if RunVim([], [], '-s Xscriptin ' .. w_arg)
+ call assert_equal(["window 17"], readfile('Xresult'), w_arg)
+ call delete('Xresult')
+ endif
+ endfor
+ call delete('Xscriptin')
+endfunc
+
" Test starting vim with various names: vim, ex, view, evim, etc.
func Test_progname()
CheckUnix
@@ -777,17 +992,12 @@ func Test_progname()
let prognames = ['nvim']
for progname in prognames
- if empty($DISPLAY)
- if progname =~# 'g'
- " Can't run gvim, gview (etc.) if $DISPLAY is not setup.
- continue
- endif
- if has('gui') && (progname ==# 'evim' || progname ==# 'eview')
- " evim or eview will start the GUI if there is gui support.
- " So don't try to start them either if $DISPLAY is not setup.
- continue
- endif
- endif
+ let run_with_gui = (progname =~# 'g') || (has('gui') && (progname ==# 'evim' || progname ==# 'eview'))
+
+ if empty($DISPLAY) && run_with_gui
+ " Can't run gvim, gview (etc.) if $DISPLAY is not setup.
+ continue
+ endif
exe 'silent !ln -s -f ' ..exepath(GetVimProg()) .. ' Xprogname/' .. progname
@@ -801,7 +1011,15 @@ func Test_progname()
if progname =~# 'g' && !has('gui')
call assert_equal("E25: GUI cannot be used: Not enabled at compile time\n", stdout_stderr, progname)
else
- call assert_equal('', stdout_stderr, progname)
+ " GUI motif can output some warnings like this:
+ " Warning:
+ " Name: subMenu
+ " Class: XmCascadeButton
+ " Illegal mnemonic character; Could not convert X KEYSYM to a keycode
+ " So don't check that stderr is empty with GUI Motif.
+ if run_with_gui && !has('gui_motif')
+ call assert_equal('', stdout_stderr, progname)
+ endif
call assert_equal(expectations[progname], readfile('Xprogname_out'), progname)
endif
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index 4e38f7ebd8..48b7b4f2f1 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -1,12 +1,12 @@
" Test 'statusline'
"
" Not tested yet:
-" %a
" %N
" %T
" %X
source view_util.vim
+source check.vim
source term_util.vim
func s:get_statusline()
@@ -61,7 +61,19 @@ func Test_statusline_will_be_disabled_with_error()
endfunc
func Test_statusline()
- new Xstatusline
+ CheckFeature quickfix
+
+ " %a: Argument list ({current} of {max})
+ set statusline=%a
+ call assert_match('^\s*$', s:get_statusline())
+ arglocal a1 a2
+ rewind
+ call assert_match('^ (1 of 2)\s*$', s:get_statusline())
+ next
+ call assert_match('^ (2 of 2)\s*$', s:get_statusline())
+ e Xstatusline
+ call assert_match('^ ((2) of 2)\s*$', s:get_statusline())
+
only
set laststatus=2
set splitbelow
@@ -428,6 +440,27 @@ func Test_statusline_removed_group()
call delete('XTest_statusline')
endfunc
+func Test_statusline_using_mode()
+ CheckScreendump
+
+ let lines =<< trim END
+ set laststatus=2
+ let &statusline = '-%{mode()}-'
+ END
+ call writefile(lines, 'XTest_statusline')
+
+ let buf = RunVimInTerminal('-S XTest_statusline', {'rows': 5, 'cols': 50})
+ call VerifyScreenDump(buf, 'Test_statusline_mode_1', {})
+
+ call term_sendkeys(buf, ":")
+ call VerifyScreenDump(buf, 'Test_statusline_mode_2', {})
+
+ " clean up
+ call term_sendkeys(buf, "\<CR>")
+ call StopVimInTerminal(buf)
+ call delete('XTest_statusline')
+endfunc
+
func Test_statusline_after_split_vsplit()
only
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 2a27f7a3a1..cc3bfe9f7f 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -746,3 +746,12 @@ func Test_sub_beyond_end()
call assert_equal('#', getline(1))
bwipe!
endfunc
+
+func Test_submatch_list_concatenate()
+ let pat = 'A\(.\)'
+ let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])}
+ " call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]")
+ call assert_equal(substitute('A1', pat, Rep, ''), "[['A1'], ['1']]")
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 4cf0e983b0..66cb0bbe22 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -30,7 +30,7 @@ func Test_syn_iskeyword()
\ 'CREATE TABLE FOOBAR(',
\ ' DLTD_BY VARCHAR2(100)',
\ ');',
- \ ''])
+ \ ''])
syntax on
set ft=sql
@@ -521,7 +521,7 @@ func Test_synstack_synIDtrans()
norm f/
call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
- call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
+ call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
norm fA
call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim
index 9cf8690d57..6bbe714d19 100644
--- a/src/nvim/testdir/test_system.vim
+++ b/src/nvim/testdir/test_system.vim
@@ -93,7 +93,6 @@ function! Test_system_exmode()
endfunc
func Test_system_with_shell_quote()
- throw 'skipped: enable after porting method patches'
CheckMSWindows
call mkdir('Xdir with spaces', 'p')
@@ -122,7 +121,8 @@ func Test_system_with_shell_quote()
let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote)
try
- let out = 'echo 123'->system()
+ " let out = 'echo 123'->system()
+ let out = system('echo 123')
catch
call assert_report(printf('%s: %s', msg, v:exception))
continue
diff --git a/src/nvim/testdir/test_tab.vim b/src/nvim/testdir/test_tab.vim
index b847dbd962..b8e8dfe062 100644
--- a/src/nvim/testdir/test_tab.vim
+++ b/src/nvim/testdir/test_tab.vim
@@ -1,3 +1,4 @@
+" Various tests for inserting a Tab.
" Tests for "r<Tab>" with 'smarttab' and 'expandtab' set/not set.
" Also test that dv_ works correctly
@@ -43,3 +44,47 @@ func Test_smarttab()
enew!
set expandtab& smartindent& copyindent& ts& sw& sts&
endfunc
+
+func Test_softtabstop()
+ new
+ set sts=0 sw=0
+ exe "normal ix\<Tab>x\<Esc>"
+ call assert_equal("x\tx", getline(1))
+
+ call setline(1, '')
+ set sts=4
+ exe "normal ix\<Tab>x\<Esc>"
+ call assert_equal("x x", getline(1))
+
+ call setline(1, '')
+ set sts=-1 sw=4
+ exe "normal ix\<Tab>x\<Esc>"
+ call assert_equal("x x", getline(1))
+
+ call setline(1, 'x ')
+ set sts=0 sw=0 backspace=start
+ exe "normal A\<BS>x\<Esc>"
+ call assert_equal("x x", getline(1))
+
+ call setline(1, 'x ')
+ set sts=4
+ exe "normal A\<BS>x\<Esc>"
+ call assert_equal("x x", getline(1))
+
+ call setline(1, 'x ')
+ set sts=-1 sw=4
+ exe "normal A\<BS>x\<Esc>"
+ call assert_equal("x x", getline(1))
+
+ call setline(1, 'x')
+ set sts=-1 sw=0 smarttab
+ exe "normal I\<Tab>\<Esc>"
+ call assert_equal("\tx", getline(1))
+
+ call setline(1, 'x')
+ exe "normal I\<Tab>\<BS>\<Esc>"
+ call assert_equal("x", getline(1))
+
+ set sts=0 sw=0 backspace& nosmarttab
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 2b6a89647e..b261b96c3b 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -142,9 +142,6 @@ endfunc
" Test autocommands
function Test_tabpage_with_autocmd()
- if !has('autocmd')
- return
- endif
command -nargs=1 -bar C :call add(s:li, '=== ' . <q-args> . ' ===')|<args>
augroup TestTabpageGroup
au!
diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim
index 242aa3a235..ffc1d63b90 100644
--- a/src/nvim/testdir/test_tagfunc.vim
+++ b/src/nvim/testdir/test_tagfunc.vim
@@ -43,12 +43,24 @@ func Test_tagfunc()
call assert_equal('one', g:tagfunc_args[0])
call assert_equal('c', g:tagfunc_args[1])
+ let g:tagfunc_args=[]
+ execute "tag /foo$"
+ call assert_equal('foo$', g:tagfunc_args[0])
+ call assert_equal('r', g:tagfunc_args[1])
+
set cpt=t
let g:tagfunc_args=[]
execute "normal! i\<c-n>\<c-y>"
- call assert_equal('ci', g:tagfunc_args[1])
+ call assert_equal('\<\k\k', g:tagfunc_args[0])
+ call assert_equal('cir', g:tagfunc_args[1])
call assert_equal('nothing1', getline('.')[0:7])
+ let g:tagfunc_args=[]
+ execute "normal! ono\<c-n>\<c-n>\<c-y>"
+ call assert_equal('\<no', g:tagfunc_args[0])
+ call assert_equal('cir', g:tagfunc_args[1])
+ call assert_equal('nothing2', getline('.')[0:7])
+
func BadTagFunc1(...)
return 0
endfunc
@@ -81,4 +93,28 @@ func Test_tagfunc()
call delete('Xfile1')
endfunc
+" Test for modifying the tag stack from a tag function and jumping to a tag
+" from a tag function
+func Test_tagfunc_settagstack()
+ func Mytagfunc1(pat, flags, info)
+ call settagstack(1, {'tagname' : 'mytag', 'from' : [0, 10, 1, 0]})
+ return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}]
+ endfunc
+ set tagfunc=Mytagfunc1
+ call writefile([''], 'Xtest')
+ call assert_fails('tag xyz', 'E986:')
+
+ func Mytagfunc2(pat, flags, info)
+ tag test_tag
+ return [{'name' : 'mytag', 'filename' : 'Xtest', 'cmd' : '1'}]
+ endfunc
+ set tagfunc=Mytagfunc2
+ call assert_fails('tag xyz', 'E986:')
+
+ call delete('Xtest')
+ set tagfunc&
+ delfunc Mytagfunc1
+ delfunc Mytagfunc2
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 7057cdefb2..9f02af7d8e 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -609,6 +609,295 @@ func Test_tagline()
set tags&
endfunc
+" Test for expanding environment variable in a tag file name
+func Test_tag_envvar()
+ call writefile(["Func1\t$FOO\t/^Func1/"], 'Xtags')
+ set tags=Xtags
+
+ let $FOO='TagTestEnv'
+
+ let caught_exception = v:false
+ try
+ tag Func1
+ catch /E429:/
+ call assert_match('E429:.*"TagTestEnv".*', v:exception)
+ let caught_exception = v:true
+ endtry
+ call assert_true(caught_exception)
+
+ set tags&
+ call delete('Xtags')
+ unlet $FOO
+endfunc
+
+" Test for :ptag
+func Test_ptag()
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "second\tXfile1\t2",
+ \ "third\tXfile1\t3",],
+ \ 'Xtags')
+ set tags=Xtags
+ call writefile(['first', 'second', 'third'], 'Xfile1')
+
+ enew | only
+ ptag third
+ call assert_equal(2, winnr())
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, getwinvar(1, '&previewwindow'))
+ call assert_equal(0, getwinvar(2, '&previewwindow'))
+ wincmd w
+ call assert_equal(3, line('.'))
+
+ " jump to the tag again
+ ptag third
+ call assert_equal(3, line('.'))
+
+ " close the preview window
+ pclose
+ call assert_equal(1, winnr('$'))
+
+ call delete('Xfile1')
+ call delete('Xtags')
+ set tags&
+endfunc
+
+" Tests for guessing the tag location
+func Test_tag_guess()
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "func1\tXfoo\t/^int func1(int x)/",
+ \ "func2\tXfoo\t/^int func2(int y)/",
+ \ "func3\tXfoo\t/^func3/",
+ \ "func4\tXfoo\t/^func4/"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+
+ int FUNC1 (int x) { }
+ int
+ func2 (int y) { }
+ int * func3 () { }
+
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ let v:statusmsg = ''
+ ta func1
+ call assert_match('E435:', v:statusmsg)
+ call assert_equal(2, line('.'))
+ let v:statusmsg = ''
+ ta func2
+ call assert_match('E435:', v:statusmsg)
+ call assert_equal(4, line('.'))
+ let v:statusmsg = ''
+ ta func3
+ call assert_match('E435:', v:statusmsg)
+ call assert_equal(5, line('.'))
+ call assert_fails('ta func4', 'E434:')
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+endfunc
+
+" Test for an unsorted tags file
+func Test_tag_sort()
+ call writefile([
+ \ "first\tXfoo\t1",
+ \ "ten\tXfoo\t3",
+ \ "six\tXfoo\t2"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int six() {}
+ int ten() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ call assert_fails('tag first', 'E432:')
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for an unsorted tags file
+func Test_tag_fold()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "!_TAG_FILE_SORTED\t2\t/0=unsorted, 1=sorted, 2=foldcase/",
+ \ "first\tXfoo\t1",
+ \ "second\tXfoo\t2",
+ \ "third\tXfoo\t3"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int second() {}
+ int third() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew
+ tag second
+ call assert_equal('Xfoo', bufname(''))
+ call assert_equal(2, line('.'))
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for the :ltag command
+func Test_ltag()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo\t1",
+ \ "second\tXfoo\t/^int second() {}$/",
+ \ "third\tXfoo\t3"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int second() {}
+ int third() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew
+ call setloclist(0, [], 'f')
+ ltag third
+ call assert_equal('Xfoo', bufname(''))
+ call assert_equal(3, line('.'))
+ call assert_equal([{'lnum': 3, 'bufnr': bufnr('Xfoo'), 'col': 0,
+ \ 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '',
+ \ 'module': '', 'text': 'third'}], getloclist(0))
+
+ ltag second
+ call assert_equal(2, line('.'))
+ call assert_equal([{'lnum': 0, 'bufnr': bufnr('Xfoo'), 'col': 0,
+ \ 'pattern': '^\Vint second() {}\$', 'valid': 1, 'vcol': 0, 'nr': 0,
+ \ 'type': '', 'module': '', 'text': 'second'}], getloclist(0))
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for setting the last search pattern to the tag search pattern
+" when cpoptions has 't'
+func Test_tag_last_search_pat()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo\t/^int first() {}/",
+ \ "second\tXfoo\t/^int second() {}/",
+ \ "third\tXfoo\t/^int third() {}/"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int second() {}
+ int third() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ enew
+ let save_cpo = &cpo
+ set cpo+=t
+ let @/ = ''
+ tag second
+ call assert_equal('^int second() {}', @/)
+ let &cpo = save_cpo
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for jumping to a tag when the tag stack is full
+func Test_tag_stack_full()
+ let l = []
+ for i in range(10, 31)
+ let l += ["var" .. i .. "\tXfoo\t/^int var" .. i .. ";$/"]
+ endfor
+ call writefile(l, 'Xtags')
+ set tags=Xtags
+
+ let l = []
+ for i in range(10, 31)
+ let l += ["int var" .. i .. ";"]
+ endfor
+ call writefile(l, 'Xfoo')
+
+ enew
+ for i in range(10, 30)
+ exe "tag var" .. i
+ endfor
+ let l = gettagstack()
+ call assert_equal(20, l.length)
+ call assert_equal('var11', l.items[0].tagname)
+ tag var31
+ let l = gettagstack()
+ call assert_equal('var12', l.items[0].tagname)
+ call assert_equal('var31', l.items[19].tagname)
+
+ " Jump from the top of the stack
+ call assert_fails('tag', 'E556:')
+
+ " Pop from an unsaved buffer
+ enew!
+ call append(1, "sample text")
+ call assert_fails('pop', 'E37:')
+ call assert_equal(21, gettagstack().curidx)
+ enew!
+
+ " Pop all the entries in the tag stack
+ call assert_fails('30pop', 'E555:')
+
+ " Pop the tag stack when it is empty
+ call settagstack(1, {'items' : []})
+ call assert_fails('pop', 'E73:')
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
+" Test for browsing multiple matching tags
+func Test_tag_multimatch()
+ call writefile([
+ \ "!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "first\tXfoo\t1",
+ \ "first\tXfoo\t2",
+ \ "first\tXfoo\t3"],
+ \ 'Xtags')
+ set tags=Xtags
+ let code =<< trim [CODE]
+ int first() {}
+ int first() {}
+ int first() {}
+ [CODE]
+ call writefile(code, 'Xfoo')
+
+ tag first
+ tlast
+ call assert_equal(3, line('.'))
+ call assert_fails('tnext', 'E428:')
+ tfirst
+ call assert_equal(1, line('.'))
+ call assert_fails('tprev', 'E425:')
+
+ call delete('Xtags')
+ call delete('Xfoo')
+ set tags&
+ %bwipe
+endfunc
+
" Test for the 'taglength' option
func Test_tag_length()
set tags=Xtags
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index d4ff42fd68..e830813081 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -126,3 +126,99 @@ func Test_tagsfile_without_trailing_newline()
call delete('Xtags')
set tags&
endfunc
+
+" Test for ignoring comments in a tags file
+func Test_tagfile_ignore_comments()
+ call writefile([
+ \ "!_TAG_PROGRAM_NAME /Test tags generator/",
+ \ "FBar\tXfoo\t2" .. ';"' .. "\textrafield\tf",
+ \ "!_TAG_FILE_FORMAT 2 /extended format/",
+ \ ], 'Xtags')
+ set tags=Xtags
+
+ let l = taglist('.*')
+ call assert_equal(1, len(l))
+ call assert_equal('FBar', l[0].name)
+
+ set tags&
+ call delete('Xtags')
+endfunc
+
+" Test for using an excmd in a tags file to position the cursor (instead of a
+" search pattern or a line number)
+func Test_tagfile_excmd()
+ call writefile([
+ \ "vFoo\tXfoo\tcall cursor(3, 4)" .. '|;"' .. "\tv",
+ \ ], 'Xtags')
+ set tags=Xtags
+
+ let l = taglist('.*')
+ call assert_equal([{
+ \ 'cmd' : 'call cursor(3, 4)',
+ \ 'static' : 0,
+ \ 'name' : 'vFoo',
+ \ 'kind' : 'v',
+ \ 'filename' : 'Xfoo'}], l)
+
+ set tags&
+ call delete('Xtags')
+endfunc
+
+" Test for duplicate fields in a tag in a tags file
+func Test_duplicate_field()
+ call writefile([
+ \ "vFoo\tXfoo\t4" .. ';"' .. "\ttypename:int\ttypename:int\tv",
+ \ ], 'Xtags')
+ set tags=Xtags
+
+ let l = taglist('.*')
+ call assert_equal([{
+ \ 'cmd' : '4',
+ \ 'static' : 0,
+ \ 'name' : 'vFoo',
+ \ 'kind' : 'v',
+ \ 'typename' : 'int',
+ \ 'filename' : 'Xfoo'}], l)
+
+ set tags&
+ call delete('Xtags')
+endfunc
+
+" Test for tag address with ;
+func Test_tag_addr_with_semicolon()
+ call writefile([
+ \ "Func1\tXfoo\t6;/^Func1/" .. ';"' .. "\tf"
+ \ ], 'Xtags')
+ set tags=Xtags
+
+ let l = taglist('.*')
+ call assert_equal([{
+ \ 'cmd' : '6;/^Func1/',
+ \ 'static' : 0,
+ \ 'name' : 'Func1',
+ \ 'kind' : 'f',
+ \ 'filename' : 'Xfoo'}], l)
+
+ set tags&
+ call delete('Xtags')
+endfunc
+
+" Test for format error in a tags file
+func Test_format_error()
+ call writefile(['vFoo-Xfoo-4'], 'Xtags')
+ set tags=Xtags
+
+ let caught_exception = v:false
+ try
+ let l = taglist('.*')
+ catch /E431:/
+ " test succeeded
+ let caught_exception = v:true
+ catch
+ call assert_report('Caught ' . v:exception . ' in ' . v:throwpoint)
+ endtry
+ call assert_true(caught_exception)
+
+ set tags&
+ call delete('Xtags')
+endfunc
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 13971a918d..ceaa5de92b 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -317,8 +317,8 @@ endfunc
" Test that the garbage collector isn't triggered if a timer callback invokes
" vgetc().
func Test_nocatch_garbage_collect()
- " skipped: Nvim does not support test_garbagecollect_soon(), test_override()
- return
+ CheckFunction test_garbagecollect_soon
+ CheckFunction test_override
" 'uptimetime. must be bigger than the timer timeout
set ut=200
call test_garbagecollect_soon()
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 3b66071d6d..54caed3983 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -3,6 +3,8 @@
" undo-able pieces. Do that by setting 'undolevels'.
" Also tests :earlier and :later.
+source check.vim
+
func Test_undotree()
new
@@ -135,7 +137,7 @@ func BackOne(expected)
endfunc
func Test_undo_del_chars()
- throw 'skipped: Nvim does not support test_settime()'
+ CheckFunction test_settime
" Setup a buffer without creating undo entries
new
@@ -330,7 +332,7 @@ func Test_insert_expr()
endfunc
func Test_undofile_earlier()
- throw 'skipped: Nvim does not support test_settime()'
+ CheckFunction test_settime
let t0 = localtime() - 43200
call test_settime(t0)
diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim
new file mode 100644
index 0000000000..2fbf130345
--- /dev/null
+++ b/src/nvim/testdir/test_vartabs.vim
@@ -0,0 +1,381 @@
+" Test for variable tabstops
+
+if !has("vartabs")
+ finish
+endif
+
+source view_util.vim
+
+func s:compare_lines(expect, actual)
+ call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
+endfunc
+
+func Test_vartabs()
+ new
+ %d
+
+ " Test normal operation of tabstops ...
+ set ts=4
+ call setline(1, join(split('aaaaa', '\zs'), "\t"))
+ retab 8
+ let expect = "a a\<tab>a a\<tab>a"
+ call assert_equal(expect, getline(1))
+
+ " ... and softtabstops
+ set ts=8 sts=6
+ exe "norm! Sb\<tab>b\<tab>b\<tab>b\<tab>b"
+ let expect = "b b\<tab> b\<tab> b\<tab>b"
+ call assert_equal(expect, getline(1))
+
+ " Test variable tabstops.
+ set sts=0 vts=4,8,4,8
+ exe "norm! Sc\<tab>c\<tab>c\<tab>c\<tab>c\<tab>c"
+ retab 8
+ let expect = "c c\<tab> c\<tab>c\<tab>c\<tab>c"
+ call assert_equal(expect, getline(1))
+
+ set et vts=4,8,4,8
+ exe "norm! Sd\<tab>d\<tab>d\<tab>d\<tab>d\<tab>d"
+ let expect = "d d d d d d"
+ call assert_equal(expect, getline(1))
+
+ " Changing ts should have no effect if vts is in use.
+ call cursor(1, 1)
+ set ts=6
+ exe "norm! Se\<tab>e\<tab>e\<tab>e\<tab>e\<tab>e"
+ let expect = "e e e e e e"
+ call assert_equal(expect, getline(1))
+
+ " Clearing vts should revert to using ts.
+ set vts=
+ exe "norm! Sf\<tab>f\<tab>f\<tab>f\<tab>f\<tab>f"
+ let expect = "f f f f f f"
+ call assert_equal(expect, getline(1))
+
+ " Test variable softtabstops.
+ set noet ts=8 vsts=12,2,6
+ exe "norm! Sg\<tab>g\<tab>g\<tab>g\<tab>g\<tab>g"
+ let expect = "g\<tab> g g\<tab> g\<tab> g\<tab>g"
+ call assert_equal(expect, getline(1))
+
+ " Variable tabstops and softtabstops combined.
+ set vsts=6,12,8 vts=4,6,8
+ exe "norm! Sh\<tab>h\<tab>h\<tab>h\<tab>h"
+ let expect = "h\<tab> h\<tab>\<tab>h\<tab>h\<tab>h"
+ call assert_equal(expect, getline(1))
+
+ " Retab with a single value, not using vts.
+ set ts=8 sts=0 vts= vsts=
+ exe "norm! Si\<tab>i\<tab>i\<tab>i\<tab>i"
+ retab 4
+ let expect = "i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i"
+ call assert_equal(expect, getline(1))
+
+ " Retab with a single value, using vts.
+ set ts=8 sts=0 vts=6 vsts=
+ exe "norm! Sj\<tab>j\<tab>j\<tab>j\<tab>j"
+ retab 4
+ let expect = "j\<tab> j\<tab>\<tab>j\<tab> j\<tab>\<tab>j"
+ call assert_equal(expect, getline(1))
+
+ " Retab with multiple values, not using vts.
+ set ts=6 sts=0 vts= vsts=
+ exe "norm! Sk\<tab>k\<tab>k\<tab>k\<tab>k\<tab>k"
+ retab 4,8
+ let expect = "k\<tab> k\<tab>k k\<tab> k\<tab> k"
+ call assert_equal(expect, getline(1))
+
+ " Retab with multiple values, using vts.
+ set ts=8 sts=0 vts=6 vsts=
+ exe "norm! Sl\<tab>l\<tab>l\<tab>l\<tab>l\<tab>l"
+ retab 4,8
+ let expect = "l\<tab> l\<tab>l l\<tab> l\<tab> l"
+ call assert_equal(expect, getline(1))
+
+ " Check that global and local values are set.
+ set ts=4 vts=6 sts=8 vsts=10
+ call assert_equal(&ts, 4)
+ call assert_equal(&vts, '6')
+ call assert_equal(&sts, 8)
+ call assert_equal(&vsts, '10')
+ new
+ call assert_equal(&ts, 4)
+ call assert_equal(&vts, '6')
+ call assert_equal(&sts, 8)
+ call assert_equal(&vsts, '10')
+ bwipeout!
+
+ " Check that local values only are set.
+ setlocal ts=5 vts=7 sts=9 vsts=11
+ call assert_equal(&ts, 5)
+ call assert_equal(&vts, '7')
+ call assert_equal(&sts, 9)
+ call assert_equal(&vsts, '11')
+ new
+ call assert_equal(&ts, 4)
+ call assert_equal(&vts, '6')
+ call assert_equal(&sts, 8)
+ call assert_equal(&vsts, '10')
+ bwipeout!
+
+ " Check that global values only are set.
+ setglobal ts=6 vts=8 sts=10 vsts=12
+ call assert_equal(&ts, 5)
+ call assert_equal(&vts, '7')
+ call assert_equal(&sts, 9)
+ call assert_equal(&vsts, '11')
+ new
+ call assert_equal(&ts, 6)
+ call assert_equal(&vts, '8')
+ call assert_equal(&sts, 10)
+ call assert_equal(&vsts, '12')
+ bwipeout!
+
+ set ts& vts& sts& vsts& et&
+ bwipeout!
+endfunc
+
+func! Test_vartabs_breakindent()
+ if !exists("+breakindent")
+ return
+ endif
+ new
+ %d
+
+ " Test normal operation of tabstops ...
+ set ts=4
+ call setline(1, join(split('aaaaa', '\zs'), "\t"))
+ retab 8
+ let expect = "a a\<tab>a a\<tab>a"
+ call assert_equal(expect, getline(1))
+
+ " ... and softtabstops
+ set ts=8 sts=6
+ exe "norm! Sb\<tab>b\<tab>b\<tab>b\<tab>b"
+ let expect = "b b\<tab> b\<tab> b\<tab>b"
+ call assert_equal(expect, getline(1))
+
+ " Test variable tabstops.
+ set sts=0 vts=4,8,4,8
+ exe "norm! Sc\<tab>c\<tab>c\<tab>c\<tab>c\<tab>c"
+ retab 8
+ let expect = "c c\<tab> c\<tab>c\<tab>c\<tab>c"
+ call assert_equal(expect, getline(1))
+
+ set et vts=4,8,4,8
+ exe "norm! Sd\<tab>d\<tab>d\<tab>d\<tab>d\<tab>d"
+ let expect = "d d d d d d"
+ call assert_equal(expect, getline(1))
+
+ " Changing ts should have no effect if vts is in use.
+ call cursor(1, 1)
+ set ts=6
+ exe "norm! Se\<tab>e\<tab>e\<tab>e\<tab>e\<tab>e"
+ let expect = "e e e e e e"
+ call assert_equal(expect, getline(1))
+
+ " Clearing vts should revert to using ts.
+ set vts=
+ exe "norm! Sf\<tab>f\<tab>f\<tab>f\<tab>f\<tab>f"
+ let expect = "f f f f f f"
+ call assert_equal(expect, getline(1))
+
+ " Test variable softtabstops.
+ set noet ts=8 vsts=12,2,6
+ exe "norm! Sg\<tab>g\<tab>g\<tab>g\<tab>g\<tab>g"
+ let expect = "g\<tab> g g\<tab> g\<tab> g\<tab>g"
+ call assert_equal(expect, getline(1))
+
+ " Variable tabstops and softtabstops combined.
+ set vsts=6,12,8 vts=4,6,8
+ exe "norm! Sh\<tab>h\<tab>h\<tab>h\<tab>h"
+ let expect = "h\<tab> h\<tab>\<tab>h\<tab>h\<tab>h"
+ call assert_equal(expect, getline(1))
+
+ " Retab with a single value, not using vts.
+ set ts=8 sts=0 vts= vsts=
+ exe "norm! Si\<tab>i\<tab>i\<tab>i\<tab>i"
+ retab 4
+ let expect = "i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i"
+ call assert_equal(expect, getline(1))
+
+ " Retab with a single value, using vts.
+ set ts=8 sts=0 vts=6 vsts=
+ exe "norm! Sj\<tab>j\<tab>j\<tab>j\<tab>j"
+ retab 4
+ let expect = "j\<tab> j\<tab>\<tab>j\<tab> j\<tab>\<tab>j"
+ call assert_equal(expect, getline(1))
+
+ " Retab with multiple values, not using vts.
+ set ts=6 sts=0 vts= vsts=
+ exe "norm! Sk\<tab>k\<tab>k\<tab>k\<tab>k\<tab>k"
+ retab 4,8
+ let expect = "k\<tab> k\<tab>k k\<tab> k\<tab> k"
+ call assert_equal(expect, getline(1))
+
+ " Retab with multiple values, using vts.
+ set ts=8 sts=0 vts=6 vsts=
+ exe "norm! Sl\<tab>l\<tab>l\<tab>l\<tab>l\<tab>l"
+ retab 4,8
+ let expect = "l\<tab> l\<tab>l l\<tab> l\<tab> l"
+ call assert_equal(expect, getline(1))
+
+ " Check that global and local values are set.
+ set ts=4 vts=6 sts=8 vsts=10
+ call assert_equal(&ts, 4)
+ call assert_equal(&vts, '6')
+ call assert_equal(&sts, 8)
+ call assert_equal(&vsts, '10')
+ new
+ call assert_equal(&ts, 4)
+ call assert_equal(&vts, '6')
+ call assert_equal(&sts, 8)
+ call assert_equal(&vsts, '10')
+ bwipeout!
+
+ " Check that local values only are set.
+ setlocal ts=5 vts=7 sts=9 vsts=11
+ call assert_equal(&ts, 5)
+ call assert_equal(&vts, '7')
+ call assert_equal(&sts, 9)
+ call assert_equal(&vsts, '11')
+ new
+ call assert_equal(&ts, 4)
+ call assert_equal(&vts, '6')
+ call assert_equal(&sts, 8)
+ call assert_equal(&vsts, '10')
+ bwipeout!
+
+ " Check that global values only are set.
+ setglobal ts=6 vts=8 sts=10 vsts=12
+ call assert_equal(&ts, 5)
+ call assert_equal(&vts, '7')
+ call assert_equal(&sts, 9)
+ call assert_equal(&vsts, '11')
+ new
+ call assert_equal(&ts, 6)
+ call assert_equal(&vts, '8')
+ call assert_equal(&sts, 10)
+ call assert_equal(&vsts, '12')
+ bwipeout!
+
+ bwipeout!
+endfunc
+
+func Test_vartabs_linebreak()
+ if winwidth(0) < 40
+ return
+ endif
+ new
+ 40vnew
+ %d
+ setl linebreak vartabstop=10,20,30,40
+ call setline(1, "\tx\tx\tx\tx")
+
+ let expect = [' x ',
+ \ 'x x ',
+ \ 'x ']
+ let lines = ScreenLines([1, 3], winwidth(0))
+ call s:compare_lines(expect, lines)
+ setl list listchars=tab:>-
+ let expect = ['>---------x>------------------ ',
+ \ 'x>------------------x>------------------',
+ \ 'x ']
+ let lines = ScreenLines([1, 3], winwidth(0))
+ call s:compare_lines(expect, lines)
+ setl linebreak vartabstop=40
+ let expect = ['>---------------------------------------',
+ \ 'x>--------------------------------------',
+ \ 'x>--------------------------------------',
+ \ 'x>--------------------------------------',
+ \ 'x ']
+ let lines = ScreenLines([1, 5], winwidth(0))
+ call s:compare_lines(expect, lines)
+
+ " cleanup
+ bw!
+ bw!
+ set nolist listchars&vim
+endfunc
+
+func Test_vartabs_shiftwidth()
+ "return
+ if winwidth(0) < 40
+ return
+ endif
+ new
+ 40vnew
+ %d
+" setl varsofttabstop=10,20,30,40
+ setl shiftwidth=0 vartabstop=10,20,30,40
+ call setline(1, "x")
+
+ " Check without any change.
+ let expect = ['x ']
+ let lines = ScreenLines(1, winwidth(0))
+ call s:compare_lines(expect, lines)
+ " Test 1:
+ " shiftwidth depends on the indent, first check with cursor at the end of the
+ " line (which is the same as the start of the line, since there is only one
+ " character).
+ norm! $>>
+ let expect1 = [' x ']
+ let lines = ScreenLines(1, winwidth(0))
+ call s:compare_lines(expect1, lines)
+ call assert_equal(10, shiftwidth())
+ call assert_equal(10, shiftwidth(1))
+ call assert_equal(20, shiftwidth(virtcol('.')))
+ norm! $>>
+ let expect2 = [' x ', '~ ']
+ let lines = ScreenLines([1, 2], winwidth(0))
+ call s:compare_lines(expect2, lines)
+ call assert_equal(20, shiftwidth(virtcol('.')-2))
+ call assert_equal(30, shiftwidth(virtcol('.')))
+ norm! $>>
+ let expect3 = [' ', ' x ', '~ ']
+ let lines = ScreenLines([1, 3], winwidth(0))
+ call s:compare_lines(expect3, lines)
+ call assert_equal(30, shiftwidth(virtcol('.')-2))
+ call assert_equal(40, shiftwidth(virtcol('.')))
+ norm! $>>
+ let expect4 = [' ', ' ', ' x ']
+ let lines = ScreenLines([1, 3], winwidth(0))
+ call assert_equal(40, shiftwidth(virtcol('.')))
+ call s:compare_lines(expect4, lines)
+
+ " Test 2: Put the cursor at the first column, result should be the same
+ call setline(1, "x")
+ norm! 0>>
+ let lines = ScreenLines(1, winwidth(0))
+ call s:compare_lines(expect1, lines)
+ norm! 0>>
+ let lines = ScreenLines([1, 2], winwidth(0))
+ call s:compare_lines(expect2, lines)
+ norm! 0>>
+ let lines = ScreenLines([1, 3], winwidth(0))
+ call s:compare_lines(expect3, lines)
+ norm! 0>>
+ let lines = ScreenLines([1, 3], winwidth(0))
+ call s:compare_lines(expect4, lines)
+
+ " cleanup
+ bw!
+ bw!
+endfunc
+
+func Test_vartabs_failures()
+ call assert_fails('set vts=8,')
+ call assert_fails('set vsts=8,')
+ call assert_fails('set vts=8,,8')
+ call assert_fails('set vsts=8,,8')
+ call assert_fails('set vts=8,,8,')
+ call assert_fails('set vsts=8,,8,')
+ call assert_fails('set vts=,8')
+ call assert_fails('set vsts=,8')
+endfunc
+
+func Test_vartabs_reset()
+ set vts=8
+ set all&
+ call assert_equal('', &vts)
+endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 7f50894f66..73c7960579 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -255,7 +255,6 @@ func TriggerTheProblem()
endfunc
func Test_visual_mode_reset()
- set belloff=all
enew
let g:msg = "Everything's fine."
enew
@@ -268,7 +267,6 @@ func Test_visual_mode_reset()
exe "normal! GV:call TriggerTheProblem()\<CR>"
call assert_equal("Everything's fine.", g:msg)
- set belloff&
endfunc
func Test_Visual_word_textobject()
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 969b75d424..a522705238 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -550,16 +550,29 @@ endfunc
func Test_winrestcmd()
2split
3vsplit
- let a = winrestcmd()
+ let restcmd = winrestcmd()
call assert_equal(2, winheight(0))
call assert_equal(3, winwidth(0))
wincmd =
call assert_notequal(2, winheight(0))
call assert_notequal(3, winwidth(0))
- exe a
+ exe restcmd
call assert_equal(2, winheight(0))
call assert_equal(3, winwidth(0))
only
+
+ wincmd v
+ wincmd s
+ wincmd v
+ redraw
+ let restcmd = winrestcmd()
+ wincmd _
+ wincmd |
+ exe restcmd
+ redraw
+ call assert_equal(restcmd, winrestcmd())
+
+ only
endfunc
function! Fun_RenewFile()
@@ -808,6 +821,55 @@ func Test_winnr()
only | tabonly
endfunc
+func Test_win_splitmove()
+ edit a
+ leftabove split b
+ leftabove vsplit c
+ leftabove split d
+ call assert_equal(0, win_splitmove(winnr(), winnr('l')))
+ call assert_equal(bufname(winbufnr(1)), 'c')
+ call assert_equal(bufname(winbufnr(2)), 'd')
+ call assert_equal(bufname(winbufnr(3)), 'b')
+ call assert_equal(bufname(winbufnr(4)), 'a')
+ call assert_equal(0, win_splitmove(winnr(), winnr('j'), {'vertical': 1}))
+ call assert_equal(0, win_splitmove(winnr(), winnr('j'), {'vertical': 1}))
+ call assert_equal(bufname(winbufnr(1)), 'c')
+ call assert_equal(bufname(winbufnr(2)), 'b')
+ call assert_equal(bufname(winbufnr(3)), 'd')
+ call assert_equal(bufname(winbufnr(4)), 'a')
+ call assert_equal(0, win_splitmove(winnr(), winnr('k'), {'vertical': 1}))
+ call assert_equal(bufname(winbufnr(1)), 'd')
+ call assert_equal(bufname(winbufnr(2)), 'c')
+ call assert_equal(bufname(winbufnr(3)), 'b')
+ call assert_equal(bufname(winbufnr(4)), 'a')
+ call assert_equal(0, win_splitmove(winnr(), winnr('j'), {'rightbelow': v:true}))
+ call assert_equal(bufname(winbufnr(1)), 'c')
+ call assert_equal(bufname(winbufnr(2)), 'b')
+ call assert_equal(bufname(winbufnr(3)), 'a')
+ call assert_equal(bufname(winbufnr(4)), 'd')
+ only | bd
+
+ call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
+ call assert_fails('call win_splitmove(123, winnr())', 'E957:')
+ call assert_fails('call win_splitmove(winnr(), winnr())', 'E957:')
+
+ tabnew
+ call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
+ tabclose
+endfunc
+
+func Test_floatwin_splitmove()
+ vsplit
+ let win2 = win_getid()
+ let popup_winid = nvim_open_win(0, 0, {'relative': 'win',
+ \ 'row': 3, 'col': 3, 'width': 12, 'height': 3})
+ call assert_fails('call win_splitmove(popup_winid, win2)', 'E957:')
+ call assert_fails('call win_splitmove(win2, popup_winid)', 'E957:')
+
+ call nvim_win_close(popup_winid, 1)
+ bwipe
+endfunc
+
func Test_window_resize()
" Vertical :resize (absolute, relative, min and max size).
vsplit
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index c6c09c80d7..94b6e9e39d 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -110,6 +110,7 @@ static char uilog_last_event[1024] = { 0 };
void ui_init(void)
{
default_grid.handle = 1;
+ msg_grid_adj.target = &default_grid;
ui_comp_init();
}
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 06efc9fa99..a2e9266fbb 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -127,6 +127,9 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
bool valid, bool on_top)
{
bool moved;
+
+ grid->comp_height = height;
+ grid->comp_width = width;
if (grid->comp_index != 0) {
moved = (row != grid->comp_row) || (col != grid->comp_col);
if (ui_comp_should_draw()) {
@@ -181,14 +184,12 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
insert_at--;
}
// not found: new grid
- kv_push(layers, grid);
- if (insert_at < kv_size(layers)-1) {
- for (size_t i = kv_size(layers)-1; i > insert_at; i--) {
- kv_A(layers, i) = kv_A(layers, i-1);
- kv_A(layers, i)->comp_index = i;
- }
- kv_A(layers, insert_at) = grid;
+ kv_pushp(layers);
+ for (size_t i = kv_size(layers)-1; i > insert_at; i--) {
+ kv_A(layers, i) = kv_A(layers, i-1);
+ kv_A(layers, i)->comp_index = i;
}
+ kv_A(layers, insert_at) = grid;
grid->comp_row = row;
grid->comp_col = col;
@@ -277,6 +278,9 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle,
// should configure all grids before entering win_update()
if (curgrid != &default_grid) {
size_t new_index = kv_size(layers)-1;
+ if (kv_A(layers, new_index) == &msg_grid) {
+ new_index--;
+ }
if (kv_A(layers, new_index) == &pum_grid) {
new_index--;
}
@@ -334,17 +338,25 @@ static void compose_line(Integer row, Integer startcol, Integer endcol,
sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row]
+(size_t)startcol];
+ int grid_width, grid_height;
while (col < endcol) {
int until = 0;
for (size_t i = 0; i < kv_size(layers); i++) {
ScreenGrid *g = kv_A(layers, i);
- if (g->comp_row > row || row >= g->comp_row + g->Rows
+ // compose_line may have been called after a shrinking operation but
+ // before the resize has actually been applied. Therefore, we need to
+ // first check to see if any grids have pending updates to width/height,
+ // to ensure that we don't accidentally put any characters into `linebuf`
+ // that have been invalidated.
+ grid_width = MIN(g->Columns, g->comp_width);
+ grid_height = MIN(g->Rows, g->comp_height);
+ if (g->comp_row > row || row >= g->comp_row + grid_height
|| g->comp_disabled) {
continue;
}
- if (g->comp_col <= col && col < g->comp_col+g->Columns) {
+ if (g->comp_col <= col && col < g->comp_col + grid_width) {
grid = g;
- until = g->comp_col+g->Columns;
+ until = g->comp_col + grid_width;
} else if (g->comp_col > col) {
until = MIN(until, g->comp_col);
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 8c2ae3bc35..f52850f6f3 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -594,13 +594,20 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
}
-# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
+// magic at start of undofile
+# define UF_START_MAGIC "Vim\237UnDo\345"
# define UF_START_MAGIC_LEN 9
-# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
-# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
-# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
-# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
-# define UF_VERSION 2 /* 2-byte undofile version number */
+// magic at start of header
+# define UF_HEADER_MAGIC 0x5fd0
+// magic after last header
+# define UF_HEADER_END_MAGIC 0xe7aa
+// magic at start of entry
+# define UF_ENTRY_MAGIC 0xf518
+// magic after last entry
+# define UF_ENTRY_END_MAGIC 0x3581
+
+// 2-byte undofile version number
+# define UF_VERSION 3
/* extra fields for header */
# define UF_LAST_SAVE_NR 1
@@ -847,6 +854,15 @@ static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
}
}
undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2);
+
+ // Write all extmark undo objects
+ for (size_t i = 0; i < kv_size(uhp->uh_extmark); i++) {
+ if (!serialize_extmark(bi, kv_A(uhp->uh_extmark, i))) {
+ return false;
+ }
+ }
+ undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2);
+
return true;
}
@@ -928,9 +944,95 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi,
return NULL;
}
+ // Unserialize all extmark undo information
+ ExtmarkUndoObject *extup;
+ kv_init(uhp->uh_extmark);
+
+ while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) {
+ bool error = false;
+ extup = unserialize_extmark(bi, &error, file_name);
+ if (error) {
+ kv_destroy(uhp->uh_extmark);
+ xfree(extup);
+ return NULL;
+ }
+ kv_push(uhp->uh_extmark, *extup);
+ xfree(extup);
+ }
+ if (c != UF_ENTRY_END_MAGIC) {
+ corruption_error("entry end", file_name);
+ u_free_uhp(uhp);
+ return NULL;
+ }
+
return uhp;
}
+static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup)
+{
+ if (extup.type == kExtmarkSplice) {
+ undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2);
+ undo_write_bytes(bi, (uintmax_t)extup.type, 4);
+ if (!undo_write(bi, (uint8_t *)&(extup.data.splice),
+ sizeof(ExtmarkSplice))) {
+ return false;
+ }
+ } else if (extup.type == kExtmarkMove) {
+ undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2);
+ undo_write_bytes(bi, (uintmax_t)extup.type, 4);
+ if (!undo_write(bi, (uint8_t *)&(extup.data.move), sizeof(ExtmarkMove))) {
+ return false;
+ }
+ }
+ // Note: We do not serialize ExtmarkSavePos information, since
+ // buffer marktrees are not retained when closing/reopening a file
+ return true;
+}
+
+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);
+ extup->type = type;
+ if (type == kExtmarkSplice) {
+ n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t);
+ buf = xcalloc(sizeof(uint8_t), n_elems);
+ 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);
+ buf = xcalloc(sizeof(uint8_t), n_elems);
+ if (!undo_read(bi, buf, n_elems)) {
+ goto error;
+ }
+ extup->data.move = *(ExtmarkMove *)buf;
+ } else {
+ goto error;
+ }
+
+ if (buf) {
+ xfree(buf);
+ }
+
+ return extup;
+
+error:
+ xfree(extup);
+ if (buf) {
+ xfree(buf);
+ }
+ *error = true;
+ return NULL;
+}
+
/// Serializes "uep".
///
/// @param bi The buffer information
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 00f49724b6..859f4353b3 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -605,6 +605,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
wp->w_vsep_width = 0;
win_config_float(wp, fconfig);
+ win_set_inner_size(wp);
wp->w_pos_changed = true;
redraw_later(wp, VALID);
return wp;
@@ -667,6 +668,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
}
bool change_external = fconfig.external != wp->w_float_config.external;
+ bool change_border = fconfig.border != wp->w_float_config.border;
+
wp->w_float_config = fconfig;
if (!ui_has(kUIMultigrid)) {
@@ -676,11 +679,18 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
win_set_inner_size(wp);
must_redraw = MAX(must_redraw, VALID);
+
wp->w_pos_changed = true;
- if (change_external) {
+ if (change_external || change_border) {
wp->w_hl_needs_update = true;
redraw_later(wp, NOT_VALID);
}
+
+ // changing border style while keeping border only requires redrawing border
+ if (fconfig.border) {
+ wp->w_redr_border = true;
+ redraw_later(wp, VALID);
+ }
}
void win_check_anchored_floats(win_T *win)
@@ -710,10 +720,10 @@ int win_fdccol_count(win_T *wp)
}
-static void ui_ext_win_position(win_T *wp)
+void ui_ext_win_position(win_T *wp)
{
if (!wp->w_floating) {
- ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow,
+ ui_call_win_pos(wp->w_grid_alloc.handle, wp->handle, wp->w_winrow,
wp->w_wincol, wp->w_width, wp->w_height);
return;
}
@@ -743,8 +753,8 @@ static void ui_ext_win_position(win_T *wp)
}
if (ui_has(kUIMultigrid)) {
String anchor = cstr_to_string(float_anchor_str[c.anchor]);
- ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle,
- row, col, c.focusable);
+ ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor,
+ grid->handle, row, col, c.focusable);
} else {
// TODO(bfredl): ideally, compositor should work like any multigrid UI
// and use standard win_pos events.
@@ -759,17 +769,17 @@ static void ui_ext_win_position(win_T *wp)
wp->w_wincol = comp_col;
bool valid = (wp->w_redr_type == 0);
bool on_top = (curwin == wp) || !curwin->w_floating;
- ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height,
- wp->w_width, valid, on_top);
- ui_check_cursor_grid(wp->w_grid.handle);
- wp->w_grid.focusable = wp->w_float_config.focusable;
+ ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,
+ wp->w_height_outer, wp->w_width_outer, valid, on_top);
+ ui_check_cursor_grid(wp->w_grid_alloc.handle);
+ wp->w_grid_alloc.focusable = wp->w_float_config.focusable;
if (!valid) {
- wp->w_grid.valid = false;
+ wp->w_grid_alloc.valid = false;
redraw_later(wp, NOT_VALID);
}
}
} else {
- ui_call_win_external_pos(wp->w_grid.handle, wp->handle);
+ ui_call_win_external_pos(wp->w_grid_alloc.handle, wp->handle);
}
}
@@ -784,260 +794,12 @@ void ui_ext_win_viewport(win_T *wp)
// interact with incomplete final line? Diff filler lines?
botline = wp->w_buffer->b_ml.ml_line_count;
}
- ui_call_win_viewport(wp->w_grid.handle, wp->handle, wp->w_topline-1,
+ ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline-1,
botline, wp->w_cursor.lnum-1, wp->w_cursor.col);
wp->w_viewport_invalid = false;
}
}
-static bool parse_float_anchor(String anchor, FloatAnchor *out)
-{
- if (anchor.size == 0) {
- *out = (FloatAnchor)0;
- }
- char *str = anchor.data;
- if (striequal(str, "NW")) {
- *out = 0; // NW is the default
- } else if (striequal(str, "NE")) {
- *out = kFloatAnchorEast;
- } else if (striequal(str, "SW")) {
- *out = kFloatAnchorSouth;
- } else if (striequal(str, "SE")) {
- *out = kFloatAnchorSouth | kFloatAnchorEast;
- } else {
- return false;
- }
- return true;
-}
-
-static bool parse_float_relative(String relative, FloatRelative *out)
-{
- char *str = relative.data;
- if (striequal(str, "editor")) {
- *out = kFloatRelativeEditor;
- } else if (striequal(str, "win")) {
- *out = kFloatRelativeWindow;
- } else if (striequal(str, "cursor")) {
- *out = kFloatRelativeCursor;
- } else {
- return false;
- }
- return true;
-}
-
-static bool parse_float_bufpos(Array bufpos, lpos_T *out)
-{
- if (bufpos.size != 2
- || bufpos.items[0].type != kObjectTypeInteger
- || bufpos.items[1].type != kObjectTypeInteger) {
- return false;
- }
- out->lnum = bufpos.items[0].data.integer;
- out->col = bufpos.items[1].data.integer;
- return true;
-}
-
-bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
- Error *err)
-{
- // TODO(bfredl): use a get/has_key interface instead and get rid of extra
- // flags
- bool has_row = false, has_col = false, has_relative = false;
- bool has_external = false, has_window = false;
- bool has_width = false, has_height = false;
- bool has_bufpos = false;
-
- for (size_t i = 0; i < config.size; i++) {
- char *key = config.items[i].key.data;
- Object val = config.items[i].value;
- if (!strcmp(key, "row")) {
- has_row = true;
- if (val.type == kObjectTypeInteger) {
- fconfig->row = val.data.integer;
- } else if (val.type == kObjectTypeFloat) {
- fconfig->row = val.data.floating;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'row' key must be Integer or Float");
- return false;
- }
- } else if (!strcmp(key, "col")) {
- has_col = true;
- if (val.type == kObjectTypeInteger) {
- fconfig->col = val.data.integer;
- } else if (val.type == kObjectTypeFloat) {
- fconfig->col = val.data.floating;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'col' key must be Integer or Float");
- return false;
- }
- } else if (strequal(key, "width")) {
- has_width = true;
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->width = val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'width' key must be a positive Integer");
- return false;
- }
- } else if (strequal(key, "height")) {
- has_height = true;
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->height= val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'height' key must be a positive Integer");
- return false;
- }
- } else if (!strcmp(key, "anchor")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'anchor' key must be String");
- return false;
- }
- if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'anchor' key");
- return false;
- }
- } else if (!strcmp(key, "relative")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'relative' key must be String");
- return false;
- }
- // ignore empty string, to match nvim_win_get_config
- if (val.data.string.size > 0) {
- has_relative = true;
- if (!parse_float_relative(val.data.string, &fconfig->relative)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'relative' key");
- return false;
- }
- }
- } else if (!strcmp(key, "win")) {
- has_window = true;
- if (val.type != kObjectTypeInteger
- && val.type != kObjectTypeWindow) {
- api_set_error(err, kErrorTypeValidation,
- "'win' key must be Integer or Window");
- return false;
- }
- fconfig->window = val.data.integer;
- } else if (!strcmp(key, "bufpos")) {
- if (val.type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation,
- "'bufpos' key must be Array");
- return false;
- }
- if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'bufpos' key");
- return false;
- }
- has_bufpos = true;
- } else if (!strcmp(key, "external")) {
- if (val.type == kObjectTypeInteger) {
- fconfig->external = val.data.integer;
- } else if (val.type == kObjectTypeBoolean) {
- fconfig->external = val.data.boolean;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'external' key must be Boolean");
- return false;
- }
- has_external = fconfig->external;
- } else if (!strcmp(key, "focusable")) {
- if (val.type == kObjectTypeInteger) {
- fconfig->focusable = val.data.integer;
- } else if (val.type == kObjectTypeBoolean) {
- fconfig->focusable = val.data.boolean;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'focusable' key must be Boolean");
- return false;
- }
- } else if (!strcmp(key, "style")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'style' key must be String");
- return false;
- }
- if (val.data.string.data[0] == NUL) {
- fconfig->style = kWinStyleUnused;
- } else if (striequal(val.data.string.data, "minimal")) {
- fconfig->style = kWinStyleMinimal;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'style' key");
- }
- } else {
- api_set_error(err, kErrorTypeValidation,
- "Invalid key '%s'", key);
- return false;
- }
- }
-
- if (has_window && !(has_relative
- && fconfig->relative == kFloatRelativeWindow)) {
- api_set_error(err, kErrorTypeValidation,
- "'win' key is only valid with relative='win'");
- return false;
- }
-
- if ((has_relative && fconfig->relative == kFloatRelativeWindow)
- && (!has_window || fconfig->window == 0)) {
- fconfig->window = curwin->handle;
- }
-
- if (has_window && !has_bufpos) {
- fconfig->bufpos.lnum = -1;
- }
-
- if (has_bufpos) {
- if (!has_row) {
- fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
- has_row = true;
- }
- if (!has_col) {
- fconfig->col = 0;
- has_col = true;
- }
- }
-
- if (has_relative && has_external) {
- api_set_error(err, kErrorTypeValidation,
- "Only one of 'relative' and 'external' must be used");
- return false;
- } else if (!reconf && !has_relative && !has_external) {
- api_set_error(err, kErrorTypeValidation,
- "One of 'relative' and 'external' must be used");
- return false;
- } else if (has_relative) {
- fconfig->external = false;
- }
-
- if (!reconf && !(has_height && has_width)) {
- api_set_error(err, kErrorTypeValidation,
- "Must specify 'width' and 'height'");
- return false;
- }
-
- if (fconfig->external && !ui_has(kUIMultigrid)) {
- api_set_error(err, kErrorTypeValidation,
- "UI doesn't support external windows");
- return false;
- }
-
- if (has_relative != has_row || has_row != has_col) {
- api_set_error(err, kErrorTypeValidation,
- "'relative' requires 'row'/'col' or 'bufpos'");
- return false;
- }
- return true;
-}
-
/*
* split the current window, implements CTRL-W s and :split
*
@@ -1539,6 +1301,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
p_wh = i;
}
+ if (!win_valid(oldwin)) {
+ return FAIL;
+ }
+
// Send the window positions to the UI
oldwin->w_pos_changed = true;
@@ -1619,6 +1385,23 @@ static void win_init_some(win_T *newp, win_T *oldp)
win_copy_options(oldp, newp);
}
+/// Return TRUE if "win" is floating window in the current tab page.
+///
+/// @param win window to check
+bool win_valid_floating(const win_T *win)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (win == NULL) {
+ return false;
+ }
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp == win) {
+ return wp->w_floating;
+ }
+ }
+ return false;
+}
/// Check if "win" is a pointer to an existing window in the current tabpage.
///
@@ -1936,12 +1719,12 @@ static void win_totop(int size, int flags)
}
if (curwin->w_floating) {
- ui_comp_remove_grid(&curwin->w_grid);
+ ui_comp_remove_grid(&curwin->w_grid_alloc);
if (ui_has(kUIMultigrid)) {
curwin->w_pos_changed = true;
} else {
// No longer a float, a non-multigrid UI shouldn't draw it as such
- ui_call_win_hide(curwin->w_grid.handle);
+ ui_call_win_hide(curwin->w_grid_alloc.handle);
win_free_grid(curwin, false);
}
} else {
@@ -2564,11 +2347,11 @@ int win_close(win_T *win, bool free_buf)
bool was_floating = win->w_floating;
if (ui_has(kUIMultigrid)) {
- ui_call_win_close(win->w_grid.handle);
+ ui_call_win_close(win->w_grid_alloc.handle);
}
if (win->w_floating) {
- ui_comp_remove_grid(&win->w_grid);
+ ui_comp_remove_grid(&win->w_grid_alloc);
if (win->w_float_config.external) {
for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
if (tp == curtab) {
@@ -3746,9 +3529,11 @@ void win_init_size(void)
{
firstwin->w_height = ROWS_AVAIL;
firstwin->w_height_inner = firstwin->w_height;
+ firstwin->w_height_outer = firstwin->w_height;
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
firstwin->w_width_inner = firstwin->w_width;
+ firstwin->w_width_outer = firstwin->w_width;
topframe->fr_width = Columns;
}
@@ -4114,7 +3899,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
win_remove(wp, old_curtab);
win_append(lastwin_nofloating(), wp);
} else {
- ui_comp_remove_grid(&wp->w_grid);
+ ui_comp_remove_grid(&wp->w_grid_alloc);
}
}
wp->w_pos_changed = true;
@@ -4709,7 +4494,7 @@ static win_T *win_alloc(win_T *after, int hidden)
new_wp->handle = ++last_win_id;
handle_register_window(new_wp);
- grid_assign_handle(&new_wp->w_grid);
+ grid_assign_handle(&new_wp->w_grid_alloc);
// Init w: variables.
new_wp->w_vars = tv_dict_alloc();
@@ -4833,15 +4618,14 @@ win_free (
void win_free_grid(win_T *wp, bool reinit)
{
- if (wp->w_grid.handle != 0 && ui_has(kUIMultigrid)) {
- ui_call_grid_destroy(wp->w_grid.handle);
- wp->w_grid.handle = 0;
+ if (wp->w_grid_alloc.handle != 0 && ui_has(kUIMultigrid)) {
+ ui_call_grid_destroy(wp->w_grid_alloc.handle);
}
- grid_free(&wp->w_grid);
+ grid_free(&wp->w_grid_alloc);
if (reinit) {
// if a float is turned into a split and back into a float, the grid
// data structure will be reused
- memset(&wp->w_grid, 0, sizeof(wp->w_grid));
+ memset(&wp->w_grid_alloc, 0, sizeof(wp->w_grid_alloc));
}
}
@@ -5502,7 +5286,7 @@ void win_setminheight(void)
// loop until there is a 'winminheight' that is possible
while (p_wmh > 0) {
const int room = Rows - p_ch;
- const int needed = frame_minheight(topframe, NULL);
+ const int needed = min_rows() - 1; // 1 was added for the cmdline
if (room >= needed) {
break;
}
@@ -5946,6 +5730,10 @@ void win_set_inner_size(win_T *wp)
if (wp->w_buffer->terminal) {
terminal_check_size(wp->w_buffer->terminal);
}
+
+ wp->w_border_adj = wp->w_floating && wp->w_float_config.border ? 1 : 0;
+ wp->w_height_outer = wp->w_height_inner + 2 * wp->w_border_adj;
+ wp->w_width_outer = wp->w_width_inner + 2 * wp->w_border_adj;
}
/// Set the width of a window.
@@ -5960,9 +5748,17 @@ void win_new_width(win_T *wp, int width)
void win_comp_scroll(win_T *wp)
{
+ const long old_w_p_scr = wp->w_p_scr;
+
wp->w_p_scr = wp->w_height / 2;
- if (wp->w_p_scr == 0)
+ if (wp->w_p_scr == 0) {
wp->w_p_scr = 1;
+ }
+ if (wp->w_p_scr != old_w_p_scr) {
+ // Used by "verbose set scroll".
+ wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_sid = SID_WINLAYOUT;
+ wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_lnum = 0;
+ }
}
/*
@@ -7074,11 +6870,11 @@ void get_framelayout(const frame_T *fr, list_T *l, bool outer)
void win_ui_flush(void)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_pos_changed && wp->w_grid.chars != NULL) {
+ if (wp->w_pos_changed && wp->w_grid_alloc.chars != NULL) {
if (tp == curtab) {
ui_ext_win_position(wp);
} else {
- ui_call_win_hide(wp->w_grid.handle);
+ ui_call_win_hide(wp->w_grid_alloc.handle);
}
wp->w_pos_changed = false;
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 437a1858f3..3db44f3f11 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -2087,4 +2087,67 @@ describe('API', function()
eq("", meths.exec("messages", true))
end)
end)
+
+
+ describe('nvim_open_term', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(100, 35)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {background = Screen.colors.Plum1};
+ [2] = {background = tonumber('0xffff40'), bg_indexed = true};
+ [3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')};
+ [4] = {bold = true, reverse = true, background = Screen.colors.Plum1};
+ })
+ end)
+
+ it('can batch process sequences', function()
+ local b = meths.create_buf(true,true)
+ meths.open_win(b, false, {width=79, height=31, row=1, col=1, relative='editor'})
+ local t = meths.open_term(b, {})
+
+ meths.chan_send(t, io.open("test/functional/fixtures/smile2.cat", "r"):read("*a"))
+ screen:expect{grid=[[
+ ^ |
+ {0:~}{1::smile }{0: }|
+ {0:~}{1: }{2:oooo$$$$$$$$$$$$oooo}{1: }{0: }|
+ {0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{0: }|
+ {0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:o$}{1: }{2:$$}{1: }{2:o$}{1: }{0: }|
+ {0:~}{1: }{2:o}{1: }{2:$}{1: }{2:oo}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:$$}{1: }{2:$$}{1: }{2:$$o$}{1: }{0: }|
+ {0:~}{1: }{2:oo}{1: }{2:$}{1: }{2:$}{1: "}{2:$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$o}{1: }{2:$$$o$$o$}{1: }{0: }|
+ {0:~}{1: "}{2:$$$$$$o$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$o}{1: }{2:$$$$$$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$}{1: """}{2:$$$}{1: }{0: }|
+ {0:~}{1: "}{2:$$$}{1:""""}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$o}{1: }{0: }|
+ {0:~}{1: }{2:o$$}{1:" }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$o}{1: }{0: }|
+ {0:~}{1: }{2:$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" "}{2:$$$$$$ooooo$$$$o}{1: }{0: }|
+ {0:~}{1: }{2:o$$$oooo$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:o$$$$$$$$$$$$$$$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$$$$$$}{1:"}{2:$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$}{1:"""""""" }{0: }|
+ {0:~}{1: """" }{2:$$$$}{1: "}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" }{2:o$$$}{1: }{0: }|
+ {0:~}{1: "}{2:$$$o}{1: """}{2:$$$$$$$$$$$$$$$$$$}{1:"}{2:$$}{1:" }{2:$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$o}{1: "}{2:$$}{1:""}{2:$$$$$$}{1:"""" }{2:o$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$$o}{1: }{2:o$$$}{1:" }{0: }|
+ {0:~}{1: "}{2:$$$$o}{1: }{2:o$$$$$$o}{1:"}{2:$$$$o}{1: }{2:o$$$$}{1: }{0: }|
+ {0:~}{1: "}{2:$$$$$oo}{1: ""}{2:$$$$o$$$$$o}{1: }{2:o$$$$}{1:"" }{0: }|
+ {0:~}{1: ""}{2:$$$$$oooo}{1: "}{2:$$$o$$$$$$$$$}{1:""" }{0: }|
+ {0:~}{1: ""}{2:$$$$$$$oo}{1: }{2:$$$$$$$$$$}{1: }{0: }|
+ {0:~}{1: """"}{2:$$$$$$$$$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$$$$$$$$$$}{1: }{0: }|
+ {0:~}{1: }{2:$$$$$$$$$$}{1:" }{0: }|
+ {0:~}{1: "}{2:$$$}{1:"""" }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{3:Press ENTER or type command to continue}{1: }{0: }|
+ {0:~}{4:term://~/config2/docs/pres//32693:vim --clean +smile 29,39 All}{0: }|
+ {0:~}{1::call nvim__screenshot("smile2.cat") }{0: }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
+ end)
end)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 7471f50dbd..ceeb84cec9 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -347,4 +347,44 @@ describe('API/win', function()
eq('', funcs.getcmdwintype())
end)
end)
+
+ describe('hide', function()
+ it('can hide current window', function()
+ local oldwin = meths.get_current_win()
+ command('split')
+ local newwin = meths.get_current_win()
+ meths.win_hide(newwin)
+ eq({oldwin}, meths.list_wins())
+ end)
+ it('can hide noncurrent window', function()
+ local oldwin = meths.get_current_win()
+ command('split')
+ local newwin = meths.get_current_win()
+ meths.win_hide(oldwin)
+ eq({newwin}, meths.list_wins())
+ end)
+ it('does not close the buffer', function()
+ local oldwin = meths.get_current_win()
+ local oldbuf = meths.get_current_buf()
+ local buf = meths.create_buf(true, false)
+ local newwin = meths.open_win(buf, true, {
+ relative='win', row=3, col=3, width=12, height=3
+ })
+ meths.win_hide(newwin)
+ eq({oldwin}, meths.list_wins())
+ eq({oldbuf, buf}, meths.list_bufs())
+ end)
+ it('deletes the buffer when bufhidden=wipe', function()
+ local oldwin = meths.get_current_win()
+ local oldbuf = meths.get_current_buf()
+ local buf = meths.create_buf(true, false)
+ local newwin = meths.open_win(buf, true, {
+ relative='win', row=3, col=3, width=12, height=3
+ })
+ meths.buf_set_option(buf, 'bufhidden', 'wipe')
+ meths.win_hide(newwin)
+ eq({oldwin}, meths.list_wins())
+ eq({oldbuf}, meths.list_bufs())
+ end)
+ end)
end)
diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index 5c67431221..e5c9a20db3 100644
--- a/test/functional/ex_cmds/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
@@ -371,4 +371,79 @@ describe('VimL dictionary notifications', function()
eq(1, eval('g:called'))
end)
+ it('does not crash when using dictwatcherdel in callback', function()
+ source([[
+ let g:d = {}
+
+ function! W1(...)
+ " Delete current and following watcher.
+ call dictwatcherdel(g:d, '*', function('W1'))
+ call dictwatcherdel(g:d, '*', function('W2'))
+ try
+ call dictwatcherdel({}, 'meh', function('tr'))
+ catch
+ let g:exc = v:exception
+ endtry
+ endfunction
+ call dictwatcheradd(g:d, '*', function('W1'))
+
+ function! W2(...)
+ endfunction
+ call dictwatcheradd(g:d, '*', function('W2'))
+
+ let g:d.foo = 23
+ ]])
+ eq(23, eval('g:d.foo'))
+ eq("Vim(call):Couldn't find a watcher matching key and callback", eval('g:exc'))
+ end)
+
+ it('does not call watcher added in callback', function()
+ source([[
+ let g:d = {}
+ let g:calls = []
+
+ function! W1(...) abort
+ call add(g:calls, 'W1')
+ call dictwatcheradd(g:d, '*', function('W2'))
+ endfunction
+
+ function! W2(...) abort
+ call add(g:calls, 'W2')
+ endfunction
+
+ call dictwatcheradd(g:d, '*', function('W1'))
+ let g:d.foo = 23
+ ]])
+ eq(23, eval('g:d.foo'))
+ eq({"W1"}, eval('g:calls'))
+ end)
+
+ it('calls watcher deleted in callback', function()
+ source([[
+ let g:d = {}
+ let g:calls = []
+
+ function! W1(...) abort
+ call add(g:calls, "W1")
+ call dictwatcherdel(g:d, '*', function('W2'))
+ endfunction
+
+ function! W2(...) abort
+ call add(g:calls, "W2")
+ endfunction
+
+ call dictwatcheradd(g:d, '*', function('W1'))
+ call dictwatcheradd(g:d, '*', function('W2'))
+ let g:d.foo = 123
+
+ unlet g:d
+ let g:d = {}
+ call dictwatcheradd(g:d, '*', function('W2'))
+ call dictwatcheradd(g:d, '*', function('W1'))
+ let g:d.foo = 123
+ ]])
+ eq(123, eval('g:d.foo'))
+ eq({"W1", "W2", "W2", "W1"}, eval('g:calls'))
+ end)
+
end)
diff --git a/test/functional/ex_cmds/excmd_spec.lua b/test/functional/ex_cmds/excmd_spec.lua
index aac2a9f469..33794eb50d 100644
--- a/test/functional/ex_cmds/excmd_spec.lua
+++ b/test/functional/ex_cmds/excmd_spec.lua
@@ -24,8 +24,6 @@ describe('Ex cmds', function()
pcall_err(command, ':menu 9999999999999999999999999999999999999999'))
eq('Vim(bdelete):E939: Positive count required',
pcall_err(command, ':bdelete 9999999999999999999999999999999999999999'))
- eq('Vim(retab):E487: Argument must be positive',
- pcall_err(command, ':retab 9999999999999999999999999999999999999999'))
assert_alive()
end)
end)
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 3bbb4c4517..bcd5e22492 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -154,6 +154,7 @@ function tests.capabilities_for_client_supports_method()
hoverProvider = true;
definitionProvider = false;
referencesProvider = false;
+ codeLensProvider = { resolveProvider = true; };
}
}
end;
@@ -402,11 +403,11 @@ function tests.basic_check_buffer_open_and_change_incremental()
contentChanges = {
{
range = {
- start = { line = 1; character = 0; };
- ["end"] = { line = 2; character = 0; };
+ start = { line = 1; character = 3; };
+ ["end"] = { line = 1; character = 3; };
};
- rangeLength = 4;
- text = "boop\n";
+ rangeLength = 0;
+ text = "boop";
};
}
})
diff --git a/test/functional/fixtures/smile2.cat b/test/functional/fixtures/smile2.cat
new file mode 100644
index 0000000000..0feb32f293
--- /dev/null
+++ b/test/functional/fixtures/smile2.cat
@@ -0,0 +1,32 @@
+31,79
+[?25l:smile
+ oooo$$$$$$$$$$$$oooo(B
+ oo$$$$$$$$$$$$$$$$$$$$$$$$o(B
+ oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o(B o$(B $$(B o$(B
+ o(B $(B oo(B o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o(B $$(B $$(B $$o$(B
+ oo(B $(B $(B "$(B o$$$$$$$$$(B $$$$$$$$$$$$$(B $$$$$$$$$o(B $$$o$$o$(B
+ "$$$$$$o$(B o$$$$$$$$$(B $$$$$$$$$$$(B $$$$$$$$$$o(B $$$$$$$$(B
+ $$$$$$$(B $$$$$$$$$$$(B $$$$$$$$$$$(B $$$$$$$$$$$$$$$$$$$$$$$(B
+ $$$$$$$$$$$$$$$$$$$$$$$(B $$$$$$$$$$$$$(B $$$$$$$$$$$$$$(B """$$$(B
+ "$$$(B""""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B "$$$(B
+ $$$(B o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B "$$$o(B
+ o$$(B" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B $$$o(B
+ $$$(B $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B" "$$$$$$ooooo$$$$o(B
+ o$$$oooo$$$$$(B $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B o$$$$$$$$$$$$$$$$$(B
+ $$$$$$$$(B"$$$$(B $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B $$$$(B""""""""
+ """" $$$$(B "$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B" o$$$(B
+ "$$$o(B """$$$$$$$$$$$$$$$$$$(B"$$(B" $$$(B
+ $$$o(B "$$(B""$$$$$$(B"""" o$$$(B
+ $$$$o(B o$$$(B"
+ "$$$$o(B o$$$$$$o(B"$$$$o(B o$$$$(B
+ "$$$$$oo(B ""$$$$o$$$$$o(B o$$$$(B""
+ ""$$$$$oooo(B "$$$o$$$$$$$$$(B"""
+ ""$$$$$$$oo(B $$$$$$$$$$(B
+ """"$$$$$$$$$$$(B
+ $$$$$$$$$$$$(B
+ $$$$$$$$$$(B"
+ "$$$(B""""
+
+Press ENTER or type command to continue(B
+(Bterm://~/config2/docs/pres//32693:vim --clean +smile 29,39 All
+(B:call nvim__screenshot("smile2.cat") [?25h \ No newline at end of file
diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua
index 5f7bbd887f..97ac96804e 100644
--- a/test/functional/legacy/memory_usage_spec.lua
+++ b/test/functional/legacy/memory_usage_spec.lua
@@ -157,8 +157,8 @@ describe('memory usage', function()
-- The usage may be a bit less than the last value, use 80%.
-- Allow for 20% tolerance at the upper limit. That's very permissive, but
-- otherwise the test fails sometimes. On Sourcehut CI with FreeBSD we need to
- -- be even more permissive.
- local upper_multiplier = uname() == 'freebsd' and 15 or 12
+ -- be even much more permissive.
+ local upper_multiplier = uname() == 'freebsd' and 19 or 12
local lower = before.last * 8 / 10
local upper = load_adjust((after.max + (after.last - before.last)) * upper_multiplier / 10)
check_result({before=before, after=after, last=last},
diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua
index 1db7afc7a7..023cdd4ae1 100644
--- a/test/functional/legacy/options_spec.lua
+++ b/test/functional/legacy/options_spec.lua
@@ -1,6 +1,10 @@
+-- See also: src/nvim/testdir/test_options.vim
local helpers = require('test.functional.helpers')(after_each)
local command, clear = helpers.command, helpers.clear
local source, expect = helpers.source, helpers.expect
+local exc_exec = helpers.exc_exec;
+local matches = helpers.matches;
+local Screen = require('test.functional.ui.screen')
describe('options', function()
setup(clear)
@@ -11,7 +15,7 @@ describe('options', function()
end)
describe('set', function()
- setup(clear)
+ before_each(clear)
it("should keep two comma when 'path' is changed", function()
source([[
@@ -24,4 +28,59 @@ describe('set', function()
foo,,bar]])
end)
+
+ it('winminheight works', function()
+ local screen = Screen.new(20, 11)
+ screen:attach()
+ source([[
+ set wmh=0 stal=2
+ below sp | wincmd _
+ below sp | wincmd _
+ below sp | wincmd _
+ below sp
+ ]])
+ matches('E36: Not enough room', exc_exec('set wmh=1'))
+ end)
+
+ it('winminheight works with tabline', function()
+ local screen = Screen.new(20, 11)
+ screen:attach()
+ source([[
+ set wmh=0 stal=2
+ split
+ split
+ split
+ split
+ tabnew
+ ]])
+ matches('E36: Not enough room', exc_exec('set wmh=1'))
+ end)
+
+ it('scroll works', function()
+ local screen = Screen.new(42, 16)
+ screen:attach()
+ source([[
+ set scroll=2
+ set laststatus=2
+ ]])
+ command('verbose set scroll?')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ scroll=7 |
+ Last set from changed window size |
+ Press ENTER or type command to continue^ |
+ ]])
+ end)
end)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 8c5e936906..6d7d9b4d8b 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -666,8 +666,146 @@ describe('lua: nvim_buf_attach on_bytes', function()
}
end)
+ it("tab with noexpandtab and softtabstop", function()
+ command("set noet")
+ command("set ts=4")
+ command("set sw=2")
+ command("set sts=2")
+
+ local check_events = setup_eventcheck(verify, {'asdfasdf'})
+
+ feed("gg0i<tab>")
+
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 4, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
+ }
+ feed("<tab>")
+
+ -- when spaces are merged into a tabstop
+ check_events {
+ { "test1", "bytes", 1, 5, 0, 2, 2, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 6, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 7, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
+ }
+
+ feed("<esc>u")
+ check_events {
+ { "test1", "bytes", 1, 8, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
+ { "test1", "bytes", 1, 8, 0, 0, 0, 0, 4, 4, 0, 0, 0 }
+ }
+
+ -- in REPLACE mode
+ feed("R<tab><tab>")
+ check_events {
+ { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
+ { "test1", "bytes", 1, 10, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 11, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
+ { "test1", "bytes", 1, 12, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
+ { "test1", "bytes", 1, 13, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
+ }
+ feed("<esc>u")
+ check_events {
+ { "test1", "bytes", 1, 14, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
+ { "test1", "bytes", 1, 14, 0, 2, 2, 0, 2, 2, 0, 1, 1 },
+ { "test1", "bytes", 1, 14, 0, 0, 0, 0, 2, 2, 0, 1, 1 }
+ }
+
+ -- in VISUALREPLACE mode
+ feed("gR<tab><tab>")
+ check_events {
+ { "test1", "bytes", 1, 15, 0, 0, 0, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 16, 0, 1, 1, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 17, 0, 2, 2, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 18, 0, 3, 3, 0, 1, 1, 0, 1, 1 };
+ { "test1", "bytes", 1, 19, 0, 3, 3, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 20, 0, 3, 3, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 22, 0, 2, 2, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 23, 0, 2, 2, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 25, 0, 1, 1, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 26, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 28, 0, 0, 0, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 29, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
+ { "test1", "bytes", 1, 31, 0, 0, 0, 0, 4, 4, 0, 1, 1 };
+ }
+ end)
+
+ it("sends events when undoing with undofile", function()
+ write_file("Xtest-undofile", dedent([[
+ 12345
+ hello world
+ ]]))
+
+ command("e! Xtest-undofile")
+ command("set undodir=. | set undofile")
+
+ local ns = helpers.request('nvim_create_namespace', "ns1")
+ meths.buf_set_extmark(0, ns, 0, 0, {})
+
+ eq({"12345", "hello world"}, meths.buf_get_lines(0, 0, -1, true))
+
+ -- splice
+ feed("gg0d2l")
+
+ eq({"345", "hello world"}, meths.buf_get_lines(0, 0, -1, true))
+
+ -- move
+ command(".m+1")
+
+ eq({"hello world", "345"}, meths.buf_get_lines(0, 0, -1, true))
+
+ -- reload undofile and undo changes
+ command("w")
+ command("set noundofile")
+ command("bw!")
+ command("e! Xtest-undofile")
+
+ command("set undofile")
+
+ local check_events = setup_eventcheck(verify, nil)
+
+ feed("u")
+ eq({"345", "hello world"}, meths.buf_get_lines(0, 0, -1, true))
+
+ check_events {
+ { "test1", "bytes", 2, 6, 1, 0, 12, 1, 0, 4, 0, 0, 0 },
+ { "test1", "bytes", 2, 6, 0, 0, 0, 0, 0, 0, 1, 0, 4 }
+ }
+
+ feed("u")
+ eq({"12345", "hello world"}, meths.buf_get_lines(0, 0, -1, true))
+
+ check_events {
+ { "test1", "bytes", 2, 8, 0, 0, 0, 0, 0, 0, 0, 2, 2 }
+ }
+ command("bw!")
+ end)
+
+ it("blockwise paste with uneven line lengths", function()
+ local check_events = setup_eventcheck(verify, {'aaaa', 'aaa', 'aaa'})
+
+ -- eq({}, meths.buf_get_lines(0, 0, -1, true))
+ feed("gg0<c-v>jj$d")
+
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 0, 4, 4, 0, 0, 0 },
+ { "test1", "bytes", 1, 3, 1, 0, 1, 0, 3, 3, 0, 0, 0 },
+ { "test1", "bytes", 1, 3, 2, 0, 2, 0, 3, 3, 0, 0, 0 },
+ }
+
+ feed("p")
+ check_events {
+ { "test1", "bytes", 1, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4 },
+ { "test1", "bytes", 1, 4, 1, 0, 5, 0, 0, 0, 0, 3, 3 },
+ { "test1", "bytes", 1, 4, 2, 0, 9, 0, 0, 0, 0, 3, 3 },
+ }
+
+ end)
+
teardown(function()
os.remove "Xtest-reload"
+ os.remove "Xtest-undofile"
+ os.remove ".Xtest-undofile.un~"
end)
end
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index e253db5297..9bf00b594b 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -715,6 +715,11 @@ describe('lua stdlib', function()
eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
end)
+ it('vim.fn should error when calling API function', function()
+ eq('Error executing lua: vim.lua:0: Tried to call API function with vim.fn: use vim.api.nvim_get_current_line instead',
+ pcall_err(exec_lua, "vim.fn.nvim_get_current_line()"))
+ end)
+
it('vim.rpcrequest and vim.rpcnotify', function()
exec_lua([[
chan = vim.fn.jobstart({'cat'}, {rpc=true})
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 8ac81daeef..6d3af115fa 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -11,6 +11,8 @@ local pesc = helpers.pesc
local insert = helpers.insert
local retry = helpers.retry
local NIL = helpers.NIL
+local read_file = require('test.helpers').read_file
+local write_file = require('test.helpers').write_file
-- Use these to get access to a coroutine so that I can run async tests and use
-- yield.
@@ -27,10 +29,10 @@ teardown(function()
os.remove(fake_lsp_logfile)
end)
-local function fake_lsp_server_setup(test_name, timeout_ms)
+local function fake_lsp_server_setup(test_name, timeout_ms, options)
exec_lua([=[
lsp = require('vim.lsp')
- local test_name, fixture_filename, logfile, timeout = ...
+ local test_name, fixture_filename, logfile, timeout, options = ...
TEST_RPC_CLIENT_ID = lsp.start_client {
cmd_env = {
NVIM_LOG_FILE = logfile;
@@ -52,18 +54,19 @@ local function fake_lsp_server_setup(test_name, timeout_ms)
on_init = function(client, result)
TEST_RPC_CLIENT = client
vim.rpcrequest(1, "init", result)
+ client.config.flags.allow_incremental_sync = options.allow_incremental_sync or false
end;
on_exit = function(...)
vim.rpcnotify(1, "exit", ...)
end;
}
- ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3)
+ ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3, options or {})
end
local function test_rpc_server(config)
if config.test_name then
clear()
- fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3)
+ fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3, config.options)
end
local client = setmetatable({}, {
__index = function(_, name)
@@ -257,6 +260,7 @@ describe('LSP', function()
eq(0, client.resolved_capabilities().text_document_did_change)
client.request('shutdown')
client.notify('exit')
+ client.stop()
end;
on_exit = function(code, signal)
eq(0, code, "exit code", fake_lsp_logfile)
@@ -334,6 +338,8 @@ describe('LSP', function()
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
eq(full_kind, client.resolved_capabilities().text_document_did_change)
eq(true, client.resolved_capabilities().text_document_save)
+ eq(false, client.resolved_capabilities().code_lens)
+ eq(false, client.resolved_capabilities().code_lens_resolve)
end;
on_exit = function(code, signal)
eq(0, code, "exit code", fake_lsp_logfile)
@@ -359,6 +365,8 @@ describe('LSP', function()
eq(true, client.resolved_capabilities().hover)
eq(false, client.resolved_capabilities().goto_definition)
eq(false, client.resolved_capabilities().rename)
+ eq(true, client.resolved_capabilities().code_lens)
+ eq(true, client.resolved_capabilities().code_lens_resolve)
-- known methods for resolved capabilities
eq(true, client.supports_method("textDocument/hover"))
@@ -680,8 +688,7 @@ describe('LSP', function()
}
end)
- -- TODO(askhan) we don't support full for now, so we can disable these tests.
- pending('should check the body and didChange incremental', function()
+ it('should check the body and didChange incremental', function()
local expected_callbacks = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
@@ -690,6 +697,7 @@ describe('LSP', function()
local client
test_rpc_server {
test_name = "basic_check_buffer_open_and_change_incremental";
+ options = { allow_incremental_sync = true };
on_setup = function()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
@@ -716,7 +724,7 @@ describe('LSP', function()
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
- "boop";
+ "123boop";
})
]]
client.notify('finish')
@@ -1257,6 +1265,99 @@ describe('LSP', function()
return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
]], make_workspace_edit(edits), target_bufnr))
end)
+ it('Supports file creation with CreateFile payload', function()
+ local tmpfile = helpers.tmpname()
+ os.remove(tmpfile) -- Should not exist, only interested in a tmpname
+ local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
+ local edit = {
+ documentChanges = {
+ {
+ kind = 'create',
+ uri = uri,
+ },
+ }
+ }
+ exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
+ eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
+ end)
+ it('createFile does not touch file if it exists and ignoreIfExists is set', function()
+ local tmpfile = helpers.tmpname()
+ write_file(tmpfile, 'Dummy content')
+ local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
+ local edit = {
+ documentChanges = {
+ {
+ kind = 'create',
+ uri = uri,
+ options = {
+ ignoreIfExists = true,
+ },
+ },
+ }
+ }
+ exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
+ eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
+ eq('Dummy content', read_file(tmpfile))
+ end)
+ it('createFile overrides file if overwrite is set', function()
+ local tmpfile = helpers.tmpname()
+ write_file(tmpfile, 'Dummy content')
+ local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
+ local edit = {
+ documentChanges = {
+ {
+ kind = 'create',
+ uri = uri,
+ options = {
+ overwrite = true,
+ ignoreIfExists = true, -- overwrite must win over ignoreIfExists
+ },
+ },
+ }
+ }
+ exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit)
+ eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
+ eq('', read_file(tmpfile))
+ end)
+ it('DeleteFile delete file and buffer', function()
+ local tmpfile = helpers.tmpname()
+ write_file(tmpfile, 'Be gone')
+ local uri = exec_lua([[
+ local fname = select(1, ...)
+ local bufnr = vim.fn.bufadd(fname)
+ vim.fn.bufload(bufnr)
+ return vim.uri_from_fname(fname)
+ ]], tmpfile)
+ local edit = {
+ documentChanges = {
+ {
+ kind = 'delete',
+ uri = uri,
+ }
+ }
+ }
+ eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit))
+ eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
+ eq(false, exec_lua('return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))', tmpfile))
+ end)
+ it('DeleteFile fails if file does not exist and ignoreIfNotExists is false', function()
+ local tmpfile = helpers.tmpname()
+ os.remove(tmpfile)
+ local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile)
+ local edit = {
+ documentChanges = {
+ {
+ kind = 'delete',
+ uri = uri,
+ options = {
+ ignoreIfNotExists = false,
+ }
+ }
+ }
+ }
+ eq(false, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit))
+ eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile))
+ end)
end)
describe('completion_list_to_complete_items', function()
@@ -1303,6 +1404,72 @@ describe('LSP', function()
end)
end)
+ describe('lsp.util.rename', function()
+ it('Can rename an existing file', function()
+ local old = helpers.tmpname()
+ write_file(old, 'Test content')
+ local new = helpers.tmpname()
+ os.remove(new) -- only reserve the name, file must not exist for the test scenario
+ local lines = exec_lua([[
+ local old = select(1, ...)
+ local new = select(2, ...)
+ vim.lsp.util.rename(old, new)
+
+ -- after rename the target file must have the contents of the source file
+ local bufnr = vim.fn.bufadd(new)
+ return vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
+ ]], old, new)
+ eq({'Test content'}, lines)
+ local exists = exec_lua('return vim.loop.fs_stat(...) ~= nil', old)
+ eq(false, exists)
+ exists = exec_lua('return vim.loop.fs_stat(...) ~= nil', new)
+ eq(true, exists)
+ os.remove(new)
+ end)
+ it('Does not rename file if target exists and ignoreIfExists is set or overwrite is false', function()
+ local old = helpers.tmpname()
+ write_file(old, 'Old File')
+ local new = helpers.tmpname()
+ write_file(new, 'New file')
+
+ exec_lua([[
+ local old = select(1, ...)
+ local new = select(2, ...)
+
+ vim.lsp.util.rename(old, new, { ignoreIfExists = true })
+ ]], old, new)
+
+ eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', old))
+ eq('New file', read_file(new))
+
+ exec_lua([[
+ local old = select(1, ...)
+ local new = select(2, ...)
+
+ vim.lsp.util.rename(old, new, { overwrite = false })
+ ]], old, new)
+
+ eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', old))
+ eq('New file', read_file(new))
+ end)
+ it('Does override target if overwrite is true', function()
+ local old = helpers.tmpname()
+ write_file(old, 'Old file')
+ local new = helpers.tmpname()
+ write_file(new, 'New file')
+ exec_lua([[
+ local old = select(1, ...)
+ local new = select(2, ...)
+
+ vim.lsp.util.rename(old, new, { overwrite = true })
+ ]], old, new)
+
+ eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', old))
+ eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', new))
+ eq('Old file\n', read_file(new))
+ end)
+ end)
+
describe('lsp.util.locations_to_items', function()
it('Convert Location[] to items', function()
local expected = {
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index cb73bfbbe1..d80d0fdbaf 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -472,4 +472,102 @@ describe('treesitter highlighting', function()
|
]]}
end)
+
+ it("supports overriding queries, like ", function()
+ if pending_c_parser(pending) then return end
+
+ insert([[
+ int x = INT_MAX;
+ #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
+ #define foo void main() { \
+ return 42; \
+ }
+ ]])
+
+ exec_lua [[
+ local injection_query = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"
+ require('vim.treesitter.query').set_query("c", "highlights", hl_query)
+ require('vim.treesitter.query').set_query("c", "injections", injection_query)
+
+ vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, "c"))
+ ]]
+
+ screen:expect{grid=[[
+ {3:int} x = {5:INT_MAX}; |
+ #define {5:READ_STRING}(x, y) ({3:char_u} *)read_string((x), ({3:size_t})(y))|
+ #define foo {3:void} main() { \ |
+ {4:return} {5:42}; \ |
+ } |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it("supports highlighting with custom highlight groups", function()
+ if pending_c_parser(pending) then return end
+
+ insert(hl_text)
+
+ exec_lua [[
+ local parser = vim.treesitter.get_parser(0, "c")
+ test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query}})
+ ]]
+
+ screen:expect{grid=[[
+ {2:/// Schedule Lua callback on main loop's event queue} |
+ {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
+ { |
+ {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
+ || {6:lstate} != {6:lstate}) { |
+ {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
+ {4:return} {11:lua_error}(lstate); |
+ } |
+ |
+ {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
+ |
+ multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
+ {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
+ {4:return} {5:0}; |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- This will change ONLY the literal strings to look like comments
+ -- The only literal string is the "vim.schedule: expected function" in this test.
+ exec_lua [[vim.cmd("highlight link cString comment")]]
+ screen:expect{grid=[[
+ {2:/// Schedule Lua callback on main loop's event queue} |
+ {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
+ { |
+ {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
+ || {6:lstate} != {6:lstate}) { |
+ {11:lua_pushliteral}(lstate, {2:"vim.schedule: expected function"}); |
+ {4:return} {11:lua_error}(lstate); |
+ } |
+ |
+ {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
+ |
+ multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
+ {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
+ {4:return} {5:0}; |
+ ^} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ screen:expect{ unchanged=true }
+ end)
end)
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index e1a72ced05..f75f700fb5 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -212,10 +212,10 @@ describe('ui/cursor', function()
if m.blinkwait then m.blinkwait = 700 end
end
if m.hl_id then
- m.hl_id = 55
+ m.hl_id = 56
m.attr = {background = Screen.colors.DarkGray}
end
- if m.id_lm then m.id_lm = 56 end
+ if m.id_lm then m.id_lm = 57 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 7a87521a6b..295a54aec8 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua
local exec = helpers.exec
local expect_events = helpers.expect_events
local meths = helpers.meths
+local command = helpers.command
describe('decorations providers', function()
local screen
@@ -178,7 +179,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} |
@@ -204,7 +205,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(win == thewin and _G.ns1 or ns2)
end;
})
]]
@@ -251,7 +252,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 |
@@ -287,7 +288,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=[[
@@ -301,6 +302,35 @@ describe('decorations providers', function()
|
]]}
end)
+
+ it('can have virtual text', function()
+ insert(mulholland)
+ setup_provider [[
+ local hl = a.nvim_get_hl_id_by_name "ErrorMsg"
+ local test_ns = a.nvim_create_namespace "mulholland"
+ function on_do(event, ...)
+ if event == "line" then
+ local win, buf, line = ...
+ a.nvim_buf_set_extmark(buf, test_ns, line, 0, {
+ virt_text = {{'+', 'ErrorMsg'}};
+ virt_text_pos='overlay';
+ ephemeral = true;
+ })
+ end
+ end
+ ]]
+
+ screen:expect{grid=[[
+ {2:+}/ just to see if there was an accident |
+ {2:+}/ on Mulholland Drive |
+ {2:+}ry_start(); |
+ {2:+}ufref_T save_buf; |
+ {2:+}witch_buffer(&save_buf, buf); |
+ {2:+}osp = getmark(mark, false); |
+ {2:+}estore_buffer(&save_buf);^ |
+ |
+ ]]}
+ end)
end)
describe('extmark decorations', function()
@@ -314,11 +344,30 @@ describe('extmark decorations', function()
[2] = {foreground = Screen.colors.Brown};
[3] = {bold = true, foreground = Screen.colors.SeaGreen};
[4] = {background = Screen.colors.Red1, foreground = Screen.colors.Gray100};
+ [5] = {foreground = Screen.colors.Brown, bold = true};
+ [6] = {foreground = Screen.colors.DarkCyan};
+ [7] = {foreground = Screen.colors.Grey0, background = tonumber('0xff4c4c')};
+ [8] = {foreground = tonumber('0x180606'), background = tonumber('0xff4c4c')};
+ [9] = {foreground = tonumber('0xe40c0c'), background = tonumber('0xff4c4c'), bold = true};
+ [10] = {foreground = tonumber('0xb20000'), background = tonumber('0xff4c4c')};
+ [11] = {blend = 30, background = Screen.colors.Red1};
+ [12] = {foreground = Screen.colors.Brown, blend = 30, background = Screen.colors.Red1, bold = true};
+ [13] = {foreground = Screen.colors.Fuchsia};
+ [14] = {background = Screen.colors.Red1, foreground = Screen.colors.Black};
+ [15] = {background = Screen.colors.Red1, foreground = tonumber('0xb20000')};
+ [16] = {blend = 30, background = Screen.colors.Red1, foreground = Screen.colors.Magenta1};
+ [17] = {bold = true, foreground = Screen.colors.Brown, background = Screen.colors.LightGrey};
+ [18] = {background = Screen.colors.LightGrey};
+ [19] = {foreground = Screen.colors.Cyan4, background = Screen.colors.LightGrey};
+ [20] = {foreground = tonumber('0x180606'), background = tonumber('0xf13f3f')};
+ [21] = {foreground = Screen.colors.Gray0, background = tonumber('0xf13f3f')};
+ [22] = {foreground = tonumber('0xb20000'), background = tonumber('0xf13f3f')};
+ [23] = {foreground = Screen.colors.Magenta1, background = Screen.colors.LightGrey};
+ [24] = {bold = true};
}
end)
- it('can have virtual text of overlay style', function()
- insert [[
+ local example_text = [[
for _,item in ipairs(items) do
local text, hl_id_cell, count = unpack(item)
if hl_id_cell ~= nil then
@@ -331,69 +380,164 @@ for _,item in ipairs(items) do
colpos = colpos+1
end
end]]
- feed 'gg'
- local ns = meths.create_namespace 'test'
- for i = 1,9 do
- meths.buf_set_extmark(0, ns, i, 0, { virt_text={{'|', 'LineNr'}}, virt_text_pos='overlay'})
- if i == 3 or (i >= 6 and i <= 9) then
- meths.buf_set_extmark(0, ns, i, 4, { virt_text={{'|', 'NonText'}}, virt_text_pos='overlay'})
+ it('can have virtual text of overlay position', function()
+ insert(example_text)
+ feed 'gg'
+
+ local ns = meths.create_namespace 'test'
+ for i = 1,9 do
+ meths.buf_set_extmark(0, ns, i, 0, { virt_text={{'|', 'LineNr'}}, virt_text_pos='overlay'})
+ if i == 3 or (i >= 6 and i <= 9) then
+ meths.buf_set_extmark(0, ns, i, 4, { virt_text={{'|', 'NonText'}}, virt_text_pos='overlay'})
+ end
end
- end
- meths.buf_set_extmark(0, ns, 9, 10, { virt_text={{'foo'}, {'bar', 'MoreMsg'}, {'!!', 'ErrorMsg'}}, virt_text_pos='overlay'})
-
- -- can "float" beyond end of line
- meths.buf_set_extmark(0, ns, 5, 28, { virt_text={{'loopy', 'ErrorMsg'}}, virt_text_pos='overlay'})
- -- bound check: right edge of window
- meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork ' }, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'})
-
- screen:expect{grid=[[
- ^for _,item in ipairs(items) do |
- {2:|} local text, hl_id_cell, count = unpack(item) |
- {2:|} if hl_id_cell ~= nil tbork bork bork {4:bork bork}|
- {2:|} {1:|} hl_id = hl_id_cell |
- {2:|} end |
- {2:|} for _ = 1, (count or 1) {4:loopy} |
- {2:|} {1:|} local cell = line[colpos] |
- {2:|} {1:|} cell.text = text |
- {2:|} {1:|} cell.hl_id = hl_id |
- {2:|} {1:|} cofoo{3:bar}{4:!!}olpos+1 |
- end |
- end |
- {1:~ }|
- {1:~ }|
- |
- ]]}
-
-
- -- handles broken lines
- screen:try_resize(22, 25)
- screen:expect{grid=[[
- ^for _,item in ipairs(i|
- tems) do |
- {2:|} local text, hl_id_|
- cell, count = unpack(i|
- tem) |
- {2:|} if hl_id_cell ~= n|
- il tbork bork bork {4:bor}|
- {2:|} {1:|} hl_id = hl_id_|
- cell |
- {2:|} end |
- {2:|} for _ = 1, (count |
- or 1) {4:loopy} |
- {2:|} {1:|} local cell = l|
- ine[colpos] |
- {2:|} {1:|} cell.text = te|
- xt |
- {2:|} {1:|} cell.hl_id = h|
- l_id |
- {2:|} {1:|} cofoo{3:bar}{4:!!}olpo|
- s+1 |
- end |
- end |
- {1:~ }|
- {1:~ }|
- |
- ]]}
+ meths.buf_set_extmark(0, ns, 9, 10, { virt_text={{'foo'}, {'bar', 'MoreMsg'}, {'!!', 'ErrorMsg'}}, virt_text_pos='overlay'})
+
+ -- can "float" beyond end of line
+ meths.buf_set_extmark(0, ns, 5, 28, { virt_text={{'loopy', 'ErrorMsg'}}, virt_text_pos='overlay'})
+ -- bound check: right edge of window
+ meths.buf_set_extmark(0, ns, 2, 26, { virt_text={{'bork bork bork ' }, {'bork bork bork', 'ErrorMsg'}}, virt_text_pos='overlay'})
+
+ screen:expect{grid=[[
+ ^for _,item in ipairs(items) do |
+ {2:|} local text, hl_id_cell, count = unpack(item) |
+ {2:|} if hl_id_cell ~= nil tbork bork bork {4:bork bork}|
+ {2:|} {1:|} hl_id = hl_id_cell |
+ {2:|} end |
+ {2:|} for _ = 1, (count or 1) {4:loopy} |
+ {2:|} {1:|} local cell = line[colpos] |
+ {2:|} {1:|} cell.text = text |
+ {2:|} {1:|} cell.hl_id = hl_id |
+ {2:|} {1:|} cofoo{3:bar}{4:!!}olpos+1 |
+ end |
+ end |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+
+ -- handles broken lines
+ screen:try_resize(22, 25)
+ screen:expect{grid=[[
+ ^for _,item in ipairs(i|
+ tems) do |
+ {2:|} local text, hl_id_|
+ cell, count = unpack(i|
+ tem) |
+ {2:|} if hl_id_cell ~= n|
+ il tbork bork bork {4:bor}|
+ {2:|} {1:|} hl_id = hl_id_|
+ cell |
+ {2:|} end |
+ {2:|} for _ = 1, (count |
+ or 1) {4:loopy} |
+ {2:|} {1:|} local cell = l|
+ ine[colpos] |
+ {2:|} {1:|} cell.text = te|
+ xt |
+ {2:|} {1:|} cell.hl_id = h|
+ l_id |
+ {2:|} {1:|} cofoo{3:bar}{4:!!}olpo|
+ s+1 |
+ end |
+ end |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it('can have virtual text of overlay position and styling', function()
+ insert(example_text)
+ feed 'gg'
+ local ns = meths.create_namespace 'test'
+
+ command 'set ft=lua'
+ command 'syntax on'
+
+ screen:expect{grid=[[
+ {5:^for} _,item {5:in} {6:ipairs}(items) {5:do} |
+ {5:local} text, hl_id_cell, count = unpack(item) |
+ {5:if} hl_id_cell ~= {13:nil} {5:then} |
+ hl_id = hl_id_cell |
+ {5:end} |
+ {5:for} _ = {13:1}, (count {5:or} {13:1}) {5:do} |
+ {5:local} cell = line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+{13:1} |
+ {5:end} |
+ {5:end} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ command 'hi Blendy guibg=Red blend=30'
+ meths.buf_set_extmark(0, ns, 1, 5, { virt_text={{'blendy text - here', 'Blendy'}}, virt_text_pos='overlay', hl_mode='blend'})
+ meths.buf_set_extmark(0, ns, 2, 5, { virt_text={{'combining color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='combine'})
+ meths.buf_set_extmark(0, ns, 3, 5, { virt_text={{'replacing color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='replace'})
+
+ meths.buf_set_extmark(0, ns, 4, 5, { virt_text={{'blendy text - here', 'Blendy'}}, virt_text_pos='overlay', hl_mode='blend', virt_text_hide=true})
+ meths.buf_set_extmark(0, ns, 5, 5, { virt_text={{'combining color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='combine', virt_text_hide=true})
+ meths.buf_set_extmark(0, ns, 6, 5, { virt_text={{'replacing color', 'Blendy'}}, virt_text_pos='overlay', hl_mode='replace', virt_text_hide=true})
+
+ screen:expect{grid=[[
+ {5:^for} _,item {5:in} {6:ipairs}(items) {5:do} |
+ {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count = unpack(item) |
+ {5:i}{12:c}{11:ombining color} {13:nil} {5:then} |
+ {11:replacing color}d_cell |
+ {5:e}{8:bl}{14:endy}{15:i}{14:text}{15:o}{14:-}{15:o}{14:h}{7:ere} |
+ {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
+ {11:replacing color} line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+{13:1} |
+ {5:end} |
+ {5:end} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed 'V5G'
+ screen:expect{grid=[[
+ {17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} |
+ {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} |
+ {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} |
+ {18: }{11:replacing color}{18:d_cell} |
+ {18: }{5:^e}{17:nd} |
+ {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} |
+ {11:replacing color} line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+{13:1} |
+ {5:end} |
+ {5:end} |
+ {1:~ }|
+ {1:~ }|
+ {24:-- VISUAL LINE --} |
+ ]]}
+
+ feed 'jj'
+ screen:expect{grid=[[
+ {17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} |
+ {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} |
+ {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} |
+ {18: }{11:replacing color}{18:d_cell} |
+ {18: }{17:end} |
+ {18: }{17:for}{18: _ = }{23:1}{18:, (count }{17:or}{18: }{23:1}{18:) }{17:do} |
+ {18: }^ {18: }{17:local}{18: cell = line[colpos]} |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+{13:1} |
+ {5:end} |
+ {5:end} |
+ {1:~ }|
+ {1:~ }|
+ {24:-- VISUAL LINE --} |
+ ]]}
end)
end)
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index 69b6ab8cf0..a8d9fb02fc 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -1049,6 +1049,8 @@ it('diff updates line numbers below filler lines', function()
[9] = {background = Screen.colors.LightMagenta},
[10] = {bold = true, foreground = Screen.colors.Brown},
[11] = {foreground = Screen.colors.Brown},
+ [12] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red};
+ [13] = {background = Screen.colors.Gray90};
})
source([[
call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b'])
@@ -1107,4 +1109,22 @@ it('diff updates line numbers below filler lines', function()
{3:[No Name] [+] }{7:[No Name] [+] }|
|
]])
+ command("set signcolumn number tgc cursorline")
+ command("hi CursorLineNr guibg=red")
+ screen:expect{grid=[[
+ {1: }a {3:│}{11: 2 }a |
+ {1: }a {3:│}{11: 1 }a |
+ {1: }a {3:│}{12:3 }{13:^a }|
+ {1: }{8:x}{9: }{3:│}{11: 1 }{8:y}{9: }|
+ {1: }{4:x }{3:│}{11: }{2:----------------}|
+ {1: }{4:x }{3:│}{11: }{2:----------------}|
+ {1: }b {3:│}{11: 2 }b |
+ {1: }b {3:│}{11: 3 }b |
+ {1: }b {3:│}{11: 4 }b |
+ {1: }b {3:│}{11: 5 }b |
+ {1: }b {3:│}{11: 6 }b |
+ {6:~ }{3:│}{6:~ }|
+ {3:[No Name] [+] }{7:[No Name] [+] }|
+ signcolumn=auto |
+ ]]}
end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 32f9ae030f..664b8e7ab7 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -14,7 +14,7 @@ local funcs = helpers.funcs
local run = helpers.run
local pcall_err = helpers.pcall_err
-describe('floatwin', function()
+describe('float window', function()
before_each(function()
clear()
end)
@@ -131,7 +131,7 @@ describe('floatwin', function()
local screen
before_each(function()
screen = Screen.new(40,7)
- screen:attach({ext_multigrid=multigrid})
+ screen:attach {ext_multigrid=multigrid}
screen:set_default_attr_ids(attrs)
end)
@@ -595,6 +595,310 @@ describe('floatwin', function()
end
end)
+ it('can have border', function()
+ local buf = meths.create_buf(false, false)
+ meths.buf_set_lines(buf, 0, -1, true, {' halloj! ',
+ ' BORDAA '})
+ local win = meths.open_win(buf, false, {relative='editor', width=9, height=2, row=2, col=5, border="double"})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:╔═════════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔═════════╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ |
+ ]]}
+ end
+
+ meths.win_set_config(win, {border="single"})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:┌─────────┐}|
+ {5:│}{1: halloj! }{5:│}|
+ {5:│}{1: BORDAA }{5:│}|
+ {5:└─────────┘}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:┌─────────┐}{0: }|
+ {0:~ }{5:│}{1: halloj! }{5:│}{0: }|
+ {0:~ }{5:│}{1: BORDAA }{5:│}{0: }|
+ {0:~ }{5:└─────────┘}{0: }|
+ |
+ ]]}
+ end
+
+ -- support: ascii char, UTF-8 char, composed char, highlight per char
+ meths.win_set_config(win, {border={"x", {"å", "ErrorMsg"}, {"\\"}, {"n̈̊", "Search"}}})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:xååååååååå\}|
+ {5:n̈̊}{1: halloj! }{5:n̈̊}|
+ {5:n̈̊}{1: BORDAA }{5:n̈̊}|
+ {5:\åååååååååx}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:x}{7:ååååååååå}{5:\}{0: }|
+ {0:~ }{17:n̈̊}{1: halloj! }{17:n̈̊}{0: }|
+ {0:~ }{17:n̈̊}{1: BORDAA }{17:n̈̊}{0: }|
+ {0:~ }{5:\}{7:ååååååååå}{5:x}{0: }|
+ |
+ ]]}
+ end
+
+ meths.win_set_config(win, {border="none"})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {1: halloj! }|
+ {1: BORDAA }|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 2, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{1: halloj! }{0: }|
+ {0:~ }{1: BORDAA }{0: }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end
+ end)
+
+ it('with border show popupmenu', function()
+ screen:try_resize(40,10)
+ local buf = meths.create_buf(false, false)
+ meths.buf_set_lines(buf, 0, -1, true, {'aaa aab ',
+ 'abb acc ', ''})
+ meths.open_win(buf, true, {relative='editor', width=9, height=3, row=0, col=5, border="double"})
+ feed 'G'
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {5:╔═════════╗}|
+ {5:║}{1:aaa aab }{5:║}|
+ {5:║}{1:abb acc }{5:║}|
+ {5:║}{1:^ }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [5] = { { id = 1002 }, "NW", 1, 0, 5, true }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ {5:╔═════════╗} |
+ {0:~ }{5:║}{1:aaa aab }{5:║}{0: }|
+ {0:~ }{5:║}{1:abb acc }{5:║}{0: }|
+ {0:~ }{5:║}{1:^ }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end
+
+ feed 'i<c-x><c-p>'
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {3:-- }{8:match 1 of 4} |
+ ## grid 5
+ {5:╔═════════╗}|
+ {5:║}{1:aaa aab }{5:║}|
+ {5:║}{1:abb acc }{5:║}|
+ {5:║}{1:acc^ }{5:║}|
+ {5:╚═════════╝}|
+ ## grid 6
+ {1: aaa }|
+ {1: aab }|
+ {1: abb }|
+ {13: acc }|
+ ]], float_pos={
+ [5] = { {
+ id = 1002
+ }, "NW", 1, 0, 5, true },
+ [6] = { {
+ id = -1
+ }, "NW", 5, 4, 0, false }
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3};
+ }}
+ else
+ screen:expect{grid=[[
+ {5:╔═════════╗} |
+ {0:~ }{5:║}{1:aaa aab }{5:║}{0: }|
+ {0:~ }{5:║}{1:abb acc }{5:║}{0: }|
+ {0:~ }{5:║}{1:acc^ }{5:║}{0: }|
+ {0:~ }{1: aaa }{0: }|
+ {0:~ }{1: aab }{0: }|
+ {0:~ }{1: abb }{0: }|
+ {0:~ }{13: acc }{0: }|
+ {0:~ }|
+ {3:-- }{8:match 1 of 4} |
+ ]]}
+ end
+ end)
+
it('can have minimum size', function()
insert("the background text")
local buf = meths.create_buf(false, true)
@@ -5433,6 +5737,155 @@ describe('floatwin', function()
]])
end
end)
+
+ it("correctly redraws when overlaid windows are resized #13991", function()
+ helpers.source([[
+ let popup_config = {"relative" : "editor",
+ \ "width" : 7,
+ \ "height" : 3,
+ \ "row" : 1,
+ \ "col" : 1,
+ \ "style" : "minimal"}
+
+ let border_config = {"relative" : "editor",
+ \ "width" : 9,
+ \ "height" : 5,
+ \ "row" : 0,
+ \ "col" : 0,
+ \ "style" : "minimal"}
+
+ let popup_buffer = nvim_create_buf(v:false, v:true)
+ let border_buffer = nvim_create_buf(v:false, v:true)
+ let popup_win = nvim_open_win(popup_buffer, v:true, popup_config)
+ let border_win = nvim_open_win(border_buffer, v:false, border_config)
+
+ call nvim_buf_set_lines(popup_buffer, 0, -1, v:true,
+ \ ["long", "longer", "longest"])
+
+ call nvim_buf_set_lines(border_buffer, 0, -1, v:true,
+ \ ["---------", "- -", "- -"])
+ ]])
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {2:^long }|
+ {2:longer }|
+ {2:longest}|
+ ## grid 6
+ {2:---------}|
+ {2:- -}|
+ {2:- -}|
+ {2: }|
+ {2: }|
+ ]], attr_ids={
+ [1] = {foreground = Screen.colors.Blue1, bold = true};
+ [2] = {background = Screen.colors.LightMagenta};
+ }, float_pos={
+ [5] = { {
+ id = 1002
+ }, "NW", 1, 1, 1, true },
+ [6] = { {
+ id = 1003
+ }, "NW", 1, 0, 0, true }
+ }}
+ else
+ screen:expect([[
+ {1:---------} |
+ {1:-^long -}{0: }|
+ {1:-longer -}{0: }|
+ {1: longest }{0: }|
+ {1: }{0: }|
+ {0:~ }|
+ |
+ ]])
+ end
+
+ helpers.source([[
+ let new_popup_config = {"width" : 1, "height" : 3}
+ let new_border_config = {"width" : 3, "height" : 5}
+
+ function! Resize()
+ call nvim_win_set_config(g:popup_win, g:new_popup_config)
+ call nvim_win_set_config(g:border_win, g:new_border_config)
+
+ call nvim_buf_set_lines(g:border_buffer, 0, -1, v:true,
+ \ ["---", "- -", "- -"])
+ endfunction
+
+ nnoremap zz <cmd>call Resize()<cr>
+ ]])
+
+ helpers.feed("zz")
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {2:^l}|
+ {2:o}|
+ {2:n}|
+ ## grid 6
+ {2:---}|
+ {2:- -}|
+ {2:- -}|
+ {2: }|
+ {2: }|
+ ]], attr_ids={
+ [1] = {foreground = Screen.colors.Blue1, bold = true};
+ [2] = {background = Screen.colors.LightMagenta};
+ }, float_pos={
+ [5] = { {
+ id = 1002
+ }, "NW", 1, 1, 1, true },
+ [6] = { {
+ id = 1003
+ }, "NW", 1, 0, 0, true }
+ }}
+ else
+ screen:expect([[
+ {1:---} |
+ {1:-^l-}{0: }|
+ {1:-o-}{0: }|
+ {1: n }{0: }|
+ {1: }{0: }|
+ {0:~ }|
+ |
+ ]])
+ end
+ end)
end
describe('with ext_multigrid', function()
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 6ce8b33a63..8883ad8270 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -38,7 +38,9 @@ describe("folded lines", function()
[6] = {background = Screen.colors.Yellow},
[7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
[8] = {foreground = Screen.colors.Brown },
- [9] = {bold = true, foreground = Screen.colors.Brown}
+ [9] = {bold = true, foreground = Screen.colors.Brown},
+ [10] = {background = Screen.colors.LightGrey, underline = true},
+ [11] = {bold=true},
})
end)
@@ -290,6 +292,569 @@ describe("folded lines", function()
end
end)
+ it("works with split", function()
+ insert([[
+ aa
+ bb
+ cc
+ dd
+ ee
+ ff]])
+ feed_command('2')
+ command("set foldcolumn=1")
+ feed('zf3j')
+ feed_command('1')
+ feed('zf2j')
+ feed('zO')
+ feed_command("rightbelow new")
+ insert([[
+ aa
+ bb
+ cc
+ dd
+ ee
+ ff]])
+ feed_command('2')
+ command("set foldcolumn=1")
+ feed('zf3j')
+ feed_command('1')
+ feed('zf2j')
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 0, 0)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ {2:[No Name] [+] }|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ {3:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:-}aa |
+ {7:-}bb |
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}^aa |
+ {7:+}{5:+--- 4 lines: bb···························}|
+ {7:│}ff |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 3, 0)
+ screen:expect([[
+ {7:-}aa |
+ {7:-}bb |
+ {2:[No Name] [+] }|
+ {7:-}^aa |
+ {7:+}{5:+--- 4 lines: bb···························}|
+ {7:│}ff |
+ {3:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 1, 0)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ {2:[No Name] [+] }|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ {3:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:-}aa |
+ {7:-}bb |
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}^aa |
+ {7:-}bb |
+ {7:2}cc |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 4, 0)
+ screen:expect([[
+ {7:-}aa |
+ {7:-}bb |
+ {2:[No Name] [+] }|
+ {7:-}^aa |
+ {7:-}bb |
+ {7:2}cc |
+ {3:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 1, 0)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ {3:[No Name] [+] }|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ {2:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:-}aa |
+ {7:+}{5:^+--- 4 lines: bb···························}|
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 0)
+ screen:expect([[
+ {7:-}aa |
+ {7:+}{5:^+--- 4 lines: bb···························}|
+ {3:[No Name] [+] }|
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ {2:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 0, 0)
+ screen:expect([[
+ ## grid 1
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ {3:[No Name] [+] }|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ {2:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:+}{5:^+-- 6 lines: aa····························}|
+ {1:~ }|
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ screen:expect([[
+ {7:+}{5:^+-- 6 lines: aa····························}|
+ {1:~ }|
+ {3:[No Name] [+] }|
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ {2:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+ end)
+
+ it("works with vsplit", function()
+ insert([[
+ aa
+ bb
+ cc
+ dd
+ ee
+ ff]])
+ feed_command('2')
+ command("set foldcolumn=1")
+ feed('zf3j')
+ feed_command('1')
+ feed('zf2j')
+ feed('zO')
+ feed_command("rightbelow vnew")
+ insert([[
+ aa
+ bb
+ cc
+ dd
+ ee
+ ff]])
+ feed_command('2')
+ command("set foldcolumn=1")
+ feed('zf3j')
+ feed_command('1')
+ feed('zf2j')
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 0, 0)
+ screen:expect([[
+ ## grid 1
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ {2:[No Name] [+] }{3:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ {7:2}dd |
+ {7:2}ee |
+ {7:│}ff |
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}^aa |
+ {7:+}{5:+--- 4 lines: bb····}|
+ {7:│}ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 0, 23)
+ screen:expect([[
+ {7:-}aa {2:│}{7:-}^aa |
+ {7:-}bb {2:│}{7:+}{5:+--- 4 lines: bb····}|
+ {7:2}cc {2:│}{7:│}ff |
+ {7:2}dd {2:│}{1:~ }|
+ {7:2}ee {2:│}{1:~ }|
+ {7:│}ff {2:│}{1:~ }|
+ {2:[No Name] [+] }{3:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 1, 0)
+ screen:expect([[
+ ## grid 1
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ {2:[No Name] [+] }{3:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ {7:2}dd |
+ {7:2}ee |
+ {7:│}ff |
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}^aa |
+ {7:-}bb |
+ {7:2}cc |
+ {7:2}dd |
+ {7:2}ee |
+ {7:│}ff |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 23)
+ screen:expect([[
+ {7:-}aa {2:│}{7:-}^aa |
+ {7:-}bb {2:│}{7:-}bb |
+ {7:2}cc {2:│}{7:2}cc |
+ {7:2}dd {2:│}{7:2}dd |
+ {7:2}ee {2:│}{7:2}ee |
+ {7:│}ff {2:│}{7:│}ff |
+ {2:[No Name] [+] }{3:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 1, 0)
+ screen:expect([[
+ ## grid 1
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:-}aa |
+ {7:+}{5:^+--- 4 lines: bb····}|
+ {7:│}ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ {7:2}dd |
+ {7:2}ee |
+ {7:│}ff |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 0)
+ screen:expect([[
+ {7:-}aa {2:│}{7:-}aa |
+ {7:+}{5:^+--- 4 lines: bb····}{2:│}{7:-}bb |
+ {7:│}ff {2:│}{7:2}cc |
+ {1:~ }{2:│}{7:2}dd |
+ {1:~ }{2:│}{7:2}ee |
+ {1:~ }{2:│}{7:│}ff |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 0, 0)
+ screen:expect([[
+ ## grid 1
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ [2:----------------------]{2:│}[4:----------------------]|
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:+}{5:^+-- 6 lines: aa·····}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :1 |
+ ## grid 4
+ {7:-}aa |
+ {7:-}bb |
+ {7:2}cc |
+ {7:2}dd |
+ {7:2}ee |
+ {7:│}ff |
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 0, 0)
+ screen:expect([[
+ {7:+}{5:^+-- 6 lines: aa·····}{2:│}{7:-}aa |
+ {1:~ }{2:│}{7:-}bb |
+ {1:~ }{2:│}{7:2}cc |
+ {1:~ }{2:│}{7:2}dd |
+ {1:~ }{2:│}{7:2}ee |
+ {1:~ }{2:│}{7:│}ff |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :1 |
+ ]])
+ end
+ end)
+
+ it("works with tab", function()
+ insert([[
+ aa
+ bb
+ cc
+ dd
+ ee
+ ff]])
+ feed_command('2')
+ command("set foldcolumn=2")
+ feed('zf3j')
+ feed_command('1')
+ feed('zf2j')
+ feed('zO')
+ feed_command("tab split")
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 1, 1)
+ screen:expect([[
+ ## grid 1
+ {10: + [No Name] }{11: + [No Name] }{2: }{10:X}|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2 (hidden)
+ {7:- }aa |
+ {7:│-}bb |
+ {7:││}cc |
+ {7:││}dd |
+ {7:││}ee |
+ {7:│ }ff |
+ {1:~ }|
+ ## grid 3
+ :tab split |
+ ## grid 4
+ {7:- }^aa |
+ {7:│+}{5:+--- 4 lines: bb··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 2, 1)
+ screen:expect([[
+ {10: + [No Name] }{11: + [No Name] }{2: }{10:X}|
+ {7:- }^aa |
+ {7:│+}{5:+--- 4 lines: bb··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :tab split |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 0, 0)
+ screen:expect([[
+ ## grid 1
+ {10: + [No Name] }{11: + [No Name] }{2: }{10:X}|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [4:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2 (hidden)
+ {7:- }aa |
+ {7:│-}bb |
+ {7:││}cc |
+ {7:││}dd |
+ {7:││}ee |
+ {7:│ }ff |
+ {1:~ }|
+ ## grid 3
+ :tab split |
+ ## grid 4
+ {7:+ }{5:^+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 0)
+ screen:expect([[
+ {10: + [No Name] }{11: + [No Name] }{2: }{10:X}|
+ {7:+ }{5:^+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :tab split |
+ ]])
+ end
+
+ feed_command("tabnext")
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 1, 1)
+ screen:expect([[
+ ## grid 1
+ {11: + [No Name] }{10: + [No Name] }{2: }{10:X}|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:- }^aa |
+ {7:│+}{5:+--- 4 lines: bb··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :tabnext |
+ ## grid 4 (hidden)
+ {7:+ }{5:+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 2, 1)
+ screen:expect([[
+ {11: + [No Name] }{10: + [No Name] }{2: }{10:X}|
+ {7:- }^aa |
+ {7:│+}{5:+--- 4 lines: bb··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :tabnext |
+ ]])
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 2, 0, 0)
+ screen:expect([[
+ ## grid 1
+ {11: + [No Name] }{10: + [No Name] }{2: }{10:X}|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [2:---------------------------------------------]|
+ [3:---------------------------------------------]|
+ ## grid 2
+ {7:+ }{5:^+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ :tabnext |
+ ## grid 4 (hidden)
+ {7:+ }{5:+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ else
+ meths.input_mouse('left', 'press', '', 0, 1, 0)
+ screen:expect([[
+ {11: + [No Name] }{10: + [No Name] }{2: }{10:X}|
+ {7:+ }{5:^+-- 6 lines: aa···························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :tabnext |
+ ]])
+ end
+ end)
+
it("works with multibyte text", function()
-- Currently the only allowed value of 'maxcombine'
eq(6, meths.get_option('maxcombine'))
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index ef3acd7d2e..8992ee27ce 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -333,10 +333,10 @@ describe('highlight defaults', function()
command('highlight clear EndOfBuffer')
screen:expect{grid=[[
^ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
|
- ]], hl_groups={EndOfBuffer=0, MsgSeparator=2}}
+ ]], hl_groups={EndOfBuffer=1, MsgSeparator=2}}
end)
end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 9313a35708..ea8968a653 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -1,16 +1,18 @@
local helpers = require('test.functional.helpers')(after_each)
-local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim
+local clear, feed_command = helpers.clear, helpers.feed_command
local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq
local command = helpers.command
local expect = helpers.expect
+local meths = helpers.meths
+local exec_lua = helpers.exec_lua
local write_file = helpers.write_file
local Screen = require('test.functional.ui.screen')
-describe('mappings', function()
- local cid
+before_each(clear)
+describe('mappings', function()
local add_mapping = function(mapping, send)
- local cmd = "nnoremap "..mapping.." :call rpcnotify("..cid..", 'mapped', '"
+ local cmd = "nnoremap "..mapping.." :call rpcnotify(1, 'mapped', '"
..send:gsub('<', '<lt>').."')<cr>"
feed_command(cmd)
end
@@ -21,8 +23,6 @@ describe('mappings', function()
end
before_each(function()
- clear()
- cid = nvim('get_api_info')[1]
add_mapping('<C-L>', '<C-L>')
add_mapping('<C-S-L>', '<C-S-L>')
add_mapping('<s-up>', '<s-up>')
@@ -115,7 +115,6 @@ describe('mappings', function()
end)
describe('input utf sequences that contain CSI/K_SPECIAL', function()
- before_each(clear)
it('ok', function()
feed('i…<esc>')
expect('…')
@@ -129,7 +128,6 @@ describe('input non-printable chars', function()
it("doesn't crash when echoing them back", function()
write_file("Xtest-overwrite", [[foobar]])
- clear()
local screen = Screen.new(60,8)
screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue1},
@@ -215,3 +213,27 @@ describe('input non-printable chars', function()
]])
end)
end)
+
+describe("event processing and input", function()
+ it('not blocked by event bursts', function()
+ meths.set_keymap('', '<f2>', "<cmd>lua vim.rpcnotify(1, 'stop') winning = true <cr>", {noremap=true})
+
+ exec_lua [[
+ winning = false
+ burst = vim.schedule_wrap(function(tell)
+ if tell then
+ vim.rpcnotify(1, 'start')
+ end
+ -- Are we winning, son?
+ if not winning then
+ burst(false)
+ end
+ end)
+ burst(true)
+ ]]
+
+ eq({'notification', 'start', {}}, next_msg())
+ feed '<f2>'
+ eq({'notification', 'stop', {}}, next_msg())
+ end)
+end)
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 1fe3a4128e..9d7719a7c0 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -456,6 +456,8 @@ describe('ui/ext_messages', function()
{1:~ }|
]], messages={
{kind="echomsg", content={{"stuff"}}},
+ }, showmode={
+ { "-- INSERT --", 3 }
}}
end)
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index ff9f30d0a1..958e137f65 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -24,10 +24,6 @@ describe('screen', function()
} )
end)
- after_each(function()
- screen:detach()
- end)
-
it('default initial screen', function()
screen:expect([[
^ |
@@ -67,10 +63,6 @@ local function screen_tests(linegrid)
} )
end)
- after_each(function()
- screen:detach()
- end)
-
describe(':suspend', function()
it('is forwarded to the UI', function()
local function check()
@@ -1004,3 +996,39 @@ describe('Screen default colors', function()
end}
end)
end)
+
+
+describe('screen with msgsep deactivated on startup', function()
+ local screen
+
+ before_each(function()
+ clear('--cmd', 'set display-=msgsep')
+ screen = Screen.new()
+ screen:attach()
+ screen:set_default_attr_ids {
+ [0] = {bold=true, foreground=255};
+ [7] = {bold = true, foreground = Screen.colors.SeaGreen};
+ }
+ end)
+
+ it('execute command with multi-line output', function()
+ feed ':ls<cr>'
+ screen:expect([[
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+ feed '<cr>' -- skip the "Press ENTER..." state or tests will hang
+ end)
+end)
diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua
index 9acc61e398..077d816903 100644
--- a/test/functional/viml/errorlist_spec.lua
+++ b/test/functional/viml/errorlist_spec.lua
@@ -68,4 +68,17 @@ describe('setloclist()', function()
command('lclose | wincmd w | lopen')
eq('foo', get_cur_win_var('quickfix_title'))
end)
+
+ it("doesn't crash when when window is closed in the middle #13721", function()
+ helpers.insert([[
+ hello world]])
+
+ command("vsplit")
+ command("autocmd WinLeave * :call nvim_win_close(0, v:true)")
+
+ command("call setloclist(0, [])")
+ command("lopen")
+
+ helpers.assert_alive()
+ end)
end)
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index aab8eb4464..d0e7cdc9e3 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -147,8 +147,8 @@ set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-3.0.0
set(MSGPACK_SHA256 bfbb71b7c02f806393bc3cbc491b40523b89e64f83860c58e3e54af47de176e4)
# https://github.com/LuaJIT/LuaJIT/tree/v2.1
-set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/1d8b747c161db457e032a023ebbff511f5de5ec2.tar.gz)
-set(LUAJIT_SHA256 20a159c38a98ecdb6368e8d655343b6036622a29a1621da9dc303f7ed9bf37f3)
+set(LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/787736990ac3b7d5ceaba2697c7d0f58f77bb782.tar.gz)
+set(LUAJIT_SHA256 2e3f74bc279f46cc463abfc67b36e69faaf0366237004771f4cac4bf2a9f5efb)
set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz)
set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333)
@@ -196,11 +196,12 @@ set(GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47
set(LIBICONV_URL https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.15.tar.gz)
set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178)
-set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/99151b1.tar.gz)
-set(TREESITTER_C_SHA256 950386f9ba77fb6a7e992198d4f219c34238a2bbc005c5f53c4212d0f8772b06)
+set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/5aa0bbb.tar.gz)
+set(TREESITTER_C_SHA256 a5dcb37460d83002dfae7f9a208170ddbc9a047f231b9d6b75da7d36d707db2f)
-set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/0.18.0.zip)
-set(TREESITTER_SHA256 ac53b7708ca47161dac7f8e852bd61accb8527d45b7ad72e29e12e8e72dbe440)
+# This is 0.19.4+2, fixes some segfaults https://github.com/tree-sitter/tree-sitter/commit/89e1157a299596f3ce2155ba9fd69d5e2c03d3e6
+set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/89e1157a299596f3ce2155ba9fd69d5e2c03d3e6.zip)
+set(TREESITTER_SHA256 d5be9fd92cbf783680f921b2adccbd721b9aa8b2c445114a216b6544330f252c)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)