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--contrib/flake.nix16
-rw-r--r--runtime/doc/api.txt88
-rw-r--r--runtime/doc/eval.txt21
-rw-r--r--runtime/doc/lsp.txt45
-rw-r--r--runtime/doc/lua.txt12
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt8
-rw-r--r--runtime/filetype.vim3
-rw-r--r--runtime/lua/vim/lsp.lua75
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua4
-rw-r--r--runtime/lua/vim/lsp/handlers.lua4
-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/util.lua248
-rw-r--r--runtime/lua/vim/shared.lua14
-rw-r--r--runtime/lua/vim/treesitter.lua6
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim168
-rw-r--r--runtime/queries/c/highlights.scm17
-rw-r--r--runtime/syntax/lsp_markdown.vim15
-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/vim.c101
-rw-r--r--src/nvim/autocmd.c20
-rw-r--r--src/nvim/channel.c17
-rw-r--r--src/nvim/decoration.c9
-rw-r--r--src/nvim/decoration.h15
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/eval.c27
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c92
-rw-r--r--src/nvim/eval/typval.h5
-rw-r--r--src/nvim/ex_cmds.c9
-rw-r--r--src/nvim/ex_getln.c15
-rw-r--r--src/nvim/ex_session.c3
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua14
-rw-r--r--src/nvim/generators/gen_eval.lua2
-rw-r--r--src/nvim/grid_defs.h8
-rw-r--r--src/nvim/if_cscope.c2
-rw-r--r--src/nvim/lua/treesitter.c5
-rw-r--r--src/nvim/lua/vim.lua11
-rw-r--r--src/nvim/memline.c4
-rw-r--r--src/nvim/mouse.c17
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/options.lua1
-rw-r--r--src/nvim/os/input.c20
-rw-r--r--src/nvim/screen.c47
-rw-r--r--src/nvim/state.c28
-rw-r--r--src/nvim/terminal.c40
-rw-r--r--src/nvim/testdir/check.vim27
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_autocmd.vim16
-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_filetype.vim1
-rw-r--r--src/nvim/testdir/test_mksession.vim14
-rw-r--r--src/nvim/testdir/test_options.vim29
-rw-r--r--src/nvim/testdir/test_startup.vim162
-rw-r--r--src/nvim/testdir/test_window_cmd.vim49
-rw-r--r--src/nvim/ui_compositor.c17
-rw-r--r--src/nvim/window.c23
-rw-r--r--test/functional/api/vim_spec.lua63
-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.lua14
-rw-r--r--test/functional/lua/vim_spec.lua5
-rw-r--r--test/functional/plugin/lsp_spec.lua181
-rw-r--r--test/functional/ui/decorations_spec.lua251
-rw-r--r--test/functional/ui/float_spec.lua149
-rw-r--r--test/functional/ui/fold_spec.lua200
-rw-r--r--test/functional/ui/input_spec.lua38
-rw-r--r--third-party/CMakeLists.txt12
80 files changed, 2484 insertions, 370 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/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/doc/api.txt b/runtime/doc/api.txt
index fdc41af1d5..c72381fd06 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
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index be7c026f5a..1832e2443f 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2498,6 +2498,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}
@@ -9370,6 +9372,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/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/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/filetype.vim b/runtime/filetype.vim
index 6a13e67ac5..4130db2534 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1330,6 +1330,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
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 841c365cbe..f0845bfc80 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -42,6 +42,8 @@ lsp._request_name_to_capability = {
['textDocument/prepareCallHierarchy'] = 'call_hierarchy';
['textDocument/rename'] = 'rename';
['textDocument/codeAction'] = 'code_action';
+ ['textDocument/codeLens'] = 'code_lens';
+ ['codeLens/resolve'] = 'code_lens_resolve';
['workspace/executeCommand'] = 'execute_command';
['textDocument/references'] = 'find_references';
['textDocument/rangeFormatting'] = 'document_range_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,18 +265,29 @@ 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);
}
}
@@ -400,6 +414,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 +455,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 +467,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 {}
@@ -808,7 +821,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)
@@ -827,23 +839,12 @@ do
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');
- };
+ 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._cached_buffers[bufnr] = lines
+ return incremental_change
end)
local full_changes = once(function()
return {
@@ -851,19 +852,12 @@ do
};
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)
-
+ 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
@@ -931,6 +925,9 @@ function lsp.buf_attach_client(bufnr, client_id)
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..a1f24706c0 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -264,10 +264,14 @@ 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
+ local start = diagnostic.range.start
+ start.line = math.min(start.line, buf_line_count - 1)
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..0cf80e1443 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)
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/util.lua b/runtime/lua/vim/lsp/util.lua
index 6fb9f09c99..5c139253ee 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -226,6 +226,165 @@ 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
+--@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)
+ 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 result = {
+ range = {
+ start = { line = start_line - 1, character = start_char - 1},
+ ["end"] = { line = adj_end_line, character = adj_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.
@@ -453,6 +612,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 +675,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 +911,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 +1097,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 +1201,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 +1214,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 +1230,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..64a5ba1fd8 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -12,11 +12,7 @@ local M = vim.tbl_extend("error", query, language)
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/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/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/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/vim.c b/src/nvim/api/vim.c
index b94c99dc5e..6358f35d0a 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.
@@ -1546,8 +1639,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)
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 3de2e0f342..2ddb925d40 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
}
@@ -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/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/decoration.c b/src/nvim/decoration.c
index a1289f202a..9a20b06660 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -230,7 +230,7 @@ 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_pos, decor->virt_text_hide, decor->hl_mode,
kv_size(decor->virt_text) && owned, -1 };
kv_pushp(state->active);
@@ -245,7 +245,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;
@@ -324,7 +325,7 @@ next_mark:
}
if ((item.start_row == state->row && item.start_col <= col)
&& item.virt_text && item.virt_col == -1) {
- item.virt_col = virt_col;
+ item.virt_col = (item.virt_text_hide && hidden) ? -2 : virt_col;
}
if (keep) {
kv_A(state->active, j++) = item;
@@ -345,7 +346,7 @@ void decor_redraw_end(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
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 47bd9abbc3..264e8a4a82 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;
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..68c7438ea3 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1024,7 +1024,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
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6d97310c1c..f190ef14c4 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,14 +5281,10 @@ 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;
@@ -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;
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index eac0feafcf..e94d7831b0 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -392,6 +392,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..9edf5c8f7e 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)
{
@@ -10730,7 +10816,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);
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 6fcb01aace..531b17cb59 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -341,8 +341,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/ex_cmds.c b/src/nvim/ex_cmds.c
index 103c081143..854faf5377 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1407,19 +1407,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_getln.c b/src/nvim/ex_getln.c
index d470bfb418..9977be56ca 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -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);
}
@@ -5117,6 +5117,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 +5207,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/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/grid_defs.h b/src/nvim/grid_defs.h
index e14aae73d8..752be4f5a8 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -76,6 +76,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;
@@ -86,6 +92,6 @@ typedef struct {
} 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, false, true, 0, 0, 0, 0, 0, false }
#endif // NVIM_GRID_DEFS_H
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/lua/treesitter.c b/src/nvim/lua/treesitter.c
index a640b97d3b..33974c71cb 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);
}
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/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/mouse.c b/src/nvim/mouse.c
index f28658aa29..9dc85797b4 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -717,23 +717,22 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// Check clicked cell is foldcolumn
int mouse_check_fold(void)
{
- int grid = mouse_grid;
- int row = mouse_row;
- int col = mouse_col;
+ 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(&grid, &row, &col);
+ 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 : &default_grid;
int fdc = win_fdccol_count(wp);
-
- row = multigrid && mouse_grid == 0 ? row : mouse_row;
- col = multigrid && mouse_grid == 0 ? col : mouse_col;
+ 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.
@@ -743,8 +742,8 @@ int mouse_check_fold(void)
}
// Check for position outside of the fold column.
- if (wp->w_p_rl ? col < wp->w_width_inner - fdc :
- col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
+ if (wp->w_p_rl ? click_col < wp->w_width_inner - fdc :
+ click_col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
mouse_char = ' ';
}
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 4d8b11f832..0b4e2e1f23 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -8103,7 +8103,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/option.c b/src/nvim/option.c
index ac25c86b5f..dbd8ceb55c 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2086,9 +2086,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 +2100,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;
@@ -6178,6 +6182,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
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index fe108ef1cc..f4c1ac9131 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=""}}
},
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/screen.c b/src/nvim/screen.c
index 20e3cc0a2e..467cac4f27 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2096,6 +2096,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 */
@@ -2656,7 +2658,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;
@@ -2850,6 +2852,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 +2942,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) {
@@ -3397,9 +3409,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 +3427,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))) {
@@ -4355,10 +4369,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);
@@ -6248,6 +6274,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;
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/terminal.c b/src/nvim/terminal.c
index 642c443318..913ef3baed 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);
@@ -1324,6 +1335,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/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/test_alot.vim b/src/nvim/testdir/test_alot.vim
index daf3c9c110..a47d20a265 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -33,7 +33,6 @@ source test_move.vim
source test_partial.vim
source test_popup.vim
source test_put.vim
-source test_recover.vim
source test_scroll_opt.vim
source test_sort.vim
source test_sha256.vim
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1f3a45a9ab..7788e09d43 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1922,20 +1922,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_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_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 6bc5fba5db..76e5de7df9 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'],
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 d23f0b5021..07e2481f95 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -230,6 +230,13 @@ func Test_set_completion()
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()
@@ -627,6 +634,28 @@ func Test_opt_winminheight_term()
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
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index e6ad92f483..4d1ad10c23 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.
@@ -777,17 +904,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 +923,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_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 969b75d424..86682cca4f 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -808,6 +808,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_compositor.c b/src/nvim/ui_compositor.c
index 06efc9fa99..946215d957 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()) {
@@ -334,17 +337,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/window.c b/src/nvim/window.c
index 0f717a2f90..eef75b10bd 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -710,7 +710,7 @@ 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,
@@ -1619,6 +1619,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.
///
@@ -5501,8 +5518,8 @@ void win_setminheight(void)
// loop until there is a 'winminheight' that is possible
while (p_wmh > 0) {
- const int room = Rows - p_ch - tabline_height();
- const int needed = frame_minheight(topframe, NULL);
+ const int room = Rows - p_ch;
+ const int needed = min_rows() - 1; // 1 was added for the cmdline
if (room >= needed) {
break;
}
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/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 d7f5df3a1e..023cdd4ae1 100644
--- a/test/functional/legacy/options_spec.lua
+++ b/test/functional/legacy/options_spec.lua
@@ -42,6 +42,20 @@ describe('set', function()
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()
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/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 7a87521a6b..7f4ab3ee5d 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=[[
@@ -314,11 +315,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 +351,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/float_spec.lua b/test/functional/ui/float_spec.lua
index 32f9ae030f..3ad14e749e 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -5433,6 +5433,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 d3b1d33956..8883ad8270 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -464,6 +464,206 @@ describe("folded lines", function()
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
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/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index aab8eb4464..578ad982dc 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,11 @@ 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)
+set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.19.3.zip)
+set(TREESITTER_SHA256 1a2c5b816fa7f78587672a022a5f671004ac656ebad39857b3c15442c657fcb0)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)