aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt2
-rw-r--r--MAINTAIN.md10
-rw-r--r--Makefile5
-rw-r--r--README.md20
-rw-r--r--appveyor.yml15
-rw-r--r--ci/build.ps127
-rw-r--r--man/nvim.12
-rw-r--r--runtime/autoload/dist/ft.vim26
-rw-r--r--runtime/autoload/health/nvim.vim11
-rw-r--r--runtime/autoload/health/provider.vim7
-rw-r--r--runtime/autoload/provider/clipboard.vim4
-rw-r--r--runtime/autoload/spellfile.vim14
-rw-r--r--runtime/doc/api.txt676
-rw-r--r--runtime/doc/autocmd.txt97
-rw-r--r--runtime/doc/change.txt19
-rw-r--r--runtime/doc/cmdline.txt3
-rw-r--r--runtime/doc/debug.txt11
-rw-r--r--runtime/doc/deprecated.txt11
-rw-r--r--runtime/doc/develop.txt26
-rw-r--r--runtime/doc/digraph.txt19
-rw-r--r--runtime/doc/editing.txt7
-rw-r--r--runtime/doc/eval.txt568
-rw-r--r--runtime/doc/if_lua.txt301
-rw-r--r--runtime/doc/indent.txt5
-rw-r--r--runtime/doc/insert.txt23
-rw-r--r--runtime/doc/intro.txt20
-rw-r--r--runtime/doc/map.txt24
-rw-r--r--runtime/doc/message.txt7
-rw-r--r--runtime/doc/motion.txt5
-rw-r--r--runtime/doc/msgpack_rpc.txt11
-rw-r--r--runtime/doc/nvim.txt10
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt284
-rw-r--r--runtime/doc/options.txt105
-rw-r--r--runtime/doc/provider.txt46
-rw-r--r--runtime/doc/quickfix.txt6
-rw-r--r--runtime/doc/quickref.txt2
-rw-r--r--runtime/doc/remote_plugin.txt8
-rw-r--r--runtime/doc/repeat.txt18
-rw-r--r--runtime/doc/starting.txt127
-rw-r--r--runtime/doc/syntax.txt2
-rw-r--r--runtime/doc/tagsrch.txt42
-rw-r--r--runtime/doc/ui.txt430
-rw-r--r--runtime/doc/usr_11.txt7
-rw-r--r--runtime/doc/usr_40.txt10
-rw-r--r--runtime/doc/usr_41.txt4
-rw-r--r--runtime/doc/usr_43.txt2
-rw-r--r--runtime/doc/various.txt179
-rw-r--r--runtime/doc/vim_diff.txt21
-rw-r--r--runtime/doc/windows.txt8
-rw-r--r--runtime/filetype.vim40
-rw-r--r--runtime/indent/vim.vim18
-rw-r--r--runtime/lua/vim/shared.lua179
-rw-r--r--runtime/nvim.appdata.xml11
-rw-r--r--runtime/nvim.pngbin6432 -> 5887 bytes
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim911
-rw-r--r--runtime/syntax/tutor.vim36
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor6
-rwxr-xr-xscripts/gen_api_vimdoc.py563
-rw-r--r--scripts/gen_help_html.py74
-rwxr-xr-xscripts/gen_vimdoc.py763
-rwxr-xr-xscripts/genappimage.sh52
-rw-r--r--scripts/lua2dox.lua666
-rwxr-xr-xscripts/lua2dox_filter87
-rwxr-xr-xscripts/vim-patch.sh47
-rw-r--r--src/Doxyfile6
-rwxr-xr-xsrc/clint.py2
-rw-r--r--src/nvim/api/buffer.c116
-rw-r--r--src/nvim/api/private/defs.h11
-rw-r--r--src/nvim/api/private/helpers.c226
-rw-r--r--src/nvim/api/private/helpers.h1
-rw-r--r--src/nvim/api/ui.c79
-rw-r--r--src/nvim/api/ui_events.in.h6
-rw-r--r--src/nvim/api/vim.c358
-rw-r--r--src/nvim/api/window.c71
-rw-r--r--src/nvim/auevents.lua4
-rw-r--r--src/nvim/buffer.c1640
-rw-r--r--src/nvim/buffer.h9
-rw-r--r--src/nvim/buffer_defs.h140
-rw-r--r--src/nvim/charset.c7
-rw-r--r--src/nvim/edit.c274
-rw-r--r--src/nvim/eval.c551
-rw-r--r--src/nvim/eval.lua8
-rw-r--r--src/nvim/eval/typval.c28
-rw-r--r--src/nvim/event/rstream.c10
-rw-r--r--src/nvim/event/stream.c10
-rw-r--r--src/nvim/event/stream.h7
-rw-r--r--src/nvim/ex_cmds.c49
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_cmds2.c45
-rw-r--r--src/nvim/ex_docmd.c80
-rw-r--r--src/nvim/ex_eval.c7
-rw-r--r--src/nvim/ex_getln.c85
-rw-r--r--src/nvim/file_search.c11
-rw-r--r--src/nvim/fileio.c283
-rw-r--r--src/nvim/fileio.h1
-rw-r--r--src/nvim/func_attr.h5
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua73
-rw-r--r--src/nvim/generators/gen_unicode_tables.lua2
-rw-r--r--src/nvim/getchar.c847
-rw-r--r--src/nvim/getchar.h34
-rw-r--r--src/nvim/gettext.h5
-rw-r--r--src/nvim/globals.h5
-rw-r--r--src/nvim/highlight.c7
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/if_cscope.c4
-rw-r--r--src/nvim/keymap.c27
-rw-r--r--src/nvim/keymap.h56
-rw-r--r--src/nvim/lib/kbtree.h20
-rw-r--r--src/nvim/log.c21
-rw-r--r--src/nvim/lua/executor.c27
-rw-r--r--src/nvim/lua/vim.lua124
-rw-r--r--src/nvim/main.c31
-rw-r--r--src/nvim/map.c12
-rw-r--r--src/nvim/mark.c103
-rw-r--r--src/nvim/mbyte.c159
-rw-r--r--src/nvim/memline.c268
-rw-r--r--src/nvim/menu.c31
-rw-r--r--src/nvim/message.c38
-rw-r--r--src/nvim/misc1.c28
-rw-r--r--src/nvim/move.c50
-rw-r--r--src/nvim/msgpack_rpc/channel.c67
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h7
-rw-r--r--src/nvim/msgpack_rpc/helpers.c39
-rw-r--r--src/nvim/normal.c18
-rw-r--r--src/nvim/ops.c57
-rw-r--r--src/nvim/option.c2317
-rw-r--r--src/nvim/option_defs.h24
-rw-r--r--src/nvim/options.lua35
-rw-r--r--src/nvim/os/env.c43
-rw-r--r--src/nvim/os/fs.c115
-rw-r--r--src/nvim/os/process.c6
-rw-r--r--src/nvim/os/stdpaths.c10
-rw-r--r--src/nvim/os/stdpaths_defs.h1
-rw-r--r--src/nvim/path.c9
-rw-r--r--src/nvim/popupmnu.c57
-rw-r--r--src/nvim/popupmnu.h1
-rw-r--r--src/nvim/quickfix.c2400
-rw-r--r--src/nvim/regexp.c16
-rw-r--r--src/nvim/regexp_defs.h6
-rw-r--r--src/nvim/regexp_nfa.c23
-rw-r--r--src/nvim/screen.c296
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/shada.c12
-rw-r--r--src/nvim/sign_defs.h1
-rw-r--r--src/nvim/spell.c17
-rw-r--r--src/nvim/spellfile.c10
-rw-r--r--src/nvim/strings.c16
-rw-r--r--src/nvim/syntax.c43
-rw-r--r--src/nvim/tag.c331
-rw-r--r--src/nvim/terminal.c153
-rw-r--r--src/nvim/testdir/Makefile10
-rw-r--r--src/nvim/testdir/load.vim6
-rw-r--r--src/nvim/testdir/test14.in94
-rw-r--r--src/nvim/testdir/test14.ok26
-rw-r--r--src/nvim/testdir/test16.in15
-rw-r--r--src/nvim/testdir/test16.ok2
-rw-r--r--src/nvim/testdir/test17.in126
-rw-r--r--src/nvim/testdir/test17.ok33
-rw-r--r--src/nvim/testdir/test17a.in3
-rw-r--r--src/nvim/testdir/test37.in116
-rw-r--r--src/nvim/testdir/test37.ok33
-rw-r--r--src/nvim/testdir/test50.in89
-rw-r--r--src/nvim/testdir/test50.ok14
-rw-r--r--src/nvim/testdir/test_arglist.vim54
-rw-r--r--src/nvim/testdir/test_autocmd.vim379
-rw-r--r--src/nvim/testdir/test_cd.vim3
-rw-r--r--src/nvim/testdir/test_checkpath.vim113
-rw-r--r--src/nvim/testdir/test_clientserver.vim3
-rw-r--r--src/nvim/testdir/test_command_count.vim1
-rw-r--r--src/nvim/testdir/test_debugger.vim232
-rw-r--r--src/nvim/testdir/test_diffmode.vim34
-rw-r--r--src/nvim/testdir/test_edit.vim48
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim28
-rw-r--r--src/nvim/testdir/test_filetype.vim5
-rw-r--r--src/nvim/testdir/test_findfile.vim1
-rw-r--r--src/nvim/testdir/test_functions.vim31
-rw-r--r--src/nvim/testdir/test_gf.vim35
-rw-r--r--src/nvim/testdir/test_highlight.vim46
-rw-r--r--src/nvim/testdir/test_jumplist.vim66
-rw-r--r--src/nvim/testdir/test_lambda.vim6
-rw-r--r--src/nvim/testdir/test_listchars.vim39
-rw-r--r--src/nvim/testdir/test_mapping.vim40
-rw-r--r--src/nvim/testdir/test_match.vim22
-rw-r--r--src/nvim/testdir/test_mksession.vim11
-rw-r--r--src/nvim/testdir/test_modeline.vim84
-rw-r--r--src/nvim/testdir/test_normal.vim95
-rw-r--r--src/nvim/testdir/test_popup.vim181
-rw-r--r--src/nvim/testdir/test_put.vim57
-rw-r--r--src/nvim/testdir/test_python2.vim89
-rw-r--r--src/nvim/testdir/test_python3.vim89
-rw-r--r--src/nvim/testdir/test_quickfix.vim541
-rw-r--r--src/nvim/testdir/test_regex_char_classes.vim240
-rw-r--r--src/nvim/testdir/test_scrollbind.vim240
-rw-r--r--src/nvim/testdir/test_search.vim24
-rw-r--r--src/nvim/testdir/test_substitute.vim116
-rw-r--r--src/nvim/testdir/test_swap.vim163
-rw-r--r--src/nvim/testdir/test_syntax.vim34
-rw-r--r--src/nvim/testdir/test_tabpage.vim32
-rw-r--r--src/nvim/testdir/test_tagjump.vim205
-rw-r--r--src/nvim/testdir/test_taglist.vim38
-rw-r--r--src/nvim/testdir/test_textformat.vim323
-rw-r--r--src/nvim/testdir/test_timers.vim34
-rw-r--r--src/nvim/testdir/test_true_false.vim4
-rw-r--r--src/nvim/testdir/test_undo.vim10
-rw-r--r--src/nvim/testdir/test_visual.vim24
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim26
-rw-r--r--src/nvim/testdir/test_window_cmd.vim50
-rw-r--r--src/nvim/testdir/test_writefile.vim25
-rw-r--r--src/nvim/tui/input.c81
-rw-r--r--src/nvim/tui/input.h3
-rw-r--r--src/nvim/tui/tui.c16
-rw-r--r--src/nvim/ui.c19
-rw-r--r--src/nvim/ui.h6
-rw-r--r--src/nvim/ui_compositor.c6
-rw-r--r--src/nvim/ui_compositor.h2
-rw-r--r--src/nvim/undo.c40
-rw-r--r--src/nvim/version.c52
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c428
-rw-r--r--test/README.md23
-rw-r--r--test/functional/api/buffer_spec.lua91
-rw-r--r--test/functional/api/keymap_spec.lua508
-rw-r--r--test/functional/api/rpc_fixture.lua6
-rw-r--r--test/functional/api/server_requests_spec.lua5
-rw-r--r--test/functional/api/vim_spec.lua34
-rw-r--r--test/functional/autocmd/autocmd_spec.lua154
-rw-r--r--test/functional/autocmd/cursormoved_spec.lua34
-rw-r--r--test/functional/autocmd/textyankpost_spec.lua16
-rw-r--r--test/functional/core/channels_spec.lua8
-rw-r--r--test/functional/core/fileio_spec.lua2
-rw-r--r--test/functional/core/main_spec.lua12
-rw-r--r--test/functional/core/spellfile_spec.lua (renamed from test/functional/spell/spellfile_spec.lua)0
-rw-r--r--test/functional/core/startup_spec.lua38
-rw-r--r--test/functional/eval/api_functions_spec.lua2
-rw-r--r--test/functional/eval/executable_spec.lua65
-rw-r--r--test/functional/eval/exepath_spec.lua14
-rw-r--r--test/functional/eval/let_spec.lua2
-rw-r--r--test/functional/ex_cmds/cd_spec.lua9
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua21
-rw-r--r--test/functional/ex_cmds/help_spec.lua27
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua23
-rw-r--r--test/functional/ex_cmds/oldfiles_spec.lua12
-rw-r--r--test/functional/ex_cmds/quickfix_commands_spec.lua10
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua10
-rw-r--r--test/functional/ex_cmds/wviminfo_spec.lua16
-rw-r--r--test/functional/example_spec.lua36
-rw-r--r--test/functional/helpers.lua169
-rw-r--r--test/functional/legacy/074_global_var_in_viminfo_spec.lua8
-rw-r--r--test/functional/legacy/assert_spec.lua9
-rw-r--r--test/functional/lua/overrides_spec.lua16
-rw-r--r--test/functional/normal/jump_spec.lua48
-rw-r--r--test/functional/options/defaults_spec.lua57
-rw-r--r--test/functional/provider/clipboard_spec.lua (renamed from test/functional/clipboard/clipboard_provider_spec.lua)18
-rw-r--r--test/functional/shada/marks_spec.lua6
-rw-r--r--test/functional/shada/merging_spec.lua11
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua16
-rw-r--r--test/functional/terminal/highlight_spec.lua50
-rw-r--r--test/functional/terminal/scrollback_spec.lua12
-rw-r--r--test/functional/terminal/tui_spec.lua39
-rw-r--r--test/functional/terminal/window_split_tab_spec.lua8
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua2
-rw-r--r--test/functional/ui/cmdline_spec.lua156
-rw-r--r--test/functional/ui/cursor_spec.lua4
-rw-r--r--test/functional/ui/diff_spec.lua408
-rw-r--r--test/functional/ui/embed_spec.lua2
-rw-r--r--test/functional/ui/float_spec.lua916
-rw-r--r--test/functional/ui/fold_spec.lua48
-rw-r--r--test/functional/ui/highlight_spec.lua78
-rw-r--r--test/functional/ui/inccommand_spec.lua27
-rw-r--r--test/functional/ui/input_spec.lua54
-rw-r--r--test/functional/ui/messages_spec.lua139
-rw-r--r--test/functional/ui/multigrid_spec.lua2
-rw-r--r--test/functional/ui/options_spec.lua6
-rw-r--r--test/functional/ui/output_spec.lua3
-rw-r--r--test/functional/ui/popupmenu_spec.lua264
-rw-r--r--test/functional/ui/screen.lua55
-rw-r--r--test/functional/ui/searchhl_spec.lua14
-rw-r--r--test/functional/ui/sign_spec.lua187
-rw-r--r--test/functional/ui/wildmode_spec.lua32
-rw-r--r--test/functional/viml/completion_spec.lua97
-rw-r--r--test/functional/viml/errorlist_spec.lua8
-rw-r--r--test/helpers.lua55
-rw-r--r--test/symbolic/klee/nvim/keymap.c20
-rw-r--r--test/unit/eval/typval_spec.lua6
-rw-r--r--test/unit/helpers.lua5
-rw-r--r--test/unit/undo_spec.lua8
-rw-r--r--test/unit/viml/expressions/lexer_spec.lua6
-rw-r--r--test/unit/viml/expressions/parser_spec.lua12
-rw-r--r--third-party/CMakeLists.txt4
-rw-r--r--third-party/cmake/BuildGperf.cmake6
-rw-r--r--unicode/CaseFolding.txt13
-rw-r--r--unicode/EastAsianWidth.txt114
-rw-r--r--unicode/UnicodeData.txt564
-rw-r--r--unicode/emoji-data.txt305
295 files changed, 20796 insertions, 8590 deletions
diff --git a/.gitignore b/.gitignore
index cf0a11804d..bf9928ce15 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Tools
.ropeproject/
+compile_commands.json
# Visual Studio
/.vs/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 848e100b02..43281dee2c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -159,7 +159,7 @@ endif()
# Log level (MIN_LOG_LEVEL in log.h)
if("${MIN_LOG_LEVEL}" MATCHES "^$")
- message(STATUS "MIN_LOG_LEVEL not specified, default is 0 (DEBUG)")
+ message(STATUS "MIN_LOG_LEVEL not specified, default is 1 (INFO)")
else()
if(NOT MIN_LOG_LEVEL MATCHES "^[0-3]$")
message(FATAL_ERROR "invalid MIN_LOG_LEVEL: " ${MIN_LOG_LEVEL})
diff --git a/MAINTAIN.md b/MAINTAIN.md
index 55f4e7afc2..e102d7a94b 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -44,14 +44,16 @@ Release Policy
Release "often", but not "early".
The (unreleased) `master` branch is the "early" channel; it should not be
-released if it's not stable. Medium-risk changes may be merged to `master` if
+released if it's not stable. High-risk changes may be merged to `master` if
the next feature-release is not imminent.
-For maintenance releases, create a `release-x.y` branch. If the current stable
-release has a major bug:
+For maintenance releases, create a `release-x.y` branch. If the current release
+has a major bug:
1. Fix the bug on `master`.
2. Cherry-pick the fix to `release-x.y`.
-3. Cut a release from `release-x.y` (run `scripts/release.sh`).
+3. Cut a release from `release-x.y`.
+ - Run `./scripts/release.sh`
+ - Update (force-push) the remote `stable` tag.
See also: https://github.com/neovim/neovim/issues/862
diff --git a/Makefile b/Makefile
index 6921209fdb..4142cacc51 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,9 @@ CMAKE_EXTRA_FLAGS ?=
# - `checkprefix` target checks that it matches the CMake-cached value. #9615
CMAKE_INSTALL_PREFIX ?= $(shell echo $(CMAKE_EXTRA_FLAGS) | 2>/dev/null \
grep -o 'CMAKE_INSTALL_PREFIX=[^ ]\+' | cut -d '=' -f2)
+ifneq (,$(CMAKE_INSTALL_PREFIX))
+ CMAKE_FLAGS += -DCMAKE_INSTALL_PREFIX=$(CMAKE_INSTALL_PREFIX)
+endif
BUILD_TYPE ?= $(shell (type ninja > /dev/null 2>&1 && echo "Ninja") || \
echo "Unix Makefiles")
@@ -166,7 +169,7 @@ appimage-%:
lint: check-single-includes clint testlint lualint
checkprefix:
- @cached_prefix=$$($(CMAKE_PRG) -L -N build | 2>/dev/null grep 'CMAKE_INSTALL_PREFIX' | cut -d '=' -f2); \
+ @cached_prefix=$$("$(CMAKE_PRG)" -L -N build | 2>/dev/null grep 'CMAKE_INSTALL_PREFIX' | cut -d '=' -f2); \
if [ -n "$(CMAKE_INSTALL_PREFIX)" ] && [ -n "$$cached_prefix" ] && ! [ "$(CMAKE_INSTALL_PREFIX)" = "$$cached_prefix" ]; then \
printf "\nerror: CMAKE_INSTALL_PREFIX '$(CMAKE_INSTALL_PREFIX)' does not match cached value '%s'\n" "$$cached_prefix"; \
printf " Run this command, then try again:\n"; \
diff --git a/README.md b/README.md
index 4979ab53ef..1bdf33d6bb 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,9 @@
-[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-600x173.png)](https://neovim.io)
+[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png)](https://neovim.io)
[Wiki](https://github.com/neovim/neovim/wiki) |
[Documentation](https://neovim.io/doc) |
-[Twitter](https://twitter.com/Neovim) |
-[Community](https://neovim.io/community/) |
-[Gitter **Chat**](https://gitter.im/neovim/neovim)
+[Chat/Discussion](https://gitter.im/neovim/neovim) |
+[Twitter](https://twitter.com/Neovim)
[![Travis build status](https://travis-ci.org/neovim/neovim.svg?branch=master)](https://travis-ci.org/neovim/neovim)
[![AppVeyor build status](https://ci.appveyor.com/api/projects/status/urdqjrik5u521fac/branch/master?svg=true)](https://ci.appveyor.com/project/neovim/neovim/branch/master)
@@ -27,16 +26,13 @@ Neovim is a project that seeks to aggressively refactor Vim in order to:
See the [Introduction](https://github.com/neovim/neovim/wiki/Introduction) wiki page and [Roadmap]
for more information.
-[![Throughput Graph](https://graphs.waffle.io/neovim/neovim/throughput.svg)](https://waffle.io/neovim/neovim/metrics)
-
Features
--------
- Modern [GUIs](https://github.com/neovim/neovim/wiki/Related-projects#gui)
- [API access](https://github.com/neovim/neovim/wiki/Related-projects#api-clients)
- from any language including C/C++, C#, Clojure, D, Elixir, Lisp, Go,
- Haskell, Java, JavaScript/Node.js, Julia, Lisp, Lua, Perl, Python, Racket,
- Ruby, Rust
+ from any language including C/C++, C#, Clojure, D, Elixir, Go, Haskell, Java,
+ JavaScript/Node.js, Julia, Lisp, Lua, Perl, Python, Racket, Ruby, Rust
- Embedded, scriptable [terminal emulator](https://neovim.io/doc/user/nvim_terminal_emulator.html)
- Asynchronous [job control](https://github.com/neovim/neovim/pull/2247)
- [Shared data (shada)](https://github.com/neovim/neovim/pull/2506) among multiple editor instances
@@ -51,8 +47,8 @@ Install from package
Pre-built packages for Windows, macOS, and Linux are found on the
[Releases](https://github.com/neovim/neovim/releases/) page.
-Managed packages are in [Homebrew], [Debian], [Ubuntu], [Fedora], [Arch Linux], [Gentoo],
-and [more](https://github.com/neovim/neovim/wiki/Installing-Neovim)!
+[Managed packages] are in Homebrew, [Debian], [Ubuntu], [Fedora], [Arch Linux],
+[Gentoo], and more!
Install from source
-------------------
@@ -144,7 +140,7 @@ See `LICENSE` for details.
[nvim-features]: https://neovim.io/doc/user/vim_diff.html#nvim-features
[Roadmap]: https://neovim.io/roadmap/
[advanced UIs]: https://github.com/neovim/neovim/wiki/Related-projects#gui
-[Homebrew]: https://github.com/neovim/homebrew-neovim#installation
+[Managed packages]: https://github.com/neovim/neovim/wiki/Installing-Neovim#install-from-package
[Debian]: https://packages.debian.org/testing/neovim
[Ubuntu]: http://packages.ubuntu.com/search?keywords=neovim
[Fedora]: https://apps.fedoraproject.org/packages/neovim
diff --git a/appveyor.yml b/appveyor.yml
index f47ec236e5..0de9174adf 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,8 @@
version: '{build}'
environment:
APPVEYOR_CACHE_ENTRY_ZIP_ARGS: "-t7z -m0=lzma -mx=9"
+ DEPS_BUILD_DIR: "C:/projects/nvim-deps"
+ DEPS_PREFIX: "C:/projects/nvim-deps/usr"
image: Visual Studio 2017
configuration:
- MSVC_64
@@ -8,6 +10,15 @@ configuration:
- MINGW_64
- MINGW_32
- MINGW_64-gcov
+init:
+- ps: |
+ # Pull requests: skip some build configurations to save time.
+ if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT -and $env:CONFIGURATION -match '^(MSVC_64|MINGW_32|MINGW_64-gcov)$') {
+ $env:APPVEYOR_CACHE_SKIP_SAVE = "true"
+ Exit-AppVeyorBuild
+ }
+# RDP
+#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
matrix:
allow_failures:
- configuration: MINGW_64-gcov
@@ -17,9 +28,7 @@ before_build:
build_script:
- powershell ci\build.ps1
cache:
-- C:\msys64\var\cache\pacman\pkg -> ci\build.ps1
-- .deps -> ci\build.ps1
-- .deps -> third-party\**
+- C:\projects\nvim-deps -> third-party\**
artifacts:
- path: build/Neovim.zip
- path: build/bin/nvim.exe
diff --git a/ci/build.ps1 b/ci/build.ps1
index 1ba3fa8a6b..4c47ac1c51 100644
--- a/ci/build.ps1
+++ b/ci/build.ps1
@@ -1,25 +1,24 @@
$ErrorActionPreference = 'stop'
Set-PSDebug -Strict -Trace 1
+$isPullRequest = ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT -ne $null)
$env:CONFIGURATION -match '^(?<compiler>\w+)_(?<bits>32|64)(?:-(?<option>\w+))?$'
$compiler = $Matches.compiler
$compileOption = $Matches.option
$bits = $Matches.bits
-$cmakeBuildType = 'RelWithDebInfo'
+$cmakeBuildType = $(if ($env:CMAKE_BUILD_TYPE -ne $null) {$env:CMAKE_BUILD_TYPE} else {'RelWithDebInfo'});
+$buildDir = [System.IO.Path]::GetFullPath("$(pwd)")
$depsCmakeVars = @{
CMAKE_BUILD_TYPE = $cmakeBuildType;
}
$nvimCmakeVars = @{
CMAKE_BUILD_TYPE = $cmakeBuildType;
BUSTED_OUTPUT_TYPE = 'nvim';
+ DEPS_BUILD_DIR=$(if ($env:DEPS_BUILD_DIR -ne $null) {$env:DEPS_BUILD_DIR} else {".deps"});
+ DEPS_PREFIX=$(if ($env:DEPS_PREFIX -ne $null) {$env:DEPS_PREFIX} else {".deps/usr"});
}
$uploadToCodeCov = $false
-# For pull requests, skip some build configurations to save time.
-if ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT -and $env:CONFIGURATION -match '^(MSVC_64|MINGW_32|MINGW_64-gcov)$') {
- exit 0
-}
-
function exitIfFailed() {
if ($LastExitCode -ne 0) {
Set-PSDebug -Off
@@ -27,6 +26,13 @@ function exitIfFailed() {
}
}
+if (-Not (Test-Path -PathType container $nvimCmakeVars["DEPS_BUILD_DIR"])) {
+ write-host "cache dir not found: $($nvimCmakeVars['DEPS_BUILD_DIR'])"
+ mkdir $nvimCmakeVars["DEPS_BUILD_DIR"]
+} else {
+ write-host "cache dir $($nvimCmakeVars['DEPS_BUILD_DIR']) size: $(Get-ChildItem $nvimCmakeVars['DEPS_BUILD_DIR'] -recurse | Measure-Object -property length -sum | Select -expand sum)"
+}
+
if ($compiler -eq 'MINGW') {
if ($bits -eq 32) {
$arch = 'i686'
@@ -89,13 +95,10 @@ function convertToCmakeArgs($vars) {
return $vars.GetEnumerator() | foreach { "-D$($_.Key)=$($_.Value)" }
}
-if (-Not (Test-Path -PathType container .deps)) {
- mkdir .deps
-}
-cd .deps
-cmake -G $cmakeGenerator $(convertToCmakeArgs($depsCmakeVars)) ..\third-party\ ; exitIfFailed
+cd $nvimCmakeVars["DEPS_BUILD_DIR"]
+cmake -G $cmakeGenerator $(convertToCmakeArgs($depsCmakeVars)) "$buildDir/third-party/" ; exitIfFailed
cmake --build . --config $cmakeBuildType -- $cmakeGeneratorArgs ; exitIfFailed
-cd ..
+cd $buildDir
# Build Neovim
mkdir build
diff --git a/man/nvim.1 b/man/nvim.1
index 4d338321c4..bf60c80a37 100644
--- a/man/nvim.1
+++ b/man/nvim.1
@@ -199,6 +199,8 @@ do not read or write a ShaDa file.
Skip loading plugins.
Implied by
.Cm -u NONE .
+.It Fl -clean
+Skip loading plugins and shada (viminfo) file.
.It Fl o Ns Op Ar N
Open
.Ar N
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 160cdcff64..6ed39cb9f1 100644
--- a/runtime/autoload/dist/ft.vim
+++ b/runtime/autoload/dist/ft.vim
@@ -1,7 +1,7 @@
" Vim functions for file type detection
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2017 Dec 05
+" Last Change: 2019 Jan 18
" These functions are moved here from runtime/filetype.vim to make startup
" faster.
@@ -197,7 +197,7 @@ func dist#ft#FTe()
exe 'setf ' . g:filetype_euphoria
else
let n = 1
- while n < 100 && n < line("$")
+ while n < 100 && n <= line("$")
if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$"
setf specman
return
@@ -211,7 +211,7 @@ endfunc
" Distinguish between HTML, XHTML and Django
func dist#ft#FThtml()
let n = 1
- while n < 10 && n < line("$")
+ while n < 10 && n <= line("$")
if getline(n) =~ '\<DTD\s\+XHTML\s'
setf xhtml
return
@@ -222,13 +222,13 @@ func dist#ft#FThtml()
endif
let n = n + 1
endwhile
- setf html
+ setf FALLBACK html
endfunc
" Distinguish between standard IDL and MS-IDL
func dist#ft#FTidl()
let n = 1
- while n < 50 && n < line("$")
+ while n < 50 && n <= line("$")
if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"'
setf msidl
return
@@ -484,6 +484,10 @@ endfunc
" Called from filetype.vim and scripts.vim.
func dist#ft#SetFileTypeSH(name)
+ if did_filetype()
+ " Filetype was already detected
+ return
+ endif
if expand("<amatch>") =~ g:ft_ignore_pat
return
endif
@@ -531,6 +535,10 @@ endfunc
" as used for Tcl.
" Also called from scripts.vim, thus can't be local to this script.
func dist#ft#SetFileTypeShell(name)
+ if did_filetype()
+ " Filetype was already detected
+ return
+ endif
if expand("<amatch>") =~ g:ft_ignore_pat
return
endif
@@ -551,6 +559,10 @@ func dist#ft#SetFileTypeShell(name)
endfunc
func dist#ft#CSH()
+ if did_filetype()
+ " Filetype was already detected
+ return
+ endif
if exists("g:filetype_csh")
call dist#ft#SetFileTypeShell(g:filetype_csh)
elseif &shell =~ "tcsh"
@@ -687,7 +699,7 @@ endfunc
func dist#ft#FTxml()
let n = 1
- while n < 100 && n < line("$")
+ while n < 100 && n <= line("$")
let line = getline(n)
" DocBook 4 or DocBook 5.
let is_docbook4 = line =~ '<!DOCTYPE.*DocBook'
@@ -713,7 +725,7 @@ endfunc
func dist#ft#FTy()
let n = 1
- while n < 100 && n < line("$")
+ while n < 100 && n <= line("$")
let line = getline(n)
if line =~ '^\s*%'
setf yacc
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
index efa3292801..8fcea2e941 100644
--- a/runtime/autoload/health/nvim.vim
+++ b/runtime/autoload/health/nvim.vim
@@ -170,6 +170,17 @@ function! s:check_tmux() abort
\ ["Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal \"screen-256color\"",
\ s:suggest_faq])
endif
+
+ " check for RGB capabilities
+ let info = system('tmux server-info')
+ let has_tc = stridx(info, " Tc: (flag) true") != -1
+ let has_rgb = stridx(info, " RGB: (flag) true") != -1
+ if !has_tc && !has_rgb
+ call health#report_warn(
+ \ "Neither Tc nor RGB capability set. True colors are disabled. |'termguicolors'| won't work properly.",
+ \ ["Put this in your ~/.tmux.conf and replace XXX by your $TERM outside of tmux:\nset-option -sa terminal-overrides ',XXX:RGB'",
+ \ "For older tmux versions use this instead:\nset-option -ga terminal-overrides ',XXX:Tc'"])
+ endif
endfunction
function! s:check_terminal() abort
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index 8f364a2ace..29bbee4888 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -106,7 +106,8 @@ endfunction
" Fetch the contents of a URL.
function! s:download(url) abort
- if executable('curl')
+ let has_curl = executable('curl')
+ if has_curl && system(['curl', '-V']) =~# 'Protocols:.*https'
let rv = s:system(['curl', '-sL', a:url], '', 1, 1)
return s:shell_error ? 'curl error with '.a:url.': '.s:shell_error : rv
elseif executable('python')
@@ -124,7 +125,9 @@ function! s:download(url) abort
\ ? 'python urllib.request error: '.s:shell_error
\ : rv
endif
- return 'missing `curl` and `python`, cannot make pypi request'
+ return 'missing `curl` '
+ \ .(has_curl ? '(with HTTPS support) ' : '')
+ \ .'and `python`, cannot make web request'
endfunction
" Check for clipboard tools.
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 2fb9d74d8d..2b06ee8c48 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -73,9 +73,9 @@ function! provider#clipboard#Executable() abort
let s:cache_enabled = 0
return 'pbcopy'
elseif exists('$WAYLAND_DISPLAY') && executable('wl-copy') && executable('wl-paste')
- let s:copy['+'] = 'wl-copy --foreground'
+ let s:copy['+'] = 'wl-copy --foreground --type text/plain'
let s:paste['+'] = 'wl-paste --no-newline'
- let s:copy['*'] = 'wl-copy --foreground --primary'
+ let s:copy['*'] = 'wl-copy --foreground --primary --type text/plain'
let s:paste['*'] = 'wl-paste --no-newline --primary'
return 'wl-copy'
elseif exists('$DISPLAY') && executable('xclip')
diff --git a/runtime/autoload/spellfile.vim b/runtime/autoload/spellfile.vim
index 9ec6091218..c0ef51cdfe 100644
--- a/runtime/autoload/spellfile.vim
+++ b/runtime/autoload/spellfile.vim
@@ -195,16 +195,6 @@ function! spellfile#GetDirChoices()
endfunc
function! spellfile#WritableSpellDir()
- " Always use the $XDG_DATA_HOME/nvim/site directory
- if exists('$XDG_DATA_HOME')
- return $XDG_DATA_HOME . "/nvim/site/spell"
- elseif !(has('win32') || has('win64'))
- return $HOME . "/.local/share/nvim/site/spell"
- endif
- for dir in split(&rtp, ',')
- if filewritable(dir) == 2
- return dir . "/spell"
- endif
- endfor
- return ''
+ " Always use the $XDG_DATA_HOME/…/site directory
+ return stdpath('data').'/site/spell'
endfunction
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index c9d526d9aa..ed6e4905b4 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -46,7 +46,7 @@ version.api_prerelease Declares the API as unstable/unreleased >
(version.api_prerelease && fn.since == version.api_level)
functions API function signatures
ui_events UI event signatures |ui|
-ui_options Supported |ui-options|
+ui_options Supported |ui-option|s
{fn}.since API level where function {fn} was introduced
{fn}.deprecated_since API level where function {fn} was deprecated
types Custom handle types defined by Nvim
@@ -206,17 +206,15 @@ Highlights are registered using the |nvim_buf_add_highlight()| function. If an
external highlighter plugin wants to add many highlights in a batch,
performance can be improved by calling |nvim_buf_add_highlight()| as an
asynchronous notification, after first (synchronously) reqesting a source id.
-Example using the Nvim python-client:
+
+Example using the Python API client (|pynvim|):
>
src = vim.new_highlight_source()
-
buf = vim.current.buffer
for i in range(5):
buf.add_highlight("String",i,0,-1,src_id=src)
-
- # some time later
-
- buf.clear_highlight(src)
+ # some time later ...
+ buf.clear_namespace(src)
<
If the highlights don't need to be deleted or updated, just pass -1 as
src_id (this is the default in python). Use |nvim_buf_clear_namespace()| to
@@ -224,15 +222,52 @@ clear highlights from a specific source, in a specific line range or the
entire buffer by passing in the line range 0, -1 (the latter is the default in
python as used above).
-An example of calling the api from vimscript: >
+Example using the API from Vimscript: >
call nvim_buf_set_lines(0, 0, 0, v:true, ["test text"])
let src = nvim_buf_add_highlight(0, 0, "String", 1, 0, 4)
call nvim_buf_add_highlight(0, src, "Identifier", 0, 5, -1)
-
- " later
+ " some time later ...
call nvim_buf_clear_namespace(0, src, 0, -1)
+
+
+==============================================================================
+Floating windows *api-floatwin*
+
+Nvim supports floating windows, windows that are displayed on top of ordinary
+windows. This is useful to implement simple widgets, such as tooltips
+displaying information next to cursor text. Floating windows are fully
+functional buffer windows and support user editing. They support the standard
+|api-window| calls and almost all window options (with some exceptions such as
+'statusline' is not supported currently).
+
+Floating windows are created either by |nvim_open_win()| to open a new window,
+or |nvim_win_set_config()| to reconfigure a normal window into a float.
+Currently the position can either be grid coordinates relative to the top-left
+of some window, or a position relative to the current window cursor. The
+parameters for positioning are described in detail at |nvim_open_win()|.
+
+|nvim_open_win()| assumes an existing buffer to display in the window. To create
+a scratch buffer for the float, |nvim_create_buf()| can be used. The text in
+the buffer can be highlighted using standard functionality, such as syntax
+highlighting, or |api-highlights|.
+
+By default, floats will use |hl-NormalFloat| as normal highlight, which
+links to |hl-Pmenu| in the builtin color scheme. The 'winhighlight' option can
+be used to override it. Currently, floating windows don't support any visual
+decorations like a border or additional widgets like scrollbar.
+
+Here is an example for creating a float with scratch buffer: >
+
+ let buf = nvim_create_buf(v:false, v:true)
+ call nvim_buf_set_lines(buf, 0, -1, v:true, ["test", "text"])
+ let opts = {'relative': 'cursor', 'width': 10, 'height': 2, 'col': 0,
+ \ 'row': 1, 'anchor': 'NW'}
+ let win = nvim_open_win(buf, 0, opts)
+ " optional: change highlight, otherwise Pmenu is used
+ call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')
>
+To close the float, |nvim_win_close()| can be used.
==============================================================================
Global Functions *api-global*
@@ -255,6 +290,9 @@ nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()*
Return: ~
Highlight definition map
+ See also: ~
+ nvim_get_hl_by_id
+
nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()*
Gets a highlight definition by id. |hlID()|
@@ -265,6 +303,9 @@ nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()*
Return: ~
Highlight definition map
+ See also: ~
+ nvim_get_hl_by_name
+
nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()*
Sends input-keys to Nvim, subject to various quirks controlled
by `mode` flags. This is a blocking call, unlike
@@ -278,6 +319,10 @@ nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()*
{escape_csi} If true, escape K_SPECIAL/CSI bytes in
`keys`
+ See also: ~
+ feedkeys()
+ vim_strsave_escape_csi
+
nvim_input({keys}) *nvim_input()*
Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a
low-level input buffer and the call is non-blocking (input is
@@ -288,7 +333,6 @@ nvim_input({keys}) *nvim_input()*
Note:
|keycodes| like <CR> are translated, so "<" is special. To
input a literal "<", send <LT>.
-
Note:
For mouse events use |nvim_input_mouse()|. The pseudokey
form "<LeftMouse><col,row>" is deprecated since
@@ -308,9 +352,8 @@ nvim_input({keys}) *nvim_input()*
nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col})
Send mouse event from GUI.
- The call is non-blocking. It doesn't wait on any resulting
- action, but queues the event to be processed soon by the event
- loop.
+ Non-blocking: does not wait on any result, but queues the
+ event to be processed soon by the event loop.
Note:
Currently this doesn't support "scripting" multiple mouse
@@ -354,6 +397,10 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
{special} Replace |keycodes|, e.g. <CR> becomes a "\n"
char.
+ See also: ~
+ replace_termcodes
+ cpoptions
+
nvim_command_output({command}) *nvim_command_output()*
Executes an ex-command and returns its (non-error) output.
Shell |:!| output is not captured.
@@ -382,7 +429,7 @@ nvim_execute_lua({code}, {args}) *nvim_execute_lua()*
inside the chunk. The chunk can return a value.
Only statements are executed. To evaluate an expression,
- prefix it with `return`: return my_function(...)
+ prefix it with `return` : return my_function(...)
Parameters: ~
{code} lua code to execute
@@ -420,7 +467,7 @@ nvim_call_dict_function({dict}, {fn}, {args}) *nvim_call_dict_function()*
Result of the function call
nvim_strwidth({text}) *nvim_strwidth()*
- Calculates the number of display cells occupied by `text`.
+ Calculates the number of display cells occupied by `text` .
<Tab> counts as one cell.
Parameters: ~
@@ -444,8 +491,6 @@ nvim_set_current_dir({dir}) *nvim_set_current_dir()*
nvim_get_current_line() *nvim_get_current_line()*
Gets the current line.
- Parameters: ~
-
Return: ~
Current line string
@@ -458,8 +503,6 @@ nvim_set_current_line({line}) *nvim_set_current_line()*
nvim_del_current_line() *nvim_del_current_line()*
Deletes the current line.
- Parameters: ~
-
nvim_get_var({name}) *nvim_get_var()*
Gets a global (g:) variable.
@@ -537,11 +580,14 @@ nvim_err_writeln({str}) *nvim_err_writeln()*
Parameters: ~
{str} Message
+ See also: ~
+ nvim_err_write()
+
nvim_list_bufs() *nvim_list_bufs()*
Gets the current list of buffer handles
- Includes unlisted (unloaded/deleted) buffers, like `:ls!`. Use
- |nvim_buf_is_loaded()| to check if a buffer is loaded.
+ Includes unlisted (unloaded/deleted) buffers, like `:ls!` .
+ Use |nvim_buf_is_loaded()| to check if a buffer is loaded.
Return: ~
List of buffer handles
@@ -576,6 +622,93 @@ nvim_set_current_win({window}) *nvim_set_current_win()*
Parameters: ~
{window} Window handle
+nvim_create_buf({listed}, {scratch}) *nvim_create_buf()*
+ Creates a new, empty, unnamed buffer.
+
+ Parameters: ~
+ {listed} Sets 'buflisted'
+ {scratch} Creates a "throwaway" |scratch-buffer| for
+ temporary work (always 'nomodified')
+
+ Return: ~
+ Buffer handle, or 0 on error
+
+ See also: ~
+ buf_open_scratch
+
+nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
+ Open a new window.
+
+ Currently this is used to open floating and external windows.
+ Floats are windows that are drawn above the split layout, at
+ some anchor position in some other window. Floats can be draw
+ internally or by external GUI with the |ui-multigrid|
+ extension. External windows are only supported with multigrid
+ GUIs, and are displayed as separate top-level windows.
+
+ For a general overview of floats, see |api-floatwin|.
+
+ Exactly one of `external` and `relative` must be specified.
+ The `width` and `height` of the new window must be specified.
+
+ With editor positioning row=0, col=0 refers to the top-left
+ corner of the screen-grid and row=Lines-1, Columns-1 refers to
+ the bottom-right corner. Floating point values are allowed,
+ but the builtin implementation (used by TUI and GUIs without
+ multigrid support) will always round down to nearest integer.
+
+ Out-of-bounds values, and configurations that make the float
+ not fit inside the main editor, are allowed. The builtin
+ implementation will truncate values so floats are completely
+ within the main screen grid. External GUIs could let floats
+ hover outside of the main window like a tooltip, but this
+ should not be used to specify arbitrary WM screen positions.
+
+ Parameters: ~
+ {buffer} handle of buffer to be displayed in the window
+ {enter} whether the window should be entered (made the
+ current window)
+ {config} Dictionary for the window configuration accepts
+ these keys:
+ - `relative` : If set, the window becomes a floating
+ window. The window will be placed with row,col
+ coordinates relative to one of the following:
+ - "editor" the global editor grid
+ - "win" a window. Use `win` to specify a
+ window id, or the current window will be
+ used by default. "cursor" the cursor
+ position in current window.
+
+ - `win` : When using relative='win', window id
+ of the window where the position is defined.
+ - `anchor` : The corner of the float that the row,col
+ position defines:
+ - "NW" north-west (default)
+ - "NE" north-east
+ - "SW" south-west
+ - "SE" south-east
+
+ - `height` : window height (in character cells).
+ Minimum of 1.
+ - `width` : window width (in character cells).
+ Minimum of 1.
+ - `row` : row position. Screen cell height are
+ used as unit. Can be floating point.
+ - `col` : column position. Screen cell width is
+ used as unit. Can be floating point.
+ - `focusable` : Whether window can be focused by
+ wincmds and mouse events. Defaults to true.
+ Even if set to false, the window can still be
+ entered using |nvim_set_current_win()| API
+ call.
+ - `external` : GUI should display the window as
+ an external top-level window. Currently
+ accepts no other positioning configuration
+ together with this.
+
+ Return: ~
+ Window handle, or 0 on error
+
nvim_list_tabpages() *nvim_list_tabpages()*
Gets the current list of tabpage handles.
@@ -630,10 +763,28 @@ nvim_unsubscribe({event}) *nvim_unsubscribe()*
{event} Event type string
nvim_get_color_by_name({name}) *nvim_get_color_by_name()*
- TODO: Documentation
+ Returns the 24-bit RGB value of a |nvim_get_color_map()| color
+ name or "#rrggbb" hexadecimal string.
+
+ Example: >
+ :echo nvim_get_color_by_name("Pink")
+ :echo nvim_get_color_by_name("#cbcbcb")
+<
+
+ Parameters: ~
+ {name} Color name or "#rrggbb" string
+
+ Return: ~
+ 24-bit RGB value, or -1 for invalid argument.
nvim_get_color_map() *nvim_get_color_map()*
- TODO: Documentation
+ Returns a map of color names and RGB values.
+
+ Keys are color names (e.g. "Aqua") and values are 24-bit RGB
+ color values (e.g. 65535).
+
+ Return: ~
+ Map of color names and RGB values.
nvim_get_mode() *nvim_get_mode()*
Gets the current mode. |mode()| "blocking" is true if Nvim is
@@ -656,6 +807,42 @@ nvim_get_keymap({mode}) *nvim_get_keymap()*
Array of maparg()-like dictionaries describing mappings.
The "buffer" key is always zero.
+nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()*
+ Sets a global |mapping| for the given mode.
+
+ To set a buffer-local mapping, use |nvim_buf_set_keymap()|.
+
+ Unlike |:map|, leading/trailing whitespace is accepted as part
+ of the {lhs} or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are
+ replaced as usual.
+
+ Example: >
+ call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
+<
+
+ is equivalent to: >
+ nmap <nowait> <Space><NL> <Nop>
+<
+
+ Parameters: ~
+ {mode} Mode short-name (map command prefix: "n", "i",
+ "v", "x", …) or "!" for |:map!|, or empty string
+ for |:map|.
+ {lhs} Left-hand-side |{lhs}| of the mapping.
+ {rhs} Right-hand-side |{rhs}| of the mapping.
+ {opts} Optional parameters map. Accepts all
+ |:map-arguments| as keys excluding |<buffer>| but
+ including |noremap|. Values are Booleans. Unknown
+ key is an error.
+
+nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()*
+ Unmaps a global |mapping| for the given mode.
+
+ To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|.
+
+ See also: ~
+ |nvim_set_keymap()|
+
nvim_get_commands({opts}) *nvim_get_commands()*
Gets a map of global (non-buffer-local) Ex commands.
@@ -682,67 +869,87 @@ nvim_get_api_info() *nvim_get_api_info()*
*nvim_set_client_info()*
nvim_set_client_info({name}, {version}, {type}, {methods},
{attributes})
- Identify the client for nvim. Can be called more than once,
- but subsequent calls will remove earlier info, which should be
- resent if it is still valid. (This could happen if a library
- first identifies the channel, and a plugin using that library
- later overrides that info)
-
- Parameters: ~
- {name} short name for the connected client
- {version} Dictionary describing the version, with the
- following possible keys (all optional)
- "major" major version (defaults to 0 if not
- set, for no release yet) "minor" minor
- version "patch" patch number "prerelease"
- string describing a prerelease, like "dev"
- or "beta1" "commit" hash or similar
- identifier of commit
- {type} Must be one of the following values. A
- client library should use "remote" if the
- library user hasn't specified other value.
- "remote" remote client that connected to
- nvim. "ui" gui frontend "embedder"
- application using nvim as a component, for
- instance IDE/editor implementing a vim mode.
- "host" plugin host, typically started by
- nvim "plugin" single plugin, started by
- nvim
+ Identifies the client. Can be called more than once;
+ subsequent calls remove earlier info, which should be included
+ by the caller if it is still valid. (E.g. if a library first
+ identifies the channel, then a plugin using that library later
+ overrides that info)
+
+ Parameters: ~
+ {name} Short name for the connected client
+ {version} Dictionary describing the version, with
+ these (optional) keys:
+ - "major" major version (defaults to 0 if
+ not set, for no release yet)
+ - "minor" minor version
+ - "patch" patch number
+ - "prerelease" string describing a
+ prerelease, like "dev" or "beta1"
+ - "commit" hash or similar identifier of
+ commit
+ {type} Must be one of the following values. Client
+ libraries should default to "remote" unless
+ overridden by the user.
+ - "remote" remote client connected to Nvim.
+ - "ui" gui frontend
+ - "embedder" application using Nvim as a
+ component (for example, IDE/editor
+ implementing a vim mode).
+ - "host" plugin host, typically started by
+ nvim
+ - "plugin" single plugin, started by nvim
{methods} Builtin methods in the client. For a host,
this does not include plugin methods which
will be discovered later. The key should be
the method name, the values are dicts with
- the following (optional) keys: "async" if
- true, send as a notification. If false or
- unspecified, use a blocking request "nargs"
- Number of arguments. Could be a single
- integer or an array two integers, minimum
- and maximum inclusive. Further keys might be
- added in later versions of nvim and unknown
- keys are thus ignored. Clients must only use
- keys defined in this or later versions of
- nvim!
- {attributes} Informal attributes describing the client.
- Clients might define their own keys, but the
- following are suggested: "website" Website
- of client (for instance github repository)
- "license" Informal description of the
- license, such as "Apache 2", "GPLv3" or
- "MIT" "logo" URI or path to image,
- preferably small logo or icon. .png or .svg
- format is preferred.
+ these (optional) keys (more keys may be
+ added in future versions of Nvim, thus
+ unknown keys are ignored. Clients must only
+ use keys defined in this or later versions
+ of Nvim):
+ - "async" if true, send as a notification.
+ If false or unspecified, use a blocking
+ request
+ - "nargs" Number of arguments. Could be a
+ single integer or an array of two
+ integers, minimum and maximum inclusive.
+ {attributes} Arbitrary string:string map of informal
+ client properties. Suggested keys:
+ - "website": Client homepage URL (e.g.
+ GitHub repository)
+ - "license": License description ("Apache
+ 2", "GPLv3", "MIT", …)
+ - "logo": URI or path to image, preferably
+ small logo or icon. .png or .svg format is
+ preferred.
nvim_get_chan_info({chan}) *nvim_get_chan_info()*
Get information about a channel.
Return: ~
- a Dictionary, describing a channel with the following
- keys: "stream" the stream underlying the channel
- "stdio" stdin and stdout of this Nvim instance "stderr"
- stderr of this Nvim instance "socket" TCP/IP socket or
- named pipe "job" job with communication over its stdio
-
- "mode" how data received on the channel is interpreted "bytes" send and recieve raw bytes "terminal" a |terminal| instance interprets ASCII sequences "rpc" |RPC| communication on the channel is active "pty" Name of pseudoterminal, if one is used (optional). On a POSIX system, this will be a device path like /dev/pts/1. Even if the name is unknown, the key will still be present to indicate a pty is used. This is currently the case when using winpty on windows. "buffer" buffer with connected |terminal| instance (optional) "client" information about the client on the other end of the RPC channel, if it has added it using |nvim_set_client_info()|. (optional)
+ Dictionary describing a channel, with these keys:
+ - "stream" the stream underlying the channel
+ - "stdio" stdin and stdout of this Nvim instance
+ - "stderr" stderr of this Nvim instance
+ - "socket" TCP/IP socket or named pipe
+ - "job" job with communication over its stdio
+
+ - "mode" how data received on the channel is interpreted
+ - "bytes" send and receive raw bytes
+ - "terminal" a |terminal| instance interprets ASCII
+ sequences
+ - "rpc" |RPC| communication on the channel is active
+
+ - "pty" Name of pseudoterminal, if one is used (optional).
+ On a POSIX system, this will be a device path like
+ /dev/pts/1. Even if the name is unknown, the key will
+ still be present to indicate a pty is used. This is
+ currently the case when using winpty on windows.
+ - "buffer" buffer with connected |terminal| instance
+ (optional)
+ - "client" information about the client on the other end
+ of the RPC channel, if it has added it using
+ |nvim_set_client_info()|. (optional)
nvim_list_chans() *nvim_list_chans()*
Get information about all open channels.
@@ -755,7 +962,13 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
Calls many API methods atomically.
This has two main usages:
- To perform several requests from an async context atomically, i.e. without interleaving redraws, RPC requests from other clients, or user interactions (however API methods may trigger autocommands or event processing which have such side-effects, e.g. |:sleep| may wake timers). To minimize RPC overhead (roundtrips) of a sequence of many requests.
+ 1. To perform several requests from an async context
+ atomically, i.e. without interleaving redraws, RPC requests
+ from other clients, or user interactions (however API
+ methods may trigger autocommands or event processing which
+ have such side-effects, e.g. |:sleep| may wake timers).
+ 2. To minimize RPC overhead (roundtrips) of a sequence of many
+ requests.
Parameters: ~
{calls} an array of calls, where each call is described
@@ -763,13 +976,13 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
and an array of arguments.
Return: ~
- an array with two elements. The first is an array of
- return values. The second is NIL if all calls succeeded.
- If a call resulted in an error, it is a three-element
- array with the zero-based index of the call which resulted
- in an error, the error type and the error message. If an
- error occurred, the values from all preceding calls will
- still be returned.
+ Array of two elements. The first is an array of return
+ values. The second is NIL if all calls succeeded. If a
+ call resulted in an error, it is a three-element array
+ with the zero-based index of the call which resulted in an
+ error, the error type and the error message. If an error
+ occurred, the values from all preceding calls will still
+ be returned.
*nvim_parse_expression()*
nvim_parse_expression({expr}, {flags}, {highlight})
@@ -779,20 +992,24 @@ nvim_parse_expression({expr}, {flags}, {highlight})
{async}
Parameters: ~
- {expr} Expression to parse. Is always treated as a
+ {expr} Expression to parse. Always treated as a
single line.
- {flags} Flags: - "m" if multiple expressions in a
- row are allowed (only the first one will be
- parsed), - "E" if EOC tokens are not allowed
- (determines whether they will stop parsing
- process or be recognized as an
- operator/space, though also yielding an
- error). - "l" when needing to start parsing
- with lvalues for ":let" or ":for". Common
- flag sets: - "m" to parse like for ":echo". -
- "E" to parse like for "<C-r>=". - empty
- string for ":call". - "lm" to parse for
- ":let".
+ {flags} Flags:
+ - "m" if multiple expressions in a row are
+ allowed (only the first one will be
+ parsed),
+ - "E" if EOC tokens are not allowed
+ (determines whether they will stop parsing
+ process or be recognized as an
+ operator/space, though also yielding an
+ error).
+ - "l" when needing to start parsing with
+ lvalues for ":let" or ":for". Common flag
+ sets:
+ - "m" to parse like for ":echo".
+ - "E" to parse like for "<C-r>=".
+ - empty string for ":call".
+ - "lm" to parse for ":let".
{highlight} If true, return value will also include
"highlight" key containing array of 4-tuples
(arrays) (Integer, Integer, Integer, String),
@@ -803,51 +1020,66 @@ nvim_parse_expression({expr}, {flags}, {highlight})
[start_col, end_col)).
Return: ~
- AST: top-level dictionary with these keys: "error":
- Dictionary with error, present only if parser saw some
- error. Contains the following keys: "message": String,
- error message in printf format, translated. Must contain
- exactly one "%.*s". "arg": String, error message argument.
- "len": Amount of bytes successfully parsed. With flags
- equal to "" that should be equal to the length of expr
- string. @note: “Sucessfully parsed” here means
- “participated in AST creation”, not “till the first
- error”. "ast": AST, either nil or a dictionary with these
- keys: "type": node type, one of the value names from
- ExprASTNodeType stringified without "kExprNode" prefix.
- "start": a pair [line, column] describing where node is
- “started” where "line" is always 0 (will not be 0 if you
- will be using nvim_parse_viml() on e.g. ":let", but that
- is not present yet). Both elements are Integers. "len":
- “length” of the node. This and "start" are there for
- debugging purposes primary (debugging parser and providing
- debug information). "children": a list of nodes described
- in top/"ast". There always is zero, one or two children,
- key will not be present if node has no children. Maximum
- number of children may be found in node_maxchildren array.
- Local values (present only for certain nodes): "scope": a
- single Integer, specifies scope for "Option" and
- "PlainIdentifier" nodes. For "Option" it is one of
- ExprOptScope values, for "PlainIdentifier" it is one of
- ExprVarScope values. "ident": identifier (without scope,
- if any), present for "Option", "PlainIdentifier",
- "PlainKey" and "Environment" nodes. "name": Integer,
- register name (one character) or -1. Only present for
- "Register" nodes. "cmp_type": String, comparison type, one
- of the value names from ExprComparisonType, stringified
- without "kExprCmp" prefix. Only present for "Comparison"
- nodes. "ccs_strategy": String, case comparison strategy,
- one of the value names from ExprCaseCompareStrategy,
- stringified without "kCCStrategy" prefix. Only present for
- "Comparison" nodes. "augmentation": String, augmentation
- type for "Assignment" nodes. Is either an empty string,
- "Add", "Subtract" or "Concat" for "=", "+=", "-=" or ".="
- respectively. "invert": Boolean, true if result of
- comparison needs to be inverted. Only present for
- "Comparison" nodes. "ivalue": Integer, integer value for
- "Integer" nodes. "fvalue": Float, floating-point value for
- "Float" nodes. "svalue": String, value for
- "SingleQuotedString" and "DoubleQuotedString" nodes.
+
+ - AST: top-level dictionary with these keys:
+ - "error": Dictionary with error, present only if parser
+ saw some error. Contains the following keys:
+ - "message": String, error message in printf format,
+ translated. Must contain exactly one "%.*s".
+ - "arg": String, error message argument.
+
+ - "len": Amount of bytes successfully parsed. With flags
+ equal to "" that should be equal to the length of expr
+ string. (“Sucessfully parsed” here means “participated
+ in AST creation”, not “till the first error”.)
+ - "ast": AST, either nil or a dictionary with these
+ keys:
+ - "type": node type, one of the value names from
+ ExprASTNodeType stringified without "kExprNode"
+ prefix.
+ - "start": a pair [line, column] describing where node
+ is "started" where "line" is always 0 (will not be 0
+ if you will be using nvim_parse_viml() on e.g.
+ ":let", but that is not present yet). Both elements
+ are Integers.
+ - "len": “length” of the node. This and "start" are
+ there for debugging purposes primary (debugging
+ parser and providing debug information).
+ - "children": a list of nodes described in top/"ast".
+ There always is zero, one or two children, key will
+ not be present if node has no children. Maximum
+ number of children may be found in node_maxchildren
+ array.
+
+ - Local values (present only for certain nodes):
+ - "scope": a single Integer, specifies scope for
+ "Option" and "PlainIdentifier" nodes. For "Option" it
+ is one of ExprOptScope values, for "PlainIdentifier"
+ it is one of ExprVarScope values.
+ - "ident": identifier (without scope, if any), present
+ for "Option", "PlainIdentifier", "PlainKey" and
+ "Environment" nodes.
+ - "name": Integer, register name (one character) or -1.
+ Only present for "Register" nodes.
+ - "cmp_type": String, comparison type, one of the value
+ names from ExprComparisonType, stringified without
+ "kExprCmp" prefix. Only present for "Comparison"
+ nodes.
+ - "ccs_strategy": String, case comparison strategy, one
+ of the value names from ExprCaseCompareStrategy,
+ stringified without "kCCStrategy" prefix. Only present
+ for "Comparison" nodes.
+ - "augmentation": String, augmentation type for
+ "Assignment" nodes. Is either an empty string, "Add",
+ "Subtract" or "Concat" for "=", "+=", "-=" or ".="
+ respectively.
+ - "invert": Boolean, true if result of comparison needs
+ to be inverted. Only present for "Comparison" nodes.
+ - "ivalue": Integer, integer value for "Integer" nodes.
+ - "fvalue": Float, floating-point value for "Float"
+ nodes.
+ - "svalue": String, value for "SingleQuotedString" and
+ "DoubleQuotedString" nodes.
nvim__id({obj}) *nvim__id()*
Returns object given as argument.
@@ -907,18 +1139,22 @@ nvim_list_uis() *nvim_list_uis()*
Gets a list of dictionaries representing attached UIs.
Return: ~
- Array of UI dictionaries
- Each dictionary has the following keys:
- "height" requested height of the UI "width" requested width of the UI "rgb" whether the UI uses rgb colors (false implies cterm colors) "ext_..." Requested UI extensions, see |ui-options| "chan" Channel id of remote UI (not present for TUI)
+ Array of UI dictionaries, each with these keys:
+ - "height" Requested height of the UI
+ - "width" Requested width of the UI
+ - "rgb" true if the UI uses RGB colors (false implies
+ |cterm-colors|)
+ - "ext_..." Requested UI extensions, see |ui-option|
+ - "chan" Channel id of remote UI (not present for TUI)
nvim_get_proc_children({pid}) *nvim_get_proc_children()*
- Gets the immediate children of process `pid`.
+ Gets the immediate children of process `pid` .
Return: ~
Array of child process ids, empty if process not found.
nvim_get_proc({pid}) *nvim_get_proc()*
- Gets info describing process `pid`.
+ Gets info describing process `pid` .
Return: ~
Map of process properties, or NIL if process not found.
@@ -940,7 +1176,7 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
{insert} Whether the selection should be inserted in the
buffer.
{finish} Finish the completion and dismiss the popupmenu.
- Implies `insert`.
+ Implies `insert` .
{opts} Optional parameters. Reserved for future use.
nvim__inspect_cell({row}, {col}) *nvim__inspect_cell()*
@@ -967,20 +1203,20 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
Gets the buffer line count
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
Line count, or 0 for unloaded buffer. |api-buffer|
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
- Activate updates from this buffer to the current channel.
+ Activates buffer-update events on the channel.
Parameters: ~
- {buffer} The buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{send_buffer} Set to true if the initial notification
should contain the whole buffer. If so, the
first notification will be a
- `nvim_buf_lines_event`. Otherwise, the
+ `nvim_buf_lines_event` . Otherwise, the
first notification will be a
`nvim_buf_changedtick_event`
{opts} Optional parameters. Reserved for future
@@ -988,14 +1224,14 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Return: ~
False when updates couldn't be enabled because the buffer
- isn't loaded or optscontained an invalid key; otherwise
+ isn't loaded or `opts` contained an invalid key; otherwise
True.
nvim_buf_detach({buffer}) *nvim_buf_detach()*
- Deactivate updates from this buffer to the current channel.
+ Deactivates buffer-update events on the channel.
Parameters: ~
- {buffer} The buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
False when updates couldn't be disabled because the buffer
@@ -1013,7 +1249,7 @@ nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
unless `strict_indexing` is set.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{start} First line index
{end} Last line index (exclusive)
{strict_indexing} Whether out-of-bounds should be an
@@ -1040,7 +1276,7 @@ nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing},
unless `strict_indexing` is set.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{start} First line index
{end} Last line index (exclusive)
{strict_indexing} Whether out-of-bounds should be an
@@ -1060,7 +1296,7 @@ nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()*
Returns -1 for unloaded buffer.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{index} Line index
Return: ~
@@ -1070,7 +1306,7 @@ nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()*
Gets a buffer-scoped (b:) variable.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Variable name
Return: ~
@@ -1080,27 +1316,46 @@ nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()*
Gets a changed tick of a buffer
Parameters: ~
- {buffer} Buffer handle.
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
- b:changedtickvalue.
+ `b:changedtick` value.
nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()*
Gets a list of buffer-local |mapping| definitions.
Parameters: ~
{mode} Mode short-name ("n", "i", "v", ...)
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
Array of maparg()-like dictionaries describing mappings.
The "buffer" key holds the associated buffer handle.
+ *nvim_buf_set_keymap()*
+nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {opts})
+ Sets a buffer-local |mapping| for the given mode.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer
+
+ See also: ~
+ |nvim_set_keymap()|
+
+nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()*
+ Unmaps a buffer-local |mapping| for the given mode.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer
+
+ See also: ~
+ |nvim_del_keymap()|
+
nvim_buf_get_commands({buffer}, {opts}) *nvim_buf_get_commands()*
Gets a map of buffer-local |user-commands|.
Parameters: ~
- {buffer} Buffer handle.
+ {buffer} Buffer handle, or 0 for current buffer
{opts} Optional parameters. Currently not used.
Return: ~
@@ -1110,7 +1365,7 @@ nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
Sets a buffer-scoped (b:) variable
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Variable name
{value} Variable value
@@ -1118,14 +1373,14 @@ nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
Removes a buffer-scoped (b:) variable
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Variable name
nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()*
Gets a buffer option value
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Option name
Return: ~
@@ -1136,7 +1391,7 @@ nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
option (only works if there's a global fallback)
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Option name
{value} Option value
@@ -1144,7 +1399,7 @@ nvim_buf_get_name({buffer}) *nvim_buf_get_name()*
Gets the full file name for the buffer
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
Buffer name
@@ -1153,7 +1408,7 @@ nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()*
Sets the full file name for a buffer
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Buffer name
nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()*
@@ -1161,7 +1416,7 @@ nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()*
more info about unloaded buffers.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
true if the buffer is valid and loaded, false otherwise.
@@ -1174,7 +1429,7 @@ nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
|api-buffer| for more info about unloaded buffers.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
Return: ~
true if the buffer is valid, false otherwise.
@@ -1184,7 +1439,7 @@ nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
named mark
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{name} Mark name
Return: ~
@@ -1208,7 +1463,7 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line},
`ns_id` to add highlights to the namespace. All highlights in
the same namespace can then be cleared with single call to
|nvim_buf_clear_namespace|. If the highlight never will be
- deleted by an API call, pass `ns_id = -1`.
+ deleted by an API call, pass `ns_id = -1` .
As a shorthand, `ns_id = 0` can be used to create a new
namespace for the highlight, the allocated id is then
@@ -1218,7 +1473,7 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line},
|nvim_create_namespace| to create a new empty namespace.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{ns_id} namespace to use or -1 for ungrouped
highlight
{hl_group} Name of the highlight group to use
@@ -1240,7 +1495,7 @@ nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end})
to line_start and line_end respectively.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{ns_id} Namespace to clear, or -1 to clear all
namespaces.
{line_start} Start of range of lines to clear
@@ -1264,14 +1519,14 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts})
both virtual text and highlights added by
|nvim_buf_add_highlight|, both can then be cleared with a
single call to |nvim_buf_clear_namespace|. If the virtual text
- never will be cleared by an API call, pass `ns_id = -1`.
+ never will be cleared by an API call, pass `ns_id = -1` .
As a shorthand, `ns_id = 0` can be used to create a new
namespace for the virtual text, the allocated id is then
returned.
Parameters: ~
- {buffer} Buffer handle
+ {buffer} Buffer handle, or 0 for current buffer
{ns_id} Namespace to use or 0 to create a namespace, or
-1 for a ungrouped annotation
{line} Line to annotate with virtual text
@@ -1436,6 +1691,50 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()*
Return: ~
true if the window is valid, false otherwise
+nvim_win_set_config({window}, {config}) *nvim_win_set_config()*
+ Configure window position. Currently this is only used to
+ configure floating and external windows (including changing a
+ split window to these types).
+
+ See documentation at |nvim_open_win()|, for the meaning of
+ parameters.
+
+ When reconfiguring a floating window, absent option keys will
+ not be changed. The following restriction apply: `row` , `col`
+ and `relative` must be reconfigured together. Only changing a
+ subset of these is an error.
+
+ Parameters: ~
+ {window} Window handle
+ {config} Dictionary of window configuration
+
+nvim_win_get_config({window}) *nvim_win_get_config()*
+ Return window configuration.
+
+ Return a dictionary containing the same config that can be
+ given to |nvim_open_win()|.
+
+ `relative` will be an empty string for normal windows.
+
+ Parameters: ~
+ {window} Window handle
+
+ Return: ~
+ Window configuration
+
+nvim_win_close({window}, {force}) *nvim_win_close()*
+ Close a window.
+
+ This is equivalent to |:close| with count except that it takes
+ a window id.
+
+ Parameters: ~
+ {window} Window handle
+ {force} Behave like `:close!` The last window of a
+ buffer with unwritten changes can be closed. The
+ buffer will become hidden, even if 'hidden' is
+ not set.
+
==============================================================================
Tabpage Functions *api-tabpage*
@@ -1447,7 +1746,7 @@ nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()*
{tabpage} Tabpage
Return: ~
- List of windows in tabpage
+ List of windows in `tabpage`
nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()*
Gets a tab-scoped (t:) variable
@@ -1506,10 +1805,27 @@ nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()*
UI Functions *api-ui*
nvim_ui_attach({width}, {height}, {options}) *nvim_ui_attach()*
- TODO: Documentation
+ Activates UI events on the channel.
+
+ Entry point of all UI clients. Allows |--embed| to continue
+ startup. Implies that the client is ready to show the UI. Adds
+ the client to the list of UIs. |nvim_list_uis()|
+
+ Note:
+ If multiple UI clients are attached, the global screen
+ dimensions degrade to the smallest client. E.g. if client
+ A requests 80x40 but client B requests 200x100, the global
+ screen has size 80x40.
+
+ Parameters: ~
+ {width} Requested screen columns
+ {height} Requested screen rows
+ {options} |ui-option| map
nvim_ui_detach() *nvim_ui_detach()*
- TODO: Documentation
+ Deactivates UI events on the channel.
+
+ Removes the client from the list of UIs. |nvim_list_uis()|
nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()*
TODO: Documentation
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 203584e280..4cc1f53cca 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -40,17 +40,17 @@ effects. Be careful not to destroy your text.
2. Defining autocommands *autocmd-define*
*:au* *:autocmd*
-:au[tocmd] [group] {event} {pat} [once] [nested] {cmd}
+:au[tocmd] [group] {event} {pat} [++once] [++nested] {cmd}
Add {cmd} to the list of commands that Vim will
execute automatically on {event} for a file matching
- {pat} |autocmd-patterns|.
+ {pat} |autocmd-pattern|.
Note: A quote character is seen as argument to the
:autocmd and won't start a comment.
Nvim always adds {cmd} after existing autocommands so
they execute in the order in which they were defined.
- See |autocmd-nested| for [nested].
+ See |autocmd-nested| for [++nested].
*autocmd-once*
- If [once] is supplied the command is executed once,
+ If [++once] is supplied the command is executed once,
then removed ("one shot").
The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand.
@@ -119,11 +119,11 @@ prompt. When one command outputs two messages this can happen anyway.
==============================================================================
3. Removing autocommands *autocmd-remove*
-:au[tocmd]! [group] {event} {pat} [once] [nested] {cmd}
+:au[tocmd]! [group] {event} {pat} [++once] [++nested] {cmd}
Remove all autocommands associated with {event} and
{pat}, and add the command {cmd}.
- See |autocmd-once| for [once].
- See |autocmd-nested| for [nested].
+ See |autocmd-once| for [++once].
+ See |autocmd-nested| for [++nested].
:au[tocmd]! [group] {event} {pat}
Remove all autocommands associated with {event} and
@@ -354,10 +354,11 @@ Name triggered by ~
|SessionLoadPost| after loading a session file
|MenuPopup| just before showing the popup menu
+|CompleteChanged| after popup menu changed, not fired on popup menu hide
|CompleteDone| after Insert mode completion is done
|User| to be used in combination with ":doautocmd"
-|Signal| after the nvim process received a signal
+|Signal| after Nvim receives a signal
@@ -588,6 +589,22 @@ CompleteDone After Insert mode completion is done. Either
The |v:completed_item| variable contains the
completed item.
+CompleteChanged *CompleteChanged*
+ After each time popup menu changed, not fired
+ on popup menu hide, use |CompleteDone| for popup
+ menu hide.
+
+ Sets these |v:event| keys:
+ completed_item
+ height
+ width
+ row
+ col
+ size
+ scrollbar
+
+ It is not allowed to change the text |textlock|.
+
*CursorHold*
CursorHold When the user doesn't press a key for the time
specified with 'updatetime'. Not re-triggered
@@ -623,8 +640,9 @@ CursorHoldI Just like CursorHold, but in Insert mode.
*CursorMoved*
CursorMoved After the cursor was moved in Normal or Visual
- mode. Also when the text of the cursor line
- has been changed, e.g., with "x", "rx" or "p".
+ mode or to another window. Also when the text
+ of the cursor line has been changed, e.g. with
+ "x", "rx" or "p".
Not triggered when there is typeahead or when
an operator is pending.
For an example see |match-parens|.
@@ -809,20 +827,25 @@ InsertCharPre When a character is typed in Insert mode,
a different character. When |v:char| is set
to more than one character this text is
inserted literally.
- It is not allowed to change the text |textlock|.
- The event is not triggered when 'paste' is
- set.
+
+ Cannot change the text. |textlock|
+ Not triggered when 'paste' is set.
*TextYankPost*
TextYankPost Just after a |yank| or |deleting| command, but not
if the black hole register |quote_| is used nor
for |setreg()|. Pattern must be *.
Sets these |v:event| keys:
+ inclusive
operator
regcontents
regname
regtype
+ The `inclusive` flag combined with the |'[|
+ and |']| marks can be used to calculate the
+ precise region of the operation.
+
Recursion is ignored.
- It is not allowed to change the text |textlock|.
+ Cannot change the text. |textlock|
*InsertEnter*
InsertEnter Just before starting Insert mode. Also for
Replace mode and Virtual Replace mode. The
@@ -848,27 +871,27 @@ MenuPopup Just before showing the popup menu (under the
i Insert
c Command line
*OptionSet*
-OptionSet After setting an option. The pattern is
- matched against the long option name.
- The |v:option_old| variable indicates the
- old option value, |v:option_new| variable
- indicates the newly set value, the
- |v:option_type| variable indicates whether
- it's global or local scoped and |<amatch>|
- indicates what option has been set.
+OptionSet After setting an option (except during
+ |startup|). The |autocmd-pattern| is matched
+ against the long option name.
+ |v:option_old| indicates the old option value,
+ |v:option_new| indicates the new value,
+ |v:option_type| indicates whether it's global
+ or local scoped and |<amatch>| indicates which
+ option was set.
Usage example: Check for the existence of the
directory in the 'backupdir' and 'undodir'
options, create the directory if it doesn't
exist yet.
- Note: It's a bad idea to reset an option
- during this autocommand, this may break a
- plugin. You can always use `:noa` to prevent
- triggering this autocommand.
+ Note: Do not reset the same option during this
+ autocommand, that may break plugins. You can
+ always use |:noautocmd| to prevent triggering
+ OptionSet.
- When using |:set| in the autocommand the event
- is not triggered again.
+ Recursion is ignored, thus |:set| in the
+ autocommand does not trigger OptionSet again.
*QuickFixCmdPre*
QuickFixCmdPre Before a quickfix command is run (|:make|,
@@ -921,10 +944,11 @@ ShellCmdPost After executing a shell command with |:!cmd|,
For non-blocking shell commands, see
|job-control|.
*Signal*
-Signal After the nvim process received a signal.
- The pattern is matched against the name of the
- received signal. Only "SIGUSR1" is supported.
- *ShellFilterPost*
+Signal After Nvim receives a signal. The pattern is
+ matched against the signal name. Only
+ "SIGUSR1" is supported. Example: >
+ autocmd Signal SIGUSR1 call some#func()
+< *ShellFilterPost*
ShellFilterPost After executing a shell command with
":{range}!cmd", ":w !cmd" or ":r !cmd".
Can be used to check for any changed files.
@@ -972,7 +996,6 @@ SwapExists Detected an existing swap file when starting
It is not allowed to change to another buffer,
change a buffer name or change directory
here.
- {only available with the +eval feature}
*Syntax*
Syntax When the 'syntax' option has been set. The
pattern is matched against the syntax name.
@@ -1102,7 +1125,7 @@ WinNew When a new window was created. Not done for
Before a WinEnter event.
==============================================================================
-6. Patterns *autocmd-patterns* *{pat}*
+6. Patterns *autocmd-pattern* *{pat}*
The {pat} argument can be a comma separated list. This works as if the
command was given with each pattern separately. Thus this command: >
@@ -1442,9 +1465,9 @@ instead of ":q!".
*autocmd-nested* *E218*
By default, autocommands do not nest. If you use ":e" or ":w" in an
autocommand, Vim does not execute the BufRead and BufWrite autocommands for
-those commands. If you do want this, use the "nested" flag for those commands
-in which you want nesting. For example: >
- :autocmd FileChangedShell *.c nested e!
+those commands. If you do want this, use the "++nested" flag for those
+commands in which you want nesting. For example: >
+ :autocmd FileChangedShell *.c ++nested e!
The nesting is limited to 10 levels to get out of recursive loops.
It's possible to use the ":au" command in an autocommand. This can be a
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index c73b460767..912231bfcd 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -939,8 +939,6 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|.
this (that's a good habit anyway).
`:retab!` may also change a sequence of spaces by
<Tab> characters, which can mess up a printf().
- Not available when |+ex_extra| feature was disabled at
- compile time.
*retab-example*
Example for using autocommands and ":retab" to edit a file which is stored
@@ -1291,21 +1289,15 @@ The next three commands always work on whole lines.
:[range]ce[nter] [width] *:ce* *:center*
Center lines in [range] between [width] columns
(default 'textwidth' or 80 when 'textwidth' is 0).
- Not available when |+ex_extra| feature was disabled at
- compile time.
:[range]ri[ght] [width] *:ri* *:right*
Right-align lines in [range] at [width] columns
(default 'textwidth' or 80 when 'textwidth' is 0).
- Not available when |+ex_extra| feature was disabled at
- compile time.
*:le* *:left*
:[range]le[ft] [indent]
Left-align lines in [range]. Sets the indent in the
lines to [indent] (default 0).
- Not available when |+ex_extra| feature was disabled at
- compile time.
*gq*
gq{motion} Format the lines that {motion} moves over.
@@ -1635,6 +1627,17 @@ j Where it makes sense, remove a comment leader when joining lines. For
// in the list ~
Becomes:
int i; // the index in the list ~
+p Don't break lines at single spaces that follow periods. This is
+ intended to complement 'joinspaces' and |cpo-J|, for prose with
+ sentences separated by two spaces. For example, with 'textwidth' set
+ to 28: >
+ Surely you're joking, Mr. Feynman!
+< Becomes: >
+ Surely you're joking,
+ Mr. Feynman!
+< Instead of: >
+ Surely you're joking, Mr.
+ Feynman!
With 't' and 'c' you can specify when Vim performs auto-wrapping:
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 47c4677b20..81b0e17322 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -789,7 +789,6 @@ function |expand()|.
#<n (where n is a number > 0) is replaced with old *:_#<* *c_#<*
file name n. See |:oldfiles| or |v:oldfiles| to get the
number. *E809*
- {only when compiled with the |+eval| and |+shada| features}
Note that these, except "#<n", give the file name as it was typed. If an
absolute path is needed (when using the file name from a different directory),
@@ -858,8 +857,6 @@ Note: these are typed literally, they are not special keys!
*%:8* *%:p* *%:.* *%:~* *%:h* *%:t* *%:r* *%:e* *%:s* *%:gs* *%:S*
The file name modifiers can be used after "%", "#", "#n", "<cfile>", "<sfile>",
"<afile>" or "<abuf>". They are also used with the |fnamemodify()| function.
-These are not available when Vim has been compiled without the |+modify_fname|
-feature.
These modifiers can be given, in this order:
:p Make file name a full path. Must be the first modifier. Also
changes "~/" (and "~user/" for Unix) to the path for the home
diff --git a/runtime/doc/debug.txt b/runtime/doc/debug.txt
index 835b35b388..7b08e61655 100644
--- a/runtime/doc/debug.txt
+++ b/runtime/doc/debug.txt
@@ -76,15 +76,10 @@ matches the EXE (same date).
If you built the executable yourself with the Microsoft Visual C++ compiler,
then the PDB was built with the EXE.
-Alternatively, if you have the source files, you can import Make_ivc.mak into
-Visual Studio as a workspace. Then select a debug configuration, build and
-you can do all kinds of debugging (set breakpoints, watch variables, etc.).
-
If you have Visual Studio, use that instead of the VC Toolkit and WinDbg.
-For other compilers, you should always use the corresponding debugger: TD for
-a Vim executable compiled with the Borland compiler; gdb (see above
-|debug-gcc|) for the Cygwin and MinGW compilers.
+For other compilers, you should always use the corresponding debugger: gdb
+(see above |debug-gcc|) for the Cygwin and MinGW compilers.
*debug-vs2005*
@@ -112,7 +107,7 @@ line numbers. Double-click one of the lines and the Find Source dialog will
appear. Navigate to the directory where the Vim source is (if you have it.)
If you don't know how to debug this any further, follow the instructions
-at ":help bug-reports". Paste the call stack into the bug report.
+at ":help bug-report". Paste the call stack into the bug report.
If you have a non-free version of Visual Studio, you can save a minidump via
the Debug menu and send it with the bug report. A minidump is a small file
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 4369ad7894..b56240353f 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -12,6 +12,9 @@ updated.
==============================================================================
+API ~
+*nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead.
+
Commands ~
*:rv*
*:rviminfo* Deprecated alias to |:rshada| command.
@@ -62,6 +65,14 @@ Options ~
*'langnoremap'* Deprecated alias to 'nolangremap'.
*'vi'*
*'viminfo'* Deprecated alias to 'shada' option.
+*'viminfofile'* Deprecated alias to 'shadafile' option.
+
+UI extensions~
+*ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled
+ by the `ext_wildmenu` |ui-option|. Emits these events:
+ ["wildmenu_show", items]
+ ["wildmenu_select", selected]
+ ["wildmenu_hide"]
Variables~
*b:terminal_job_pid* PID of the top-level process in a |:terminal|.
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index 4b1cbe2cce..843e23ee54 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -123,7 +123,7 @@ autoload/provider/python.vim script; the provider#python#Call function is only
defined if a valid external Python host is found. That works well with the
`has('python')` expression (normally used by Python plugins) because if the
Python host isn't installed then the plugin will "think" it is running in
-a Vim compiled without the |+python| feature.
+a Vim compiled without the "+python" feature.
DOCUMENTATION *dev-doc*
@@ -157,28 +157,32 @@ with a {thing} that groups functions under a common concept).
Use existing common {action} names if possible:
add Append to, or insert into, a collection
- get Get a thing (or subset of things by some query)
- set Set a thing
+ get Get a thing (or group of things by query)
+ set Set a thing (or group of things)
del Delete a thing (or group of things)
list Get all things
Use consistent names for {thing} in all API functions. E.g. a buffer is called
"buf" everywhere, not "buffer" in some places and "buf" in others.
-Example: `nvim_get_current_line` acts on the global editor state; the common
-{action} "get" is used but {thing} is omitted.
+Example:
+ `nvim_get_current_line` acts on the global editor state; the common
+ {action} "get" is used but {thing} is omitted.
-Example: `nvim_buf_add_highlight` acts on a `Buffer` object (the first
-parameter) and uses the common {action} "add".
+Example:
+ `nvim_buf_add_highlight` acts on a `Buffer` object (the first parameter)
+ and uses the common {action} "add".
-Example: `nvim_list_bufs` operates in a global context (first parameter is
-_not_ a Buffer). The common {action} "list" indicates that it lists all
-bufs (plural) in the global context.
+Example:
+ `nvim_list_bufs` operates in a global context (first parameter is not
+ a Buffer). The common {action} "list" indicates that it lists all bufs
+ (plural) in the global context.
Use this template to name new API events:
nvim_{thing}_{event}_event
-Example: `nvim_buf_changedtick_event`.
+Example:
+ `nvim_buf_changedtick_event`
API-CLIENT *dev-api-client*
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index f05c73d737..95bc3722d0 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -32,9 +32,6 @@ An alternative is using the 'keymap' option.
first character, it has a special meaning in the
future.
-Vim is normally compiled with the |+digraphs| feature. If the feature is
-disabled, the ":digraph" command will display an error message.
-
Example of the output of ":digraphs": >
TH Þ 222 ss ß 223 a! à 224 a' á 225 a> â 226 a? ã 227 a: ä 228
@@ -53,19 +50,6 @@ conversion to be available, it might fail. For the NUL character you will see
"10". That's because NUL characters are internally represented with a NL
character. When you write the file it will become a NUL character.
-When Vim was compiled without the |+multi_byte| feature, you need to specify
-the character in the encoding given with 'encoding'. You might want to use
-something like this: >
-
- if has("multi_byte")
- digraph oe 339
- elseif &encoding == "iso-8859-15"
- digraph oe 189
- endif
-
-This defines the "oe" digraph for a character that is number 339 in Unicode
-and 189 in latin9 (iso-8859-15).
-
==============================================================================
2. Using digraphs *digraphs-use*
@@ -155,8 +139,7 @@ a standard meaning:
Example: a: is ä and o: is ö
These are the RFC1345 digraphs for the one-byte characters. See the output of
-":digraphs" for the others. The characters above 255 are only available when
-Vim was compiled with the |+multi_byte| feature.
+":digraphs" for the others.
EURO
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 34fb779fe7..f4d3164dbc 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1170,9 +1170,8 @@ If you want to always use ":confirm", set the 'confirm' option.
and |:qall| if 'confirm' is set.
{only in Win32 GUI}
When ":browse" is not possible you get an error
- message. If the |+browse| feature is missing or the
- {command} doesn't support browsing, the {command} is
- executed without a dialog.
+ message. If {command} doesn't support browsing, the
+ {command} is executed without a dialog.
":browse set" works like |:options|.
See also |:oldfiles| for ":browse oldfiles".
@@ -1460,8 +1459,6 @@ problem goes away the next day.
==============================================================================
11. File Searching *file-searching*
-{not available when compiled without the |+path_extra| feature}
-
The file searching is currently used for the 'path', 'cdpath' and 'tags'
options, for |finddir()| and |findfile()|. Other commands use |wildcards|
which is slightly different.
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index da17faaaf6..22157206a4 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -8,10 +8,6 @@ Expression evaluation *expression* *expr* *E15* *eval*
Using expressions is introduced in chapter 41 of the user manual |usr_41.txt|.
-Note: Expression evaluation can be disabled at compile time. If this has been
-done, the features in this document are not available. See |+eval| and
-|no-eval-feature|.
-
Type |gO| to see the table of contents.
==============================================================================
@@ -22,8 +18,6 @@ done, the features in this document are not available. See |+eval| and
There are six types of variables:
Number A 32 or 64 bit signed number. |expr-number| *Number*
- 64-bit Number is available only when compiled with the
- |+num64| feature.
Examples: -123 0x10 0177 0b1011
Float A floating point number. |floating-point-format| *Float*
@@ -1307,8 +1301,7 @@ is deleted when the window is closed.
*tabpage-variable* *t:var* *t:*
A variable name that is preceded with "t:" is local to the current tab page,
-It is deleted when the tab page is closed. {not available when compiled
-without the |+windows| feature}
+It is deleted when the tab page is closed.
*global-variable* *g:var* *g:*
Inside functions global variables are accessed with "g:". Omitting this will
@@ -1419,7 +1412,7 @@ v:beval_text The text under or after the mouse pointer. Usually a word as
but a dot and "->" before the position is included. When on a
']' the text before it is used, including the matching '[' and
word before it. When on a Visual area within one line the
- highlighted text is used.
+ highlighted text is used. Also see |<cexpr>|.
Only valid while evaluating the 'balloonexpr' option.
*v:beval_winnr* *beval_winnr-variable*
@@ -1509,7 +1502,8 @@ v:dying Normally zero. When a deadly signal is caught it's set to
v:exiting Exit code, or |v:null| if not exiting. |VimLeave|
*v:errmsg* *errmsg-variable*
-v:errmsg Last given error message. It's allowed to set this variable.
+v:errmsg Last given error message.
+ Modifiable (can be set).
Example: >
:let v:errmsg = ""
:silent! next
@@ -1539,6 +1533,7 @@ v:event Dictionary of event data for the current |autocommand|. Valid
cmdlevel Level of cmdline.
cmdtype Type of cmdline, |cmdline-char|.
cwd Current working directory.
+ inclusive Motion is |inclusive|, else exclusive.
scope Event-specific scope name.
operator Current |operator|. Also set for Ex
commands (unlike |v:operator|). For
@@ -1552,6 +1547,19 @@ v:event Dictionary of event data for the current |autocommand|. Valid
operation.
regtype Type of register as returned by
|getregtype()|.
+ completed_item Current selected complete item on
+ |CompleteChanged|, Is `{}` when no complete
+ item selected.
+ height Height of popup menu on |CompleteChanged|
+ width width of popup menu on |CompleteChanged|
+ row Row count of popup menu on |CompleteChanged|,
+ relative to screen.
+ col Col count of popup menu on |CompleteChanged|,
+ relative to screen.
+ size Total number of completion items on
+ |CompleteChanged|.
+ scrollbar Is |v:true| if popup menu have scrollbar, or
+ |v:false| if not.
*v:exception* *exception-variable*
v:exception The value of the exception most recently caught and not
@@ -1731,7 +1739,6 @@ v:oldfiles List of file names that is loaded from the |shada| file on
The List can be modified, but this has no effect on what is
stored in the |shada| file later. If you use values other
than String this will cause trouble.
- {only when compiled with the |+shada| feature}
*v:option_new*
v:option_new New value of the option. Valid while executing an |OptionSet|
@@ -1821,7 +1828,8 @@ v:shell_error Result of the last shell command. When non-zero, the last
:endif
<
*v:statusmsg* *statusmsg-variable*
-v:statusmsg Last given status message. It's allowed to set this variable.
+v:statusmsg Last given status message.
+ Modifiable (can be set).
*v:stderr* *stderr-variable*
v:stderr |channel-id| corresponding to stderr. The value is always 2;
@@ -1882,15 +1890,14 @@ v:termresponse The escape sequence returned by the terminal for the DA
is the terminal type: 0 for vt100 and 1 for vt220. Pv is the
patch level (since this was introduced in patch 95, it's
always 95 or bigger). Pc is always zero.
- {only when compiled with |+termresponse| feature}
*v:testing* *testing-variable*
v:testing Must be set before using `test_garbagecollect_now()`.
*v:this_session* *this_session-variable*
-v:this_session Full filename of the last loaded or saved session file. See
- |:mksession|. It is allowed to set this variable. When no
- session file has been saved, this variable is empty.
+v:this_session Full filename of the last loaded or saved session file.
+ Empty when no session file has been saved. See |:mksession|.
+ Modifiable (can be set).
*v:throwpoint* *throwpoint-variable*
v:throwpoint The point where the exception most recently caught and not
@@ -1917,22 +1924,20 @@ v:val Value of the current item of a |List| or |Dictionary|. Only
|filter()|. Read-only.
*v:version* *version-variable*
-v:version Version number of Vim: Major version number times 100 plus
- minor version number. Version 5.0 is 500. Version 5.1 (5.01)
- is 501. Read-only. "version" also works, for backwards
- compatibility.
- Use |has()| to check if a certain patch was included, e.g.: >
- if has("patch-7.4.123")
-< Note that patch numbers are specific to the version, thus both
- version 5.0 and 5.1 may have a patch 123, but these are
- completely different.
+v:version Vim version number: major version times 100 plus minor
+ version. Vim 5.0 is 500, Vim 5.1 is 501.
+ Read-only.
+ Use |has()| to check the Nvim (not Vim) version: >
+ :if has("nvim-0.2.1")
+<
*v:vim_did_enter* *vim_did_enter-variable*
-v:vim_did_enter Zero until most of startup is done. It is set to one just
- before |VimEnter| autocommands are triggered.
+v:vim_did_enter 0 during startup, 1 just before |VimEnter|.
+ Read-only.
*v:warningmsg* *warningmsg-variable*
-v:warningmsg Last given warning message. It's allowed to set this variable.
+v:warningmsg Last given warning message.
+ Modifiable (can be set).
*v:windowid* *windowid-variable*
v:windowid Application-specific window "handle" which may be set by any
@@ -1956,11 +1961,12 @@ and({expr}, {expr}) Number bitwise AND
api_info() Dict api metadata
append({lnum}, {string}) Number append {string} below line {lnum}
append({lnum}, {list}) Number append lines {list} below line {lnum}
-argc() Number number of files in the argument list
+argc([{winid}]) Number number of files in the argument list
argidx() Number current index in the argument list
arglistid([{winnr} [, {tabnr}]]) Number argument list id
-argv({nr}) String {nr} entry of the argument list
-argv() List the argument list
+argv({nr} [, {winid}]) String {nr} entry of the argument list
+argv([-1, {winid}]) List the argument list
+assert_beeps({cmd}) none assert {cmd} causes a beep
assert_equal({exp}, {act} [, {msg}])
none assert {exp} is equal to {act}
assert_exception({error} [, {msg}])
@@ -2007,6 +2013,7 @@ col({expr}) Number column nr of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match
complete_check() Number check for key typed during completion
+complete_info([{what}]) Dict get current completion information
confirm({msg} [, {choices} [, {default} [, {type}]]])
Number number of choice picked by user
copy({expr}) any make a shallow copy of {expr}
@@ -2090,6 +2097,8 @@ getfperm({fname}) String file permissions of file {fname}
getfsize({fname}) Number size in bytes of file {fname}
getftime({fname}) Number last modification time of file
getftype({fname}) String description of type of file {fname}
+getjumplist([{winnr} [, {tabnr}]])
+ List list of jump list items
getline({lnum}) String line {lnum} of current buffer
getline({lnum}, {end}) List lines {lnum} to {end} of current buffer
getloclist({nr} [, {what}]) List list of location list items
@@ -2105,10 +2114,11 @@ gettabvar({nr}, {varname} [, {def}])
any variable {varname} in tab {nr} or {def}
gettabwinvar({tabnr}, {winnr}, {name} [, {def}])
any {name} in {winnr} in tab page {tabnr}
+gettagstack([{nr}]) Dict get the tag stack of window {nr}
getwininfo([{winid}]) List list of windows
getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window
-getwinposx() Number X coord in pixels of GUI Vim window
-getwinposy() Number Y coord in pixels of GUI Vim window
+getwinposx() Number X coord in pixels of Vim window
+getwinposy() Number Y coord in pixels of Vim window
getwinvar({nr}, {varname} [, {def}])
any variable {varname} in window {nr}
glob({expr} [, {nosuf} [, {list} [, {alllinks}]]])
@@ -2270,6 +2280,8 @@ setreg({n}, {v}[, {opt}]) Number set register to value and type
settabvar({nr}, {varname}, {val}) set {varname} in tab page {nr} to {val}
settabwinvar({tabnr}, {winnr}, {varname}, {val}) set {varname} in window
{winnr} in tab page {tabnr} to {val}
+settagstack({nr}, {dict} [, {action}])
+ Number modify tag stack using {dict}
setwinvar({nr}, {varname}, {val}) set {varname} in window {nr} to {val}
sha256({string}) String SHA256 checksum of {string}
shellescape({string} [, {special}])
@@ -2314,6 +2326,8 @@ submatch({nr} [, {list}]) String or List
specific match in ":s" or substitute()
substitute({expr}, {pat}, {sub}, {flags})
String all {pat} in {expr} replaced with {sub}
+swapinfo({fname}) Dict information about swap file {fname}
+swapname({expr}) String swap file of buffer {expr}
synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col}
synIDattr({synID}, {what} [, {mode}])
String attribute {what} of syntax ID {synID}
@@ -2431,8 +2445,15 @@ append({lnum}, {text}) *append()*
:let failed = append(0, ["Chapter 1", "the beginning"])
<
*argc()*
-argc() The result is the number of files in the argument list of the
- current window. See |arglist|.
+argc([{winid}])
+ The result is the number of files in the argument list. See
+ |arglist|.
+ If {winid} is not supplied, the argument list of the current
+ window is used.
+ If {winid} is -1, the global argument list is used.
+ Otherwise {winid} specifies the window of which the argument
+ list is used: either the window number or the window ID.
+ Returns -1 if the {winid} argument is invalid.
*argidx()*
argidx() The result is the current index in the argument list. 0 is
@@ -2443,7 +2464,7 @@ arglistid([{winnr} [, {tabnr}]])
Return the argument list ID. This is a number which
identifies the argument list being used. Zero is used for the
global argument list. See |arglist|.
- Return -1 if the arguments are invalid.
+ Returns -1 if the arguments are invalid.
Without arguments use the current window.
With {winnr} only use this window in the current tab page.
@@ -2452,17 +2473,24 @@ arglistid([{winnr} [, {tabnr}]])
{winnr} can be the window number or the |window-ID|.
*argv()*
-argv([{nr}]) The result is the {nr}th file in the argument list of the
- current window. See |arglist|. "argv(0)" is the first one.
- Example: >
+argv([{nr} [, {winid}])
+ The result is the {nr}th file in the argument list. See
+ |arglist|. "argv(0)" is the first one. Example: >
:let i = 0
:while i < argc()
: let f = escape(fnameescape(argv(i)), '.')
: exe 'amenu Arg.' . f . ' :e ' . f . '<CR>'
: let i = i + 1
:endwhile
-< Without the {nr} argument a |List| with the whole |arglist| is
- returned.
+< Without the {nr} argument, or when {nr} is -1, a |List| with
+ the whole |arglist| is returned.
+
+ The {winid} argument specifies the window ID, see |argc()|.
+
+assert_beeps({cmd}) *assert_beeps()*
+ Run {cmd} and add an error message to |v:errors| if it does
+ NOT produce a beep or visual bell.
+ Also see |assert_fails()|.
*assert_equal()*
assert_equal({expected}, {actual}, [, {msg}])
@@ -2496,6 +2524,8 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
Run {cmd} and add an error message to |v:errors| if it does
NOT produce an error.
When {error} is given it must match in |v:errmsg|.
+ Note that beeping is not considered an error, and some failing
+ commands only beep. Use |assert_beeps()| for those.
assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
@@ -2830,8 +2860,8 @@ cindent({lnum}) *cindent()*
See |C-indenting|.
clearmatches() *clearmatches()*
- Clears all matches previously defined by |matchadd()| and the
- |:match| commands.
+ Clears all matches previously defined for the current window
+ by |matchadd()| and the |:match| commands.
*col()*
col({expr}) The result is a Number, which is the byte index of the column
@@ -2916,6 +2946,55 @@ complete_check() *complete_check()*
Only to be used by the function specified with the
'completefunc' option.
+ *complete_info()*
+complete_info([{what}])
+ Returns a Dictionary with information about Insert mode
+ completion. See |ins-completion|.
+ The items are:
+ mode Current completion mode name string.
+ See |completion_info_mode| for the values.
+ pum_visible |TRUE| if popup menu is visible.
+ See |pumvisible()|.
+ items List of completion matches. Each item is a
+ dictionary containing the entries "word",
+ "abbr", "menu", "kind", "info" and "user_data".
+ See |complete-items|.
+ selected Selected item index. First index is zero.
+ Index is -1 if no item is selected (showing
+ typed text only)
+ inserted Inserted string. [NOT IMPLEMENT YET]
+
+ *complete_info_mode*
+ mode values are:
+ "" Not in completion mode
+ "keyword" Keyword completion |i_CTRL-X_CTRL-N|
+ "ctrl_x" Just pressed CTRL-X |i_CTRL-X|
+ "whole_line" Whole lines |i_CTRL-X_CTRL-L|
+ "files" File names |i_CTRL-X_CTRL-F|
+ "tags" Tags |i_CTRL-X_CTRL-]|
+ "path_defines" Definition completion |i_CTRL-X_CTRL-D|
+ "path_patterns" Include completion |i_CTRL-X_CTRL-I|
+ "dictionary" Dictionary |i_CTRL-X_CTRL-K|
+ "thesaurus" Thesaurus |i_CTRL-X_CTRL-T|
+ "cmdline" Vim Command line |i_CTRL-X_CTRL-V|
+ "function" User defined completion |i_CTRL-X_CTRL-U|
+ "omni" Omni completion |i_CTRL-X_CTRL-O|
+ "spell" Spelling suggestions |i_CTRL-X_s|
+ "eval" |complete()| completion
+ "unknown" Other internal modes
+
+ If the optional {what} list argument is supplied, then only
+ the items listed in {what} are returned. Unsupported items in
+ {what} are silently ignored.
+
+ Examples: >
+ " Get all items
+ call complete_info()
+ " Get only 'mode'
+ call complete_info(['mode'])
+ " Get only 'mode' and 'pum_visible'
+ call complete_info(['mode', 'pum_visible'])
+<
*confirm()*
confirm({msg} [, {choices} [, {default} [, {type}]]])
Confirm() offers the user a dialog, from which a choice can be
@@ -3206,7 +3285,7 @@ escape({string}, {chars}) *escape()*
:echo escape('c:\program files\vim', ' \')
< results in: >
c:\\program\ files\\vim
-< Also see |shellescape()|.
+< Also see |shellescape()| and |fnameescape()|.
*eval()*
eval({string}) Evaluate {string} and return the result. Especially useful to
@@ -3600,8 +3679,6 @@ finddir({name} [, {path} [, {count}]]) *finddir()*
{name} in {path} instead of the first one.
When {count} is negative return all the matches in a |List|.
This is quite similar to the ex-command |:find|.
- {only available when compiled with the |+file_in_path|
- feature}
findfile({name} [, {path} [, {count}]]) *findfile()*
Just like |finddir()|, but find a file instead of a directory.
@@ -3618,7 +3695,7 @@ float2nr({expr}) *float2nr()*
When the value of {expr} is out of range for a |Number| the
result is truncated to 0x7fffffff or -0x7fffffff (or when
64-bit Number support is enabled, 0x7fffffffffffffff or
- -0x7fffffffffffffff. NaN results in -0x80000000 (or when
+ -0x7fffffffffffffff). NaN results in -0x80000000 (or when
64-bit Number support is enabled, -0x8000000000000000).
Examples: >
echo float2nr(3.95)
@@ -3722,7 +3799,6 @@ foldtext() Returns a String, to be displayed for a closed fold. This is
When used to draw the actual foldtext, the rest of the line
will be filled with the fold char from the 'fillchars'
setting.
- {not available when compiled without the |+folding| feature}
foldtextresult({lnum}) *foldtextresult()*
Returns the text that is displayed for the closed fold at line
@@ -3732,7 +3808,6 @@ foldtextresult({lnum}) *foldtextresult()*
{lnum} is used like with |getline()|. Thus "." is the current
line, "'m" mark m, etc.
Useful when exporting folded text, e.g., to HTML.
- {not available when compiled without the |+folding| feature}
*foreground()*
foreground() Move the Vim window to the foreground. Useful when sent from
@@ -3768,7 +3843,7 @@ function({name} [, {arglist}] [, {dict}])
same function.
When {arglist} or {dict} is present this creates a partial.
- That mans the argument list and/or the dictionary is stored in
+ That means the argument list and/or the dictionary is stored in
the Funcref and will be used when the Funcref is called.
The arguments are passed to the function in front of other
@@ -3947,14 +4022,14 @@ getchar([expr]) *getchar()*
not consumed. Return zero if no character available.
Without [expr] and when [expr] is 0 a whole character or
- special key is returned. If it is an 8-bit character, the
+ special key is returned. If it is a single character, the
result is a number. Use nr2char() to convert it to a String.
Otherwise a String is returned with the encoded character.
- For a special key it's a sequence of bytes starting with 0x80
- (decimal: 128). This is the same value as the string
- "\<Key>", e.g., "\<Left>". The returned value is also a
- String when a modifier (shift, control, alt) was used that is
- not included in the character.
+ For a special key it's a String with a sequence of bytes
+ starting with 0x80 (decimal: 128). This is the same value as
+ the String "\<Key>", e.g., "\<Left>". The returned value is
+ also a String when a modifier (shift, control, alt) was used
+ that is not included in the character.
When [expr] is 0 and Esc is typed, there will be a short delay
while Vim waits to see if this is the start of an escape
@@ -4226,6 +4301,26 @@ getftype({fname}) *getftype()*
systems that support it. On some systems only "dir" and
"file" are returned.
+ *getjumplist()*
+getjumplist([{winnr} [, {tabnr}]])
+ Returns the |jumplist| for the specified window.
+
+ Without arguments use the current window.
+ With {winnr} only use this window in the current tab page.
+ {winnr} can also be a |window-ID|.
+ With {winnr} and {tabnr} use the window in the specified tab
+ page.
+
+ The returned list contains two entries: a list with the jump
+ locations and the last used jump position number in the list.
+ Each entry in the jump location list is a dictionary with
+ the following entries:
+ bufnr buffer number
+ col column number
+ coladd column offset for 'virtualedit'
+ filename filename if available
+ lnum line number
+
*getline()*
getline({lnum} [, {end}])
Without {end} the result is a String, which is line {lnum}
@@ -4265,10 +4360,11 @@ getloclist({nr},[, {what}]) *getloclist()*
|getqflist()| for the supported items in {what}.
getmatches() *getmatches()*
- Returns a |List| with all matches previously defined by
- |matchadd()| and the |:match| commands. |getmatches()| is
- useful in combination with |setmatches()|, as |setmatches()|
- can restore a list of matches saved by |getmatches()|.
+ Returns a |List| with all matches previously defined for the
+ current window by |matchadd()| and the |:match| commands.
+ |getmatches()| is useful in combination with |setmatches()|,
+ as |setmatches()| can restore a list of matches saved by
+ |getmatches()|.
Example: >
:echo getmatches()
< [{'group': 'MyGroup1', 'pattern': 'TODO',
@@ -4481,6 +4577,41 @@ gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()*
To obtain all window-local variables use: >
gettabwinvar({tabnr}, {winnr}, '&')
+gettagstack([{nr}]) *gettagstack()*
+ The result is a Dict, which is the tag stack of window {nr}.
+ {nr} can be the window number or the |window-ID|.
+ When {nr} is not specified, the current window is used.
+ When window {nr} doesn't exist, an empty Dict is returned.
+
+ The returned dictionary contains the following entries:
+ curidx Current index in the stack. When at
+ top of the stack, set to (length + 1).
+ Index of bottom of the stack is 1.
+ items List of items in the stack. Each item
+ is a dictionary containing the
+ entries described below.
+ length Number of entries in the stack.
+
+ Each item in the stack is a dictionary with the following
+ entries:
+ bufnr buffer number of the current jump
+ from cursor position before the tag jump.
+ See |getpos()| for the format of the
+ returned list.
+ matchnr current matching tag number. Used when
+ multiple matching tags are found for a
+ name.
+ tagname name of the tag
+
+ See |tagstack| for more information about the tag stack.
+
+getwinpos([{timeout}]) *getwinpos()*
+ The result is a list with two numbers, the result of
+ getwinposx() and getwinposy() combined:
+ [x-pos, y-pos]
+ {timeout} can be used to specify how long to wait in msec for
+ a response from the terminal. When omitted 100 msec is used.
+
*getwinposx()*
getwinposx() The result is a Number, which is the X coordinate in pixels of
the left hand side of the GUI Vim window. The result will be
@@ -4609,10 +4740,47 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
supported, thus using 'path' will not always work properly.
*has()*
-has({feature}) The result is a Number, which is 1 if the feature {feature} is
- supported, zero otherwise. The {feature} argument is a
- string. See |feature-list| below.
- Also see |exists()|.
+has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The
+ {feature} argument is a feature name like "nvim-0.2.1" or
+ "win32", see below. See also |exists()|.
+
+ Vim's compile-time feature-names (prefixed with "+") are not
+ recognized because Nvim is always compiled with all possible
+ features. |feature-compile|
+
+ Feature names can be:
+ 1. Nvim version. For example the "nvim-0.2.1" feature means
+ that Nvim is version 0.2.1 or later: >
+ :if has("nvim-0.2.1")
+
+< 2. Runtime condition or other pseudo-feature. For example the
+ "win32" feature checks if the current system is Windows: >
+ :if has("win32")
+< *feature-list*
+ List of supported pseudo-feature names:
+ acl |ACL| support
+ iconv Can use |iconv()| for conversion.
+ +shellslash Can use backslashes in filenames (Windows)
+ clipboard |clipboard| provider is available.
+ nvim This is Nvim.
+ python2 Legacy Vim |python2| interface. |has-python|
+ python3 Legacy Vim |python3| interface. |has-python|
+ pythonx Legacy Vim |python_x| interface. |has-pythonx|
+ ttyin input is a terminal (tty)
+ ttyout output is a terminal (tty)
+ unix Unix system.
+ *vim_starting* True during |startup|.
+ win32 Windows system (32 or 64 bit).
+ wsl WSL (Windows Subsystem for Linux) system
+
+ *has-patch*
+ 3. Vim patch. For example the "patch123" feature means that
+ Vim patch 123 at the current |v:version| was included: >
+ :if v:version > 602 || v:version == 602 && has("patch148")
+
+< 4. Vim version. For example the "patch-7.4.237" feature means
+ that Nvim is Vim-compatible to version 7.4.237 or later. >
+ :if has("patch-7.4.237")
has_key({dict}, {key}) *has_key()*
@@ -5489,7 +5657,7 @@ match({expr}, {pat} [, {start} [, {count}]]) *match()*
the pattern. 'smartcase' is NOT used. The matching is always
done like 'magic' is set and 'cpoptions' is empty.
- *matchadd()* *E798* *E799* *E801*
+ *matchadd()* *E798* *E799* *E801* *E957*
matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
Defines a pattern to be highlighted in the current window (a
"match"). It will be highlighted with {group}. Returns an
@@ -5528,6 +5696,8 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
conceal Special character to show instead of the
match (only for |hl-Conceal| highlighed
matches, see |:syn-cchar|)
+ window Instead of the current window use the
+ window with this number or window ID.
The number of matches is not limited, as it is the case with
the |:match| commands.
@@ -6831,6 +7001,7 @@ setline({lnum}, {text}) *setline()*
:for [n, l] in [[5, 'aaa'], [6, 'bbb'], [7, 'ccc']]
: call setline(n, l)
:endfor
+
< Note: The '[ and '] marks are not set.
setloclist({nr}, {list} [, {action}[, {what}]]) *setloclist()*
@@ -6848,9 +7019,10 @@ setloclist({nr}, {list} [, {action}[, {what}]]) *setloclist()*
for the list of supported keys in {what}.
setmatches({list}) *setmatches()*
- Restores a list of matches saved by |getmatches()|. Returns 0
- if successful, otherwise -1. All current matches are cleared
- before the list is restored. See example for |getmatches()|.
+ Restores a list of matches saved by |getmatches() for the
+ current window|. Returns 0 if successful, otherwise -1. All
+ current matches are cleared before the list is restored. See
+ example for |getmatches()|.
*setpos()*
setpos({expr}, {list})
@@ -6914,6 +7086,8 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()*
buffer
filename name of a file; only used when "bufnr" is not
present or it is invalid.
+ module name of a module; if given it will be used in
+ quickfix error window instead of the filename
lnum line number in the file
pattern search pattern used to locate the error
col column number
@@ -6958,9 +7132,6 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()*
freed. To add a new quickfix list at the end of the stack,
set "nr" in {what} to "$".
- If {title} is given, it will be used to set |w:quickfix_title|
- after opening the quickfix window.
-
If the optional {what} dictionary argument is supplied, then
only the items listed in {what} are set. The first {list}
argument is ignored. The following items can be specified in
@@ -7034,16 +7205,17 @@ setreg({regname}, {value} [, {options}])
:call setreg('a', "1\n2\n3", 'b5')
< This example shows using the functions to save and restore a
- register (note: you may not reliably restore register value
- without using the third argument to |getreg()| as without it
- newlines are represented as newlines AND Nul bytes are
- represented as newlines as well, see |NL-used-for-Nul|). >
+ register: >
:let var_a = getreg('a', 1, 1)
:let var_amode = getregtype('a')
....
:call setreg('a', var_a, var_amode)
+< Note: you may not reliably restore register value
+ without using the third argument to |getreg()| as without it
+ newlines are represented as newlines AND Nul bytes are
+ represented as newlines as well, see |NL-used-for-Nul|.
-< You can also change the type of a register by appending
+ You can also change the type of a register by appending
nothing: >
:call setreg('a', '', 'al')
@@ -7070,6 +7242,38 @@ settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()*
:call settabwinvar(3, 2, "myvar", "foobar")
< This function is not available in the |sandbox|.
+settagstack({nr}, {dict} [, {action}]) *settagstack()*
+ Modify the tag stack of the window {nr} using {dict}.
+ {nr} can be the window number or the |window-ID|.
+
+ For a list of supported items in {dict}, refer to
+ |gettagstack()|
+ *E962*
+ If {action} is not present or is set to 'r', then the tag
+ stack is replaced. If {action} is set to 'a', then new entries
+ from {dict} are pushed onto the tag stack.
+
+ Returns zero for success, -1 for failure.
+
+ Examples:
+ Set current index of the tag stack to 4: >
+ call settagstack(1005, {'curidx' : 4})
+
+< Empty the tag stack of window 3: >
+ call settagstack(3, {'items' : []})
+
+< Push a new item onto the tag stack: >
+ let pos = [bufnr('myfile.txt'), 10, 1, 0]
+ let newtag = [{'tagname' : 'mytag', 'from' : pos}]
+ call settagstack(2, {'items' : newtag}, 'a')
+
+< Save and restore the tag stack: >
+ let stack = gettagstack(1003)
+ " do something else
+ call settagstack(1003, stack)
+ unlet stack
+<
+
setwinvar({nr}, {varname}, {val}) *setwinvar()*
Like |settabwinvar()| for the current tab page.
Examples: >
@@ -7640,6 +7844,31 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
|submatch()| returns. Example: >
:echo substitute(s, '\(\x\x\)', {m -> '0x' . m[1]}, 'g')
+swapinfo({fname}) *swapinfo()*
+ The result is a dictionary, which holds information about the
+ swapfile {fname}. The available fields are:
+ version VIM version
+ user user name
+ host host name
+ fname original file name
+ pid PID of the VIM process that created the swap
+ file
+ mtime last modification time in seconds
+ inode Optional: INODE number of the file
+ dirty 1 if file was modified, 0 if not
+ In case of failure an "error" item is added with the reason:
+ Cannot open file: file not found or in accessible
+ Cannot read file: cannot read first block
+ Not a swap file: does not contain correct block ID
+ Magic number mismatch: Info in first block is invalid
+
+swapname({expr}) *swapname()*
+ The result is the swap file path of the buffer {expr}.
+ For the use of {expr}, see |bufname()| above.
+ If buffer {expr} is the current buffer, the result is equal to
+ |:swapname| (unless no swap file).
+ If buffer {expr} has no swap file, returns an empty string.
+
synID({lnum}, {col}, {trans}) *synID()*
The result is a Number, which is the syntax ID at the position
{lnum} and {col} in the current window.
@@ -7762,7 +7991,7 @@ system({cmd} [, {input}]) *system()* *E677*
items converted to NULs).
When {input} is given and is a valid buffer id, the content of
the buffer is written to the file line by line, each line
- terminated by a NL (and NUL where the text has NL).
+ terminated by NL (and NUL where the text has NL).
*E5677*
Note: system() cannot write to or read from backgrounded ("&")
shell commands, e.g.: >
@@ -7779,7 +8008,7 @@ system({cmd} [, {input}]) *system()* *E677*
The characters in 'shellquote' and 'shellxquote' may also
cause trouble.
- The result is a String. Example: >
+ Result is a String. Example: >
:let files = system("ls " . shellescape(expand('%:h')))
:let files = system('ls ' . expand('%:h:S'))
@@ -7993,6 +8222,10 @@ timer_start({time}, {callback} [, {options}])
"repeat" Number of times to repeat calling the
callback. -1 means forever. When not present
the callback will be called once.
+ If the timer causes an error three times in a
+ row the repeat is cancelled. This avoids that
+ Vim becomes unusable because of all the error
+ messages.
Example: >
func MyHandler(timer)
@@ -8236,7 +8469,7 @@ win_getid([{win} [, {tab}]]) *win_getid()*
Get the |window-ID| for the specified window.
When {win} is missing use the current window.
With {win} this is the window number. The top window has
- number 1.
+ number 1. Use `win_getid(winnr())` for the current window.
Without {tab} use the current tab, otherwise the tab with
number {tab}. The first tab has number one.
Return zero if the window cannot be found.
@@ -8298,17 +8531,30 @@ winline() The result is a Number, which is the screen line of the cursor
*winnr()*
winnr([{arg}]) The result is a Number, which is the number of the current
window. The top window has number 1.
- When the optional argument is "$", the number of the
- last window is returned (the window count). >
- let window_count = winnr('$')
-< When the optional argument is "#", the number of the last
- accessed window is returned (where |CTRL-W_p| goes to).
- If there is no previous window or it is in another tab page 0
- is returned.
+
+ The optional argument {arg} supports the following values:
+ $ the number of the last window (the window
+ count).
+ # the number of the last accessed window (where
+ |CTRL-W_p| goes to). If there is no previous
+ window or it is in another tab page 0 is
+ returned.
+ {N}j the number of the Nth window below the
+ current window (where |CTRL-W_j| goes to).
+ {N}k the number of the Nth window above the current
+ window (where |CTRL-W_k| goes to).
+ {N}h the number of the Nth window left of the
+ current window (where |CTRL-W_h| goes to).
+ {N}l the number of the Nth window right of the
+ current window (where |CTRL-W_l| goes to).
The number can be used with |CTRL-W_w| and ":wincmd w"
|:wincmd|.
Also see |tabpagewinnr()| and |win_getid()|.
-
+ Examples: >
+ let window_count = winnr('$')
+ let prev_window = winnr('#')
+ let wnum = winnr('3k')
+<
*winrestcmd()*
winrestcmd() Returns a sequence of |:resize| commands that should restore
the current window sizes. Only works properly when no windows
@@ -8437,154 +8683,6 @@ xor({expr}, {expr}) *xor()*
<
- *feature-list*
-There are four types of features:
-1. Features that are only supported when they have been enabled when Vim
- was compiled |+feature-list|. Example: >
- :if has("cindent")
-2. Features that are only supported when certain conditions have been met.
- Example: >
- :if has("win32")
-< *has-patch*
-3. Nvim version. The "nvim-0.2.1" feature means that the Nvim version is
- 0.2.1 or later. Example: >
- :if has("nvim-0.2.1")
-<
-4. Included patches. The "patch123" feature means that patch 123 has been
- included. Note that this form does not check the version of Vim, you need
- to inspect |v:version| for that.
- Example (checking version 6.2.148 or later): >
- :if v:version > 602 || v:version == 602 && has("patch148")
-< Note that it's possible for patch 147 to be omitted even though 148 is
- included.
-
-5. Beyond a certain version or at a certain version and including a specific
- patch. The "patch-7.4.237" feature means that the Vim version is 7.5 or
- later, or it is version 7.4 and patch 237 was included.
- Note that this only works for patch 7.4.237 and later, before that you
- need to use the example above that checks v:version. Example: >
- :if has("patch-7.4.248")
-< Note that it's possible for patch 147 to be omitted even though 148 is
- included.
-
-Hint: To find out if Vim supports backslashes in a file name (MS-Windows),
-use: `if exists('+shellslash')`
-
-
-acl Compiled with |ACL| support.
-arabic Compiled with Arabic support |Arabic|.
-autocmd Compiled with autocommand support. |autocommand|
-browse Compiled with |:browse| support, and browse() will
- work.
-browsefilter Compiled with support for |browsefilter|.
-byte_offset Compiled with support for 'o' in 'statusline'
-cindent Compiled with 'cindent' support.
-clipboard Compiled with 'clipboard' support.
-cmdline_compl Compiled with |cmdline-completion| support.
-cmdline_hist Compiled with |cmdline-history| support.
-cmdline_info Compiled with 'showcmd' and 'ruler' support.
-comments Compiled with |'comments'| support.
-cscope Compiled with |cscope| support.
-debug Compiled with "DEBUG" defined.
-dialog_con Compiled with console dialog support.
-digraphs Compiled with support for digraphs.
-eval Compiled with expression evaluation support. Always
- true, of course!
-ex_extra |+ex_extra|, always true now
-extra_search Compiled with support for |'incsearch'| and
- |'hlsearch'|
-file_in_path Compiled with support for |gf| and |<cfile>|
-filterpipe When 'shelltemp' is off pipes are used for shell
- read/write/filter commands
-find_in_path Compiled with support for include file searches
- |+find_in_path|.
-float Compiled with support for |Float|.
-fname_case Case in file names matters (for Windows this is not
- present).
-folding Compiled with |folding| support.
-gettext Compiled with message translation |multi-lang|
-iconv Can use iconv() for conversion.
-insert_expand Compiled with support for CTRL-X expansion commands in
- Insert mode.
-jumplist Compiled with |jumplist| support.
-keymap Compiled with 'keymap' support.
-lambda Compiled with |lambda| support.
-langmap Compiled with 'langmap' support.
-libcall Compiled with |libcall()| support.
-linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
- 'breakindent' support.
-lispindent Compiled with support for lisp indenting.
-listcmds Compiled with commands for the buffer list |:files|
- and the argument list |arglist|.
-localmap Compiled with local mappings and abbr. |:map-local|
-mac macOS version of Nvim.
-menu Compiled with support for |:menu|.
-mksession Compiled with support for |:mksession|.
-modify_fname Compiled with file name modifiers. |filename-modifiers|
-mouse Compiled with support mouse.
-mouseshape Compiled with support for 'mouseshape'.
-multi_byte Compiled with support for 'encoding'
-multi_byte_encoding 'encoding' is set to a multi-byte encoding.
-multi_lang Compiled with support for multiple languages.
-num64 Compiled with 64-bit |Number| support.
-nvim This is Nvim. |has-patch|
-path_extra Compiled with up/downwards search in 'path' and 'tags'
-persistent_undo Compiled with support for persistent undo history.
-postscript Compiled with PostScript file printing.
-printer Compiled with |:hardcopy| support.
-profile Compiled with |:profile| support.
-python Legacy Vim Python 2.x API is available. |has-python|
-python3 Legacy Vim Python 3.x API is available. |has-python|
-pythonx Compiled with |python_x| interface. |has-pythonx|
-quickfix Compiled with |quickfix| support.
-reltime Compiled with |reltime()| support.
-rightleft Compiled with 'rightleft' support.
-scrollbind Compiled with 'scrollbind' support.
-shada Compiled with shada support.
-showcmd Compiled with 'showcmd' support.
-signs Compiled with |:sign| support.
-smartindent Compiled with 'smartindent' support.
-spell Compiled with spell checking support |spell|.
-startuptime Compiled with |--startuptime| support.
-statusline Compiled with support for 'statusline', 'rulerformat'
- and special formats of 'titlestring' and 'iconstring'.
-syntax Compiled with syntax highlighting support |syntax|.
-syntax_items There are active syntax highlighting items for the
- current buffer.
-tablineat 'tabline' option accepts %@Func@ items.
-tag_binary Compiled with binary searching in tags files
- |tag-binary-search|.
-tag_old_static Compiled with support for old static tags
- |tag-old-static|.
-tag_any_white Compiled with support for any white characters in tags
- files |tag-any-white|.
-termresponse Compiled with support for t_RV and |v:termresponse|.
-textobjects Compiled with support for |text-objects|.
-timers Compiled with |timer_start()| support.
-title Compiled with window title support |'title'|.
-ttyin input is a terminal (tty)
-ttyout output is a terminal (tty)
-unix Unix version of Vim.
-unnamedplus Compiled with support for "unnamedplus" in 'clipboard'
-user_commands User-defined commands.
-vertsplit Compiled with vertically split windows |:vsplit|.
-vim_starting True while initial source'ing takes place. |startup|
- *vim_starting*
-virtualedit Compiled with 'virtualedit' option.
-visual Compiled with Visual mode.
-visualextra Compiled with extra Visual mode commands.
- |blockwise-operators|.
-vreplace Compiled with |gR| and |gr| commands.
-vtp Compiled for vcon support |+vtp| (check vcon to find
- out if it works in the current console)).
-wildignore Compiled with 'wildignore' option.
-wildmenu Compiled with 'wildmenu' option.
-win32 Windows version of Vim (32 or 64 bit).
-winaltkeys Compiled with 'winaltkeys' option.
-windows Compiled with support for more than one window.
-writebackup Compiled with 'writebackup' default on.
-wsl WSL (Windows Subsystem for Linux) version of Vim.
-
*string-match*
Matching a pattern in a String
diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt
index c36aeffa1a..7f90074ff0 100644
--- a/runtime/doc/if_lua.txt
+++ b/runtime/doc/if_lua.txt
@@ -16,11 +16,11 @@ an idea of what lurks beneath: >
:lua print(vim.inspect(package.loaded))
-Nvim includes a "standard library" |lua-stdlib| for Lua. This library
-complements the Nvim editor |functions| and Ex commands (the editor "stdlib"),
-which can also be used from Lua code.
+Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
+"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can
+be used from Lua code.
-Nvim resolves module conflicts by "last wins". For example if both of these
+Module conflicts are resolved by "last wins". For example if both of these
are on 'runtimepath':
runtime/lua/foo.lua
~/.config/nvim/lua/foo.lua
@@ -30,7 +30,7 @@ finds and loads Lua modules. The conventions are similar to VimL plugins,
with some extra features. See |lua-require-example| for a walkthrough.
==============================================================================
-Importing modules *lua-require*
+Importing Lua modules *lua-require*
Nvim automatically adjusts `package.path` and `package.cpath` according to
effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
@@ -258,11 +258,81 @@ position are restricted when the command is executed in the |sandbox|.
==============================================================================
-vim.* *lua-vim* *lua-stdlib*
+luaeval() *lua-luaeval* *lua-eval*
+ *luaeval()*
+
+The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
+"luaeval". "luaeval" takes an expression string and an optional argument used
+for _A inside expression and returns the result of the expression. It is
+semantically equivalent in Lua to:
+>
+ local chunkheader = "local _A = select(1, ...) return "
+ function luaeval (expstr, arg)
+ local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
+ return chunk(arg) -- return typval
+ end
+
+Lua nils, numbers, strings, tables and booleans are converted to their
+respective VimL types. An error is thrown if conversion of any other Lua types
+is attempted.
+
+The magic global "_A" contains the second argument to luaeval().
+
+Example: >
+ :echo luaeval('_A[1] + _A[2]', [40, 2])
+ 42
+ :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
+ foo
+
+Lua tables are used as both dictionaries and lists, so it is impossible to
+determine whether empty table is meant to be empty list or empty dictionary.
+Additionally lua does not have integer numbers. To distinguish between these
+cases there is the following agreement:
+
+0. Empty table is empty list.
+1. Table with N incrementally growing integral numbers, starting from 1 and
+ ending with N is considered to be a list.
+2. Table with string keys, none of which contains NUL byte, is considered to
+ be a dictionary.
+3. Table with string keys, at least one of which contains NUL byte, is also
+ considered to be a dictionary, but this time it is converted to
+ a |msgpack-special-map|.
+ *lua-special-tbl*
+4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
+ value:
+ - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
+ a floating-point 1.0. Note that by default integral lua numbers are
+ converted to |Number|s, non-integral are converted to |Float|s. This
+ variant allows integral |Float|s.
+ - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty
+ dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is
+ converted to a dictionary `{'a': 42}`: non-string keys are ignored.
+ Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3.
+ are errors.
+ - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well
+ as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not
+ form a 1-step sequence from 1 to N are ignored, as well as all
+ non-integral keys.
+
+Examples: >
+
+ :echo luaeval('math.pi')
+ :function Rand(x,y) " random uniform between x and y
+ : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
+ : endfunction
+ :echo Rand(1,10)
+
+Note that currently second argument to `luaeval` undergoes VimL to lua
+conversion, so changing containers in lua do not affect values in VimL. Return
+value is also always converted. When converting, |msgpack-special-dict|s are
+treated specially.
+
+==============================================================================
+Lua standard modules *lua-stdlib*
-The "standard library" (stdlib) of Nvim Lua is the `vim` module, which exposes
-various functions and sub-modules. The module is implicitly loaded, thus
-require("vim") is unnecessary.
+The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes
+various functions and sub-modules. It is always loaded, thus require("vim")
+is unnecessary.
You can peek at the module properties: >
@@ -288,11 +358,11 @@ To find documentation on e.g. the "deepcopy" function: >
:help vim.deepcopy
-Note: Underscore-prefixed functions (e.g. "_os_proc_children") are
+Note that underscore-prefixed functions (e.g. "_os_proc_children") are
internal/private and must not be used by plugins.
------------------------------------------------------------------------------
-vim.api.* functions
+VIM.API *lua-api*
`vim.api` exposes the full Nvim |API| as a table of Lua functions.
@@ -302,55 +372,18 @@ For example, to use the "nvim_get_current_line()" API function, call
print(tostring(vim.api.nvim_get_current_line()))
------------------------------------------------------------------------------
-vim.* builtin functions
-
-vim.deepcopy({object}) *vim.deepcopy*
- Performs a deep copy of the given object, and returns that copy.
- For a non-table object, that just means a usual copy of the object,
- while for a table all subtables are copied recursively.
-
-vim.gsplit({s}, {sep}, {plain}) *vim.gsplit*
- Split a given string by a separator. Returns an iterator of the
- split components. The separator can be a lua pattern, see
- https://www.lua.org/pil/20.2.html
- Setting {plain} to `true` turns off pattern matching, as it is passed
- to `string:find`, see
- http://lua-users.org/wiki/StringLibraryTutorial
-
- Parameters:~
- {s} String: String to split
- {sep} String: Separator pattern. If empty, split by chars.
- {plain} Boolean: If false, match {sep} verbatim
-
- Return:~
- Iterator of strings, which are the components of {s} after
- splitting
-
-vim.split({s}, {sep}, {plain}) *vim.split*
- Split a given string by a separator. Returns a table containing the
- split components. The separator can be a lua pattern, see
- https://www.lua.org/pil/20.2.html
- Setting {plain} to `true` turns off pattern matching, as it is passed
- to `string:find`, see
- http://lua-users.org/wiki/StringLibraryTutorial
-
- Parameters:~
- {s} String: String to split
- {sep} String: Separator pattern. If empty, split by chars.
- {plain} Boolean: If false, match {sep} verbatim
-
- Return:~
- Table of strings, which are the components of {s} after
- splitting
+VIM *lua-util*
+
+vim.inspect({object}, {options}) *vim.inspect*
+ Return a human-readable representation of the passed object. See
+ https://github.com/kikito/inspect.lua
+ for details and possible options.
vim.stricmp(a, b) *lua-vim.stricmp*
Function used for case-insensitive string comparison. Takes two
string arguments and returns 0, 1 or -1 if strings are equal, a is
greater then b or a is lesser then b respectively.
-vim.trim({string}) *vim.trim*
- Returns the string with all leading and trailing whitespace removed.
-
vim.type_idx *lua-vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the
values from |lua-vim.types| allows typing the empty table (it is
@@ -386,86 +419,106 @@ vim.types *lua-vim.types*
`vim.types.dictionary` will not change or that `vim.types` table will
only contain values for these three types.
-------------------------------------------------------------------------------
-vim.* runtime functions
+==============================================================================
+Lua module: vim *lua-vim*
-Those functions are only available after the runtime files have been loaded.
-In particular, they are not available when using `nvim -u NONE`.
+trim({s}) *vim.trim()*
+ Trim whitespace (Lua pattern "%%s") from both sides of a
+ string.
-vim.inspect({object}, {options}) *vim.inspect*
- Return a human-readable representation of the passed object. See
- https://github.com/kikito/inspect.lua
- for details and possible options.
+ Parameters: ~
+ {s} String to trim
-==============================================================================
-luaeval() *lua-luaeval* *lua-eval*
- *luaeval()*
+ Return: ~
+ String with whitespace removed from its beginning and end
-The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
-"luaeval". "luaeval" takes an expression string and an optional argument used
-for _A inside expression and returns the result of the expression. It is
-semantically equivalent in Lua to:
->
- local chunkheader = "local _A = select(1, ...) return "
- function luaeval (expstr, arg)
- local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
- return chunk(arg) -- return typval
- end
+ See also: ~
+ https://www.lua.org/pil/20.2.html
-Lua nils, numbers, strings, tables and booleans are converted to their
-respective VimL types. An error is thrown if conversion of any other Lua types
-is attempted.
-The magic global "_A" contains the second argument to luaeval().
-Example: >
- :echo luaeval('_A[1] + _A[2]', [40, 2])
- 42
- :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
- foo
-Lua tables are used as both dictionaries and lists, so it is impossible to
-determine whether empty table is meant to be empty list or empty dictionary.
-Additionally lua does not have integer numbers. To distinguish between these
-cases there is the following agreement:
+deepcopy({orig}) *vim.deepcopy()*
+ Returns a deep copy of the given object. Non-table objects are
+ copied as in a typical Lua assignment, whereas table objects
+ are copied recursively.
-0. Empty table is empty list.
-1. Table with N incrementally growing integral numbers, starting from 1 and
- ending with N is considered to be a list.
-2. Table with string keys, none of which contains NUL byte, is considered to
- be a dictionary.
-3. Table with string keys, at least one of which contains NUL byte, is also
- considered to be a dictionary, but this time it is converted to
- a |msgpack-special-map|.
- *lua-special-tbl*
-4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
- value:
- - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
- a floating-point 1.0. Note that by default integral lua numbers are
- converted to |Number|s, non-integral are converted to |Float|s. This
- variant allows integral |Float|s.
- - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty
- dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is
- converted to a dictionary `{'a': 42}`: non-string keys are ignored.
- Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3.
- are errors.
- - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well
- as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not
- form a 1-step sequence from 1 to N are ignored, as well as all
- non-integral keys.
+ Parameters: ~
+ {orig} Table to copy
-Examples: >
+ Return: ~
+ New table of copied keys and (nested) values.
- :echo luaeval('math.pi')
- :function Rand(x,y) " random uniform between x and y
- : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
- : endfunction
- :echo Rand(1,10)
+gsplit({s}, {sep}, {plain}) *vim.gsplit()*
+ Splits a string at each instance of a separator.
-Note that currently second argument to `luaeval` undergoes VimL to lua
-conversion, so changing containers in lua do not affect values in VimL. Return
-value is also always converted. When converting, |msgpack-special-dict|s are
-treated specially.
+ Parameters: ~
+ {s} String to split
+ {sep} Separator string or pattern
+ {plain} If `true` use `sep` literally (passed to
+ String.find)
-==============================================================================
- vim:tw=78:ts=8:et:ft=help:norl:
+ Return: ~
+ Iterator over the split components
+
+ See also: ~
+ |vim.split()|
+ https://www.lua.org/pil/20.2.html
+ http://lua-users.org/wiki/StringLibraryTutorial
+
+split({s}, {sep}, {plain}) *vim.split()*
+ Splits a string at each instance of a separator.
+
+ Examples: >
+ split(":aa::b:", ":") --> {'','aa','','bb',''}
+ split("axaby", "ab?") --> {'','x','y'}
+ split(x*yz*o, "*", true) --> {'x','yz','o'}
+<
+
+ Parameters: ~
+ {s} String to split
+ {sep} Separator string or pattern
+ {plain} If `true` use `sep` literally (passed to
+ String.find)
+
+ Return: ~
+ List-like table of the split components.
+
+ See also: ~
+ |vim.gsplit()|
+
+tbl_contains({t}, {value}) *vim.tbl_contains()*
+ Checks if a list-like (vector) table contains `value` .
+
+ Parameters: ~
+ {t} Table to check
+ {value} Value to compare
+
+ Return: ~
+ true if `t` contains `value`
+
+tbl_extend({behavior}, {...}) *vim.tbl_extend()*
+ Merges two or more map-like tables.
+
+ Parameters: ~
+ {behavior} Decides what to do if a key is found in more
+ than one map:
+ - "error": raise an error
+ - "keep": use value from the leftmost map
+ - "force": use value from the rightmost map
+ {...} Two or more map-like tables.
+
+ See also: ~
+ |extend()|
+
+tbl_flatten({t}) *vim.tbl_flatten()*
+ Creates a copy of a list-like table such that any nested
+ tables are "unrolled" and appended to the result.
+
+ Parameters: ~
+ {t} List-like table
+
+ Return: ~
+ Flattened copy of the given list-like table.
+
+ vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt
index aae091aa99..6820b9c240 100644
--- a/runtime/doc/indent.txt
+++ b/runtime/doc/indent.txt
@@ -56,12 +56,13 @@ typing certain characters or commands in certain contexts. Note that this not
only triggers C-indenting. When 'indentexpr' is not empty 'indentkeys' is
used instead. The format of 'cinkeys' and 'indentkeys' is equal.
-The default is "0{,0},0),:,0#,!^F,o,O,e" which specifies that indenting occurs
-as follows:
+The default is "0{,0},0),0],:,0#,!^F,o,O,e" which specifies that indenting
+occurs as follows:
"0{" if you type '{' as the first character in a line
"0}" if you type '}' as the first character in a line
"0)" if you type ')' as the first character in a line
+ "0]" if you type ']' as the first character in a line
":" if you type ':' after a label or case statement
"0#" if you type '#' as the first character in a line
"!^F" if you type CTRL-F (which is not inserted)
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index f22f90548b..fce4d8628e 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -359,8 +359,8 @@ CTRL-\ CTRL-O like CTRL-O but don't move the cursor *i_CTRL-\_CTRL-O*
CTRL-L when 'insertmode' is set: go to Normal mode *i_CTRL-L*
CTRL-G u break undo sequence, start new change *i_CTRL-G_u*
CTRL-G U don't break undo with next left/right cursor *i_CTRL-G_U*
- movement (but only if the cursor stays
- within same the line)
+ movement, if the cursor stays within
+ same the line
-----------------------------------------------------------------------
The CTRL-O command sometimes has a side effect: If the cursor was beyond the
@@ -617,6 +617,7 @@ and one of the CTRL-X commands. You exit CTRL-X mode by typing a key that is
not a valid CTRL-X mode command. Valid keys are the CTRL-X command itself,
CTRL-N (next), and CTRL-P (previous).
+To get the current completion information, |complete_info()| can be used.
Also see the 'infercase' option if you want to adjust the case of the match.
*complete_CTRL-E*
@@ -1074,6 +1075,9 @@ items:
icase when non-zero case is to be ignored when comparing
items to be equal; when omitted zero is used, thus
items that only differ in case are added
+ equal when non-zero, always treat this item to be equal when
+ comparing. Which means, "equal=1" disables filtering
+ of this item.
dup when non-zero this match will be added even when an
item with the same word is already present.
empty when non-zero this match will be added even when it is
@@ -1081,10 +1085,10 @@ items:
user_data custom data which is associated with the item and
available in |v:completed_item|
-All of these except "icase", "dup" and "empty" must be a string. If an item
-does not meet these requirements then an error message is given and further
-items in the list are not used. You can mix string and Dictionary items in
-the returned list.
+All of these except "icase", "equal", "dup" and "empty" must be a string. If
+an item does not meet these requirements then an error message is given and
+further items in the list are not used. You can mix string and Dictionary
+items in the returned list.
The "menu" item is used in the popup menu and may be truncated, thus it should
be relatively short. The "info" item can be longer, it will be displayed in
@@ -1840,7 +1844,8 @@ NOTE: These commands cannot be used with |:global| or |:vglobal|.
":endif", ":for" and ":endfor", ":while" and ":endwhile".
*:start* *:startinsert*
-:star[tinsert][!] Start Insert mode just after executing this command.
+:star[tinsert][!] Start Insert mode (or |Terminal-mode| in a |terminal|
+ buffer) just after executing this command.
Works like typing "i" in Normal mode. When the ! is
included it works like "A", append to the line.
Otherwise insertion starts at the cursor position.
@@ -1850,8 +1855,8 @@ NOTE: These commands cannot be used with |:global| or |:vglobal|.
This command does not work from |:normal|.
*:stopi* *:stopinsert*
-:stopi[nsert] Stop Insert mode as soon as possible. Works like
- typing <Esc> in Insert mode.
+:stopi[nsert] Stop Insert mode or |Terminal-mode| as soon as
+ possible. Works like typing <Esc> in Insert mode.
Can be used in an autocommand, example: >
:au BufEnter scratch stopinsert
<
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index fee7d9aa69..1fb06e169c 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -58,7 +58,7 @@ For more information try one of these:
==============================================================================
Nvim on the interwebs *internet*
- *www* *WWW* *faq* *FAQ* *distribution* *download*
+ *www* *faq* *distribution* *download*
Nvim home page: https://neovim.io/
Nvim FAQ: https://github.com/neovim/neovim/wiki/FAQ
@@ -67,9 +67,10 @@ Nvim on the interwebs *internet*
Vim home page: https://www.vim.org/
-Bug reports: *bugs* *bug-reports* *bugreport.vim*
+ *bugs* *bug-report* *bugreport.vim* *feature-request*
-Report bugs on GitHub: https://github.com/neovim/neovim/issues
+Report bugs and request features here:
+https://github.com/neovim/neovim/issues
Be brief, yet complete. Always give a reproducible example and try to find
out which settings or other things trigger the bug.
@@ -359,16 +360,24 @@ notation meaning equivalent decimal value(s) ~
<End> end *end*
<PageUp> page-up *page_up* *page-up*
<PageDown> page-down *page_down* *page-down*
+<kUp> keypad cursor-up *keypad-cursor-up*
+<kDown> keypad cursor-down *keypad-cursor-down*
+<kLeft> keypad cursor-left *keypad-cursor-left*
+<kRight> keypad cursor-right *keypad-cursor-right*
<kHome> keypad home (upper left) *keypad-home*
<kEnd> keypad end (lower left) *keypad-end*
+<kOrigin> keypad origin (middle) *keypad-origin*
<kPageUp> keypad page-up (upper right) *keypad-page-up*
<kPageDown> keypad page-down (lower right) *keypad-page-down*
+<kDel> keypad delete *keypad-delete*
<kPlus> keypad + *keypad-plus*
<kMinus> keypad - *keypad-minus*
<kMultiply> keypad * *keypad-multiply*
<kDivide> keypad / *keypad-divide*
+<kPoint> keypad . *keypad-point*
+<kComma> keypad , *keypad-comma*
+<kEqual> keypad = *keypad-equal*
<kEnter> keypad Enter *keypad-enter*
-<kPoint> keypad Decimal point *keypad-point*
<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9*
<S-...> shift-key *shift* *<S-*
<C-...> control-key *control* *ctrl* *<C-*
@@ -392,6 +401,9 @@ recognized as the non-keypad code. For example, when <kHome> sends the same
code as <Home>, when pressing <kHome> Vim will think <Home> was pressed.
Mapping <kHome> will not work then.
+Note: If numlock is on, the |TUI| receives plain ASCII values, so
+mappings to <k0> - <k9> and <kPoint> will not work.
+
*<>*
Examples are often given in the <> notation. Sometimes this is just to make
clear what you need to type, but often it can be typed literally, e.g., with
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index fa5e10b5e5..eac42df791 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -227,14 +227,14 @@ should not either insert or change the v:char.
Be very careful about side effects! The expression is evaluated while
obtaining characters, you may very well make the command dysfunctional.
-For this reason the following is blocked:
+Therefore the following is blocked for <expr> mappings:
- Changing the buffer text |textlock|.
- Editing another buffer.
- The |:normal| command.
- Moving the cursor is allowed, but it is restored afterwards.
- If the cmdline is changed, the old text and cursor position are restored.
If you want the mapping to do any of these let the returned characters do
-that. Or use a |<Cmd>| mapping (which doesn't have these restrictions).
+that. (Or use a |<Cmd>| mapping instead.)
You can use getchar(), it consumes typeahead if there is any. E.g., if you
have these mappings: >
@@ -274,9 +274,9 @@ Using 0x80 as a single byte before other text does not work, it will be seen
as a special key.
*<Cmd>* *:map-cmd*
-The <Cmd> pseudokey may be used to define a "command mapping", which executes
-the command directly (without changing modes, etc.). Where you might use
-":...<CR>" in the {lhs} of a mapping, you can instead use "<Cmd>...<CR>".
+The <Cmd> pseudokey begins a "command mapping", which executes the command
+directly (without changing modes). Where you might use ":...<CR>" in the
+{lhs} of a mapping, you can instead use "<Cmd>...<CR>".
Example: >
noremap x <Cmd>echo mode(1)<cr>
<
@@ -284,17 +284,19 @@ This is more flexible than `:<C-U>` in visual and operator-pending mode, or
`<C-O>:` in insert-mode, because the commands are executed directly in the
current mode (instead of always going to normal-mode). Visual-mode is
preserved, so tricks with |gv| are not needed. Commands can be invoked
-directly in cmdline-mode (which otherwise would require timer hacks).
-
-Because <Cmd> avoids mode-changes (unlike ":") it does not trigger
-|CmdlineEnter| and |CmdlineLeave| events. This helps performance.
+directly in cmdline-mode (which would otherwise require timer hacks).
Unlike <expr> mappings, there are no special restrictions on the <Cmd>
command: it is executed as if an (unrestricted) |autocmd| was invoked or an
async event event was processed.
-In select-mode, |:map| and |:vmap| command mappings are executed in
-visual-mode. Use |:smap| to handle select-mode.
+Note:
+- Because <Cmd> avoids mode-changes (unlike ":") it does not trigger
+ |CmdlineEnter| and |CmdlineLeave| events. This helps performance.
+- For the same reason, |keycodes| like <C-R><C-W> are interpreted as plain,
+ unmapped keys.
+- In select-mode, |:map| and |:vmap| command mappings are executed in
+ visual-mode. Use |:smap| to handle select-mode.
*E5520*
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index a46648119e..205db12f3b 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -740,11 +740,10 @@ You tried to set an option after startup that only allows changes during
startup.
*E943* >
- Command table needs to be updated, run 'make cmdidxs'
+ Command table needs to be updated, run 'make'
-This can only happen when changing the source code, when adding a command in
-src/ex_cmds.h. The lookup table then needs to be updated, by running: >
- make cmdidxs
+This can only happen when changing the source code, after adding a command in
+src/ex_cmds.lua. Update the lookup table by re-running the build. >
==============================================================================
3. Messages *messages*
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 84867318a4..d5a123e3ea 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -66,10 +66,11 @@ and end position. Generally, motions that move between lines affect lines
characterwise). However, there are some exceptions.
*exclusive* *inclusive*
-A character motion is either inclusive or exclusive. When inclusive, the
+Character motion is either inclusive or exclusive. When inclusive, the
start and end position of the motion are included in the operation. When
exclusive, the last character towards the end of the buffer is not included.
-Linewise motions always include the start and end position.
+Linewise motions always include the start and end position. Plugins can
+check the v:event.inclusive flag of the |TextYankPost| event.
Which motions are linewise, inclusive or exclusive is mentioned with the
command. There are however, two general exceptions:
diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt
index 862aa7b750..91a4a3c267 100644
--- a/runtime/doc/msgpack_rpc.txt
+++ b/runtime/doc/msgpack_rpc.txt
@@ -114,17 +114,18 @@ You can also embed an Nvim instance via |jobstart()|, and communicate using
==============================================================================
4. Implementing API clients *rpc-api-client* *api-client*
-"API clients" wrap the Nvim API to provide idiomatic "SDKs" for their
-respective platforms (see |dev-jargon|). You can build a new API client for
-your favorite platform or programming language.
+API clients wrap the Nvim API to provide idiomatic "SDKs" for their respective
+platforms (see |jargon|). You can build a new API client for your favorite
+platform or programming language.
-Existing API clients are listed here:
+API clients are listed here:
https://github.com/neovim/neovim/wiki/Related-projects#api-clients
+ *pynvim*
The Python client is the reference implementation for API clients. It is
always up-to-date with the Nvim API, so its source code and test suite are
authoritative references.
- https://github.com/neovim/python-client
+ https://github.com/neovim/pynvim
API client implementation guidelines ~
diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt
index 07eb48aea3..513d27ccad 100644
--- a/runtime/doc/nvim.txt
+++ b/runtime/doc/nvim.txt
@@ -8,14 +8,14 @@ Nvim *nvim* *nvim-intro*
Nvim is based on Vim by Bram Moolenaar.
+If you already use Vim see |nvim-from-vim| for a quickstart.
If you are new to Vim, try the 30-minute tutorial: >
- :Tutor<Enter>
-If you already use Vim see |nvim-from-vim| for a quickstart.
+ :Tutor<Enter>
-Nvim is emphatically a fork of Vim, not a clone: compatibility with Vim is
-maintained where possible. See |vim_diff.txt| for the complete reference of
-differences from Vim.
+Nvim is emphatically a fork of Vim, not a clone: compatibility with Vim
+(especially editor and VimL features) is maintained where possible. See
+|vim-differences| for the complete reference of differences from Vim.
Type |gO| to see the table of contents.
diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt
index cdcb61404f..aba0571dc0 100644
--- a/runtime/doc/nvim_terminal_emulator.txt
+++ b/runtime/doc/nvim_terminal_emulator.txt
@@ -7,8 +7,8 @@
Terminal emulator *terminal* *terminal-emulator*
Nvim embeds a VT220/xterm terminal emulator based on libvterm. The terminal is
-presented as a special buffer type, asynchronously updated from the virtual
-terminal as data is received from the program connected to it.
+presented as a special 'buftype', asynchronously updated as data is received
+from the connected program.
Terminal buffers behave like normal buffers, except:
- With 'modifiable', lines can be edited but not deleted.
@@ -23,11 +23,11 @@ Terminal buffers behave like normal buffers, except:
==============================================================================
Start *terminal-start*
-There are 3 ways to create a terminal buffer:
+There are several ways to create a terminal buffer:
-- By invoking the |:terminal| ex command.
-- By calling the |termopen()| function.
-- By editing a file with a name matching `term://(.{-}//(\d+:)?)?\zs.*`.
+- Invoke the |:terminal| command.
+- Call the |termopen()| function.
+- Edit a file with a name matching `term://(.{-}//(\d+:)?)?\zs.*`.
For example:
>
:edit term://bash
@@ -35,7 +35,7 @@ There are 3 ways to create a terminal buffer:
<
Note: The "term://" pattern is handled by a BufReadCmd handler, so the
|autocmd-nested| modifier is required to use it in an autocmd. >
- autocmd VimEnter * nested split term://sh
+ autocmd VimEnter * ++nested split term://sh
< This is only mentioned for reference; use |:terminal| instead.
When the terminal starts, the buffer contents are updated and the buffer is
@@ -98,14 +98,21 @@ global configuration.
- 'wrap' is disabled
You can change the defaults with a TermOpen autocommand: >
- au TermOpen * setlocal list
+ au TermOpen * setlocal list
TERMINAL COLORS ~
-The `{g,b}:terminal_color_$NUM` variables control the terminal color palette,
-where `$NUM` is the color index between 0 and 255 inclusive. This setting only
-affects UIs with RGB capabilities; for normal terminals the color index is
-just forwarded. The variables are read only during |TermOpen|.
+The `{g,b}:terminal_color_x` variables control the terminal color palette,
+where `x` is the color index between 0 and 255 inclusive. The variables are
+read during |TermOpen|. The value must be a color name or hexadecimal string.
+Example: >
+ let g:terminal_color_4 = '#ff0000'
+ let g:terminal_color_5 = 'green'
+Only works for RGB UIs (see 'termguicolors'); for 256-color terminals the
+color index is just forwarded.
+
+Editor highlighting (|syntax-highlighting|, |highlight-groups|, etc.) has
+higher precedence: it is applied after terminal colors are resolved.
==============================================================================
Status Variables *terminal-status*
@@ -124,4 +131,257 @@ a local 'statusline'. Example: >
:autocmd TermOpen * setlocal statusline=%{b:term_title}
<
==============================================================================
+5. Debugging *terminal-debug* *terminal-debugger*
+
+The Terminal debugging plugin can be used to debug a program with gdb and view
+the source code in a Vim window. Since this is completely contained inside
+Vim this also works remotely over an ssh connection.
+
+Starting ~
+ *termdebug-starting*
+Load the plugin with this command: >
+ packadd termdebug
+< *:Termdebug*
+To start debugging use `:Termdebug` or `:TermdebugCommand` followed by the
+command name, for example: >
+ :Termdebug vim
+
+This opens two windows:
+
+gdb window A terminal window in which "gdb vim" is executed. Here you
+ can directly interact with gdb. The buffer name is "!gdb".
+
+program window A terminal window for the executed program. When "run" is
+ used in gdb the program I/O will happen in this window, so
+ that it does not interfere with controlling gdb. The buffer
+ name is "gdb program".
+
+The current window is used to show the source code. When gdb pauses the
+source file location will be displayed, if possible. A sign is used to
+highlight the current position, using highlight group debugPC.
+
+If the buffer in the current window is modified, another window will be opened
+to display the current gdb position.
+
+Focus the terminal of the executed program to interact with it. This works
+the same as any command running in a terminal window.
+
+When the debugger ends, typically by typing "quit" in the gdb window, the two
+opened windows are closed.
+
+Only one debugger can be active at a time.
+ *:TermdebugCommand*
+If you want to give specific commands to the command being debugged, you can
+use the `:TermdebugCommand` command followed by the command name and
+additional parameters. >
+ :TermdebugCommand vim --clean -c ':set nu'
+
+Both the `:Termdebug` and `:TermdebugCommand` support an optional "!" bang
+argument to start the command right away, without pausing at the gdb window
+(and cursor will be in the debugged window). For example: >
+ :TermdebugCommand! vim --clean
+
+To attach gdb to an already running executable or use a core file, pass extra
+arguments. E.g.: >
+ :Termdebug vim core
+ :Termdebug vim 98343
+
+If no argument is given, you'll end up in a gdb window, in which you need to
+specify which command to run using e.g. the gdb `file` command.
+
+
+Example session ~
+ *termdebug-example*
+Start in the Vim "src" directory and build Vim: >
+ % make
+Start Vim: >
+ % ./vim
+Load the termdebug plugin and start debugging Vim: >
+ :packadd termdebug
+ :Termdebug vim
+You should now have three windows:
+ source - where you started
+ gdb - you can type gdb commands here
+ program - the executed program will use this window
+
+Put focus on the gdb window and type: >
+ break ex_help
+ run
+Vim will start running in the program window. Put focus there and type: >
+ :help gui
+Gdb will run into the ex_help breakpoint. The source window now shows the
+ex_cmds.c file. A red "1 " marker will appear in the signcolumn where the
+breakpoint was set. The line where the debugger stopped is highlighted. You
+can now step through the program. You will see the highlighting move as the
+debugger executes a line of source code.
+
+Run ":Next" a few times until the for loop is highlighted. Put the cursor on
+the end of "eap->arg", then call ":Eval". You will see this displayed:
+ "eap->arg": 0x555555e68855 "gui" ~
+This way you can inspect the value of local variables. You can also focus the
+gdb window and use a "print" command, e.g.: >
+ print *eap
+If mouse pointer movements are working, Vim will also show a balloon when the
+mouse rests on text that can be evaluated by gdb.
+You can also use the "K" mapping that will either use neovim floating windows
+if available to show the results or print below the status bar.
+
+Now go back to the source window and put the cursor on the first line after
+the for loop, then type: >
+ :Break
+You will see a "1" marker appear, this indicates the new breakpoint. Now
+run ":Cont" command and the code until the breakpoint will be executed.
+
+You can type more advanced commands in the gdb window. For example, type: >
+ watch curbuf
+Now run ":Cont" (or type "cont" in the gdb window). Execution
+will now continue until the value of "curbuf" changes, which is in do_ecmd().
+To remove this watchpoint again type in the gdb window: >
+ delete 3
+
+You can see the stack by typing in the gdb window: >
+ where
+Move through the stack frames, e.g. with: >
+ frame 3
+The source window will show the code, at the point where the call was made to
+a deeper level.
+
+
+Stepping through code ~
+ *termdebug-stepping*
+Put focus on the gdb window to type commands there. Some common ones are:
+- CTRL-C interrupt the program
+- next execute the current line and stop at the next line
+- step execute the current line and stop at the next statement,
+ entering functions
+- finish execute until leaving the current function
+- where show the stack
+- frame N go to the Nth stack frame
+- continue continue execution
+
+ *:Run* *:Arguments*
+In the window showing the source code these commands can be used to control
+gdb:
+ `:Run` [args] run the program with [args] or the previous arguments
+ `:Arguments` {args} set arguments for the next `:Run`
+
+ *:Break* set a breakpoint at the current line; a sign will be displayed
+ *:Clear* delete the breakpoint at the current line
+
+ *:Step* execute the gdb "step" command
+ *:Over* execute the gdb "next" command (`:Next` is a Vim command)
+ *:Finish* execute the gdb "finish" command
+ *:Continue* execute the gdb "continue" command
+ *:Stop* interrupt the program
+
+If gdb stops at a source line and there is no window currently showing the
+source code, a new window will be created for the source code. This also
+happens if the buffer in the source code window has been modified and can't be
+abandoned.
+
+Gdb gives each breakpoint a number. In Vim the number shows up in the sign
+column, with a red background. You can use these gdb commands:
+- info break list breakpoints
+- delete N delete breakpoint N
+You can also use the `:Clear` command if the cursor is in the line with the
+breakpoint, or use the "Clear breakpoint" right-click menu entry.
+
+
+Inspecting variables ~
+ *termdebug-variables* *:Evaluate*
+ `:Evaluate` evaluate the expression under the cursor
+ `K` same
+ `:Evaluate` {expr} evaluate {expr}
+ `:'<,'>Evaluate` evaluate the Visually selected text
+
+This is similar to using "print" in the gdb window.
+You can usually shorten `:Evaluate` to `:Ev`.
+
+
+Other commands ~
+ *termdebug-commands*
+ *:Gdb* jump to the gdb window
+ *: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
+
+
+Communication ~
+ *termdebug-communication*
+There is another, hidden, buffer, which is used for Vim to communicate with
+gdb. The buffer name is "gdb communication". Do not delete this buffer, it
+will break the debugger.
+
+Gdb has some weird behavior, the plugin does its best to work around that.
+For example, after typing "continue" in the gdb window a CTRL-C can be used to
+interrupt the running program. But after using the MI command
+"-exec-continue" pressing CTRL-C does not interrupt. Therefore you will see
+"continue" being used for the `:Continue` command, instead of using the
+communication channel.
+
+
+Customizing ~
+
+GDB command *termdebug-customizing*
+
+To change the name of the gdb command, set the "termdebugger" variable before
+invoking `:Termdebug`: >
+ let termdebugger = "mygdb"
+
+To not use neovim floating windows for previewing variable evaluation, set the
+`g:termdebug_useFloatingHover` variable like this: >
+ let g:termdebug_useFloatingHover = 0
+
+If you are a mouse person, you can also define a mapping using your right
+click to one of the terminal command like evaluate the variable under the
+cursor: >
+ nnoremap <RightMouse> :Evaluate<CR>
+or set/unset a breakpoint: >
+ nnoremap <RightMouse> :Break<CR>
+
+< *gdb-version*
+Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI
+interface. The "new-ui" command requires gdb version 7.12 or later. if you
+get this error:
+ Undefined command: "new-ui". Try "help".~
+Then your gdb is too old.
+
+
+Colors *hl-debugPC* *hl-debugBreakpoint*
+
+The color of the signs can be adjusted with these highlight groups:
+- debugPC the current position
+- debugBreakpoint a breakpoint
+
+The defaults are, when 'background' is "light":
+ hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
+ hi debugBreakpoint term=reverse ctermbg=red guibg=red
+
+When 'background' is "dark":
+ hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
+ hi debugBreakpoint term=reverse ctermbg=red guibg=red
+
+
+Shorcuts *termdebug_shortcuts*
+
+You can define your own shortcuts (mappings) to control gdb, that can work in
+any window, using the TermDebugSendCommand() function. Example: >
+ map ,w :call TermDebugSendCommand('where')<CR>
+The argument is the gdb command.
+
+
+Vim window width *termdebug_wide*
+
+To change the width of the Vim window when debugging starts, and use a
+vertical split: >
+ let g:termdebug_wide = 163
+This will set &columns to 163 when `:Termdebug` is used. The value is restored
+when quitting the debugger.
+If g:termdebug_wide is set and &columns is already larger than
+g:termdebug_wide then a vertical split will be used without changing &columns.
+Set it to 1 to get a vertical split without every changing &columns (useful
+for when the terminal can't be resized by Vim).
+
+
+
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 505c0c5ea0..cce9f681f9 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -694,8 +694,9 @@ A jump table for the options with a short description can be found at |Q_op|.
*'background'* *'bg'*
'background' 'bg' string (default "dark")
global
- When set to "dark" or "light", Nvim will adjust the default color
- groups for a dark or light background, respectively.
+ When set to "dark" or "light", adjusts the default color groups for
+ that background type. The |TUI| or other UI sets this on startup
+ (triggering |OptionSet|) if it can detect the background color.
This option does NOT change the background color, it tells Nvim what
the "inherited" (terminal/GUI) background looks like.
@@ -878,7 +879,7 @@ A jump table for the options with a short description can be found at |Q_op|.
A list of file patterns. When one of the patterns matches with the
name of the file which is written, no backup file is created. Both
the specified file name and the full path name of the file are used.
- The pattern is used like with |:autocmd|, see |autocmd-patterns|.
+ The pattern is used like with |:autocmd|, see |autocmd-pattern|.
Watch out for special characters, see |option-backslash|.
When $TMPDIR, $TMP or $TEMP is not defined, it is not used for the
default value. "/tmp/*" is only used for Unix.
@@ -1214,7 +1215,7 @@ A jump table for the options with a short description can be found at |Q_op|.
This option is not used when 'paste' is set.
*'cinkeys'* *'cink'*
-'cinkeys' 'cink' string (default "0{,0},0),:,0#,!^F,o,O,e")
+'cinkeys' 'cink' string (default "0{,0},0),0],:,0#,!^F,o,O,e")
local to buffer
A list of keys that, when typed in Insert mode, cause reindenting of
the current line. Only used if 'cindent' is on and 'indentexpr' is
@@ -1831,9 +1832,9 @@ A jump table for the options with a short description can be found at |Q_op|.
name. See |option-backslash| about using backslashes.
This has nothing to do with the |Dictionary| variable type.
Where to find a list of words?
- - On FreeBSD, there is the file "/usr/share/dict/words".
- - In the Simtel archive, look in the "msdos/linguist" directory.
- - In "miscfiles" of the GNU collection.
+ - BSD/macOS include the "/usr/share/dict/words" file.
+ - Try "apt install spell" to get the "/usr/share/dict/words" file on
+ apt-managed systems (Debian/Ubuntu).
The use of |:set+=| and |:set-=| is preferred when adding or removing
directories from the list. This avoids problems when a future version
uses another default.
@@ -3265,7 +3266,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'indentkeys'* *'indk'*
-'indentkeys' 'indk' string (default "0{,0},:,0#,!^F,o,O,e")
+'indentkeys' 'indk' string (default "0{,0},0),0],:,0#,!^F,o,O,e")
local to buffer
A list of keys that, when typed in Insert mode, cause reindenting of
the current line. Only happens if 'indentexpr' isn't empty.
@@ -4527,10 +4528,14 @@ A jump table for the options with a short description can be found at |Q_op|.
'redrawtime' 'rdt' number (default 2000)
global
Time in milliseconds for redrawing the display. Applies to
- 'hlsearch', 'inccommand' and |:match| highlighting.
+ 'hlsearch', 'inccommand', |:match| highlighting and syntax
+ highlighting.
When redrawing takes more than this many milliseconds no further
- matches will be highlighted. This is used to avoid that Vim hangs
- when using a very complicated pattern.
+ matches will be highlighted.
+ For syntax highlighting the time applies per window. When over the
+ limit syntax highlighting is disabled until |CTRL-L| is used.
+ This is used to avoid that Vim hangs when using a very complicated
+ pattern.
*'regexpengine'* *'re'*
'regexpengine' 're' number (default 0)
@@ -4658,12 +4663,11 @@ A jump table for the options with a short description can be found at |Q_op|.
:set rulerformat=%15(%c%V\ %p%%%)
<
*'runtimepath'* *'rtp'* *vimfiles*
-'runtimepath' 'rtp' string (default:
- Unix: "$XDG_CONFIG_HOME/nvim,
+'runtimepath' 'rtp' string (default: "$XDG_CONFIG_HOME/nvim,
$XDG_CONFIG_DIRS[1]/nvim,
$XDG_CONFIG_DIRS[2]/nvim,
- $XDG_DATA_HOME/nvim/site,
+ $XDG_DATA_HOME/nvim[-data]/site,
$XDG_DATA_DIRS[1]/nvim/site,
$XDG_DATA_DIRS[2]/nvim/site,
@@ -4671,14 +4675,13 @@ A jump table for the options with a short description can be found at |Q_op|.
$XDG_DATA_DIRS[2]/nvim/site/after,
$XDG_DATA_DIRS[1]/nvim/site/after,
- $XDG_DATA_HOME/nvim/site/after,
+ $XDG_DATA_HOME/nvim[-data]/site/after,
$XDG_CONFIG_DIRS[2]/nvim/after,
$XDG_CONFIG_DIRS[1]/nvim/after,
$XDG_CONFIG_HOME/nvim/after")
global
- This is a list of directories which will be searched for runtime
- files:
+ List of directories to be searched for these runtime files:
filetype.vim filetypes by file name |new-filetype|
scripts.vim filetypes by file contents |new-filetype-scripts|
autoload/ automatically loaded scripts |autoload-functions|
@@ -4693,25 +4696,27 @@ A jump table for the options with a short description can be found at |Q_op|.
pack/ packages |:packadd|
plugin/ plugin scripts |write-plugin|
print/ files for printing |postscript-print-encoding|
+ rplugin/ |remote-plugin| scripts
spell/ spell checking files |spell|
syntax/ syntax files |mysyntaxfile|
tutor/ tutorial files |:Tutor|
And any other file searched for with the |:runtime| command.
- The defaults for most systems are setup to search these locations:
- 1. In your home directory (XDG_CONFIG_HOME defaults to $HOME/.config),
- for your personal preferences.
- 2. In directories which must contain configuration files according to
- XDG ($XDG_CONFIG_DIRS). This also contains preferences from system
- administrator (XDG_CONFIG_DIRS defaults to /etc/xdg).
- 3. In data home directory, for plugins installed by user.
- 4. In nvim/site subdirectories for each directory in $XDG_DATA_DIRS.
- This is for plugins which were installed by system administrator,
- but are not part of the Neovim distribution. XDG_DATA_DIRS defaults
- to /usr/local/share/:/usr/share/, so system administrators are
+ Defaults are setup to search these locations:
+ 1. Your home directory, for personal preferences.
+ Given by `stdpath("config")`. |$XDG_CONFIG_HOME|
+ 2. Directories which must contain configuration files according to
+ |xdg| ($XDG_CONFIG_DIRS, defaults to /etc/xdg). This also contains
+ preferences from system administrator.
+ 3. Data home directory, for plugins installed by user.
+ Given by `stdpath("data")`. |$XDG_DATA_HOME|
+ 4. nvim/site subdirectories for each directory in $XDG_DATA_DIRS.
+ This is for plugins which were installed by system administrator,
+ but are not part of the Nvim distribution. XDG_DATA_DIRS defaults
+ to /usr/local/share/:/usr/share/, so system administrators are
expected to install site plugins to /usr/share/nvim/site.
- 5. In $VIMRUNTIME, for files distributed with Neovim.
+ 5. $VIMRUNTIME, for files distributed with Neovim.
*after-directory*
6, 7, 8, 9. In after/ subdirectories of 1, 2, 3 and 4, with reverse
ordering. This is for preferences to overrule or add to the
@@ -4998,9 +5003,9 @@ A jump table for the options with a short description can be found at |Q_op|.
has been used since the last search command.
*shada-n*
n Name of the shada file. The name must immediately follow
- the 'n'. Must be at the end of the option! If the "-i"
- argument was given when starting Vim, that file name overrides
- the one given here with 'shada'. Environment variables are
+ the 'n'. Must be at the end of the option! If the
+ 'shadafile' option is set, that file name overrides the one
+ given here with 'shada'. Environment variables are
expanded when opening the file, not when setting the option.
*shada-r*
r Removable media. The argument is a string (up to the next
@@ -5042,6 +5047,14 @@ A jump table for the options with a short description can be found at |Q_op|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
+ *'shadafile'* *'sdf'*
+'shadafile' 'sdf' string (default: "")
+ global
+ When non-empty, overrides the file name used for |shada| (viminfo).
+ When equal to "NONE" no shada file will be read or written.
+ This option can be set with the |-i| command line flag. The |--clean|
+ command line flag sets it to "NONE".
+
*'shell'* *'sh'* *E91*
'shell' 'sh' string (default $SHELL or "sh",
Windows: "cmd.exe")
@@ -5403,10 +5416,14 @@ A jump table for the options with a short description can be found at |Q_op|.
*'signcolumn'* *'scl'*
'signcolumn' 'scl' string (default "auto")
local to window
- Whether or not to draw the signcolumn. Valid values are:
+ When and how to draw the signcolumn. Valid values are:
"auto" only when there is a sign to display
+ "auto:[1-9]" resize to accommodate multiple signs up to the
+ given number (maximum 9), e.g. "auto:4"
"no" never
"yes" always
+ "yes:[1-9]" always, with fixed space for signs up to the given
+ number (maximum 9), e.g. "yes:3"
*'smartcase'* *'scs'* *'nosmartcase'* *'noscs'*
@@ -5555,7 +5572,7 @@ A jump table for the options with a short description can be found at |Q_op|.
After this option has been set successfully, Vim will source the files
"spell/LANG.vim" in 'runtimepath'. "LANG" is the value of 'spelllang'
- up to the first comma, dot or underscore.
+ up to the first character that is not an ASCII letter and not a dash.
Also see |set-spc-auto|.
@@ -5816,7 +5833,7 @@ A jump table for the options with a short description can be found at |Q_op|.
Watch out for errors in expressions. They may render Vim unusable!
If you are stuck, hold down ':' or 'Q' to get a prompt, then quit and
- edit your vimrc or whatever with "vim -u NONE" to get it right.
+ edit your vimrc or whatever with "vim --clean" to get it right.
Examples:
Emulate standard status line with 'ruler' set >
@@ -6114,17 +6131,16 @@ A jump table for the options with a short description can be found at |Q_op|.
*'termguicolors'* *'tgc'*
'termguicolors' 'tgc' boolean (default off)
global
- When on, uses |highlight-guifg| and |highlight-guibg| attributes in
- the terminal (thus using 24-bit color). Requires a ISO-8613-3
- compatible terminal.
+ Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight|
+ attributes instead of "cterm" attributes. |highlight-guifg|
+ Requires an ISO-8613-3 compatible terminal.
*'terse'* *'noterse'*
'terse' boolean (default off)
global
When set: Add 's' flag to 'shortmess' option (this makes the message
for a search that hits the start or end of the file not being
- displayed). When reset: Remove 's' flag from 'shortmess' option. {Vi
- shortens a lot of messages}
+ displayed). When reset: Remove 's' flag from 'shortmess' option.
*'textwidth'* *'tw'*
'textwidth' 'tw' number (default 0)
@@ -6146,9 +6162,7 @@ A jump table for the options with a short description can be found at |Q_op|.
the file should contain words with similar meaning, separated by
non-keyword characters (white space is preferred). Maximum line
length is 510 bytes.
- To obtain a file to be used here, check out this ftp site:
- [Sorry this link doesn't work anymore, do you know the right one?]
- ftp://ftp.ox.ac.uk/pub/wordlists/ First get the README file.
+
To include a comma in a file name precede it with a backslash. Spaces
after a comma are ignored, otherwise spaces are included in the file
name. See |option-backslash| about using backslashes.
@@ -6502,7 +6516,7 @@ A jump table for the options with a short description can be found at |Q_op|.
patterns is ignored when expanding |wildcards|, completing file or
directory names, and influences the result of |expand()|, |glob()| and
|globpath()| unless a flag is passed to disable this.
- The pattern is used like with |:autocmd|, see |autocmd-patterns|.
+ The pattern is used like with |:autocmd|, see |autocmd-pattern|.
Also see 'suffixes'.
Example: >
:set wildignore=*.o,*.obj
@@ -6599,12 +6613,13 @@ A jump table for the options with a short description can be found at |Q_op|.
'wildoptions' 'wop' string (default "")
global
A list of words that change how command line completion is done.
- Currently only one word is allowed:
tagfile When using CTRL-D to list matching tags, the kind of
tag and the file of the tag is listed. Only one match
is displayed per line. Often used tag kinds are:
d #define
f function
+ pum Display the completion matches using the popupmenu
+ in the same style as the |ins-completion-menu|.
Also see |cmdline-completion|.
*'winaltkeys'* *'wak'*
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index 09fdc6872b..364fbac351 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -26,29 +26,34 @@ Nvim supports Python |remote-plugin|s and the Vim legacy |python2| and
Note: Only the Vim 7.3 legacy interface is supported, not later features such
as |python-bindeval| (Vim 7.4); use the Nvim API instead.
+
PYTHON QUICKSTART ~
-Install the "pynvim" Python package:
+To use Python plugins, you need the "pynvim" module. Run |:checkhealth| to see
+if you already have it (some package managers install the module with Nvim
+itself).
+
+For Python 3 plugins:
+1. Make sure Python 3.4+ is available in your $PATH.
+2. Install the module (try "python" if "python3" is missing): >
+ python3 -m pip install --user --upgrade pynvim
+
+For Python 2 plugins:
+1. Make sure Python 2.7 is available in your $PATH.
+2. Install the module (try "python" if "python2" is missing): >
+ python2 -m pip install --user --upgrade pynvim
-- Run |:checkhealth| to see if you already have the package (some package
- managers install the "pynvim" Python package with Nvim itself).
+The pip `--upgrade` flag ensures that you get the latest version even if
+a previous version was already installed.
-- For Python 2 plugins, make sure Python 2.7 is available in your $PATH, then
- install the package systemwide: >
- sudo pip2 install --upgrade pynvim
-< or for the current user: >
- pip2 install --user --upgrade pynvim
-< If "pip2" is missing, try "pip".
+See also |python-virtualenv|.
-- For Python 3 plugins, make sure Python 3.4+ is available in your $PATH, then
- install the package systemwide: >
- sudo pip3 install --upgrade pynvim
-< or for the current user: >
- pip3 install --user --upgrade pynvim
-< If "pip3" is missing, try "pip".
+Note: The old "neovim" module was renamed to "pynvim".
+https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
+If you run into problems, uninstall _both_ then install "pynvim" again: >
+ python -m pip uninstall neovim pynvim
+ python -m pip install --user --upgrade pynvim
-- The `--upgrade` flag ensures you have the latest version even if a previous
- version was already installed.
PYTHON PROVIDER CONFIGURATION ~
*g:python_host_prog*
@@ -69,8 +74,9 @@ To disable Python 2 support: >
To disable Python 3 support: >
let g:loaded_python3_provider = 1
-PYTHON VIRTUALENVS ~
+PYTHON VIRTUALENVS ~
+ *python-virtualenv*
If you plan to use per-project virtualenvs often, you should assign one
virtualenv for Neovim and hard-code the interpreter path via
|g:python3_host_prog| (or |g:python_host_prog|) so that the "pynvim" package
@@ -93,6 +99,7 @@ Ruby integration *provider-ruby*
Nvim supports Ruby |remote-plugin|s and the Vim legacy |ruby-vim| interface
(which is itself implemented as a Nvim remote-plugin).
+
RUBY QUICKSTART ~
To use Ruby plugins with Nvim, install the latest "neovim" RubyGem: >
@@ -100,6 +107,7 @@ To use Ruby plugins with Nvim, install the latest "neovim" RubyGem: >
Run |:checkhealth| to see if your system is up-to-date.
+
RUBY PROVIDER CONFIGURATION ~
*g:loaded_ruby_provider*
To disable Ruby support: >
@@ -122,6 +130,7 @@ Node.js integration *provider-nodejs*
Nvim supports Node.js |remote-plugin|s.
https://github.com/neovim/node-client/
+
NODEJS QUICKSTART~
To use javascript remote-plugins with Nvim, install the "neovim" npm package: >
@@ -129,6 +138,7 @@ To use javascript remote-plugins with Nvim, install the "neovim" npm package: >
Run |:checkhealth| to see if your system is up-to-date.
+
NODEJS PROVIDER CONFIGURATION~
*g:loaded_node_provider*
To disable Node.js support: >
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index 7aa81f612b..d20a91dc2d 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -1204,6 +1204,7 @@ you want to match case, add "\C" to the pattern |/\C|.
Basic items
%f file name (finds a string)
+ %o module name (finds a string)
%l line number (finds a number)
%c column number (finds a number representing character
column of the error, (1 <tab> == 1 character column))
@@ -1248,6 +1249,11 @@ conversion can be used to locate lines without a line number in the error
output. Like the output of the "grep" shell command.
When the pattern is present the line number will not be used.
+The "%o" conversion specifies the module name in quickfix entry. If present
+it will be used in quickfix error window instead of the filename. The module
+name is used only for displaying purposes, the file name is used when jumping
+to the file.
+
Changing directory
The following uppercase conversion characters specify the type of special
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index da064ab89b..d3d9303d3c 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -857,7 +857,7 @@ Short explanation of each option: *option-list*
'showtabline' 'stal' tells when the tab pages line is displayed
'sidescroll' 'ss' minimum number of columns to scroll horizontal
'sidescrolloff' 'siso' min. nr. of columns to left and right of cursor
-'signcolumn' 'scl' when to display the sign column
+'signcolumn' 'scl' when and how to display the sign column
'smartcase' 'scs' no ignore case when pattern has uppercase
'smartindent' 'si' smart autoindenting for C programs
'smarttab' 'sta' use 'shiftwidth' when inserting <Tab>
diff --git a/runtime/doc/remote_plugin.txt b/runtime/doc/remote_plugin.txt
index 6a9874660b..4cdcbed250 100644
--- a/runtime/doc/remote_plugin.txt
+++ b/runtime/doc/remote_plugin.txt
@@ -122,10 +122,10 @@ the example, say the Java plugin is a semantic completion engine for Java code.
If it defines the autocommand "BufEnter *.java", then the Java host is spawned
only when Nvim loads a buffer matching "*.java".
-If the explicit call to |:UpdateRemotePlugins| seems incovenient, try to see it
-like this: It's a way to provide IDE capabilities in Nvim while still keeping
-it fast and lightweight for general use. It's also analogous to the |:helptags|
-command.
+If the explicit call to |:UpdateRemotePlugins| seems inconvenient, try to see
+it like this: It's a way to provide IDE capabilities in Nvim while still
+keeping it fast and lightweight for general use. It's also analogous to the
+|:helptags| command.
*$NVIM_RPLUGIN_MANIFEST*
Unless $NVIM_RPLUGIN_MANIFEST is set the manifest will be written to a file
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index 23ae3458ea..0a552a1309 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -432,6 +432,16 @@ flag when defining the function, it is not relevant when executing it. >
.
:endfunction
:set cpo-=C
+<
+ *line-continuation-comment*
+To add a comment in between the lines start with '\" '. Notice the space
+after the double quote. Example: >
+ let array = [
+ "\ first entry comment
+ \ 'first',
+ "\ second entry comment
+ \ 'second',
+ \ ]
Rationale:
Most programs work with a trailing backslash to indicate line
@@ -440,6 +450,14 @@ Rationale:
:map xx asdf\
< Therefore the unusual leading backslash is used.
+ Starting a comment in a continuation line results in all following
+ continuation lines to be part of the comment. Since it was like this
+ for a long time, when making it possible to add a comment halfway a
+ sequence of continuation lines, it was not possible to use \", since
+ that was a valid continuation line. Using '"\ ' comes closest, even
+ though it may look a bit weird. Requiring the space after the
+ backslash is to make it very unlikely this is a normal comment line.
+
==============================================================================
5. Using Vim packages *packages*
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 88aa4121e4..2a230d9449 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -86,6 +86,11 @@ argument.
|:version| command.
See |info-message| about capturing the text.
+ *--clean*
+--clean Equivalent to "-u NONE -i NONE":
+ - Skips initializations from files and environment variables.
+ - No 'shada' file is read or written.
+
*--noplugin*
--noplugin Skip loading plugins. Resets the 'loadplugins' option.
Note that the |-u| argument may also disable loading plugins:
@@ -190,33 +195,33 @@ argument.
-E Start Nvim in Ex mode |gQ|.
If stdin is not a TTY:
- -e reads stdin as Ex commands.
+ -e reads/executes stdin as Ex commands.
-E reads stdin as text (into buffer 1).
- *-es* *-Es*
--es *-s-ex* *silent-mode*
--Es Silent or batch mode: execute Ex commands from a file instead
- of a terminal. Special case of |-s| (which takes an argument
- while "-es" doesn't). Disables most prompts, messages,
- warnings and errors.
- Output of these commands is displayed (to stdout):
+-es *-es* *-Es* *-s-ex* *silent-mode*
+-Es Silent or batch mode. Special case of |-s| (which takes an
+ argument while "-es" doesn't). Disables most prompts,
+ messages, warnings and errors.
+
+ -es reads/executes stdin as Ex commands. >
+ printf "put ='foo'\n%%print\n" | nvim -es
+
+< -Es reads stdin as text (into buffer 1). Use |-c| or "+" to
+ send commands. >
+ printf "foo\n" | nvim -Es +"%print"
+
+< Output of these commands is displayed (to stdout):
:print
:list
:number
- :set to display option values.
- When 'verbose' is set messages are printed to stderr, e.g.: >
+ :set (to display option values)
+ When 'verbose' is set messages are printed to stderr. >
echo foo | nvim -V1 -es
-<
- User |init.vim| is skipped (unless given with |-u|).
- |$TERM| is not used.
- If stdin is not a TTY:
- -es reads stdin as Ex commands.
- -Es reads stdin as text (into buffer 1).
+< User |init.vim| is skipped (unless given with |-u|).
+ Swap file is skipped (like |-n|).
+ User |shada| is loaded (unless "-i NONE" is given).
- Example: >
- printf "put ='foo'\n%%print\n" | nvim -es
-<
*-b*
-b Binary mode. File I/O will only recognize <NL> to separate
lines. The 'expandtab' option will be reset. The 'textwidth'
@@ -255,7 +260,7 @@ argument.
{not available when compiled without the |+eval| feature}
*-n*
--n No swap file will be used. Recovery after a crash will be
+-n No |swap-file| will be used. Recovery after a crash will be
impossible. Handy if you want to view or edit a file on a
very slow medium (e.g., a floppy).
Can also be done with ":set updatecount=0". You can switch it
@@ -294,19 +299,21 @@ argument.
*-u* *E282*
-u {vimrc} The file {vimrc} is read for initializations. Most other
- initializations are skipped; see |initialization|. This can
- be used to start Vim in a special mode, with special
+ initializations are skipped; see |initialization|.
+
+ This can be used to start Vim in a special mode, with special
mappings and settings. A shell alias can be used to make
this easy to use. For example: >
alias vimc vim -u ~/.config/nvim/c_init.vim !*
< Also consider using autocommands; see |autocommand|.
- When {vimrc} is equal to "NONE" (all uppercase), all
- initializations from files and environment variables are
- skipped, including reading the |ginit.vim| file when the GUI
- starts. Plugins and syntax highlighting are also skipped.
- When {vimrc} is equal to "NORC" (all uppercase), this has the
- same effect as "NONE", but plugins and syntax highlighting are
- not skipped.
+
+ When {vimrc} is "NONE" (all uppercase), all initializations
+ from files and environment variables are skipped. Plugins and
+ syntax highlighting are also skipped.
+
+ When {vimrc} is "NORC" (all uppercase), this has the same
+ effect as "NONE", but plugins and syntax highlighting are not
+ skipped.
*-i*
-i {shada} The file {shada} is used instead of the default ShaDa
@@ -347,35 +354,33 @@ argument.
--embed Use stdin/stdout as a msgpack-RPC channel, so applications can
embed and control Nvim via the |rpc-api|.
- By default nvim will wait for the embedding process to call
- `nvim_ui_attach` before sourcing startup files and reading
- buffers. This is so that UI can show startup messages and
- possible swap file dialog for the first loaded file. The
- process can do requests before the `nvim_ui_attach`, for
- instance a `nvim_get_api_info` call so that UI features can be
- safely detected by the UI before attaching.
-
- See |ui-startup| for more information about UI startup.
-
- To embed nvim without using the UI protocol, `--headless` should
- be supplied together with `--embed`. Then initialization is
- performed without waiting for an UI. This is also equivalent
- to the following alternative: >
- nvim --headless --cmd "call stdioopen({'rpc': v:true})"
-<
- See also |channel-stdio|.
+ Waits for the client ("embedder") to call |nvim_ui_attach()|
+ before sourcing startup files and reading buffers, so that UIs
+ can deterministically handle (display) early messages,
+ dialogs, etc. The client can do other requests before
+ `nvim_ui_attach` (e.g. `nvim_get_api_info` for feature-detection).
+ During this pre-startup phase the user config is of course not
+ available (similar to `--cmd`).
+
+ Embedders _not_ using the UI protocol must pass |--headless|: >
+ nvim --embed --headless
+
+< Then startup will continue without waiting for `nvim_ui_attach`.
+ This is equivalent to: >
+ nvim --headless --cmd "call stdioopen({'rpc': v:true})"
+
+< See also: |ui-startup| |channel-stdio|
*--headless*
---headless Start nvim without an UI. The TUI is not used, so stdio
- can be used as an arbitrary communication channel.
- |channel-stdio| When used together with `--embed`, do not wait
- for the embedder to attach an UI.
+--headless Start without UI, and do not wait for `nvim_ui_attach`. The
+ builtin TUI is not used, so stdio works as an arbitrary
+ communication channel. |channel-stdio|
Also useful for scripting (tests) to see messages that would
not be printed by |-es|.
To detect if a UI is available, check if |nvim_list_uis()| is
- empty in or after |VimEnter|.
+ empty during or after |VimEnter|.
To read stdin as text, "-" must be given explicitly:
--headless cannot assume that stdin is just text. >
@@ -481,6 +486,7 @@ accordingly. Vim proceeds in this order:
Loading plugins won't be done when:
- The 'loadplugins' option was reset in a vimrc file.
- The |--noplugin| command line argument is used.
+ - The |--clean| command line argument is used.
- The "-u NONE" command line argument is used |-u|.
- When Vim was compiled without the |+eval| feature.
Note that using "-c 'set noloadplugins'" doesn't work, because the
@@ -1075,15 +1081,16 @@ even if other entries (with known name/type/etc) are merged. |shada-merging|
SHADA FILE NAME *shada-file-name*
-- The default name of the ShaDa file is "$XDG_DATA_HOME/nvim/shada/main.shada"
- for Unix. Default for $XDG_DATA_HOME is ~/.local/share. |base-directories|
-- The 'n' flag in the 'shada' option can be used to specify another ShaDa
- file name |'shada'|.
-- The "-i" Vim argument can be used to set another file name, |-i|. When the
- file name given is "NONE" (all uppercase), no ShaDa file is ever read or
- written. Also not for the commands below!
-- The 'viminfofile' option can be used like the "-i" argument. In fact, the
- value form the "-i" argument is stored in the 'viminfofile' option.
+- Default name of the |shada| file is:
+ Unix: "$XDG_DATA_HOME/nvim/shada/main.shada"
+ Windows: "$XDG_DATA_HOME/nvim-data/shada/main.shada"
+ See also |base-directories|.
+- To choose a different file name you can use:
+ - The "n" flag in the 'shada' option.
+ - The |-i| startup argument. "NONE" means no shada file is ever read or
+ written. Also not for the commands below!
+ - The 'shadafile' option. The value from the "-i" argument (if any) is
+ stored in the 'shadafile' option.
- For the commands below, another file name can be given, overriding the
default and the name given with 'shada' or "-i" (unless it's NONE).
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 684830b78d..b60a952def 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -4953,6 +4953,8 @@ NonText '@' at the end of the window, characters from 'showbreak'
fit at the end of the line). See also |hl-EndOfBuffer|.
*hl-Normal*
Normal normal text
+ *hl-NormalFloat*
+NormalFloat Normal text in floating windows.
*hl-NormalNC*
NormalNC normal text in non-current windows
*hl-Pmenu*
diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt
index 367da7750e..a4526a7f2c 100644
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -173,6 +173,9 @@ commands explained above the tag stack will look like this:
1 1 main 1 harddisk2:text/vim/test
2 1 FuncB 59 harddisk2:text/vim/src/main.c
+The gettagstack() function returns the tag stack of a specified window. The
+settagstack() function modifies the tag stack of a window.
+
*E73*
When you try to use the tag stack while it doesn't contain anything you will
get an error message.
@@ -498,28 +501,21 @@ gnatxref For Ada. See http://www.gnuada.org/. gnatxref is
part of the gnat package.
-The lines in the tags file must have one of these three formats:
+The lines in the tags file must have one of these two formats:
1. {tagname} {TAB} {tagfile} {TAB} {tagaddress}
-2. {tagfile}:{tagname} {TAB} {tagfile} {TAB} {tagaddress}
-3. {tagname} {TAB} {tagfile} {TAB} {tagaddress} {term} {field} ..
+2. {tagname} {TAB} {tagfile} {TAB} {tagaddress} {term} {field} ..
+
+Previously an old format was supported, see |tag-old-static|.
-The first is a normal tag, which is completely compatible with Vi. It is the
-only format produced by traditional ctags implementations. This is often used
-for functions that are global, also referenced in other files.
+The first format is a normal tag, which is completely compatible with Vi. It
+is the only format produced by traditional ctags implementations. This is
+often used for functions that are global, also referenced in other files.
The lines in the tags file can end in <LF> or <CR><LF>. On the Macintosh <CR>
also works. The <CR> and <NL> characters can never appear inside a line.
- *tag-old-static*
-The second format is for a static tag only. It is obsolete now, replaced by
-the third format. It is only supported by Elvis 1.x and Vim and a few
-versions of ctags. A static tag is often used for functions that are local,
-only referenced in the file {tagfile}. Note that for the static tag, the two
-occurrences of {tagfile} must be exactly the same. Also see |tags-option|
-below, for how static tags are used.
-
-The third format is new. It includes additional information in optional
+The second format is new. It includes additional information in optional
fields at the end of each line. It is backwards compatible with Vi. It is
only supported by new versions of ctags (such as Exuberant ctags).
@@ -561,6 +557,7 @@ only supported by new versions of ctags (such as Exuberant ctags).
The only other field currently recognized by Vim is "file:"
(with an empty value). It is used for a static tag.
+
The first lines in the tags file can contain lines that start with
!_TAG_
These are sorted to the first lines, only rare tags that start with "!" can
@@ -613,6 +610,21 @@ If the command is a normal search command (it starts and ends with "/" or
followed by white space and a '('. This will find macro names and function
names with a type prepended.
+
+ *tag-old-static*
+Until March 2019 (patch 8.1.1092) an outdated format was supported:
+ {tagfile}:{tagname} {TAB} {tagfile} {TAB} {tagaddress}
+
+This format is for a static tag only. It is obsolete now, replaced by
+the second format. It is only supported by Elvis 1.x, older Vim versions and
+a few versions of ctags. A static tag is often used for functions that are
+local, only referenced in the file {tagfile}. Note that for the static tag,
+the two occurrences of {tagfile} must be exactly the same. Also see
+|tags-option| below, for how static tags are used.
+
+The support was removed, since when you can update to the new Vim version you
+should also be able to update ctags to one that supports the second format.
+
==============================================================================
6. Include file searches *include-search* *definition-search*
*E387* *E388* *E389*
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 270c4fb556..60d55bda61 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -11,70 +11,98 @@ Nvim UI protocol *ui*
==============================================================================
UI Events *ui-events*
-GUIs can be implemented as external processes communicating with Nvim over the
-RPC API. The default UI model consists of a terminal-like grid with a single,
-monospace font size. The UI can opt-in to have windows drawn on separate
-grids, as well as to have some elements (UI "widgets") be drawn by the UI
-itself rather than by nvim ("externalized").
-
-
- *ui-options*
-The |nvim_ui_attach()| API method is used to tell Nvim that your program wants to
-draw the Nvim screen grid with a size of width × height cells. This is typically
-done by an embedder, see |ui-startup| below for details, but an UI can also
-connect to a running nvim instance and invoke this method. `options` must be
-a dictionary with these (optional) keys:
- `rgb` Decides the color format. *ui-rgb*
- Set true (default) for 24-bit RGB colors.
- Set false for terminal colors (max of 256).
- *ui-ext-options*
- `ext_popupmenu` Externalize the popupmenu. |ui-popupmenu|
- `ext_tabline` Externalize the tabline. |ui-tabline|
+UIs can be implemented as external client processes communicating with Nvim
+over the RPC API. The default UI model is a terminal-like grid with a single,
+monospace font. The UI can opt-in to have windows drawn on separate grids, and
+have some elements ("widgets") presented by the UI itself rather than by Nvim
+("externalized").
+
+ *ui-option*
+Call |nvim_ui_attach()| to tell Nvim that your program wants to draw the Nvim
+screen grid with a size of width × height cells. This is typically done by an
+embedder at startup (see |ui-startup|), but UIs can also connect to a running
+Nvim instance and invoke nvim_ui_attach(). The `options` parameter is a map
+with these (optional) keys:
+ *ui-rgb*
+ `rgb` Decides the color format.
+ true: (default) 24-bit RGB colors
+ false: Terminal colors (8-bit, max 256)
+ *ui-override*
+ `override` Decides how UI capabilities are resolved.
+ true: Enable requested UI capabilities, even
+ if not supported by all connected UIs
+ (including |TUI|).
+ false: (default) Disable UI capabilities not
+ supported by all connected UIs
+ (including TUI).
+ *ui-ext-options*
`ext_cmdline` Externalize the cmdline. |ui-cmdline|
- `ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
+ `ext_hlstate` Detailed highlight state. |ui-hlstate|
+ Sets `ext_linegrid` implicitly.
+ `ext_linegrid` Line-based grid events. |ui-linegrid|
+ Deactivates |ui-grid-old| implicitly.
`ext_messages` Externalize messages. |ui-messages|
- `ext_linegrid` Use new revision of the grid events. |ui-linegrid|
- `ext_multigrid` Use per-window grid based events. |ui-multigrid|
- `ext_hlstate` Use detailed highlight state. |ui-hlstate|
+ Sets `ext_linegrid` and `ext_cmdline` implicitly.
+ `ext_multigrid` Per-window grid events. |ui-multigrid|
+ Sets `ext_linegrid` implicitly.
+ `ext_popupmenu` Externalize |popupmenu-completion| and
+ 'wildmenu'. |ui-popupmenu|
+ `ext_tabline` Externalize the tabline. |ui-tabline|
`ext_termcolors` Use external default colors.
-Specifying a non-existent option is an error. UIs can check the |api-metadata|
-`ui_options` key for supported options. Additionally Nvim (currently) requires
-that all connected UIs use the same set of widgets. Therefore the active
-widgets will be the intersection of the requested widget sets of all connected
-UIs. The "option_set" event announces which widgets actually are active.
-
-Nvim sends msgpack-rpc notifications to all attached UIs, with method name
-"redraw" and a single argument: an array (batch) of screen "update events".
-Each update event is itself an array whose first element is the event name and
-remaining elements are event-parameter tuples. This allows multiple events of
-the same kind to be sent in a row without the event name being repeated. This
-batching is mostly used for "grid_line", because each "grid_line" event puts
-contents in one grid line, but clients must be prepared for multiple argument
-sets being batched for all event kinds.
-
-Events must be handled in-order. A "flush" event is sent when nvim is done
-redrawing the entire screen (so that all windows have a consistent view of
-buffer state, options etc). Clients should be prepared that several "redraw"
-batches are sent before the entire screen has been redrawn, and only the last
-batch will end in "flush". The user should only see the final state when
-"flush" is sent, and not any intermediate state after processing only part of
-the batch array, nor after a batch not ending with "flush".
-
-By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to
-implement a terminal-like interface. However there are two revisions of the
-grid part of the protocol. The newer revision |ui-linegrid|, enabled by
-`ext_linegrid` option, has a more effecient representation of text (especially
-highlighted text), and allows extensions that use multiple grids.
-
-The older revision is available and used by default only for backwards
-compatibility reasons. New UIs are strongly recommended to use |ui-linegrid|,
-as further protocol extensions require it. The |ui-multigrid| extension
-enables |ui-linegrid| implicitly.
-
-Nvim optionally sends screen elements "semantically" as structured events
-instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present
-those elements itself; Nvim will not draw those elements on the grid.
+Specifying an unknown option is an error; UIs can check the |api-metadata|
+`ui_options` key for supported options.
+
+By default Nvim requires all connected UIs to support the same capabilities,
+thus the active capabilities are the intersection of those requested. UIs may
+specify |ui-override| to invert this behavior (useful for debugging). The
+"option_set" event announces which capabilities are active.
+
+Nvim sends RPC notifications to all attached UIs, with method name "redraw"
+and a single argument: an array (batch) of screen "update events". Each update
+event is itself an array whose first element is the event name and remaining
+elements are event-parameter tuples. Thus multiple events of the same kind can
+be sent contiguously without repeating the event name.
+
+Example of a typical "redraw" batch in a single RPC notification: >
+
+ ['notification', 'redraw',
+ [
+ ['grid_resize', [2, 77, 36]],
+ ['grid_line',
+ [2, 0, 0, [[' ' , 0, 77]]],
+ [2, 1, 0, [['~', 7], [' ', 7, 76]]],
+ [2, 9, 0, [['~', 7], [' ', 7, 76]]],
+ ...
+ [2, 35, 0, [['~', 7], [' ', 7, 76]]],
+ [1, 36, 0, [['[', 9], ['N'], ['o'], [' '], ['N'], ['a'], ['m'], ['e'], [']']]],
+ [1, 36, 9, [[' ', 9, 50]]],
+ [1, 36, 59, [['0', 9], [','], ['0'], ['-' ], ['1'], [' ', 9, 10], ['A'], ['l', 9, 2]]]
+ ],
+ ['msg_showmode', [[]]],
+ ['win_pos', [2, 1000, 0, 0, 77, 36]],
+ ['grid_cursor_goto', [2, 0, 0]],
+ ['flush', []]
+ ]
+ ]
+
+Events must be handled in-order. Nvim sends a "flush" event when it has
+completed a redraw of the entire screen (so all windows have a consistent view
+of buffer state, options, etc.). Multiple "redraw" batches may be sent before
+the entire screen has been redrawn, with "flush" following only the last
+batch. The user should only see the final state (when "flush" is sent), not
+any intermediate state while processing part of the batch array, nor after
+a batch not ending with "flush".
+
+By default, Nvim sends |ui-global| and |ui-grid-old| events (for backwards
+compatibility); these suffice to implement a terminal-like interface. However
+the new |ui-linegrid| represents text more efficiently (especially highlighted
+text), and allows UI capabilities requiring multiple grids. New UIs should
+implement |ui-linegrid| instead of |ui-grid-old|.
+
+Nvim optionally sends various screen elements "semantically" as structured
+events instead of raw grid-lines, as specified by |ui-ext-options|. The UI
+must present such elements itself, Nvim will not draw them on the grid.
Future versions of Nvim may add new update kinds and may append new parameters
to existing update kinds. Clients must be prepared to ignore such extensions,
@@ -83,40 +111,36 @@ for forward-compatibility. |api-contract|
==============================================================================
UI startup *ui-startup*
-Nvim defines a standard procedure for how an embedding UI should interact with
-the startup phase of Nvim. When spawning the nvim process, use the |--embed| flag
-but not the |--headless| flag. The started Nvim process will pause before loading
-startup files and reading buffers, and give the UI a chance to invoke requests
-to do early initialization. As soon as the UI invokes |nvim_ui_attach()|, the
-startup will continue.
+UI embedders (clients that start Nvim with |--embed| and later call
+|nvim_ui_attach()|) must start Nvim without |--headless|: >
+ nvim --embed
+Nvim will pause before loading startup files and reading buffers, so the UI
+has a chance to invoke requests and do early initialization. Startup will
+continue as soon as the UI invokes |nvim_ui_attach()|.
-A simple UI only need to do a single |nvim_ui_attach()| request and then
-be prepared to handle any UI event. A more featureful UI, which might need
-additional configuration of the nvim process, should use the following startup
+A simple UI only needs to do a single |nvim_ui_attach()| request and then
+prepare to handle any UI event. A more featureful UI, which might need
+additional configuration of the Nvim process, should use the following startup
procedure:
-1. Invoke |nvim_get_api_info()|, if this is needed to setup the client library
- and/or to get the list of supported UI extensions.
-2. At this time, any configuration that should be happen before init.vim
- loading should be done. Buffers and windows are not available at this
- point, but this could be used to set |g:| variables visible to init.vim
-3. If the UI wants to do additional setup after the init.vim file was loaded
- register an autocmd for VimEnter at this point: >
-
- nvim_command("autocmd VimEnter * call rpcrequest(1, 'vimenter')")
-
-<4. Now invoke |nvim_ui_attach()|. The UI will need to handle keyboard input
- at this point, as sourcing init.vim and loading buffers might lead to
- blocking prompts.
-5. If step 3 was used, nvim will send a blocking "vimenter" request to the
- UI. Inside this request handler, the UI can safely do any initialization
- before entering normal mode, for instance reading variables set by
- init.vim.
+1. Invoke |nvim_get_api_info()|, if needed to setup the client library and/or
+ to get the list of supported UI extensions.
+2. Do any configuration that should be happen before user config is loaded.
+ Buffers and windows are not available at this point, but this could be used
+ to set |g:| variables visible to init.vim
+3. If the UI wants to do additional setup after user config is loaded,
+ register a VimEnter autocmd: >
+ nvim_command("autocmd VimEnter * call rpcrequest(1, 'vimenter')")
+<4. Now invoke |nvim_ui_attach()|. The UI must handle user input by now:
+ sourcing init.vim and loading buffers might lead to blocking prompts.
+5. If step 3 was used, Nvim will send a blocking "vimenter" request to the UI.
+ Inside this request handler, the UI can safely do any initialization before
+ entering normal mode, for example reading variables set by init.vim.
==============================================================================
Global Events *ui-global*
-The following events will always be available, and describe global state of
+The following UI events are always emitted, and describe global state of
the editor.
["set_title", title]
@@ -189,7 +213,7 @@ the editor.
["mouse_off"]
Tells the client whether mouse support, as determined by |'mouse'|
option, is considered to be active in the current mode. This is mostly
- useful for a terminal frontend, or other situations where nvim mouse
+ useful for a terminal frontend, or other situations where Nvim mouse
would conflict with other usages of the mouse. It is safe for a client
to ignore this and always send mouse events.
@@ -218,20 +242,20 @@ the editor.
==============================================================================
Grid Events (line-based) *ui-linegrid*
-These events are used if `ext_linegrid` option is set (recommended for all new
-UIs). The biggest change compared to previous revision is to use a single
-event `grid_line` to update the contents of a screen line (where the old
-protocol used a combination of cursor, highlight and text events)
+Activated by the `ext_linegrid` |ui-option|. Recommended for all new UIs.
+Deactivates |ui-grid-old| implicitly.
+
+The biggest change compared to |ui-grid-old| is to use a single `grid_line`
+event to update the contents of a screen line (whereas the old protocol used
+a combination of cursor, highlight and text events)
Most of these events take a `grid` index as first parameter. Grid 1 is the
-global grid used by default for the entire editor screen state. Grids other
-than that will be defined by future extensions. Just activating the
-`ext_linegrid` option by itself will never cause any additional grids to be
-created. To enable per-window grids, `ext_multigrid` option should be set (see
-|ui-multigrid|).
+global grid used by default for the entire editor screen state. The
+`ext_linegrid` capability by itself will never cause any additional grids to
+be created; to enable per-window grids, activate |ui-multigrid|.
Highlight attribute groups are predefined. UIs should maintain a table to map
-numerical highlight `id`:s to the actual attributes.
+numerical highlight ids to the actual attributes.
["grid_resize", grid, width, height]
Resize a `grid`. If `grid` wasn't seen by the client before, a new grid is
@@ -242,16 +266,16 @@ numerical highlight `id`:s to the actual attributes.
special colors respectively. `cterm_fg` and `cterm_bg` specifies the
default color codes to use in a 256-color terminal.
- The rgb values will always be valid colors, by default. If no
+ The RGB values will always be valid colors, by default. If no
colors have been set, they will default to black and white, depending
on 'background'. By setting the `ext_termcolors` option, instead
- -1 will be used for unset colors. This is mostly useful for a
- TUI implementation, where using the terminal emulators builitn
- defaults are expected.
+ -1 will be used for unset colors. This is mostly useful for a TUI
+ implementation, where using the terminal builtin ("ANSI") defaults
+ are expected.
- Note: unlike the corresponding events in the first revision, the
- screen is not always cleared after sending this event. The GUI has to
- repaint the screen with changed background color itself.
+ Note: Unlike the corresponding |ui-grid-old| events, the screen is not
+ always cleared after sending this event. The UI must repaint the
+ screen with changed background color itself.
*ui-event-hl_attr_define*
["hl_attr_define", id, rgb_attr, cterm_attr, info]
@@ -275,7 +299,7 @@ numerical highlight `id`:s to the actual attributes.
All boolean keys default to false, and will only be sent when they
are true.
- Highlights are always transmitted both for both the rgb format and as
+ Highlights are always transmitted both for both the RGB format and as
terminal 256-color codes, as the `rgb_attr` and `cterm_attr` parameters
respectively. The |ui-rgb| option has no effect effect anymore.
Most external UIs will only need to store and use the `rgb_attr`
@@ -284,17 +308,17 @@ numerical highlight `id`:s to the actual attributes.
`id` 0 will always be used for the default highlight with colors defined
by `default_colors_set` and no styles applied.
- Note: `id`:s can be reused if Nvim's internal highlight table is full.
- In this case, Nvim will always issue redraws of screen cells that are
- affected by redefined `id`:s, so UIs do not need to keep track of this
+ Note: Nvim may reuse `id` values if its internal highlight table is full.
+ In that case Nvim will always issue redraws of screen cells that are
+ affected by redefined ids, so UIs do not need to keep track of this
themselves.
- `info` is an empty array per default, and will be used by the
- |ui-hlstate| extension explaned below.
+ `info` is an empty array by default, and will be used by the
+ |ui-hlstate| extension explained below.
*ui-event-grid_line*
["grid_line", grid, row, col_start, cells]
- Redraw a continous part of a `row` on a `grid`, starting at the column
+ Redraw a continuous part of a `row` on a `grid`, starting at the column
`col_start`. `cells` is an array of arrays each with 1 to 3 items:
`[text(, hl_id, repeat)]` . `text` is the UTF-8 text that should be put in
a cell, with the highlight `hl_id` defined by a previous `hl_attr_define`
@@ -325,9 +349,13 @@ numerical highlight `id`:s to the actual attributes.
indicates the visible cursor position.
["grid_scroll", grid, top, bot, left, right, rows, cols]
- Scroll the text in the a region of `grid`. The diagrams below illustrate
- what will happen, depending on the scroll direction. "=" is used to
- represent the SR(scroll region) boundaries and "-" the moved rectangles.
+ Scroll a region of `grid`. This is semantically unrelated to editor
+ |scrolling|, rather this is an optimized way to say "copy these screen
+ cells".
+
+ The following diagrams show what happens per scroll direction.
+ "===" represents the SR (scroll region) boundaries.
+ "---" represents the moved rectangles.
Note that dst and src share a common region.
If `rows` is bigger than 0, move a rectangle in the SR up, this can
@@ -368,10 +396,10 @@ numerical highlight `id`:s to the actual attributes.
part of handling the scroll event.
==============================================================================
-Legacy Grid Events (cell based) *ui-grid-old*
+Grid Events (cell-based) *ui-grid-old*
-This is an older representation of the screen grid, used if `ext_linegrid`
-option is not set. New UIs should use |ui-linegrid|.
+This is the legacy representation of the screen grid, emitted if |ui-linegrid|
+is not active. New UIs should implement |ui-linegrid| instead.
["resize", width, height]
The grid is resized to `width` and `height` cells.
@@ -403,7 +431,7 @@ option is not set. New UIs should use |ui-linegrid|.
updates. All boolean keys default to false.
`foreground`: foreground color.
- `background`: backround color.
+ `background`: background color.
`special`: color to use for underline and undercurl, when present.
`reverse`: reverse video. Foreground and background colors are
switched.
@@ -458,13 +486,13 @@ option is not set. New UIs should use |ui-linegrid|.
==============================================================================
Detailed highlight state Extension *ui-hlstate*
-Only sent if `ext_hlstate` option is set in |ui-options|. `ext_hlstate` implies
-`ext_linegrid`.
+Activated by the `ext_hlstate` |ui-option|.
+Activates |ui-linegrid| implicitly.
-By default, nvim will only describe grid cells using the final calculated
-higlight attributes, as described by the dict keys in |ui-event-highlight_set|.
+By default Nvim will only describe grid cells using the final calculated
+highlight attributes, as described by the dict keys in |ui-event-highlight_set|.
The `ext_hlstate` extension allows to the UI to also receive a semantic
-describtion of the higlights active in a cell. In this mode highlights will be
+description of the highlights active in a cell. In this mode highlights will be
predefined in a table, see |ui-event-hl_attr_define| and |ui-event-grid_line|.
The `info` parameter in `hl_attr_define` will contain a semantic description
of the highlights. As highlight groups can be combined, this will be an array
@@ -472,14 +500,13 @@ of items, with the item with highest priority last. Each item is a dictionary
with the following possible keys:
`kind`: always present. One of the following values:
- "ui": A builtin ui highlight.
- "syntax": highlight applied to a buffer by a syntax declaration or
- other runtime/plugin functionallity such as
+ "ui": Builtin UI highlight. |highlight-groups|
+ "syntax": Highlight applied to a buffer by a syntax declaration or
+ other runtime/plugin functionality such as
|nvim_buf_add_highlight()|
"terminal": highlight from a process running in a |terminal-emulator|.
- Contains no futher semantic information.
- `ui_name`: Name of the builtin highlight. See |highlight-groups| for
- possible values. Only present for "ui".
+ Contains no further semantic information.
+ `ui_name`: Highlight name from |highlight-groups|. Only for "ui" kind.
`hi_name`: Name of the final |:highlight| group where the used
attributes are defined.
`id`: Unique numeric id representing this item.
@@ -493,30 +520,32 @@ screen elements, even if no attributes have been applied.
==============================================================================
Multigrid Events *ui-multigrid*
-Only sent if `ext_multigrid` option is set in |ui-options|. Enables the
-`ext_linegrid` extension implicitly.
+Activated by the `ext_multigrid` |ui-option|.
+Activates |ui-linegrid| implicitly.
-The multigrid extension gives the UIs more control over how windows are
-displayed. The UIs receive updates on a separate grid for each window. The UIs
-can set the grid size independently of how much space the window occupies on
-the global layout. This enables the UIs to set a different font size for each
-window if the UI so desires. The UI can also reserve space around the border
-of the window for its own elements, for instance scrollbars from the UI
-toolkit.
-
-By default, the grid size is handled by nvim and set to the outer grid size
-(i.e. the size of the window frame in nvim) whenever the split is created.
-Once a UI sets a grid size, nvim does not handle the size for that grid and
+See |ui-linegrid| for grid events.
+See |nvim_ui_try_resize_grid()| to request changing the grid size.
+See |nvim_input_mouse()| for sending mouse events to Nvim.
+
+The multigrid extension gives UIs more control over how windows are displayed:
+- UIs receive updates on a separate grid for each window.
+- UIs can set the grid size independently of how much space the window
+ occupies on the global layout. So the UI could use a different font size
+ per-window. Or reserve space around the border of the window for its own
+ elements, such as scrollbars from the UI toolkit.
+
+By default, the grid size is handled by Nvim and set to the outer grid size
+(i.e. the size of the window frame in Nvim) whenever the split is created.
+Once a UI sets a grid size, Nvim does not handle the size for that grid and
the UI must change the grid size whenever the outer size is changed. To
-delegate the handling of grid size back to nvim, the UIs should request the
-size (0, 0).
+delegate grid-size handling back to Nvim, request the size (0, 0).
A window can be hidden and redisplayed without its grid being deallocated.
This can happen multiple times for the same window, for instance when switching
tabs.
["win_pos", grid, win, start_row, start_col, width, height]
- Set the position and size of the grid in nvim (i.e. the outer grid
+ Set the position and size of the grid in Nvim (i.e. the outer grid
size). If the window was previously hidden, it should now be shown
again.
@@ -528,7 +557,7 @@ tabs.
["win_external_pos", grid, win]
Display or reconfigure external window `win`. The window should be
- displayed as a separate top-level window in the desktop envirionment,
+ displayed as a separate top-level window in the desktop environment,
or something similar.
["win_hide", grid]
@@ -547,14 +576,13 @@ tabs.
["win_close", grid]
Close the window.
-See |ui-linegrid| for grid events.
-See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size.
-See |nvim_input_mouse| for sending mouse events to Nvim.
-
==============================================================================
Popupmenu Events *ui-popupmenu*
-Only sent if `ext_popupmenu` option is set in |ui-options|
+Activated by the `ext_popupmenu` |ui-option|.
+
+This UI extension delegates presentation of the |popupmenu-completion| and
+command-line 'wildmenu'.
["popupmenu_show", items, selected, row, col, grid]
Show |popupmenu-completion|. `items` is an array of completion items
@@ -564,7 +592,9 @@ Only sent if `ext_popupmenu` option is set in |ui-options|
index into the array of items (-1 if no item is selected). `row` and
`col` give the anchor position, where the first character of the
completed word will be. When |ui-multigrid| is used, `grid` is the
- grid for the anchor position.
+ grid for the anchor position. When `ext_cmdline` is active, `grid` is
+ set to -1 to indicate the popupmenu should be anchored to the external
+ cmdline. Then `col` will be a byte position in the cmdline text.
["popupmenu_select", selected]
Select an item in the current popupmenu. `selected` is a zero-based
@@ -577,7 +607,7 @@ Only sent if `ext_popupmenu` option is set in |ui-options|
==============================================================================
Tabline Events *ui-tabline*
-Only sent if `ext_tabline` option is set in |ui-options|
+Activated by the `ext_tabline` |ui-option|.
["tabline_update", curtab, tabs]
Tabline was updated. UIs should present this data in a custom tabline
@@ -588,7 +618,10 @@ Only sent if `ext_tabline` option is set in |ui-options|
==============================================================================
Cmdline Events *ui-cmdline*
-Only sent if `ext_cmdline` option is set in |ui-options|.
+Activated by the `ext_cmdline` |ui-option|.
+
+This UI extension delegates presentation of the |cmdline| (except 'wildmenu').
+For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
["cmdline_show", content, pos, firstc, prompt, indent, level]
content: List of [attrs, string]
@@ -644,64 +677,49 @@ Only sent if `ext_cmdline` option is set in |ui-options|.
Hide the block.
==============================================================================
-Wildmenu Events *ui-wildmenu*
-
-Only sent if `ext_wildmenu` option is set in |ui-options|
-
-["wildmenu_show", items]
- Activate the wildmenu (command-line completion). `items` is an array
- with the completion items.
-
-["wildmenu_select", selected]
- Select an item in the current wildmenu. `selected` is a zero-based
- index into the array of items from the last wildmenu_show event, or -1
- if no item is selected.
-
-["wildmenu_hide"]
- Hide the wildmenu.
-
-==============================================================================
-Message Events *ui-messages*
+Message/Dialog Events *ui-messages*
-Only sent if `ext_messages` option is set in |ui-options|. This option implies
-`ext_linegrid` and `ext_cmdline` also being set. |ui-linegrid| and |ui-cmdline| events
-will thus also be sent.
+Activated by the `ext_messages` |ui-option|.
+Activates |ui-linegrid| and |ui-cmdline| implicitly.
-This extension allows the UI to control the display of messages that otherwise
-would have been displayed in the message/cmdline area in the bottom of the
-screen.
+This UI extension delegates presentation of messages and dialogs. Messages
+that would otherwise render in the message/cmdline screen space, are emitted
+as UI events.
-Activating this extension means that Nvim will allocate no screen space for
-the cmdline or messages, and 'cmdheight' will be set to zero. Attempting to
-change 'cmdheight' will silently be ignored. |ui-cmdline| events will be used
-to represent the state of the cmdline.
+Nvim will not allocate screen space for the cmdline or messages, and
+'cmdheight' will be forced zero. Cmdline state is emitted as |ui-cmdline|
+events, which the UI must handle.
["msg_show", kind, content, replace_last]
Display a message to the user.
- `kind` will be one of the following
- `emsg`: Internal error message
- `echo`: temporary message from plugin (|:echo|)
- `echomsg`: ordinary message from plugin (|:echomsg|)
- `echoerr`: error message from plugin (|:echoerr|)
- `return_prompt`: |press-enter| prompt after a group of messages
- `quickfix`: Quickfix navigation message
- `kind` can also be the empty string. The message is then some internal
- informative or warning message, that hasn't yet been assigned a kind.
- New message kinds can be added in later versions; clients should
- handle messages of an unknown kind just like empty kind.
-
- `content` will be an array of `[attr_id, text_chunk]` tuples,
- building up the message text of chunks of different highlights.
- No extra spacing should be added between chunks, the `text_chunk` by
- itself should contain any necessary whitespace. Messages can contain
- line breaks `"\n"`.
-
- `replace_last` controls how multiple messages should be displayed.
- If `replace_last` is false, this message should be displayed together
- with all previous messages that are still visible. If `replace_last`
- is true, this message should replace the message in the most recent
- `msg_show` call, but any other visible message should still remain.
+ kind
+ Name indicating the message kind:
+ "" (empty) Unknown, report a |feature-request|
+ "confirm" |confirm()| or |:confirm| dialog
+ "confirm_sub" |:substitute| confirm dialog |:s_c|
+ "emsg" Error (|errors|, internal error, |:throw|, …)
+ "echo" |:echo| message
+ "echomsg" |:echomsg| message
+ "echoerr" |:echoerr| message
+ "return_prompt" |press-enter| prompt after a multiple messages
+ "quickfix" Quickfix navigation message
+ "wmsg" Warning ("search hit BOTTOM", |W10|, …)
+ New kinds may be added in the future; clients should treat unknown
+ kinds as the empty kind.
+
+ content
+ Array of `[attr_id, text_chunk]` tuples, building up the message
+ text of chunks of different highlights. No extra spacing should be
+ added between chunks, the `text_chunk` by itself contains any
+ necessary whitespace. Messages can contain line breaks "\n".
+
+ replace_last
+ Decides how multiple messages should be displayed:
+ false: Display the message together with all previous messages
+ that are still visible.
+ true: Replace the message in the most-recent `msg_show` call,
+ but any other visible message should still remain.
["msg_clear"]
Clear all messages currently displayed by "msg_show". (Messages sent
diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt
index e5591ac1d1..42b564e962 100644
--- a/runtime/doc/usr_11.txt
+++ b/runtime/doc/usr_11.txt
@@ -205,6 +205,13 @@ something wrong. It may be one of these two situations.
NEWER than swap file! ~
+NOTE that in the following situation Vim knows the swap file is not useful and
+will automatically delete it:
+- The file is a valid swap file (Magic number is correct).
+- The flag that the file was modified is not set.
+- The process is not running.
+
+
UNREADABLE SWAP FILE
Sometimes the line
diff --git a/runtime/doc/usr_40.txt b/runtime/doc/usr_40.txt
index e5d55fb857..9a1fe50f31 100644
--- a/runtime/doc/usr_40.txt
+++ b/runtime/doc/usr_40.txt
@@ -453,15 +453,15 @@ matching BufWritePre autocommands and executes them, and then it
performs the ":write".
The general form of the :autocmd command is as follows: >
- :autocmd [group] {events} {file_pattern} [nested] {command}
+ :autocmd [group] {events} {file_pattern} [++nested] {command}
The [group] name is optional. It is used in managing and calling the commands
(more on this later). The {events} parameter is a list of events (comma
separated) that trigger the command.
{file_pattern} is a filename, usually with wildcards. For example, using
"*.txt" makes the autocommand be used for all files whose name end in ".txt".
-The optional [nested] flag allows for nesting of autocommands (see below), and
-finally, {command} is the command to be executed.
+The optional [++nested] flag allows for nesting of autocommands (see below),
+and finally, {command} is the command to be executed.
EVENTS
@@ -576,9 +576,9 @@ NESTING
Generally, commands executed as the result of an autocommand event will not
trigger any new events. If you read a file in response to a FileChangedShell
event, it will not trigger the autocommands that would set the syntax, for
-example. To make the events triggered, add the "nested" argument: >
+example. To make the events triggered, add the "++nested" flag: >
- :autocmd FileChangedShell * nested edit
+ :autocmd FileChangedShell * ++nested edit
EXECUTING AUTOCOMMANDS
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 4adb69aaee..87164c8d90 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -806,6 +806,7 @@ Buffers, windows and the argument list:
getbufinfo() get a list with buffer information
gettabinfo() get a list with tab page information
getwininfo() get a list with window information
+ getjumplist() get a list of jump list entries
Command line: *command-line-functions*
getcmdline() get the current command line
@@ -825,6 +826,7 @@ Insert mode completion: *completion-functions*
complete() set found matches
complete_add() add to found matches
complete_check() check if completion should be aborted
+ complete_info() get current completion information
pumvisible() check if the popup menu is displayed
Folding: *folding-functions*
@@ -956,6 +958,8 @@ Various: *various-functions*
taglist() get list of matching tags
tagfiles() get a list of tags files
+ gettagstack() get the tag stack
+ settagstack() modify the tag stack
luaeval() evaluate Lua expression
py3eval() evaluate Python expression (|+python3|)
diff --git a/runtime/doc/usr_43.txt b/runtime/doc/usr_43.txt
index 04e9f9c000..15c94cd15e 100644
--- a/runtime/doc/usr_43.txt
+++ b/runtime/doc/usr_43.txt
@@ -94,7 +94,7 @@ unless it was set already. This will make sure that 'filetype' isn't set
twice.
You can use many different patterns to match the name of your file. Directory
-names can also be included. See |autocmd-patterns|. For example, the files
+names can also be included. See |autocmd-pattern|. For example, the files
under "/usr/share/scripts/" are all "ruby" files, but don't have the expected
file name extension. Adding this to the example above: >
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 2b8ba56c28..a0b2846b2b 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -235,35 +235,32 @@ g8 Print the hex values of the bytes used in the
*:!cmd* *:!* *E34*
:!{cmd} Execute {cmd} with 'shell'. See also |:terminal|.
- Any '!' in {cmd} is replaced with the previous
- external command (see also 'cpoptions'). But not when
- there is a backslash before the '!', then that
- backslash is removed. Example: ":!ls" followed by
+ The command runs in a non-interactive shell connected
+ to a pipe (not a terminal). Use |:terminal| to run an
+ interactive shell connected to a terminal.
+
+ Backgrounded ("&") commands must not write to stdout
+ or stderr, the streams are closed immediately. |E5677|
+ Use |jobstart()| instead. >
+ :call jobstart('foo', {'detach':1})
+<
+ Any "!" in {cmd} is replaced with the previous
+ external command (see also 'cpoptions'), unless
+ escaped by a backslash. Example: ":!ls" followed by
":!echo ! \! \\!" executes "echo ls ! \!".
- A '|' in {cmd} is passed to the shell, you cannot use
- it to append a Vim command. See |:bar|.
+ Any "|" in {cmd} is passed to the shell, you cannot
+ use it to append a Vim command. See |:bar|.
- If {cmd} contains "%" it is expanded to the current
- file name. Special characters are not escaped, use
- quotes to avoid their special meaning: >
+ Any "%" in {cmd} is expanded to the current file name.
+ Special characters are not escaped, use quotes or
+ |shellescape()|: >
:!ls "%"
-< If the file name contains a "$" single quotes might
- work better (but a single quote causes trouble): >
- :!ls '%'
-< This should always work, but it's more typing: >
:exe "!ls " . shellescape(expand("%"))
<
- A newline character ends {cmd}, what follows is
- interpreted as a following ":" command. However, if
- there is a backslash before the newline it is removed
- and {cmd} continues. It doesn't matter how many
- backslashes are before the newline, only one is
- removed.
-
- The command runs in a non-interactive shell connected
- to a pipe (not a terminal). Use |:terminal| to run an
- interactive shell connected to a terminal.
+ Newline character ends {cmd} unless a backslash
+ precedes the newline. What follows is interpreted as
+ another |:| command.
After the command has been executed, the timestamp and
size of the current file is checked |timestamp|.
@@ -273,143 +270,15 @@ g8 Print the hex values of the bytes used in the
data is lost, this only affects the display. The last
few lines are always displayed (never skipped).
- Vim redraws the screen after the command is finished,
- because it may have printed any text. This requires a
- hit-enter prompt, so that you can read any messages.
- To avoid this use: >
+ To avoid the hit-enter prompt use: >
:silent !{cmd}
-< The screen is not redrawn then, thus you have to use
- CTRL-L or ":redraw!" if the command did display
- something.
-
+<
*:!!*
:!! Repeat last ":!{cmd}".
*:ve* *:version*
-:ve[rsion] Print the version number of the editor. The following
- lines contain information about which features were
- enabled when Vim was compiled. When there is a
- preceding '+', the feature is included, when there is
- a '-' it is excluded. To change this, you have to
- edit feature.h and recompile Vim. To check for this
- in an expression, see |has()|. Here is an overview of
- the features. The first column shows the smallest
- version in which they are included:
- T tiny
- S small
- N normal
- B big
- H huge
- m manually enabled or depends on other features
- (none) system dependent
- Thus if a feature is marked with "N", it is included
- in the normal, big and huge versions of Vim.
-
- *+feature-list*
- *+acl* |ACL| support included
-B *+arabic* |Arabic| language support
-N *+autocmd* |:autocmd|, automatic commands
-N *+browse* |:browse| command
-N *+byte_offset* support for 'o' flag in 'statusline' option, "go"
- and ":goto" commands.
-N *+cindent* |'cindent'|, C indenting
-N *+clientserver* Unix and Win32: Remote invocation |clientserver|
- *+clipboard* |clipboard| support
-N *+cmdline_compl* command line completion |cmdline-completion|
-S *+cmdline_hist* command line history |cmdline-history|
-N *+cmdline_info* |'showcmd'| and |'ruler'|
-N *+comments* |'comments'| support
-B *+conceal* "conceal" support, see |conceal| |:syn-conceal| etc.
-B *+cscope* |cscope| support
-m *+cursorbind* |'cursorbind'| support
-m *+debug* Compiled for debugging.
-N *+dialog_gui* Support for |:confirm| with GUI dialog.
-N *+dialog_con* Support for |:confirm| with console dialog.
-N *+dialog_con_gui* Support for |:confirm| with GUI and console dialog.
-N *+digraphs* |digraphs| *E196*
-N *+eval* expression evaluation |eval.txt|
-N *+ex_extra* always on now, used to be for Vim's extra Ex commands
-N *+extra_search* |'hlsearch'| and |'incsearch'| options.
-N *+file_in_path* |gf|, |CTRL-W_f| and |<cfile>|
-N *+find_in_path* include file searches: |[I|, |:isearch|,
- |CTRL-W_CTRL-I|, |:checkpath|, etc.
-N *+folding* |folding|
-N *+gettext* message translations |multi-lang|
- *+iconv* Compiled with the |iconv()| function
- *+iconv/dyn* Likewise |iconv-dynamic| |/dyn|
-N *+insert_expand* |insert_expand| Insert mode completion
-S *+jumplist* |jumplist|
-B *+keymap* |'keymap'|
-N *+lambda* |lambda| and |closure|
-B *+langmap* |'langmap'|
-N *+libcall* |libcall()|
-N *+linebreak* |'linebreak'|, |'breakat'| and |'showbreak'|
-N *+lispindent* |'lisp'|
-N *+listcmds* Vim commands for the list of buffers |buffer-hidden|
- and argument list |:argdelete|
-N *+localmap* Support for mappings local to a buffer |:map-local|
-N *+menu* |:menu|
-N *+mksession* |:mksession|
-N *+modify_fname* |filename-modifiers|
-N *+mouse* Mouse handling |mouse-using|
-N *+mouseshape* |'mouseshape'|
-N *+multi_byte* 16 and 32 bit characters |multibyte|
- *+multi_byte_ime* Win32 input method for multibyte chars |multibyte-ime|
-N *+multi_lang* non-English language support |multi-lang|
- *+num64* 64-bit Number support |Number|
-N *+path_extra* Up/downwards search in 'path' and 'tags'
-N *+persistent_undo* Persistent undo |undo-persistence|
- *+postscript* |:hardcopy| writes a PostScript file
-N *+printer* |:hardcopy| command
-H *+profile* |:profile| command
-m *+python* Python 2 interface |python|
-m *+python3* Python 3 interface |python|
-N *+quickfix* |:make| and |quickfix| commands
-N *+reltime* |reltime()| function, 'hlsearch'/'incsearch' timeout,
- 'redrawtime' option
-B *+rightleft* Right to left typing |'rightleft'|
-N *+scrollbind* |'scrollbind'|
-N *+shada* |'shada'|
-B *+signs* |:sign|
-N *+smartindent* |'smartindent'|
-N *+startuptime* |--startuptime| argument
-N *+statusline* Options 'statusline', 'rulerformat' and special
- formats of 'titlestring' and 'iconstring'
-N *+syntax* Syntax highlighting |syntax|
-N *+tablineat* 'tabline' option recognizing %@Func@ items.
-T *+tag_binary* binary searching in tags file |tag-binary-search|
-N *+tag_old_static* old method for static tags |tag-old-static|
-m *+tag_any_white* any white space allowed in tags file |tag-any-white|
-B *+termguicolors* 24-bit color in xterm-compatible terminals support
-N *+termresponse* support for t_RV and |v:termresponse|
-N *+textobjects* |text-objects| selection
-N *+timers* the |timer_start()| function
-N *+title* Setting the window 'title' and 'icon'
-N *+toolbar* |gui-toolbar|
-N *+user_commands* User-defined commands. |user-commands|
- *+vertsplit* Vertically split windows |:vsplit|
-N *+virtualedit* |'virtualedit'|
-S *+visual* Visual mode |Visual-mode| Always enabled since 7.4.200.
-N *+visualextra* extra Visual mode commands |blockwise-operators|
-N *+vreplace* |gR| and |gr|
- *+vtp* on MS-Windows console: support for 'termguicolors'
-N *+wildignore* |'wildignore'|
-N *+wildmenu* |'wildmenu'|
- *+windows* more than one window
-m *+writebackup* |'writebackup'| is default on
-m *+xim* X input method |xim|
- *+xfontset* X fontset support |xfontset|
- *+xpm* pixmap support
-
- */dyn* *E370* *E448*
- To some of the features "/dyn" is added when the
- feature is only available when the related library can
- be dynamically loaded.
-
-:ve[rsion] {nr} Is now ignored. This was previously used to check the
- version number of a .vimrc file. It was removed,
- because you can now use the ":if" command for
- version-dependent behavior.
+:ve[rsion] Print editor version and build information.
+ See also |feature-compile|.
*:redi* *:redir*
:redi[r][!] > {file} Redirect messages to file {file}. The messages which
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 25ca2974bc..b7974aa735 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -6,8 +6,9 @@
Differences between Nvim and Vim *vim-differences*
-Nvim differs from Vim in many ways, big and small. This document is
-a complete and centralized reference of those differences.
+Nvim differs from Vim in many ways, although editor and VimL features are
+mostly identical. This document is a complete and centralized reference of
+the differences.
Type |gO| to see the table of contents.
@@ -133,7 +134,7 @@ Command-line highlighting:
removed in the future).
Commands:
- |:autocmd| accepts the `once` flag
+ |:autocmd| accepts the `++once` flag
|:checkhealth|
|:cquit| can use [count] to set the exit code
|:drop| is always available
@@ -161,6 +162,7 @@ Functions:
Highlight groups:
|expr-highlight| highlight groups (prefixed with "Nvim")
+ |hl-NormalFloat| highlights floating window
|hl-NormalNC| highlights non-current windows
|hl-MsgSeparator| highlights separator for scrolled messages
|hl-QuickFixLine|
@@ -192,10 +194,15 @@ Options:
'listchars' local to window
'pumblend' pseudo-transparent popupmenu
'scrollback'
+ 'signcolumn' supports up to 9 dynamic/fixed columns
'statusline' supports unlimited alignment sections
'tabline' %@Func@foo%X can call any function on mouse-click
+ 'wildoptions' `pum` flag to use popupmenu for wildmode completion
'winhighlight' window-local highlights
+Signs:
+ Signs are removed if the associated line is deleted.
+
Variables:
|v:event|
|v:exiting|
@@ -312,6 +319,9 @@ Macro/|recording| behavior
macros and 'keymap' at the same time. This also means you can use |:imap| on
the results of keys from 'keymap'.
+Motion:
+ The |jumplist| avoids useless/phantom jumps.
+
Normal commands:
|Q| is the same as |gQ|
@@ -336,7 +346,10 @@ Shell:
Startup:
|-e| and |-es| invoke the same "improved Ex mode" as -E and -Es.
- |-E| and |-Es| reads stdin as text (into buffer 1).
+ |-E| and |-Es| read stdin as text (into buffer 1).
+ |-es| and |-Es| have improved behavior:
+ - Quits automatically, don't need "-c qa!".
+ - Skips swap-file dialog.
|-s| reads Normal commands from stdin if the script name is "-".
Reading text (instead of commands) from stdin |--|:
- works by default: "-" file is optional
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 6e1ad0f1f4..63ffd91bfc 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -880,15 +880,15 @@ CTRL-W g } *CTRL-W_g}*
cursor. This is less clever than using |:ptag|, but you don't
need a tags file and it will also find matches in system
include files. Example: >
- :au! CursorHold *.[ch] nested exe "silent! psearch " . expand("<cword>")
+ :au! CursorHold *.[ch] ++nested exe "silent! psearch " . expand("<cword>")
< Warning: This can be slow.
Example *CursorHold-example* >
- :au! CursorHold *.[ch] nested exe "silent! ptag " . expand("<cword>")
+ :au! CursorHold *.[ch] ++nested exe "silent! ptag " . expand("<cword>")
This will cause a ":ptag" to be executed for the keyword under the cursor,
-when the cursor hasn't moved for the time set with 'updatetime'. The "nested"
+when the cursor hasn't moved for the time set with 'updatetime'. "++nested"
makes other autocommands be executed, so that syntax highlighting works in the
preview window. The "silent!" avoids an error message when the tag could not
be found. Also see |CursorHold|. To disable this again: >
@@ -898,7 +898,7 @@ be found. Also see |CursorHold|. To disable this again: >
A nice addition is to highlight the found tag, avoid the ":ptag" when there
is no word under the cursor, and a few other things: >
- :au! CursorHold *.[ch] nested call PreviewWord()
+ :au! CursorHold *.[ch] ++nested call PreviewWord()
:func PreviewWord()
: if &previewwindow " don't do this in the preview window
: return
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index a4d46fd842..a670f54898 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -707,8 +707,8 @@ au BufNewFile,BufRead *.erb,*.rhtml setf eruby
" HTML with M4
au BufNewFile,BufRead *.html.m4 setf htmlm4
-" HTML Cheetah template
-au BufNewFile,BufRead *.tmpl setf htmlcheetah
+" Some template. Used to be HTML Cheetah.
+au BufNewFile,BufRead *.tmpl setf template
" Host config
au BufNewFile,BufRead */etc/host.conf setf hostconf
@@ -1458,9 +1458,11 @@ au BufNewFile,BufRead sgml.catalog* call s:StarSetf('catalog')
" Shell scripts (sh, ksh, bash, bash2, csh); Allow .profile_foo etc.
" Gentoo ebuilds and Arch Linux PKGBUILDs are actually bash scripts
-au BufNewFile,BufRead .bashrc*,bashrc,bash.bashrc,.bash[_-]profile*,.bash[_-]logout*,.bash[_-]aliases*,bash-fc[-.]*,*.bash,*/{,.}bash[_-]completion{,.d,.sh}{,/*},*.ebuild,*.eclass,PKGBUILD* call dist#ft#SetFileTypeSH("bash")
-au BufNewFile,BufRead .kshrc*,*.ksh call dist#ft#SetFileTypeSH("ksh")
-au BufNewFile,BufRead */etc/profile,.profile*,*.sh,*.env call dist#ft#SetFileTypeSH(getline(1))
+" NOTE: Patterns ending in a star are further down, these have lower priority.
+au BufNewFile,BufRead .bashrc,bashrc,bash.bashrc,.bash[_-]profile,.bash[_-]logout,.bash[_-]aliases,bash-fc[-.],*.bash,*/{,.}bash[_-]completion{,.d,.sh}{,/*},*.ebuild,*.eclass,PKGBUILD call dist#ft#SetFileTypeSH("bash")
+au BufNewFile,BufRead .kshrc,*.ksh call dist#ft#SetFileTypeSH("ksh")
+au BufNewFile,BufRead */etc/profile,.profile,*.sh,*.env call dist#ft#SetFileTypeSH(getline(1))
+
" Shell script (Arch Linux) or PHP file (Drupal)
au BufNewFile,BufRead *.install
@@ -1470,15 +1472,16 @@ au BufNewFile,BufRead *.install
\ call dist#ft#SetFileTypeSH("bash") |
\ endif
-" tcsh scripts
-au BufNewFile,BufRead .tcshrc*,*.tcsh,tcsh.tcshrc,tcsh.login call dist#ft#SetFileTypeShell("tcsh")
+" tcsh scripts (patterns ending in a star further below)
+au BufNewFile,BufRead .tcshrc,*.tcsh,tcsh.tcshrc,tcsh.login call dist#ft#SetFileTypeShell("tcsh")
" csh scripts, but might also be tcsh scripts (on some systems csh is tcsh)
-au BufNewFile,BufRead .login*,.cshrc*,csh.cshrc,csh.login,csh.logout,*.csh,.alias call dist#ft#CSH()
+" (patterns ending in a start further below)
+au BufNewFile,BufRead .login,.cshrc,csh.cshrc,csh.login,csh.logout,*.csh,.alias call dist#ft#CSH()
-" Z-Shell script
+" Z-Shell script (patterns ending in a star further below)
au BufNewFile,BufRead .zprofile,*/etc/zprofile,.zfbfmarks setf zsh
-au BufNewFile,BufRead .zsh*,.zlog*,.zcompdump* call s:StarSetf('zsh')
+au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh
au BufNewFile,BufRead *.zsh setf zsh
" Scheme
@@ -1691,6 +1694,9 @@ au BufNewFile,BufReadPost *.tutor setf tutor
" TWIG files
au BufNewFile,BufReadPost *.twig setf twig
+" Typescript
+au BufNewFile,BufReadPost *.ts setf typescript
+
" Motif UIT/UIL files
au BufNewFile,BufRead *.uit,*.uil setf uil
@@ -2073,6 +2079,17 @@ au BufRead,BufNewFile *.rdf call dist#ft#Redif()
" Remind
au BufNewFile,BufRead .reminders* call s:StarSetf('remind')
+" Shell scripts ending in a star
+au BufNewFile,BufRead .bashrc*,.bash[_-]profile*,.bash[_-]logout*,.bash[_-]aliases*,bash-fc[-.]*,,PKGBUILD* call dist#ft#SetFileTypeSH("bash")
+au BufNewFile,BufRead .kshrc* call dist#ft#SetFileTypeSH("ksh")
+au BufNewFile,BufRead .profile* call dist#ft#SetFileTypeSH(getline(1))
+
+" tcsh scripts ending in a star
+au BufNewFile,BufRead .tcshrc* call dist#ft#SetFileTypeShell("tcsh")
+
+" csh scripts ending in a star
+au BufNewFile,BufRead .login*,.cshrc* call dist#ft#CSH()
+
" Vim script
au BufNewFile,BufRead *vimrc* call s:StarSetf('vim')
@@ -2100,7 +2117,8 @@ au BufNewFile,BufRead */etc/xinetd.d/* call s:StarSetf('xinetd')
" yum conf (close enough to dosini)
au BufNewFile,BufRead */etc/yum.repos.d/* call s:StarSetf('dosini')
-" Z-Shell script
+" Z-Shell script ending in a star
+au BufNewFile,BufRead .zsh*,.zlog*,.zcompdump* call s:StarSetf('zsh')
au BufNewFile,BufRead zsh*,zlog* call s:StarSetf('zsh')
diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim
index 8ebfa12caf..cd735c3a3c 100644
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -10,7 +10,7 @@ endif
let b:did_indent = 1
setlocal indentexpr=GetVimIndent()
-setlocal indentkeys+==end,=else,=cat,=fina,=END,0\\
+setlocal indentkeys+==end,=else,=cat,=fina,=END,0\\,0=\"\\\
let b:undo_indent = "setl indentkeys< indentexpr<"
@@ -31,15 +31,17 @@ function GetVimIndent()
endtry
endfunc
+let s:lineContPat = '^\s*\(\\\|"\\ \)'
+
function GetVimIndentIntern()
" Find a non-blank line above the current line.
let lnum = prevnonblank(v:lnum - 1)
- " If the current line doesn't start with '\' and below a line that starts
- " with '\', use the indent of the line above it.
+ " If the current line doesn't start with '\' or '"\ ' and below a line that
+ " starts with '\' or '"\ ', use the indent of the line above it.
let cur_text = getline(v:lnum)
- if cur_text !~ '^\s*\\'
- while lnum > 0 && getline(lnum) =~ '^\s*\\'
+ if cur_text !~ s:lineContPat
+ while lnum > 0 && getline(lnum) =~ s:lineContPat
let lnum = lnum - 1
endwhile
endif
@@ -51,10 +53,10 @@ function GetVimIndentIntern()
let prev_text = getline(lnum)
" Add a 'shiftwidth' after :if, :while, :try, :catch, :finally, :function
- " and :else. Add it three times for a line that starts with '\' after
- " a line that doesn't (or g:vim_indent_cont if it exists).
+ " and :else. Add it three times for a line that starts with '\' or '"\ '
+ " after a line that doesn't (or g:vim_indent_cont if it exists).
let ind = indent(lnum)
- if cur_text =~ '^\s*\\' && v:lnum > 1 && prev_text !~ '^\s*\\'
+ if cur_text =~ s:lineContPat && v:lnum > 1 && prev_text !~ s:lineContPat
if exists("g:vim_indent_cont")
let ind = ind + g:vim_indent_cont
else
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
new file mode 100644
index 0000000000..07145c6e3f
--- /dev/null
+++ b/runtime/lua/vim/shared.lua
@@ -0,0 +1,179 @@
+-- Functions shared by Nvim and its test-suite.
+--
+-- The singular purpose of this module is to share code with the Nvim
+-- test-suite. If, in the future, Nvim itself is used to run the test-suite
+-- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua
+
+
+--- Returns a deep copy of the given object. Non-table objects are copied as
+--- in a typical Lua assignment, whereas table objects are copied recursively.
+---
+--@param orig Table to copy
+--@returns New table of copied keys and (nested) values.
+local function deepcopy(orig)
+ error(orig)
+end
+local function _id(v)
+ return v
+end
+local deepcopy_funcs = {
+ table = function(orig)
+ local copy = {}
+ for k, v in pairs(orig) do
+ copy[deepcopy(k)] = deepcopy(v)
+ end
+ return copy
+ end,
+ number = _id,
+ string = _id,
+ ['nil'] = _id,
+ boolean = _id,
+}
+deepcopy = function(orig)
+ return deepcopy_funcs[type(orig)](orig)
+end
+
+--- Splits a string at each instance of a separator.
+---
+--@see |vim.split()|
+--@see https://www.lua.org/pil/20.2.html
+--@see http://lua-users.org/wiki/StringLibraryTutorial
+---
+--@param s String to split
+--@param sep Separator string or pattern
+--@param plain If `true` use `sep` literally (passed to String.find)
+--@returns Iterator over the split components
+local function gsplit(s, sep, plain)
+ assert(type(s) == "string")
+ assert(type(sep) == "string")
+ assert(type(plain) == "boolean" or type(plain) == "nil")
+
+ local start = 1
+ local done = false
+
+ local function _pass(i, j, ...)
+ if i then
+ assert(j+1 > start, "Infinite loop detected")
+ local seg = s:sub(start, i - 1)
+ start = j + 1
+ return seg, ...
+ else
+ done = true
+ return s:sub(start)
+ end
+ end
+
+ return function()
+ if done then
+ return
+ end
+ if sep == '' then
+ if start == #s then
+ done = true
+ end
+ return _pass(start+1, start)
+ end
+ return _pass(s:find(sep, start, plain))
+ end
+end
+
+--- Splits a string at each instance of a separator.
+---
+--- Examples:
+--- <pre>
+--- split(":aa::b:", ":") --> {'','aa','','bb',''}
+--- split("axaby", "ab?") --> {'','x','y'}
+--- split(x*yz*o, "*", true) --> {'x','yz','o'}
+--- </pre>
+--
+--@see |vim.gsplit()|
+---
+--@param s String to split
+--@param sep Separator string or pattern
+--@param plain If `true` use `sep` literally (passed to String.find)
+--@returns List-like table of the split components.
+local function split(s,sep,plain)
+ local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end
+ return t
+end
+
+--- Checks if a list-like (vector) table contains `value`.
+---
+--@param t Table to check
+--@param value Value to compare
+--@returns true if `t` contains `value`
+local function tbl_contains(t, value)
+ if type(t) ~= 'table' then
+ error('t must be a table')
+ end
+ for _,v in ipairs(t) do
+ if v == value then
+ return true
+ end
+ end
+ return false
+end
+
+--- Merges two or more map-like tables.
+---
+--@see |extend()|
+---
+--@param behavior Decides what to do if a key is found in more than one map:
+--- - "error": raise an error
+--- - "keep": use value from the leftmost map
+--- - "force": use value from the rightmost map
+--@param ... Two or more map-like tables.
+local function tbl_extend(behavior, ...)
+ if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then
+ error('invalid "behavior": '..tostring(behavior))
+ end
+ local ret = {}
+ for i = 1, select('#', ...) do
+ local tbl = select(i, ...)
+ if tbl then
+ for k, v in pairs(tbl) do
+ if behavior ~= 'force' and ret[k] ~= nil then
+ if behavior == 'error' then
+ error('key found in more than one map: '..k)
+ end -- Else behavior is "keep".
+ else
+ ret[k] = v
+ end
+ end
+ end
+ end
+ return ret
+end
+
+--- Creates a copy of a list-like table such that any nested tables are
+--- "unrolled" and appended to the result.
+---
+--@param t List-like table
+--@returns Flattened copy of the given list-like table.
+local function tbl_flatten(t)
+ -- From https://github.com/premake/premake-core/blob/master/src/base/table.lua
+ local result = {}
+ local function _tbl_flatten(_t)
+ local n = #_t
+ for i = 1, n do
+ local v = _t[i]
+ if type(v) == "table" then
+ _tbl_flatten(v)
+ elseif v then
+ table.insert(result, v)
+ end
+ end
+ end
+ _tbl_flatten(t)
+ return result
+end
+
+local module = {
+ deepcopy = deepcopy,
+ gsplit = gsplit,
+ split = split,
+ tbl_contains = tbl_contains,
+ tbl_extend = tbl_extend,
+ tbl_flatten = tbl_flatten,
+}
+return module
diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml
index 095e6c6fa8..fb409ff0b8 100644
--- a/runtime/nvim.appdata.xml
+++ b/runtime/nvim.appdata.xml
@@ -8,6 +8,7 @@
-->
<component type="desktop-application">
<id>nvim</id>
+ <translation type="gettext">nvim</translation>
<project_license>Apache-2.0</project_license>
<metadata_license>CC0-1.0</metadata_license>
<name>Neovim</name>
@@ -24,6 +25,16 @@
</screenshot>
</screenshots>
+ <releases>
+ <release date="2019-04-29" version="0.3.5"/>
+ <release date="2019-01-13" version="0.3.4"/>
+ <release date="2019-01-05" version="0.3.3"/>
+ <release date="2018-12-31" version="0.3.2"/>
+ <release date="2018-07-19" version="0.3.1"/>
+ <release date="2018-07-11" version="0.3.0"/>
+ </releases>
+
+ <content_rating type="oars-1.1"/>
<launchable type="desktop-id">nvim.desktop</launchable>
<url type="homepage">https://neovim.io/</url>
<url type="bugtracker">https://github.com/neovim/neovim/issues</url>
diff --git a/runtime/nvim.png b/runtime/nvim.png
index dbc65ef363..5d6cee3b49 100644
--- a/runtime/nvim.png
+++ b/runtime/nvim.png
Binary files differ
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 506179297a..04d940948a 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -1,133 +1,890 @@
" Debugger plugin using gdb.
"
-" WORK IN PROGRESS - much doesn't work yet
+" Author: Bram Moolenaar
+" Copyright: Vim license applies, see ":help license"
+" Last Update: 2018 Jun 3
+"
+" WORK IN PROGRESS - Only the basics work
+" Note: On MS-Windows you need a recent version of gdb. The one included with
+" MingW is too old (7.6.1).
+" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb
+"
+" There are two ways to run gdb:
+" - In a terminal window; used if possible, does not work on MS-Windows
+" Not used when g:termdebug_use_prompt is set to 1.
+" - Using a "prompt" buffer; may use a terminal window for the program
"
-" Open two visible terminal windows:
-" 1. run a pty, as with ":term NONE"
-" 2. run gdb, passing the pty
-" The current window is used to view source code and follows gdb.
+" For both the current window is used to view source code and shows the
+" current statement from gdb.
"
+" USING A TERMINAL WINDOW
+"
+" Opens two visible terminal windows:
+" 1. runs a pty for the debugged program, as with ":term NONE"
+" 2. runs gdb, passing the pty of the debugged program
" A third terminal window is hidden, it is used for communication with gdb.
"
+" USING A PROMPT BUFFER
+"
+" Opens a window with a prompt buffer to communicate with gdb.
+" Gdb is run as a job with callbacks for I/O.
+" On Unix another terminal window is opened to run the debugged program
+" On MS-Windows a separate console is opened to run the debugged program
+"
" The communication with gdb uses GDB/MI. See:
" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
"
+" For neovim compatibility, the vim specific calls were replaced with neovim
+" specific calls:
+" term_start -> term_open
+" term_sendkeys -> jobsend
+" term_getline -> getbufline
+" job_info && term_getjob -> using linux command ps to get the tty
+" balloon -> nvim floating window
+"
+" The code for opening the floating window was taken from the beautiful
+" 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
+" to use the prompt buffer was removed.
+"
" Author: Bram Moolenaar
" Copyright: Vim license applies, see ":help license"
-" In case this gets loaded twice.
+" In case this gets sourced twice.
if exists(':Termdebug')
finish
endif
+
+let s:keepcpo = &cpo
+set cpo&vim
+
" The command that starts debugging, e.g. ":Termdebug vim".
" To end type "quit" in the gdb window.
-command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
+command -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>)
+command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
" Name of the gdb command, defaults to "gdb".
-if !exists('debugger')
- let debugger = 'gdb'
+if !exists('termdebugger')
+ let termdebugger = 'gdb'
endif
-" Sign used to highlight the line where the program has stopped.
-sign define debugPC linehl=debugPC
-if &background == 'light'
- hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
-else
- hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
-endif
let s:pc_id = 12
+let s:break_id = 13 " breakpoint number is added to this
+let s:stopped = 1
+
+" 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.
+func s:Breakpoint2SignNumber(id, subid)
+ return s:break_id + a:id * 1000 + a:subid
+endfunction
-func s:StartDebug(cmd)
- let s:startwin = win_getid(winnr())
+func s:Highlight(init, old, new)
+ let default = a:init ? 'default ' : ''
+ if a:new ==# 'light' && a:old !=# 'light'
+ exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
+ elseif a:new ==# 'dark' && a:old !=# 'dark'
+ exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue"
+ endif
+endfunc
+
+call s:Highlight(1, '', &background)
+hi default debugBreakpoint term=reverse ctermbg=red guibg=red
+
+func s:StartDebug(bang, ...)
+ " First argument is the command to debug, second core file or process ID.
+ call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
+endfunc
+
+func s:StartDebugCommand(bang, ...)
+ " First argument is the command to debug, rest are run arguments.
+ call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
+endfunc
+
+func s:StartDebug_internal(dict)
+ if exists('s:gdbwin')
+ echoerr 'Terminal debugger already running'
+ return
+ endif
+ let s:ptywin = 0
+ let s:pid = 0
+
+ " Uncomment this line to write logging in "debuglog".
+ " call ch_logfile('debuglog', 'w')
+
+ let s:sourcewin = win_getid(winnr())
let s:startsigncolumn = &signcolumn
- " Open a terminal window without a job, to run the debugged program
- let s:ptybuf = term_start('NONE', {
- \ 'term_name': 'gdb program',
- \ })
- if s:ptybuf == 0
+ let s:save_columns = 0
+ if exists('g:termdebug_wide')
+ if &columns < g:termdebug_wide
+ let s:save_columns = &columns
+ let &columns = g:termdebug_wide
+ endif
+ let s:vertical = 1
+ else
+ let s:vertical = 0
+ endif
+
+ call s:StartDebug_term(a:dict)
+endfunc
+
+" Use when debugger didn't start or ended.
+func s:CloseBuffers()
+ exe 'bwipe! ' . s:ptybuf
+ unlet! s:gdbwin
+endfunc
+
+func s:StartDebug_term(dict)
+ " Open a terminal window without a job, to run the debugged program in.
+ execute 'new'
+ let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
+ if s:pty_job_id == 0
+ echoerr 'invalid argument (or job table is full) while opening terminal window'
+ return
+ elseif s:pty_job_id == -1
echoerr 'Failed to open the program terminal window'
return
endif
- let pty = job_info(term_getjob(s:ptybuf))['tty_out']
+ let pty_job_info = nvim_get_chan_info(s:pty_job_id)
+ let s:ptybuf = pty_job_info['buffer']
+ let pty = pty_job_info['pty']
+ let s:ptywin = win_getid(winnr())
+ if s:vertical
+ " Assuming the source code window will get a signcolumn, use two more
+ " columns for that, thus one less for the terminal window.
+ exe (&columns / 2 - 1) . "wincmd |"
+ endif
" Create a hidden terminal window to communicate with gdb
- let s:commbuf = term_start('NONE', {
- \ 'term_name': 'gdb communication',
- \ 'out_cb': function('s:CommOutput'),
- \ 'hidden': 1,
- \ })
- if s:commbuf == 0
+ let s:comm_job_id = jobstart('tail -f /dev/null;#gdb communication', {
+ \ 'on_stdout': function('s:CommOutput'),
+ \ 'pty': v:true,
+ \ })
+ " hide terminal buffer
+ if s:comm_job_id == 0
+ echoerr 'invalid argument (or job table is full) while opening communication terminal window'
+ exe 'bwipe! ' . s:ptybuf
+ return
+ elseif s:comm_job_id == -1
echoerr 'Failed to open the communication terminal window'
exe 'bwipe! ' . s:ptybuf
return
endif
- let commpty = job_info(term_getjob(s:commbuf))['tty_out']
+ let comm_job_info = nvim_get_chan_info(s:comm_job_id)
+ let commpty = comm_job_info['pty']
" Open a terminal window to run the debugger.
- let cmd = [g:debugger, '-tty', pty, a:cmd]
- echomsg 'executing "' . join(cmd) . '"'
- let gdbbuf = term_start(cmd, {
- \ 'exit_cb': function('s:EndDebug'),
- \ 'term_finish': 'close',
- \ })
- if gdbbuf == 0
- echoerr 'Failed to open the gdb terminal window'
+ " Add -quiet to avoid the intro message causing a hit-enter prompt.
+ let gdb_args = get(a:dict, 'gdb_args', [])
+ let proc_args = get(a:dict, 'proc_args', [])
+
+ let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
+ "call ch_log('executing "' . join(cmd) . '"')
+ execute 'new'
+ let s:gdb_job_id = termopen(cmd, {'on_exit': function('s:EndTermDebug')})
+ if s:gdb_job_id == 0
+ echoerr 'invalid argument (or job table is full) while opening gdb terminal window'
exe 'bwipe! ' . s:ptybuf
- exe 'bwipe! ' . s:commbuf
return
+ elseif s:gdb_job_id == -1
+ echoerr 'Failed to open the gdb terminal window'
+ call s:CloseBuffers()
+ return
+ endif
+ let gdb_job_info = nvim_get_chan_info(s:gdb_job_id)
+ let s:gdbbuf = gdb_job_info['buffer']
+ let s:gdbwin = win_getid(winnr())
+
+ " Set arguments to be run
+ if len(proc_args)
+ call jobsend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r")
endif
" Connect gdb to the communication pty, using the GDB/MI interface
- call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
+ call jobsend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r")
+
+ " Wait for the response to show up, users may not notice the error and wonder
+ " why the debugger doesn't work.
+ let try_count = 0
+ while 1
+ if nvim_get_chan_info(s:gdb_job_id) == {}
+ echoerr string(g:termdebugger) . ' exited unexpectedly'
+ call s:CloseBuffers()
+ return
+ endif
+
+ let response = ''
+ for lnum in range(1,200)
+ if len(getbufline(s:gdbbuf, lnum)) > 0 && getbufline(s:gdbbuf, lnum)[0] =~ 'new-ui mi '
+ " response can be in the same line or the next line
+ let response = getbufline(s:gdbbuf, lnum)[0] . getbufline(s:gdbbuf, lnum + 1)[0]
+ if response =~ 'Undefined command'
+ echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
+ call s:CloseBuffers()
+ return
+ endif
+ if response =~ 'New UI allocated'
+ " Success!
+ break
+ endif
+ endif
+ endfor
+ if response =~ 'New UI allocated'
+ break
+ endif
+ let try_count += 1
+ if try_count > 100
+ echoerr 'Cannot check if your gdb works, continuing anyway'
+ break
+ endif
+ sleep 10m
+ endwhile
+
+ " Interpret commands while the target is running. This should usualy only be
+ " exec-interrupt, since many commands don't work properly while the target is
+ " running.
+ call s:SendCommand('-gdb-set mi-async on')
+ " Older gdb uses a different command.
+ call s:SendCommand('-gdb-set target-async on')
+
+ " Disable pagination, it causes everything to stop at the gdb
+ " "Type <return> to continue" prompt.
+ call s:SendCommand('set pagination off')
+
+ call s:StartDebugCommon(a:dict)
endfunc
-func s:EndDebug(job, status)
- exe 'bwipe! ' . s:ptybuf
- exe 'bwipe! ' . s:commbuf
- call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn)
+
+func s:StartDebugCommon(dict)
+ " Sign used to highlight the line where the program has stopped.
+ " There can be only one.
+ sign define debugPC linehl=debugPC
+
+ " Install debugger commands in the text window.
+ call win_gotoid(s:sourcewin)
+ call s:InstallCommands()
+ call win_gotoid(s:gdbwin)
+
+ " Contains breakpoints that have been placed, key is a string with the GDB
+ " breakpoint number.
+ " Each entry is a dict, containing the sub-breakpoints. Key is the subid.
+ " For a breakpoint that is just a number the subid is zero.
+ " For a breakpoint "123.4" the id is "123" and subid is "4".
+ " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
+ " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
+ let s:breakpoints = {}
+
+ " Contains breakpoints by file/lnum. The key is "fname:lnum".
+ " Each entry is a list of breakpoint IDs at that position.
+ let s:breakpoint_locations = {}
+
+ augroup TermDebug
+ au BufRead * call s:BufRead()
+ au BufUnload * call s:BufUnloaded()
+ au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
+ augroup END
+
+ " Run the command if the bang attribute was given and got to the debug
+ " window.
+ if get(a:dict, 'bang', 0)
+ call s:SendCommand('-exec-run')
+ call win_gotoid(s:ptywin)
+ endif
+endfunc
+
+" Send a command to gdb. "cmd" is the string without line terminator.
+func s:SendCommand(cmd)
+ "call ch_log('sending to gdb: ' . a:cmd)
+ call jobsend(s:comm_job_id, a:cmd . "\r")
+endfunc
+
+" This is global so that a user can create their mappings with this.
+func TermDebugSendCommand(cmd)
+ let do_continue = 0
+ if !s:stopped
+ let do_continue = 1
+ call s:SendCommand('-exec-interrupt')
+ sleep 10m
+ endif
+ call jobsend(s:gdb_job_id, a:cmd . "\r")
+ if do_continue
+ Continue
+ endif
+endfunc
+
+" Decode a message from gdb. quotedText starts with a ", return the text up
+" to the next ", unescaping characters.
+func s:DecodeMessage(quotedText)
+ if a:quotedText[0] != '"'
+ echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
+ return
+ endif
+ let result = ''
+ let i = 1
+ while a:quotedText[i] != '"' && i < len(a:quotedText)
+ if a:quotedText[i] == '\'
+ let i += 1
+ if a:quotedText[i] == 'n'
+ " drop \n
+ let i += 1
+ continue
+ endif
+ endif
+ let result .= a:quotedText[i]
+ let i += 1
+ endwhile
+ return result
+endfunc
+
+" Extract the "name" value from a gdb message with fullname="name".
+func s:GetFullname(msg)
+ if a:msg !~ 'fullname'
+ return ''
+ endif
+ let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
+ if has('win32') && name =~ ':\\\\'
+ " sometimes the name arrives double-escaped
+ let name = substitute(name, '\\\\', '\\', 'g')
+ endif
+ return name
+endfunc
+
+function s:EndTermDebug(job_id, exit_code, event)
+ unlet s:gdbwin
+
+ call s:EndDebugCommon()
endfunc
-" Handle a message received from gdb on the GDB/MI interface.
-func s:CommOutput(chan, msg)
- let msgs = split(a:msg, "\r")
+func s:EndDebugCommon()
+ let curwinid = win_getid(winnr())
- for msg in msgs
+ if exists('s:ptybuf') && s:ptybuf
+ exe 'bwipe! ' . s:ptybuf
+ endif
+
+ call win_gotoid(s:sourcewin)
+ let &signcolumn = s:startsigncolumn
+ call s:DeleteCommands()
+
+ call win_gotoid(curwinid)
+
+ if s:save_columns > 0
+ let &columns = s:save_columns
+ endif
+
+ au! TermDebug
+endfunc
+
+func s:CommOutput(job_id, msgs, event)
+
+ for msg in a:msgs
" remove prefixed NL
if msg[0] == "\n"
let msg = msg[1:]
endif
if msg != ''
- if msg =~ '^\*\(stopped\|running\)'
- let wid = win_getid(winnr())
-
- if win_gotoid(s:startwin)
- if msg =~ '^\*stopped'
- " TODO: proper parsing
- let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '')
- let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
- if lnum =~ '^[0-9]*$'
- if expand('%:h') != fname
- if &modified
- " TODO: find existing window
- exe 'split ' . fnameescape(fname)
- let s:startwin = win_getid(winnr())
- else
- exe 'edit ' . fnameescape(fname)
- endif
- endif
- exe lnum
- exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
- setlocal signcolumn=yes
- endif
- else
- exe 'sign unplace ' . s:pc_id
- endif
-
- call win_gotoid(wid)
- endif
+ if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
+ call s:HandleCursor(msg)
+ elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
+ call s:HandleNewBreakpoint(msg)
+ elseif msg =~ '^=breakpoint-deleted,'
+ call s:HandleBreakpointDelete(msg)
+ elseif msg =~ '^=thread-group-started'
+ call s:HandleProgramRun(msg)
+ elseif msg =~ '^\^done,value='
+ call s:HandleEvaluate(msg)
+ elseif msg =~ '^\^error,msg='
+ call s:HandleError(msg)
+ endif
+ endif
+ endfor
+endfunc
+
+" Install commands in the current window to control the debugger.
+func s:InstallCommands()
+ let save_cpo = &cpo
+ set cpo&vim
+
+ command Break call s:SetBreakpoint()
+ command Clear call s:ClearBreakpoint()
+ command Step call s:SendCommand('-exec-step')
+ command Over call s:SendCommand('-exec-next')
+ command Finish call s:SendCommand('-exec-finish')
+ command -nargs=* Run call s:Run(<q-args>)
+ command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
+ command Stop call s:SendCommand('-exec-interrupt')
+
+ " using -exec-continue results in CTRL-C in gdb window not working
+ command Continue call jobsend(s:gdb_job_id, "continue\r")
+
+ command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
+ command Gdb call win_gotoid(s:gdbwin)
+ command Program call win_gotoid(s:ptywin)
+ command Source call s:GotoSourcewinOrCreateIt()
+ command Winbar call s:InstallWinbar()
+
+ " TODO: can the K mapping be restored?
+ nnoremap K :Evaluate<CR>
+
+ let &cpo = save_cpo
+endfunc
+
+let s:winbar_winids = []
+
+" Delete installed debugger commands in the current window.
+func s:DeleteCommands()
+ delcommand Break
+ delcommand Clear
+ delcommand Step
+ delcommand Over
+ delcommand Finish
+ delcommand Run
+ delcommand Arguments
+ delcommand Stop
+ delcommand Continue
+ delcommand Evaluate
+ delcommand Gdb
+ delcommand Program
+ delcommand Source
+ delcommand Winbar
+
+ nunmap K
+
+ exe 'sign unplace ' . s:pc_id
+ for [id, entries] in items(s:breakpoints)
+ for subid in keys(entries)
+ exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
+ endfor
+ endfor
+ unlet s:breakpoints
+ unlet s:breakpoint_locations
+
+ sign undefine debugPC
+ for val in s:BreakpointSigns
+ exe "sign undefine debugBreakpoint" . val
+ endfor
+ let s:BreakpointSigns = []
+endfunc
+
+" :Break - Set a breakpoint at the cursor position.
+func s:SetBreakpoint()
+ " Setting a breakpoint may not work while the program is running.
+ " Interrupt to make it work.
+ let do_continue = 0
+ if !s:stopped
+ let do_continue = 1
+ call s:SendCommand('-exec-interrupt')
+ sleep 10m
+ endif
+ " Use the fname:lnum format, older gdb can't handle --source.
+ call s:SendCommand('-break-insert '
+ \ . fnameescape(expand('%:p')) . ':' . line('.'))
+ if do_continue
+ call s:SendCommand('-exec-continue')
+ endif
+endfunc
+
+" :Clear - Delete a breakpoint at the cursor position.
+func s:ClearBreakpoint()
+ let fname = fnameescape(expand('%:p'))
+ let lnum = line('.')
+ let bploc = printf('%s:%d', fname, lnum)
+ if has_key(s:breakpoint_locations, bploc)
+ let idx = 0
+ for id in s:breakpoint_locations[bploc]
+ if has_key(s:breakpoints, id)
+ " Assume this always works, the reply is simply "^done".
+ call s:SendCommand('-break-delete ' . id)
+ for subid in keys(s:breakpoints[id])
+ exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
+ endfor
+ unlet s:breakpoints[id]
+ unlet s:breakpoint_locations[bploc][idx]
+ break
+ else
+ let idx += 1
+ endif
+ endfor
+ if empty(s:breakpoint_locations[bploc])
+ unlet s:breakpoint_locations[bploc]
+ endif
+ endif
+endfunc
+
+func s:Run(args)
+ if a:args != ''
+ call s:SendCommand('-exec-arguments ' . a:args)
+ endif
+ call s:SendCommand('-exec-run')
+endfunc
+
+func s:SendEval(expr)
+ call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
+ let s:evalexpr = a:expr
+endfunc
+
+" :Evaluate - evaluate what is under the cursor
+func s:Evaluate(range, arg)
+ if a:arg != ''
+ let expr = a:arg
+ let s:evalFromBalloonExpr = 0
+ elseif a:range == 2
+ let pos = getcurpos()
+ let reg = getreg('v', 1, 1)
+ let regt = getregtype('v')
+ normal! gv"vy
+ let expr = @v
+ call setpos('.', pos)
+ call setreg('v', reg, regt)
+ let s:evalFromBalloonExpr = 1
+ else
+ let expr = expand('<cexpr>')
+ let s:evalFromBalloonExpr = 1
+ endif
+ let s:ignoreEvalError = 0
+ call s:SendEval(expr)
+endfunc
+
+let s:ignoreEvalError = 0
+let s:evalFromBalloonExpr = 0
+let s:evalFromBalloonExprResult = ''
+
+" Handle the result of data-evaluate-expression
+func s:HandleEvaluate(msg)
+ let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
+ let value = substitute(value, '\\"', '"', 'g')
+ let value = substitute(value, ' ', '\1', '')
+ if s:evalFromBalloonExpr
+ if s:evalFromBalloonExprResult == ''
+ let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
+ else
+ let s:evalFromBalloonExprResult .= ' = ' . value
+ endif
+ let s:evalFromBalloonExprResult = split(s:evalFromBalloonExprResult, '\\n')
+ call s:OpenHoverPreview(s:evalFromBalloonExprResult, v:null)
+ else
+ echomsg '"' . s:evalexpr . '": ' . value
+ endif
+
+ if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
+ " Looks like a pointer, also display what it points to.
+ let s:ignoreEvalError = 1
+ call s:SendEval('*' . s:evalexpr)
+ else
+ let s:evalFromBalloonExprResult = ''
+ endif
+endfunc
+
+function! s:ShouldUseFloatWindow() abort
+ if exists('*nvim_open_win') && (get(g:, 'termdebug_useFloatingHover', 1) == 1)
+ return v:true
+ else
+ return v:false
+ endif
+endfunction
+
+function! s:CloseFloatingHoverOnCursorMove(win_id, opened) abort
+ if getpos('.') == a:opened
+ " Just after opening floating window, CursorMoved event is run.
+ " To avoid closing floating window immediately, check the cursor
+ " was really moved
+ return
+ endif
+ autocmd! nvim_termdebug_close_hover
+ let winnr = win_id2win(a:win_id)
+ if winnr == 0
+ return
+ endif
+ call nvim_win_close(a:win_id, v:true)
+endfunction
+
+function! s:CloseFloatingHoverOnBufEnter(win_id, bufnr) abort
+ let winnr = win_id2win(a:win_id)
+ if winnr == 0
+ " Float window was already closed
+ autocmd! nvim_termdebug_close_hover
+ return
+ endif
+ if winnr == winnr()
+ " Cursor is moving into floating window. Do not close it
+ return
+ endif
+ if bufnr('%') == a:bufnr
+ " When current buffer opened hover window, it's not another buffer. Skipped
+ return
+ endif
+ autocmd! nvim_termdebug_close_hover
+ call nvim_win_close(a:win_id, v:true)
+ endfunction
+
+" Open preview window. Window is open in:
+" - Floating window on Neovim (0.4.0 or later)
+" - Preview window on Neovim (0.3.0 or earlier) or Vim
+function! s:OpenHoverPreview(lines, filetype) abort
+ " Use local variable since parameter is not modifiable
+ let lines = a:lines
+ let bufnr = bufnr('%')
+
+ let use_float_win = s:ShouldUseFloatWindow()
+ if use_float_win
+ let pos = getpos('.')
+
+ " Calculate width and height
+ let width = 0
+ for index in range(len(lines))
+ let line = lines[index]
+ let lw = strdisplaywidth(line)
+ if lw > width
+ let width = lw
+ endif
+ let lines[index] = line
+ endfor
+
+ let height = len(lines)
+
+ " Calculate anchor
+ " Prefer North, but if there is no space, fallback into South
+ let bottom_line = line('w0') + winheight(0) - 1
+ if pos[1] + height <= bottom_line
+ let vert = 'N'
+ let row = 1
+ else
+ let vert = 'S'
+ let row = 0
+ endif
+
+ " Prefer West, but if there is no space, fallback into East
+ if pos[2] + width <= &columns
+ let hor = 'W'
+ let col = 0
+ else
+ let hor = 'E'
+ let col = 1
+ endif
+
+ let buf = nvim_create_buf(v:false, v:true)
+ call nvim_buf_set_lines(buf, 0, -1, v:true, lines)
+ " using v:true for second argument of nvim_open_win make the floating
+ " window disappear
+ let float_win_id = nvim_open_win(buf, v:false, {
+ \ 'relative': 'cursor',
+ \ 'anchor': vert . hor,
+ \ 'row': row,
+ \ 'col': col,
+ \ 'width': width,
+ \ 'height': height,
+ \ })
+ call nvim_win_set_option(float_win_id, 'relativenumber', v:false)
+ call nvim_win_set_option(float_win_id, 'signcolumn', 'no')
+ call nvim_win_set_option(float_win_id, 'signcolumn', 'no')
+ if a:filetype isnot v:null
+ call nvim_win_set_option(float_win_id, 'filetype', a:filetype)
+ endif
+
+ call nvim_buf_set_option(buf, 'modified', v:false)
+ call nvim_buf_set_option(buf, 'modifiable', v:false)
+
+ " Unlike preview window, :pclose does not close window. Instead, close
+ " hover window automatically when cursor is moved.
+ let call_after_move = printf('<SID>CloseFloatingHoverOnCursorMove(%d, %s)', float_win_id, string(pos))
+ let call_on_bufenter = printf('<SID>CloseFloatingHoverOnBufEnter(%d, %d)', float_win_id, bufnr)
+ augroup nvim_termdebug_close_hover
+ execute 'autocmd CursorMoved,CursorMovedI,InsertEnter <buffer> call ' . call_after_move
+ execute 'autocmd BufEnter * call ' . call_on_bufenter
+ augroup END
+ else
+ echomsg a:lines[0]
+ endif
+endfunction
+
+" Handle an error.
+func s:HandleError(msg)
+ if s:ignoreEvalError
+ " Result of s:SendEval() failed, ignore.
+ let s:ignoreEvalError = 0
+ let s:evalFromBalloonExpr = 0
+ return
+ endif
+ echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
+endfunc
+
+func s:GotoSourcewinOrCreateIt()
+ if !win_gotoid(s:sourcewin)
+ new
+ let s:sourcewin = win_getid(winnr())
+ call s:InstallWinbar()
+ endif
+endfunc
+
+" Handle stopping and running message from gdb.
+" Will update the sign that shows the current position.
+func s:HandleCursor(msg)
+ let wid = win_getid(winnr())
+
+ if a:msg =~ '^\*stopped'
+ "call ch_log('program stopped')
+ let s:stopped = 1
+ elseif a:msg =~ '^\*running'
+ "call ch_log('program running')
+ let s:stopped = 0
+ endif
+
+ if a:msg =~ 'fullname='
+ let fname = s:GetFullname(a:msg)
+ else
+ let fname = ''
+ endif
+ if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
+ let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
+ if lnum =~ '^[0-9]*$'
+ call s:GotoSourcewinOrCreateIt()
+ if expand('%:p') != fnamemodify(fname, ':p')
+ if &modified
+ " TODO: find existing window
+ exe 'split ' . fnameescape(fname)
+ let s:sourcewin = win_getid(winnr())
+ call s:InstallWinbar()
+ else
+ exe 'edit ' . fnameescape(fname)
+ endif
endif
+ exe lnum
+ exe 'sign unplace ' . s:pc_id
+ exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
+ setlocal signcolumn=yes
+ endif
+ elseif !s:stopped || fname != ''
+ exe 'sign unplace ' . s:pc_id
+ endif
+
+ call win_gotoid(wid)
+endfunc
+
+let s:BreakpointSigns = []
+
+func s:CreateBreakpoint(id, subid)
+ let nr = printf('%d.%d', a:id, a:subid)
+ if index(s:BreakpointSigns, nr) == -1
+ call add(s:BreakpointSigns, nr)
+ exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
+ endif
+endfunc
+
+func! s:SplitMsg(s)
+ return split(a:s, '{.\{-}}\zs')
+endfunction
+
+" Handle setting a breakpoint
+" Will update the sign that shows the breakpoint
+func s:HandleNewBreakpoint(msg)
+ if a:msg !~ 'fullname='
+ " a watch does not have a file name
+ return
+ endif
+ for msg in s:SplitMsg(a:msg)
+ let fname = s:GetFullname(msg)
+ if empty(fname)
+ continue
+ endif
+ let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
+ if empty(nr)
+ return
+ endif
+
+ " If "nr" is 123 it becomes "123.0" and subid is "0".
+ " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
+ let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
+ call s:CreateBreakpoint(id, subid)
+
+ if has_key(s:breakpoints, id)
+ let entries = s:breakpoints[id]
+ else
+ let entries = {}
+ let s:breakpoints[id] = entries
+ endif
+ if has_key(entries, subid)
+ let entry = entries[subid]
+ else
+ let entry = {}
+ let entries[subid] = entry
endif
+
+ let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
+ let entry['fname'] = fname
+ let entry['lnum'] = lnum
+
+ let bploc = printf('%s:%d', fname, lnum)
+ if !has_key(s:breakpoint_locations, bploc)
+ let s:breakpoint_locations[bploc] = []
+ endif
+ let s:breakpoint_locations[bploc] += [id]
+
+ if bufloaded(fname)
+ call s:PlaceSign(id, subid, entry)
+ endif
+ endfor
+endfunc
+
+func s:PlaceSign(id, subid, entry)
+ let nr = printf('%d.%d', a:id, a:subid)
+ exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname']
+ let a:entry['placed'] = 1
+endfunc
+
+" Handle deleting a breakpoint
+" Will remove the sign that shows the breakpoint
+func s:HandleBreakpointDelete(msg)
+ let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
+ if empty(id)
+ return
+ endif
+ if has_key(s:breakpoints, id)
+ for [subid, entry] in items(s:breakpoints[id])
+ if has_key(entry, 'placed')
+ exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
+ unlet entry['placed']
+ endif
+ endfor
+ unlet s:breakpoints[id]
+ endif
+endfunc
+
+" Handle the debugged program starting to run.
+" Will store the process ID in s:pid
+func s:HandleProgramRun(msg)
+ let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
+ if nr == 0
+ return
+ endif
+ let s:pid = nr
+ "call ch_log('Detected process ID: ' . s:pid)
+endfunc
+
+" Handle a BufRead autocommand event: place any signs.
+func s:BufRead()
+ let fname = expand('<afile>:p')
+ for [id, entries] in items(s:breakpoints)
+ for [subid, entry] in items(entries)
+ if entry['fname'] == fname
+ call s:PlaceSign(id, subid, entry)
+ endif
+ endfor
+ endfor
+endfunc
+
+" Handle a BufUnloaded autocommand event: unplace any signs.
+func s:BufUnloaded()
+ let fname = expand('<afile>:p')
+ for [id, entries] in items(s:breakpoints)
+ for [subid, entry] in items(entries)
+ if entry['fname'] == fname
+ let entry['placed'] = 0
+ endif
+ endfor
endfor
endfunc
+
diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim
index cb101ee9a7..6305eef734 100644
--- a/runtime/syntax/tutor.vim
+++ b/runtime/syntax/tutor.vim
@@ -49,29 +49,29 @@ syn match tutorInlineType /{\(normal\|vim\)}/ contained conceal
syn match tutorInlineOK /✓/
syn match tutorInlineX /✗/
-hi! tutorLink cterm=underline gui=underline ctermfg=lightblue guifg=#0088ff
-hi! link tutorLinkBands Delimiter
-hi! link tutorLinkAnchor Underlined
-hi! link tutorInternalAnchor Underlined
-hi! link tutorURL tutorLink
-hi! link tutorEmail tutorLink
+hi def tutorLink cterm=underline gui=underline ctermfg=lightblue guifg=#0088ff
+hi def link tutorLinkBands Delimiter
+hi def link tutorLinkAnchor Underlined
+hi def link tutorInternalAnchor Underlined
+hi def link tutorURL tutorLink
+hi def link tutorEmail tutorLink
-hi! link tutorSection Title
-hi! link tutorSectionBullet Delimiter
+hi def link tutorSection Title
+hi def link tutorSectionBullet Delimiter
-hi! link tutorTOC Directory
+hi def link tutorTOC Directory
-hi! tutorMarks cterm=bold gui=bold
+hi def tutorMarks cterm=bold gui=bold
-hi! tutorEmphasis gui=italic cterm=italic
-hi! tutorBold gui=bold cterm=bold
+hi def tutorEmphasis gui=italic cterm=italic
+hi def tutorBold gui=bold cterm=bold
-hi! link tutorExpect Special
-hi! tutorOK ctermfg=green guifg=#00ff88 cterm=bold gui=bold
-hi! tutorX ctermfg=red guifg=#ff2000 cterm=bold gui=bold
-hi! link tutorInlineOK tutorOK
-hi! link tutorInlineX tutorX
+hi def link tutorExpect Special
+hi def tutorOK ctermfg=green guifg=#00ff88 cterm=bold gui=bold
+hi def tutorX ctermfg=red guifg=#ff2000 cterm=bold gui=bold
+hi def link tutorInlineOK tutorOK
+hi def link tutorInlineX tutorX
-hi! link tutorShellPrompt Delimiter
+hi def link tutorShellPrompt Delimiter
let b:current_syntax = "tutor"
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
index 2b30ccb5f8..4e6154b24a 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -159,9 +159,9 @@ There is also some text missing here.
2. At the shell prompt type this command:
~~~ sh
- $ vim tutor
+ $ nvim tutor
~~~
- 'vim' is the command to start the Vim editor, 'tutor' is the name of
+ 'nvim' is the command to start the Nvim editor, 'tutor' is the name of
the file you wish to edit. Use a file that may be changed.
3. Insert and delete text as you learned in the previous lessons.
@@ -186,7 +186,7 @@ There is also some text missing here.
2. To start Vim from the shell prompt type:
~~~ sh
- $ vim FILENAME
+ $ nvim FILENAME
~~~
3. To exit Vim type: `<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} to trash
diff --git a/scripts/gen_api_vimdoc.py b/scripts/gen_api_vimdoc.py
deleted file mode 100755
index 515964bfe8..0000000000
--- a/scripts/gen_api_vimdoc.py
+++ /dev/null
@@ -1,563 +0,0 @@
-#!/usr/bin/env python3
-"""Parses Doxygen XML output to generate Neovim's API documentation.
-
-This would be easier using lxml and XSLT, but:
-
- 1. This should avoid needing Python dependencies, especially ones that are
- C modules that have library dependencies (lxml requires libxml and
- libxslt).
- 2. I wouldn't know how to deal with nested indentation in <para> tags using
- XSLT.
-
-Each function documentation is formatted with the following rules:
-
- - Maximum width of 78 characters (`text_width`).
- - Spaces for indentation.
- - Function signature and helptag are on the same line.
- - Helptag is right aligned.
- - Signature and helptag must have a minimum of 8 spaces between them.
- - If the signature is too long, it is placed on the line after the
- helptag. The signature wraps at `text_width - 8` characters with
- subsequent lines indented to the open parenthesis.
- - Documentation body will be indented by 16 spaces.
- - Subsection bodies are indented an additional 4 spaces.
- - Documentation body consists of the function description, parameter details,
- return description, and C declaration.
- - Parameters are omitted for the `void` and `Error *` types, or if the
- parameter is marked as [out].
- - Each function documentation is separated by a single line.
-
-The C declaration is added to the end to show actual argument types.
-"""
-import os
-import re
-import sys
-import shutil
-import textwrap
-import subprocess
-
-from xml.dom import minidom
-
-if sys.version_info[0] < 3:
- print("use Python 3")
- sys.exit(1)
-
-doc_filename = 'api.txt'
-# String used to find the start of the generated part of the doc.
-section_start_token = '*api-global*'
-# Required prefix for API function names.
-api_func_name_prefix = 'nvim_'
-
-# Section name overrides.
-section_name = {
- 'vim.c': 'Global',
-}
-
-# Section ordering.
-section_order = (
- 'vim.c',
- 'buffer.c',
- 'window.c',
- 'tabpage.c',
- 'ui.c',
-)
-
-param_exclude = (
- 'channel_id',
-)
-
-# Annotations are displayed as line items after API function descriptions.
-annotation_map = {
- 'FUNC_API_ASYNC': '{async}',
-}
-
-text_width = 78
-script_path = os.path.abspath(__file__)
-base_dir = os.path.dirname(os.path.dirname(script_path))
-src_dir = os.path.join(base_dir, 'src/nvim/api')
-out_dir = os.path.join(base_dir, 'tmp-api-doc')
-filter_cmd = '%s %s' % (sys.executable, script_path)
-seen_funcs = set()
-
-# Tracks `xrefsect` titles. As of this writing, used only for separating
-# deprecated functions.
-xrefs = set()
-
-
-# XML Parsing Utilities {{{
-def find_first(parent, name):
- """Finds the first matching node within parent."""
- sub = parent.getElementsByTagName(name)
- if not sub:
- return None
- return sub[0]
-
-
-def get_children(parent, name):
- """Yield matching child nodes within parent."""
- for child in parent.childNodes:
- if child.nodeType == child.ELEMENT_NODE and child.nodeName == name:
- yield child
-
-
-def get_child(parent, name):
- """Get the first matching child node."""
- for child in get_children(parent, name):
- return child
- return None
-
-
-def clean_text(text):
- """Cleans text.
-
- Only cleans superfluous whitespace at the moment.
- """
- return ' '.join(text.split()).strip()
-
-
-def clean_lines(text):
- """Removes superfluous lines.
-
- The beginning and end of the string is trimmed. Empty lines are collapsed.
- """
- return re.sub(r'\A\n\s*\n*|\n\s*\n*\Z', '', re.sub(r'(\n\s*\n+)+', '\n\n', text))
-
-
-def get_text(parent):
- """Combine all text in a node."""
- if parent.nodeType == parent.TEXT_NODE:
- return parent.data
-
- out = ''
- for node in parent.childNodes:
- if node.nodeType == node.TEXT_NODE:
- out += clean_text(node.data)
- elif node.nodeType == node.ELEMENT_NODE:
- out += ' ' + get_text(node)
- return out
-
-
-def doc_wrap(text, prefix='', width=70, func=False):
- """Wraps text to `width`.
-
- The first line is prefixed with `prefix`, and subsequent lines are aligned.
- If `func` is True, only wrap at commas.
- """
- if not width:
- return text
-
- indent_space = ' ' * len(prefix)
-
- if func:
- lines = [prefix]
- for part in text.split(', '):
- if part[-1] not in ');':
- part += ', '
- if len(lines[-1]) + len(part) > width:
- lines.append(indent_space)
- lines[-1] += part
- return '\n'.join(x.rstrip() for x in lines).rstrip()
-
- tw = textwrap.TextWrapper(break_long_words = False,
- break_on_hyphens = False,
- width=width,
- initial_indent=prefix,
- subsequent_indent=indent_space)
- return '\n'.join(tw.wrap(text.strip()))
-
-
-def parse_params(parent, width=62):
- """Parse Doxygen `parameterlist`."""
- name_length = 0
- items = []
- for child in parent.childNodes:
- if child.nodeType == child.TEXT_NODE:
- continue
-
- name_node = find_first(child, 'parametername')
- if name_node.getAttribute('direction') == 'out':
- continue
-
- name = get_text(name_node)
- if name in param_exclude:
- continue
-
- name = '{%s}' % name
- name_length = max(name_length, len(name) + 2)
-
- desc = ''
- desc_node = get_child(child, 'parameterdescription')
- if desc_node:
- desc = parse_parblock(desc_node, width=None)
- items.append((name.strip(), desc.strip()))
-
- out = 'Parameters: ~\n'
- for name, desc in items:
- name = ' %s' % name.ljust(name_length)
- out += doc_wrap(desc, prefix=name, width=width) + '\n'
- return out.strip()
-
-
-def parse_para(parent, width=62):
- """Parse doxygen `para` tag.
-
- I assume <para> is a paragraph block or "a block of text". It can contain
- text nodes, or other tags.
- """
- line = ''
- lines = []
- for child in parent.childNodes:
- if child.nodeType == child.TEXT_NODE:
- line += child.data
- elif child.nodeName == 'computeroutput':
- line += '`%s`' % get_text(child)
- else:
- if line:
- lines.append(doc_wrap(line, width=width))
- line = ''
-
- if child.nodeName == 'parameterlist':
- lines.append(parse_params(child, width=width))
- elif child.nodeName == 'xrefsect':
- title = get_text(get_child(child, 'xreftitle'))
- xrefs.add(title)
- xrefdesc = parse_para(get_child(child, 'xrefdescription'))
- lines.append(doc_wrap(xrefdesc, prefix='%s: ' % title,
- width=width) + '\n')
- elif child.nodeName == 'simplesect':
- kind = child.getAttribute('kind')
- if kind == 'note':
- lines.append('Note:')
- lines.append(doc_wrap(parse_para(child),
- prefix=' ',
- width=width))
- elif kind == 'return':
- lines.append('%s: ~' % kind.title())
- lines.append(doc_wrap(parse_para(child),
- prefix=' ',
- width=width))
- else:
- lines.append(get_text(child))
-
- if line:
- lines.append(doc_wrap(line, width=width))
- return clean_lines('\n'.join(lines).strip())
-
-
-def parse_parblock(parent, width=62):
- """Parses a nested block of `para` tags.
-
- Named after the \parblock command, but not directly related.
- """
- paragraphs = []
- for child in parent.childNodes:
- if child.nodeType == child.TEXT_NODE:
- paragraphs.append(doc_wrap(child.data, width=width))
- elif child.nodeName == 'para':
- paragraphs.append(parse_para(child, width=width))
- else:
- paragraphs.append(doc_wrap(get_text(child), width=width))
- paragraphs.append('')
- return clean_lines('\n'.join(paragraphs).strip())
-# }}}
-
-
-def parse_source_xml(filename):
- """Collects API functions.
-
- Returns two strings:
- 1. API functions
- 2. Deprecated API functions
-
- Caller decides what to do with the deprecated documentation.
- """
- global xrefs
- xrefs = set()
- functions = []
- deprecated_functions = []
-
- dom = minidom.parse(filename)
- for member in dom.getElementsByTagName('memberdef'):
- if member.getAttribute('static') == 'yes' or \
- member.getAttribute('kind') != 'function':
- continue
-
- loc = find_first(member, 'location')
- if 'private' in loc.getAttribute('file'):
- continue
-
- return_type = get_text(get_child(member, 'type'))
- if return_type == '':
- continue
-
- if return_type.startswith(('ArrayOf', 'DictionaryOf')):
- parts = return_type.strip('_').split('_')
- return_type = '%s(%s)' % (parts[0], ', '.join(parts[1:]))
-
- name = get_text(get_child(member, 'name'))
-
- annotations = get_text(get_child(member, 'argsstring'))
- if annotations and ')' in annotations:
- annotations = annotations.rsplit(')', 1)[-1].strip()
- # XXX: (doxygen 1.8.11) 'argsstring' only includes attributes of
- # non-void functions. Special-case void functions here.
- if name == 'nvim_get_mode' and len(annotations) == 0:
- annotations += 'FUNC_API_ASYNC'
- annotations = filter(None, map(lambda x: annotation_map.get(x),
- annotations.split()))
-
- vimtag = '*%s()*' % name
- args = []
- type_length = 0
-
- for param in get_children(member, 'param'):
- arg_type = get_text(get_child(param, 'type')).strip()
- arg_name = ''
- declname = get_child(param, 'declname')
- if declname:
- arg_name = get_text(declname).strip()
-
- if arg_name in param_exclude:
- continue
-
- if arg_type.endswith('*'):
- arg_type = arg_type.strip('* ')
- arg_name = '*' + arg_name
- type_length = max(type_length, len(arg_type))
- args.append((arg_type, arg_name))
-
- c_args = []
- for arg_type, arg_name in args:
- c_args.append(' ' + (
- '%s %s' % (arg_type.ljust(type_length), arg_name)).strip())
-
- c_decl = textwrap.indent('%s %s(\n%s\n);' % (return_type, name,
- ',\n'.join(c_args)),
- ' ')
-
- prefix = '%s(' % name
- suffix = '%s)' % ', '.join('{%s}' % a[1] for a in args
- if a[0] not in ('void', 'Error'))
-
- # Minimum 8 chars between signature and vimtag
- lhs = (text_width - 8) - len(prefix)
-
- if len(prefix) + len(suffix) > lhs:
- signature = vimtag.rjust(text_width) + '\n'
- signature += doc_wrap(suffix, width=text_width-8, prefix=prefix,
- func=True)
- else:
- signature = prefix + suffix
- signature += vimtag.rjust(text_width - len(signature))
-
- doc = ''
- desc = find_first(member, 'detaileddescription')
- if desc:
- doc = parse_parblock(desc)
- if 'DEBUG' in os.environ:
- print(textwrap.indent(
- re.sub(r'\n\s*\n+', '\n',
- desc.toprettyxml(indent=' ', newl='\n')), ' ' * 16))
-
- if not doc:
- doc = 'TODO: Documentation'
-
- annotations = '\n'.join(annotations)
- if annotations:
- annotations = ('\n\nAttributes: ~\n' +
- textwrap.indent(annotations, ' '))
- i = doc.rfind('Parameters: ~')
- if i == -1:
- doc += annotations
- else:
- doc = doc[:i] + annotations + '\n\n' + doc[i:]
-
- if 'INCLUDE_C_DECL' in os.environ:
- doc += '\n\nC Declaration: ~\n>\n'
- doc += c_decl
- doc += '\n<'
-
- func_doc = signature + '\n'
- func_doc += textwrap.indent(clean_lines(doc), ' ' * 16)
- func_doc = re.sub(r'^\s+([<>])$', r'\1', func_doc, flags=re.M)
-
- if 'Deprecated' in xrefs:
- deprecated_functions.append(func_doc)
- elif name.startswith(api_func_name_prefix):
- functions.append(func_doc)
-
- xrefs.clear()
-
- return '\n\n'.join(functions), '\n\n'.join(deprecated_functions)
-
-
-def delete_lines_below(filename, tokenstr):
- """Deletes all lines below the line containing `tokenstr`, the line itself,
- and one line above it.
- """
- lines = open(filename).readlines()
- i = 0
- for i, line in enumerate(lines, 1):
- if tokenstr in line:
- break
- i = max(0, i - 2)
- with open(filename, 'wt') as fp:
- fp.writelines(lines[0:i])
-
-def gen_docs(config):
- """Generate documentation.
-
- Doxygen is called and configured through stdin.
- """
- p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE)
- p.communicate(config.format(input=src_dir, output=out_dir,
- filter=filter_cmd).encode('utf8'))
- if p.returncode:
- sys.exit(p.returncode)
-
- sections = {}
- intros = {}
- sep = '=' * text_width
-
- base = os.path.join(out_dir, 'xml')
- dom = minidom.parse(os.path.join(base, 'index.xml'))
-
- # generate docs for section intros
- for compound in dom.getElementsByTagName('compound'):
- if compound.getAttribute('kind') != 'group':
- continue
-
- groupname = get_text(find_first(compound, 'name'))
- groupxml = os.path.join(base, '%s.xml' % compound.getAttribute('refid'))
-
- desc = find_first(minidom.parse(groupxml), 'detaileddescription')
- if desc:
- doc = parse_parblock(desc)
- if doc:
- intros[groupname] = doc
-
- for compound in dom.getElementsByTagName('compound'):
- if compound.getAttribute('kind') != 'file':
- continue
-
- filename = get_text(find_first(compound, 'name'))
- if filename.endswith('.c'):
- functions, deprecated = parse_source_xml(
- os.path.join(base, '%s.xml' % compound.getAttribute('refid')))
-
- if not functions and not deprecated:
- continue
-
- if functions or deprecated:
- name = os.path.splitext(os.path.basename(filename))[0]
- if name == 'ui':
- name = name.upper()
- else:
- name = name.title()
-
- doc = ''
-
- intro = intros.get('api-%s' % name.lower())
- if intro:
- doc += '\n\n' + intro
-
- if functions:
- doc += '\n\n' + functions
-
- if 'INCLUDE_DEPRECATED' in os.environ and deprecated:
- doc += '\n\n\nDeprecated %s Functions: ~\n\n' % name
- doc += deprecated
-
- if doc:
- filename = os.path.basename(filename)
- name = section_name.get(filename, name)
- title = '%s Functions' % name
- helptag = '*api-%s*' % name.lower()
- sections[filename] = (title, helptag, doc)
-
- if not sections:
- return
-
- docs = ''
-
- i = 0
- for filename in section_order:
- if filename not in sections:
- continue
- title, helptag, section_doc = sections.pop(filename)
-
- i += 1
- docs += sep
- docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title)))
- docs += section_doc
- docs += '\n\n\n'
-
- if sections:
- # In case new API sources are added without updating the order dict.
- for title, helptag, section_doc in sections.values():
- i += 1
- docs += sep
- docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title)))
- docs += section_doc
- docs += '\n\n\n'
-
- docs = docs.rstrip() + '\n\n'
- docs += ' vim:tw=78:ts=8:ft=help:norl:\n'
-
- doc_file = os.path.join(base_dir, 'runtime/doc', doc_filename)
- delete_lines_below(doc_file, section_start_token)
- with open(doc_file, 'ab') as fp:
- fp.write(docs.encode('utf8'))
- shutil.rmtree(out_dir)
-
-
-def filter_source(filename):
- """Filters the source to fix macros that confuse Doxygen."""
- with open(filename, 'rt') as fp:
- print(re.sub(r'^(ArrayOf|DictionaryOf)(\(.*?\))',
- lambda m: m.group(1)+'_'.join(
- re.split(r'[^\w]+', m.group(2))),
- fp.read(), flags=re.M))
-
-
-# Doxygen Config {{{
-Doxyfile = '''
-OUTPUT_DIRECTORY = {output}
-INPUT = {input}
-INPUT_ENCODING = UTF-8
-FILE_PATTERNS = *.h *.c
-RECURSIVE = YES
-INPUT_FILTER = "{filter}"
-EXCLUDE =
-EXCLUDE_SYMLINKS = NO
-EXCLUDE_PATTERNS = */private/*
-EXCLUDE_SYMBOLS =
-
-GENERATE_HTML = NO
-GENERATE_DOCSET = NO
-GENERATE_HTMLHELP = NO
-GENERATE_QHP = NO
-GENERATE_TREEVIEW = NO
-GENERATE_LATEX = NO
-GENERATE_RTF = NO
-GENERATE_MAN = NO
-GENERATE_DOCBOOK = NO
-GENERATE_AUTOGEN_DEF = NO
-
-GENERATE_XML = YES
-XML_OUTPUT = xml
-XML_PROGRAMLISTING = NO
-
-ENABLE_PREPROCESSING = YES
-MACRO_EXPANSION = YES
-EXPAND_ONLY_PREDEF = NO
-'''
-# }}}
-
-if __name__ == "__main__":
- if len(sys.argv) > 1:
- filter_source(sys.argv[1])
- else:
- gen_docs(Doxyfile)
-
-# vim: set ft=python ts=4 sw=4 tw=79 et fdm=marker :
diff --git a/scripts/gen_help_html.py b/scripts/gen_help_html.py
index 6cca81d250..dbdeb3c162 100644
--- a/scripts/gen_help_html.py
+++ b/scripts/gen_help_html.py
@@ -1,5 +1,10 @@
# Converts Vim/Nvim documentation to HTML.
#
+# USAGE:
+# 1. python3 scripts/gen_help_html.py runtime/doc/ ~/neovim.github.io/t/
+# 3. cd ~/neovim.github.io/ && jekyll serve --host 0.0.0.0
+# 2. Visit http://localhost:4000/t/help.txt.html
+#
# Adapted from https://github.com/c4rlo/vimhelp/
# License: MIT
#
@@ -23,7 +28,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-import re, urllib.parse
+import os
+import re
+import urllib.parse
+import datetime
+import sys
from itertools import chain
HEAD = """\
@@ -32,6 +41,17 @@ HEAD = """\
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset={encoding}"/>
+<style>
+.h {{
+ font-weight: bold;
+}}
+h1 {{
+ font-family: sans-serif;
+}}
+pre {{
+ font-family: sans-serif;
+}}
+</style>
<title>Nvim: {filename}</title>
"""
@@ -39,22 +59,19 @@ HEAD_END = '</head>\n<body>\n'
INTRO = """
<h1>Nvim help files</h1>
-<p>HTML export of the <a href="https://neovim.io/">Nvim</a> help pages{vers-note}.
-Updated <a href="https://github.com/neovim/bot-ci" class="d">automatically</a> from the <a
-href="https://github.com/vim/vim/tree/master/runtime/doc" class="d">Nvim source repository</a>.
-Also includes the <a href="vim_faq.txt.html">Vim FAQ</a>, pulled from its
-<a href="https://github.com/chrisbra/vim_faq" class="d">source repository</a>.</p>
+<p>
+<a href="https://neovim.io/">Nvim</a> help pages{vers-note}.
+Updated <a href="https://github.com/neovim/bot-ci" class="d">automatically</a>
+from the <a href="https://github.com/neovim/neovim" class="d">Nvim source</a>.
+</p>
"""
-VERSION_NOTE = ", current as of Vim {version}"
+VERSION_NOTE = ", current as of Nvim {version}"
SITENAVI_LINKS = """
-Quick links:
-<a href="/">help overview</a> &middot;
-<a href="quickref.txt.html">quick reference</a> &middot;
-<a href="usr_toc.txt.html">user manual toc</a> &middot;
-<a href="{helptxt}#reference_toc">reference manual toc</a> &middot;
-<a href="vim_faq.txt.html">faq</a>
+<a href="quickref.txt.html">Quick reference</a> &middot;
+<a href="usr_toc.txt.html">User manual</a> &middot;
+<a href="{helptxt}#reference_toc">Reference manual</a> &middot;
"""
SITENAVI_LINKS_PLAIN = SITENAVI_LINKS.format(helptxt='help.txt.html')
@@ -77,15 +94,14 @@ TEXTSTART = """
FOOTER = '</pre>'
FOOTER2 = """
-<p id="footer">This site is maintained by Carlo Teubner (<i>(my first name) dot (my last name) at gmail dot com</i>).</p>
+<p id="footer">Generated {generated_date} from <code>{commit}</code></p>
</div>
</div>
</body>
</html>
-"""
-
-VIM_FAQ_LINE = '<a href="vim_faq.txt.html#vim_faq.txt" class="l">' \
- 'vim_faq.txt</a> Frequently Asked Questions\n'
+""".format(
+ generated_date='{0:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()),
+ commit='?')
RE_TAGLINE = re.compile(r'(\S+)\s+(\S+)')
@@ -127,6 +143,8 @@ RE_TAGWORD = re.compile(
PAT_URL + '|' +
PAT_WORD)
RE_NEWLINE = re.compile(r'[\r\n]')
+# H1 header "=====…"
+# H2 header "-----…"
RE_HRULE = re.compile(r'[-=]{3,}.*[-=]{3,3}$')
RE_EG_START = re.compile(r'(?:.* )?>$')
RE_EG_END = re.compile(r'\S')
@@ -211,13 +229,20 @@ class VimH2H(object):
inexample = 0
filename = str(filename)
is_help_txt = (filename == 'help.txt')
- faq_line = False
+ last = ''
for line in RE_NEWLINE.split(contents):
line = line.rstrip('\r\n')
line_tabs = line
line = line.expandtabs()
+ if last == 'h1':
+ out.extend(('</pre>')) # XXX
+ out.extend(('<h1>', line.rstrip(), '</h1>\n'))
+ out.extend(('<pre>'))
+ last = ''
+ continue
if RE_HRULE.match(line):
- out.extend(('<span class="h">', line, '</span>\n'))
+ # out.extend(('<span class="h">', line, '</span>\n'))
+ last = 'h1'
continue
if inexample == 2:
if RE_EG_END.match(line):
@@ -234,8 +259,6 @@ class VimH2H(object):
m = RE_SECTION.match(line)
out.extend((r'<span class="c">', m.group(0), r'</span>'))
line = line[m.end():]
- if is_help_txt and RE_LOCAL_ADD.match(line_tabs):
- faq_line = True
lastpos = 0
for match in RE_TAGWORD.finditer(line):
pos = match.start()
@@ -278,9 +301,6 @@ class VimH2H(object):
out.append(html_escape[line[lastpos:]])
out.append('\n')
if inexample == 1: inexample = 2
- if faq_line:
- out.append(VIM_FAQ_LINE)
- faq_line = False
header = []
header.append(HEAD.format(encoding=encoding, filename=filename))
@@ -310,9 +330,6 @@ html_escape = HtmlEscCache()
-import sys, os, os.path
-#import cProfile
-sys.path.append('.')
def slurp(filename):
try:
@@ -352,4 +369,3 @@ def main():
of.close()
main()
-#cProfile.run('main()')
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
new file mode 100755
index 0000000000..e491ff59b0
--- /dev/null
+++ b/scripts/gen_vimdoc.py
@@ -0,0 +1,763 @@
+#!/usr/bin/env python3
+"""Generates Nvim help docs from C docstrings, by parsing Doxygen XML.
+
+This would be easier using lxml and XSLT, but:
+
+ 1. This should avoid needing Python dependencies, especially ones that are
+ C modules that have library dependencies (lxml requires libxml and
+ libxslt).
+ 2. I wouldn't know how to deal with nested indentation in <para> tags using
+ XSLT.
+
+Each function documentation is formatted with the following rules:
+
+ - Maximum width of 78 characters (`text_width`).
+ - Spaces for indentation.
+ - Function signature and helptag are on the same line.
+ - Helptag is right aligned.
+ - Signature and helptag must have a minimum of 8 spaces between them.
+ - If the signature is too long, it is placed on the line after the
+ helptag. The signature wraps at `text_width - 8` characters with
+ subsequent lines indented to the open parenthesis.
+ - Documentation body will be indented by 16 spaces.
+ - Subsection bodies are indented an additional 4 spaces.
+ - Documentation body consists of the function description, parameter details,
+ return description, and C declaration.
+ - Parameters are omitted for the `void` and `Error *` types, or if the
+ parameter is marked as [out].
+ - Each function documentation is separated by a single line.
+
+The C declaration is added to the end to show actual argument types.
+"""
+import os
+import re
+import sys
+import shutil
+import textwrap
+import subprocess
+import collections
+import pprint
+
+from xml.dom import minidom
+
+if sys.version_info[0] < 3:
+ print("use Python 3")
+ sys.exit(1)
+
+DEBUG = ('DEBUG' in os.environ)
+INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
+INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
+
+text_width = 78
+script_path = os.path.abspath(__file__)
+base_dir = os.path.dirname(os.path.dirname(script_path))
+out_dir = os.path.join(base_dir, 'tmp-{mode}-doc')
+filter_cmd = '%s %s' % (sys.executable, script_path)
+seen_funcs = set()
+lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter')
+
+CONFIG = {
+ 'api': {
+ 'filename': 'api.txt',
+ # String used to find the start of the generated part of the doc.
+ 'section_start_token': '*api-global*',
+ # Section ordering.
+ 'section_order' : [
+ 'vim.c',
+ 'buffer.c',
+ 'window.c',
+ 'tabpage.c',
+ 'ui.c',
+ ],
+ # List of files/directories for doxygen to read, separated by blanks
+ 'files': os.path.join(base_dir, 'src/nvim/api'),
+ # file patterns used by doxygen
+ 'file_patterns': '*.h *.c',
+ # Only function with this prefix are considered
+ 'func_name_prefix': 'nvim_',
+ # Section name overrides.
+ 'section_name': {
+ 'vim.c': 'Global',
+ },
+ # Module name overrides (for Lua).
+ 'module_override': {},
+ # Append the docs for these modules, do not start a new section.
+ 'append_only' : [],
+ },
+ 'lua': {
+ 'filename': 'if_lua.txt',
+ 'section_start_token': '*lua-vim*',
+ 'section_order' : [
+ 'vim.lua',
+ 'shared.lua',
+ ],
+ 'files': ' '.join([
+ os.path.join(base_dir, 'src/nvim/lua/vim.lua'),
+ os.path.join(base_dir, 'runtime/lua/vim/shared.lua'),
+ ]),
+ 'file_patterns': '*.lua',
+ 'func_name_prefix': '',
+ 'section_name': {},
+ 'module_override': {
+ 'shared': 'vim', # `shared` functions are exposed on the `vim` module.
+ },
+ 'append_only' : [
+ 'shared.lua',
+ ],
+ },
+}
+
+param_exclude = (
+ 'channel_id',
+)
+
+# Annotations are displayed as line items after API function descriptions.
+annotation_map = {
+ 'FUNC_API_ASYNC': '{async}',
+}
+
+
+# Tracks `xrefsect` titles. As of this writing, used only for separating
+# deprecated functions.
+xrefs = set()
+
+def debug_this(s, n):
+ o = n if isinstance(n, str) else n.toprettyxml(indent=' ', newl='\n')
+ name = '' if isinstance(n, str) else n.nodeName
+ if s in o:
+ raise RuntimeError('xxx: {}\n{}'.format(name, o))
+
+
+# XML Parsing Utilities {{{
+def find_first(parent, name):
+ """Finds the first matching node within parent."""
+ sub = parent.getElementsByTagName(name)
+ if not sub:
+ return None
+ return sub[0]
+
+
+def get_children(parent, name):
+ """Yield matching child nodes within parent."""
+ for child in parent.childNodes:
+ if child.nodeType == child.ELEMENT_NODE and child.nodeName == name:
+ yield child
+
+
+def get_child(parent, name):
+ """Get the first matching child node."""
+ for child in get_children(parent, name):
+ return child
+ return None
+
+
+def clean_text(text):
+ """Cleans text.
+
+ Only cleans superfluous whitespace at the moment.
+ """
+ return ' '.join(text.split()).strip()
+
+
+def clean_lines(text):
+ """Removes superfluous lines.
+
+ The beginning and end of the string is trimmed. Empty lines are collapsed.
+ """
+ return re.sub(r'\A\n\s*\n*|\n\s*\n*\Z', '', re.sub(r'(\n\s*\n+)+', '\n\n', text))
+
+
+def is_blank(text):
+ return '' == clean_lines(text)
+
+
+def get_text(parent, preformatted=False):
+ """Combine all text in a node."""
+ if parent.nodeType == parent.TEXT_NODE:
+ return parent.data
+
+ out = ''
+ for node in parent.childNodes:
+ if node.nodeType == node.TEXT_NODE:
+ out += node.data if preformatted else clean_text(node.data)
+ elif node.nodeType == node.ELEMENT_NODE:
+ out += ' ' + get_text(node, preformatted)
+ return out
+
+
+# Gets the length of the last line in `text`, excluding newline ("\n") char.
+def len_lastline(text):
+ lastnl = text.rfind('\n')
+ if -1 == lastnl:
+ return len(text)
+ if '\n' == text[-1]:
+ return lastnl - (1+ text.rfind('\n', 0, lastnl))
+ return len(text) - (1 + lastnl)
+
+
+def len_lastline_withoutindent(text, indent):
+ n = len_lastline(text)
+ return (n - len(indent)) if n > len(indent) else 0
+
+
+# Returns True if node `n` contains only inline (not block-level) elements.
+def is_inline(n):
+ for c in n.childNodes:
+ if c.nodeType != c.TEXT_NODE and c.nodeName != 'computeroutput':
+ return False
+ if not is_inline(c):
+ return False
+ return True
+
+def doc_wrap(text, prefix='', width=70, func=False, indent=None):
+ """Wraps text to `width`.
+
+ First line is prefixed with `prefix`, subsequent lines are aligned.
+ If `func` is True, only wrap at commas.
+ """
+ if not width:
+ # return prefix + text
+ return text
+
+ # Whitespace used to indent all lines except the first line.
+ indent = ' ' * len(prefix) if indent is None else indent
+ indent_only = (prefix == '' and indent is not None)
+
+ if func:
+ lines = [prefix]
+ for part in text.split(', '):
+ if part[-1] not in ');':
+ part += ', '
+ if len(lines[-1]) + len(part) > width:
+ lines.append(indent)
+ lines[-1] += part
+ return '\n'.join(x.rstrip() for x in lines).rstrip()
+
+ # XXX: Dummy prefix to force TextWrapper() to wrap the first line.
+ if indent_only:
+ prefix = indent
+
+ tw = textwrap.TextWrapper(break_long_words = False,
+ break_on_hyphens = False,
+ width=width,
+ initial_indent=prefix,
+ subsequent_indent=indent)
+ result = '\n'.join(tw.wrap(text.strip()))
+
+ # XXX: Remove the dummy prefix.
+ if indent_only:
+ result = result[len(indent):]
+
+ return result
+
+
+def has_nonexcluded_params(nodes):
+ """Returns true if any of the given <parameterlist> elements has at least
+ one non-excluded item."""
+ for n in nodes:
+ if render_params(n) != '':
+ return True
+
+
+def render_params(parent, width=62):
+ """Renders Doxygen <parameterlist> tag as Vim help text."""
+ name_length = 0
+ items = []
+ for node in parent.childNodes:
+ if node.nodeType == node.TEXT_NODE:
+ continue
+
+ name_node = find_first(node, 'parametername')
+ if name_node.getAttribute('direction') == 'out':
+ continue
+
+ name = get_text(name_node)
+ if name in param_exclude:
+ continue
+
+ name = '{%s}' % name
+ name_length = max(name_length, len(name) + 2)
+ items.append((name.strip(), node))
+
+ out = ''
+ for name, node in items:
+ name = ' {}'.format(name.ljust(name_length))
+
+ desc = ''
+ desc_node = get_child(node, 'parameterdescription')
+ if desc_node:
+ desc = parse_parblock(desc_node, width=width,
+ indent=(' ' * len(name)))
+
+ out += '{}{}\n'.format(name, desc)
+ return out.rstrip()
+
+# Renders a node as Vim help text, recursively traversing all descendants.
+def render_node(n, text, prefix='', indent='', width=62):
+ text = ''
+ # space_preceding = (len(text) > 0 and ' ' == text[-1][-1])
+ # text += (int(not space_preceding) * ' ')
+
+ if n.nodeType == n.TEXT_NODE:
+ # `prefix` is NOT sent to doc_wrap, it was already handled by now.
+ text += doc_wrap(n.data, indent=indent, width=width)
+ elif n.nodeName == 'computeroutput':
+ text += ' `{}` '.format(get_text(n))
+ elif n.nodeName == 'preformatted':
+ o = get_text(n, preformatted=True)
+ ensure_nl = '' if o[-1] == '\n' else '\n'
+ text += ' >{}{}\n<'.format(ensure_nl, o)
+ elif is_inline(n):
+ for c in n.childNodes:
+ text += render_node(c, text)
+ text = doc_wrap(text, indent=indent, width=width)
+ elif n.nodeName == 'verbatim':
+ # TODO: currently we don't use this. The "[verbatim]" hint is there as
+ # a reminder that we must decide how to format this if we do use it.
+ text += ' [verbatim] {}'.format(get_text(n))
+ elif n.nodeName == 'listitem':
+ for c in n.childNodes:
+ text += indent + prefix + render_node(c, text, indent=indent+(' ' * len(prefix)), width=width)
+ elif n.nodeName in ('para', 'heading'):
+ for c in n.childNodes:
+ text += render_node(c, text, indent=indent, width=width)
+ if is_inline(n):
+ text = doc_wrap(text, indent=indent, width=width)
+ elif n.nodeName == 'itemizedlist':
+ for c in n.childNodes:
+ text += '{}\n'.format(render_node(c, text, prefix='- ',
+ indent=indent, width=width))
+ elif n.nodeName == 'orderedlist':
+ i = 1
+ for c in n.childNodes:
+ if is_blank(get_text(c)):
+ text += '\n'
+ continue
+ text += '{}\n'.format(render_node(c, text, prefix='{}. '.format(i),
+ indent=indent, width=width))
+ i = i + 1
+ elif n.nodeName == 'simplesect' and 'note' == n.getAttribute('kind'):
+ text += 'Note:\n '
+ for c in n.childNodes:
+ text += render_node(c, text, indent=' ', width=width)
+ text += '\n'
+ elif n.nodeName == 'simplesect' and 'warning' == n.getAttribute('kind'):
+ text += 'Warning:\n '
+ for c in n.childNodes:
+ text += render_node(c, text, indent=' ', width=width)
+ text += '\n'
+ elif (n.nodeName == 'simplesect'
+ and n.getAttribute('kind') in ('return', 'see')):
+ text += ' '
+ for c in n.childNodes:
+ text += render_node(c, text, indent=' ', width=width)
+ else:
+ raise RuntimeError('unhandled node type: {}\n{}'.format(
+ n.nodeName, n.toprettyxml(indent=' ', newl='\n')))
+ return text
+
+def render_para(parent, indent='', width=62):
+ """Renders Doxygen <para> containing arbitrary nodes.
+
+ NB: Blank lines in a docstring manifest as <para> tags.
+ """
+ if is_inline(parent):
+ return clean_lines(doc_wrap(render_node(parent, ''),
+ indent=indent, width=width).strip())
+
+ # Ordered dict of ordered lists.
+ groups = collections.OrderedDict([
+ ('params', []),
+ ('return', []),
+ ('seealso', []),
+ ('xrefs', []),
+ ])
+
+ # Gather nodes into groups. Mostly this is because we want "parameterlist"
+ # nodes to appear together.
+ text = ''
+ kind = ''
+ last = ''
+ for child in parent.childNodes:
+ if child.nodeName == 'parameterlist':
+ groups['params'].append(child)
+ elif child.nodeName == 'xrefsect':
+ groups['xrefs'].append(child)
+ elif child.nodeName == 'simplesect':
+ last = kind
+ kind = child.getAttribute('kind')
+ if kind == 'return' or (kind == 'note' and last == 'return'):
+ groups['return'].append(child)
+ elif kind == 'see':
+ groups['seealso'].append(child)
+ elif kind in ('note', 'warning'):
+ text += render_node(child, text, indent=indent, width=width)
+ else:
+ raise RuntimeError('unhandled simplesect: {}\n{}'.format(
+ child.nodeName, child.toprettyxml(indent=' ', newl='\n')))
+ else:
+ text += render_node(child, text, indent=indent, width=width)
+
+ chunks = [text]
+ # Generate text from the gathered items.
+ if len(groups['params']) > 0 and has_nonexcluded_params(groups['params']):
+ chunks.append('\nParameters: ~')
+ for child in groups['params']:
+ chunks.append(render_params(child, width=width))
+ if len(groups['return']) > 0:
+ chunks.append('\nReturn: ~')
+ for child in groups['return']:
+ chunks.append(render_node(child, chunks[-1][-1], indent=indent, width=width))
+ if len(groups['seealso']) > 0:
+ chunks.append('\nSee also: ~')
+ for child in groups['seealso']:
+ chunks.append(render_node(child, chunks[-1][-1], indent=indent, width=width))
+ for child in groups['xrefs']:
+ title = get_text(get_child(child, 'xreftitle'))
+ xrefs.add(title)
+ xrefdesc = render_para(get_child(child, 'xrefdescription'), width=width)
+ chunks.append(doc_wrap(xrefdesc, prefix='{}: '.format(title),
+ width=width) + '\n')
+
+ return clean_lines('\n'.join(chunks).strip())
+
+
+def parse_parblock(parent, prefix='', width=62, indent=''):
+ """Renders a nested block of <para> tags as Vim help text."""
+ paragraphs = []
+ for child in parent.childNodes:
+ paragraphs.append(render_para(child, width=width, indent=indent))
+ paragraphs.append('')
+ return clean_lines('\n'.join(paragraphs).strip())
+# }}}
+
+
+def parse_source_xml(filename, mode):
+ """Collects API functions.
+
+ Returns two strings:
+ 1. API functions
+ 2. Deprecated API functions
+
+ Caller decides what to do with the deprecated documentation.
+ """
+ global xrefs
+ xrefs = set()
+ functions = []
+ deprecated_functions = []
+
+ dom = minidom.parse(filename)
+ compoundname = get_text(dom.getElementsByTagName('compoundname')[0])
+ for member in dom.getElementsByTagName('memberdef'):
+ if member.getAttribute('static') == 'yes' or \
+ member.getAttribute('kind') != 'function' or \
+ member.getAttribute('prot') == 'private' or \
+ get_text(get_child(member, 'name')).startswith('_'):
+ continue
+
+ loc = find_first(member, 'location')
+ if 'private' in loc.getAttribute('file'):
+ continue
+
+ return_type = get_text(get_child(member, 'type'))
+ if return_type == '':
+ continue
+
+ if return_type.startswith(('ArrayOf', 'DictionaryOf')):
+ parts = return_type.strip('_').split('_')
+ return_type = '{}({})'.format(parts[0], ', '.join(parts[1:]))
+
+ name = get_text(get_child(member, 'name'))
+
+ annotations = get_text(get_child(member, 'argsstring'))
+ if annotations and ')' in annotations:
+ annotations = annotations.rsplit(')', 1)[-1].strip()
+ # XXX: (doxygen 1.8.11) 'argsstring' only includes attributes of
+ # non-void functions. Special-case void functions here.
+ if name == 'nvim_get_mode' and len(annotations) == 0:
+ annotations += 'FUNC_API_ASYNC'
+ annotations = filter(None, map(lambda x: annotation_map.get(x),
+ annotations.split()))
+
+ if mode == 'lua':
+ fstem = compoundname.split('.')[0]
+ fstem = CONFIG[mode]['module_override'].get(fstem, fstem)
+ vimtag = '*{}.{}()*'.format(fstem, name)
+ else:
+ vimtag = '*{}()*'.format(name)
+
+ params = []
+ type_length = 0
+
+ for param in get_children(member, 'param'):
+ param_type = get_text(get_child(param, 'type')).strip()
+ param_name = ''
+ declname = get_child(param, 'declname')
+ if declname:
+ param_name = get_text(declname).strip()
+ elif mode == 'lua':
+ # that's how it comes out of lua2dox
+ param_name = param_type
+ param_type = ''
+
+ if param_name in param_exclude:
+ continue
+
+ if param_type.endswith('*'):
+ param_type = param_type.strip('* ')
+ param_name = '*' + param_name
+ type_length = max(type_length, len(param_type))
+ params.append((param_type, param_name))
+
+ c_args = []
+ for param_type, param_name in params:
+ c_args.append(' ' + (
+ '%s %s' % (param_type.ljust(type_length), param_name)).strip())
+
+ c_decl = textwrap.indent('%s %s(\n%s\n);' % (return_type, name,
+ ',\n'.join(c_args)),
+ ' ')
+
+ prefix = '%s(' % name
+ suffix = '%s)' % ', '.join('{%s}' % a[1] for a in params
+ if a[0] not in ('void', 'Error'))
+
+ # Minimum 8 chars between signature and vimtag
+ lhs = (text_width - 8) - len(prefix)
+
+ if len(prefix) + len(suffix) > lhs:
+ signature = vimtag.rjust(text_width) + '\n'
+ signature += doc_wrap(suffix, width=text_width-8, prefix=prefix,
+ func=True)
+ else:
+ signature = prefix + suffix
+ signature += vimtag.rjust(text_width - len(signature))
+
+ doc = ''
+ desc = find_first(member, 'detaileddescription')
+ if desc:
+ doc = parse_parblock(desc)
+ if DEBUG:
+ print(textwrap.indent(
+ re.sub(r'\n\s*\n+', '\n',
+ desc.toprettyxml(indent=' ', newl='\n')), ' ' * 16))
+
+ if not doc:
+ doc = 'TODO: Documentation'
+
+ annotations = '\n'.join(annotations)
+ if annotations:
+ annotations = ('\n\nAttributes: ~\n' +
+ textwrap.indent(annotations, ' '))
+ i = doc.rfind('Parameters: ~')
+ if i == -1:
+ doc += annotations
+ else:
+ doc = doc[:i] + annotations + '\n\n' + doc[i:]
+
+ if INCLUDE_C_DECL:
+ doc += '\n\nC Declaration: ~\n>\n'
+ doc += c_decl
+ doc += '\n<'
+
+ func_doc = signature + '\n'
+ func_doc += textwrap.indent(clean_lines(doc), ' ' * 16)
+ func_doc = re.sub(r'^\s+([<>])$', r'\1', func_doc, flags=re.M)
+
+ if 'Deprecated' in xrefs:
+ deprecated_functions.append(func_doc)
+ elif name.startswith(CONFIG[mode]['func_name_prefix']):
+ functions.append(func_doc)
+
+ xrefs.clear()
+
+ return '\n\n'.join(functions), '\n\n'.join(deprecated_functions)
+
+
+def delete_lines_below(filename, tokenstr):
+ """Deletes all lines below the line containing `tokenstr`, the line itself,
+ and one line above it.
+ """
+ lines = open(filename).readlines()
+ i = 0
+ for i, line in enumerate(lines, 1):
+ if tokenstr in line:
+ break
+ i = max(0, i - 2)
+ with open(filename, 'wt') as fp:
+ fp.writelines(lines[0:i])
+
+def gen_docs(config):
+ """Generate documentation.
+
+ Doxygen is called and configured through stdin.
+ """
+ for mode in CONFIG:
+ output_dir = out_dir.format(mode=mode)
+ p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE)
+ p.communicate(
+ config.format(
+ input=CONFIG[mode]['files'],
+ output=output_dir,
+ filter=filter_cmd,
+ file_patterns=CONFIG[mode]['file_patterns'])
+ .encode('utf8')
+ )
+ if p.returncode:
+ sys.exit(p.returncode)
+
+ sections = {}
+ intros = {}
+ sep = '=' * text_width
+
+ base = os.path.join(output_dir, 'xml')
+ dom = minidom.parse(os.path.join(base, 'index.xml'))
+
+ # generate docs for section intros
+ for compound in dom.getElementsByTagName('compound'):
+ if compound.getAttribute('kind') != 'group':
+ continue
+
+ groupname = get_text(find_first(compound, 'name'))
+ groupxml = os.path.join(base, '%s.xml' % compound.getAttribute('refid'))
+
+ desc = find_first(minidom.parse(groupxml), 'detaileddescription')
+ if desc:
+ doc = parse_parblock(desc)
+ if doc:
+ intros[groupname] = doc
+
+ for compound in dom.getElementsByTagName('compound'):
+ if compound.getAttribute('kind') != 'file':
+ continue
+
+ filename = get_text(find_first(compound, 'name'))
+ if filename.endswith('.c') or filename.endswith('.lua'):
+ functions, deprecated = parse_source_xml(
+ os.path.join(base, '%s.xml' %
+ compound.getAttribute('refid')), mode)
+
+ if not functions and not deprecated:
+ continue
+
+ if functions or deprecated:
+ name = os.path.splitext(os.path.basename(filename))[0]
+ if name == 'ui':
+ name = name.upper()
+ else:
+ name = name.title()
+
+ doc = ''
+
+ intro = intros.get('api-%s' % name.lower())
+ if intro:
+ doc += '\n\n' + intro
+
+ if functions:
+ doc += '\n\n' + functions
+
+ if INCLUDE_DEPRECATED and deprecated:
+ doc += '\n\n\nDeprecated %s Functions: ~\n\n' % name
+ doc += deprecated
+
+ if doc:
+ filename = os.path.basename(filename)
+ name = CONFIG[mode]['section_name'].get(filename, name)
+
+ if mode == 'lua':
+ title = 'Lua module: {}'.format(name.lower())
+ helptag = '*lua-{}*'.format(name.lower())
+ else:
+ title = '{} Functions'.format(name)
+ helptag = '*api-{}*'.format(name.lower())
+ sections[filename] = (title, helptag, doc)
+
+ if not sections:
+ return
+
+ docs = ''
+
+ i = 0
+ for filename in CONFIG[mode]['section_order']:
+ if filename not in sections:
+ raise RuntimeError('found new module "{}"; update the "section_order" map'.format(filename))
+ title, helptag, section_doc = sections.pop(filename)
+ i += 1
+ if filename not in CONFIG[mode]['append_only']:
+ docs += sep
+ docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title)))
+ docs += section_doc
+ docs += '\n\n\n'
+
+ docs = docs.rstrip() + '\n\n'
+ docs += ' vim:tw=78:ts=8:ft=help:norl:\n'
+
+ doc_file = os.path.join(base_dir, 'runtime', 'doc',
+ CONFIG[mode]['filename'])
+
+ delete_lines_below(doc_file, CONFIG[mode]['section_start_token'])
+ with open(doc_file, 'ab') as fp:
+ fp.write(docs.encode('utf8'))
+
+ shutil.rmtree(output_dir)
+
+
+def filter_source(filename):
+ name, extension = os.path.splitext(filename)
+ if extension == '.lua':
+ p = subprocess.run([lua2dox_filter, filename], stdout=subprocess.PIPE)
+ op = ('?' if 0 != p.returncode else p.stdout.decode('utf-8'))
+ print(op)
+ else:
+ """Filters the source to fix macros that confuse Doxygen."""
+ with open(filename, 'rt') as fp:
+ print(re.sub(r'^(ArrayOf|DictionaryOf)(\(.*?\))',
+ lambda m: m.group(1)+'_'.join(
+ re.split(r'[^\w]+', m.group(2))),
+ fp.read(), flags=re.M))
+
+
+# Doxygen Config {{{
+Doxyfile = '''
+OUTPUT_DIRECTORY = {output}
+INPUT = {input}
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = {file_patterns}
+RECURSIVE = YES
+INPUT_FILTER = "{filter}"
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS = */private/*
+EXCLUDE_SYMBOLS =
+EXTENSION_MAPPING = lua=C
+EXTRACT_PRIVATE = NO
+
+GENERATE_HTML = NO
+GENERATE_DOCSET = NO
+GENERATE_HTMLHELP = NO
+GENERATE_QHP = NO
+GENERATE_TREEVIEW = NO
+GENERATE_LATEX = NO
+GENERATE_RTF = NO
+GENERATE_MAN = NO
+GENERATE_DOCBOOK = NO
+GENERATE_AUTOGEN_DEF = NO
+
+GENERATE_XML = YES
+XML_OUTPUT = xml
+XML_PROGRAMLISTING = NO
+
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+MARKDOWN_SUPPORT = YES
+'''
+# }}}
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ filter_source(sys.argv[1])
+ else:
+ gen_docs(Doxyfile)
+
+# vim: set ft=python ts=4 sw=4 tw=79 et fdm=marker :
diff --git a/scripts/genappimage.sh b/scripts/genappimage.sh
index c0ef2a8591..cc88ab5559 100755
--- a/scripts/genappimage.sh
+++ b/scripts/genappimage.sh
@@ -37,29 +37,24 @@ VERSION=$("$ROOT_DIR"/build/bin/nvim --version | head -n 1 | grep -o 'v.*')
cd "$APP_BUILD_DIR"
-curl -Lo "$APP_BUILD_DIR"/appimage_functions.sh https://github.com/AppImage/AppImages/raw/master/functions.sh
-. ./appimage_functions.sh
-
-# Copy desktop and icon file to AppDir for AppRun to pick them up.
-# get_apprun
-# get_desktop
-cp "$ROOT_DIR/runtime/nvim.desktop" "$APP_DIR/"
-cp "$ROOT_DIR/runtime/nvim.png" "$APP_DIR/"
+# Only downloads linuxdeploy if the remote file is different from local
+if [ -e "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage ]; then
+ curl -Lo "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage \
+ -z "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage \
+ https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+else
+ curl -Lo "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage \
+ https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+fi
+
+chmod +x "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage
+
+# metainfo is not packaged automatically by linuxdeploy
mkdir "$APP_DIR/usr/share/metainfo/"
cp "$ROOT_DIR/runtime/nvim.appdata.xml" "$APP_DIR/usr/share/metainfo/"
cd "$APP_DIR"
-# copy dependencies
-copy_deps
-# Move the libraries to usr/bin
-move_lib
-
-# Delete stuff that should not go into the AppImage.
-# Delete dangerous libraries; see
-# https://github.com/AppImage/AppImages/blob/master/excludelist
-delete_blacklisted
-
########################################################################
# AppDir complete. Now package it as an AppImage.
########################################################################
@@ -78,21 +73,24 @@ chmod 755 AppRun
cd "$APP_BUILD_DIR" # Get out of AppImage directory.
+# Set the name of the file generated by appimage
+export OUTPUT=nvim.appimage
+
+# If it's a release generate the zsync file
+if [ -n "$TAG" ]; then
+ export UPDATE_INFORMATION="gh-releases-zsync|neovim|neovim|$TAG|nvim.appimage.zsync"
+fi
+
# Generate AppImage.
# - Expects: $ARCH, $APP, $VERSION env vars
# - Expects: ./$APP.AppDir/ directory
-# - Produces: ../out/$APP-$VERSION.glibc$GLIBC_NEEDED-$ARCH.AppImage
-if [ -n "$TAG" ]; then
- generate_type2_appimage -u "gh-releases-zsync|neovim|neovim|$TAG|nvim.appimage.zsync"
-else
- generate_type2_appimage
-fi
+# - Produces: ./nvim.appimage
+./linuxdeploy-x86_64.AppImage --appdir $APP.AppDir -d $ROOT_DIR/runtime/nvim.desktop -i \
+"$ROOT_DIR/runtime/nvim.png" --output appimage
# Moving the final executable to a different folder so it isn't in the
# way for a subsequent build.
-mv "$ROOT_DIR"/out/*.AppImage* "$ROOT_DIR"/build/bin
-# Remove the (now empty) folder the AppImage was built in
-rmdir "$ROOT_DIR"/out
+mv "$ROOT_DIR"/build/nvim.appimage* "$ROOT_DIR"/build/bin
echo 'genappimage.sh: finished'
diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua
new file mode 100644
index 0000000000..ae76a36d50
--- /dev/null
+++ b/scripts/lua2dox.lua
@@ -0,0 +1,666 @@
+--[[--------------------------------------------------------------------------
+-- Copyright (C) 2012 by Simon Dales --
+-- simon@purrsoft.co.uk --
+-- --
+-- This program is free software; you can redistribute it and/or modify --
+-- it under the terms of the GNU General Public License as published by --
+-- the Free Software Foundation; either version 2 of the License, or --
+-- (at your option) any later version. --
+-- --
+-- This program is distributed in the hope that it will be useful, --
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of --
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --
+-- GNU General Public License for more details. --
+-- --
+-- You should have received a copy of the GNU General Public License --
+-- along with this program; if not, write to the --
+-- Free Software Foundation, Inc., --
+-- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. --
+----------------------------------------------------------------------------]]
+--[[!
+\file
+\brief a hack lua2dox converter
+]]
+
+--[[!
+\mainpage
+
+Introduction
+------------
+
+A hack lua2dox converter
+Version 0.2
+
+This lets us make Doxygen output some documentation to let
+us develop this code.
+
+It is partially cribbed from the functionality of lua2dox
+(http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm).
+Found on CPAN when looking for something else; kinda handy.
+
+Improved from lua2dox to make the doxygen output more friendly.
+Also it runs faster in lua rather than Perl.
+
+Because this Perl based system is called "lua2dox"., I have decided to add ".lua" to the name
+to keep the two separate.
+
+Running
+-------
+
+<ol>
+<li> Ensure doxygen is installed on your system and that you are familiar with its use.
+Best is to try to make and document some simple C/C++/PHP to see what it produces.
+You can experiment with the enclosed example code.
+
+<li> Run "doxygen -g" to create a default Doxyfile.
+
+Then alter it to let it recognise lua. Add the two following lines:
+
+\code{.bash}
+FILE_PATTERNS = *.lua
+
+FILTER_PATTERNS = *.lua=lua2dox_filter
+\endcode
+
+
+Either add them to the end or find the appropriate entry in Doxyfile.
+
+There are other lines that you might like to alter, but see futher documentation for details.
+
+<li> When Doxyfile is edited run "doxygen"
+
+The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language.
+It only has to be good enough for doxygen to see it as legal.
+Therefore our lua interpreter is fairly limited, but "good enough".
+
+One limitation is that each line is treated separately (except for long comments).
+The implication is that class and function declarations must be on the same line.
+Some functions can have their parameter lists extended over multiple lines to make it look neat.
+Managing this where there are also some comments is a bit more coding than I want to do at this stage,
+so it will probably not document accurately if we do do this.
+
+However I have put in a hack that will insert the "missing" close paren.
+The effect is that you will get the function documented, but not with the parameter list you might expect.
+</ol>
+
+Installation
+------------
+
+Here for linux or unix-like, for any other OS you need to refer to other documentation.
+
+This file is "lua2dox.lua". It gets called by "lua2dox_filter"(bash).
+Somewhere in your path (e.g. "~/bin" or "/usr/local/bin") put a link to "lua2dox_filter".
+
+Documentation
+-------------
+
+Read the external documentation that should be part of this package.
+For example look for the "README" and some .PDFs.
+
+]]
+
+-- we won't use our library code, so this becomes more portable
+
+-- require 'elijah_fix_require'
+-- require 'elijah_class'
+--
+--! \brief ``declare'' as class
+--!
+--! use as:
+--! \code{.lua}
+--! TWibble = class()
+--! function TWibble.init(this,Str)
+--! this.str = Str
+--! -- more stuff here
+--! end
+--! \endcode
+--!
+function class(BaseClass, ClassInitialiser)
+ local newClass = {} -- a new class newClass
+ if not ClassInitialiser and type(BaseClass) == 'function' then
+ ClassInitialiser = BaseClass
+ BaseClass = nil
+ elseif type(BaseClass) == 'table' then
+ -- our new class is a shallow copy of the base class!
+ for i,v in pairs(BaseClass) do
+ newClass[i] = v
+ end
+ newClass._base = BaseClass
+ end
+ -- the class will be the metatable for all its newInstanceects,
+ -- and they will look up their methods in it.
+ newClass.__index = newClass
+
+ -- expose a constructor which can be called by <classname>(<args>)
+ local classMetatable = {}
+ classMetatable.__call =
+ function(class_tbl, ...)
+ local newInstance = {}
+ setmetatable(newInstance,newClass)
+ --if init then
+ -- init(newInstance,...)
+ if class_tbl.init then
+ class_tbl.init(newInstance,...)
+ else
+ -- make sure that any stuff from the base class is initialized!
+ if BaseClass and BaseClass.init then
+ BaseClass.init(newInstance, ...)
+ end
+ end
+ return newInstance
+ end
+ newClass.init = ClassInitialiser
+ newClass.is_a =
+ function(this, klass)
+ local thisMetatable = getmetatable(this)
+ while thisMetatable do
+ if thisMetatable == klass then
+ return true
+ end
+ thisMetatable = thisMetatable._base
+ end
+ return false
+ end
+ setmetatable(newClass, classMetatable)
+ return newClass
+end
+
+-- require 'elijah_clock'
+
+--! \class TCore_Clock
+--! \brief a clock
+TCore_Clock = class()
+
+--! \brief get the current time
+function TCore_Clock.GetTimeNow()
+ if os.gettimeofday then
+ return os.gettimeofday()
+ else
+ return os.time()
+ end
+end
+
+--! \brief constructor
+function TCore_Clock.init(this,T0)
+ if T0 then
+ this.t0 = T0
+ else
+ this.t0 = TCore_Clock.GetTimeNow()
+ end
+end
+
+--! \brief get time string
+function TCore_Clock.getTimeStamp(this,T0)
+ local t0
+ if T0 then
+ t0 = T0
+ else
+ t0 = this.t0
+ end
+ return os.date('%c %Z',t0)
+end
+
+
+--require 'elijah_io'
+
+--! \class TCore_IO
+--! \brief io to console
+--!
+--! pseudo class (no methods, just to keep documentation tidy)
+TCore_IO = class()
+--
+--! \brief write to stdout
+function TCore_IO_write(Str)
+ if (Str) then
+ io.write(Str)
+ end
+end
+
+--! \brief write to stdout
+function TCore_IO_writeln(Str)
+ if (Str) then
+ io.write(Str)
+ end
+ io.write("\n")
+end
+
+
+--require 'elijah_string'
+
+--! \brief trims a string
+function string_trim(Str)
+ return Str:match("^%s*(.-)%s*$")
+end
+
+--! \brief split a string
+--!
+--! \param Str
+--! \param Pattern
+--! \returns table of string fragments
+function string_split(Str, Pattern)
+ local splitStr = {}
+ local fpat = "(.-)" .. Pattern
+ local last_end = 1
+ local str, e, cap = string.find(Str,fpat, 1)
+ while str do
+ if str ~= 1 or cap ~= "" then
+ table.insert(splitStr,cap)
+ end
+ last_end = e+1
+ str, e, cap = string.find(Str,fpat, last_end)
+ end
+ if last_end <= #Str then
+ cap = string.sub(Str,last_end)
+ table.insert(splitStr, cap)
+ end
+ return splitStr
+end
+
+
+--require 'elijah_commandline'
+
+--! \class TCore_Commandline
+--! \brief reads/parses commandline
+TCore_Commandline = class()
+
+--! \brief constructor
+function TCore_Commandline.init(this)
+ this.argv = arg
+ this.parsed = {}
+ this.params = {}
+end
+
+--! \brief get value
+function TCore_Commandline.getRaw(this,Key,Default)
+ local val = this.argv[Key]
+ if not val then
+ val = Default
+ end
+ return val
+end
+
+
+--require 'elijah_debug'
+
+-------------------------------
+--! \brief file buffer
+--!
+--! an input file buffer
+TStream_Read = class()
+
+--! \brief get contents of file
+--!
+--! \param Filename name of file to read (or nil == stdin)
+function TStream_Read.getContents(this,Filename)
+ -- get lines from file
+ local filecontents
+ if Filename then
+ -- syphon lines to our table
+ --TCore_Debug_show_var('Filename',Filename)
+ filecontents={}
+ for line in io.lines(Filename) do
+ table.insert(filecontents,line)
+ end
+ else
+ -- get stuff from stdin as a long string (with crlfs etc)
+ filecontents=io.read('*a')
+ -- make it a table of lines
+ filecontents = TString_split(filecontents,'[\n]') -- note this only works for unix files.
+ Filename = 'stdin'
+ end
+
+ if filecontents then
+ this.filecontents = filecontents
+ this.contentsLen = #filecontents
+ this.currentLineNo = 1
+ end
+
+ return filecontents
+end
+
+--! \brief get lineno
+function TStream_Read.getLineNo(this)
+ return this.currentLineNo
+end
+
+--! \brief get a line
+function TStream_Read.getLine(this)
+ local line
+ if this.currentLine then
+ line = this.currentLine
+ this.currentLine = nil
+ else
+ -- get line
+ if this.currentLineNo<=this.contentsLen then
+ line = this.filecontents[this.currentLineNo]
+ this.currentLineNo = this.currentLineNo + 1
+ else
+ line = ''
+ end
+ end
+ return line
+end
+
+--! \brief save line fragment
+function TStream_Read.ungetLine(this,LineFrag)
+ this.currentLine = LineFrag
+end
+
+--! \brief is it eof?
+function TStream_Read.eof(this)
+ if this.currentLine or this.currentLineNo<=this.contentsLen then
+ return false
+ end
+ return true
+end
+
+--! \brief output stream
+TStream_Write = class()
+
+--! \brief constructor
+function TStream_Write.init(this)
+ this.tailLine = {}
+end
+
+--! \brief write immediately
+function TStream_Write.write(this,Str)
+ TCore_IO_write(Str)
+end
+
+--! \brief write immediately
+function TStream_Write.writeln(this,Str)
+ TCore_IO_writeln(Str)
+end
+
+--! \brief write immediately
+function TStream_Write.writelnComment(this,Str)
+ TCore_IO_write('// ZZ: ')
+ TCore_IO_writeln(Str)
+end
+
+--! \brief write to tail
+function TStream_Write.writelnTail(this,Line)
+ if not Line then
+ Line = ''
+ end
+ table.insert(this.tailLine,Line)
+end
+
+--! \brief outout tail lines
+function TStream_Write.write_tailLines(this)
+ for k,line in ipairs(this.tailLine) do
+ TCore_IO_writeln(line)
+ end
+ TCore_IO_write('// Lua2DoX new eof')
+end
+
+--! \brief input filter
+TLua2DoX_filter = class()
+
+--! \brief allow us to do errormessages
+function TLua2DoX_filter.warning(this,Line,LineNo,Legend)
+ this.outStream:writelnTail(
+ '//! \todo warning! ' .. Legend .. ' (@' .. LineNo .. ')"' .. Line .. '"'
+ )
+end
+
+--! \brief trim comment off end of string
+--!
+--! If the string has a comment on the end, this trims it off.
+--!
+local function TString_removeCommentFromLine(Line)
+ local pos_comment = string.find(Line,'%-%-')
+ local tailComment
+ if pos_comment then
+ Line = string.sub(Line,1,pos_comment-1)
+ tailComment = string.sub(Line,pos_comment)
+ end
+ return Line,tailComment
+end
+
+--! \brief get directive from magic
+local function getMagicDirective(Line)
+ local macro,tail
+ local macroStr = '[\\@]'
+ local pos_macro = string.find(Line,macroStr)
+ if pos_macro then
+ --! ....\\ macro...stuff
+ --! ....\@ macro...stuff
+ local line = string.sub(Line,pos_macro+1)
+ local space = string.find(line,'%s+')
+ if space then
+ macro = string.sub(line,1,space-1)
+ tail = string_trim(string.sub(line,space+1))
+ else
+ macro = line
+ tail = ''
+ end
+ end
+ return macro,tail
+end
+
+--! \brief check comment for fn
+local function checkComment4fn(Fn_magic,MagicLines)
+ local fn_magic = Fn_magic
+ -- TCore_IO_writeln('// checkComment4fn "' .. MagicLines .. '"')
+
+ local magicLines = string_split(MagicLines,'\n')
+
+ local macro,tail
+
+ for k,line in ipairs(magicLines) do
+ macro,tail = getMagicDirective(line)
+ if macro == 'fn' then
+ fn_magic = tail
+ -- TCore_IO_writeln('// found fn "' .. fn_magic .. '"')
+ else
+ --TCore_IO_writeln('// not found fn "' .. line .. '"')
+ end
+ end
+
+ return fn_magic
+end
+--! \brief run the filter
+function TLua2DoX_filter.readfile(this,AppStamp,Filename)
+ local err
+
+ local inStream = TStream_Read()
+ local outStream = TStream_Write()
+ this.outStream = outStream -- save to this obj
+
+ if (inStream:getContents(Filename)) then
+ -- output the file
+ local line
+ local fn_magic -- function name/def from magic comment
+
+ outStream:writelnTail('// #######################')
+ outStream:writelnTail('// app run:' .. AppStamp)
+ outStream:writelnTail('// #######################')
+ outStream:writelnTail()
+
+ while not (err or inStream:eof()) do
+ line = string_trim(inStream:getLine())
+ -- TCore_Debug_show_var('inStream',inStream)
+ -- TCore_Debug_show_var('line',line )
+ if string.sub(line,1,2)=='--' then -- its a comment
+ if string.sub(line,3,3)=='@' then -- it's a magic comment
+ local magic = string.sub(line,4)
+ outStream:writeln('/// @' .. magic)
+ fn_magic = checkComment4fn(fn_magic,magic)
+ elseif string.sub(line,3,3)=='-' then -- it's a nonmagic doc comment
+ local comment = string.sub(line,4)
+ outStream:writeln('/// '.. comment)
+ elseif string.sub(line,3,4)=='[[' then -- it's a long comment
+ line = string.sub(line,5) -- nibble head
+ local comment = ''
+ local closeSquare,hitend,thisComment
+ while (not err) and (not hitend) and (not inStream:eof()) do
+ closeSquare = string.find(line,']]')
+ if not closeSquare then -- need to look on another line
+ thisComment = line .. '\n'
+ line = inStream:getLine()
+ else
+ thisComment = string.sub(line,1,closeSquare-1)
+ hitend = true
+
+ -- unget the tail of the line
+ -- in most cases it's empty. This may make us less efficient but
+ -- easier to program
+ inStream:ungetLine(string_trim(string.sub(line,closeSquare+2)))
+ end
+ comment = comment .. thisComment
+ end
+ if string.sub(comment,1,1)=='@' then -- it's a long magic comment
+ outStream:write('/*' .. comment .. '*/ ')
+ fn_magic = checkComment4fn(fn_magic,comment)
+ else -- discard
+ outStream:write('/* zz:' .. comment .. '*/ ')
+ fn_magic = nil
+ end
+ else
+ outStream:writeln('// zz:"' .. line .. '"')
+ fn_magic = nil
+ end
+ elseif string.find(line,'^function') or string.find(line,'^local%s+function') then
+ -- it's a function
+ local pos_fn = string.find(line,'function')
+ -- function
+ -- ....v...
+ if pos_fn then
+ -- we've got a function
+ local fn_type
+ if string.find(line,'^local%s+') then
+ fn_type = ''--'static ' -- static functions seem to be excluded
+ else
+ fn_type = ''
+ end
+ local fn = TString_removeCommentFromLine(string_trim(string.sub(line,pos_fn+8)))
+ if fn_magic then
+ fn = fn_magic
+ fn_magic = nil
+ end
+
+ if string.sub(fn,1,1)=='(' then
+ -- it's an anonymous function
+ outStream:writelnComment(line)
+ else
+ -- fn has a name, so is interesting
+
+ -- want to fix for iffy declarations
+ local open_paren = string.find(fn,'[%({]')
+ local fn0 = fn
+ if open_paren then
+ fn0 = string.sub(fn,1,open_paren-1)
+ -- we might have a missing close paren
+ if not string.find(fn,'%)') then
+ fn = fn .. ' ___MissingCloseParenHere___)'
+ end
+ end
+
+ local dot = string.find(fn0,'[%.:]')
+ if dot then -- it's a method
+ local klass = string.sub(fn,1,dot-1)
+ local method = string.sub(fn,dot+1)
+ --TCore_IO_writeln('function ' .. klass .. '::' .. method .. ftail .. '{}')
+ --TCore_IO_writeln(klass .. '::' .. method .. ftail .. '{}')
+ outStream:writeln(
+ '/*! \\memberof ' .. klass .. ' */ '
+ .. method .. '{}'
+ )
+ else
+ -- add vanilla function
+
+ outStream:writeln(fn_type .. 'function ' .. fn .. '{}')
+ end
+ end
+ else
+ this:warning(inStream:getLineNo(),'something weird here')
+ end
+ fn_magic = nil -- mustn't indavertently use it again
+ elseif string.find(line,'=%s*class%(') then
+ -- it's a class declaration
+ local tailComment
+ line,tailComment = TString_removeCommentFromLine(line)
+ local equals = string.find(line,'=')
+ local klass = string_trim(string.sub(line,1,equals-1))
+ local tail = string_trim(string.sub(line,equals+1))
+ -- class(wibble wibble)
+ -- ....v.
+ local parent = string.sub(tail,7,-2)
+ if #parent>0 then
+ parent = ' :public ' .. parent
+ end
+ outStream:writeln('class ' .. klass .. parent .. '{};')
+ else
+ -- we don't know what this line means, so we can probably just comment it out
+ if #line>0 then
+ outStream:writeln('// zz: ' .. line)
+ else
+ outStream:writeln() -- keep this line blank
+ end
+ end
+ end
+
+ -- output the tail
+ outStream:write_tailLines()
+ else
+ outStream:writeln('!empty file')
+ end
+end
+
+--! \brief this application
+TApp = class()
+
+--! \brief constructor
+function TApp.init(this)
+ local t0 = TCore_Clock()
+ this.timestamp = t0:getTimeStamp()
+ this.name = 'Lua2DoX'
+ this.version = '0.2 20130128'
+ this.copyright = 'Copyright (c) Simon Dales 2012-13'
+end
+
+function TApp.getRunStamp(this)
+ return this.name .. ' (' .. this.version .. ') '
+ .. this.timestamp
+end
+
+function TApp.getVersion(this)
+ return this.name .. ' (' .. this.version .. ') '
+end
+
+function TApp.getCopyright(this)
+ return this.copyright
+end
+
+local This_app = TApp()
+
+--main
+local cl = TCore_Commandline()
+
+local argv1 = cl:getRaw(2)
+if argv1 == '--help' then
+ TCore_IO_writeln(This_app:getVersion())
+ TCore_IO_writeln(This_app:getCopyright())
+ TCore_IO_writeln([[
+ run as:
+ lua2dox_filter <param>
+ --------------
+ Param:
+ <filename> : interprets filename
+ --version : show version/copyright info
+ --help : this help text]])
+elseif argv1 == '--version' then
+ TCore_IO_writeln(This_app:getVersion())
+ TCore_IO_writeln(This_app:getCopyright())
+else
+ -- it's a filter
+ local appStamp = This_app:getRunStamp()
+ local filename = argv1
+
+ local filter = TLua2DoX_filter()
+ filter:readfile(appStamp,filename)
+end
+
+
+--eof
diff --git a/scripts/lua2dox_filter b/scripts/lua2dox_filter
new file mode 100755
index 0000000000..6cb16ef060
--- /dev/null
+++ b/scripts/lua2dox_filter
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+###########################################################################
+# Copyright (C) 2012 by Simon Dales #
+# simon@purrsoft.co.uk #
+# #
+# This program is free software; you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation; either version 2 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program; if not, write to the #
+# Free Software Foundation, Inc., #
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
+###########################################################################
+LANG=""
+
+##! \brief test executable to see if it exists
+test_executable(){
+ P_EXE="$1"
+ #########
+ WHICH=`which ${P_EXE}`
+ if test -z "${WHICH}"
+ then
+ echo "not found \"${P_EXE}\""
+ else
+ EXE="${P_EXE}"
+ fi
+ }
+
+##! \brief sets the lua interpreter
+set_lua(){
+ test_executable 'texlua'
+ if test -z "${EXE}"
+ then
+ test_executable 'lua'
+ fi
+ #echo "final EXE=\"${EXE}\""
+ }
+
+##! \brief makes canonical name of file
+##!
+##! Note that "readlink -f" doesn't work in MacOSX
+##!
+do_readlink(){
+ pushd . > /dev/null
+ TARGET_FILE=$1
+
+ cd `dirname $TARGET_FILE`
+ TARGET_FILE=`basename $TARGET_FILE`
+
+ # Iterate down a (possible) chain of symlinks
+ while [ -L "$TARGET_FILE" ]
+ do
+ TARGET_FILE=`readlink $TARGET_FILE`
+ cd `dirname $TARGET_FILE`
+ TARGET_FILE=`basename $TARGET_FILE`
+ done
+
+ PHYS_DIR=`pwd -P`
+ RESULT=$PHYS_DIR
+ popd > /dev/null
+ }
+
+##main
+set_lua
+if test -z "${EXE}"
+then
+ echo "no lua interpreter available"
+else
+ BASENAME=`basename "$0"`
+ do_readlink "$0"
+ DIRNAME="${RESULT}"
+
+ LUASCRIPT="${DIRNAME}/lua2dox.lua ${BASENAME}"
+ #echo "lua[${LUASCRIPT}]"
+
+ ${EXE} ${LUASCRIPT} $@
+fi
+#
+##eof
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index 7339fdc691..eeb77dbbdc 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -357,13 +357,12 @@ list_vim_commits() { (
# Prints all (sorted) "vim-patch:xxx" tokens found in the Nvim git log.
list_vimpatch_tokens() {
- local tokens
- # Find all "vim-patch:xxx" tokens in the Nvim git log.
- tokens="$(cd "${NVIM_SOURCE_DIR}" && git log -E --grep='vim-patch:[^ ]+' | grep 'vim-patch')"
- echo "$tokens" | grep -E 'vim-patch:[^ ,{]{7,}' \
- | sed 's/.*\(vim-patch:[.0-9a-z]\+\).*/\1/' \
+ # Use sed…{7,7} to normalize (internal) Git hashes (for tokens caches).
+ git -C "${NVIM_SOURCE_DIR}" log -E --grep='vim-patch:[^ ,{]{7,}' \
+ | grep -oE 'vim-patch:[^ ,{:]{7,}' \
| sort \
- | uniq
+ | uniq \
+ | sed -nE 's/^(vim-patch:([0-9]+\.[^ ]+|[0-9a-z]{7,7})).*/\1/p'
}
# Prints all patch-numbers (for the current v:version) for which there is
@@ -377,29 +376,31 @@ list_vimpatch_numbers() {
# Prints a newline-delimited list of Vim commits, for use by scripts.
list_missing_vimpatches() {
- local tokens vim_commit vim_commits is_missing vim_tag patch_number
+ local token vim_commit vim_tag patch_number
+ declare -A tokens
# Find all "vim-patch:xxx" tokens in the Nvim git log.
- tokens="$(list_vimpatch_tokens)"
+ for token in $(list_vimpatch_tokens); do
+ tokens[$token]=1
+ done
# Get missing Vim commits
- vim_commits="$(list_vim_commits)"
- for vim_commit in ${vim_commits}; do
+ for vim_commit in $(list_vim_commits); do
# Check for vim-patch:<commit_hash> (usually runtime updates).
- is_missing="$(echo "$tokens" | >/dev/null 2>&1 grep "vim\-patch:${vim_commit:0:7}" && echo false || echo true)"
-
- if ! [ "$is_missing" = "false" ] \
- && vim_tag="$(cd "${VIM_SOURCE_DIR}" && git describe --tags --exact-match "${vim_commit}" 2>/dev/null)"
- then
- # Vim version number (not commit hash).
- # Check for vim-patch:<tag> (not commit hash).
- patch_number="${vim_tag:1}" # "v7.4.0001" => "7.4.0001"
- is_missing="$(echo "$tokens" | >/dev/null 2>&1 grep "vim\-patch:${patch_number}" && echo false || echo true)"
- vim_commit="${vim_tag#v}"
+ token="vim-patch:${vim_commit:0:7}"
+ if [[ "${tokens[$token]-}" ]]; then
+ continue
fi
- if ! [ "$is_missing" = "false" ]; then
- echo "${vim_commit}"
+ if vim_tag="$(git -C "${VIM_SOURCE_DIR}" describe --tags --exact-match "${vim_commit}" 2>/dev/null)"; then
+ # Check for vim-patch:<tag> (not commit hash).
+ patch_number="vim-patch:${vim_tag:1}" # "v7.4.0001" => "7.4.0001"
+ if [[ "${tokens[$patch_number]-}" ]]; then
+ continue
+ fi
+ echo "$vim_tag"
+ else
+ echo "$vim_commit"
fi
done
}
@@ -410,7 +411,7 @@ show_vimpatches() {
printf "\nVim patches missing from Neovim:\n"
list_missing_vimpatches | while read vim_commit; do
- if (cd "${VIM_SOURCE_DIR}" && git --no-pager show --color=never --name-only "v${vim_commit}" 2>/dev/null) | grep -q ^runtime; then
+ if [ -n "$(git -C "$VIM_SOURCE_DIR" diff-tree --no-commit-id --name-only "${vim_commit}" -- runtime)" ]; then
printf ' • %s (+runtime)\n' "${vim_commit}"
else
printf ' • %s\n' "${vim_commit}"
diff --git a/src/Doxyfile b/src/Doxyfile
index de31c8355f..461fafe99d 100644
--- a/src/Doxyfile
+++ b/src/Doxyfile
@@ -243,7 +243,7 @@ OPTIMIZE_OUTPUT_VHDL = NO
# that for custom extensions you also need to set FILE_PATTERNS otherwise the
# files are not read by doxygen.
-EXTENSION_MAPPING =
+EXTENSION_MAPPING = lua=C
# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
# comments according to the Markdown format, which allows for more readable
@@ -672,7 +672,7 @@ INPUT_ENCODING = UTF-8
# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
# *.f90 *.f *.for *.vhd *.vhdl
-FILE_PATTERNS = *.h *.c
+FILE_PATTERNS = *.h *.c *.lua
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
@@ -758,7 +758,7 @@ INPUT_FILTER =
# info on how filters are used. If FILTER_PATTERNS is empty or if
# non of the patterns match the file name, INPUT_FILTER is applied.
-FILTER_PATTERNS =
+FILTER_PATTERNS = *.lua=scripts/lua2dox_filter
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
diff --git a/src/clint.py b/src/clint.py
index 1ef31820ee..6643d8956a 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -3246,7 +3246,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
r'|li_(?:next|prev|tv))\b', line)
if match:
error(filename, linenum, 'runtime/deprecated', 4,
- 'Accessing list_T internals directly is prohibited')
+ 'Accessing list_T internals directly is prohibited (hint: see commit d46e37cb4c71)')
# Check for suspicious usage of "if" like
# } if (a == b) {
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 3613a8f8bc..9a5ffecad4 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -15,6 +15,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/getchar.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/misc1.h"
@@ -49,7 +50,7 @@
/// Gets the buffer line count
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Line count, or 0 for unloaded buffer. |api-buffer|
Integer nvim_buf_line_count(Buffer buffer, Error *err)
@@ -97,15 +98,16 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
return rv;
}
-/// Activate updates from this buffer to the current channel.
+/// Activates buffer-update events on the channel.
///
-/// @param buffer The buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param send_buffer Set to true if the initial notification should contain
/// the whole buffer. If so, the first notification will be a
/// `nvim_buf_lines_event`. Otherwise, the first notification will be
/// a `nvim_buf_changedtick_event`
/// @param opts Optional parameters. Reserved for future use.
-/// @param[out] err Details of an error that may have occurred
+/// @param[out] err Error details, if any
/// @return False when updates couldn't be enabled because the buffer isn't
/// loaded or `opts` contained an invalid key; otherwise True.
Boolean nvim_buf_attach(uint64_t channel_id,
@@ -128,11 +130,12 @@ Boolean nvim_buf_attach(uint64_t channel_id,
return buf_updates_register(buf, channel_id, send_buffer);
}
-//
-/// Deactivate updates from this buffer to the current channel.
+
+/// Deactivates buffer-update events on the channel.
///
-/// @param buffer The buffer handle
-/// @param[out] err Details of an error that may have occurred
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param[out] err Error details, if any
/// @return False when updates couldn't be disabled because the buffer
/// isn't loaded; otherwise True.
Boolean nvim_buf_detach(uint64_t channel_id,
@@ -221,7 +224,8 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
/// Out-of-bounds indices are clamped to the nearest valid value, unless
/// `strict_indexing` is set.
///
-/// @param buffer Buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
/// @param end Last line index (exclusive)
/// @param strict_indexing Whether out-of-bounds should be an error.
@@ -290,7 +294,7 @@ end:
/// newend = end + int(include_end) + int(end < 0)
/// int(bool) = 1 if bool is true else 0
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
/// @param end Last line index
/// @param include_start True if the slice includes the `start` parameter
@@ -324,7 +328,8 @@ void buffer_set_line_slice(Buffer buffer,
/// Out-of-bounds indices are clamped to the nearest valid value, unless
/// `strict_indexing` is set.
///
-/// @param buffer Buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param start First line index
/// @param end Last line index (exclusive)
/// @param strict_indexing Whether out-of-bounds should be an error.
@@ -470,6 +475,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
false);
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
+ fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
end:
for (size_t i = 0; i < new_len; i++) {
@@ -491,7 +497,7 @@ end:
/// Unlike |line2byte()|, throws error for out-of-bounds indexing.
/// Returns -1 for unloaded buffer.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param index Line index
/// @param[out] err Error details, if any
/// @return Integer byte offset, or -1 for unloaded buffer.
@@ -518,7 +524,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
/// Gets a buffer-scoped (b:) variable.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param[out] err Error details, if any
/// @return Variable value
@@ -536,7 +542,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
/// Gets a changed tick of a buffer
///
-/// @param[in] buffer Buffer handle.
+/// @param[in] buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
///
/// @return `b:changedtick` value.
@@ -555,7 +561,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// Gets a list of buffer-local |mapping| definitions.
///
/// @param mode Mode short-name ("n", "i", "v", ...)
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @returns Array of maparg()-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
@@ -571,9 +577,34 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
return keymap_array(mode, buf);
}
+/// Sets a buffer-local |mapping| for the given mode.
+///
+/// @see |nvim_set_keymap()|
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs,
+ Dictionary opts, Error *err)
+ FUNC_API_SINCE(6)
+{
+ modify_keymap(buffer, false, mode, lhs, rhs, opts, err);
+}
+
+/// Unmaps a buffer-local |mapping| for the given mode.
+///
+/// @see |nvim_del_keymap()|
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
+ FUNC_API_SINCE(6)
+{
+ String rhs = { .data = "", .size = 0 };
+ Dictionary opts = ARRAY_DICT_INIT;
+ modify_keymap(buffer, true, mode, lhs, rhs, opts, err);
+}
+
/// Gets a map of buffer-local |user-commands|.
///
-/// @param buffer Buffer handle.
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param opts Optional parameters. Currently not used.
/// @param[out] err Error details, if any.
///
@@ -613,7 +644,7 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dictionary opts, Error *err)
/// Sets a buffer-scoped (b:) variable
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param value Variable value
/// @param[out] err Error details, if any
@@ -631,7 +662,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err)
/// Removes a buffer-scoped (b:) variable
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param[out] err Error details, if any
void nvim_buf_del_var(Buffer buffer, String name, Error *err)
@@ -650,7 +681,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
///
/// @deprecated
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param value Variable value
/// @param[out] err Error details, if any
@@ -673,7 +704,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
///
/// @deprecated
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Variable name
/// @param[out] err Error details, if any
/// @return Old value
@@ -691,7 +722,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err)
/// Gets a buffer option value
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
@@ -710,7 +741,8 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
/// Sets a buffer option value. Passing 'nil' as value deletes the option (only
/// works if there's a global fallback)
///
-/// @param buffer Buffer handle
+/// @param channel_id
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Option name
/// @param value Option value
/// @param[out] err Error details, if any
@@ -732,7 +764,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer,
/// @deprecated The buffer number now is equal to the object id,
/// so there is no need to use this function.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer number
Integer nvim_buf_get_number(Buffer buffer, Error *err)
@@ -751,7 +783,7 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
/// Gets the full file name for the buffer
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer name
String nvim_buf_get_name(Buffer buffer, Error *err)
@@ -769,7 +801,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
/// Sets the full file name for a buffer
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Buffer name
/// @param[out] err Error details, if any
void nvim_buf_set_name(Buffer buffer, String name, Error *err)
@@ -801,7 +833,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
/// Checks if a buffer is valid and loaded. See |api-buffer| for more info
/// about unloaded buffers.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @return true if the buffer is valid and loaded, false otherwise.
Boolean nvim_buf_is_loaded(Buffer buffer)
FUNC_API_SINCE(5)
@@ -817,7 +849,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer)
/// @note Even if a buffer is valid it may have been unloaded. See |api-buffer|
/// for more info about unloaded buffers.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @return true if the buffer is valid, false otherwise.
Boolean nvim_buf_is_valid(Buffer buffer)
FUNC_API_SINCE(1)
@@ -849,7 +881,7 @@ void buffer_insert(Buffer buffer,
/// Return a tuple (row,col) representing the position of the named mark
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param name Mark name
/// @param[out] err Error details, if any
/// @return (row, col) tuple
@@ -914,7 +946,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// supported for backwards compatibility, new code should use
/// |nvim_create_namespace| to create a new empty namespace.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id namespace to use or -1 for ungrouped highlight
/// @param hl_group Name of the highlight group to use
/// @param line Line to highlight (zero-indexed)
@@ -964,7 +996,7 @@ Integer nvim_buf_add_highlight(Buffer buffer,
/// To clear the namespace in the entire buffer, pass in 0 and -1 to
/// line_start and line_end respectively.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace to clear, or -1 to clear all namespaces.
/// @param line_start Start of range of lines to clear
/// @param line_end End of range of lines to clear (exclusive) or -1 to clear
@@ -997,7 +1029,7 @@ void nvim_buf_clear_namespace(Buffer buffer,
///
/// @deprecated use |nvim_buf_clear_namespace|.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace to clear, or -1 to clear all.
/// @param line_start Start of range of lines to clear
/// @param line_end End of range of lines to clear (exclusive) or -1 to clear
@@ -1031,7 +1063,7 @@ void nvim_buf_clear_highlight(Buffer buffer,
/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
/// virtual text, the allocated id is then returned.
///
-/// @param buffer Buffer handle
+/// @param buffer Buffer handle, or 0 for current buffer
/// @param ns_id Namespace to use or 0 to create a namespace,
/// or -1 for a ungrouped annotation
/// @param line Line to annotate with virtual text (zero-indexed)
@@ -1101,6 +1133,26 @@ free_exit:
return 0;
}
+// Check if deleting lines made the cursor position invalid.
+// Changed lines from `lo` to `hi`; added `extra` lines (negative if deleted).
+static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
+{
+ if (curwin->w_cursor.lnum >= lo) {
+ // Adjust cursor position if it's in/after the changed lines.
+ if (curwin->w_cursor.lnum >= hi) {
+ curwin->w_cursor.lnum += extra;
+ check_cursor_col();
+ } else if (extra < 0) {
+ curwin->w_cursor.lnum = lo;
+ check_cursor();
+ } else {
+ check_cursor_col();
+ }
+ changed_cline_bef_curs();
+ }
+ invalidate_botline();
+}
+
// Normalizes 0-based indexes to buffer line numbers
static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
{
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index feca140547..978c55691b 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -29,14 +29,13 @@ typedef enum {
} ErrorType;
typedef enum {
- kMessageTypeRequest,
- kMessageTypeResponse,
- kMessageTypeNotification
+ kMessageTypeUnknown = -1,
+ // Per msgpack-rpc spec.
+ kMessageTypeRequest = 0,
+ kMessageTypeResponse = 1,
+ kMessageTypeNotification = 2,
} MessageType;
-/// Used as the message ID of notifications.
-#define NO_RESPONSE UINT64_MAX
-
/// Mask for all internal calls
#define INTERNAL_CALL_MASK (((uint64_t)1) << (sizeof(uint64_t) * 8 - 1))
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index c2b382804d..521ec11906 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -744,6 +744,232 @@ String ga_take_string(garray_T *ga)
return str;
}
+/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
+/// functions like @ref nvim_buf_set_keymap.
+///
+/// Arguments are handled like @ref nvim_set_keymap unless noted.
+/// @param buffer Buffer handle for a specific buffer, or 0 for the current
+/// buffer, or -1 to signify global behavior ("all buffers")
+/// @param is_unmap When true, removes the mapping that matches {lhs}.
+void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs,
+ String rhs, Dictionary opts, Error *err)
+{
+ char *err_msg = NULL; // the error message to report, if any
+ char *err_arg = NULL; // argument for the error message format string
+ ErrorType err_type = kErrorTypeNone;
+
+ char_u *lhs_buf = NULL;
+ char_u *rhs_buf = NULL;
+
+ bool global = (buffer == -1);
+ if (global) {
+ buffer = 0;
+ }
+ buf_T *target_buf = find_buffer_by_handle(buffer, err);
+
+ MapArguments parsed_args;
+ memset(&parsed_args, 0, sizeof(parsed_args));
+ if (parse_keymap_opts(opts, &parsed_args, err)) {
+ goto fail_and_free;
+ }
+ parsed_args.buffer = !global;
+
+ set_maparg_lhs_rhs((char_u *)lhs.data, lhs.size,
+ (char_u *)rhs.data, rhs.size,
+ CPO_TO_CPO_FLAGS, &parsed_args);
+
+ if (parsed_args.lhs_len > MAXMAPLEN) {
+ err_msg = "LHS exceeds maximum map length: %s";
+ err_arg = lhs.data;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ if (mode.size > 1) {
+ err_msg = "Shortname is too long: %s";
+ err_arg = mode.data;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+ int mode_val; // integer value of the mapping mode, to be passed to do_map()
+ char_u *p = (char_u *)((mode.size) ? mode.data : "m");
+ if (STRNCMP(p, "!", 2) == 0) {
+ mode_val = get_map_mode(&p, true); // mapmode-ic
+ } else {
+ mode_val = get_map_mode(&p, false);
+ if ((mode_val == VISUAL + SELECTMODE + NORMAL + OP_PENDING)
+ && mode.size > 0) {
+ // get_map_mode() treats unrecognized mode shortnames as ":map".
+ // This is an error unless the given shortname was empty string "".
+ err_msg = "Invalid mode shortname: \"%s\"";
+ err_arg = (char *)p;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+ }
+
+ if (parsed_args.lhs_len == 0) {
+ err_msg = "Invalid (empty) LHS";
+ err_arg = "";
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ bool is_noremap = parsed_args.noremap;
+ assert(!(is_unmap && is_noremap));
+
+ if (!is_unmap && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
+ if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
+ parsed_args.rhs_is_noop = true;
+ } else {
+ // the given RHS was nonempty and not a <Nop>, but was parsed as if it
+ // were empty?
+ assert(false && "Failed to parse nonempty RHS!");
+ err_msg = "Parsing of nonempty RHS failed: %s";
+ err_arg = rhs.data;
+ err_type = kErrorTypeException;
+ goto fail_with_message;
+ }
+ } else if (is_unmap && parsed_args.rhs_len) {
+ err_msg = "Gave nonempty RHS in unmap command: %s";
+ err_arg = (char *)parsed_args.rhs;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ // buf_do_map() reads noremap/unmap as its own argument.
+ int maptype_val = 0;
+ if (is_unmap) {
+ maptype_val = 1;
+ } else if (is_noremap) {
+ maptype_val = 2;
+ }
+
+ switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) {
+ case 0:
+ break;
+ case 1:
+ api_set_error(err, kErrorTypeException, (char *)e_invarg, 0);
+ goto fail_and_free;
+ case 2:
+ api_set_error(err, kErrorTypeException, (char *)e_nomap, 0);
+ goto fail_and_free;
+ case 5:
+ api_set_error(err, kErrorTypeException,
+ "E227: mapping already exists for %s", parsed_args.lhs);
+ goto fail_and_free;
+ default:
+ assert(false && "Unrecognized return code!");
+ goto fail_and_free;
+ } // switch
+
+ xfree(lhs_buf);
+ xfree(rhs_buf);
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+
+ return;
+
+fail_with_message:
+ api_set_error(err, err_type, err_msg, err_arg);
+
+fail_and_free:
+ xfree(lhs_buf);
+ xfree(rhs_buf);
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+ return;
+}
+
+/// Read in the given opts, setting corresponding flags in `out`.
+///
+/// @param opts A dictionary passed to @ref nvim_set_keymap or
+/// @ref nvim_buf_set_keymap.
+/// @param[out] out MapArguments object in which to set parsed
+/// |:map-arguments| flags.
+/// @param[out] err Error details, if any.
+///
+/// @returns Zero on success, nonzero on failure.
+Integer parse_keymap_opts(Dictionary opts, MapArguments *out, Error *err)
+{
+ char *err_msg = NULL; // the error message to report, if any
+ char *err_arg = NULL; // argument for the error message format string
+ ErrorType err_type = kErrorTypeNone;
+
+ out->buffer = false;
+ out->nowait = false;
+ out->silent = false;
+ out->script = false;
+ out->expr = false;
+ out->unique = false;
+
+ for (size_t i = 0; i < opts.size; i++) {
+ KeyValuePair *key_and_val = &opts.items[i];
+ char *optname = key_and_val->key.data;
+
+ if (key_and_val->value.type != kObjectTypeBoolean) {
+ err_msg = "Gave non-boolean value for an opt: %s";
+ err_arg = optname;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+
+ bool was_valid_opt = false;
+ switch (optname[0]) {
+ // note: strncmp up to and including the null terminator, so that
+ // "nowaitFoobar" won't match against "nowait"
+
+ // don't recognize 'buffer' as a key; user shouldn't provide <buffer>
+ // when calling nvim_set_keymap or nvim_buf_set_keymap, since it can be
+ // inferred from which function they called
+ case 'n':
+ if (STRNCMP(optname, "noremap", 8) == 0) {
+ was_valid_opt = true;
+ out->noremap = key_and_val->value.data.boolean;
+ } else if (STRNCMP(optname, "nowait", 7) == 0) {
+ was_valid_opt = true;
+ out->nowait = key_and_val->value.data.boolean;
+ }
+ break;
+ case 's':
+ if (STRNCMP(optname, "silent", 7) == 0) {
+ was_valid_opt = true;
+ out->silent = key_and_val->value.data.boolean;
+ } else if (STRNCMP(optname, "script", 7) == 0) {
+ was_valid_opt = true;
+ out->script = key_and_val->value.data.boolean;
+ }
+ break;
+ case 'e':
+ if (STRNCMP(optname, "expr", 5) == 0) {
+ was_valid_opt = true;
+ out->expr = key_and_val->value.data.boolean;
+ }
+ break;
+ case 'u':
+ if (STRNCMP(optname, "unique", 7) == 0) {
+ was_valid_opt = true;
+ out->unique = key_and_val->value.data.boolean;
+ }
+ break;
+ default:
+ break;
+ } // switch
+ if (!was_valid_opt) {
+ err_msg = "Invalid key: %s";
+ err_arg = optname;
+ err_type = kErrorTypeValidation;
+ goto fail_with_message;
+ }
+ } // for
+
+ return 0;
+
+fail_with_message:
+ api_set_error(err, err_type, err_msg, err_arg);
+ return 1;
+}
+
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 0634764f13..cc74824402 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -5,6 +5,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/vim.h"
+#include "nvim/getchar.h"
#include "nvim/memory.h"
#include "nvim/ex_eval.h"
#include "nvim/lib/kvec.h"
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index d3cbb46dad..4f28ea5af3 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -29,11 +29,12 @@ typedef struct {
uint64_t channel_id;
Array buffer;
- int hl_id; // current higlight for legacy put event
- Integer cursor_row, cursor_col; // Intended visibule cursor position
+ int hl_id; // Current highlight for legacy put event.
+ Integer cursor_row, cursor_col; // Intended visible cursor position.
// Position of legacy cursor, used both for drawing and visible user cursor.
Integer client_row, client_col;
+ bool wildmenu_active;
} UIData;
static PMap(uint64_t) *connected_uis = NULL;
@@ -75,6 +76,21 @@ void remote_ui_wait_for_attach(void)
pmap_has(uint64_t)(connected_uis, CHAN_STDIO));
}
+/// Activates UI events on the channel.
+///
+/// Entry point of all UI clients. Allows |\-\-embed| to continue startup.
+/// Implies that the client is ready to show the UI. Adds the client to the
+/// list of UIs. |nvim_list_uis()|
+///
+/// @note If multiple UI clients are attached, the global screen dimensions
+/// degrade to the smallest client. E.g. if client A requests 80x40 but
+/// client B requests 200x100, the global screen has size 80x40.
+///
+/// @param channel_id
+/// @param width Requested screen columns
+/// @param height Requested screen rows
+/// @param options |ui-option| map
+/// @param[out] err Error details, if any
void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
Dictionary options, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
@@ -94,6 +110,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->width = (int)width;
ui->height = (int)height;
ui->rgb = true;
+ ui->override = false;
ui->grid_resize = remote_ui_grid_resize;
ui->grid_clear = remote_ui_grid_clear;
ui->grid_cursor_goto = remote_ui_grid_cursor_goto;
@@ -146,6 +163,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
data->buffer = (Array)ARRAY_DICT_INIT;
data->hl_id = 0;
data->client_col = -1;
+ data->wildmenu_active = false;
ui->data = data;
pmap_put(uint64_t)(connected_uis, channel_id, ui);
@@ -162,6 +180,12 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height,
api_free_dictionary(opts);
}
+/// Deactivates UI events on the channel.
+///
+/// Removes the client from the list of UIs. |nvim_list_uis()|
+///
+/// @param channel_id
+/// @param[out] err Error details, if any
void nvim_ui_detach(uint64_t channel_id, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
@@ -213,6 +237,15 @@ void nvim_ui_set_option(uint64_t channel_id, String name,
static void ui_set_option(UI *ui, bool init, String name, Object value,
Error *error)
{
+ if (strequal(name.data, "override")) {
+ if (value.type != kObjectTypeBoolean) {
+ api_set_error(error, kErrorTypeValidation, "override must be a Boolean");
+ return;
+ }
+ ui->override = value.data.boolean;
+ return;
+ }
+
if (strequal(name.data, "rgb")) {
if (value.type != kObjectTypeBoolean) {
api_set_error(error, kErrorTypeValidation, "rgb must be a Boolean");
@@ -262,20 +295,22 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
///
/// On invalid grid handle, fails with error.
///
+/// @param channel_id
/// @param grid The handle of the grid to be changed.
/// @param width The new requested width.
/// @param height The new requested height.
+/// @param[out] err Error details, if any
void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width,
- Integer height, Error *error)
+ Integer height, Error *err)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY
{
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
- api_set_error(error, kErrorTypeException,
+ api_set_error(err, kErrorTypeException,
"UI not attached to channel: %" PRId64, channel_id);
return;
}
- ui_grid_resize((handle_T)grid, (int)width, (int)height, error);
+ ui_grid_resize((handle_T)grid, (int)width, (int)height, err);
}
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
@@ -586,6 +621,7 @@ static Array translate_firstarg(UI *ui, Array args)
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
{
+ UIData *data = ui->data;
if (!ui->ui_ext[kUILinegrid]) {
// the representation of highlights in cmdline changed, translate back
// never consumes args
@@ -611,6 +647,39 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
}
}
+ // Back-compat: translate popupmenu_xx to legacy wildmenu_xx.
+ if (ui->ui_ext[kUIWildmenu]) {
+ if (strequal(name, "popupmenu_show")) {
+ data->wildmenu_active = (args.items[4].data.integer == -1)
+ || !ui->ui_ext[kUIPopupmenu];
+ if (data->wildmenu_active) {
+ Array new_args = ARRAY_DICT_INIT;
+ Array items = args.items[0].data.array;
+ Array new_items = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < items.size; i++) {
+ ADD(new_items, copy_object(items.items[i].data.array.items[0]));
+ }
+ ADD(new_args, ARRAY_OBJ(new_items));
+ push_call(ui, "wildmenu_show", new_args);
+ if (args.items[1].data.integer != -1) {
+ Array new_args2 = ARRAY_DICT_INIT;
+ ADD(new_args2, args.items[1]);
+ push_call(ui, "wildmenu_select", new_args);
+ }
+ return;
+ }
+ } else if (strequal(name, "popupmenu_select")) {
+ if (data->wildmenu_active) {
+ name = "wildmenu_select";
+ }
+ } else if (strequal(name, "popupmenu_hide")) {
+ if (data->wildmenu_active) {
+ name = "wildmenu_hide";
+ }
+ }
+ }
+
+
Array my_args = ARRAY_DICT_INIT;
// Objects are currently single-reference
// make a copy, but only if necessary
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index b89c5b6014..a1d25766fe 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -143,11 +143,11 @@ void cmdline_block_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_show(Array items)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void wildmenu_select(Integer selected)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void wildmenu_hide(void)
- FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void msg_show(String kind, Array content, Boolean replace_last)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index cb5ed5ecda..b8c863704a 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -15,6 +15,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/buffer.h"
+#include "nvim/api/window.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/lua/executor.h"
@@ -213,8 +214,8 @@ Integer nvim_input(String keys)
/// Send mouse event from GUI.
///
-/// The call is non-blocking. It doesn't wait on any resulting action, but
-/// queues the event to be processed soon by the event loop.
+/// Non-blocking: does not wait on any result, but queues the event to be
+/// processed soon by the event loop.
///
/// @note Currently this doesn't support "scripting" multiple mouse events
/// by calling it multiple times in a loop: the intermediate mouse
@@ -232,6 +233,7 @@ Integer nvim_input(String keys)
/// @param grid Grid number if the client uses |ui-multigrid|, else 0.
/// @param row Mouse row-position (zero-based, like redraw events)
/// @param col Mouse column-position (zero-based, like redraw events)
+/// @param[out] err Error details, if any
void nvim_input_mouse(String button, String action, String modifier,
Integer grid, Integer row, Integer col, Error *err)
FUNC_API_SINCE(6) FUNC_API_ASYNC
@@ -681,14 +683,14 @@ void nvim_set_current_dir(String dir, Error *err)
try_start();
- if (vim_chdir((char_u *)string, kCdScopeGlobal)) {
+ if (vim_chdir((char_u *)string)) {
if (!try_end(err)) {
api_set_error(err, kErrorTypeException, "Failed to change directory");
}
return;
}
- post_chdir(kCdScopeGlobal);
+ post_chdir(kCdScopeGlobal, true);
try_end(err);
}
@@ -755,9 +757,9 @@ void nvim_del_var(String name, Error *err)
/// @deprecated
/// @see nvim_set_var
-/// @return Old value or nil if there was no previous value.
/// @warning May return nil if there was no previous value
/// OR if previous value was `v:null`.
+/// @return Old value or nil if there was no previous value.
Object vim_set_var(String name, Object value, Error *err)
{
return dict_set_var(&globvardict, name, value, false, true, err);
@@ -805,6 +807,7 @@ Object nvim_get_option(String name, Error *err)
/// Sets an option value.
///
+/// @param channel_id
/// @param name Option name
/// @param value New option value
/// @param[out] err Error details, if any
@@ -937,6 +940,7 @@ Window nvim_get_current_win(void)
/// Sets the current window.
///
/// @param window Window handle
+/// @param[out] err Error details, if any
void nvim_set_current_win(Window window, Error *err)
FUNC_API_SINCE(1)
{
@@ -958,7 +962,7 @@ void nvim_set_current_win(Window window, Error *err)
/// Creates a new, empty, unnamed buffer.
///
-/// @param listed Controls 'buflisted'
+/// @param listed Sets 'buflisted'
/// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
/// (always 'nomodified')
/// @param[out] err Error details, if any
@@ -997,38 +1001,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// GUI with the |ui-multigrid| extension. External windows are only supported
/// with multigrid GUIs, and are displayed as separate top-level windows.
///
-/// Exactly one of `external` and `relative` must be specified.
+/// For a general overview of floats, see |api-floatwin|.
///
-/// @param buffer handle of buffer to be displayed in the window
-/// @param enter whether the window should be entered (made the current window)
-/// @param width width of window (in character cells)
-/// @param height height of window (in character cells)
-/// @param options dict of options for configuring window positioning
-/// accepts the following keys:
-/// `relative`: If set, the window becomes a floating window. The window
-/// will be placed with row,col coordinates relative one of the
-/// following:
-/// "editor" the global editor grid
-/// "win" a window. Use 'win' option below to specify window id,
-/// or current window will be used by default.
-/// "cursor" the cursor position in current window.
-/// `anchor`: the corner of the float that the row,col position defines
-/// "NW" north-west (default)
-/// "NE" north-east
-/// "SW" south-west
-/// "SE" south-east
-/// `focusable`: Whether window can be focused by wincmds and
-/// mouse events. Defaults to true. Even if set to false, the window
-/// can still be entered using |nvim_set_current_win()| API call.
-/// `row`: row position. Screen cell height are used as unit. Can be
-/// floating point.
-/// `col`: column position. Screen cell width is used as unit. Can be
-/// floating point.
-/// `win`: when using relative='win', window id of the window where the
-/// position is defined.
-/// `external` GUI should display the window as an external
-/// top-level window. Currently accepts no other positioning options
-/// together with this.
+/// Exactly one of `external` and `relative` must be specified. The `width` and
+/// `height` of the new window must be specified.
///
/// With editor positioning row=0, col=0 refers to the top-left corner of the
/// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner.
@@ -1042,27 +1018,55 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// could let floats hover outside of the main window like a tooltip, but
/// this should not be used to specify arbitrary WM screen positions.
///
+/// @param buffer handle of buffer to be displayed in the window
+/// @param enter whether the window should be entered (made the current window)
+/// @param config Dictionary for the window configuration accepts these keys:
+/// - `relative`: If set, the window becomes a floating window. The window
+/// will be placed with row,col coordinates relative to one of the
+/// following:
+/// - "editor" the global editor grid
+/// - "win" a window. Use `win` to specify a window id,
+/// or the current window will be used by default.
+/// "cursor" the cursor position in current window.
+/// - `win`: When using relative='win', window id of the window where the
+/// position is defined.
+/// - `anchor`: The corner of the float that the row,col position defines:
+/// - "NW" north-west (default)
+/// - "NE" north-east
+/// - "SW" south-west
+/// - "SE" south-east
+/// - `height`: window height (in character cells). Minimum of 1.
+/// - `width`: window width (in character cells). Minimum of 1.
+/// - `row`: row position. Screen cell height are used as unit. Can be
+/// floating point.
+/// - `col`: column position. Screen cell width is used as unit. Can be
+/// floating point.
+/// - `focusable`: Whether window can be focused by wincmds and
+/// mouse events. Defaults to true. Even if set to false, the window
+/// can still be entered using |nvim_set_current_win()| API call.
+/// - `external`: GUI should display the window as an external
+/// top-level window. Currently accepts no other positioning
+/// configuration together with this.
/// @param[out] err Error details, if any
-/// @return the window handle or 0 when error
-Window nvim_open_win(Buffer buffer, Boolean enter,
- Integer width, Integer height,
- Dictionary options, Error *err)
+///
+/// @return Window handle, or 0 on error
+Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
+ Error *err)
FUNC_API_SINCE(6)
{
- win_T *old = curwin;
- FloatConfig config = FLOAT_CONFIG_INIT;
- if (!parse_float_config(options, &config, false, err)) {
+ FloatConfig fconfig = FLOAT_CONFIG_INIT;
+ if (!parse_float_config(config, &fconfig, false, err)) {
return 0;
}
- win_T *wp = win_new_float(NULL, (int)width, (int)height, config, err);
+ win_T *wp = win_new_float(NULL, fconfig, err);
if (!wp) {
return 0;
}
- if (buffer > 0) {
- nvim_set_current_buf(buffer, err);
+ if (enter) {
+ win_enter(wp, false);
}
- if (!enter) {
- win_enter(old, false);
+ if (buffer > 0) {
+ nvim_win_set_buf(wp->handle, buffer, err);
}
return wp->handle;
}
@@ -1194,12 +1198,29 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
rpc_unsubscribe(channel_id, e);
}
+/// Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or
+/// "#rrggbb" hexadecimal string.
+///
+/// Example:
+/// <pre>
+/// :echo nvim_get_color_by_name("Pink")
+/// :echo nvim_get_color_by_name("#cbcbcb")
+/// </pre>
+///
+/// @param name Color name or "#rrggbb" string
+/// @return 24-bit RGB value, or -1 for invalid argument.
Integer nvim_get_color_by_name(String name)
FUNC_API_SINCE(1)
{
return name_to_color((char_u *)name.data);
}
+/// Returns a map of color names and RGB values.
+///
+/// Keys are color names (e.g. "Aqua") and values are 24-bit RGB color values
+/// (e.g. 65535).
+///
+/// @return Map of color names and RGB values.
Dictionary nvim_get_color_map(void)
FUNC_API_SINCE(1)
{
@@ -1241,6 +1262,49 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
return keymap_array(mode, NULL);
}
+/// Sets a global |mapping| for the given mode.
+///
+/// To set a buffer-local mapping, use |nvim_buf_set_keymap()|.
+///
+/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs}
+/// or {rhs}. Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
+///
+/// Example:
+/// <pre>
+/// call nvim_set_keymap('n', ' <NL>', '', {'nowait': v:true})
+/// </pre>
+///
+/// is equivalent to:
+/// <pre>
+/// nmap <nowait> <Space><NL> <Nop>
+/// </pre>
+///
+/// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …)
+/// or "!" for |:map!|, or empty string for |:map|.
+/// @param lhs Left-hand-side |{lhs}| of the mapping.
+/// @param rhs Right-hand-side |{rhs}| of the mapping.
+/// @param opts Optional parameters map. Accepts all |:map-arguments|
+/// as keys excluding |<buffer>| but including |noremap|.
+/// Values are Booleans. Unknown key is an error.
+/// @param[out] err Error details, if any.
+void nvim_set_keymap(String mode, String lhs, String rhs,
+ Dictionary opts, Error *err)
+ FUNC_API_SINCE(6)
+{
+ modify_keymap(-1, false, mode, lhs, rhs, opts, err);
+}
+
+/// Unmaps a global |mapping| for the given mode.
+///
+/// To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|.
+///
+/// @see |nvim_set_keymap()|
+void nvim_del_keymap(String mode, String lhs, Error *err)
+ FUNC_API_SINCE(6)
+{
+ nvim_buf_del_keymap(-1, mode, lhs, err);
+}
+
/// Gets a map of global (non-buffer-local) Ex commands.
///
/// Currently only |user-commands| are supported, not builtin Ex commands.
@@ -1272,47 +1336,48 @@ Array nvim_get_api_info(uint64_t channel_id)
return rv;
}
-/// Identify the client for nvim. Can be called more than once, but subsequent
-/// calls will remove earlier info, which should be resent if it is still
-/// valid. (This could happen if a library first identifies the channel, and a
+/// Identifies the client. Can be called more than once; subsequent calls
+/// remove earlier info, which should be included by the caller if it is
+/// still valid. (E.g. if a library first identifies the channel, then a
/// plugin using that library later overrides that info)
///
-/// @param name short name for the connected client
-/// @param version Dictionary describing the version, with the following
-/// possible keys (all optional)
+/// @param channel_id
+/// @param name Short name for the connected client
+/// @param version Dictionary describing the version, with these
+/// (optional) keys:
/// - "major" major version (defaults to 0 if not set, for no release yet)
/// - "minor" minor version
/// - "patch" patch number
/// - "prerelease" string describing a prerelease, like "dev" or "beta1"
/// - "commit" hash or similar identifier of commit
-/// @param type Must be one of the following values. A client library should
-/// use "remote" if the library user hasn't specified other value.
-/// - "remote" remote client that connected to nvim.
+/// @param type Must be one of the following values. Client libraries should
+/// default to "remote" unless overridden by the user.
+/// - "remote" remote client connected to Nvim.
/// - "ui" gui frontend
-/// - "embedder" application using nvim as a component, for instance
-/// IDE/editor implementing a vim mode.
+/// - "embedder" application using Nvim as a component (for example,
+/// IDE/editor implementing a vim mode).
/// - "host" plugin host, typically started by nvim
/// - "plugin" single plugin, started by nvim
/// @param methods Builtin methods in the client. For a host, this does not
/// include plugin methods which will be discovered later.
/// The key should be the method name, the values are dicts with
-/// the following (optional) keys:
+/// these (optional) keys (more keys may be added in future
+/// versions of Nvim, thus unknown keys are ignored. Clients
+/// must only use keys defined in this or later versions of
+/// Nvim):
/// - "async" if true, send as a notification. If false or unspecified,
/// use a blocking request
/// - "nargs" Number of arguments. Could be a single integer or an array
-/// two integers, minimum and maximum inclusive.
-/// Further keys might be added in later versions of nvim and unknown keys
-/// are thus ignored. Clients must only use keys defined in this or later
-/// versions of nvim!
-///
-/// @param attributes Informal attributes describing the client. Clients might
-/// define their own keys, but the following are suggested:
-/// - "website" Website of client (for instance github repository)
-/// - "license" Informal description of the license, such as "Apache 2",
-/// "GPLv3" or "MIT"
-/// - "logo" URI or path to image, preferably small logo or icon.
-/// .png or .svg format is preferred.
+/// of two integers, minimum and maximum inclusive.
+///
+/// @param attributes Arbitrary string:string map of informal client properties.
+/// Suggested keys:
+/// - "website": Client homepage URL (e.g. GitHub repository)
+/// - "license": License description ("Apache 2", "GPLv3", "MIT", …)
+/// - "logo": URI or path to image, preferably small logo or icon.
+/// .png or .svg format is preferred.
///
+/// @param[out] err Error details, if any
void nvim_set_client_info(uint64_t channel_id, String name,
Dictionary version, String type,
Dictionary methods, Dictionary attributes,
@@ -1344,15 +1409,14 @@ void nvim_set_client_info(uint64_t channel_id, String name,
/// Get information about a channel.
///
-/// @returns a Dictionary, describing a channel with the
-/// following keys:
-/// - "stream" the stream underlying the channel
+/// @returns Dictionary describing a channel, with these keys:
+/// - "stream" the stream underlying the channel
/// - "stdio" stdin and stdout of this Nvim instance
/// - "stderr" stderr of this Nvim instance
/// - "socket" TCP/IP socket or named pipe
/// - "job" job with communication over its stdio
/// - "mode" how data received on the channel is interpreted
-/// - "bytes" send and recieve raw bytes
+/// - "bytes" send and receive raw bytes
/// - "terminal" a |terminal| instance interprets ASCII sequences
/// - "rpc" |RPC| communication on the channel is active
/// - "pty" Name of pseudoterminal, if one is used (optional).
@@ -1393,13 +1457,13 @@ Array nvim_list_chans(void)
/// processing which have such side-effects, e.g. |:sleep| may wake timers).
/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests.
///
+/// @param channel_id
/// @param calls an array of calls, where each call is described by an array
-/// with two elements: the request name, and an array of arguments.
-/// @param[out] err Details of a validation error of the nvim_multi_request call
-/// itself, i.e. malformed `calls` parameter. Errors from called methods will
-/// be indicated in the return value, see below.
+/// with two elements: the request name, and an array of arguments.
+/// @param[out] err Validation error details (malformed `calls` parameter),
+/// if any. Errors from batched calls are given in the return value.
///
-/// @return an array with two elements. The first is an array of return
+/// @return Array of two elements. The first is an array of return
/// values. The second is NIL if all calls succeeded. If a call resulted in
/// an error, it is a three-element array with the zero-based index of the call
/// which resulted in an error, the error type and the error message. If an
@@ -1490,9 +1554,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// Parse a VimL expression.
///
-/// @param[in] expr Expression to parse. Is always treated as a single line.
-/// @param[in] flags Flags:
-///
+/// @param[in] expr Expression to parse. Always treated as a single line.
+/// @param[in] flags Flags:
/// - "m" if multiple expressions in a row are allowed (only
/// the first one will be parsed),
/// - "E" if EOC tokens are not allowed (determines whether
@@ -1500,7 +1563,6 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// operator/space, though also yielding an error).
/// - "l" when needing to start parsing with lvalues for
/// ":let" or ":for".
-///
/// Common flag sets:
/// - "m" to parse like for ":echo".
/// - "E" to parse like for "<C-r>=".
@@ -1513,63 +1575,57 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// starting column and ending column (latter exclusive:
/// one should highlight region [start_col, end_col)).
///
-/// @return AST: top-level dictionary with these keys:
-///
-/// "error": Dictionary with error, present only if parser saw some
-/// error. Contains the following keys:
-///
-/// "message": String, error message in printf format, translated.
-/// Must contain exactly one "%.*s".
-/// "arg": String, error message argument.
-///
-/// "len": Amount of bytes successfully parsed. With flags equal to ""
-/// that should be equal to the length of expr string.
-///
-/// @note: “Sucessfully parsed” here means “participated in AST
-/// creation”, not “till the first error”.
-///
-/// "ast": AST, either nil or a dictionary with these keys:
-///
-/// "type": node type, one of the value names from ExprASTNodeType
-/// stringified without "kExprNode" prefix.
-/// "start": a pair [line, column] describing where node is “started”
-/// where "line" is always 0 (will not be 0 if you will be
-/// using nvim_parse_viml() on e.g. ":let", but that is not
-/// present yet). Both elements are Integers.
-/// "len": “length” of the node. This and "start" are there for
-/// debugging purposes primary (debugging parser and providing
-/// debug information).
-/// "children": a list of nodes described in top/"ast". There always
-/// is zero, one or two children, key will not be present
-/// if node has no children. Maximum number of children
-/// may be found in node_maxchildren array.
-///
-/// Local values (present only for certain nodes):
-///
-/// "scope": a single Integer, specifies scope for "Option" and
-/// "PlainIdentifier" nodes. For "Option" it is one of
-/// ExprOptScope values, for "PlainIdentifier" it is one of
-/// ExprVarScope values.
-/// "ident": identifier (without scope, if any), present for "Option",
-/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
-/// "name": Integer, register name (one character) or -1. Only present
-/// for "Register" nodes.
-/// "cmp_type": String, comparison type, one of the value names from
-/// ExprComparisonType, stringified without "kExprCmp"
-/// prefix. Only present for "Comparison" nodes.
-/// "ccs_strategy": String, case comparison strategy, one of the
-/// value names from ExprCaseCompareStrategy,
-/// stringified without "kCCStrategy" prefix. Only
-/// present for "Comparison" nodes.
-/// "augmentation": String, augmentation type for "Assignment" nodes.
-/// Is either an empty string, "Add", "Subtract" or
-/// "Concat" for "=", "+=", "-=" or ".=" respectively.
-/// "invert": Boolean, true if result of comparison needs to be
-/// inverted. Only present for "Comparison" nodes.
-/// "ivalue": Integer, integer value for "Integer" nodes.
-/// "fvalue": Float, floating-point value for "Float" nodes.
-/// "svalue": String, value for "SingleQuotedString" and
-/// "DoubleQuotedString" nodes.
+/// @return
+/// - AST: top-level dictionary with these keys:
+/// - "error": Dictionary with error, present only if parser saw some
+/// error. Contains the following keys:
+/// - "message": String, error message in printf format, translated.
+/// Must contain exactly one "%.*s".
+/// - "arg": String, error message argument.
+/// - "len": Amount of bytes successfully parsed. With flags equal to ""
+/// that should be equal to the length of expr string.
+/// (“Sucessfully parsed” here means “participated in AST
+/// creation”, not “till the first error”.)
+/// - "ast": AST, either nil or a dictionary with these keys:
+/// - "type": node type, one of the value names from ExprASTNodeType
+/// stringified without "kExprNode" prefix.
+/// - "start": a pair [line, column] describing where node is "started"
+/// where "line" is always 0 (will not be 0 if you will be
+/// using nvim_parse_viml() on e.g. ":let", but that is not
+/// present yet). Both elements are Integers.
+/// - "len": “length” of the node. This and "start" are there for
+/// debugging purposes primary (debugging parser and providing
+/// debug information).
+/// - "children": a list of nodes described in top/"ast". There always
+/// is zero, one or two children, key will not be present
+/// if node has no children. Maximum number of children
+/// may be found in node_maxchildren array.
+/// - Local values (present only for certain nodes):
+/// - "scope": a single Integer, specifies scope for "Option" and
+/// "PlainIdentifier" nodes. For "Option" it is one of
+/// ExprOptScope values, for "PlainIdentifier" it is one of
+/// ExprVarScope values.
+/// - "ident": identifier (without scope, if any), present for "Option",
+/// "PlainIdentifier", "PlainKey" and "Environment" nodes.
+/// - "name": Integer, register name (one character) or -1. Only present
+/// for "Register" nodes.
+/// - "cmp_type": String, comparison type, one of the value names from
+/// ExprComparisonType, stringified without "kExprCmp"
+/// prefix. Only present for "Comparison" nodes.
+/// - "ccs_strategy": String, case comparison strategy, one of the
+/// value names from ExprCaseCompareStrategy,
+/// stringified without "kCCStrategy" prefix. Only
+/// present for "Comparison" nodes.
+/// - "augmentation": String, augmentation type for "Assignment" nodes.
+/// Is either an empty string, "Add", "Subtract" or
+/// "Concat" for "=", "+=", "-=" or ".=" respectively.
+/// - "invert": Boolean, true if result of comparison needs to be
+/// inverted. Only present for "Comparison" nodes.
+/// - "ivalue": Integer, integer value for "Integer" nodes.
+/// - "fvalue": Float, floating-point value for "Float" nodes.
+/// - "svalue": String, value for "SingleQuotedString" and
+/// "DoubleQuotedString" nodes.
+/// @param[out] err Error details, if any
Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
Error *err)
FUNC_API_SINCE(4) FUNC_API_ASYNC
@@ -2034,15 +2090,12 @@ Dictionary nvim__stats(void)
/// Gets a list of dictionaries representing attached UIs.
///
-/// @return Array of UI dictionaries
-///
-/// Each dictionary has the following keys:
-/// - "height" requested height of the UI
-/// - "width" requested width of the UI
-/// - "rgb" whether the UI uses rgb colors (false implies cterm colors)
-/// - "ext_..." Requested UI extensions, see |ui-options|
-/// - "chan" Channel id of remote UI (not present for TUI)
-///
+/// @return Array of UI dictionaries, each with these keys:
+/// - "height" Requested height of the UI
+/// - "width" Requested width of the UI
+/// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
+/// - "ext_..." Requested UI extensions, see |ui-option|
+/// - "chan" Channel id of remote UI (not present for TUI)
Array nvim_list_uis(void)
FUNC_API_SINCE(4)
{
@@ -2145,6 +2198,7 @@ Object nvim_get_proc(Integer pid, Error *err)
/// @param finish Finish the completion and dismiss the popupmenu. Implies
/// `insert`.
/// @param opts Optional parameters. Reserved for future use.
+/// @param[out] err Error details, if any
void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish,
Dictionary opts, Error *err)
FUNC_API_SINCE(6)
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 157f73c9fa..e1c50cb89d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -67,6 +67,10 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
buffer);
}
+ // If window is not current, state logic will not validate its cursor.
+ // So do it now.
+ validate_cursor();
+
restore_win(save_curwin, save_curtab, false);
}
@@ -345,6 +349,7 @@ Object nvim_win_get_option(Window window, String name, Error *err)
/// Sets a window option value. Passing 'nil' as value deletes the option(only
/// works if there's a global fallback)
///
+/// @param channel_id
/// @param window Window handle
/// @param name Option name
/// @param value Option value
@@ -438,14 +443,16 @@ Boolean nvim_win_is_valid(Window window)
/// floating and external windows (including changing a split window to these
/// types).
///
-/// See documentation at |nvim_open_win()|, for the meaning of parameters. Pass
-/// in -1 for 'witdh' and 'height' to keep exiting size.
+/// See documentation at |nvim_open_win()|, for the meaning of parameters.
///
/// When reconfiguring a floating window, absent option keys will not be
/// changed. The following restriction apply: `row`, `col` and `relative`
/// must be reconfigured together. Only changing a subset of these is an error.
-void nvim_win_config(Window window, Integer width, Integer height,
- Dictionary options, Error *err)
+///
+/// @param window Window handle
+/// @param config Dictionary of window configuration
+/// @param[out] err Error details, if any
+void nvim_win_set_config(Window window, Dictionary config, Error *err)
FUNC_API_SINCE(6)
{
win_T *win = find_window_by_handle(window, err);
@@ -453,25 +460,67 @@ void nvim_win_config(Window window, Integer width, Integer height,
return;
}
bool new_float = !win->w_floating;
- width = width > 0 ? width: win->w_width;
- height = height > 0 ? height : win->w_height;
// reuse old values, if not overriden
- FloatConfig config = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
+ FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
- if (!parse_float_config(options, &config, !new_float, err)) {
+ if (!parse_float_config(config, &fconfig, !new_float, err)) {
return;
}
if (new_float) {
- if (!win_new_float(win, (int)width, (int)height, config, err)) {
+ if (!win_new_float(win, fconfig, err)) {
return;
}
redraw_later(NOT_VALID);
} else {
- win_config_float(win, (int)width, (int)height, config);
+ win_config_float(win, fconfig);
win->w_pos_changed = true;
}
}
+/// Return window configuration.
+///
+/// Return a dictionary containing the same config that can be given to
+/// |nvim_open_win()|.
+///
+/// `relative` will be an empty string for normal windows.
+///
+/// @param window Window handle
+/// @param[out] err Error details, if any
+/// @return Window configuration
+Dictionary nvim_win_get_config(Window window, Error *err)
+ FUNC_API_SINCE(6)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+
+ win_T *wp = find_window_by_handle(window, err);
+ if (!wp) {
+ return rv;
+ }
+
+ PUT(rv, "focusable", BOOLEAN_OBJ(wp->w_float_config.focusable));
+ PUT(rv, "external", BOOLEAN_OBJ(wp->w_float_config.external));
+
+ if (wp->w_floating) {
+ PUT(rv, "width", INTEGER_OBJ(wp->w_float_config.width));
+ PUT(rv, "height", INTEGER_OBJ(wp->w_float_config.height));
+ if (!wp->w_float_config.external) {
+ if (wp->w_float_config.relative == kFloatRelativeWindow) {
+ PUT(rv, "win", INTEGER_OBJ(wp->w_float_config.window));
+ }
+ PUT(rv, "anchor", STRING_OBJ(cstr_to_string(
+ float_anchor_str[wp->w_float_config.anchor])));
+ PUT(rv, "row", FLOAT_OBJ(wp->w_float_config.row));
+ PUT(rv, "col", FLOAT_OBJ(wp->w_float_config.col));
+ }
+ }
+
+ const char *rel = (wp->w_floating && !wp->w_float_config.external
+ ? float_relative_str[wp->w_float_config.relative] : "");
+ PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel)));
+
+ return rv;
+}
+
/// Close a window.
///
/// This is equivalent to |:close| with count except that it takes a window id.
@@ -480,9 +529,7 @@ void nvim_win_config(Window window, Integer width, Integer height,
/// @param force Behave like `:close!` The last window of a buffer with
/// unwritten changes can be closed. The buffer will become
/// hidden, even if 'hidden' is not set.
-///
/// @param[out] err Error details, if any
-/// @return Window number
void nvim_win_close(Window window, Boolean force, Error *err)
FUNC_API_SINCE(6)
{
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index cc0ed0f587..ef528f72b8 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -29,6 +29,7 @@ return {
'CmdWinLeave', -- before leaving the cmdline window
'ColorScheme', -- after loading a colorscheme
'ColorSchemePre', -- before loading a colorscheme
+ 'CompleteChanged', -- after popup menu changed
'CompleteDone', -- after finishing insert complete
'CursorHold', -- cursor in same position for a while
'CursorHoldI', -- idem, in Insert mode
@@ -64,7 +65,6 @@ return {
'InsertCharPre', -- before inserting a char
'InsertEnter', -- when entering Insert mode
'InsertLeave', -- when leaving Insert mode
- 'JobActivity', -- when job sent some data
'MenuPopup', -- just before popup menu is displayed
'OptionSet', -- after setting any option
'QuickFixCmdPost', -- after :make, :grep etc.
@@ -88,7 +88,7 @@ return {
'TabNew', -- when creating a new tab
'TabNewEntered', -- after entering a new tab
'TermChanged', -- after changing 'term'
- 'TermClose', -- after the processs exits
+ 'TermClose', -- after the process exits
'TermOpen', -- after opening a terminal buffer
'TermResponse', -- after setting "v:termresponse"
'TextChanged', -- text was modified
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8cb4e32815..cdb226b94d 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1,23 +1,23 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * buffer.c: functions for dealing with the buffer structure
- */
+//
+// buffer.c: functions for dealing with the buffer structure
+//
-/*
- * The buffer list is a double linked list of all buffers.
- * Each buffer can be in one of these states:
- * never loaded: BF_NEVERLOADED is set, only the file name is valid
- * not loaded: b_ml.ml_mfp == NULL, no memfile allocated
- * hidden: b_nwindows == 0, loaded but not displayed in a window
- * normal: loaded and displayed in a window
- *
- * Instead of storing file names all over the place, each file name is
- * stored in the buffer list. It can be referenced by a number.
- *
- * The current implementation remembers all file names ever used.
- */
+//
+// The buffer list is a double linked list of all buffers.
+// Each buffer can be in one of these states:
+// never loaded: BF_NEVERLOADED is set, only the file name is valid
+// not loaded: b_ml.ml_mfp == NULL, no memfile allocated
+// hidden: b_nwindows == 0, loaded but not displayed in a window
+// normal: loaded and displayed in a window
+//
+// Instead of storing file names all over the place, each file name is
+// stored in the buffer list. It can be referenced by a number.
+//
+// The current implementation remembers all file names ever used.
+//
#include <stdbool.h>
#include <string.h>
@@ -145,16 +145,13 @@ read_buffer(
return retval;
}
-/*
- * Open current buffer, that is: open the memfile and read the file into
- * memory.
- * Return FAIL for failure, OK otherwise.
- */
-int
-open_buffer (
- int read_stdin, /* read file from stdin */
- exarg_T *eap, /* for forced 'ff' and 'fenc' or NULL */
- int flags /* extra flags for readfile() */
+// Open current buffer, that is: open the memfile and read the file into
+// memory.
+// Return FAIL for failure, OK otherwise.
+int open_buffer(
+ int read_stdin, // read file from stdin
+ exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
+ int flags // extra flags for readfile()
)
{
int retval = OK;
@@ -169,14 +166,14 @@ open_buffer (
*/
if (readonlymode && curbuf->b_ffname != NULL
&& (curbuf->b_flags & BF_NEVERLOADED))
- curbuf->b_p_ro = TRUE;
+ curbuf->b_p_ro = true;
if (ml_open(curbuf) == FAIL) {
/*
* There MUST be a memfile, otherwise we can't do anything
* If we can't create one for the current buffer, take another buffer
*/
- close_buffer(NULL, curbuf, 0, FALSE);
+ close_buffer(NULL, curbuf, 0, false);
curbuf = NULL;
FOR_ALL_BUFFERS(buf) {
@@ -196,8 +193,9 @@ open_buffer (
}
EMSG(_("E83: Cannot allocate buffer, using other one..."));
enter_buffer(curbuf);
- if (old_tw != curbuf->b_p_tw)
+ if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
+ }
return FAIL;
}
@@ -206,7 +204,7 @@ open_buffer (
set_bufref(&old_curbuf, curbuf);
modified_was_set = false;
- /* mark cursor position as being invalid */
+ // mark cursor position as being invalid
curwin->w_valid = 0;
if (curbuf->b_ffname != NULL) {
@@ -265,7 +263,7 @@ open_buffer (
* it possible to retry when 'fileformat' or 'fileencoding' was
* guessed wrong.
*/
- curbuf->b_p_bin = TRUE;
+ curbuf->b_p_bin = true;
retval = readfile(NULL, NULL, (linenr_T)0,
(linenr_T)0, (linenr_T)MAXLNUM, NULL,
flags | (READ_NEW + READ_STDIN));
@@ -275,7 +273,7 @@ open_buffer (
}
}
- /* if first time loading this buffer, init b_chartab[] */
+ // if first time loading this buffer, init b_chartab[]
if (curbuf->b_flags & BF_NEVERLOADED) {
(void)buf_init_chartab(curbuf, false);
parse_cino(curbuf);
@@ -302,20 +300,21 @@ open_buffer (
curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
- /* require "!" to overwrite the file, because it wasn't read completely */
- if (aborting())
+ // require "!" to overwrite the file, because it wasn't read completely
+ if (aborting()) {
curbuf->b_flags |= BF_READERR;
+ }
/* Need to update automatic folding. Do this before the autocommands,
* they may use the fold info. */
foldUpdateAll(curwin);
- /* need to set w_topline, unless some autocommand already did that. */
+ // need to set w_topline, unless some autocommand already did that.
if (!(curwin->w_valid & VALID_TOPLINE)) {
curwin->w_topline = 1;
curwin->w_topfill = 0;
}
- apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);
+ apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, false, curbuf, &retval);
if (retval == FAIL) {
return FAIL;
@@ -333,10 +332,10 @@ open_buffer (
do_modelines(0);
curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
- apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,
- &retval);
+ apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, false, curbuf,
+ &retval);
- /* restore curwin/curbuf and a few other things */
+ // restore curwin/curbuf and a few other things
aucmd_restbuf(&aco);
}
@@ -457,14 +456,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
set_last_cursor(win);
}
buflist_setfpos(buf, win,
- win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
- win->w_cursor.col, TRUE);
+ win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
+ win->w_cursor.col, true);
}
bufref_T bufref;
set_bufref(&bufref, buf);
- /* When the buffer is no longer in a window, trigger BufWinLeave */
+ // When the buffer is no longer in a window, trigger BufWinLeave
if (buf->b_nwindows == 1) {
buf->b_locked++;
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false,
@@ -497,8 +496,9 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
return;
}
}
- if (aborting()) /* autocmds may abort script processing */
+ if (aborting()) { // autocmds may abort script processing
return;
+ }
}
// If the buffer was in curwin and the window has changed, go back to that
@@ -531,9 +531,10 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
terminal_close(buf->terminal, NULL);
}
- /* Always remove the buffer when there is no file name. */
- if (buf->b_ffname == NULL)
- del_buf = TRUE;
+ // Always remove the buffer when there is no file name.
+ if (buf->b_ffname == NULL) {
+ del_buf = true;
+ }
/*
* Free all things allocated for this buffer.
@@ -573,8 +574,9 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
* obtained anyway. Therefore only return if curbuf changed to the
* deleted buffer.
*/
- if (buf == curbuf && !is_curbuf)
+ if (buf == curbuf && !is_curbuf) {
return;
+ }
if (win != NULL // Avoid bogus clang warning.
&& win_valid_any_tab(win)
@@ -588,7 +590,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
buf->b_nwindows--;
}
- /* Change directories when the 'acd' option is set. */
+ // Change directories when the 'acd' option is set.
do_autochdir();
// Disable buffer-updates for the current buffer.
@@ -601,30 +603,33 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
if (wipe_buf) {
xfree(buf->b_ffname);
xfree(buf->b_sfname);
- if (buf->b_prev == NULL)
+ if (buf->b_prev == NULL) {
firstbuf = buf->b_next;
- else
+ } else {
buf->b_prev->b_next = buf->b_next;
- if (buf->b_next == NULL)
+ }
+ if (buf->b_next == NULL) {
lastbuf = buf->b_prev;
- else
+ } else {
buf->b_next->b_prev = buf->b_prev;
+ }
free_buffer(buf);
} else {
if (del_buf) {
/* Free all internal variables and reset option values, to make
* ":bdel" compatible with Vim 5.7. */
- free_buffer_stuff(buf, TRUE);
+ free_buffer_stuff(buf, true);
- /* Make it look like a new buffer. */
+ // Make it look like a new buffer.
buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
- /* Init the options when loaded again. */
+ // Init the options when loaded again.
buf->b_p_initialized = false;
}
buf_clear_file(buf);
- if (del_buf)
- buf->b_p_bl = FALSE;
+ if (del_buf) {
+ buf->b_p_bl = false;
+ }
}
}
@@ -634,13 +639,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
void buf_clear_file(buf_T *buf)
{
buf->b_ml.ml_line_count = 1;
- unchanged(buf, TRUE);
- buf->b_p_eol = TRUE;
- buf->b_start_eol = TRUE;
- buf->b_p_bomb = FALSE;
- buf->b_start_bomb = FALSE;
+ unchanged(buf, true);
+ buf->b_p_eol = true;
+ buf->b_start_eol = true;
+ buf->b_p_bomb = false;
+ buf->b_start_bomb = false;
buf->b_ml.ml_mfp = NULL;
- buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
+ buf->b_ml.ml_flags = ML_EMPTY; // empty buffer
}
/// Clears the current buffer contents.
@@ -714,29 +719,30 @@ void buf_freeall(buf_T *buf, int flags)
* it's OK to delete the curbuf, because a new one is obtained anyway.
* Therefore only return if curbuf changed to the deleted buffer.
*/
- if (buf == curbuf && !is_curbuf)
+ if (buf == curbuf && !is_curbuf) {
return;
+ }
diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer.
// Remove any ownsyntax, unless exiting.
if (curwin != NULL && curwin->w_buffer == buf) {
reset_synblock(curwin);
}
- /* No folds in an empty buffer. */
+ // No folds in an empty buffer.
FOR_ALL_TAB_WINDOWS(tp, win) {
if (win->w_buffer == buf) {
clearFolding(win);
}
}
- ml_close(buf, TRUE); /* close and delete the memline/memfile */
- buf->b_ml.ml_line_count = 0; /* no lines in buffer */
+ ml_close(buf, true); // close and delete the memline/memfile
+ buf->b_ml.ml_line_count = 0; // no lines in buffer
if ((flags & BFA_KEEP_UNDO) == 0) {
- u_blockfree(buf); /* free the memory allocated for undo */
- u_clearall(buf); /* reset all undo information */
+ u_blockfree(buf); // free the memory allocated for undo
+ u_clearall(buf); // reset all undo information
}
- syntax_clear(&buf->b_s); /* reset syntax info */
- buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */
+ syntax_clear(&buf->b_s); // reset syntax info
+ buf->b_flags &= ~BF_READERR; // a read error is no longer relevant
}
/*
@@ -775,10 +781,10 @@ static void free_buffer(buf_T *buf)
/*
* Free stuff in the buffer for ":bdel" and when wiping out the buffer.
*/
-static void
-free_buffer_stuff (
+static void
+free_buffer_stuff(
buf_T *buf,
- int free_options /* free options as well */
+ int free_options // free options as well
)
{
if (free_options) {
@@ -842,14 +848,14 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count)
if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') {
cleanup_T cs;
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a window. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a window.
enter_cleanup(&cs);
// Quitting means closing the split window, nothing else.
win_close(curwin, true);
swap_exists_action = SEA_NONE;
- swap_exists_did_quit = TRUE;
+ swap_exists_did_quit = true;
/* Restore the error/interrupt/exception state if not discarded by a
* new aborting error, interrupt, or uncaught exception. */
@@ -871,8 +877,8 @@ void handle_swap_exists(bufref_T *old_curbuf)
buf_T *buf;
if (swap_exists_action == SEA_QUIT) {
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a buffer. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a buffer.
enter_cleanup(&cs);
// User selected Quit at ATTENTION prompt. Go back to previous
@@ -902,20 +908,20 @@ void handle_swap_exists(bufref_T *old_curbuf)
check_colorcolumn(curwin);
}
}
- /* If "old_curbuf" is NULL we are in big trouble here... */
+ // If "old_curbuf" is NULL we are in big trouble here...
/* Restore the error/interrupt/exception state if not discarded by a
* new aborting error, interrupt, or uncaught exception. */
leave_cleanup(&cs);
} else if (swap_exists_action == SEA_RECOVER) {
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a buffer. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a buffer.
enter_cleanup(&cs);
- /* User selected Recover at ATTENTION prompt. */
- msg_scroll = TRUE;
+ // User selected Recover at ATTENTION prompt.
+ msg_scroll = true;
ml_recover();
- MSG_PUTS("\n"); /* don't overwrite the last message */
+ MSG_PUTS("\n"); // don't overwrite the last message
cmdline_row = msg_row;
do_modelines(0);
@@ -940,30 +946,32 @@ void handle_swap_exists(bufref_T *old_curbuf)
* Returns error message or NULL
*/
char_u *
-do_bufdel (
+do_bufdel(
int command,
- char_u *arg, /* pointer to extra arguments */
+ char_u *arg, // pointer to extra arguments
int addr_count,
- int start_bnr, /* first buffer number in a range */
- int end_bnr, /* buffer nr or last buffer nr in a range */
+ int start_bnr, // first buffer number in a range
+ int end_bnr, // buffer nr or last buffer nr in a range
int forceit
)
{
- int do_current = 0; /* delete current buffer? */
- int deleted = 0; /* number of buffers deleted */
- char_u *errormsg = NULL; /* return value */
- int bnr; /* buffer number */
+ int do_current = 0; // delete current buffer?
+ int deleted = 0; // number of buffers deleted
+ char_u *errormsg = NULL; // return value
+ int bnr; // buffer number
char_u *p;
if (addr_count == 0) {
(void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
} else {
if (addr_count == 2) {
- if (*arg) /* both range and argument is not allowed */
+ if (*arg) { // both range and argument is not allowed
return (char_u *)_(e_trailing);
+ }
bnr = start_bnr;
- } else /* addr_count == 1 */
+ } else { // addr_count == 1
bnr = end_bnr;
+ }
for (; !got_int; os_breakcheck()) {
/*
@@ -972,61 +980,71 @@ do_bufdel (
* the current one and will be loaded, which may then
* also be deleted, etc.
*/
- if (bnr == curbuf->b_fnum)
+ if (bnr == curbuf->b_fnum) {
do_current = bnr;
- else if (do_buffer(command, DOBUF_FIRST, FORWARD, bnr,
- forceit) == OK)
- ++deleted;
+ } else if (do_buffer(command, DOBUF_FIRST, FORWARD, bnr,
+ forceit) == OK) {
+ deleted++;
+ }
/*
* find next buffer number to delete/unload
*/
if (addr_count == 2) {
- if (++bnr > end_bnr)
+ if (++bnr > end_bnr) {
break;
- } else { /* addr_count == 1 */
+ }
+ } else { // addr_count == 1
arg = skipwhite(arg);
- if (*arg == NUL)
+ if (*arg == NUL) {
break;
+ }
if (!ascii_isdigit(*arg)) {
p = skiptowhite_esc(arg);
bnr = buflist_findpat(arg, p, command == DOBUF_WIPE,
- FALSE, FALSE);
- if (bnr < 0) /* failed */
+ false, false);
+ if (bnr < 0) { // failed
break;
+ }
arg = p;
} else
bnr = getdigits_int(&arg);
}
}
- if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
- FORWARD, do_current, forceit) == OK)
- ++deleted;
+ if (!got_int && do_current
+ && do_buffer(command, DOBUF_FIRST,
+ FORWARD, do_current, forceit) == OK) {
+ deleted++;
+ }
if (deleted == 0) {
- if (command == DOBUF_UNLOAD)
+ if (command == DOBUF_UNLOAD) {
STRCPY(IObuff, _("E515: No buffers were unloaded"));
- else if (command == DOBUF_DEL)
+ } else if (command == DOBUF_DEL) {
STRCPY(IObuff, _("E516: No buffers were deleted"));
- else
+ } else {
STRCPY(IObuff, _("E517: No buffers were wiped out"));
+ }
errormsg = IObuff;
} else if (deleted >= p_report) {
if (command == DOBUF_UNLOAD) {
- if (deleted == 1)
+ if (deleted == 1) {
MSG(_("1 buffer unloaded"));
- else
+ } else {
smsg(_("%d buffers unloaded"), deleted);
+ }
} else if (command == DOBUF_DEL) {
- if (deleted == 1)
+ if (deleted == 1) {
MSG(_("1 buffer deleted"));
- else
+ } else {
smsg(_("%d buffers deleted"), deleted);
+ }
} else {
- if (deleted == 1)
+ if (deleted == 1) {
MSG(_("1 buffer wiped out"));
- else
+ } else {
smsg(_("%d buffers wiped out"), deleted);
+ }
}
}
}
@@ -1055,8 +1073,8 @@ static int empty_curbuf(int close_others, int forceit, int action)
set_bufref(&bufref, buf);
if (close_others) {
- /* Close any other windows on this buffer, then make it empty. */
- close_windows(buf, TRUE);
+ // Close any other windows on this buffer, then make it empty.
+ close_windows(buf, true);
}
setpcmark();
@@ -1092,13 +1110,13 @@ static int empty_curbuf(int close_others, int forceit, int action)
*
* Return FAIL or OK.
*/
-int
-do_buffer (
+int
+do_buffer(
int action,
int start,
- int dir, /* FORWARD or BACKWARD */
- int count, /* buffer number or number of buffers */
- int forceit /* TRUE for :...! */
+ int dir, // FORWARD or BACKWARD
+ int count, // buffer number or number of buffers
+ int forceit // true for :...!
)
{
buf_T *buf;
@@ -1111,51 +1129,56 @@ do_buffer (
case DOBUF_LAST: buf = lastbuf; break;
default: buf = curbuf; break;
}
- if (start == DOBUF_MOD) { /* find next modified buffer */
+ if (start == DOBUF_MOD) { // find next modified buffer
while (count-- > 0) {
do {
buf = buf->b_next;
- if (buf == NULL)
+ if (buf == NULL) {
buf = firstbuf;
+ }
} while (buf != curbuf && !bufIsChanged(buf));
}
if (!bufIsChanged(buf)) {
EMSG(_("E84: No modified buffer found"));
return FAIL;
}
- } else if (start == DOBUF_FIRST && count) { /* find specified buffer number */
- while (buf != NULL && buf->b_fnum != count)
+ } else if (start == DOBUF_FIRST && count) { // find specified buffer number
+ while (buf != NULL && buf->b_fnum != count) {
buf = buf->b_next;
+ }
} else {
bp = NULL;
while (count > 0 || (!unload && !buf->b_p_bl && bp != buf)) {
/* remember the buffer where we start, we come back there when all
* buffers are unlisted. */
- if (bp == NULL)
+ if (bp == NULL) {
bp = buf;
+ }
if (dir == FORWARD) {
buf = buf->b_next;
- if (buf == NULL)
+ if (buf == NULL) {
buf = firstbuf;
+ }
} else {
buf = buf->b_prev;
- if (buf == NULL)
+ if (buf == NULL) {
buf = lastbuf;
+ }
}
- /* don't count unlisted buffers */
+ // don't count unlisted buffers
if (unload || buf->b_p_bl) {
- --count;
- bp = NULL; /* use this buffer as new starting point */
+ count--;
+ bp = NULL; // use this buffer as new starting point
}
if (bp == buf) {
- /* back where we started, didn't find anything. */
+ // back where we started, didn't find anything.
EMSG(_("E85: There is no listed buffer"));
return FAIL;
}
}
}
- if (buf == NULL) { /* could not find it */
+ if (buf == NULL) { // could not find it
if (start == DOBUF_FIRST) {
// don't warn when deleting
if (!unload) {
@@ -1180,8 +1203,9 @@ do_buffer (
/* When unloading or deleting a buffer that's already unloaded and
* unlisted: fail silently. */
- if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
+ if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl) {
return FAIL;
+ }
if (!forceit && (buf->terminal || bufIsChanged(buf))) {
if ((p_confirm || cmdmod.confirm) && p_write && !buf->terminal) {
@@ -1231,8 +1255,9 @@ do_buffer (
break;
}
}
- if (bp == NULL && buf == curbuf)
- return empty_curbuf(TRUE, forceit, action);
+ if (bp == NULL && buf == curbuf) {
+ return empty_curbuf(true, forceit, action);
+ }
/*
* If the deleted buffer is the current one, close the current window
@@ -1242,8 +1267,9 @@ do_buffer (
while (buf == curbuf
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
&& (!ONE_WINDOW || first_tabpage->tp_next != NULL)) {
- if (win_close(curwin, false) == FAIL)
+ if (win_close(curwin, false) == FAIL) {
break;
+ }
}
/*
@@ -1273,61 +1299,72 @@ do_buffer (
int jumpidx;
jumpidx = curwin->w_jumplistidx - 1;
- if (jumpidx < 0)
+ if (jumpidx < 0) {
jumpidx = curwin->w_jumplistlen - 1;
+ }
forward = jumpidx;
while (jumpidx != curwin->w_jumplistidx) {
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
if (buf != NULL) {
- if (buf == curbuf || !buf->b_p_bl)
- buf = NULL; /* skip current and unlisted bufs */
- else if (buf->b_ml.ml_mfp == NULL) {
- /* skip unloaded buf, but may keep it for later */
- if (bp == NULL)
+ if (buf == curbuf || !buf->b_p_bl) {
+ buf = NULL; // skip current and unlisted bufs
+ } else if (buf->b_ml.ml_mfp == NULL) {
+ // skip unloaded buf, but may keep it for later
+ if (bp == NULL) {
bp = buf;
+ }
buf = NULL;
}
}
- if (buf != NULL) /* found a valid buffer: stop searching */
+ if (buf != NULL) { // found a valid buffer: stop searching
break;
- /* advance to older entry in jump list */
- if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
+ }
+ // advance to older entry in jump list
+ if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) {
break;
- if (--jumpidx < 0)
+ }
+ if (--jumpidx < 0) {
jumpidx = curwin->w_jumplistlen - 1;
- if (jumpidx == forward) /* List exhausted for sure */
+ }
+ if (jumpidx == forward) { // List exhausted for sure
break;
+ }
}
}
- if (buf == NULL) { /* No previous buffer, Try 2'nd approach */
- forward = TRUE;
+ if (buf == NULL) { // No previous buffer, Try 2'nd approach
+ forward = true;
buf = curbuf->b_next;
for (;; ) {
if (buf == NULL) {
- if (!forward) /* tried both directions */
+ if (!forward) { // tried both directions
break;
+ }
buf = curbuf->b_prev;
- forward = FALSE;
+ forward = false;
continue;
}
- /* in non-help buffer, try to skip help buffers, and vv */
+ // in non-help buffer, try to skip help buffers, and vv
if (buf->b_help == curbuf->b_help && buf->b_p_bl) {
- if (buf->b_ml.ml_mfp != NULL) /* found loaded buffer */
+ if (buf->b_ml.ml_mfp != NULL) { // found loaded buffer
break;
- if (bp == NULL) /* remember unloaded buf for later */
+ }
+ if (bp == NULL) { // remember unloaded buf for later
bp = buf;
+ }
}
- if (forward)
+ if (forward) {
buf = buf->b_next;
- else
+ } else {
buf = buf->b_prev;
+ }
}
}
- if (buf == NULL) /* No loaded buffer, use unloaded one */
+ if (buf == NULL) { // No loaded buffer, use unloaded one
buf = bp;
- if (buf == NULL) { /* No loaded buffer, find listed one */
+ }
+ if (buf == NULL) { // No loaded buffer, find listed one
FOR_ALL_BUFFERS(buf2) {
if (buf2->b_p_bl && buf2 != curbuf) {
buf = buf2;
@@ -1335,39 +1372,44 @@ do_buffer (
}
}
}
- if (buf == NULL) { /* Still no buffer, just take one */
- if (curbuf->b_next != NULL)
+ if (buf == NULL) { // Still no buffer, just take one
+ if (curbuf->b_next != NULL) {
buf = curbuf->b_next;
- else
+ } else {
buf = curbuf->b_prev;
+ }
}
}
if (buf == NULL) {
/* Autocommands must have wiped out all other buffers. Only option
* now is to make the current buffer empty. */
- return empty_curbuf(FALSE, forceit, action);
+ return empty_curbuf(false, forceit, action);
}
/*
* make buf current buffer
*/
- if (action == DOBUF_SPLIT) { /* split window first */
- /* If 'switchbuf' contains "useopen": jump to first window containing
- * "buf" if one exists */
- if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
+ if (action == DOBUF_SPLIT) { // split window first
+ // If 'switchbuf' contains "useopen": jump to first window containing
+ // "buf" if one exists
+ if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf)) {
return OK;
- /* If 'switchbuf' contains "usetab": jump to first window in any tab
- * page containing "buf" if one exists */
- if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
+ }
+ // If 'switchbuf' contains "usetab": jump to first window in any tab
+ // page containing "buf" if one exists
+ if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf)) {
return OK;
- if (win_split(0, 0) == FAIL)
+ }
+ if (win_split(0, 0) == FAIL) {
return FAIL;
+ }
}
- /* go to current buffer - nothing to do */
- if (buf == curbuf)
+ // go to current buffer - nothing to do
+ if (buf == curbuf) {
return OK;
+ }
/*
* Check if the current buffer may be abandoned.
@@ -1388,15 +1430,16 @@ do_buffer (
}
}
- /* Go to the other buffer. */
+ // Go to the other buffer.
set_curbuf(buf, action);
if (action == DOBUF_SPLIT) {
- RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */
+ RESET_BINDING(curwin); // reset 'scrollbind' and 'cursorbind'
}
- if (aborting()) /* autocmds may abort script processing */
+ if (aborting()) { // autocmds may abort script processing
return FAIL;
+ }
return OK;
}
@@ -1419,12 +1462,13 @@ void set_curbuf(buf_T *buf, int action)
long old_tw = curbuf->b_p_tw;
setpcmark();
- if (!cmdmod.keepalt)
- curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */
- buflist_altfpos(curwin); /* remember curpos */
+ if (!cmdmod.keepalt) {
+ curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file
+ }
+ buflist_altfpos(curwin); // remember curpos
- /* Don't restart Select mode after switching to another buffer. */
- VIsual_reselect = FALSE;
+ // Don't restart Select mode after switching to another buffer.
+ VIsual_reselect = false;
// close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
prevbuf = curbuf;
@@ -1470,8 +1514,9 @@ void set_curbuf(buf_T *buf, int action)
) || curwin->w_buffer == NULL
) {
enter_buffer(buf);
- if (old_tw != curbuf->b_p_tw)
+ if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
+ }
}
if (bufref_valid(&prevbufref) && prevbuf->terminal != NULL) {
@@ -1486,75 +1531,83 @@ void set_curbuf(buf_T *buf, int action)
*/
void enter_buffer(buf_T *buf)
{
- /* Copy buffer and window local option values. Not for a help buffer. */
+ // Copy buffer and window local option values. Not for a help buffer.
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
- if (!buf->b_help)
+ if (!buf->b_help) {
get_winopts(buf);
- else
- /* Remove all folds in the window. */
+ } else {
+ // Remove all folds in the window.
clearFolding(curwin);
- foldUpdateAll(curwin); /* update folds (later). */
+ }
+ foldUpdateAll(curwin); // update folds (later).
- /* Get the buffer in the current window. */
+ // Get the buffer in the current window.
curwin->w_buffer = buf;
curbuf = buf;
- ++curbuf->b_nwindows;
+ curbuf->b_nwindows++;
- if (curwin->w_p_diff)
+ if (curwin->w_p_diff) {
diff_buf_add(curbuf);
+ }
- curwin->w_s = &(buf->b_s);
+ curwin->w_s = &(curbuf->b_s);
- /* Cursor on first line by default. */
+ // Cursor on first line by default.
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
- curwin->w_topline_was_set = FALSE;
+ curwin->w_set_curswant = true;
+ curwin->w_topline_was_set = false;
- /* mark cursor position as being invalid */
+ // mark cursor position as being invalid
curwin->w_valid = 0;
- /* Make sure the buffer is loaded. */
- if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */
- /* If there is no filetype, allow for detecting one. Esp. useful for
- * ":ball" used in an autocommand. If there already is a filetype we
- * might prefer to keep it. */
- if (*curbuf->b_p_ft == NUL)
- did_filetype = FALSE;
+ // Make sure the buffer is loaded.
+ if (curbuf->b_ml.ml_mfp == NULL) { // need to load the file
+ // If there is no filetype, allow for detecting one. Esp. useful for
+ // ":ball" used in an autocommand. If there already is a filetype we
+ // might prefer to keep it.
+ if (*curbuf->b_p_ft == NUL) {
+ did_filetype = false;
+ }
- open_buffer(FALSE, NULL, 0);
+ open_buffer(false, NULL, 0);
} else {
- if (!msg_silent)
- need_fileinfo = TRUE; /* display file info after redraw */
- (void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */
+ if (!msg_silent) {
+ need_fileinfo = true; // display file info after redraw
+ }
+ (void)buf_check_timestamp(curbuf, false); // check if file changed
curwin->w_topline = 1;
curwin->w_topfill = 0;
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, false, curbuf);
}
/* If autocommands did not change the cursor position, restore cursor lnum
* and possibly cursor col. */
- if (curwin->w_cursor.lnum == 1 && inindent(0))
+ if (curwin->w_cursor.lnum == 1 && inindent(0)) {
buflist_getfpos();
+ }
- check_arg_idx(curwin); /* check for valid arg_idx */
+ check_arg_idx(curwin); // check for valid arg_idx
maketitle();
- /* when autocmds didn't change it */
- if (curwin->w_topline == 1 && !curwin->w_topline_was_set)
- scroll_cursor_halfway(FALSE); /* redisplay at correct position */
+ // when autocmds didn't change it
+ if (curwin->w_topline == 1 && !curwin->w_topline_was_set) {
+ scroll_cursor_halfway(false); // redisplay at correct position
+ }
- /* Change directories when the 'acd' option is set. */
+ // Change directories when the 'acd' option is set.
do_autochdir();
- if (curbuf->b_kmap_state & KEYMAP_INIT)
+ if (curbuf->b_kmap_state & KEYMAP_INIT) {
(void)keymap_init();
- /* May need to set the spell language. Can only do this after the buffer
- * has been properly setup. */
- if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
+ }
+ // May need to set the spell language. Can only do this after the buffer
+ // has been properly setup.
+ if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
(void)did_set_spelllang(curwin);
+ }
redraw_later(NOT_VALID);
}
@@ -1567,6 +1620,7 @@ void do_autochdir(void)
if (starting == 0
&& curbuf->b_ffname != NULL
&& vim_chdirfile(curbuf->b_ffname) == OK) {
+ post_chdir(kCdScopeGlobal, false);
shorten_fnames(true);
}
}
@@ -1601,11 +1655,11 @@ static inline void buf_init_changedtick(buf_T *const buf)
/// Add a file name to the buffer list.
/// If the same file name already exists return a pointer to that buffer.
/// If it does not exist, or if fname == NULL, a new entry is created.
-/// If (flags & BLN_CURBUF) is TRUE, may use current buffer.
-/// If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list.
-/// If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer.
-/// If (flags & BLN_NEW) is TRUE, don't use an existing buffer.
-/// If (flags & BLN_NOOPT) is TRUE, don't copy options from the current buffer
+/// If (flags & BLN_CURBUF) is true, may use current buffer.
+/// If (flags & BLN_LISTED) is true, add new buffer to buffer list.
+/// If (flags & BLN_DUMMY) is true, don't count it as a real buffer.
+/// If (flags & BLN_NEW) is true, don't use an existing buffer.
+/// If (flags & BLN_NOOPT) is true, don't copy options from the current buffer
/// if the buffer already exists.
/// This is the ONLY way to create a new buffer.
///
@@ -1664,22 +1718,22 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
* buffer.)
*/
buf = NULL;
- if ((flags & BLN_CURBUF)
- && curbuf != NULL
- && curbuf->b_ffname == NULL
- && curbuf->b_nwindows <= 1
- && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) {
+ if ((flags & BLN_CURBUF) && curbuf_reusable()) {
+ assert(curbuf != NULL);
buf = curbuf;
/* It's like this buffer is deleted. Watch out for autocommands that
* change curbuf! If that happens, allocate a new buffer anyway. */
- if (curbuf->b_p_bl)
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
- if (buf == curbuf)
- apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf);
- if (aborting()) /* autocmds may abort script processing */
+ if (curbuf->b_p_bl) {
+ apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
+ }
+ if (buf == curbuf) {
+ apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, false, curbuf);
+ }
+ if (aborting()) { // autocmds may abort script processing
return NULL;
+ }
if (buf == curbuf) {
- /* Make sure 'bufhidden' and 'buftype' are empty */
+ // Make sure 'bufhidden' and 'buftype' are empty
clear_string_option(&buf->b_p_bh);
clear_string_option(&buf->b_p_bt);
}
@@ -1688,6 +1742,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
+ buf->b_signcols_max = -1;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -1705,35 +1760,38 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
buf->b_ffname = NULL;
xfree(buf->b_sfname);
buf->b_sfname = NULL;
- if (buf != curbuf)
+ if (buf != curbuf) {
free_buffer(buf);
+ }
return NULL;
}
if (buf == curbuf) {
- /* free all things allocated for this buffer */
+ // free all things allocated for this buffer
buf_freeall(buf, 0);
- if (buf != curbuf) /* autocommands deleted the buffer! */
+ if (buf != curbuf) { // autocommands deleted the buffer!
return NULL;
- if (aborting()) /* autocmds may abort script processing */
+ }
+ if (aborting()) { // autocmds may abort script processing
return NULL;
- free_buffer_stuff(buf, FALSE); /* delete local variables et al. */
+ }
+ free_buffer_stuff(buf, false); // delete local variables et al.
- /* Init the options. */
+ // Init the options.
buf->b_p_initialized = false;
buf_copy_options(buf, BCO_ENTER);
- /* need to reload lmaps and set b:keymap_name */
+ // need to reload lmaps and set b:keymap_name
curbuf->b_kmap_state |= KEYMAP_INIT;
} else {
/*
* put new buffer at the end of the buffer list
*/
buf->b_next = NULL;
- if (firstbuf == NULL) { /* buffer list is empty */
+ if (firstbuf == NULL) { // buffer list is empty
buf->b_prev = NULL;
firstbuf = buf;
- } else { /* append new buffer at end of list */
+ } else { // append new buffer at end of list
lastbuf->b_next = buf;
buf->b_prev = lastbuf;
}
@@ -1771,8 +1829,9 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
}
buf->b_u_synced = true;
buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
- if (flags & BLN_DUMMY)
+ if (flags & BLN_DUMMY) {
buf->b_flags |= BF_DUMMY;
+ }
buf_clear_file(buf);
clrallmarks(buf); // clear marks
fmarks_check_names(buf); // check file marks for this file
@@ -1803,9 +1862,21 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
return buf;
}
+/// Return true if the current buffer is empty, unnamed, unmodified and used in
+/// only one window. That means it can be reused.
+bool curbuf_reusable(void)
+{
+ return (curbuf != NULL
+ && curbuf->b_ffname == NULL
+ && curbuf->b_nwindows <= 1
+ && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())
+ && !bt_quickfix(curbuf)
+ && !curbufIsChanged());
+}
+
/*
* Free the memory for the options of a buffer.
- * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and
+ * If "free_p_ff" is true also free 'fileformat', 'buftype' and
* 'fileencoding'.
*/
void free_buf_options(buf_T *buf, int free_p_ff)
@@ -1884,25 +1955,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
buf = buflist_findnr(n);
if (buf == NULL) {
- if ((options & GETF_ALT) && n == 0)
+ if ((options & GETF_ALT) && n == 0) {
EMSG(_(e_noalt));
- else
+ } else {
EMSGN(_("E92: Buffer %" PRId64 " not found"), n);
+ }
return FAIL;
}
- /* if alternate file is the current buffer, nothing to do */
- if (buf == curbuf)
+ // if alternate file is the current buffer, nothing to do
+ if (buf == curbuf) {
return OK;
+ }
if (text_locked()) {
text_locked_msg();
return FAIL;
}
- if (curbuf_locked())
+ if (curbuf_locked()) {
return FAIL;
+ }
- /* altfpos may be changed by getfile(), get it now */
+ // altfpos may be changed by getfile(), get it now
if (lnum == 0) {
fpos = buflist_findfpos(buf);
lnum = fpos->lnum;
@@ -1942,12 +2016,12 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
(options & GETF_SETMARK), lnum, forceit))) {
RedrawingDisabled--;
- /* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */
+ // cursor is at to BOL and w_cursor.lnum is checked due to getfile()
if (!p_sol && col != 0) {
curwin->w_cursor.col = col;
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
return OK;
}
@@ -1965,13 +2039,13 @@ void buflist_getfpos(void)
curwin->w_cursor.lnum = fpos->lnum;
check_cursor_lnum();
- if (p_sol)
+ if (p_sol) {
curwin->w_cursor.col = 0;
- else {
+ } else {
curwin->w_cursor.col = fpos->col;
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
}
@@ -1984,12 +2058,13 @@ buf_T *buflist_findname_exp(char_u *fname)
char_u *ffname;
buf_T *buf = NULL;
- /* First make the name into a full path name */
+ // First make the name into a full path name
ffname = (char_u *)FullName_save((char *)fname,
#ifdef UNIX
- TRUE /* force expansion, get rid of symbolic links */
+ // force expansion, get rid of symbolic links
+ true
#else
- FALSE
+ false
#endif
);
if (ffname != NULL) {
@@ -2059,34 +2134,37 @@ int buflist_findpat(
if (diffmode && !(found_buf && diff_mode_buf(found_buf))) {
match = -1;
}
- }
- /*
- * Try four ways of matching a listed buffer:
- * attempt == 0: without '^' or '$' (at any position)
- * attempt == 1: with '^' at start (only at position 0)
- * attempt == 2: with '$' at end (only match at end)
- * attempt == 3: with '^' at start and '$' at end (only full match)
- * Repeat this for finding an unlisted buffer if there was no matching
- * listed buffer.
- */
- else {
- pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE);
- if (pat == NULL)
+ } else {
+ //
+ // Try four ways of matching a listed buffer:
+ // attempt == 0: without '^' or '$' (at any position)
+ // attempt == 1: with '^' at start (only at position 0)
+ // attempt == 2: with '$' at end (only match at end)
+ // attempt == 3: with '^' at start and '$' at end (only full match)
+ // Repeat this for finding an unlisted buffer if there was no matching
+ // listed buffer.
+ //
+
+ pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, false);
+ if (pat == NULL) {
return -1;
+ }
patend = pat + STRLEN(pat) - 1;
toggledollar = (patend > pat && *patend == '$');
- /* First try finding a listed buffer. If not found and "unlisted"
- * is TRUE, try finding an unlisted buffer. */
- find_listed = TRUE;
+ // First try finding a listed buffer. If not found and "unlisted"
+ // is true, try finding an unlisted buffer.
+ find_listed = true;
for (;; ) {
- for (attempt = 0; attempt <= 3; ++attempt) {
- /* may add '^' and '$' */
- if (toggledollar)
- *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */
+ for (attempt = 0; attempt <= 3; attempt++) {
+ // may add '^' and '$'
+ if (toggledollar) {
+ *patend = (attempt < 2) ? NUL : '$'; // add/remove '$'
+ }
p = pat;
- if (*p == '^' && !(attempt & 1)) /* add/remove '^' */
- ++p;
+ if (*p == '^' && !(attempt & 1)) { // add/remove '^'
+ p++;
+ }
regmatch_T regmatch;
regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
@@ -2113,33 +2191,36 @@ int buflist_findpat(
continue;
}
}
- if (match >= 0) { /* already found a match */
+ if (match >= 0) { // already found a match
match = -2;
break;
}
- match = buf->b_fnum; /* remember first match */
+ match = buf->b_fnum; // remember first match
}
}
vim_regfree(regmatch.regprog);
- if (match >= 0) /* found one match */
+ if (match >= 0) { // found one match
break;
+ }
}
/* Only search for unlisted buffers if there was no match with
* a listed buffer. */
- if (!unlisted || !find_listed || match != -1)
+ if (!unlisted || !find_listed || match != -1) {
break;
- find_listed = FALSE;
+ }
+ find_listed = false;
}
xfree(pat);
}
- if (match == -2)
+ if (match == -2) {
EMSG2(_("E93: More than one match for %s"), pattern);
- else if (match < 0)
+ } else if (match < 0) {
EMSG2(_("E94: No matching buffer for %s"), pattern);
+ }
return match;
}
@@ -2157,10 +2238,10 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
int attempt;
char_u *patc;
- *num_file = 0; /* return values in case of FAIL */
+ *num_file = 0; // return values in case of FAIL
*file = NULL;
- /* Make a copy of "pat" and change "^" to "\(^\|[\/]\)". */
+ // Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
if (*pat == '^') {
patc = xmalloc(STRLEN(pat) + 11);
STRCPY(patc, "\\(^\\|[\\/]\\)");
@@ -2172,15 +2253,17 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
* attempt == 0: try match with '\<', match at start of word
* attempt == 1: try match without '\<', match anywhere
*/
- for (attempt = 0; attempt <= 1; ++attempt) {
- if (attempt > 0 && patc == pat)
- break; /* there was no anchor, no need to try again */
+ for (attempt = 0; attempt <= 1; attempt++) {
+ if (attempt > 0 && patc == pat) {
+ break; // there was no anchor, no need to try again
+ }
regmatch_T regmatch;
regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
if (regmatch.regprog == NULL) {
- if (patc != pat)
+ if (patc != pat) {
xfree(patc);
+ }
return FAIL;
}
@@ -2188,37 +2271,42 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
* round == 1: Count the matches.
* round == 2: Build the array to keep the matches.
*/
- for (round = 1; round <= 2; ++round) {
+ for (round = 1; round <= 2; round++) {
count = 0;
FOR_ALL_BUFFERS(buf) {
- if (!buf->b_p_bl) /* skip unlisted buffers */
+ if (!buf->b_p_bl) { // skip unlisted buffers
continue;
+ }
p = buflist_match(&regmatch, buf, p_wic);
if (p != NULL) {
- if (round == 1)
- ++count;
- else {
- if (options & WILD_HOME_REPLACE)
+ if (round == 1) {
+ count++;
+ } else {
+ if (options & WILD_HOME_REPLACE) {
p = home_replace_save(buf, p);
- else
+ } else {
p = vim_strsave(p);
+ }
(*file)[count++] = p;
}
}
}
- if (count == 0) /* no match found, break here */
+ if (count == 0) { // no match found, break here
break;
+ }
if (round == 1) {
*file = xmalloc((size_t)count * sizeof(**file));
}
}
vim_regfree(regmatch.regprog);
- if (count) /* match(es) found, break here */
+ if (count) { // match(es) found, break here
break;
+ }
}
- if (patc != pat)
+ if (patc != pat) {
xfree(patc);
+ }
*num_file = count;
return count == 0 ? FAIL : OK;
@@ -2227,7 +2315,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
/// Check for a match on the file name for buffer "buf" with regprog "prog".
///
-/// @param ignore_case When TRUE, ignore case. Use 'fic' otherwise.
+/// @param ignore_case When true, ignore case. Use 'fic' otherwise.
static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
{
// First try the short file name, then the long file name.
@@ -2240,7 +2328,7 @@ static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case)
/// Try matching the regexp in "prog" with file name "name".
///
-/// @param ignore_case When TRUE, ignore case. Use 'fileignorecase' otherwise.
+/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise.
/// @return "name" when there is a match, NULL when not.
static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case)
{
@@ -2250,13 +2338,14 @@ static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case)
if (name != NULL) {
// Ignore case when 'fileignorecase' or the argument is set.
rmp->rm_ic = p_fic || ignore_case;
- if (vim_regexec(rmp, name, (colnr_T)0))
+ if (vim_regexec(rmp, name, (colnr_T)0)) {
match = name;
- else {
- /* Replace $(HOME) with '~' and try matching again. */
+ } else {
+ // Replace $(HOME) with '~' and try matching again.
p = home_replace_save(NULL, name);
- if (vim_regexec(rmp, p, (colnr_T)0))
+ if (vim_regexec(rmp, p, (colnr_T)0)) {
match = name;
+ }
xfree(p);
}
}
@@ -2281,17 +2370,18 @@ buf_T *buflist_findnr(int nr)
* Returns a pointer to allocated memory, of NULL when failed.
*/
char_u *
-buflist_nr2name (
+buflist_nr2name(
int n,
int fullname,
- int helptail /* for help buffers return tail only */
+ int helptail // for help buffers return tail only
)
{
buf_T *buf;
buf = buflist_findnr(n);
- if (buf == NULL)
+ if (buf == NULL) {
return NULL;
+ }
return home_replace_save(helptail ? buf : NULL,
fullname ? buf->b_ffname : buf->b_fname);
}
@@ -2311,23 +2401,28 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
{
wininfo_T *wip;
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
- if (wip->wi_win == win)
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (wip->wi_win == win) {
break;
+ }
+ }
if (wip == NULL) {
- /* allocate a new entry */
+ // allocate a new entry
wip = xcalloc(1, sizeof(wininfo_T));
wip->wi_win = win;
- if (lnum == 0) /* set lnum even when it's 0 */
+ if (lnum == 0) { // set lnum even when it's 0
lnum = 1;
+ }
} else {
- /* remove the entry from the list */
- if (wip->wi_prev)
+ // remove the entry from the list
+ if (wip->wi_prev) {
wip->wi_prev->wi_next = wip->wi_next;
- else
+ } else {
buf->b_wininfo = wip->wi_next;
- if (wip->wi_next)
+ }
+ if (wip->wi_next) {
wip->wi_next->wi_prev = wip->wi_prev;
+ }
if (copy_options && wip->wi_optset) {
clear_winopt(&wip->wi_opt);
deleteFoldRecurse(&wip->wi_folds);
@@ -2338,19 +2433,20 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
wip->wi_fpos.col = col;
}
if (copy_options) {
- /* Save the window-specific option values. */
+ // Save the window-specific option values.
copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
wip->wi_fold_manual = win->w_fold_manual;
cloneFoldGrowArray(&win->w_folds, &wip->wi_folds);
wip->wi_optset = true;
}
- /* insert the entry in front of the list */
+ // insert the entry in front of the list
wip->wi_next = buf->b_wininfo;
buf->b_wininfo = wip;
wip->wi_prev = NULL;
- if (wip->wi_next)
+ if (wip->wi_next) {
wip->wi_next->wi_prev = wip;
+ }
return;
}
@@ -2377,7 +2473,7 @@ static bool wininfo_other_tab_diff(wininfo_T *wip)
/*
* Find info for the current window in buffer "buf".
* If not found, return the info for the most recently used window.
- * When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in
+ * When "skip_diff_buffer" is true avoid windows with 'diff' set that is in
* another tab page.
* Returns NULL when there isn't any info.
*/
@@ -2395,11 +2491,14 @@ static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer)
* 'diff' set and is in another tab page). */
if (wip == NULL) {
if (skip_diff_buffer) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
- if (!wininfo_other_tab_diff(wip))
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (!wininfo_other_tab_diff(wip)) {
break;
- } else
+ }
+ }
+ } else {
wip = buf->b_wininfo;
+ }
}
return wip;
}
@@ -2433,9 +2532,10 @@ void get_winopts(buf_T *buf)
} else
copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
- /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */
- if (p_fdls >= 0)
+ // Set 'foldlevel' to 'foldlevelstart' if it's not negative.
+ if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;
+ }
didset_window_options(curwin);
}
@@ -2448,7 +2548,7 @@ pos_T *buflist_findfpos(buf_T *buf)
{
static pos_T no_position = { 1, 0, 0 };
- wininfo_T *wip = find_wininfo(buf, FALSE);
+ wininfo_T *wip = find_wininfo(buf, false);
return (wip == NULL) ? &no_position : &(wip->wi_fpos);
}
@@ -2509,7 +2609,7 @@ void buflist_list(exarg_T *eap)
len = IOSIZE - 20;
}
- /* put "line 999" in column 40 or after the file name */
+ // put "line 999" in column 40 or after the file name
i = 40 - vim_strsize(IObuff);
do {
IObuff[len++] = ' ';
@@ -2519,7 +2619,7 @@ void buflist_list(exarg_T *eap)
buf == curbuf ? (int64_t)curwin->w_cursor.lnum
: (int64_t)buflist_findlnum(buf));
msg_outtrans(IObuff);
- ui_flush(); /* output one line at a time */
+ ui_flush(); // output one line at a time
os_breakcheck();
}
}
@@ -2535,8 +2635,9 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
buf_T *buf;
buf = buflist_findnr(fnum);
- if (buf == NULL || buf->b_fname == NULL)
+ if (buf == NULL || buf->b_fname == NULL) {
return FAIL;
+ }
*fname = buf->b_fname;
*lnum = buflist_findlnum(buf);
@@ -2550,12 +2651,12 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
* Returns FAIL for failure (file name already in use by other buffer)
* OK otherwise.
*/
-int
-setfname (
+int
+setfname(
buf_T *buf,
char_u *ffname,
char_u *sfname,
- int message /* give message when buffer already exists */
+ int message // give message when buffer already exists
)
{
buf_T *obuf = NULL;
@@ -2563,15 +2664,16 @@ setfname (
bool file_id_valid = false;
if (ffname == NULL || *ffname == NUL) {
- /* Removing the name. */
+ // Removing the name.
xfree(buf->b_ffname);
xfree(buf->b_sfname);
buf->b_ffname = NULL;
buf->b_sfname = NULL;
} else {
- fname_expand(buf, &ffname, &sfname); /* will allocate ffname */
- if (ffname == NULL) /* out of memory */
+ fname_expand(buf, &ffname, &sfname); // will allocate ffname
+ if (ffname == NULL) { // out of memory
return FAIL;
+ }
/*
* if the file name is already used in another buffer:
@@ -2583,18 +2685,19 @@ setfname (
obuf = buflist_findname_file_id(ffname, &file_id, file_id_valid);
}
if (obuf != NULL && obuf != buf) {
- if (obuf->b_ml.ml_mfp != NULL) { /* it's loaded, fail */
- if (message)
+ if (obuf->b_ml.ml_mfp != NULL) { // it's loaded, fail
+ if (message) {
EMSG(_("E95: Buffer with this name already exists"));
+ }
xfree(ffname);
return FAIL;
}
- /* delete from the list */
- close_buffer(NULL, obuf, DOBUF_WIPE, FALSE);
+ // delete from the list
+ close_buffer(NULL, obuf, DOBUF_WIPE, false);
}
sfname = vim_strsave(sfname);
#ifdef USE_FNAME_CASE
- path_fix_case(sfname); /* set correct case for short file name */
+ path_fix_case(sfname); // set correct case for short file name
#endif
xfree(buf->b_ffname);
xfree(buf->b_sfname);
@@ -2643,15 +2746,17 @@ void buf_name_changed(buf_T *buf)
/*
* If the file name changed, also change the name of the swapfile
*/
- if (buf->b_ml.ml_mfp != NULL)
+ if (buf->b_ml.ml_mfp != NULL) {
ml_setname(buf);
+ }
- if (curwin->w_buffer == buf)
- check_arg_idx(curwin); /* check file name for arg list */
- maketitle(); /* set window title */
- status_redraw_all(); /* status lines need to be redrawn */
- fmarks_check_names(buf); /* check named file marks */
- ml_timestamp(buf); /* reset timestamp */
+ if (curwin->w_buffer == buf) {
+ check_arg_idx(curwin); // check file name for arg list
+ }
+ maketitle(); // set window title
+ status_redraw_all(); // status lines need to be redrawn
+ fmarks_check_names(buf); // check named file marks
+ ml_timestamp(buf); // reset timestamp
}
/*
@@ -2684,8 +2789,9 @@ char_u * getaltfname(
linenr_T dummy;
if (buflist_name_nr(0, &fname, &dummy) == FAIL) {
- if (errmsg)
+ if (errmsg) {
EMSG(_(e_noalt));
+ }
return NULL;
}
return fname;
@@ -2715,10 +2821,12 @@ int buflist_add(char_u *fname, int flags)
void buflist_slash_adjust(void)
{
FOR_ALL_BUFFERS(bp) {
- if (bp->b_ffname != NULL)
+ if (bp->b_ffname != NULL) {
slash_adjust(bp->b_ffname);
- if (bp->b_sfname != NULL)
+ }
+ if (bp->b_sfname != NULL) {
slash_adjust(bp->b_sfname);
+ }
}
}
@@ -2730,7 +2838,7 @@ void buflist_slash_adjust(void)
*/
void buflist_altfpos(win_T *win)
{
- buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE);
+ buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, true);
}
/// Check that "ffname" is not the same file as current file.
@@ -2818,9 +2926,9 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id)
/*
* Print info about the current buffer.
*/
-void
-fileinfo (
- int fullname, /* when non-zero print full path */
+void
+fileinfo(
+ int fullname, // when non-zero print full path
int shorthelp,
int dont_truncate
)
@@ -2833,20 +2941,21 @@ fileinfo (
buffer = xmalloc(IOSIZE);
- if (fullname > 1) { /* 2 CTRL-G: include buffer number */
+ if (fullname > 1) { // 2 CTRL-G: include buffer number
vim_snprintf((char *)buffer, IOSIZE, "buf %d: ", curbuf->b_fnum);
p = buffer + STRLEN(buffer);
} else
p = buffer;
*p++ = '"';
- if (buf_spname(curbuf) != NULL)
+ if (buf_spname(curbuf) != NULL) {
STRLCPY(p, buf_spname(curbuf), IOSIZE - (p - buffer));
- else {
- if (!fullname && curbuf->b_fname != NULL)
+ } else {
+ if (!fullname && curbuf->b_fname != NULL) {
name = curbuf->b_fname;
- else
+ } else {
name = curbuf->b_ffname;
+ }
home_replace(shorthelp ? curbuf : NULL, name, p,
(size_t)(IOSIZE - (p - buffer)), true);
}
@@ -2877,12 +2986,13 @@ fileinfo (
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg));
} else if (p_ru) {
- /* Current line and column are already on the screen -- webb */
- if (curbuf->b_ml.ml_line_count == 1)
+ // Current line and column are already on the screen -- webb
+ if (curbuf->b_ml.ml_line_count == 1) {
vim_snprintf_add((char *)buffer, IOSIZE, _("1 line --%d%%--"), n);
- else
+ } else {
vim_snprintf_add((char *)buffer, IOSIZE, _("%" PRId64 " lines --%d%%--"),
- (int64_t)curbuf->b_ml.ml_line_count, n);
+ (int64_t)curbuf->b_ml.ml_line_count, n);
+ }
} else {
vim_snprintf_add((char *)buffer, IOSIZE,
_("line %" PRId64 " of %" PRId64 " --%d%%-- col "),
@@ -2902,18 +3012,19 @@ fileinfo (
* First call msg_start() to get the message in the right place. */
msg_start();
n = msg_scroll;
- msg_scroll = TRUE;
+ msg_scroll = true;
msg(buffer);
msg_scroll = n;
} else {
- p = msg_trunc_attr(buffer, FALSE, 0);
- if (restart_edit != 0 || (msg_scrolled && !need_wait_return))
- /* Need to repeat the message after redrawing when:
- * - When restart_edit is set (otherwise there will be a delay
- * before redrawing).
- * - When the screen was scrolled but there is no wait-return
- * prompt. */
+ p = msg_trunc_attr(buffer, false, 0);
+ if (restart_edit != 0 || (msg_scrolled && !need_wait_return)) {
+ // Need to repeat the message after redrawing when:
+ // - When restart_edit is set (otherwise there will be a delay
+ // before redrawing).
+ // - When the screen was scrolled but there is no wait-return
+ // prompt.
set_keep_msg(p, 0);
+ }
}
xfree(buffer);
@@ -2921,10 +3032,11 @@ fileinfo (
void col_print(char_u *buf, size_t buflen, int col, int vcol)
{
- if (col == vcol)
+ if (col == vcol) {
vim_snprintf((char *)buf, buflen, "%d", col);
- else
+ } else {
vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol);
+ }
}
/*
@@ -2945,14 +3057,15 @@ void maketitle(void)
char buf[IOSIZE];
if (!redrawing()) {
- /* Postpone updating the title when 'lazyredraw' is set. */
- need_maketitle = TRUE;
+ // Postpone updating the title when 'lazyredraw' is set.
+ need_maketitle = true;
return;
}
- need_maketitle = FALSE;
- if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL)
+ need_maketitle = false;
+ if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL) {
return;
+ }
if (p_title) {
if (p_titlelen > 0) {
@@ -2964,7 +3077,7 @@ void maketitle(void)
if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) {
- int use_sandbox = FALSE;
+ int use_sandbox = false;
int save_called_emsg = called_emsg;
use_sandbox = was_set_insecurely((char_u *)"titlestring", 0);
@@ -3075,11 +3188,11 @@ void maketitle(void)
i_str = (char_u *)buf;
if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) {
- int use_sandbox = FALSE;
+ int use_sandbox = false;
int save_called_emsg = called_emsg;
use_sandbox = was_set_insecurely((char_u *)"iconstring", 0);
- called_emsg = FALSE;
+ called_emsg = false;
build_stl_str_hl(curwin, i_str, sizeof(buf),
p_iconstring, use_sandbox,
0, 0, NULL, NULL);
@@ -3090,17 +3203,19 @@ void maketitle(void)
} else
i_str = p_iconstring;
} else {
- if (buf_spname(curbuf) != NULL)
+ if (buf_spname(curbuf) != NULL) {
i_name = buf_spname(curbuf);
- else /* use file name only in icon */
+ } else { // use file name only in icon
i_name = path_tail(curbuf->b_ffname);
+ }
*i_str = NUL;
- /* Truncate name at 100 bytes. */
+ // Truncate name at 100 bytes.
len = (int)STRLEN(i_name);
if (len > 100) {
len -= 100;
- if (has_mbyte)
+ if (has_mbyte) {
len += (*mb_tail_off)(i_name, i_name + len) + 1;
+ }
i_name += len;
}
STRCPY(i_str, i_name);
@@ -3110,8 +3225,9 @@ void maketitle(void)
mustset |= ti_change(i_str, &lasticon);
- if (mustset)
+ if (mustset) {
resettitle();
+ }
}
/// Used for title and icon: Check if "str" differs from "*last". Set "*last"
@@ -3233,15 +3349,17 @@ int build_stl_str_hl(
// use the result as the actual format string.
if (fmt[0] == '%' && fmt[1] == '!') {
usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
- if (usefmt == NULL)
+ if (usefmt == NULL) {
usefmt = fmt;
+ }
}
- if (fillchar == 0)
+ if (fillchar == 0) {
fillchar = ' ';
- // Can't handle a multi-byte fill character yet.
- else if (mb_char2len(fillchar) > 1)
+ } else if (mb_char2len(fillchar) > 1) {
+ // Can't handle a multi-byte fill character yet.
fillchar = '-';
+ }
// Get line & check if empty (cursorpos will show "0-1").
char_u *line_ptr = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false);
@@ -3296,8 +3414,9 @@ int build_stl_str_hl(
// If we have processed the entire format string or run out of
// room in our output buffer, exit the loop.
- if (*fmt_p == NUL || out_p >= out_end_p)
+ if (*fmt_p == NUL || out_p >= out_end_p) {
break;
+ }
// The rest of this loop will handle a single `%` item.
// Note: We increment here to skip over the `%` character we are currently
@@ -3381,7 +3500,7 @@ int build_stl_str_hl(
// { Determine the number of bytes to remove
long n;
if (has_mbyte) {
- /* Find the first character that should be included. */
+ // Find the first character that should be included.
n = 0;
while (group_len >= items[groupitems[groupdepth]].maxwid) {
group_len -= ptr2cells(t + n);
@@ -3468,8 +3587,9 @@ int build_stl_str_hl(
// The first digit group is the item's min width
if (ascii_isdigit(*fmt_p)) {
minwid = getdigits_int(&fmt_p);
- if (minwid < 0) /* overflow */
+ if (minwid < 0) { // overflow
minwid = 0;
+ }
}
// User highlight groups override the min width field
@@ -3553,8 +3673,9 @@ int build_stl_str_hl(
fmt_p++;
if (ascii_isdigit(*fmt_p)) {
maxwid = getdigits_int(&fmt_p);
- if (maxwid <= 0) /* overflow */
+ if (maxwid <= 0) { // overflow
maxwid = 50;
+ }
}
}
@@ -3606,13 +3727,14 @@ int build_stl_str_hl(
home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, true);
}
trans_characters(NameBuff, MAXPATHL);
- if (opt != STL_FILENAME)
+ if (opt != STL_FILENAME) {
str = NameBuff;
- else
+ } else {
str = path_tail(NameBuff);
+ }
break;
}
- case STL_VIM_EXPR: /* '{' */
+ case STL_VIM_EXPR: // '{'
{
itemisflag = true;
@@ -3621,8 +3743,9 @@ int build_stl_str_hl(
char_u *t = out_p;
while (*fmt_p != '}' && *fmt_p != NUL && out_p < out_end_p)
*out_p++ = *fmt_p++;
- if (*fmt_p != '}') /* missing '}' or out of space */
+ if (*fmt_p != '}') { // missing '}' or out of space
break;
+ }
fmt_p++;
*out_p = 0;
@@ -3689,7 +3812,7 @@ int build_stl_str_hl(
getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
wp->w_p_list = true;
}
- ++virtcol;
+ virtcol++;
// Don't display %V if it's the same as %c.
if (opt == STL_VIRTCOL_ALT
&& (virtcol == (colnr_T)(!(State & INSERT) && empty_line
@@ -3730,8 +3853,9 @@ int build_stl_str_hl(
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN))
+ if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) {
str = tmp;
+ }
break;
case STL_PAGENUM:
num = printer_page_num;
@@ -3758,17 +3882,19 @@ int build_stl_str_hl(
FALLTHROUGH;
case STL_BYTEVAL:
num = byteval;
- if (num == NL)
+ if (num == NL) {
num = 0;
- else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC)
+ } else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) {
num = NL;
+ }
break;
case STL_ROFLAG:
case STL_ROFLAG_ALT:
itemisflag = true;
- if (wp->w_buffer->b_p_ro)
+ if (wp->w_buffer->b_p_ro) {
str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"));
+ }
break;
case STL_HELPFLAG:
@@ -3844,7 +3970,7 @@ int build_stl_str_hl(
// { The name of the highlight is surrounded by `#`
char_u *t = fmt_p;
while (*fmt_p != '#' && *fmt_p != NUL) {
- ++fmt_p;
+ fmt_p++;
}
// }
@@ -3899,8 +4025,9 @@ int build_stl_str_hl(
}
// Early out if there isn't enough room for the truncation marker
- if (out_p >= out_end_p)
+ if (out_p >= out_end_p) {
break;
+ }
// Add the truncation marker
*out_p++ = '<';
@@ -3911,10 +4038,11 @@ int build_stl_str_hl(
if (minwid > 0) {
for (; l < minwid && out_p < out_end_p; l++) {
// Don't put a "-" in front of a digit.
- if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t))
+ if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
*out_p++ = ' ';
- else
+ } else {
*out_p++ = fillchar;
+ }
}
minwid = 0;
} else {
@@ -3941,8 +4069,9 @@ int build_stl_str_hl(
// Otherwise if the item is a number, copy that to the output buffer.
} else if (num >= 0) {
- if (out_p + 20 > out_end_p)
- break; /* not sufficient space */
+ if (out_p + 20 > out_end_p) {
+ break; // not sufficient space
+ }
prevchar_isitem = true;
// { Build the formatting string
@@ -4028,8 +4157,9 @@ int build_stl_str_hl(
xfree(str);
}
- if (num >= 0 || (!itemisflag && str && *str))
- prevchar_isflag = false; /* Item not NULL, but not a flag */
+ if (num >= 0 || (!itemisflag && str && *str)) {
+ prevchar_isflag = false; // Item not NULL, but not a flag
+ }
// Item processed, move to the next
curitem++;
@@ -4082,8 +4212,9 @@ int build_stl_str_hl(
width = 0;
for (;; ) {
width += ptr2cells(trunc_p);
- if (width >= maxwidth)
+ if (width >= maxwidth) {
break;
+ }
// Note: Only advance the pointer if the next
// character will fit in the available output space
@@ -4287,8 +4418,8 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
return;
}
- long above; /* number of lines above window */
- long below; /* number of lines below window */
+ long above; // number of lines above window
+ long below; // number of lines below window
above = wp->w_topline - 1;
above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
@@ -4298,14 +4429,15 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
above = 0;
}
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
- if (below <= 0)
+ if (below <= 0) {
STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen);
- else if (above <= 0)
+ } else if (above <= 0) {
STRLCPY(buf, _("Top"), buflen);
- else
+ } else {
vim_snprintf((char *)buf, (size_t)buflen, "%2d%%", above > 1000000L
- ? (int)(above / ((above + below) / 100L))
- : (int)(above * 100L / (above + below)));
+ ? (int)(above / ((above + below) / 100L))
+ : (int)(above * 100L / (above + below)));
+ }
}
/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
@@ -4350,11 +4482,13 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
*/
void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
{
- if (*ffname == NULL) /* if no file name given, nothing to do */
+ if (*ffname == NULL) { // if no file name given, nothing to do
return;
- if (*sfname == NULL) /* if no short file name given, use ffname */
+ }
+ if (*sfname == NULL) { // if no short file name given, use ffname
*sfname = *ffname;
- *ffname = (char_u *)fix_fname((char *)*ffname); /* expand to full path */
+ }
+ *ffname = (char_u *)fix_fname((char *)*ffname); // expand to full path
#ifdef WIN32
if (!buf->b_p_bin) {
@@ -4376,35 +4510,36 @@ char_u *alist_name(aentry_T *aep)
{
buf_T *bp;
- /* Use the name from the associated buffer if it exists. */
+ // Use the name from the associated buffer if it exists.
bp = buflist_findnr(aep->ae_fnum);
- if (bp == NULL || bp->b_fname == NULL)
+ if (bp == NULL || bp->b_fname == NULL) {
return aep->ae_fname;
+ }
return bp->b_fname;
}
/*
* do_arg_all(): Open up to 'count' windows, one for each argument.
*/
-void
-do_arg_all (
+void
+do_arg_all(
int count,
- int forceit, /* hide buffers in current windows */
- int keep_tabs /* keep current tabs, for ":tab drop file" */
+ int forceit, // hide buffers in current windows
+ int keep_tabs // keep current tabs, for ":tab drop file"
)
{
int i;
- char_u *opened; /* Array of weight for which args are open:
- * 0: not opened
- * 1: opened in other tab
- * 2: opened in curtab
- * 3: opened in curtab and curwin
- */
- int opened_len; /* length of opened[] */
- int use_firstwin = FALSE; /* use first window for arglist */
+ char_u *opened; // Array of weight for which args are open:
+ // 0: not opened
+ // 1: opened in other tab
+ // 2: opened in curtab
+ // 3: opened in curtab and curwin
+
+ int opened_len; // length of opened[]
+ int use_firstwin = false; // use first window for arglist
int split_ret = OK;
bool p_ea_save;
- alist_T *alist; /* argument list to be used */
+ alist_T *alist; // argument list to be used
buf_T *buf;
tabpage_T *tpnext;
int had_tab = cmdmod.tab;
@@ -4429,7 +4564,7 @@ do_arg_all (
* freed while we are working here by "locking" it. We still have to
* watch out for its size to be changed. */
alist = curwin->w_alist;
- ++alist->al_refcount;
+ alist->al_refcount++;
old_curwin = curwin;
old_curtab = curtab;
@@ -4442,8 +4577,9 @@ do_arg_all (
* Windows that have a changed buffer and can't be hidden won't be closed.
* When the ":tab" modifier was used do this for all tab pages.
*/
- if (had_tab > 0)
- goto_tabpage_tp(first_tabpage, TRUE, TRUE);
+ if (had_tab > 0) {
+ goto_tabpage_tp(first_tabpage, true, true);
+ }
for (;; ) {
win_T *wpnext = NULL;
tpnext = curtab->tp_next;
@@ -4455,7 +4591,7 @@ do_arg_all (
i = opened_len;
} else {
// check if the buffer in this window is in the arglist
- for (i = 0; i < opened_len; ++i) {
+ for (i = 0; i < opened_len; i++) {
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(alist)[i]),
@@ -4463,28 +4599,31 @@ do_arg_all (
int weight = 1;
if (old_curtab == curtab) {
- ++weight;
- if (old_curwin == wp)
- ++weight;
+ weight++;
+ if (old_curwin == wp) {
+ weight++;
+ }
}
if (weight > (int)opened[i]) {
opened[i] = (char_u)weight;
if (i == 0) {
- if (new_curwin != NULL)
+ if (new_curwin != NULL) {
new_curwin->w_arg_idx = opened_len;
+ }
new_curwin = wp;
new_curtab = curtab;
}
- } else if (keep_tabs)
+ } else if (keep_tabs) {
i = opened_len;
+ }
if (wp->w_alist != alist) {
/* Use the current argument list for all windows
* containing a file from it. */
alist_unlink(wp->w_alist);
wp->w_alist = alist;
- ++wp->w_alist->al_refcount;
+ wp->w_alist->al_refcount++;
}
break;
}
@@ -4516,33 +4655,36 @@ do_arg_all (
// check if autocommands removed the next window
if (!win_valid(wpnext)) {
// start all over...
- wpnext = firstwin;
+ wpnext = firstwin;
}
}
}
}
}
- /* Without the ":tab" modifier only do the current tab page. */
- if (had_tab == 0 || tpnext == NULL)
+ // Without the ":tab" modifier only do the current tab page.
+ if (had_tab == 0 || tpnext == NULL) {
break;
+ }
- /* check if autocommands removed the next tab page */
- if (!valid_tabpage(tpnext))
- tpnext = first_tabpage; /* start all over...*/
- goto_tabpage_tp(tpnext, TRUE, TRUE);
+ // check if autocommands removed the next tab page
+ if (!valid_tabpage(tpnext)) {
+ tpnext = first_tabpage; // start all over...
+ }
+ goto_tabpage_tp(tpnext, true, true);
}
/*
* Open a window for files in the argument list that don't have one.
* ARGCOUNT may change while doing this, because of autocommands.
*/
- if (count > opened_len || count <= 0)
+ if (count > opened_len || count <= 0) {
count = opened_len;
+ }
- /* Don't execute Win/Buf Enter/Leave autocommands here. */
- ++autocmd_no_enter;
- ++autocmd_no_leave;
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
+ autocmd_no_leave++;
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
@@ -4558,7 +4700,7 @@ do_arg_all (
arg_had_last = true;
}
if (opened[i] > 0) {
- /* Move the already present window to below the current window */
+ // Move the already present window to below the current window
if (curwin->w_arg_idx != i) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_arg_idx == i) {
@@ -4573,15 +4715,17 @@ do_arg_all (
}
}
} else if (split_ret == OK) {
- if (!use_firstwin) { /* split current window */
+ if (!use_firstwin) { // split current window
p_ea_save = p_ea;
- p_ea = true; /* use space from all windows */
+ p_ea = true; // use space from all windows
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
p_ea = p_ea_save;
- if (split_ret == FAIL)
+ if (split_ret == FAIL) {
continue;
- } else /* first window: do autocmd for leaving this buffer */
- --autocmd_no_leave;
+ }
+ } else { // first window: do autocmd for leaving this buffer
+ autocmd_no_leave--;
+ }
/*
* edit file "i"
@@ -4603,12 +4747,13 @@ do_arg_all (
}
os_breakcheck();
- /* When ":tab" was used open a new tab for a new window repeatedly. */
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
+ // When ":tab" was used open a new tab for a new window repeatedly.
+ if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
cmdmod.tab = 9999;
+ }
}
- /* Remove the "lock" on the argument list. */
+ // Remove the "lock" on the argument list.
alist_unlink(alist);
autocmd_no_enter--;
@@ -4629,7 +4774,7 @@ do_arg_all (
win_enter(new_curwin, false);
}
- --autocmd_no_leave;
+ autocmd_no_leave--;
xfree(opened);
}
@@ -4645,18 +4790,20 @@ void ex_buffer_all(exarg_T *eap)
int open_wins = 0;
int r;
long count; // Maximum number of windows to open.
- int all; // When TRUE also load inactive buffers.
+ int all; // When true also load inactive buffers.
int had_tab = cmdmod.tab;
tabpage_T *tpnext;
- if (eap->addr_count == 0) /* make as many windows as possible */
+ if (eap->addr_count == 0) { // make as many windows as possible
count = 9999;
- else
- count = eap->line2; /* make as many windows as specified */
- if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide)
- all = FALSE;
- else
- all = TRUE;
+ } else {
+ count = eap->line2; // make as many windows as specified
+ }
+ if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide) {
+ all = false;
+ } else {
+ all = true;
+ }
setpcmark();
@@ -4665,8 +4812,9 @@ void ex_buffer_all(exarg_T *eap)
* Close superfluous windows (two windows for the same buffer).
* Also close windows that are not full-width.
*/
- if (had_tab > 0)
- goto_tabpage_tp(first_tabpage, TRUE, TRUE);
+ if (had_tab > 0) {
+ goto_tabpage_tp(first_tabpage, true, true);
+ }
for (;; ) {
tpnext = curtab->tp_next;
for (wp = firstwin; wp != NULL; wp = wpnext) {
@@ -4685,44 +4833,51 @@ void ex_buffer_all(exarg_T *eap)
// something strange with windows
tpnext = first_tabpage; // start all over...
open_wins = 0;
- } else
- ++open_wins;
+ } else {
+ open_wins++;
+ }
}
- /* Without the ":tab" modifier only do the current tab page. */
- if (had_tab == 0 || tpnext == NULL)
+ // Without the ":tab" modifier only do the current tab page.
+ if (had_tab == 0 || tpnext == NULL) {
break;
- goto_tabpage_tp(tpnext, TRUE, TRUE);
+ }
+ goto_tabpage_tp(tpnext, true, true);
}
- /*
- * Go through the buffer list. When a buffer doesn't have a window yet,
- * open one. Otherwise move the window to the right position.
- * Watch out for autocommands that delete buffers or windows!
- */
- /* Don't execute Win/Buf Enter/Leave autocommands here. */
- ++autocmd_no_enter;
+ //
+ // Go through the buffer list. When a buffer doesn't have a window yet,
+ // open one. Otherwise move the window to the right position.
+ // Watch out for autocommands that delete buffers or windows!
+ //
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
win_enter(lastwin, false);
- ++autocmd_no_leave;
+ autocmd_no_leave++;
for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) {
- /* Check if this buffer needs a window */
- if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl)
+ // Check if this buffer needs a window
+ if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) {
continue;
+ }
if (had_tab != 0) {
- /* With the ":tab" modifier don't move the window. */
- if (buf->b_nwindows > 0)
- wp = lastwin; /* buffer has a window, skip it */
- else
+ // With the ":tab" modifier don't move the window.
+ if (buf->b_nwindows > 0) {
+ wp = lastwin; // buffer has a window, skip it
+ } else {
wp = NULL;
+ }
} else {
- /* Check if this buffer already has a window */
- for (wp = firstwin; wp != NULL; wp = wp->w_next)
- if (wp->w_buffer == buf)
+ // Check if this buffer already has a window
+ for (wp = firstwin; wp != NULL; wp = wp->w_next) {
+ if (wp->w_buffer == buf) {
break;
- /* If the buffer already has a window, move it */
- if (wp != NULL)
+ }
+ }
+ // If the buffer already has a window, move it
+ if (wp != NULL) {
win_move_after(wp, curwin);
+ }
}
if (wp == NULL && split_ret == OK) {
@@ -4730,14 +4885,15 @@ void ex_buffer_all(exarg_T *eap)
set_bufref(&bufref, buf);
// Split the window and put the buffer in it.
p_ea_save = p_ea;
- p_ea = true; /* use space from all windows */
+ p_ea = true; // use space from all windows
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
- ++open_wins;
+ open_wins++;
p_ea = p_ea_save;
- if (split_ret == FAIL)
+ if (split_ret == FAIL) {
continue;
+ }
- /* Open the buffer in this window. */
+ // Open the buffer in this window.
swap_exists_action = SEA_DIALOG;
set_curbuf(buf, DOBUF_GOTO);
if (!bufref_valid(&bufref)) {
@@ -4748,15 +4904,15 @@ void ex_buffer_all(exarg_T *eap)
if (swap_exists_action == SEA_QUIT) {
cleanup_T cs;
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a window. */
+ // Reset the error/interrupt/exception state here so that
+ // aborting() returns false when closing a window.
enter_cleanup(&cs);
// User selected Quit at ATTENTION prompt; close this window.
win_close(curwin, true);
open_wins--;
swap_exists_action = SEA_NONE;
- swap_exists_did_quit = TRUE;
+ swap_exists_did_quit = true;
/* Restore the error/interrupt/exception state if not
* discarded by a new aborting error, interrupt, or uncaught
@@ -4768,19 +4924,21 @@ void ex_buffer_all(exarg_T *eap)
os_breakcheck();
if (got_int) {
- (void)vgetc(); /* only break the file loading, not the rest */
+ (void)vgetc(); // only break the file loading, not the rest
break;
}
- /* Autocommands deleted the buffer or aborted script processing!!! */
- if (aborting())
+ // Autocommands deleted the buffer or aborted script processing!!!
+ if (aborting()) {
break;
- /* When ":tab" was used open a new tab for a new window repeatedly. */
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
+ }
+ // When ":tab" was used open a new tab for a new window repeatedly.
+ if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
cmdmod.tab = 9999;
+ }
}
- --autocmd_no_enter;
- win_enter(firstwin, false); /* back to first window */
- --autocmd_no_leave;
+ autocmd_no_enter--;
+ win_enter(firstwin, false); // back to first window
+ autocmd_no_leave--;
/*
* Close superfluous windows.
@@ -4789,7 +4947,7 @@ void ex_buffer_all(exarg_T *eap)
r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
|| autowrite(wp->w_buffer, false) == OK);
if (!win_valid(wp)) {
- /* BufWrite Autocommands made the window invalid, start over */
+ // BufWrite Autocommands made the window invalid, start over
wp = lastwin;
} else if (r) {
win_close(wp, !buf_hide(wp->w_buffer));
@@ -4797,8 +4955,9 @@ void ex_buffer_all(exarg_T *eap)
wp = lastwin;
} else {
wp = wp->w_prev;
- if (wp == NULL)
+ if (wp == NULL) {
break;
+ }
}
}
}
@@ -4820,40 +4979,46 @@ void do_modelines(int flags)
int nmlines;
static int entered = 0;
- if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
+ if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0) {
return;
+ }
/* Disallow recursive entry here. Can happen when executing a modeline
* triggers an autocommand, which reloads modelines with a ":do". */
- if (entered)
+ if (entered) {
return;
+ }
- ++entered;
+ entered++;
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines;
- ++lnum)
- if (chk_modeline(lnum, flags) == FAIL)
+ lnum++) {
+ if (chk_modeline(lnum, flags) == FAIL) {
nmlines = 0;
+ }
+ }
for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines
- && lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
- if (chk_modeline(lnum, flags) == FAIL)
+ && lnum > curbuf->b_ml.ml_line_count - nmlines; lnum--) {
+ if (chk_modeline(lnum, flags) == FAIL) {
nmlines = 0;
- --entered;
+ }
+ }
+ entered--;
}
/*
* chk_modeline() - check a single line for a mode string
* Return FAIL if an error encountered.
*/
-static int
-chk_modeline (
+static int
+chk_modeline(
linenr_T lnum,
- int flags /* Same as for do_modelines(). */
+ int flags // Same as for do_modelines().
)
{
char_u *s;
char_u *e;
- char_u *linecopy; /* local copy of any modeline found */
+ char_u *linecopy; // local copy of any modeline found
int prev;
intmax_t vers;
int end;
@@ -4863,17 +5028,18 @@ chk_modeline (
scid_T save_SID;
prev = -1;
- for (s = ml_get(lnum); *s != NUL; ++s) {
+ for (s = ml_get(lnum); *s != NUL; s++) {
if (prev == -1 || ascii_isspace(prev)) {
if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0)
|| STRNCMP(s, "vi:", (size_t)3) == 0)
break;
- /* Accept both "vim" and "Vim". */
+ // Accept both "vim" and "Vim".
if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm') {
- if (s[3] == '<' || s[3] == '=' || s[3] == '>')
+ if (s[3] == '<' || s[3] == '=' || s[3] == '>') {
e = s + 4;
- else
+ } else {
e = s + 3;
+ }
if (getdigits_safe(&e, &vers) != OK) {
continue;
}
@@ -4897,32 +5063,36 @@ chk_modeline (
return retval;
}
- do /* skip over "ex:", "vi:" or "vim:" */
- ++s;
- while (s[-1] != ':');
+ do { // skip over "ex:", "vi:" or "vim:"
+ s++;
+ } while (s[-1] != ':');
- s = linecopy = vim_strsave(s); /* copy the line, it will change */
+ s = linecopy = vim_strsave(s); // copy the line, it will change
save_sourcing_lnum = sourcing_lnum;
save_sourcing_name = sourcing_name;
- sourcing_lnum = lnum; /* prepare for emsg() */
+ sourcing_lnum = lnum; // prepare for emsg()
sourcing_name = (char_u *)"modelines";
- end = FALSE;
- while (end == FALSE) {
+ end = false;
+ while (end == false) {
s = skipwhite(s);
- if (*s == NUL)
+ if (*s == NUL) {
break;
+ }
/*
* Find end of set command: ':' or end of line.
* Skip over "\:", replacing it with ":".
*/
- for (e = s; *e != ':' && *e != NUL; ++e)
- if (e[0] == '\\' && e[1] == ':')
+ for (e = s; *e != ':' && *e != NUL; e++) {
+ if (e[0] == '\\' && e[1] == ':') {
STRMOVE(e, e + 1);
- if (*e == NUL)
- end = TRUE;
+ }
+ }
+ if (*e == NUL) {
+ end = true;
+ }
/*
* If there is a "set" command, require a terminating ':' and
@@ -4933,22 +5103,30 @@ chk_modeline (
*/
if (STRNCMP(s, "set ", (size_t)4) == 0
|| STRNCMP(s, "se ", (size_t)3) == 0) {
- if (*e != ':') /* no terminating ':'? */
+ if (*e != ':') { // no terminating ':'?
break;
- end = TRUE;
+ }
+ end = true;
s = vim_strchr(s, ' ') + 1;
}
- *e = NUL; /* truncate the set command */
+ *e = NUL; // truncate the set command
- if (*s != NUL) { /* skip over an empty "::" */
+ if (*s != NUL) { // skip over an empty "::"
+ const int secure_save = secure;
save_SID = current_SID;
current_SID = SID_MODELINE;
+ // Make sure no risky things are executed as a side effect.
+ secure = 1;
+
retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
+
+ secure = secure_save;
current_SID = save_SID;
- if (retval == FAIL) /* stop if error found */
+ if (retval == FAIL) { // stop if error found
break;
+ }
}
- s = e + 1; /* advance to next part */
+ s = e + 1; // advance to next part
}
sourcing_lnum = save_sourcing_lnum;
@@ -4965,6 +5143,55 @@ bool bt_help(const buf_T *const buf)
return buf != NULL && buf->b_help;
}
+// Return true if "buf" is the quickfix buffer.
+bool bt_quickfix(const buf_T *const buf)
+{
+ return buf != NULL && buf->b_p_bt[0] == 'q';
+}
+
+// Return true if "buf" is a terminal buffer.
+bool bt_terminal(const buf_T *const buf)
+{
+ return buf != NULL && buf->b_p_bt[0] == 't';
+}
+
+// Return true if "buf" is a "nofile", "acwrite" or "terminal" buffer.
+// This means the buffer name is not a file name.
+bool bt_nofile(const buf_T *const buf)
+{
+ return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
+ || buf->b_p_bt[0] == 'a' || buf->terminal);
+}
+
+// Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer.
+bool bt_dontwrite(const buf_T *const buf)
+{
+ return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal);
+}
+
+bool bt_dontwrite_msg(const buf_T *const buf)
+{
+ if (bt_dontwrite(buf)) {
+ EMSG(_("E382: Cannot write, 'buftype' option is set"));
+ return true;
+ }
+ return false;
+}
+
+// Return true if the buffer should be hidden, according to 'hidden', ":hide"
+// and 'bufhidden'.
+bool buf_hide(const buf_T *const buf)
+{
+ // 'bufhidden' overrules 'hidden' and ":hide", check it first
+ switch (buf->b_p_bh[0]) {
+ case 'u': // "unload"
+ case 'w': // "wipe"
+ case 'd': return false; // "delete"
+ case 'h': return true; // "hide"
+ }
+ return p_hid || cmdmod.hide;
+}
+
/*
* Return special buffer name.
* Returns NULL when the buffer has a normal file name.
@@ -4979,20 +5206,23 @@ char_u *buf_spname(buf_T *buf)
* For location list window, w_llist_ref points to the location list.
* For quickfix window, w_llist_ref is NULL.
*/
- if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL)
+ if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL) {
return (char_u *)_(msg_loclist);
- else
+ } else {
return (char_u *)_(msg_qflist);
+ }
}
- /* There is no _file_ when 'buftype' is "nofile", b_sfname
- * contains the name as specified by the user */
+ // There is no _file_ when 'buftype' is "nofile", b_sfname
+ // contains the name as specified by the user.
if (bt_nofile(buf)) {
- if (buf->b_sfname != NULL)
+ if (buf->b_sfname != NULL) {
return buf->b_sfname;
+ }
return (char_u *)_("[Scratch]");
}
- if (buf->b_fname == NULL)
+ if (buf->b_fname == NULL) {
return (char_u *)_("[No Name]");
+ }
return NULL;
}
@@ -5024,19 +5254,24 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
* Insert the sign into the signlist.
*/
static void insert_sign(
- buf_T *buf, /* buffer to store sign in */
- signlist_T *prev, /* previous sign entry */
- signlist_T *next, /* next sign entry */
- int id, /* sign ID */
- linenr_T lnum, /* line number which gets the mark */
- int typenr /* typenr of sign we are adding */
- )
+ buf_T *buf, // buffer to store sign in
+ signlist_T *prev, // previous sign entry
+ signlist_T *next, // next sign entry
+ int id, // sign ID
+ linenr_T lnum, // line number which gets the mark
+ int typenr // typenr of sign we are adding
+)
{
signlist_T *newsign = xmalloc(sizeof(signlist_T));
newsign->id = id;
newsign->lnum = lnum;
newsign->typenr = typenr;
newsign->next = next;
+ newsign->prev = prev;
+ if (next != NULL) {
+ next->prev = newsign;
+ }
+ buf->b_signcols_max = -1;
if (prev == NULL) {
/* When adding first sign need to redraw the windows to create the
@@ -5046,7 +5281,7 @@ static void insert_sign(
changed_cline_bef_curs();
}
- /* first sign in signlist */
+ // first sign in signlist
buf->b_signlist = newsign;
}
else {
@@ -5054,18 +5289,109 @@ static void insert_sign(
}
}
+static int sign_compare(const void *a1, const void *a2)
+{
+ const signlist_T *s1 = *(const signlist_T **)a1;
+ const signlist_T *s2 = *(const signlist_T **)a2;
+
+ // Sort by line number and the by id
+
+ if (s1->lnum > s2->lnum) {
+ return 1;
+ }
+ if (s1->lnum < s2->lnum) {
+ return -1;
+ }
+ if (s1->id > s2->id) {
+ return 1;
+ }
+ if (s1->id < s2->id) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int buf_signcols(buf_T *buf)
+{
+ if (buf->b_signcols_max == -1) {
+ signlist_T *sign; // a sign in the signlist
+ signlist_T **signs_array;
+ signlist_T **prev_sign;
+ int nr_signs = 0, i = 0, same;
+
+ // Count the number of signs
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ nr_signs++;
+ }
+
+ // Make an array of all the signs
+ signs_array = xcalloc((size_t)nr_signs, sizeof(*sign));
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ signs_array[i] = sign;
+ i++;
+ }
+
+ // Sort the array
+ qsort(signs_array, (size_t)nr_signs, sizeof(signlist_T *),
+ sign_compare);
+
+ // Find the maximum amount of signs existing in a single line
+ buf->b_signcols_max = 0;
+
+ same = 1;
+ for (i = 1; i < nr_signs; i++) {
+ if (signs_array[i - 1]->lnum != signs_array[i]->lnum) {
+ if (buf->b_signcols_max < same) {
+ buf->b_signcols_max = same;
+ }
+ same = 1;
+ } else {
+ same++;
+ }
+ }
+
+ if (nr_signs > 0 && buf->b_signcols_max < same) {
+ buf->b_signcols_max = same;
+ }
+
+ // Recreate the linked list with the sorted order of the array
+ buf->b_signlist = NULL;
+ prev_sign = &buf->b_signlist;
+
+ for (i = 0; i < nr_signs; i++) {
+ sign = signs_array[i];
+ sign->next = NULL;
+ *prev_sign = sign;
+
+ prev_sign = &sign->next;
+ }
+
+ xfree(signs_array);
+
+ // Check if we need to redraw
+ if (buf->b_signcols_max != buf->b_signcols) {
+ buf->b_signcols = buf->b_signcols_max;
+ redraw_buf_later(buf, NOT_VALID);
+ }
+ }
+
+ return buf->b_signcols;
+}
+
/*
* Add the sign into the signlist. Find the right spot to do it though.
*/
void buf_addsign(
- buf_T *buf, /* buffer to store sign in */
- int id, /* sign ID */
- linenr_T lnum, /* line number which gets the mark */
- int typenr /* typenr of sign we are adding */
- )
+ buf_T *buf, // buffer to store sign in
+ int id, // sign ID
+ linenr_T lnum, // line number which gets the mark
+ int typenr // typenr of sign we are adding
+)
{
- signlist_T *sign; /* a sign in the signlist */
- signlist_T *prev; /* the previous sign */
+ signlist_T **lastp; // pointer to pointer to current sign
+ signlist_T *sign; // a sign in the signlist
+ signlist_T *prev; // the previous sign
prev = NULL;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
@@ -5073,26 +5399,57 @@ void buf_addsign(
sign->typenr = typenr;
return;
} else if ((lnum == sign->lnum && id != sign->id)
- || (id < 0 && lnum < sign->lnum)) { // attempt to keep signs sorted by lnum
- insert_sign(buf, prev, sign, id, lnum, typenr);
- return;
+ || (id < 0 && lnum < sign->lnum)) {
+ // keep signs sorted by lnum: insert new sign at head of list for
+ // this lnum
+ while (prev != NULL && prev->lnum == lnum) {
+ prev = prev->prev;
+ }
+ if (prev == NULL) {
+ sign = buf->b_signlist;
+ } else {
+ sign = prev->next;
+ }
+ insert_sign(buf, prev, sign, id, lnum, typenr);
+ return;
}
prev = sign;
}
+
+ // insert new sign at head of list for this lnum
+ while (prev != NULL && prev->lnum == lnum) {
+ prev = prev->prev;
+ }
+ if (prev == NULL) {
+ sign = buf->b_signlist;
+ } else {
+ sign = prev->next;
+ }
insert_sign(buf, prev, sign, id, lnum, typenr);
- return;
+ // Having more than one sign with _the same type_ and on the _same line_ is
+ // unwanted, let's prevent it.
+
+ lastp = &buf->b_signlist;
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ if (lnum == sign->lnum && sign->typenr == typenr && id != sign->id) {
+ *lastp = sign->next;
+ xfree(sign);
+ } else {
+ lastp = &sign->next;
+ }
+ }
}
// For an existing, placed sign "markId" change the type to "typenr".
// Returns the line number of the sign, or zero if the sign is not found.
linenr_T buf_change_sign_type(
- buf_T *buf, /* buffer to store sign in */
- int markId, /* sign ID */
- int typenr /* typenr of sign we are adding */
- )
+ buf_T *buf, // buffer to store sign in
+ int markId, // sign ID
+ int typenr // typenr of sign we are adding
+)
{
- signlist_T *sign; /* a sign in the signlist */
+ signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->id == markId) {
@@ -5104,16 +5461,23 @@ linenr_T buf_change_sign_type(
return (linenr_T)0;
}
+
/// Gets a sign from a given line.
-/// In case of multiple signs, returns the most recently placed one.
///
/// @param buf Buffer in which to search
/// @param lnum Line in which to search
/// @param type Type of sign to look for
-/// @return Identifier of the first matching sign, or 0
-int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
+/// @param idx if there multiple signs, this index will pick the n-th
+// out of the most `max_signs` sorted ascending by Id.
+/// @param max_signs the number of signs, with priority for the ones
+// with the highest Ids.
+/// @return Identifier of the matching sign, or 0
+int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type,
+ int idx, int max_signs)
{
signlist_T *sign; // a sign in a b_signlist
+ signlist_T *matches[9];
+ int nr_matches = 0;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum
@@ -5124,29 +5488,50 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
&& sign_get_attr(sign->typenr, SIGN_LINEHL) != 0)
|| (type == SIGN_NUMHL
&& sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) {
- return sign->typenr;
+ matches[nr_matches] = sign;
+ nr_matches++;
+
+ if (nr_matches == ARRAY_SIZE(matches)) {
+ break;
+ }
}
}
+
+ if (nr_matches > 0) {
+ if (nr_matches > max_signs) {
+ idx += nr_matches - max_signs;
+ }
+
+ if (idx >= nr_matches) {
+ return 0;
+ }
+
+ return matches[idx]->typenr;
+ }
+
return 0;
}
-
linenr_T buf_delsign(
- buf_T *buf, /* buffer sign is stored in */
- int id /* sign id */
- )
+ buf_T *buf, // buffer sign is stored in
+ int id // sign id
+)
{
- signlist_T **lastp; /* pointer to pointer to current sign */
- signlist_T *sign; /* a sign in a b_signlist */
- signlist_T *next; /* the next sign in a b_signlist */
- linenr_T lnum; /* line number whose sign was deleted */
+ signlist_T **lastp; // pointer to pointer to current sign
+ signlist_T *sign; // a sign in a b_signlist
+ signlist_T *next; // the next sign in a b_signlist
+ linenr_T lnum; // line number whose sign was deleted
+ buf->b_signcols_max = -1;
lastp = &buf->b_signlist;
lnum = 0;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
next = sign->next;
if (sign->id == id) {
*lastp = next;
+ if (next != NULL) {
+ next->prev = sign->prev;
+ }
lnum = sign->lnum;
xfree(sign);
break;
@@ -5172,11 +5557,11 @@ linenr_T buf_delsign(
* get loaded.
*/
int buf_findsign(
- buf_T *buf, /* buffer to store sign in */
- int id /* sign ID */
- )
+ buf_T *buf, // buffer to store sign in
+ int id // sign ID
+)
{
- signlist_T *sign; /* a sign in the signlist */
+ signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->id == id) {
@@ -5188,11 +5573,11 @@ int buf_findsign(
}
int buf_findsign_id(
- buf_T *buf, /* buffer whose sign we are searching for */
- linenr_T lnum /* line number of sign */
- )
+ buf_T *buf, // buffer whose sign we are searching for
+ linenr_T lnum // line number of sign
+)
{
- signlist_T *sign; /* a sign in the signlist */
+ signlist_T *sign; // a sign in the signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum) {
@@ -5223,6 +5608,7 @@ void buf_delete_signs(buf_T *buf)
xfree(buf->b_signlist);
buf->b_signlist = next;
}
+ buf->b_signcols_max = -1;
}
/*
@@ -5277,18 +5663,27 @@ void sign_list_placed(buf_T *rbuf)
*/
void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
{
- signlist_T *sign; /* a sign in a b_signlist */
+ signlist_T *sign; // a sign in a b_signlist
+ signlist_T *next; // the next sign in a b_signlist
+ signlist_T **lastp; // pointer to pointer to current sign
+
+ curbuf->b_signcols_max = -1;
+ lastp = &curbuf->b_signlist;
- for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) {
+ for (sign = curbuf->b_signlist; sign != NULL; sign = next) {
+ next = sign->next;
if (sign->lnum >= line1 && sign->lnum <= line2) {
if (amount == MAXLNUM) {
- sign->lnum = line1;
+ *lastp = next;
+ xfree(sign);
+ continue;
} else {
sign->lnum += amount;
}
- }
- else if (sign->lnum > line2)
+ } else if (sign->lnum > line2) {
sign->lnum += amount_after;
+ }
+ lastp = &sign->next;
}
}
@@ -5642,10 +6037,11 @@ void set_buflisted(int on)
{
if (on != curbuf->b_p_bl) {
curbuf->b_p_bl = on;
- if (on)
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
- else
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
+ if (on) {
+ apply_autocmds(EVENT_BUFADD, NULL, NULL, false, curbuf);
+ } else {
+ apply_autocmds(EVENT_BUFDELETE, NULL, NULL, false, curbuf);
+ }
}
}
@@ -5681,7 +6077,7 @@ bool buf_contents_changed(buf_T *buf)
// compare the two files line by line
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
differ = false;
- for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
+ for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) {
differ = true;
break;
@@ -5706,10 +6102,10 @@ bool buf_contents_changed(buf_T *buf)
* this buffer. Call this to wipe out a temp buffer that does not contain any
* marks.
*/
-void
-wipe_buffer (
+void
+wipe_buffer(
buf_T *buf,
- int aucmd /* When TRUE trigger autocommands. */
+ int aucmd // When true trigger autocommands.
)
{
if (!aucmd) {
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 64c906fc96..ee3fda5f6d 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -74,6 +74,8 @@ static inline void buf_set_changedtick(buf_T *const buf,
static inline void buf_set_changedtick(buf_T *const buf,
const varnumber_T changedtick)
{
+ typval_T old_val = buf->changedtick_di.di_tv;
+
#ifndef NDEBUG
dictitem_T *const changedtick_di = tv_dict_find(
buf->b_vars, S_LEN("changedtick"));
@@ -87,6 +89,13 @@ static inline void buf_set_changedtick(buf_T *const buf,
assert(changedtick_di == (dictitem_T *)&buf->changedtick_di);
#endif
buf->changedtick_di.di_tv.vval.v_number = changedtick;
+
+ if (tv_dict_is_watched(buf->b_vars)) {
+ tv_dict_watcher_notify(buf->b_vars,
+ (char *)buf->changedtick_di.di_key,
+ &buf->changedtick_di.di_tv,
+ &old_val);
+ }
}
static inline varnumber_T buf_get_changedtick(const buf_T *const buf)
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 48cef9b1e7..5e28a7b513 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -388,24 +388,25 @@ typedef struct {
* a window may have its own instance.
*/
typedef struct {
- hashtab_T b_keywtab; /* syntax keywords hash table */
- hashtab_T b_keywtab_ic; /* idem, ignore case */
- int b_syn_error; /* TRUE when error occurred in HL */
- int b_syn_ic; /* ignore case for :syn cmds */
- int b_syn_spell; /* SYNSPL_ values */
- garray_T b_syn_patterns; /* table for syntax patterns */
- garray_T b_syn_clusters; /* table for syntax clusters */
- int b_spell_cluster_id; /* @Spell cluster ID or 0 */
- int b_nospell_cluster_id; /* @NoSpell cluster ID or 0 */
- int b_syn_containedin; /* TRUE when there is an item with a
- "containedin" argument */
- int b_syn_sync_flags; /* flags about how to sync */
- short b_syn_sync_id; /* group to sync on */
- long b_syn_sync_minlines; /* minimal sync lines offset */
- long b_syn_sync_maxlines; /* maximal sync lines offset */
- long b_syn_sync_linebreaks; /* offset for multi-line pattern */
- char_u *b_syn_linecont_pat; /* line continuation pattern */
- regprog_T *b_syn_linecont_prog; /* line continuation program */
+ hashtab_T b_keywtab; // syntax keywords hash table
+ hashtab_T b_keywtab_ic; // idem, ignore case
+ int b_syn_error; // TRUE when error occurred in HL
+ bool b_syn_slow; // true when 'redrawtime' reached
+ int b_syn_ic; // ignore case for :syn cmds
+ int b_syn_spell; // SYNSPL_ values
+ garray_T b_syn_patterns; // table for syntax patterns
+ garray_T b_syn_clusters; // table for syntax clusters
+ int b_spell_cluster_id; // @Spell cluster ID or 0
+ int b_nospell_cluster_id; // @NoSpell cluster ID or 0
+ int b_syn_containedin; // TRUE when there is an item with a
+ // "containedin" argument
+ int b_syn_sync_flags; // flags about how to sync
+ int16_t b_syn_sync_id; // group to sync on
+ long b_syn_sync_minlines; // minimal sync lines offset
+ long b_syn_sync_maxlines; // maximal sync lines offset
+ long b_syn_sync_linebreaks; // offset for multi-line pattern
+ char_u *b_syn_linecont_pat; // line continuation pattern
+ regprog_T *b_syn_linecont_prog; // line continuation program
syn_time_T b_syn_linecont_time;
int b_syn_linecont_ic; /* ignore-case flag for above */
int b_syn_topgrp; /* for ":syntax include" */
@@ -778,7 +779,9 @@ struct file_buffer {
* normally points to this, but some windows
* may use a different synblock_T. */
- signlist_T *b_signlist; /* list of signs to draw */
+ signlist_T *b_signlist; // list of signs to draw
+ int b_signcols_max; // cached maximum number of sign columns
+ int b_signcols; // last calculated number of sign columns
Terminal *terminal; // Terminal instance associated with the buffer
@@ -958,24 +961,32 @@ struct matchitem {
int conceal_char; ///< cchar for Conceal highlighting
};
-typedef enum {
- kFloatAnchorEast = 1,
- kFloatAnchorSouth = 2,
+typedef int FloatAnchor;
+typedef int FloatRelative;
- kFloatAnchorNW = 0,
- kFloatAnchorNE = 1,
- kFloatAnchorSW = 2,
- kFloatAnchorSE = 3,
-} FloatAnchor;
+enum {
+ kFloatAnchorEast = 1,
+ kFloatAnchorSouth = 2,
+};
+
+// NW -> 0
+// NE -> kFloatAnchorEast
+// SW -> kFloatAnchorSouth
+// SE -> kFloatAnchorSouth | kFloatAnchorEast
+EXTERN const char *const float_anchor_str[] INIT(= { "NW", "NE", "SW", "SE" });
+
+enum {
+ kFloatRelativeEditor = 0,
+ kFloatRelativeWindow = 1,
+ kFloatRelativeCursor = 2,
+};
-typedef enum {
- kFloatRelativeEditor = 0,
- kFloatRelativeWindow = 1,
- kFloatRelativeCursor = 2,
-} FloatRelative;
+EXTERN const char *const float_relative_str[] INIT(= { "editor", "window",
+ "cursor" });
typedef struct {
Window window;
+ int height, width;
double row, col;
FloatAnchor anchor;
FloatRelative relative;
@@ -983,22 +994,31 @@ typedef struct {
bool focusable;
} FloatConfig;
-#define FLOAT_CONFIG_INIT ((FloatConfig){ .row = 0, .col = 0, .anchor = 0, \
+#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
+ .row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \
.focusable = true })
-/*
- * Structure which contains all information that belongs to a window
- *
- * All row numbers are relative to the start of the window, except w_winrow.
- */
+// Structure to store last cursor position and topline. Used by check_lnums()
+// and reset_lnums().
+typedef struct
+{
+ int w_topline_save; // original topline value
+ int w_topline_corr; // corrected topline value
+ pos_T w_cursor_save; // original cursor position
+ pos_T w_cursor_corr; // corrected cursor position
+} pos_save_T;
+
+/// Structure which contains all information that belongs to a window.
+///
+/// All row numbers are relative to the start of the window, except w_winrow.
struct window_S {
handle_T handle; ///< unique identifier for the window
buf_T *w_buffer; ///< buffer we are a window into (used
///< often, keep it the first item!)
- synblock_T *w_s; /* for :ownsyntax */
+ synblock_T *w_s; ///< for :ownsyntax
int w_hl_id_normal; ///< 'winhighlight' normal id
int w_hl_attr_normal; ///< 'winhighlight' normal final attrs
@@ -1008,24 +1028,25 @@ struct window_S {
int w_hl_needs_update; ///< attrs need to be recalculated
- win_T *w_prev; /* link to previous window */
- win_T *w_next; /* link to next window */
- bool w_closing; /* window is being closed, don't let
- autocommands close it too. */
+ win_T *w_prev; ///< link to previous window
+ win_T *w_next; ///< link to next window
+ bool w_closing; ///< window is being closed, don't let
+ /// autocommands close it too.
- frame_T *w_frame; /* frame containing this window */
+ frame_T *w_frame; ///< frame containing this window
- pos_T w_cursor; /* cursor position in buffer */
+ pos_T w_cursor; ///< cursor position in buffer
- colnr_T w_curswant; /* The column we'd like to be at. This is
- used to try to stay in the same column
- for up/down cursor motions. */
+ colnr_T w_curswant; ///< Column we want to be at. This is
+ /// used to try to stay in the same column
+ /// for up/down cursor motions.
int w_set_curswant; // If set, then update w_curswant the next
// time through cursupdate() to the
// current virtual column
linenr_T w_last_cursorline; ///< where last 'cursorline' was drawn
+ pos_T w_last_cursormoved; ///< for CursorMoved event
// the next seven are used to update the visual part
char w_old_visual_mode; ///< last known VIsual_mode
@@ -1080,17 +1101,18 @@ struct window_S {
colnr_T w_skipcol; /* starting column when a single line
doesn't fit in the window */
- /*
- * Layout of the window in the screen.
- * May need to add "msg_scrolled" to "w_winrow" in rare situations.
- */
- int w_winrow; /* first row of window in screen */
- int w_height; /* number of rows in window, excluding
- status/command line(s) */
- int w_status_height; /* number of status lines (0 or 1) */
- int w_wincol; /* Leftmost column of window in screen. */
- int w_width; /* Width of window, excluding separation. */
- int w_vsep_width; /* Number of separator columns (0 or 1). */
+ //
+ // Layout of the window in the screen.
+ // May need to add "msg_scrolled" to "w_winrow" in rare situations.
+ //
+ int w_winrow; // first row of window in screen
+ int w_height; // number of rows in window, excluding
+ // status/command line(s)
+ int w_status_height; // number of status lines (0 or 1)
+ int w_wincol; // Leftmost column of window in screen.
+ int w_width; // Width of window, excluding separation.
+ int w_vsep_width; // Number of separator columns (0 or 1).
+ pos_save_T w_save_cursor; // backup of cursor pos and topline
// inner size of window, which can be overridden by external UI
int w_height_inner;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index ddff93d83b..b155b3861f 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1777,9 +1777,12 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
#define PARSE_NUMBER(base, cond, conv) \
do { \
while (!STRING_ENDED(ptr) && (cond)) { \
+ const uvarnumber_T digit = (uvarnumber_T)(conv); \
/* avoid ubsan error for overflow */ \
- if (un < UVARNUMBER_MAX / base) { \
- un = base * un + (uvarnumber_T)(conv); \
+ if (un < UVARNUMBER_MAX / base \
+ || (un == UVARNUMBER_MAX / base \
+ && (base != 10 || digit <= UVARNUMBER_MAX % 10))) { \
+ un = base * un + digit; \
} else { \
un = UVARNUMBER_MAX; \
} \
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 667bc54e2e..49bc2ab2f0 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -58,9 +58,10 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
-/*
- * definitions used for CTRL-X submode
- */
+// Definitions used for CTRL-X submode.
+// Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[]
+// and ctrl_x_mode_names[].
+
#define CTRL_X_WANT_IDENT 0x100
#define CTRL_X_NOT_DEFINED_YET 1
@@ -83,17 +84,18 @@
#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
#define CTRL_X_MODE_LINE_OR_EVAL(m) (m == CTRL_X_WHOLE_LINE || m == CTRL_X_EVAL)
+// Message for CTRL-X mode, index is ctrl_x_mode.
static char *ctrl_x_msgs[] =
{
- N_(" Keyword completion (^N^P)"), /* ctrl_x_mode == 0, ^P/^N compl. */
+ N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl.
N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"),
- NULL,
+ NULL, // CTRL_X_SCROLL: depends on state
N_(" Whole line completion (^L^N^P)"),
N_(" File name completion (^F^N^P)"),
N_(" Tag completion (^]^N^P)"),
N_(" Path pattern completion (^N^P)"),
N_(" Definition completion (^D^N^P)"),
- NULL,
+ NULL, // CTRL_X_FINISHED
N_(" Dictionary completion (^K^N^P)"),
N_(" Thesaurus completion (^T^N^P)"),
N_(" Command-line completion (^V^N^P)"),
@@ -104,6 +106,26 @@ static char *ctrl_x_msgs[] =
NULL, // CTRL_X_EVAL doesn't use msg.
};
+static char *ctrl_x_mode_names[] = {
+ "keyword",
+ "ctrl_x",
+ "unknown", // CTRL_X_SCROLL
+ "whole_line",
+ "files",
+ "tags",
+ "path_patterns",
+ "path_defines",
+ "unknown", // CTRL_X_FINISHED
+ "dictionary",
+ "thesaurus",
+ "cmdline",
+ "function",
+ "omni",
+ "spell",
+ NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
+ "eval"
+};
+
static char e_hitend[] = N_("Hit end of paragraph");
static char e_complwin[] = N_("E839: Completion function changed window");
static char e_compldel[] = N_("E840: Completion function deleted text");
@@ -115,16 +137,18 @@ typedef struct compl_S compl_T;
struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
- char_u *cp_str; /* matched text */
- char cp_icase; /* TRUE or FALSE: ignore case */
- char_u *(cp_text[CPT_COUNT]); /* text for the menu */
- char_u *cp_fname; /* file containing the match, allocated when
- * cp_flags has FREE_FNAME */
- int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */
- int cp_number; /* sequence number */
+ char_u *cp_str; // matched text
+ char cp_icase; // TRUE or FALSE: ignore case
+ char cp_equal; // TRUE or FALSE: ins_compl_equal always ok
+ char_u *(cp_text[CPT_COUNT]); // text for the menu
+ char_u *cp_fname; // file containing the match, allocated when
+ // cp_flags has FREE_FNAME
+ int cp_flags; // ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME
+ int cp_number; // sequence number
};
-#define ORIGINAL_TEXT (1) /* the original text when the expansion begun */
+// flags for ins_compl_add()
+#define ORIGINAL_TEXT (1) // the original text when the expansion begun
#define FREE_FNAME (2)
/*
@@ -645,8 +669,9 @@ static int insert_execute(VimState *state, int key)
// there is nothing to add, CTRL-L works like CTRL-P then.
if (s->c == Ctrl_L
&& (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)
- || (int)STRLEN(compl_shown_match->cp_str)
- > curwin->w_cursor.col - compl_col)) {
+ || (compl_shown_match->cp_str != NULL
+ && (int)STRLEN(compl_shown_match->cp_str)
+ > curwin->w_cursor.col - compl_col))) {
ins_compl_addfrommatch();
return 1; // continue
}
@@ -1376,7 +1401,7 @@ ins_redraw (
// Trigger CursorMoved if the cursor moved. Not when the popup menu is
// visible, the command might delete it.
if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0)
- && !equalpos(last_cursormoved, curwin->w_cursor)
+ && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)
&& !pum_visible()) {
// Need to update the screen first, to make sure syntax
// highlighting is correct after making a change (e.g., inserting
@@ -1392,7 +1417,7 @@ ins_redraw (
ins_apply_autocmds(EVENT_CURSORMOVEDI);
}
conceal_cursor_moved = true;
- last_cursormoved = curwin->w_cursor;
+ curwin->w_last_cursormoved = curwin->w_cursor;
}
// Trigger TextChangedI if changedtick differs.
@@ -2012,14 +2037,14 @@ static bool ins_compl_accept_char(int c)
return vim_iswordc(c);
}
-/*
- * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
- * case of the originally typed text is used, and the case of the completed
- * text is inferred, ie this tries to work out what case you probably wanted
- * the rest of the word to be in -- webb
- */
-int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags)
+// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
+// case of the originally typed text is used, and the case of the completed
+// text is inferred, ie this tries to work out what case you probably wanted
+// the rest of the word to be in -- webb
+int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname,
+ int dir, int flags)
{
+ char_u *str = str_arg;
int i, c;
int actual_len; /* Take multi-byte characters */
int actual_compl_length; /* into account. */
@@ -2148,10 +2173,10 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
xfree(wca);
- return ins_compl_add(IObuff, len, icase, fname, NULL, false, dir, flags,
- false);
+ str = IObuff;
}
- return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false);
+ return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags,
+ false, false);
}
/// Add a match to the list of matches
@@ -2168,6 +2193,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int
/// cptext itself will not be freed.
/// @param[in] cdir Completion direction.
/// @param[in] adup True if duplicate matches are to be accepted.
+/// @param[in] equal Match is always accepted by ins_compl_equal.
///
/// @return NOTDONE if the given string is already in the list of completions,
/// otherwise it is added to the list and OK is returned. FAIL will be
@@ -2176,7 +2202,8 @@ static int ins_compl_add(char_u *const str, int len,
const bool icase, char_u *const fname,
char_u *const *const cptext,
const bool cptext_allocated,
- const Direction cdir, int flags, const bool adup)
+ const Direction cdir, int flags, const bool adup,
+ int equal)
FUNC_ATTR_NONNULL_ARG(1)
{
compl_T *match;
@@ -2228,6 +2255,7 @@ static int ins_compl_add(char_u *const str, int len,
match->cp_number = 0;
match->cp_str = vim_strnsave(str, len);
match->cp_icase = icase;
+ match->cp_equal = equal;
/* match-fname is:
* - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -2301,6 +2329,9 @@ static int ins_compl_add(char_u *const str, int len,
static bool ins_compl_equal(compl_T *match, char_u *str, size_t len)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
+ if (match->cp_equal) {
+ return true;
+ }
if (match->cp_icase) {
return STRNICMP(match->cp_str, str, len) == 0;
}
@@ -2374,7 +2405,8 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase)
for (i = 0; i < num_matches && add_r != FAIL; i++)
if ((add_r = ins_compl_add(matches[i], -1, icase,
- NULL, NULL, false, dir, 0, false)) == OK) {
+ NULL, NULL, false, dir, 0, false,
+ false)) == OK) {
// If dir was BACKWARD then honor it just once.
dir = FORWARD;
}
@@ -2443,7 +2475,7 @@ void set_completion(colnr_T startcol, list_T *list)
compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col,
compl_length);
if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0,
- ORIGINAL_TEXT, false) != OK) {
+ ORIGINAL_TEXT, false, false) != OK) {
return;
}
@@ -2656,8 +2688,25 @@ void ins_compl_show_pum(void)
col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
pum_selected_item = cur;
- pum_display(compl_match_array, compl_match_arraysize, cur, array_changed);
+ pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0);
curwin->w_cursor.col = col;
+
+ if (!has_event(EVENT_COMPLETECHANGED)) {
+ return;
+ }
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+ if (cur < 0) {
+ tv_dict_add_dict(dict, S_LEN("completed_item"), tv_dict_alloc());
+ } else {
+ dict_T *item = ins_compl_dict_alloc(compl_curr_match);
+ tv_dict_add_dict(dict, S_LEN("completed_item"), item);
+ }
+ pum_set_boundings(dict);
+ tv_dict_set_keys_readonly(dict);
+ textlock++;
+ apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, false, curbuf);
+ textlock--;
+ tv_dict_clear(dict);
}
#define DICT_FIRST (1) /* use just first element in "dict" */
@@ -2963,6 +3012,99 @@ bool ins_compl_active(void)
return compl_started;
}
+// Get complete information
+void get_complete_info(list_T *what_list, dict_T *retdict)
+{
+#define CI_WHAT_MODE 0x01
+#define CI_WHAT_PUM_VISIBLE 0x02
+#define CI_WHAT_ITEMS 0x04
+#define CI_WHAT_SELECTED 0x08
+#define CI_WHAT_INSERTED 0x10
+#define CI_WHAT_ALL 0xff
+ int what_flag;
+
+ if (what_list == NULL) {
+ what_flag = CI_WHAT_ALL;
+ } else {
+ what_flag = 0;
+ for (listitem_T *item = tv_list_first(what_list)
+ ; item != NULL
+ ; item = TV_LIST_ITEM_NEXT(what_list, item)) {
+ const char *what = tv_get_string(TV_LIST_ITEM_TV(item));
+
+ if (STRCMP(what, "mode") == 0) {
+ what_flag |= CI_WHAT_MODE;
+ } else if (STRCMP(what, "pum_visible") == 0) {
+ what_flag |= CI_WHAT_PUM_VISIBLE;
+ } else if (STRCMP(what, "items") == 0) {
+ what_flag |= CI_WHAT_ITEMS;
+ } else if (STRCMP(what, "selected") == 0) {
+ what_flag |= CI_WHAT_SELECTED;
+ } else if (STRCMP(what, "inserted") == 0) {
+ what_flag |= CI_WHAT_INSERTED;
+ }
+ }
+ }
+
+ int ret = OK;
+ if (what_flag & CI_WHAT_MODE) {
+ ret = tv_dict_add_str(retdict, S_LEN("mode"),
+ (char *)ins_compl_mode());
+ }
+
+ if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) {
+ ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible());
+ }
+
+ if (ret == OK && (what_flag & CI_WHAT_ITEMS)) {
+ list_T *li = tv_list_alloc(ins_compl_len());
+
+ ret = tv_dict_add_list(retdict, S_LEN("items"), li);
+ if (ret == OK && compl_first_match != NULL) {
+ compl_T *match = compl_first_match;
+ do {
+ if (!(match->cp_flags & ORIGINAL_TEXT)) {
+ dict_T *di = tv_dict_alloc();
+
+ tv_list_append_dict(li, di);
+ tv_dict_add_str(di, S_LEN("word"),
+ (char *)EMPTY_IF_NULL(match->cp_str));
+ tv_dict_add_str(di, S_LEN("abbr"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR]));
+ tv_dict_add_str(di, S_LEN("menu"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU]));
+ tv_dict_add_str(di, S_LEN("kind"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND]));
+ tv_dict_add_str(di, S_LEN("info"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO]));
+ tv_dict_add_str(di, S_LEN("user_data"),
+ (char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA]));
+ }
+ match = match->cp_next;
+ } while (match != NULL && match != compl_first_match);
+ }
+ }
+
+ if (ret == OK && (what_flag & CI_WHAT_SELECTED)) {
+ ret = tv_dict_add_nr(retdict, S_LEN("selected"),
+ (compl_curr_match != NULL)
+ ? compl_curr_match->cp_number - 1 : -1);
+ }
+
+ // TODO(vim):
+ // if (ret == OK && (what_flag & CI_WHAT_INSERTED))
+}
+
+// Return Insert completion mode name string
+static char_u * ins_compl_mode(void)
+{
+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) {
+ return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT];
+ }
+ return (char_u *)"";
+}
+
+
/*
* Delete one character before the cursor and show the subset of the matches
* that match the word that is now before the cursor.
@@ -3626,6 +3768,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
bool icase = false;
bool adup = false;
bool aempty = false;
+ bool aequal = false;
char *(cptext[CPT_COUNT]);
if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) {
@@ -3640,6 +3783,9 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase");
adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup");
aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty");
+ if (tv_dict_get_string(tv->vval.v_dict, "equal", false) != NULL) {
+ aequal = tv_dict_get_number(tv->vval.v_dict, "equal");
+ }
} else {
word = (const char *)tv_get_string_chk(tv);
memset(cptext, 0, sizeof(cptext));
@@ -3651,7 +3797,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir)
return FAIL;
}
return ins_compl_add((char_u *)word, -1, icase, NULL,
- (char_u **)cptext, true, dir, 0, adup);
+ (char_u **)cptext, true, dir, 0, adup, aequal);
}
/*
@@ -4097,31 +4243,37 @@ static void ins_compl_insert(int in_compl_func)
else
compl_used_match = TRUE;
- // Set completed item.
+ dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict);
+ if (!in_compl_func) {
+ compl_curr_match = compl_shown_match;
+ }
+}
+
+// Convert to complete item dict
+static dict_T *ins_compl_dict_alloc(compl_T *match)
+{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc();
tv_dict_add_str(
dict, S_LEN("word"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str));
+ (const char *)EMPTY_IF_NULL(match->cp_str));
tv_dict_add_str(
dict, S_LEN("abbr"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR]));
tv_dict_add_str(
dict, S_LEN("menu"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU]));
tv_dict_add_str(
dict, S_LEN("kind"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND]));
tv_dict_add_str(
dict, S_LEN("info"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO]));
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO]));
tv_dict_add_str(
dict, S_LEN("user_data"),
- (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_USER_DATA]));
- set_vim_var_dict(VV_COMPLETED_ITEM, dict);
- if (!in_compl_func) {
- compl_curr_match = compl_shown_match;
- }
+ (const char *)EMPTY_IF_NULL(match->cp_text[CPT_USER_DATA]));
+ return dict;
}
/*
@@ -4807,7 +4959,7 @@ static int ins_complete(int c, bool enable_pum)
xfree(compl_orig_text);
compl_orig_text = vim_strnsave(line + compl_col, compl_length);
if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0,
- ORIGINAL_TEXT, false) != OK) {
+ ORIGINAL_TEXT, false, false) != OK) {
xfree(compl_pattern);
compl_pattern = NULL;
xfree(compl_orig_text);
@@ -5486,16 +5638,33 @@ internal_format (
/* remember position of blank just before text */
end_col = curwin->w_cursor.col;
- /* find start of sequence of blanks */
+ // find start of sequence of blanks
+ int wcc = 0; // counter for whitespace chars
while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
dec_cursor();
cc = gchar_cursor();
+
+ // Increment count of how many whitespace chars in this
+ // group; we only need to know if it's more than one.
+ if (wcc < 2) {
+ wcc++;
+ }
}
- if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
- break; /* only spaces in front of text */
- /* Don't break until after the comment leader */
- if (curwin->w_cursor.col < leader_len)
+ if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
+ break; // only spaces in front of text
+ }
+
+ // Don't break after a period when 'formatoptions' has 'p' and
+ // there are less than two spaces.
+ if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
+ continue;
+ }
+
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
break;
+ }
+
if (has_format_option(FO_ONE_LETTER)) {
/* do not break after one-letter words */
if (curwin->w_cursor.col == 0)
@@ -5859,10 +6028,7 @@ comp_textwidth (
textwidth -= 1;
}
textwidth -= curwin->w_p_fdc;
-
- if (signcolumn_on(curwin)) {
- textwidth -= 1;
- }
+ textwidth -= win_signcol_count(curwin);
if (curwin->w_p_nu || curwin->w_p_rnu)
textwidth -= 8;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5ef2a8772e..6479163028 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -241,13 +241,14 @@ typedef enum {
///< the value (prevents error message).
} GetLvalFlags;
-// function flags
+// flags used in uf_flags
#define FC_ABORT 0x01 // abort function on error
#define FC_RANGE 0x02 // function accepts range
#define FC_DICT 0x04 // Dict function, uses "self"
#define FC_CLOSURE 0x08 // closure, uses outer scope variables
#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
+#define FC_SANDBOX 0x40 // function defined in the sandbox
// The names of packages that once were loaded are remembered.
static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL };
@@ -448,6 +449,7 @@ typedef struct {
int timer_id;
int repeat_count;
int refcount;
+ int emsg_count; ///< Errors in a repeating timer.
long timeout;
bool stopped;
bool paused;
@@ -3933,7 +3935,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
int op;
varnumber_T n1, n2;
bool use_float = false;
- float_T f1 = 0, f2;
+ float_T f1 = 0, f2 = 0;
bool error = false;
/*
@@ -5219,7 +5221,7 @@ bool garbage_collect(bool testing)
(void)garbage_collect(testing);
}
} else if (p_verbose > 0) {
- verb_msg((char_u *)_(
+ verb_msg(_(
"Not enough memory to set references, garbage collection aborted!"));
}
#undef ABORTING
@@ -5853,6 +5855,9 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
if (prof_def_func()) {
func_do_profile(fp);
}
+ if (sandbox) {
+ flags |= FC_SANDBOX;
+ }
fp->uf_varargs = true;
fp->uf_flags = flags;
fp->uf_calls = 0;
@@ -6328,8 +6333,12 @@ call_func(
}
- /* execute the function if no errors detected and executing */
- if (evaluate && error == ERROR_NONE) {
+ // Execute the function if executing and no errors were detected.
+ if (!evaluate) {
+ // Not evaluating, which means the return value is unknown. This
+ // matters for giving error messages.
+ rettv->v_type = VAR_UNKNOWN;
+ } else if (error == ERROR_NONE) {
char_u *rfname = fname;
/* Ignore "g:" before a function name. */
@@ -6654,12 +6663,24 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1; /* Failed */
}
-/*
- * "argc()" function
- */
static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- rettv->vval.v_number = ARGCOUNT;
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // use the current window
+ rettv->vval.v_number = ARGCOUNT;
+ } else if (argvars[0].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[0]) == -1) {
+ // use the global argument list
+ rettv->vval.v_number = GARGCOUNT;
+ } else {
+ // use the argument list of the specified window
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp != NULL) {
+ rettv->vval.v_number = WARGCOUNT(wp);
+ } else {
+ rettv->vval.v_number = -1;
+ }
+ }
}
/*
@@ -6680,28 +6701,54 @@ static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// Get the argument list for a given window
+static void get_arglist_as_rettv(aentry_T *arglist, int argcount,
+ typval_T *rettv)
+{
+ tv_list_alloc_ret(rettv, argcount);
+ if (arglist != NULL) {
+ for (int idx = 0; idx < argcount; idx++) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)alist_name(&arglist[idx]), -1);
+ }
+ }
+}
+
/*
* "argv(nr)" function
*/
static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int idx;
+ aentry_T *arglist = NULL;
+ int argcount = -1;
if (argvars[0].v_type != VAR_UNKNOWN) {
- idx = (int)tv_get_number_chk(&argvars[0], NULL);
- if (idx >= 0 && idx < ARGCOUNT) {
- rettv->vval.v_string = (char_u *)xstrdup(
- (const char *)alist_name(&ARGLIST[idx]));
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ arglist = ARGLIST;
+ argcount = ARGCOUNT;
+ } else if (argvars[1].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[1]) == -1) {
+ arglist = GARGLIST;
+ argcount = GARGCOUNT;
} else {
- rettv->vval.v_string = NULL;
+ win_T *wp = find_win_by_nr_or_id(&argvars[1]);
+ if (wp != NULL) {
+ // Use the argument list of the specified window
+ arglist = WARGLIST(wp);
+ argcount = WARGCOUNT(wp);
+ }
}
rettv->v_type = VAR_STRING;
- } else {
- tv_list_alloc_ret(rettv, ARGCOUNT);
- for (idx = 0; idx < ARGCOUNT; idx++) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)alist_name(&ARGLIST[idx]), -1);
+ rettv->vval.v_string = NULL;
+ int idx = tv_get_number_chk(&argvars[0], NULL);
+ if (arglist != NULL && idx >= 0 && idx < argcount) {
+ rettv->vval.v_string = (char_u *)xstrdup(
+ (const char *)alist_name(&arglist[idx]));
+ } else if (idx == -1) {
+ get_arglist_as_rettv(arglist, argcount, rettv);
}
+ } else {
+ get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
}
}
@@ -6830,6 +6877,27 @@ static void assert_equal_common(typval_T *argvars, assert_type_T atype)
}
}
+static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ const char *const cmd = tv_get_string_chk(&argvars[0]);
+ garray_T ga;
+
+ called_vim_beep = false;
+ suppress_errthrow = true;
+ emsg_silent = false;
+ do_cmdline_cmd(cmd);
+ if (!called_vim_beep) {
+ prepare_assert_error(&ga);
+ ga_concat(&ga, (const char_u *)"command did not beep: ");
+ ga_concat(&ga, (const char_u *)cmd);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+
+ suppress_errthrow = false;
+ emsg_on_display = false;
+}
+
// "assert_equal(expected, actual[, msg])" function
static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -7556,6 +7624,23 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
RedrawingDisabled = saved;
}
+// "complete_info()" function
+static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_dict_alloc_ret(rettv);
+
+ list_T *what_list = NULL;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ if (argvars[0].v_type != VAR_LIST) {
+ EMSG(_(e_listreq));
+ return;
+ }
+ what_list = argvars[0].vval.v_list;
+ }
+ get_complete_info(what_list, rettv->vval.v_dict);
+}
+
/*
* "confirm(message, buttons[, default [, type]])" function
*/
@@ -7983,7 +8068,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hlID = HLF_CHD; // Changed line.
}
}
- rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID;
+ rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1);
}
/*
@@ -8194,6 +8279,19 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = path;
}
+/// Find a window: When using a Window ID in any tab page, when using a number
+/// in the current tab page.
+win_T * find_win_by_nr_or_id(typval_T *vp)
+{
+ int nr = (int)tv_get_number_chk(vp, NULL);
+
+ if (nr >= LOWEST_WIN_ID) {
+ return win_id2wp(vp);
+ }
+
+ return find_win_by_nr(vp, NULL);
+}
+
/*
* "exists()" function
*/
@@ -9953,6 +10051,37 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = type;
}
+// "getjumplist()" function
+static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ win_T *const wp = find_tabwin(&argvars[0], &argvars[1]);
+ if (wp == NULL) {
+ return;
+ }
+
+ cleanup_jumplist(wp, true);
+
+ list_T *const l = tv_list_alloc(wp->w_jumplistlen);
+ tv_list_append_list(rettv->vval.v_list, l);
+ tv_list_append_number(rettv->vval.v_list, wp->w_jumplistidx);
+
+ for (int i = 0; i < wp->w_jumplistlen; i++) {
+ if (wp->w_jumplist[i].fmark.mark.lnum == 0) {
+ continue;
+ }
+ dict_T *const d = tv_dict_alloc();
+ tv_list_append_dict(l, d);
+ tv_dict_add_nr(d, S_LEN("lnum"), wp->w_jumplist[i].fmark.mark.lnum);
+ tv_dict_add_nr(d, S_LEN("col"), wp->w_jumplist[i].fmark.mark.col);
+ tv_dict_add_nr(d, S_LEN("coladd"), wp->w_jumplist[i].fmark.mark.coladd);
+ tv_dict_add_nr(d, S_LEN("bufnr"), wp->w_jumplist[i].fmark.fnum);
+ if (wp->w_jumplist[i].fname != NULL) {
+ tv_dict_add_str(d, S_LEN("filename"), (char *)wp->w_jumplist[i].fname);
+ }
+ }
+}
+
/*
* "getline(lnum, [end])" function
*/
@@ -9988,7 +10117,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
dict_T *d = what_arg->vval.v_dict;
if (d != NULL) {
- get_errorlist_properties(wp, d, rettv->vval.v_dict);
+ qf_get_properties(wp, d, rettv->vval.v_dict);
}
} else {
EMSG(_(e_dictreq));
@@ -10000,7 +10129,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
/// "getloclist()" function
static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp = find_win_by_nr(&argvars[0], NULL);
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
get_qf_loc_list(false, wp, &argvars[1], rettv);
}
@@ -10019,7 +10148,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// match added with matchaddpos()
for (i = 0; i < MAXPOSMATCH; i++) {
llpos_T *llpos;
- char buf[6];
+ char buf[30]; // use 30 to avoid compiler warning
llpos = &cur->pos.pos[i];
if (llpos->lnum == 0) {
@@ -10306,6 +10435,23 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
getwinvar(argvars, rettv, 1);
}
+// "gettagstack()" function
+static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ win_T *wp = curwin; // default is current window
+
+ tv_dict_alloc_ret(rettv);
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ return;
+ }
+ }
+
+ get_tagstack(wp, rettv->vval.v_dict);
+}
+
/// Returns information about a window as a dictionary.
static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
{
@@ -10371,11 +10517,19 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv, 2);
- const win_T *const wp = find_win_by_nr(&argvars[0], NULL);
+ const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
}
+// "getwinpos({timeout})" function
+static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, 2);
+ tv_list_append_number(rettv->vval.v_list, -1);
+ tv_list_append_number(rettv->vval.v_list, -1);
+}
+
/*
* "getwinposx()" function
*/
@@ -10707,6 +10861,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef __APPLE__
"mac",
"macunix",
+ "osx",
#endif
"menu",
"mksession",
@@ -11180,7 +11335,6 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static int inputsecret_flag = 0;
-
/*
* This function is used by f_input() and f_inputdialog() functions. The third
* argument to f_input() specifies the type of completion to use at the
@@ -11369,25 +11523,21 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL};
-/*
- * "inputrestore()" function
- */
+/// "inputrestore()" function
static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (!GA_EMPTY(&ga_userinput)) {
- --ga_userinput.ga_len;
+ ga_userinput.ga_len--;
restore_typeahead((tasave_T *)(ga_userinput.ga_data)
- + ga_userinput.ga_len);
- /* default return is zero == OK */
+ + ga_userinput.ga_len);
+ // default return is zero == OK
} else if (p_verbose > 1) {
- verb_msg((char_u *)_("called inputrestore() more often than inputsave()"));
- rettv->vval.v_number = 1; /* Failed */
+ verb_msg(_("called inputrestore() more often than inputsave()"));
+ rettv->vval.v_number = 1; // Failed
}
}
-/*
- * "inputsave()" function
- */
+/// "inputsave()" function
static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
// Add an entry to the stack of typeahead storage.
@@ -11395,9 +11545,7 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
save_typeahead(p);
}
-/*
- * "inputsecret()" function
- */
+/// "inputsecret()" function
static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
cmdline_star++;
@@ -11564,10 +11712,11 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FUNC_ATTR_NONNULL_ALL
{
- const int len = vim_vsnprintf(NULL, 0, "%p", dummy_ap, argvars);
+ const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = xmalloc(len + 1);
- vim_vsnprintf((char *)rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars);
+ vim_vsnprintf_typval((char *)rettv->vval.v_string, len + 1, "%p",
+ dummy_ap, argvars);
}
/*
@@ -12503,6 +12652,31 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
find_some_match(argvars, rettv, kSomeMatch);
}
+static int matchadd_dict_arg(typval_T *tv, const char **conceal_char,
+ win_T **win)
+{
+ dictitem_T *di;
+
+ if (tv->v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return FAIL;
+ }
+
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) {
+ *conceal_char = tv_get_string(&di->di_tv);
+ }
+
+ if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) {
+ *win = find_win_by_nr_or_id(&di->di_tv);
+ if (*win == NULL) {
+ EMSG(_("E957: Invalid window number"));
+ return FAIL;
+ }
+ }
+
+ return OK;
+}
+
/*
* "matchadd()" function
*/
@@ -12516,6 +12690,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int id = -1;
bool error = false;
const char *conceal_char = NULL;
+ win_T *win = curwin;
rettv->vval.v_number = -1;
@@ -12526,16 +12701,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
prio = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN) {
- if (argvars[4].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
- return;
- }
- dictitem_T *di;
- if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
- != NULL) {
- conceal_char = tv_get_string(&di->di_tv);
- }
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
+ return;
}
}
}
@@ -12547,8 +12715,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL,
- conceal_char);
+ rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char);
}
static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
@@ -12576,21 +12743,15 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int prio = 10;
int id = -1;
const char *conceal_char = NULL;
+ win_T *win = curwin;
if (argvars[2].v_type != VAR_UNKNOWN) {
prio = tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
id = tv_get_number_chk(&argvars[3], &error);
- if (argvars[4].v_type != VAR_UNKNOWN) {
- if (argvars[4].v_type != VAR_DICT) {
- EMSG(_(e_dictreq));
- return;
- }
- dictitem_T *di;
- if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal")))
- != NULL) {
- conceal_char = tv_get_string(&di->di_tv);
- }
+ if (argvars[4].v_type != VAR_UNKNOWN
+ && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) {
+ return;
}
}
}
@@ -12604,8 +12765,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l,
- conceal_char);
+ rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char);
}
/*
@@ -12752,33 +12912,36 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char buf[NUMBUFLEN];
const char *const dir = tv_get_string_buf(&argvars[0], buf);
if (*dir == NUL) {
- rettv->vval.v_number = FAIL;
- } else {
- if (*path_tail((char_u *)dir) == NUL) {
- // Remove trailing slashes.
- *path_tail_with_sep((char_u *)dir) = NUL;
- }
+ return;
+ }
- if (argvars[1].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_UNKNOWN) {
- prot = tv_get_number_chk(&argvars[2], NULL);
- }
- if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) {
- char *failed_dir;
- int ret = os_mkdir_recurse(dir, prot, &failed_dir);
- if (ret != 0) {
- EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
- xfree(failed_dir);
- rettv->vval.v_number = FAIL;
- return;
- } else {
- rettv->vval.v_number = OK;
- return;
- }
+ if (*path_tail((char_u *)dir) == NUL) {
+ // Remove trailing slashes.
+ *path_tail_with_sep((char_u *)dir) = NUL;
+ }
+
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ prot = tv_get_number_chk(&argvars[2], NULL);
+ if (prot == -1) {
+ return;
+ }
+ }
+ if (strcmp(tv_get_string(&argvars[1]), "p") == 0) {
+ char *failed_dir;
+ int ret = os_mkdir_recurse(dir, prot, &failed_dir);
+ if (ret != 0) {
+ EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
+ xfree(failed_dir);
+ rettv->vval.v_number = FAIL;
+ return;
+ } else {
+ rettv->vval.v_number = OK;
+ return;
}
}
- rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot);
}
+ rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
}
/// "mode()" function
@@ -13020,11 +13183,11 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
did_emsg = false;
char buf[NUMBUFLEN];
const char *fmt = tv_get_string_buf(&argvars[0], buf);
- len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1);
+ len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1);
if (!did_emsg) {
char *s = xmalloc(len + 1);
rettv->vval.v_string = (char_u *)s;
- (void)vim_vsnprintf(s, len + 1, fmt, dummy_ap, argvars + 1);
+ (void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1);
}
did_emsg |= saved_did_emsg;
}
@@ -14761,7 +14924,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
skip_args:
if (!title) {
- title = (wp ? "setloclist()" : "setqflist()");
+ title = (wp ? ":setloclist()" : ":setqflist()");
}
recursive++;
@@ -14781,7 +14944,7 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = -1;
- win = find_win_by_nr(&argvars[0], NULL);
+ win = find_win_by_nr_or_id(&argvars[0]);
if (win != NULL) {
set_qf_ll_list(win, &argvars[1], rettv);
}
@@ -14837,7 +15000,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// match from matchaddpos()
for (i = 1; i < 9; i++) {
- char buf[5];
+ char buf[30]; // use 30 to avoid compiler warning
snprintf(buf, sizeof(buf), "pos%d", i);
dictitem_T *const pos_di = tv_dict_find(d, buf, -1);
if (pos_di != NULL) {
@@ -15087,6 +15250,60 @@ static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
setwinvar(argvars, rettv, 1);
}
+// "settagstack()" function
+static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ static char *e_invact2 = N_("E962: Invalid action: '%s'");
+ win_T *wp;
+ dict_T *d;
+ int action = 'r';
+
+ rettv->vval.v_number = -1;
+
+ // first argument: window number or id
+ wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
+ return;
+ }
+
+ // second argument: dict with items to set in the tag stack
+ if (argvars[1].v_type != VAR_DICT) {
+ EMSG(_(e_dictreq));
+ return;
+ }
+ d = argvars[1].vval.v_dict;
+ if (d == NULL) {
+ return;
+ }
+
+ // third argument: action - 'a' for append and 'r' for replace.
+ // default is to replace the stack.
+ if (argvars[2].v_type == VAR_UNKNOWN) {
+ action = 'r';
+ } else if (argvars[2].v_type == VAR_STRING) {
+ const char *actstr;
+ actstr = tv_get_string_chk(&argvars[2]);
+ if (actstr == NULL) {
+ return;
+ }
+ if ((*actstr == 'r' || *actstr == 'a') && actstr[1] == NUL) {
+ action = *actstr;
+ } else {
+ EMSG2(_(e_invact2), actstr);
+ return;
+ }
+ } else {
+ EMSG(_(e_stringreq));
+ return;
+ }
+
+ if (set_tagstack(wp, d, action) == OK) {
+ rettv->vval.v_number = 0;
+ } else {
+ EMSG(_(e_listreq));
+ }
+}
+
/*
* "setwinvar()" function
*/
@@ -16306,6 +16523,27 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+/// "swapinfo(swap_filename)" function
+static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_dict_alloc_ret(rettv);
+ get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
+}
+
+/// "swapname(expr)" function
+static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ buf_T *buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL
+ || buf->b_ml.ml_mfp == NULL
+ || buf->b_ml.ml_mfp->mf_fname == NULL) {
+ rettv->vval.v_string = NULL;
+ } else {
+ rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname);
+ }
+}
+
/// "synID(lnum, col, trans)" function
static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -16631,7 +16869,6 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-
/*
* "tabpagenr()" function
*/
@@ -16668,6 +16905,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
twin = (tp == curtab) ? curwin : tp->tp_curwin;
if (argvar->v_type != VAR_UNKNOWN) {
+ bool invalid_arg = false;
const char *const arg = tv_get_string_chk(argvar);
if (arg == NULL) {
nr = 0; // Type error; errmsg already given.
@@ -16679,6 +16917,31 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
nr = 0;
}
} else {
+ // Extract the window count (if specified). e.g. winnr('3j')
+ char_u *endp;
+ long count = strtol((char *)arg, (char **)&endp, 10);
+ if (count <= 0) {
+ // if count is not specified, default to 1
+ count = 1;
+ }
+ if (endp != NULL && *endp != '\0') {
+ if (strequal((char *)endp, "j")) {
+ twin = win_vert_neighbor(tp, twin, false, count);
+ } else if (strequal((char *)endp, "k")) {
+ twin = win_vert_neighbor(tp, twin, true, count);
+ } else if (strequal((char *)endp, "h")) {
+ twin = win_horz_neighbor(tp, twin, true, count);
+ } else if (strequal((char *)endp, "l")) {
+ twin = win_horz_neighbor(tp, twin, false, count);
+ } else {
+ invalid_arg = true;
+ }
+ } else {
+ invalid_arg = true;
+ }
+ }
+
+ if (invalid_arg) {
EMSG2(_(e_invexpr2), arg);
nr = 0;
}
@@ -16712,7 +16975,6 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = nr;
}
-
/*
* "tagfiles()" function
*/
@@ -17075,6 +17337,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer->refcount = 1;
timer->stopped = false;
timer->paused = false;
+ timer->emsg_count = 0;
timer->repeat_count = repeat;
timer->timeout = timeout;
timer->timer_id = last_timer_id++;
@@ -17117,6 +17380,9 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
static void timer_due_cb(TimeWatcher *tw, void *data)
{
timer_T *timer = (timer_T *)data;
+ int save_did_emsg = did_emsg;
+ int save_called_emsg = called_emsg;
+
if (timer->stopped || timer->paused) {
return;
}
@@ -17131,8 +17397,24 @@ static void timer_due_cb(TimeWatcher *tw, void *data)
argv[0].v_type = VAR_NUMBER;
argv[0].vval.v_number = timer->timer_id;
typval_T rettv = TV_INITIAL_VALUE;
+ called_emsg = false;
callback_call(&timer->callback, 1, argv, &rettv);
+
+ // Handle error message
+ if (called_emsg && did_emsg) {
+ timer->emsg_count++;
+ if (current_exception != NULL) {
+ discard_current_exception();
+ }
+ }
+ did_emsg = save_did_emsg;
+ called_emsg = save_called_emsg;
+
+ if (timer->emsg_count >= 3) {
+ timer_stop(timer);
+ }
+
tv_clear(&rettv);
if (!timer->stopped && timer->timeout == 0) {
@@ -17419,7 +17701,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// If there is no file name there will be no undo file.
rettv->vval.v_string = NULL;
} else {
- char *ffname = FullName_save(fname, false);
+ char *ffname = FullName_save(fname, true);
if (ffname != NULL) {
rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false);
@@ -17532,18 +17814,15 @@ static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = win_id2win(argvars);
}
-/*
- * "winbufnr(nr)" function
- */
+/// "winbufnr(nr)" function
static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp;
-
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL)
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = wp->w_buffer->b_fnum;
+ }
}
/*
@@ -17555,18 +17834,15 @@ static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = curwin->w_wcol + 1;
}
-/*
- * "winheight(nr)" function
- */
+/// "winheight(nr)" function
static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp;
-
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL)
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = wp->w_height;
+ }
}
/*
@@ -17830,18 +18106,15 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)
return ret;
}
-/*
- * "winwidth(nr)" function
- */
+/// "winwidth(nr)" function
static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- win_T *wp;
-
- wp = find_win_by_nr(&argvars[0], NULL);
- if (wp == NULL)
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp == NULL) {
rettv->vval.v_number = -1;
- else
+ } else {
rettv->vval.v_number = wp->w_width;
+ }
}
/// "wordcount()" function
@@ -18053,7 +18326,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
* Return FAIL when conversion is not possible, doesn't check the position for
* validity.
*/
-static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
+int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)
{
list_T *l;
long i = 0;
@@ -19570,16 +19843,15 @@ void ex_echo(exarg_T *eap)
{
char_u *arg = eap->arg;
typval_T rettv;
- bool needclr = true;
bool atstart = true;
const int did_emsg_before = did_emsg;
if (eap->skip)
++emsg_skip;
while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) {
- /* If eval1() causes an error message the text from the command may
- * still need to be cleared. E.g., "echo 22,44". */
- need_clr_eos = needclr;
+ // If eval1() causes an error message the text from the command may
+ // still need to be cleared. E.g., "echo 22,44".
+ need_clr_eos = true;
{
char_u *p = arg;
@@ -19623,14 +19895,14 @@ void ex_echo(exarg_T *eap)
}
eap->nextcmd = check_nextcmd(arg);
- if (eap->skip)
- --emsg_skip;
- else {
- /* remove text that may still be there from the command */
- if (needclr)
- msg_clr_eos();
- if (eap->cmdidx == CMD_echo)
+ if (eap->skip) {
+ emsg_skip--;
+ } else {
+ // remove text that may still be there from the command
+ msg_clr_eos();
+ if (eap->cmdidx == CMD_echo) {
msg_end();
+ }
}
}
@@ -20348,6 +20620,9 @@ void ex_function(exarg_T *eap)
if (prof_def_func())
func_do_profile(fp);
fp->uf_varargs = varargs;
+ if (sandbox) {
+ flags |= FC_SANDBOX;
+ }
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
@@ -21338,6 +21613,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
char_u *save_sourcing_name;
linenr_T save_sourcing_lnum;
scid_T save_current_SID;
+ bool using_sandbox = false;
funccall_T *fc;
int save_did_emsg;
static int depth = 0;
@@ -21495,6 +21771,12 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
save_sourcing_name = sourcing_name;
save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 1;
+
+ if (fp->uf_flags & FC_SANDBOX) {
+ using_sandbox = true;
+ sandbox++;
+ }
+
// need space for new sourcing_name:
// * save_sourcing_name
// * "["number"].." or "function "
@@ -21655,6 +21937,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
if (do_profiling_yes) {
script_prof_restore(&wait_start);
}
+ if (using_sandbox) {
+ sandbox--;
+ }
if (p_verbose >= 12 && sourcing_name != NULL) {
++no_wait_return;
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 78cac4878d..d4bb69613e 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -25,6 +25,7 @@ return {
arglistid={args={0, 2}},
argv={args={0, 1}},
asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc
+ assert_beeps={args={1, 2}},
assert_equal={args={2, 3}},
assert_exception={args={1, 2}},
assert_fails={args={1, 2}},
@@ -64,6 +65,7 @@ return {
complete={args=2},
complete_add={args=1},
complete_check={},
+ complete_info={args={0, 1}},
confirm={args={1, 4}},
copy={args=1},
cos={args=1, func="float_op_wrapper", data="&cos"},
@@ -129,6 +131,7 @@ return {
getfsize={args=1},
getftime={args=1},
getftype={args=1},
+ getjumplist={args={0, 2}},
getline={args={1, 2}},
getloclist={args={1, 2}},
getmatches={},
@@ -140,7 +143,9 @@ return {
gettabinfo={args={0, 1}},
gettabvar={args={2, 3}},
gettabwinvar={args={3, 4}},
+ gettagstack={args={0, 1}},
getwininfo={args={0, 1}},
+ getwinpos={args={0, 1}},
getwinposx={},
getwinposy={},
getwinvar={args={2, 3}},
@@ -266,6 +271,7 @@ return {
setreg={args={2, 3}},
settabvar={args=3},
settabwinvar={args=4},
+ settagstack={args={2, 3}},
setwinvar={args=3},
sha256={args=1},
shellescape={args={1, 2}},
@@ -298,6 +304,8 @@ return {
strwidth={args=1},
submatch={args={1, 2}},
substitute={args=4},
+ swapinfo={args={1}},
+ swapname={args={1}},
synID={args=3},
synIDattr={args={2, 3}},
synIDtrans={args=1},
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 912aecafec..ffb46abfea 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1656,12 +1656,7 @@ int tv_dict_add_special(dict_T *const d, const char *const key,
/// Add a string entry to dictionary
///
-/// @param[out] d Dictionary to add entry to.
-/// @param[in] key Key to add.
-/// @param[in] key_len Key length.
-/// @param[in] val String to add.
-///
-/// @return OK in case of success, FAIL when key already exists.
+/// @see tv_dict_add_allocated_str
int tv_dict_add_str(dict_T *const d,
const char *const key, const size_t key_len,
const char *const val)
@@ -1672,6 +1667,27 @@ int tv_dict_add_str(dict_T *const d,
/// Add a string entry to dictionary
///
+/// @param[out] d Dictionary to add entry to.
+/// @param[in] key Key to add.
+/// @param[in] key_len Key length.
+/// @param[in] val String to add. NULL adds empty string.
+/// @param[in] len Use this many bytes from `val`, or -1 for whole string.
+///
+/// @return OK in case of success, FAIL when key already exists.
+int tv_dict_add_str_len(dict_T *const d,
+ const char *const key, const size_t key_len,
+ char *const val, int len)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
+{
+ char *s = val ? val : "";
+ if (val != NULL) {
+ s = (len < 0) ? xstrdup(val) : xstrndup(val, (size_t)len);
+ }
+ return tv_dict_add_allocated_str(d, key, key_len, s);
+}
+
+/// Add a string entry to dictionary
+///
/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of
/// creating a new copy.
///
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 6812b342bf..19907d2e45 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -21,16 +21,14 @@
#endif
void rstream_init_fd(Loop *loop, Stream *stream, int fd, size_t bufsize)
- FUNC_ATTR_NONNULL_ARG(1)
- FUNC_ATTR_NONNULL_ARG(2)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
stream_init(loop, stream, fd, NULL);
rstream_init(stream, bufsize);
}
void rstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t bufsize)
- FUNC_ATTR_NONNULL_ARG(1)
- FUNC_ATTR_NONNULL_ARG(2)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
stream_init(NULL, stream, -1, uvstream);
rstream_init(stream, bufsize);
@@ -138,6 +136,10 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
}
// Called by the by the 'idle' handle to emulate a reading event
+//
+// Idle callbacks are invoked once per event loop:
+// - to perform some very low priority activity.
+// - to keep the loop "alive" (so there is always an event to process)
static void fread_idle_cb(uv_idle_t *handle)
{
uv_fs_t req;
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index ba25b76ec7..70b1d890d6 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -53,9 +53,19 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
stream->uv.idle.data = stream;
} else {
assert(type == UV_NAMED_PIPE || type == UV_TTY);
+#ifdef WIN32
+ if (type == UV_TTY) {
+ uv_tty_init(&loop->uv, &stream->uv.tty, fd, 0);
+ uv_tty_set_mode(&stream->uv.tty, UV_TTY_MODE_RAW);
+ stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.tty);
+ } else {
+#endif
uv_pipe_init(&loop->uv, &stream->uv.pipe, 0);
uv_pipe_open(&stream->uv.pipe, fd);
stream->uvstream = STRUCT_CAST(uv_stream_t, &stream->uv.pipe);
+#ifdef WIN32
+ }
+#endif
}
}
diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h
index e713323f5c..a5c33a66a2 100644
--- a/src/nvim/event/stream.h
+++ b/src/nvim/event/stream.h
@@ -13,7 +13,7 @@ typedef struct stream Stream;
/// Type of function called when the Stream buffer is filled with data
///
/// @param stream The Stream instance
-/// @param rbuffer The associated RBuffer instance
+/// @param buf The associated RBuffer instance
/// @param count Number of bytes that was read.
/// @param data User-defined data
/// @param eof If the stream reached EOF.
@@ -23,7 +23,7 @@ typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count,
/// Type of function called when the Stream has information about a write
/// request.
///
-/// @param wstream The Stream instance
+/// @param stream The Stream instance
/// @param data User-defined data
/// @param status 0 on success, anything else indicates failure
typedef void (*stream_write_cb)(Stream *stream, void *data, int status);
@@ -36,6 +36,9 @@ struct stream {
uv_pipe_t pipe;
uv_tcp_t tcp;
uv_idle_t idle;
+#ifdef WIN32
+ uv_tty_t tty;
+#endif
} uv;
uv_stream_t *uvstream;
uv_buf_t uvbuf;
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4356767cc9..8436ac810e 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -855,7 +855,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
return FAIL;
for (extra = 0, l = line1; l <= line2; l++) {
str = vim_strsave(ml_get(l + extra));
- ml_append(dest + l - line1, str, (colnr_T)0, FALSE);
+ ml_append(dest + l - line1, str, (colnr_T)0, false);
xfree(str);
if (dest < line1)
extra++;
@@ -914,9 +914,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
return FAIL;
- for (l = line1; l <= line2; l++)
- ml_delete(line1 + extra, TRUE);
-
+ for (l = line1; l <= line2; l++) {
+ ml_delete(line1 + extra, true);
+ }
if (!global_busy && num_lines > p_report) {
if (num_lines == 1)
MSG(_("1 line moved"));
@@ -982,7 +982,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
/* need to use vim_strsave() because the line will be unlocked within
* ml_append() */
p = vim_strsave(ml_get(line1));
- ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE);
+ ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false);
xfree(p);
/* situation 2: skip already copied lines */
@@ -2720,7 +2720,7 @@ static int append_indent = 0; /* autoindent for first line */
void ex_append(exarg_T *eap)
{
char_u *theline;
- int did_undo = FALSE;
+ bool did_undo = false;
linenr_T lnum = eap->line2;
int indent = 0;
char_u *p;
@@ -2808,16 +2808,16 @@ void ex_append(exarg_T *eap)
if (p[0] == NUL)
theline[0] = NUL;
- did_undo = TRUE;
- ml_append(lnum, theline, (colnr_T)0, FALSE);
+ did_undo = true;
+ ml_append(lnum, theline, (colnr_T)0, false);
appended_lines_mark(lnum + (empty ? 1 : 0), 1L);
xfree(theline);
++lnum;
if (empty) {
- ml_delete(2L, FALSE);
- empty = FALSE;
+ ml_delete(2L, false);
+ empty = 0;
}
}
State = NORMAL;
@@ -2862,7 +2862,7 @@ void ex_change(exarg_T *eap)
for (lnum = eap->line2; lnum >= eap->line1; --lnum) {
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
break;
- ml_delete(eap->line1, FALSE);
+ ml_delete(eap->line1, false);
}
/* make sure the cursor is not beyond the end of the file now */
@@ -3181,6 +3181,7 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
subflags->do_ask = false;
subflags->do_error = true;
subflags->do_print = false;
+ subflags->do_list = false;
subflags->do_count = false;
subflags->do_number = false;
subflags->do_ic = kSubHonorOptions;
@@ -3691,10 +3692,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
i = msg_scroll;
msg_scroll = 0; /* truncate msg when
needed */
- msg_no_more = TRUE;
- /* write message same highlighting as for
- * wait_return */
- smsg_attr(HL_ATTR(HLF_R),
+ msg_no_more = true;
+ msg_ext_set_kind("confirm_sub");
+ smsg_attr(HL_ATTR(HLF_R), // Same highlight as wait_return().
_("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
msg_no_more = FALSE;
msg_scroll = i;
@@ -3819,7 +3819,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
if (!preview || has_second_delim) {
if (subflags.do_count) {
// prevent accidentally changing the buffer by a function
- save_ma = curbuf->b_p_ma;
curbuf->b_p_ma = false;
sandbox++;
}
@@ -3830,9 +3829,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
sub, sub_firstline, false, p_magic, true);
+ // If getting the substitute string caused an error, don't do
+ // the replacement.
// Don't keep flags set by a recursive call
subflags = subflags_save;
- if (subflags.do_count) {
+ if (aborting() || subflags.do_count) {
curbuf->b_p_ma = save_ma;
if (sandbox > 0) {
sandbox--;
@@ -3982,8 +3983,9 @@ skip:
++lnum;
if (u_savedel(lnum, nmatch_tl) != OK)
break;
- for (i = 0; i < nmatch_tl; ++i)
- ml_delete(lnum, (int)FALSE);
+ for (i = 0; i < nmatch_tl; i++) {
+ ml_delete(lnum, false);
+ }
mark_adjust(lnum, lnum + nmatch_tl - 1,
(long)MAXLNUM, -nmatch_tl, false);
if (subflags.do_ask) {
@@ -5147,10 +5149,11 @@ void fix_help_buffer(void)
}
convert_setup(&vc, NULL, NULL);
- ml_append(lnum, cp, (colnr_T)0, FALSE);
- if (cp != IObuff)
+ ml_append(lnum, cp, (colnr_T)0, false);
+ if (cp != IObuff) {
xfree(cp);
- ++lnum;
+ }
+ lnum++;
}
fclose(fd);
}
@@ -6385,6 +6388,7 @@ void ex_substitute(exarg_T *eap)
}
block_autocmds(); // Disable events during command preview.
+ input_disable_events();
char_u *save_eap = eap->arg;
garray_T save_view;
@@ -6427,6 +6431,7 @@ void ex_substitute(exarg_T *eap)
restore_search_patterns();
win_size_restore(&save_view);
ga_clear(&save_view);
+ input_enable_events();
unblock_autocmds();
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 4806eff1b4..0f69d476f9 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -1004,7 +1004,7 @@ return {
},
{
command='function',
- flags=bit.bor(EXTRA, BANG, CMDWIN),
+ flags=bit.bor(EXTRA, BANG, SBOXOK, CMDWIN),
addr_type=ADDR_LINES,
func='ex_function',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 4684a1b31d..40ff29d4a8 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1298,7 +1298,12 @@ void dialog_changed(buf_T *buf, bool checkall)
{
char_u buff[DIALOG_MSG_SIZE];
int ret;
- exarg_T ea;
+ // Init ea pseudo-structure, this is needed for the check_overwrite()
+ // function.
+ exarg_T ea = {
+ .append = false,
+ .forceit = false,
+ };
dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
if (checkall) {
@@ -1307,10 +1312,6 @@ void dialog_changed(buf_T *buf, bool checkall)
ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
}
- // Init ea pseudo-structure, this is needed for the check_overwrite()
- // function.
- ea.append = ea.forceit = false;
-
if (ret == VIM_YES) {
if (buf->b_fname != NULL
&& check_overwrite(&ea,
@@ -1952,14 +1953,17 @@ void ex_next(exarg_T *eap)
void ex_argedit(exarg_T *eap)
{
int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
+ // Whether curbuf will be reused, curbuf->b_ffname will be set.
+ bool curbuf_is_reusable = curbuf_reusable();
if (do_arglist(eap->arg, AL_ADD, i) == FAIL) {
return;
}
maketitle();
- if (curwin->w_arg_idx == 0 && (curbuf->b_ml.ml_flags & ML_EMPTY)
- && curbuf->b_ffname == NULL) {
+ if (curwin->w_arg_idx == 0
+ && (curbuf->b_ml.ml_flags & ML_EMPTY)
+ && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
i = 0;
}
// Edit the argument.
@@ -2257,7 +2261,8 @@ static int alist_add_list(int count, char_u **files, int after)
}
for (int i = 0; i < count; i++) {
ARGLIST[after + i].ae_fname = files[i];
- ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
+ ARGLIST[after + i].ae_fnum = buflist_add(files[i],
+ BLN_LISTED | BLN_CURBUF);
}
ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) {
@@ -3399,13 +3404,18 @@ char_u *getsourceline(int c, void *cookie, int indent)
// Get the next line and concatenate it when it starts with a
// backslash. We always need to read the next line, keep it in
// sp->nextline.
+ // Also check for a comment in between continuation lines: "\ .
sp->nextline = get_one_sourceline(sp);
- if (sp->nextline != NULL && *(p = skipwhite(sp->nextline)) == '\\') {
+ if (sp->nextline != NULL
+ && (*(p = skipwhite(sp->nextline)) == '\\'
+ || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) {
garray_T ga;
ga_init(&ga, (int)sizeof(char_u), 400);
ga_concat(&ga, line);
- ga_concat(&ga, p + 1);
+ if (*p == '\\') {
+ ga_concat(&ga, p + 1);
+ }
for (;; ) {
xfree(sp->nextline);
sp->nextline = get_one_sourceline(sp);
@@ -3413,15 +3423,16 @@ char_u *getsourceline(int c, void *cookie, int indent)
break;
}
p = skipwhite(sp->nextline);
- if (*p != '\\') {
+ if (*p == '\\') {
+ // Adjust the growsize to the current length to speed up
+ // concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
+ }
+ ga_concat(&ga, p + 1);
+ } else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') {
break;
}
- // Adjust the growsize to the current length to speed up
- // concatenating many lines.
- if (ga.ga_len > 400) {
- ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
- }
- ga_concat(&ga, p + 1);
}
ga_append(&ga, NUL);
xfree(line);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5b9b4fed12..9c4a3f389a 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -78,7 +78,7 @@
static int quitmore = 0;
static int ex_pressedreturn = FALSE;
-/* whether ":lcd" was produced for a session */
+/// Whether ":lcd" or ":tcd" was produced for a session.
static int did_lcd;
typedef struct ucmd {
@@ -839,9 +839,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
sourcing_lnum = current_exception->throw_lnum;
current_exception->throw_name = NULL;
- discard_current_exception(); /* uses IObuff if 'verbose' */
- suppress_errthrow = TRUE;
- force_abort = TRUE;
+ discard_current_exception(); // uses IObuff if 'verbose'
+ suppress_errthrow = true;
+ force_abort = true;
+ msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993
if (messages != NULL) {
do {
@@ -2263,8 +2264,11 @@ static char_u * do_one_cmd(char_u **cmdlinep,
need_rethrow = check_cstack = FALSE;
doend:
- if (curwin->w_cursor.lnum == 0) /* can happen with zero line number */
+ // can happen with zero line number
+ if (curwin->w_cursor.lnum == 0) {
curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+ }
if (errormsg != NULL && *errormsg != NUL && !did_emsg) {
if (flags & DOCMD_VERBOSE) {
@@ -2440,10 +2444,24 @@ static char_u *find_command(exarg_T *eap, int *full)
}
}
- if (ASCII_ISLOWER(*eap->cmd))
- eap->cmdidx = cmdidxs[CharOrdLow(*eap->cmd)];
- else
- eap->cmdidx = cmdidxs[26];
+ if (ASCII_ISLOWER(eap->cmd[0])) {
+ const int c1 = eap->cmd[0];
+ const int c2 = eap->cmd[1];
+
+ if (command_count != (int)CMD_SIZE) {
+ iemsg((char *)_("E943: Command table needs to be updated, run 'make'"));
+ getout(1);
+ }
+
+ // Use a precomputed index for fast look-up in cmdnames[]
+ // taking into account the first 2 letters of eap->cmd.
+ eap->cmdidx = cmdidxs1[CharOrdLow(c1)];
+ if (ASCII_ISLOWER(c2)) {
+ eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)];
+ }
+ } else {
+ eap->cmdidx = CMD_bang;
+ }
for (; (int)eap->cmdidx < (int)CMD_SIZE;
eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1))
@@ -3235,7 +3253,7 @@ const char * set_one_cmd_context(
case CMD_tjump:
case CMD_stjump:
case CMD_ptjump:
- if (*p_wop != NUL) {
+ if (wop_flags & WOP_TAGFILE) {
xp->xp_context = EXPAND_TAGS_LISTFILES;
} else {
xp->xp_context = EXPAND_TAGS;
@@ -4488,8 +4506,8 @@ static int get_tabpage_arg(exarg_T *eap)
tab_number = 0;
} else {
tab_number = eap->line2;
- if (!unaccept_arg0 && **eap->cmdlinep == '-') {
- --tab_number;
+ if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-') {
+ tab_number--;
if (tab_number < unaccept_arg0) {
eap->errmsg = e_invarg;
}
@@ -6903,10 +6921,11 @@ static void ex_resize(exarg_T *eap)
n = 9999;
win_setwidth_win(n, wp);
} else {
- if (*eap->arg == '-' || *eap->arg == '+')
+ if (*eap->arg == '-' || *eap->arg == '+') {
n += curwin->w_height;
- else if (n == 0 && eap->arg[0] == NUL) /* default is very wide */
+ } else if (n == 0 && eap->arg[0] == NUL) { // default is very high
n = 9999;
+ }
win_setheight_win(n, wp);
}
}
@@ -7195,10 +7214,11 @@ static void ex_read(exarg_T *eap)
else
lnum = 1;
if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) {
- ml_delete(lnum, FALSE);
+ ml_delete(lnum, false);
if (curwin->w_cursor.lnum > 1
- && curwin->w_cursor.lnum >= lnum)
- --curwin->w_cursor.lnum;
+ && curwin->w_cursor.lnum >= lnum) {
+ curwin->w_cursor.lnum--;
+ }
deleted_lines_mark(lnum, 1L);
}
}
@@ -7224,7 +7244,7 @@ void free_cd_dir(void)
/// Deal with the side effects of changing the current directory.
///
/// @param scope Scope of the function call (global, tab or window).
-void post_chdir(CdScope scope)
+void post_chdir(CdScope scope, bool trigger_dirchanged)
{
// Always overwrite the window-local CWD.
xfree(curwin->w_localdir);
@@ -7264,7 +7284,10 @@ void post_chdir(CdScope scope)
}
shorten_fnames(true);
- do_autocmd_dirchanged(cwd, scope);
+
+ if (trigger_dirchanged) {
+ do_autocmd_dirchanged(cwd, scope);
+ }
}
/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
@@ -7323,10 +7346,10 @@ void ex_cd(exarg_T *eap)
break;
}
- if (vim_chdir(new_dir, scope)) {
+ if (vim_chdir(new_dir)) {
EMSG(_(e_failed));
} else {
- post_chdir(scope);
+ post_chdir(scope, true);
// Echo the new current directory if the command was typed.
if (KeyTyped || p_verbose >= 5) {
ex_pwd(eap);
@@ -7787,11 +7810,12 @@ static void ex_redir(exarg_T *eap)
redir_off = FALSE;
}
-/*
- * ":redraw": force redraw
- */
+/// ":redraw": force redraw
static void ex_redraw(exarg_T *eap)
{
+ if (State & CMDPREVIEW) {
+ return; // Ignore :redraw during 'inccommand' preview. #9777
+ }
int r = RedrawingDisabled;
int p = p_lz;
@@ -7820,11 +7844,12 @@ static void ex_redraw(exarg_T *eap)
ui_flush();
}
-/*
- * ":redrawstatus": force redraw of status line(s)
- */
+/// ":redrawstatus": force redraw of status line(s)
static void ex_redrawstatus(exarg_T *eap)
{
+ if (State & CMDPREVIEW) {
+ return; // Ignore :redrawstatus during 'inccommand' preview. #9777
+ }
int r = RedrawingDisabled;
int p = p_lz;
@@ -9130,6 +9155,7 @@ makeopens(
|| put_eol(fd) == FAIL) {
return FAIL;
}
+ did_lcd = true;
}
/* Don't continue in another tab page when doing only the current one
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 7f7851f078..5b2a1c5c51 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -569,10 +569,12 @@ static void discard_exception(except_T *excp, int was_finished)
*/
void discard_current_exception(void)
{
- discard_exception(current_exception, false);
+ if (current_exception != NULL) {
+ discard_exception(current_exception, false);
+ current_exception = NULL;
+ }
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
- current_exception = NULL;
need_rethrow = false;
}
@@ -1766,6 +1768,7 @@ void enter_cleanup(cleanup_T *csp)
*/
if (current_exception || need_rethrow) {
csp->exception = current_exception;
+ current_exception = NULL;
} else {
csp->exception = NULL;
if (did_emsg) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8e6fc5ad4f..b16023b0ec 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -214,6 +214,15 @@ static int hislen = 0; /* actual length of history tables */
/// user interrupting highlight function to not interrupt command-line.
static bool getln_interrupted_highlight = false;
+// "compl_match_array" points the currently displayed list of entries in the
+// popup menu. It is NULL when there is no popup menu.
+static pumitem_T *compl_match_array = NULL;
+static int compl_match_arraysize;
+// First column in cmdline of the matched item for completion.
+static int compl_startcol;
+static int compl_selected;
+
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
@@ -357,7 +366,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
// redraw the statusline for statuslines that display the current mode
// using the mode() function.
- if (KeyTyped && msg_scrolled == 0) {
+ if (!cmd_silent && msg_scrolled == 0) {
curwin->w_redr_status = true;
redraw_statuslines();
}
@@ -615,8 +624,10 @@ static int command_line_execute(VimState *state, int key)
if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm
&& s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
&& s->c != Ctrl_L) {
- if (ui_has(kUIWildmenu)) {
- ui_call_wildmenu_hide();
+ if (compl_match_array) {
+ pum_undisplay(true);
+ xfree(compl_match_array);
+ compl_match_array = NULL;
}
if (s->xpc.xp_numfiles != -1) {
(void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
@@ -1378,6 +1389,7 @@ static int command_line_handle_key(CommandLineState *s)
}
}
}
+ ccline.special_char = NUL;
redrawcmd();
return command_line_changed(s);
@@ -1436,6 +1448,9 @@ static int command_line_handle_key(CommandLineState *s)
&& ccline.cmdbuff[ccline.cmdpos - 1] != ' ');
set_cmdspos_cursor();
+ if (ccline.special_char != NUL) {
+ putcmdline(ccline.special_char, ccline.special_shift);
+ }
return command_line_not_changed(s);
@@ -1693,6 +1708,7 @@ static int command_line_handle_key(CommandLineState *s)
putcmdline('^', true);
s->c = get_literal(); // get next (two) character(s)
s->do_abbr = false; // don't do abbreviation now
+ ccline.special_char = NUL;
// may need to remove ^ when composing char was typed
if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) {
if (ui_has(kUICmdline)) {
@@ -1710,6 +1726,7 @@ static int command_line_handle_key(CommandLineState *s)
s->ignore_drag_release = true;
putcmdline('?', true);
s->c = get_digraph(true);
+ ccline.special_char = NUL;
if (s->c != NUL) {
break;
@@ -3071,15 +3088,13 @@ void putcmdline(int c, int shift)
draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
}
msg_no_more = false;
- } else {
- ccline.special_char = c;
- ccline.special_shift = shift;
- if (ccline.redraw_state != kCmdRedrawAll) {
+ } else if (ccline.redraw_state != kCmdRedrawAll) {
ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift,
ccline.level);
- }
}
cursorcmd();
+ ccline.special_char = c;
+ ccline.special_shift = shift;
ui_cursor_shape();
}
@@ -3097,6 +3112,7 @@ void unputcmdline(void)
}
msg_no_more = false;
cursorcmd();
+ ccline.special_char = NUL;
ui_cursor_shape();
}
@@ -3457,6 +3473,10 @@ void redrawcmd(void)
set_cmdspos_cursor();
+ if (ccline.special_char != NUL) {
+ putcmdline(ccline.special_char, ccline.special_shift);
+ }
+
/*
* An emsg() before may have set msg_scroll. This is used in normal mode,
* in cmdline mode we can reset them now.
@@ -3746,13 +3766,12 @@ ExpandOne (
else
findex = -1;
}
- if (p_wmnu) {
- if (ui_has(kUIWildmenu)) {
- ui_call_wildmenu_select(findex);
- } else {
- win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
- findex, cmd_showtail);
- }
+ if (compl_match_array) {
+ compl_selected = findex;
+ cmdline_pum_display(false);
+ } else if (p_wmnu) {
+ win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
+ findex, cmd_showtail);
}
if (findex == -1) {
return vim_strsave(orig_save);
@@ -4069,6 +4088,12 @@ void tilde_replace(char_u *orig_pat, int num_files, char_u **files)
}
}
+void cmdline_pum_display(bool changed_array)
+{
+ pum_display(compl_match_array, compl_match_arraysize, compl_selected,
+ changed_array, compl_startcol);
+}
+
/*
* Show all matches for completion on the command line.
* Returns EXPAND_NOTHING when the character that triggered expansion should
@@ -4102,12 +4127,28 @@ static int showmatches(expand_T *xp, int wildmenu)
showtail = cmd_showtail;
}
- if (ui_has(kUIWildmenu)) {
- Array args = ARRAY_DICT_INIT;
+ bool compl_use_pum = (ui_has(kUICmdline)
+ ? ui_has(kUIPopupmenu)
+ : wildmenu && (wop_flags & WOP_PUM))
+ || ui_has(kUIWildmenu);
+
+ if (compl_use_pum) {
+ compl_match_arraysize = num_files;
+ compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T));
for (i = 0; i < num_files; i++) {
- ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i])));
+ compl_match_array[i].pum_text = L_SHOWFILE(i);
}
- ui_call_wildmenu_show(args);
+ ssize_t offset = showtail ? sm_gettail(xp->xp_pattern)-xp->xp_pattern : 0;
+ if (ui_has(kUICmdline)) {
+ compl_startcol = ccline.cmdpos - strnlen((char *)xp->xp_pattern+offset,
+ xp->xp_pattern_len-offset);
+ } else {
+ compl_startcol = ccline.cmdspos
+ - mb_string2cells_len(xp->xp_pattern+offset,
+ xp->xp_pattern_len-offset);
+ }
+ compl_selected = -1;
+ cmdline_pum_display(true);
return EXPAND_OK;
}
@@ -6069,9 +6110,9 @@ static int open_cmdwin(void)
do {
if (++i == hislen)
i = 0;
- if (history[histtype][i].hisstr != NULL)
- ml_append(lnum++, history[histtype][i].hisstr,
- (colnr_T)0, FALSE);
+ if (history[histtype][i].hisstr != NULL) {
+ ml_append(lnum++, history[histtype][i].hisstr, (colnr_T)0, false);
+ }
} while (i != hisidx[histtype]);
}
}
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index ee775bab4a..05611fb8ba 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -687,20 +687,24 @@ char_u *vim_findfile(void *search_ctx_arg)
if (!vim_isAbsName(stackp->ffs_fix_path)
&& search_ctx->ffsc_start_dir) {
if (STRLEN(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCPY(file_path, search_ctx->ffsc_start_dir);
if (!add_pathsep((char *)file_path)) {
+ ff_free_stack_element(stackp);
goto fail;
}
}
// append the fix part of the search path
if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCAT(file_path, stackp->ffs_fix_path);
if (!add_pathsep((char *)file_path)) {
+ ff_free_stack_element(stackp);
goto fail;
}
@@ -715,6 +719,7 @@ char_u *vim_findfile(void *search_ctx_arg)
if (*p > 0) {
(*p)--;
if (len + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
file_path[len++] = '*';
@@ -743,6 +748,7 @@ char_u *vim_findfile(void *search_ctx_arg)
while (*rest_of_wildcards
&& !vim_ispathsep(*rest_of_wildcards)) {
if (len + 1 >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
file_path[len++] = *rest_of_wildcards++;
@@ -792,10 +798,12 @@ char_u *vim_findfile(void *search_ctx_arg)
// prepare the filename to be checked for existence below
if (STRLEN(stackp->ffs_filearray[i]) + 1
+ STRLEN(search_ctx->ffsc_file_to_search) >= MAXPATHL) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCPY(file_path, stackp->ffs_filearray[i]);
if (!add_pathsep((char *)file_path)) {
+ ff_free_stack_element(stackp);
goto fail;
}
STRCAT(file_path, search_ctx->ffsc_file_to_search);
@@ -964,7 +972,6 @@ char_u *vim_findfile(void *search_ctx_arg)
}
fail:
- ff_free_stack_element(stackp);
xfree(file_path);
return NULL;
}
@@ -1634,7 +1641,7 @@ int vim_chdirfile(char_u *fname)
}
/// Change directory to "new_dir". Search 'cdpath' for relative directory names.
-int vim_chdir(char_u *new_dir, CdScope scope)
+int vim_chdir(char_u *new_dir)
{
char_u *dir_name = find_directory_in_path(new_dir, STRLEN(new_dir),
FNAME_MESS, curbuf->b_ffname);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 08a4b76783..55463bdf30 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -11,6 +11,7 @@
#include <fcntl.h>
#include "nvim/vim.h"
+#include "nvim/api/private/handle.h"
#include "nvim/ascii.h"
#include "nvim/fileio.h"
#include "nvim/buffer.h"
@@ -47,6 +48,7 @@
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
#include "nvim/types.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -308,29 +310,30 @@ readfile (
#ifdef UNIX
int swap_mode = -1; /* protection bits for swap file */
#endif
- int fileformat = 0; /* end-of-line format */
- int keep_fileformat = FALSE;
+ int fileformat = 0; // end-of-line format
+ bool keep_fileformat = false;
+ FileInfo file_info;
int file_readonly;
linenr_T skip_count = 0;
linenr_T read_count = 0;
int msg_save = msg_scroll;
linenr_T read_no_eol_lnum = 0; // non-zero lnum when last line of
// last read was missing the eol
- int file_rewind = false;
+ bool file_rewind = false;
int can_retry;
- linenr_T conv_error = 0; /* line nr with conversion error */
- linenr_T illegal_byte = 0; /* line nr with illegal byte */
- int keep_dest_enc = FALSE; /* don't retry when char doesn't fit
- in destination encoding */
+ linenr_T conv_error = 0; // line nr with conversion error
+ linenr_T illegal_byte = 0; // line nr with illegal byte
+ bool keep_dest_enc = false; // don't retry when char doesn't fit
+ // in destination encoding
int bad_char_behavior = BAD_REPLACE;
/* BAD_KEEP, BAD_DROP or character to
* replace with */
char_u *tmpname = NULL; /* name of 'charconvert' output file */
int fio_flags = 0;
- char_u *fenc; /* fileencoding to use */
- int fenc_alloced; /* fenc_next is in allocated memory */
- char_u *fenc_next = NULL; /* next item in 'fencs' or NULL */
- int advance_fenc = FALSE;
+ char_u *fenc; // fileencoding to use
+ bool fenc_alloced; // fenc_next is in allocated memory
+ char_u *fenc_next = NULL; // next item in 'fencs' or NULL
+ bool advance_fenc = false;
long real_size = 0;
# ifdef USE_ICONV
iconv_t iconv_fd = (iconv_t)-1; /* descriptor for iconv() or -1 */
@@ -481,7 +484,6 @@ readfile (
if (newfile && !read_stdin && !read_buffer && !read_fifo) {
// Remember time of file.
- FileInfo file_info;
if (os_fileinfo((char *)fname, &file_info)) {
buf_store_file_info(curbuf, &file_info);
curbuf->b_mtime_read = curbuf->b_mtime;
@@ -627,13 +629,30 @@ readfile (
// Set swap file protection bits after creating it.
if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL
&& curbuf->b_ml.ml_mfp->mf_fname != NULL) {
- (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname,
- (long)swap_mode);
+ const char *swap_fname = (const char *)curbuf->b_ml.ml_mfp->mf_fname;
+
+ // If the group-read bit is set but not the world-read bit, then
+ // the group must be equal to the group of the original file. If
+ // we can't make that happen then reset the group-read bit. This
+ // avoids making the swap file readable to more users when the
+ // primary group of the user is too permissive.
+ if ((swap_mode & 044) == 040) {
+ FileInfo swap_info;
+
+ if (os_fileinfo(swap_fname, &swap_info)
+ && file_info.stat.st_gid != swap_info.stat.st_gid
+ && os_fchown(curbuf->b_ml.ml_mfp->mf_fd, -1, file_info.stat.st_gid)
+ == -1) {
+ swap_mode &= 0600;
+ }
+ }
+
+ (void)os_setperm(swap_fname, swap_mode);
}
#endif
}
- /* If "Quit" selected at ATTENTION dialog, don't load the file */
+ // If "Quit" selected at ATTENTION dialog, don't load the file.
if (swap_exists_action == SEA_QUIT) {
if (!read_buffer && !read_stdin)
close(fd);
@@ -747,11 +766,11 @@ readfile (
*/
if (eap != NULL && eap->force_enc != 0) {
fenc = enc_canonize(eap->cmd + eap->force_enc);
- fenc_alloced = TRUE;
- keep_dest_enc = TRUE;
+ fenc_alloced = true;
+ keep_dest_enc = true;
} else if (curbuf->b_p_bin) {
- fenc = (char_u *)""; /* binary: don't convert */
- fenc_alloced = FALSE;
+ fenc = (char_u *)""; // binary: don't convert
+ fenc_alloced = false;
} else if (curbuf->b_help) {
// Help files are either utf-8 or latin1. Try utf-8 first, if this
// fails it must be latin1.
@@ -762,12 +781,12 @@ readfile (
fenc_alloced = false;
} else if (*p_fencs == NUL) {
- fenc = curbuf->b_p_fenc; /* use format from buffer */
- fenc_alloced = FALSE;
+ fenc = curbuf->b_p_fenc; // use format from buffer
+ fenc_alloced = false;
} else {
fenc_next = p_fencs; /* try items in 'fileencodings' */
fenc = next_fenc(&fenc_next);
- fenc_alloced = TRUE;
+ fenc_alloced = true;
}
/*
@@ -800,10 +819,11 @@ retry:
error = true;
goto failed;
}
- /* Delete the previously read lines. */
- while (lnum > from)
- ml_delete(lnum--, FALSE);
- file_rewind = FALSE;
+ // Delete the previously read lines.
+ while (lnum > from) {
+ ml_delete(lnum--, false);
+ }
+ file_rewind = false;
if (set_options) {
curbuf->b_p_bomb = FALSE;
curbuf->b_start_bomb = FALSE;
@@ -815,9 +835,9 @@ retry:
* When retrying with another "fenc" and the first time "fileformat"
* will be reset.
*/
- if (keep_fileformat)
- keep_fileformat = FALSE;
- else {
+ if (keep_fileformat) {
+ keep_fileformat = false;
+ } else {
if (eap != NULL && eap->force_ff != 0) {
fileformat = get_fileformat_force(curbuf, eap);
try_unix = try_dos = try_mac = FALSE;
@@ -841,7 +861,7 @@ retry:
/*
* Try the next entry in 'fileencodings'.
*/
- advance_fenc = FALSE;
+ advance_fenc = false;
if (eap != NULL && eap->force_enc != 0) {
/* Conversion given with "++cc=" wasn't possible, read
@@ -851,7 +871,7 @@ retry:
if (fenc_alloced)
xfree(fenc);
fenc = (char_u *)"";
- fenc_alloced = FALSE;
+ fenc_alloced = false;
} else {
if (fenc_alloced)
xfree(fenc);
@@ -860,7 +880,7 @@ retry:
fenc_alloced = (fenc_next != NULL);
} else {
fenc = (char_u *)"";
- fenc_alloced = FALSE;
+ fenc_alloced = false;
}
}
if (tmpname != NULL) {
@@ -926,8 +946,8 @@ retry:
if (tmpname == NULL) {
tmpname = readfile_charconvert(fname, fenc, &fd);
if (tmpname == NULL) {
- /* Conversion failed. Try another one. */
- advance_fenc = TRUE;
+ // Conversion failed. Try another one.
+ advance_fenc = true;
if (fd < 0) {
/* Re-opening the original file failed! */
EMSG(_("E202: Conversion made file unreadable!"));
@@ -945,7 +965,7 @@ retry:
) {
/* Conversion wanted but we can't.
* Try the next conversion in 'fileencodings' */
- advance_fenc = TRUE;
+ advance_fenc = true;
goto retry;
}
}
@@ -1180,14 +1200,14 @@ retry:
if (fio_flags == FIO_UCSBOM) {
if (ccname == NULL) {
- /* No BOM detected: retry with next encoding. */
- advance_fenc = TRUE;
+ // No BOM detected: retry with next encoding.
+ advance_fenc = true;
} else {
/* BOM detected: set "fenc" and jump back */
if (fenc_alloced)
xfree(fenc);
fenc = ccname;
- fenc_alloced = FALSE;
+ fenc_alloced = false;
}
/* retry reading without getting new bytes or rewinding */
skip_read = TRUE;
@@ -1512,9 +1532,9 @@ rewind_retry:
did_iconv = TRUE;
else
# endif
- /* use next item from 'fileencodings' */
- advance_fenc = TRUE;
- file_rewind = TRUE;
+ // use next item from 'fileencodings'
+ advance_fenc = true;
+ file_rewind = true;
goto retry;
}
}
@@ -1648,8 +1668,8 @@ rewind_retry:
fileformat = EOL_UNIX;
if (set_options)
set_fileformat(EOL_UNIX, OPT_LOCAL);
- file_rewind = TRUE;
- keep_fileformat = TRUE;
+ file_rewind = true;
+ keep_fileformat = true;
goto retry;
}
ff_error = EOL_DOS;
@@ -1760,8 +1780,8 @@ failed:
if (!recoverymode) {
/* need to delete the last line, which comes from the empty buffer */
if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY)) {
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
- --linecnt;
+ ml_delete(curbuf->b_ml.ml_line_count, false);
+ linecnt--;
}
linecnt = curbuf->b_ml.ml_line_count - linecnt;
if (filesize == 0)
@@ -2762,7 +2782,7 @@ buf_write (
backup_ext = p_bex;
if (backup_copy) {
- char_u *wp;
+ char_u *wp;
int some_error = false;
char_u *dirp;
char_u *rootname;
@@ -3456,7 +3476,7 @@ restore_backup:
// copy the file.
if (os_copy((char *)backup, (char *)fname, UV_FS_COPYFILE_FICLONE)
== 0) {
- end = 1;
+ end = 1; // success
}
} else {
if (vim_rename(backup, fname) == 0) {
@@ -3591,9 +3611,11 @@ restore_backup:
/*
* Remove the backup unless 'backup' option is set
*/
- if (!p_bk && backup != NULL && os_remove((char *)backup) != 0)
+ if (!p_bk && backup != NULL
+ && !write_info.bw_conv_error
+ && os_remove((char *)backup) != 0) {
EMSG(_("E207: Can't delete backup file"));
-
+ }
goto nofail;
@@ -4812,9 +4834,9 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
/* Copy the lines in "frombuf" to "tobuf". */
curbuf = tobuf;
- for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; ++lnum) {
- p = vim_strsave(ml_get_buf(frombuf, lnum, FALSE));
- if (ml_append(lnum - 1, p, 0, FALSE) == FAIL) {
+ for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) {
+ p = vim_strsave(ml_get_buf(frombuf, lnum, false));
+ if (ml_append(lnum - 1, p, 0, false) == FAIL) {
xfree(p);
retval = FAIL;
break;
@@ -4825,13 +4847,14 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
/* Delete all the lines in "frombuf". */
if (retval != FAIL) {
curbuf = frombuf;
- for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; --lnum)
- if (ml_delete(lnum, FALSE) == FAIL) {
- /* Oops! We could try putting back the saved lines, but that
- * might fail again... */
+ for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; lnum--) {
+ if (ml_delete(lnum, false) == FAIL) {
+ // Oops! We could try putting back the saved lines, but that
+ // might fail again...
retval = FAIL;
break;
}
+ }
}
curbuf = tbuf;
@@ -5509,39 +5532,48 @@ static void au_del_cmd(AutoCmd *ac)
au_need_clean = true;
}
-/*
- * Cleanup autocommands and patterns that have been deleted.
- * This is only done when not executing autocommands.
- */
+/// Cleanup autocommands and patterns that have been deleted.
+/// This is only done when not executing autocommands.
static void au_cleanup(void)
{
AutoPat *ap, **prev_ap;
AutoCmd *ac, **prev_ac;
event_T event;
- if (autocmd_busy || !au_need_clean)
+ if (autocmd_busy || !au_need_clean) {
return;
+ }
- /* loop over all events */
+ // Loop over all events.
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1)) {
- /* loop over all autocommand patterns */
+ // Loop over all autocommand patterns.
prev_ap = &(first_autopat[(int)event]);
for (ap = *prev_ap; ap != NULL; ap = *prev_ap) {
- /* loop over all commands for this pattern */
+ // Loop over all commands for this pattern.
prev_ac = &(ap->cmds);
+ bool has_cmd = false;
+
for (ac = *prev_ac; ac != NULL; ac = *prev_ac) {
- /* remove the command if the pattern is to be deleted or when
- * the command has been marked for deletion */
+ // Remove the command if the pattern is to be deleted or when
+ // the command has been marked for deletion.
if (ap->pat == NULL || ac->cmd == NULL) {
*prev_ac = ac->next;
xfree(ac->cmd);
xfree(ac);
- } else
+ } else {
+ has_cmd = true;
prev_ac = &(ac->next);
+ }
+ }
+
+ if (ap->pat != NULL && !has_cmd) {
+ // Pattern was not marked for deletion, but all of its commands were.
+ // So mark the pattern for deletion.
+ au_remove_pat(ap);
}
- /* remove the pattern if it has been marked for deletion */
+ // Remove the pattern if it has been marked for deletion.
if (ap->pat == NULL) {
if (ap->next == NULL) {
if (prev_ap == &(first_autopat[(int)event])) {
@@ -5555,12 +5587,13 @@ static void au_cleanup(void)
*prev_ap = ap->next;
vim_regfree(ap->reg_prog);
xfree(ap);
- } else
+ } else {
prev_ap = &(ap->next);
+ }
}
}
- au_need_clean = FALSE;
+ au_need_clean = false;
}
/*
@@ -5965,20 +5998,36 @@ void do_autocmd(char_u *arg_in, int forceit)
}
}
- // Check for "once" flag.
cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "once", 4) == 0
- && ascii_iswhite(cmd[4])) {
- once = true;
- cmd = skipwhite(cmd + 4);
- }
+ for (size_t i = 0; i < 2; i++) {
+ if (*cmd != NUL) {
+ // Check for "++once" flag.
+ if (STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) {
+ if (once) {
+ EMSG2(_(e_duparg2), "++once");
+ }
+ once = true;
+ cmd = skipwhite(cmd + 6);
+ }
- // Check for "nested" flag.
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
- && ascii_iswhite(cmd[6])) {
- nested = true;
- cmd = skipwhite(cmd + 6);
+ // Check for "++nested" flag.
+ if ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))) {
+ if (nested) {
+ EMSG2(_(e_duparg2), "++nested");
+ }
+ nested = true;
+ cmd = skipwhite(cmd + 8);
+ }
+
+ // Check for the old (deprecated) "nested" flag.
+ if (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
+ if (nested) {
+ EMSG2(_(e_duparg2), "nested");
+ }
+ nested = true;
+ cmd = skipwhite(cmd + 6);
+ }
+ }
}
// Find the start of the commands.
@@ -6393,21 +6442,17 @@ bool check_nomodeline(char_u **argp)
return true;
}
-/*
- * Prepare for executing autocommands for (hidden) buffer "buf".
- * Search for a visible window containing the current buffer. If there isn't
- * one then use "aucmd_win".
- * Set "curbuf" and "curwin" to match "buf".
- */
-void
-aucmd_prepbuf (
- aco_save_T *aco, /* structure to save values in */
- buf_T *buf /* new curbuf */
-)
+/// Prepare for executing autocommands for (hidden) buffer `buf`.
+/// If the current buffer is not in any visible window, put it in a temporary
+/// floating window `aucmd_win`.
+/// Set `curbuf` and `curwin` to match `buf`.
+///
+/// @param aco structure to save values in
+/// @param buf new curbuf
+void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
{
- win_T *win;
- int save_ea;
- int save_acd;
+ win_T *win;
+ bool need_append = true; // Append `aucmd_win` to the window list.
/* Find a window that is for the new buffer */
if (buf == curbuf) { /* be quick when buf is curbuf */
@@ -6422,9 +6467,10 @@ aucmd_prepbuf (
}
}
- /* Allocate "aucmd_win" when needed. */
+ // Allocate the `aucmd_win` dummy floating window.
if (win == NULL && aucmd_win == NULL) {
win_alloc_aucmd_win();
+ need_append = false;
}
if (win == NULL && aucmd_win_used)
/* Strange recursive autocommand, fall back to using the current
@@ -6432,6 +6478,7 @@ aucmd_prepbuf (
win = curwin;
aco->save_curwin = curwin;
+ aco->save_prevwin = prevwin;
aco->save_curbuf = curbuf;
if (win != NULL) {
/* There is a window for "buf" in the current tab page, make it the
@@ -6458,21 +6505,16 @@ aucmd_prepbuf (
aco->globaldir = globaldir;
globaldir = NULL;
-
- /* Split the current window, put the aucmd_win in the upper half.
- * We don't want the BufEnter or WinEnter autocommands. */
- block_autocmds();
- make_snapshot(SNAP_AUCMD_IDX);
- save_ea = p_ea;
- p_ea = false;
-
- /* Prevent chdir() call in win_enter_ext(), through do_autochdir(). */
- save_acd = p_acd;
+ block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
+ if (need_append) {
+ win_append(lastwin, aucmd_win);
+ handle_register_window(aucmd_win);
+ win_config_float(aucmd_win, aucmd_win->w_float_config);
+ }
+ // Prevent chdir() call in win_enter_ext(), through do_autochdir()
+ int save_acd = p_acd;
p_acd = false;
-
- (void)win_split_ins(0, WSP_TOP, aucmd_win, 0);
- (void)win_comp_pos(); /* recompute window positions */
- p_ea = save_ea;
+ win_enter(aucmd_win, false);
p_acd = save_acd;
unblock_autocmds();
curwin = aucmd_win;
@@ -6488,8 +6530,6 @@ aucmd_prepbuf (
/// @param aco structure holding saved values
void aucmd_restbuf(aco_save_T *aco)
{
- int dummy;
-
if (aco->use_aucmd_win) {
curbuf->b_nwindows--;
// Find "aucmd_win", it can't be closed, but it may be in another tab page.
@@ -6508,9 +6548,14 @@ void aucmd_restbuf(aco_save_T *aco)
}
win_found:
- // Remove the window and frame from the tree of frames.
- (void)winframe_remove(curwin, &dummy, NULL);
win_remove(curwin, NULL);
+ handle_unregister_window(curwin);
+ if (curwin->w_grid.chars != NULL) {
+ ui_comp_remove_grid(&curwin->w_grid);
+ ui_call_win_hide(curwin->w_grid.handle);
+ grid_free(&curwin->w_grid);
+ }
+
aucmd_win_used = false;
last_status(false); // may need to remove last status line
@@ -6519,8 +6564,6 @@ win_found:
close_tabpage(curtab);
}
- restore_snapshot(SNAP_AUCMD_IDX, false);
- (void)win_comp_pos(); // recompute window positions
unblock_autocmds();
if (win_valid(aco->save_curwin)) {
@@ -6529,6 +6572,8 @@ win_found:
// Hmm, original window disappeared. Just use the first one.
curwin = firstwin;
}
+ prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
+ : firstwin; // window disappeared?
vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
curbuf = curwin->w_buffer;
@@ -6561,6 +6606,8 @@ win_found:
}
curwin = aco->save_curwin;
+ prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
+ : firstwin; // window disappeared?
curbuf = curwin->w_buffer;
// In case the autocommand moves the cursor to a position that does not
// exist in curbuf
@@ -6956,6 +7003,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
do_cmdline(NULL, getnextac, (void *)&patcmd,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+ reset_lnums(); // restore cursor and topline, unless they were changed
+
if (eap != NULL) {
(void)set_cmdarg(NULL, save_cmdarg);
set_vim_var_nr(VV_CMDBANG, save_cmdbang);
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 8db4b89806..a6011ec414 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -23,6 +23,7 @@ typedef struct {
buf_T *save_curbuf; ///< saved curbuf
int use_aucmd_win; ///< using aucmd_win
win_T *save_curwin; ///< saved curwin
+ win_T *save_prevwin; ///< saved prevwin
win_T *new_curwin; ///< new curwin
bufref_T new_curbuf; ///< new curbuf
char_u *globaldir; ///< saved value of globaldir
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h
index 6e5e47c060..d3b600a40c 100644
--- a/src/nvim/func_attr.h
+++ b/src/nvim/func_attr.h
@@ -205,10 +205,15 @@
#endif
#ifdef DEFINE_FUNC_ATTRIBUTES
+/// Non-deferred API function.
# define FUNC_API_ASYNC
+/// Internal C function not exposed in the RPC API.
# define FUNC_API_NOEXPORT
+/// API function not exposed in VimL/eval.
# define FUNC_API_REMOTE_ONLY
+/// API function introduced at the given API level.
# define FUNC_API_SINCE(X)
+/// API function deprecated since the given API level.
# define FUNC_API_DEPRECATED_SINCE(X)
# define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC
# define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x)
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index cb566d46ca..7859d7c71a 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -28,34 +28,41 @@ local lastchar = nil
local i
local cmd
local first = true
-local prevfirstchar = nil
local byte_a = string.byte('a')
local byte_z = string.byte('z')
+local a_to_z = byte_z - byte_a + 1
-local cmdidxs = string.format([[
-static const cmdidx_T cmdidxs[%u] = {
-]], byte_z - byte_a + 2)
+-- Table giving the index of the first command in cmdnames[] to lookup
+-- based on the first letter of a command.
+local cmdidxs1_out = string.format([[
+static const uint16_t cmdidxs1[%u] = {
+]], a_to_z)
+-- Table giving the index of the first command in cmdnames[] to lookup
+-- based on the first 2 letters of a command.
+-- Values in cmdidxs2[c1][c2] are relative to cmdidxs1[c1] so that they
+-- fit in a byte.
+local cmdidxs2_out = string.format([[
+static const char_u cmdidxs2[%u][%u] = {
+/* a b c d e f g h i j k l m n o p q r s t u v w x y z */
+
+]], a_to_z, a_to_z)
enumfile:write([[
typedef enum CMD_index {
]])
defsfile:write(string.format([[
+static const int command_count = %u;
+]], #defs))
+defsfile:write(string.format([[
static CommandDefinition cmdnames[%u] = {
]], #defs))
+local cmds, cmdidxs1, cmdidxs2 = {}, {}, {}
for i, cmd in ipairs(defs) do
local enumname = cmd.enum or ('CMD_' .. cmd.command)
- firstchar = string.byte(cmd.command)
- if firstchar ~= prevfirstchar then
- if (not prevfirstchar
- or (byte_a <= firstchar and firstchar <= byte_z)
- or (byte_a <= prevfirstchar and prevfirstchar <= byte_z)) then
- if not first then
- cmdidxs = cmdidxs .. ',\n'
- end
- cmdidxs = cmdidxs .. ' ' .. enumname
- end
- prevfirstchar = firstchar
+ local byte_cmd = cmd.command:sub(1, 1):byte()
+ if byte_a <= byte_cmd and byte_cmd <= byte_z then
+ table.insert(cmds, cmd.command)
end
if first then
first = false
@@ -71,6 +78,35 @@ for i, cmd in ipairs(defs) do
.cmd_addr_type = %i
}]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type))
end
+for i = #cmds, 1, -1 do
+ local cmd = cmds[i]
+ -- First and second characters of the command
+ local c1 = cmd:sub(1, 1)
+ cmdidxs1[c1] = i - 1
+ if cmd:len() >= 2 then
+ local c2 = cmd:sub(2, 2)
+ local byte_c2 = string.byte(c2)
+ if byte_a <= byte_c2 and byte_c2 <= byte_z then
+ if not cmdidxs2[c1] then
+ cmdidxs2[c1] = {}
+ end
+ cmdidxs2[c1][c2] = i - 1
+ end
+ end
+end
+for i = byte_a, byte_z do
+ local c1 = string.char(i)
+ cmdidxs1_out = cmdidxs1_out .. ' /* ' .. c1 .. ' */ ' .. cmdidxs1[c1] .. ',\n'
+ cmdidxs2_out = cmdidxs2_out .. ' /* ' .. c1 .. ' */ {'
+ for j = byte_a, byte_z do
+ local c2 = string.char(j)
+ cmdidxs2_out = cmdidxs2_out ..
+ ((cmdidxs2[c1] and cmdidxs2[c1][c2])
+ and string.format('%3d', cmdidxs2[c1][c2] - cmdidxs1[c1])
+ or ' 0') .. ','
+ end
+ cmdidxs2_out = cmdidxs2_out .. ' },\n'
+end
defsfile:write([[
};
@@ -81,8 +117,5 @@ enumfile:write([[
CMD_USER_BUF = -2
} cmdidx_T;
]])
-cmdidxs = cmdidxs .. [[
-
-};
-]]
-defsfile:write(cmdidxs)
+defsfile:write(cmdidxs1_out .. '};\n')
+defsfile:write(cmdidxs2_out .. '};\n')
diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua
index 66430ba26e..3130cecd82 100644
--- a/src/nvim/generators/gen_unicode_tables.lua
+++ b/src/nvim/generators/gen_unicode_tables.lua
@@ -17,7 +17,7 @@
-- which don't have ambiguous or double width, and emoji_all has all Emojis.
if arg[1] == '--help' then
print('Usage:')
- print(' genunicodetables.lua unicode/ unicode_tables.generated.h')
+ print(' gen_unicode_tables.lua unicode/ unicode_tables.generated.h')
os.exit(0)
end
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 139bf7b802..ec14803a2e 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1287,9 +1287,9 @@ openscript (
oldcurscript = curscript;
do {
- update_topline_cursor(); /* update cursor position and topline */
- normal_cmd(&oa, FALSE); /* execute one command */
- vpeekc(); /* check for end of file */
+ update_topline_cursor(); // update cursor position and topline
+ normal_cmd(&oa, false); // execute one command
+ vpeekc(); // check for end of file
} while (scriptin[oldcurscript] != NULL);
State = save_State;
@@ -1425,49 +1425,54 @@ int vgetc(void)
/* a keypad or special function key was not mapped, use it like
* its ASCII equivalent */
switch (c) {
- case K_KPLUS: c = '+'; break;
- case K_KMINUS: c = '-'; break;
- case K_KDIVIDE: c = '/'; break;
- case K_KMULTIPLY: c = '*'; break;
- case K_KENTER: c = CAR; break;
- case K_KPOINT:
- c = '.'; break;
- case K_K0: c = '0'; break;
- case K_K1: c = '1'; break;
- case K_K2: c = '2'; break;
- case K_K3: c = '3'; break;
- case K_K4: c = '4'; break;
- case K_K5: c = '5'; break;
- case K_K6: c = '6'; break;
- case K_K7: c = '7'; break;
- case K_K8: c = '8'; break;
- case K_K9: c = '9'; break;
-
- case K_XHOME:
- case K_ZHOME: if (mod_mask == MOD_MASK_SHIFT) {
- c = K_S_HOME;
- mod_mask = 0;
- } else if (mod_mask == MOD_MASK_CTRL) {
- c = K_C_HOME;
- mod_mask = 0;
- } else
- c = K_HOME;
- break;
- case K_XEND:
- case K_ZEND: if (mod_mask == MOD_MASK_SHIFT) {
- c = K_S_END;
- mod_mask = 0;
- } else if (mod_mask == MOD_MASK_CTRL) {
- c = K_C_END;
- mod_mask = 0;
- } else
- c = K_END;
- break;
+ case K_KPLUS: c = '+'; break;
+ case K_KMINUS: c = '-'; break;
+ case K_KDIVIDE: c = '/'; break;
+ case K_KMULTIPLY: c = '*'; break;
+ case K_KENTER: c = CAR; break;
+ case K_KPOINT: c = '.'; break;
+ case K_KCOMMA: c = ','; break;
+ case K_KEQUAL: c = '='; break;
+ case K_K0: c = '0'; break;
+ case K_K1: c = '1'; break;
+ case K_K2: c = '2'; break;
+ case K_K3: c = '3'; break;
+ case K_K4: c = '4'; break;
+ case K_K5: c = '5'; break;
+ case K_K6: c = '6'; break;
+ case K_K7: c = '7'; break;
+ case K_K8: c = '8'; break;
+ case K_K9: c = '9'; break;
+
+ case K_XHOME:
+ case K_ZHOME:
+ if (mod_mask == MOD_MASK_SHIFT) {
+ c = K_S_HOME;
+ mod_mask = 0;
+ } else if (mod_mask == MOD_MASK_CTRL) {
+ c = K_C_HOME;
+ mod_mask = 0;
+ } else {
+ c = K_HOME;
+ }
+ break;
+ case K_XEND:
+ case K_ZEND:
+ if (mod_mask == MOD_MASK_SHIFT) {
+ c = K_S_END;
+ mod_mask = 0;
+ } else if (mod_mask == MOD_MASK_CTRL) {
+ c = K_C_END;
+ mod_mask = 0;
+ } else {
+ c = K_END;
+ }
+ break;
- case K_XUP: c = K_UP; break;
- case K_XDOWN: c = K_DOWN; break;
- case K_XLEFT: c = K_LEFT; break;
- case K_XRIGHT: c = K_RIGHT; break;
+ case K_XUP: c = K_UP; break;
+ case K_XDOWN: c = K_DOWN; break;
+ case K_XLEFT: c = K_LEFT; break;
+ case K_XRIGHT: c = K_RIGHT; break;
}
/* For a multi-byte character get all the bytes and return the
@@ -2265,18 +2270,25 @@ static int vgetorpeek(int advance)
// that has a <Nop> RHS.
timedout = false;
}
+
+ long wait_time = 0;
+
+ if (advance) {
+ if (typebuf.tb_len == 0
+ || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) {
+ // blocking wait
+ wait_time = -1L;
+ } else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) {
+ wait_time = p_ttm;
+ } else {
+ wait_time = p_tm;
+ }
+ }
+
wait_tb_len = typebuf.tb_len;
c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len,
- typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1,
- !advance
- ? 0
- : ((typebuf.tb_len == 0
- || !(p_timeout || (p_ttimeout
- && keylen == KEYLEN_PART_KEY)))
- ? -1L
- : ((keylen == KEYLEN_PART_KEY && p_ttm >= 0)
- ? p_ttm
- : p_tm)));
+ typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1,
+ wait_time);
if (i != 0)
pop_showcmd();
@@ -2490,286 +2502,334 @@ int fix_input_buffer(char_u *buf, int len)
return len;
}
-/*
- * map[!] : show all key mappings
- * map[!] {lhs} : show key mapping for {lhs}
- * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs}
- * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs}
- * unmap[!] {lhs} : remove key mapping for {lhs}
- * abbr : show all abbreviations
- * abbr {lhs} : show abbreviations for {lhs}
- * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs}
- * noreabbr {lhs} {rhs} : same, but no remapping for {rhs}
- * unabbr {lhs} : remove abbreviation for {lhs}
- *
- * maptype: 0 for :map, 1 for :unmap, 2 for noremap.
- *
- * arg is pointer to any arguments. Note: arg cannot be a read-only string,
- * it will be modified.
- *
- * for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING
- * for :map! mode is INSERT + CMDLINE
- * for :cmap mode is CMDLINE
- * for :imap mode is INSERT
- * for :lmap mode is LANGMAP
- * for :nmap mode is NORMAL
- * for :vmap mode is VISUAL + SELECTMODE
- * for :xmap mode is VISUAL
- * for :smap mode is SELECTMODE
- * for :omap mode is OP_PENDING
- * for :tmap mode is TERM_FOCUS
- *
- * for :abbr mode is INSERT + CMDLINE
- * for :iabbr mode is INSERT
- * for :cabbr mode is CMDLINE
- *
- * Return 0 for success
- * 1 for invalid arguments
- * 2 for no match
- * 4 for out of mem (deprecated, WON'T HAPPEN)
- * 5 for entry not unique
- */
-int
-do_map (
- int maptype,
- char_u *arg,
- int mode,
- int abbrev /* not a mapping but an abbreviation */
-)
+/// Replace termcodes in the given LHS and RHS and store the results into the
+/// `lhs` and `rhs` of the given @ref MapArguments struct.
+///
+/// `rhs` and `orig_rhs` will both point to new allocated buffers. `orig_rhs`
+/// will hold a copy of the given `orig_rhs`.
+///
+/// The `*_len` variables will be set appropriately. If the length of
+/// the final `lhs` exceeds `MAXMAPLEN`, `lhs_len` will be set equal to the
+/// original larger length and `lhs` will be truncated.
+///
+/// If RHS is equal to "<Nop>", `rhs` will be the empty string, `rhs_len`
+/// will be zero, and `rhs_is_noop` will be set to true.
+///
+/// Any memory allocated by @ref replace_termcodes is freed before this function
+/// returns.
+///
+/// @param[in] orig_lhs Original mapping LHS, with characters to replace.
+/// @param[in] orig_lhs_len `strlen` of orig_lhs.
+/// @param[in] orig_rhs Original mapping RHS, with characters to replace.
+/// @param[in] orig_rhs_len `strlen` of orig_rhs.
+/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
+/// @param[out] mapargs MapArguments struct holding the replaced strings.
+void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len,
+ const char_u *orig_rhs, const size_t orig_rhs_len,
+ int cpo_flags, MapArguments *mapargs)
{
- char_u *keys;
- mapblock_T *mp, **mpp;
- char_u *rhs;
- char_u *p;
- int n;
- int len = 0; /* init for GCC */
- int hasarg;
- int haskey;
- int did_it = FALSE;
- int did_local = FALSE;
- int round;
- char_u *keys_buf = NULL;
- char_u *arg_buf = NULL;
- int retval = 0;
- int do_backslash;
- int hash;
- int new_hash;
- mapblock_T **abbr_table;
- mapblock_T **map_table;
- bool unique = false;
- bool nowait = false;
- bool silent = false;
- bool expr = false;
- int noremap;
- char_u *orig_rhs;
+ char_u *lhs_buf = NULL;
+ char_u *rhs_buf = NULL;
- keys = arg;
- map_table = maphash;
- abbr_table = &first_abbr;
+ // If mapping has been given as ^V<C_UP> say, then replace the term codes
+ // with the appropriate two bytes. If it is a shifted special key, unshift
+ // it too, giving another two bytes.
+ //
+ // replace_termcodes() may move the result to allocated memory, which
+ // needs to be freed later (*lhs_buf and *rhs_buf).
+ // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
+ char_u *replaced = replace_termcodes(orig_lhs, orig_lhs_len, &lhs_buf,
+ true, true, true, cpo_flags);
+ mapargs->lhs_len = STRLEN(replaced);
+ xstrlcpy((char *)mapargs->lhs, (char *)replaced, sizeof(mapargs->lhs));
+
+ mapargs->orig_rhs_len = orig_rhs_len;
+ mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char_u));
+ xstrlcpy((char *)mapargs->orig_rhs, (char *)orig_rhs,
+ mapargs->orig_rhs_len + 1);
+
+ if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing
+ mapargs->rhs = xcalloc(1, sizeof(char_u)); // single null-char
+ mapargs->rhs_len = 0;
+ mapargs->rhs_is_noop = true;
+ } else {
+ replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf,
+ false, true, true, cpo_flags);
+ mapargs->rhs_len = STRLEN(replaced);
+ mapargs->rhs_is_noop = false;
+ mapargs->rhs = xcalloc(mapargs->rhs_len + 1, sizeof(char_u));
+ xstrlcpy((char *)mapargs->rhs, (char *)replaced, mapargs->rhs_len + 1);
+ }
- /* For ":noremap" don't remap, otherwise do remap. */
- if (maptype == 2)
- noremap = REMAP_NONE;
- else
- noremap = REMAP_YES;
+ xfree(lhs_buf);
+ xfree(rhs_buf);
+}
- /* Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
- * any order. */
- for (;; ) {
- /*
- * Check for "<buffer>": mapping local to buffer.
- */
- if (STRNCMP(keys, "<buffer>", 8) == 0) {
- keys = skipwhite(keys + 8);
- map_table = curbuf->b_maphash;
- abbr_table = &curbuf->b_first_abbr;
+/// Parse a string of |:map-arguments| into a @ref MapArguments struct.
+///
+/// Termcodes, backslashes, CTRL-V's, etc. inside the extracted {lhs} and
+/// {rhs} are replaced by @ref set_maparg_lhs_rhs.
+///
+/// rhs and orig_rhs in the returned mapargs will be set to null or a pointer
+/// to allocated memory and should be freed even on error.
+///
+/// @param[in] strargs String of map args, e.g. "<buffer> <expr><silent>".
+/// May contain leading or trailing whitespace.
+/// @param[in] is_unmap True, if strargs should be parsed like an |:unmap|
+/// command. |:unmap| commands interpret *all* text to the
+/// right of the last map argument as the {lhs} of the
+/// mapping, i.e. a literal ' ' character is treated like
+/// a "<space>", rather than separating the {lhs} from the
+/// {rhs}.
+/// @param[out] mapargs MapArguments struct holding all extracted argument
+/// values.
+/// @return 0 on success, 1 if invalid arguments are detected.
+int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *mapargs)
+{
+ const char_u *to_parse = strargs;
+ to_parse = skipwhite(to_parse);
+ MapArguments parsed_args; // copy these into mapargs "all at once" when done
+ memset(&parsed_args, 0, sizeof(parsed_args));
+
+ // Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in
+ // any order.
+ while (true) {
+ if (STRNCMP(to_parse, "<buffer>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.buffer = true;
continue;
}
- /*
- * Check for "<nowait>": don't wait for more characters.
- */
- if (STRNCMP(keys, "<nowait>", 8) == 0) {
- keys = skipwhite(keys + 8);
- nowait = true;
+ if (STRNCMP(to_parse, "<nowait>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.nowait = true;
continue;
}
- /*
- * Check for "<silent>": don't echo commands.
- */
- if (STRNCMP(keys, "<silent>", 8) == 0) {
- keys = skipwhite(keys + 8);
- silent = true;
+ if (STRNCMP(to_parse, "<silent>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.silent = true;
continue;
}
// Ignore obsolete "<special>" modifier.
- if (STRNCMP(keys, "<special>", 9) == 0) {
- keys = skipwhite(keys + 9);
+ if (STRNCMP(to_parse, "<special>", 9) == 0) {
+ to_parse = skipwhite(to_parse + 9);
continue;
}
- /*
- * Check for "<script>": remap script-local mappings only
- */
- if (STRNCMP(keys, "<script>", 8) == 0) {
- keys = skipwhite(keys + 8);
- noremap = REMAP_SCRIPT;
+ if (STRNCMP(to_parse, "<script>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.script = true;
continue;
}
- /*
- * Check for "<expr>": {rhs} is an expression.
- */
- if (STRNCMP(keys, "<expr>", 6) == 0) {
- keys = skipwhite(keys + 6);
- expr = true;
+ if (STRNCMP(to_parse, "<expr>", 6) == 0) {
+ to_parse = skipwhite(to_parse + 6);
+ parsed_args.expr = true;
continue;
}
- /*
- * Check for "<unique>": don't overwrite an existing mapping.
- */
- if (STRNCMP(keys, "<unique>", 8) == 0) {
- keys = skipwhite(keys + 8);
- unique = true;
+
+ if (STRNCMP(to_parse, "<unique>", 8) == 0) {
+ to_parse = skipwhite(to_parse + 8);
+ parsed_args.unique = true;
continue;
}
break;
}
- validate_maphash();
-
- /*
- * Find end of keys and skip CTRL-Vs (and backslashes) in it.
- * Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
- * with :unmap white space is included in the keys, no argument possible.
- */
- p = keys;
- do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
- while (*p && (maptype == 1 || !ascii_iswhite(*p))) {
- if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) && p[1] != NUL) {
- p++; // skip CTRL-V or backslash
+ // Find the next whitespace character, call that the end of {lhs}.
+ //
+ // If a character (e.g. whitespace) is immediately preceded by a CTRL-V,
+ // "scan past" that character, i.e. don't "terminate" LHS with that character
+ // if it's whitespace.
+ //
+ // Treat backslash like CTRL-V when 'cpoptions' does not contain 'B'.
+ //
+ // With :unmap, literal white space is included in the {lhs}; there is no
+ // separate {rhs}.
+ const char_u *lhs_end = to_parse;
+ bool do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
+ while (*lhs_end && (is_unmap || !ascii_iswhite(*lhs_end))) {
+ if ((lhs_end[0] == Ctrl_V || (do_backslash && lhs_end[0] == '\\'))
+ && lhs_end[1] != NUL) {
+ lhs_end++; // skip CTRL-V or backslash
}
- p++;
+ lhs_end++;
+ }
+
+ // {lhs_end} is a pointer to the "terminating whitespace" after {lhs}.
+ // Use that to initialize {rhs_start}.
+ const char_u *rhs_start = skipwhite(lhs_end);
+
+ // Given {lhs} might be larger than MAXMAPLEN before replace_termcodes
+ // (e.g. "<Space>" is longer than ' '), so first copy into a buffer.
+ size_t orig_lhs_len = (size_t)(lhs_end - to_parse);
+ char_u *lhs_to_replace = xcalloc(orig_lhs_len + 1, sizeof(char_u));
+ xstrlcpy((char *)lhs_to_replace, (char *)to_parse, orig_lhs_len + 1);
+
+ size_t orig_rhs_len = STRLEN(rhs_start);
+ set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len,
+ rhs_start, orig_rhs_len,
+ CPO_TO_CPO_FLAGS, &parsed_args);
+
+ xfree(lhs_to_replace);
+
+ *mapargs = parsed_args;
+
+ if (parsed_args.lhs_len > MAXMAPLEN) {
+ return 1;
+ }
+ return 0;
+}
+
+/// Sets or removes a mapping or abbreviation in buffer `buf`.
+///
+/// @param maptype @see do_map
+/// @param args Fully parsed and "preprocessed" arguments for the
+/// (un)map/abbrev command. Termcodes should have already been
+/// replaced; whitespace, `<` and `>` signs, etc. in {lhs} and
+/// {rhs} are assumed to be literal components of the mapping.
+/// @param mode @see do_map
+/// @param is_abbrev @see do_map
+/// @param buf Target Buffer
+int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
+ buf_T *buf)
+{
+ mapblock_T *mp, **mpp;
+ char_u *p;
+ int n;
+ int len = 0; // init for GCC
+ int did_it = false;
+ int did_local = false;
+ int round;
+ int retval = 0;
+ int hash;
+ int new_hash;
+ mapblock_T **abbr_table;
+ mapblock_T **map_table;
+ int noremap;
+
+ map_table = maphash;
+ abbr_table = &first_abbr;
+
+ // For ":noremap" don't remap, otherwise do remap.
+ if (maptype == 2) {
+ noremap = REMAP_NONE;
+ } else {
+ noremap = REMAP_YES;
}
- if (*p != NUL) {
- *p++ = NUL;
+
+ if (args->buffer) {
+ // If <buffer> was given, we'll be searching through the buffer's
+ // mappings/abbreviations, not the globals.
+ map_table = buf->b_maphash;
+ abbr_table = &buf->b_first_abbr;
}
+ if (args->script) {
+ noremap = REMAP_SCRIPT;
+ }
+
+ validate_maphash();
- p = skipwhite(p);
- rhs = p;
- hasarg = (*rhs != NUL);
- haskey = (*keys != NUL);
+ bool has_lhs = (args->lhs[0] != NUL);
+ bool has_rhs = (args->rhs[0] != NUL) || args->rhs_is_noop;
- /* check for :unmap without argument */
- if (maptype == 1 && !haskey) {
+ // check for :unmap without argument
+ if (maptype == 1 && !has_lhs) {
retval = 1;
goto theend;
}
- // If mapping has been given as ^V<C_UP> say, then replace the term codes
- // with the appropriate two bytes. If it is a shifted special key, unshift
- // it too, giving another two bytes.
- // replace_termcodes() may move the result to allocated memory, which
- // needs to be freed later (*keys_buf and *arg_buf).
- // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
- if (haskey) {
- keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
- CPO_TO_CPO_FLAGS);
- }
- orig_rhs = rhs;
- if (hasarg) {
- if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing
- rhs = (char_u *)"";
- } else {
- rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, true,
- CPO_TO_CPO_FLAGS);
- }
- }
+ char_u *lhs = (char_u *)&args->lhs;
+ char_u *rhs = (char_u *)args->rhs;
+ char_u *orig_rhs = args->orig_rhs;
- //
// check arguments and translate function keys
- //
- if (haskey) {
- len = (int)STRLEN(keys);
- if (len > MAXMAPLEN) { /* maximum length of MAXMAPLEN chars */
+ if (has_lhs) {
+ len = (int)args->lhs_len;
+ if (len > MAXMAPLEN) {
retval = 1;
goto theend;
}
- if (abbrev && maptype != 1) {
- /*
- * If an abbreviation ends in a keyword character, the
- * rest must be all keyword-char or all non-keyword-char.
- * Otherwise we won't be able to find the start of it in a
- * vi-compatible way.
- */
+ if (is_abbrev && maptype != 1) {
+ //
+ // If an abbreviation ends in a keyword character, the
+ // rest must be all keyword-char or all non-keyword-char.
+ // Otherwise we won't be able to find the start of it in a
+ // vi-compatible way.
+ //
if (has_mbyte) {
int first, last;
int same = -1;
- first = vim_iswordp(keys);
+ first = vim_iswordp(lhs);
last = first;
- p = keys + (*mb_ptr2len)(keys);
+ p = lhs + (*mb_ptr2len)(lhs);
n = 1;
- while (p < keys + len) {
- ++n; /* nr of (multi-byte) chars */
- last = vim_iswordp(p); /* type of last char */
- if (same == -1 && last != first)
- same = n - 1; /* count of same char type */
+ while (p < lhs + len) {
+ n++; // nr of (multi-byte) chars
+ last = vim_iswordp(p); // type of last char
+ if (same == -1 && last != first) {
+ same = n - 1; // count of same char type
+ }
p += (*mb_ptr2len)(p);
}
if (last && n > 2 && same >= 0 && same < n - 1) {
retval = 1;
goto theend;
}
- } else if (vim_iswordc(keys[len - 1])) /* ends in keyword char */
- for (n = 0; n < len - 2; ++n)
- if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) {
+ } else if (vim_iswordc(lhs[len - 1])) { // ends in keyword char
+ for (n = 0; n < len - 2; n++) {
+ if (vim_iswordc(lhs[n]) != vim_iswordc(lhs[len - 2])) {
retval = 1;
goto theend;
}
- /* An abbreviation cannot contain white space. */
- for (n = 0; n < len; ++n)
- if (ascii_iswhite(keys[n])) {
+ } // for
+ }
+ // An abbreviation cannot contain white space.
+ for (n = 0; n < len; n++) {
+ if (ascii_iswhite(lhs[n])) {
retval = 1;
goto theend;
}
+ } // for
}
}
- if (haskey && hasarg && abbrev) /* if we will add an abbreviation */
- no_abbr = FALSE; /* reset flag that indicates there are
- no abbreviations */
+ if (has_lhs && has_rhs && is_abbrev) { // if we will add an abbreviation,
+ no_abbr = false; // reset flag that indicates there are no abbreviations
+ }
- if (!haskey || (maptype != 1 && !hasarg))
+ if (!has_lhs || (maptype != 1 && !has_rhs)) {
msg_start();
+ }
- /*
- * Check if a new local mapping wasn't already defined globally.
- */
- if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) {
- /* need to loop over all global hash lists */
- for (hash = 0; hash < 256 && !got_int; ++hash) {
- if (abbrev) {
- if (hash != 0) /* there is only one abbreviation list */
+ // Check if a new local mapping wasn't already defined globally.
+ if (map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
break;
+ }
mp = first_abbr;
- } else
+ } else {
mp = maphash[hash];
+ }
for (; mp != NULL && !got_int; mp = mp->m_next) {
- /* check entries with the same mode */
+ // check entries with the same mode
if ((mp->m_mode & mode) != 0
&& mp->m_keylen == len
- && unique
- && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) {
- if (abbrev)
+ && args->unique
+ && STRNCMP(mp->m_keys, lhs, (size_t)len) == 0) {
+ if (is_abbrev) {
EMSG2(_("E224: global abbreviation already exists for %s"),
- mp->m_keys);
- else
- EMSG2(_("E225: global mapping already exists for %s"),
- mp->m_keys);
+ mp->m_keys);
+ } else {
+ EMSG2(_("E225: global mapping already exists for %s"), mp->m_keys);
+ }
retval = 5;
goto theend;
}
@@ -2777,30 +2837,29 @@ do_map (
}
}
- /*
- * When listing global mappings, also list buffer-local ones here.
- */
- if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) {
- /* need to loop over all global hash lists */
- for (hash = 0; hash < 256 && !got_int; ++hash) {
- if (abbrev) {
- if (hash != 0) /* there is only one abbreviation list */
+ // When listing global mappings, also list buffer-local ones here.
+ if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
+ // need to loop over all global hash lists
+ for (hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash != 0) { // there is only one abbreviation list
break;
- mp = curbuf->b_first_abbr;
- } else
- mp = curbuf->b_maphash[hash];
+ }
+ mp = buf->b_first_abbr;
+ } else {
+ mp = buf->b_maphash[hash];
+ }
for (; mp != NULL && !got_int; mp = mp->m_next) {
- /* check entries with the same mode */
+ // check entries with the same mode
if ((mp->m_mode & mode) != 0) {
- if (!haskey) { /* show all entries */
- showmap(mp, TRUE);
- did_local = TRUE;
+ if (!has_lhs) { // show all entries
+ showmap(mp, true);
+ did_local = true;
} else {
n = mp->m_keylen;
- if (STRNCMP(mp->m_keys, keys,
- (size_t)(n < len ? n : len)) == 0) {
- showmap(mp, TRUE);
- did_local = TRUE;
+ if (STRNCMP(mp->m_keys, lhs, (size_t)(n < len ? n : len)) == 0) {
+ showmap(mp, true);
+ did_local = true;
}
}
}
@@ -2808,103 +2867,98 @@ do_map (
}
}
- /*
- * Find an entry in the maphash[] list that matches.
- * For :unmap we may loop two times: once to try to unmap an entry with a
- * matching 'from' part, a second time, if the first fails, to unmap an
- * entry with a matching 'to' part. This was done to allow ":ab foo bar"
- * to be unmapped by typing ":unab foo", where "foo" will be replaced by
- * "bar" because of the abbreviation.
- */
+ // Find an entry in the maphash[] list that matches.
+ // For :unmap we may loop two times: once to try to unmap an entry with a
+ // matching 'from' part, a second time, if the first fails, to unmap an
+ // entry with a matching 'to' part. This was done to allow ":ab foo bar"
+ // to be unmapped by typing ":unab foo", where "foo" will be replaced by
+ // "bar" because of the abbreviation.
for (round = 0; (round == 0 || maptype == 1) && round <= 1
- && !did_it && !got_int; ++round) {
- /* need to loop over all hash lists */
- for (hash = 0; hash < 256 && !got_int; ++hash) {
- if (abbrev) {
- if (hash > 0) /* there is only one abbreviation list */
+ && !did_it && !got_int; round++) {
+ // need to loop over all hash lists
+ for (hash = 0; hash < 256 && !got_int; hash++) {
+ if (is_abbrev) {
+ if (hash > 0) { // there is only one abbreviation list
break;
+ }
mpp = abbr_table;
- } else
+ } else {
mpp = &(map_table[hash]);
+ }
for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) {
-
- if (!(mp->m_mode & mode)) { /* skip entries with wrong mode */
+ if (!(mp->m_mode & mode)) { // skip entries with wrong mode
mpp = &(mp->m_next);
continue;
}
- if (!haskey) { /* show all entries */
+ if (!has_lhs) { // show all entries
showmap(mp, map_table != maphash);
- did_it = TRUE;
- } else { /* do we have a match? */
- if (round) { /* second round: Try unmap "rhs" string */
+ did_it = true;
+ } else { // do we have a match?
+ if (round) { // second round: Try unmap "rhs" string
n = (int)STRLEN(mp->m_str);
p = mp->m_str;
} else {
n = mp->m_keylen;
p = mp->m_keys;
}
- if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) {
- if (maptype == 1) { /* delete entry */
- /* Only accept a full match. For abbreviations we
- * ignore trailing space when matching with the
- * "lhs", since an abbreviation can't have
- * trailing space. */
- if (n != len && (!abbrev || round || n > len
- || *skipwhite(keys + n) != NUL)) {
+ if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) {
+ if (maptype == 1) { // delete entry
+ // Only accept a full match. For abbreviations we
+ // ignore trailing space when matching with the
+ // "lhs", since an abbreviation can't have
+ // trailing space.
+ if (n != len && (!is_abbrev || round || n > len
+ || *skipwhite(lhs + n) != NUL)) {
mpp = &(mp->m_next);
continue;
}
- /*
- * We reset the indicated mode bits. If nothing is
- * left the entry is deleted below.
- */
+ // We reset the indicated mode bits. If nothing is
+ // left the entry is deleted below.
mp->m_mode &= ~mode;
- did_it = TRUE; /* remember we did something */
- } else if (!hasarg) { /* show matching entry */
+ did_it = true; // remember we did something
+ } else if (!has_rhs) { // show matching entry
showmap(mp, map_table != maphash);
- did_it = TRUE;
- } else if (n != len) { /* new entry is ambiguous */
+ did_it = true;
+ } else if (n != len) { // new entry is ambiguous
mpp = &(mp->m_next);
continue;
- } else if (unique) {
- if (abbrev)
- EMSG2(_("E226: abbreviation already exists for %s"),
- p);
- else
+ } else if (args->unique) {
+ if (is_abbrev) {
+ EMSG2(_("E226: abbreviation already exists for %s"), p);
+ } else {
EMSG2(_("E227: mapping already exists for %s"), p);
+ }
retval = 5;
goto theend;
- } else { /* new rhs for existing entry */
- mp->m_mode &= ~mode; /* remove mode bits */
- if (mp->m_mode == 0 && !did_it) { /* reuse entry */
+ } else { // new rhs for existing entry
+ mp->m_mode &= ~mode; // remove mode bits
+ if (mp->m_mode == 0 && !did_it) { // reuse entry
xfree(mp->m_str);
mp->m_str = vim_strsave(rhs);
xfree(mp->m_orig_str);
mp->m_orig_str = vim_strsave(orig_rhs);
mp->m_noremap = noremap;
- mp->m_nowait = nowait;
- mp->m_silent = silent;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
mp->m_mode = mode;
- mp->m_expr = expr;
+ mp->m_expr = args->expr;
mp->m_script_ID = current_SID;
- did_it = TRUE;
+ did_it = true;
}
}
- if (mp->m_mode == 0) { // entry can be deleted
+ if (mp->m_mode == 0) { // entry can be deleted
mapblock_free(mpp);
- continue; // continue with *mpp
+ continue; // continue with *mpp
}
- /*
- * May need to put this entry into another hash list.
- */
+ // May need to put this entry into another hash list.
new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
- if (!abbrev && new_hash != hash) {
+ if (!is_abbrev && new_hash != hash) {
*mpp = mp->m_next;
mp->m_next = map_table[new_hash];
map_table[new_hash] = mp;
- continue; /* continue with *mpp */
+ continue; // continue with *mpp
}
}
}
@@ -2913,13 +2967,13 @@ do_map (
}
}
- if (maptype == 1) { /* delete entry */
+ if (maptype == 1) { // delete entry
if (!did_it) {
- retval = 2; /* no match */
- } else if (*keys == Ctrl_C) {
+ retval = 2; // no match
+ } else if (*lhs == Ctrl_C) {
// If CTRL-C has been unmapped, reuse it for Interrupting.
- if (map_table == curbuf->b_maphash) {
- curbuf->b_mapped_ctrl_c &= ~mode;
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c &= ~mode;
} else {
mapped_ctrl_c &= ~mode;
}
@@ -2927,48 +2981,46 @@ do_map (
goto theend;
}
- if (!haskey || !hasarg) { /* print entries */
- if (!did_it
- && !did_local
- ) {
- if (abbrev)
+ if (!has_lhs || !has_rhs) { // print entries
+ if (!did_it && !did_local) {
+ if (is_abbrev) {
MSG(_("No abbreviation found"));
- else
+ } else {
MSG(_("No mapping found"));
+ }
}
- goto theend; /* listing finished */
+ goto theend; // listing finished
}
- if (did_it) /* have added the new entry already */
+ if (did_it) { // have added the new entry already
goto theend;
+ }
- /*
- * Get here when adding a new entry to the maphash[] list or abbrlist.
- */
+ // Get here when adding a new entry to the maphash[] list or abbrlist.
mp = xmalloc(sizeof(mapblock_T));
// If CTRL-C has been mapped, don't always use it for Interrupting.
- if (*keys == Ctrl_C) {
- if (map_table == curbuf->b_maphash) {
- curbuf->b_mapped_ctrl_c |= mode;
+ if (*lhs == Ctrl_C) {
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c |= mode;
} else {
mapped_ctrl_c |= mode;
}
}
- mp->m_keys = vim_strsave(keys);
+ mp->m_keys = vim_strsave(lhs);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
- mp->m_nowait = nowait;
- mp->m_silent = silent;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
mp->m_mode = mode;
- mp->m_expr = expr;
+ mp->m_expr = args->expr;
mp->m_script_ID = current_SID;
- /* add the new entry in front of the abbrlist or maphash[] list */
- if (abbrev) {
+ // add the new entry in front of the abbrlist or maphash[] list
+ if (is_abbrev) {
mp->m_next = *abbr_table;
*abbr_table = mp;
} else {
@@ -2978,11 +3030,80 @@ do_map (
}
theend:
- xfree(keys_buf);
- xfree(arg_buf);
return retval;
}
+
+/// Set or remove a mapping or an abbreviation in the current buffer, OR
+/// display (matching) mappings/abbreviations.
+///
+/// ```vim
+/// map[!] " show all key mappings
+/// map[!] {lhs} " show key mapping for {lhs}
+/// map[!] {lhs} {rhs} " set key mapping for {lhs} to {rhs}
+/// noremap[!] {lhs} {rhs} " same, but no remapping for {rhs}
+/// unmap[!] {lhs} " remove key mapping for {lhs}
+/// abbr " show all abbreviations
+/// abbr {lhs} " show abbreviations for {lhs}
+/// abbr {lhs} {rhs} " set abbreviation for {lhs} to {rhs}
+/// noreabbr {lhs} {rhs} " same, but no remapping for {rhs}
+/// unabbr {lhs} " remove abbreviation for {lhs}
+///
+/// for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING
+/// for :map! mode is INSERT + CMDLINE
+/// for :cmap mode is CMDLINE
+/// for :imap mode is INSERT
+/// for :lmap mode is LANGMAP
+/// for :nmap mode is NORMAL
+/// for :vmap mode is VISUAL + SELECTMODE
+/// for :xmap mode is VISUAL
+/// for :smap mode is SELECTMODE
+/// for :omap mode is OP_PENDING
+/// for :tmap mode is TERM_FOCUS
+///
+/// for :abbr mode is INSERT + CMDLINE
+/// for :iabbr mode is INSERT
+/// for :cabbr mode is CMDLINE
+/// ```
+///
+/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|.
+/// @param arg C-string containing the arguments of the map/abbrev
+/// command, i.e. everything except the initial `:[X][nore]map`.
+/// - Cannot be a read-only string; it will be modified.
+/// @param mode Bitflags representing the mode in which to set the mapping.
+/// See @ref get_map_mode.
+/// @param is_abbrev True if setting an abbreviation, false otherwise.
+///
+/// @return 0 on success. On failure, will return one of the following:
+/// - 1 for invalid arguments
+/// - 2 for no match
+/// - 4 for out of mem (deprecated, WON'T HAPPEN)
+/// - 5 for entry not unique
+///
+int do_map(int maptype, char_u *arg, int mode, bool is_abbrev)
+{
+ MapArguments parsed_args;
+ int result = str_to_mapargs(arg, maptype == 1, &parsed_args);
+ switch (result) {
+ case 0:
+ break;
+ case 1:
+ result = 1; // invalid arguments
+ goto free_and_return;
+ default:
+ assert(false && "Unknown return code from str_to_mapargs!");
+ result = -1;
+ goto free_and_return;
+ } // switch
+
+ result = buf_do_map(maptype, &parsed_args, mode, is_abbrev, curbuf);
+
+free_and_return:
+ xfree(parsed_args.rhs);
+ xfree(parsed_args.orig_rhs);
+ return result;
+}
+
/*
* Delete one entry from the abbrlist or maphash[].
* "mpp" is a pointer to the m_next field of the PREVIOUS entry!
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 4f548d975a..a40ea7730a 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -5,6 +5,7 @@
#include "nvim/types.h"
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/vim.h"
/// Values for "noremap" argument of ins_typebuf()
///
@@ -23,6 +24,39 @@ typedef enum {
FLUSH_INPUT // flush typebuf and inchar() input
} flush_buffers_T;
+/// All possible |:map-arguments| usable in a |:map| command.
+///
+/// The <special> argument has no effect on mappings and is excluded from this
+/// struct declaration. |noremap| is included, since it behaves like a map
+/// argument when used in a mapping.
+///
+/// @see mapblock_T
+struct map_arguments {
+ bool buffer;
+ bool expr;
+ bool noremap;
+ bool nowait;
+ bool script;
+ bool silent;
+ bool unique;
+
+ /// The {lhs} of the mapping.
+ ///
+ /// vim limits this to MAXMAPLEN characters, allowing us to use a static
+ /// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
+ /// that {lhs} was too long and truncated.
+ char_u lhs[MAXMAPLEN + 1];
+ size_t lhs_len;
+
+ char_u *rhs; /// The {rhs} of the mapping.
+ size_t rhs_len;
+ bool rhs_is_noop; /// True when the {orig_rhs} is <nop>.
+
+ char_u *orig_rhs; /// The original text of the {rhs}.
+ size_t orig_rhs_len;
+};
+typedef struct map_arguments MapArguments;
+
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
#define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */
diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h
index acc7e3a92c..629301e8fe 100644
--- a/src/nvim/gettext.h
+++ b/src/nvim/gettext.h
@@ -11,6 +11,11 @@
# define N_(x) x
# endif
# define NGETTEXT(x, xs, n) ngettext(x, xs, n)
+// On a Mac, gettext's libintl.h defines "setlocale" to be replaced by
+// "libintl_setlocal" which leads to wrong return values. #9789
+# if defined(__APPLE__) && defined(setlocale)
+# undef setlocale
+# endif
#else
# define _(x) ((char *)(x))
# define N_(x) x
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 004b3252da..ec14ada3d2 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -227,6 +227,7 @@ EXTERN dict_T vimvardict; /* Dictionary with v: variables */
EXTERN dict_T globvardict; /* Dictionary with g: variables */
EXTERN int did_emsg; /* set by emsg() when the message
is displayed or thrown */
+EXTERN bool called_vim_beep; // set if vim_beep() is called
EXTERN int did_emsg_syntax; /* did_emsg set because of a
syntax error */
EXTERN int called_emsg; /* always set by emsg() */
@@ -754,7 +755,6 @@ EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */
EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
EXTERN int need_highlight_changed INIT(= true);
-EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use
EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to.
@@ -788,8 +788,6 @@ EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd
-// for CursorMoved event
-EXTERN pos_T last_cursormoved INIT(= { 0, 0, 0 });
EXTERN int postponed_split INIT(= 0); /* for CTRL-W CTRL-] command */
EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */
@@ -933,6 +931,7 @@ EXTERN char_u e_interr[] INIT(= N_("Interrupted"));
EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address"));
EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument"));
EXTERN char_u e_invarg2[] INIT(= N_("E475: Invalid argument: %s"));
+EXTERN char_u e_duparg2[] INIT(= N_("E983: Duplicate argument: %s"));
EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s"));
EXTERN char_u e_invrange[] INIT(= N_("E16: Invalid range"));
EXTERN char_u e_invcmd[] INIT(= N_("E476: Invalid command"));
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 4c5fca6d39..3ba02be32d 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -160,14 +160,19 @@ void update_window_hl(win_T *wp, bool invalid)
wp->w_hl_needs_update = false;
// determine window specific background set in 'winhighlight'
+ bool float_win = wp->w_floating && !wp->w_float_config.external;
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
wp->w_hl_ids[HLF_INACTIVE], true);
+ } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] > 0) {
+ wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
+ wp->w_hl_ids[HLF_NFLOAT], true);
} else if (wp->w_hl_id_normal > 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, true);
} else {
- wp->w_hl_attr_normal = 0;
+ wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
+
if (wp != curwin) {
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
wp->w_hl_attr_normal);
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 1da33bfea5..746d2c2dfc 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -90,6 +90,7 @@ typedef enum {
, HLF_0 // Whitespace
, HLF_INACTIVE // NormalNC: Normal text in non-current windows
, HLF_MSGSEP // message separator line
+ , HLF_NFLOAT // Floating window
, HLF_COUNT // MUST be the last one
} hlf_T;
@@ -142,6 +143,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_0] = "Whitespace",
[HLF_INACTIVE] = "NormalNC",
[HLF_MSGSEP] = "MsgSeparator",
+ [HLF_NFLOAT] = "NormalFloat",
});
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 625d6baa17..8b6fd6c705 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -16,11 +16,10 @@
#include <inttypes.h>
#include <fcntl.h>
-#include "nvim/vim.h"
+#include "nvim/buffer.h"
#include "nvim/ascii.h"
#include "nvim/if_cscope.h"
#include "nvim/charset.h"
-#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/message.h"
#include "nvim/memory.h"
@@ -29,7 +28,6 @@
#include "nvim/quickfix.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
-#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/event/stream.h"
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index ade5487ec8..9145813525 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -163,6 +163,7 @@ static const struct key_name_entry {
{ K_DEL, "Del" },
{ K_DEL, "Delete" }, // Alternative name
{ K_KDEL, "kDel" },
+ { K_KDEL, "KPPeriod" }, // libtermkey name
{ K_UP, "Up" },
{ K_DOWN, "Down" },
{ K_LEFT, "Left" },
@@ -171,6 +172,14 @@ static const struct key_name_entry {
{ K_XDOWN, "xDown" },
{ K_XLEFT, "xLeft" },
{ K_XRIGHT, "xRight" },
+ { K_KUP, "kUp" },
+ { K_KUP, "KP8" },
+ { K_KDOWN, "kDown" },
+ { K_KDOWN, "KP2" },
+ { K_KLEFT, "kLeft" },
+ { K_KLEFT, "KP4" },
+ { K_KRIGHT, "kRight" },
+ { K_KRIGHT, "KP6" },
{ K_F1, "F1" },
{ K_F2, "F2" },
@@ -223,25 +232,41 @@ static const struct key_name_entry {
{ K_INS, "Insert" },
{ K_INS, "Ins" }, // Alternative name
{ K_KINS, "kInsert" },
+ { K_KINS, "KP0" },
{ K_HOME, "Home" },
{ K_KHOME, "kHome" },
+ { K_KHOME, "KP7" },
{ K_XHOME, "xHome" },
{ K_ZHOME, "zHome" },
{ K_END, "End" },
{ K_KEND, "kEnd" },
+ { K_KEND, "KP1" },
{ K_XEND, "xEnd" },
{ K_ZEND, "zEnd" },
{ K_PAGEUP, "PageUp" },
{ K_PAGEDOWN, "PageDown" },
{ K_KPAGEUP, "kPageUp" },
+ { K_KPAGEUP, "KP9" },
{ K_KPAGEDOWN, "kPageDown" },
+ { K_KPAGEDOWN, "KP3" },
+ { K_KORIGIN, "kOrigin" },
+ { K_KORIGIN, "KP5" },
{ K_KPLUS, "kPlus" },
+ { K_KPLUS, "KPPlus" },
{ K_KMINUS, "kMinus" },
+ { K_KMINUS, "KPMinus" },
{ K_KDIVIDE, "kDivide" },
+ { K_KDIVIDE, "KPDiv" },
{ K_KMULTIPLY, "kMultiply" },
+ { K_KMULTIPLY, "KPMult" },
{ K_KENTER, "kEnter" },
+ { K_KENTER, "KPEnter" },
{ K_KPOINT, "kPoint" },
+ { K_KCOMMA, "kComma" },
+ { K_KCOMMA, "KPComma" },
+ { K_KEQUAL, "kEqual" },
+ { K_KEQUAL, "KPEquals" },
{ K_K0, "k0" },
{ K_K1, "k1" },
@@ -663,7 +688,7 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
*modp = modifiers;
*srcp = end_of_name;
return key;
- }
+ } // else { ELOG("unknown key: '%s'", src); }
}
}
return 0;
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index baf8963aa8..7f0483826d 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -256,9 +256,13 @@ enum key_extra {
#define K_ZERO TERMCAP2KEY(KS_ZERO, KE_FILLER)
#define K_UP TERMCAP2KEY('k', 'u')
+#define K_KUP TERMCAP2KEY('K', 'u') // keypad up
#define K_DOWN TERMCAP2KEY('k', 'd')
+#define K_KDOWN TERMCAP2KEY('K', 'd') // keypad down
#define K_LEFT TERMCAP2KEY('k', 'l')
+#define K_KLEFT TERMCAP2KEY('K', 'l') // keypad left
#define K_RIGHT TERMCAP2KEY('k', 'r')
+#define K_KRIGHT TERMCAP2KEY('K', 'r') // keypad right
#define K_S_UP TERMCAP2KEY(KS_EXTRA, KE_S_UP)
#define K_S_DOWN TERMCAP2KEY(KS_EXTRA, KE_S_DOWN)
#define K_S_LEFT TERMCAP2KEY('#', '4')
@@ -284,7 +288,7 @@ enum key_extra {
#define K_XLEFT TERMCAP2KEY(KS_EXTRA, KE_XLEFT)
#define K_XRIGHT TERMCAP2KEY(KS_EXTRA, KE_XRIGHT)
-#define K_F1 TERMCAP2KEY('k', '1') /* function keys */
+#define K_F1 TERMCAP2KEY('k', '1') // function keys
#define K_F2 TERMCAP2KEY('k', '2')
#define K_F3 TERMCAP2KEY('k', '3')
#define K_F4 TERMCAP2KEY('k', '4')
@@ -331,7 +335,7 @@ enum key_extra {
#define K_S_XF3 TERMCAP2KEY(KS_EXTRA, KE_S_XF3)
#define K_S_XF4 TERMCAP2KEY(KS_EXTRA, KE_S_XF4)
-#define K_S_F1 TERMCAP2KEY(KS_EXTRA, KE_S_F1) /* shifted func. keys */
+#define K_S_F1 TERMCAP2KEY(KS_EXTRA, KE_S_F1) // shifted func. keys
#define K_S_F2 TERMCAP2KEY(KS_EXTRA, KE_S_F2)
#define K_S_F3 TERMCAP2KEY(KS_EXTRA, KE_S_F3)
#define K_S_F4 TERMCAP2KEY(KS_EXTRA, KE_S_F4)
@@ -356,35 +360,39 @@ enum key_extra {
#define K_DEL TERMCAP2KEY('k', 'D')
#define K_KDEL TERMCAP2KEY(KS_EXTRA, KE_KDEL)
#define K_HOME TERMCAP2KEY('k', 'h')
-#define K_KHOME TERMCAP2KEY('K', '1') /* keypad home (upper left) */
+#define K_KHOME TERMCAP2KEY('K', '1') // keypad home (upper left)
#define K_XHOME TERMCAP2KEY(KS_EXTRA, KE_XHOME)
#define K_ZHOME TERMCAP2KEY(KS_EXTRA, KE_ZHOME)
#define K_END TERMCAP2KEY('@', '7')
-#define K_KEND TERMCAP2KEY('K', '4') /* keypad end (lower left) */
+#define K_KEND TERMCAP2KEY('K', '4') // keypad end (lower left)
#define K_XEND TERMCAP2KEY(KS_EXTRA, KE_XEND)
#define K_ZEND TERMCAP2KEY(KS_EXTRA, KE_ZEND)
#define K_PAGEUP TERMCAP2KEY('k', 'P')
#define K_PAGEDOWN TERMCAP2KEY('k', 'N')
-#define K_KPAGEUP TERMCAP2KEY('K', '3') /* keypad pageup (upper R.) */
-#define K_KPAGEDOWN TERMCAP2KEY('K', '5') /* keypad pagedown (lower R.) */
-
-#define K_KPLUS TERMCAP2KEY('K', '6') /* keypad plus */
-#define K_KMINUS TERMCAP2KEY('K', '7') /* keypad minus */
-#define K_KDIVIDE TERMCAP2KEY('K', '8') /* keypad / */
-#define K_KMULTIPLY TERMCAP2KEY('K', '9') /* keypad * */
-#define K_KENTER TERMCAP2KEY('K', 'A') /* keypad Enter */
-#define K_KPOINT TERMCAP2KEY('K', 'B') /* keypad . or ,*/
-
-#define K_K0 TERMCAP2KEY('K', 'C') /* keypad 0 */
-#define K_K1 TERMCAP2KEY('K', 'D') /* keypad 1 */
-#define K_K2 TERMCAP2KEY('K', 'E') /* keypad 2 */
-#define K_K3 TERMCAP2KEY('K', 'F') /* keypad 3 */
-#define K_K4 TERMCAP2KEY('K', 'G') /* keypad 4 */
-#define K_K5 TERMCAP2KEY('K', 'H') /* keypad 5 */
-#define K_K6 TERMCAP2KEY('K', 'I') /* keypad 6 */
-#define K_K7 TERMCAP2KEY('K', 'J') /* keypad 7 */
-#define K_K8 TERMCAP2KEY('K', 'K') /* keypad 8 */
-#define K_K9 TERMCAP2KEY('K', 'L') /* keypad 9 */
+#define K_KPAGEUP TERMCAP2KEY('K', '3') // keypad pageup (upper R.)
+#define K_KPAGEDOWN TERMCAP2KEY('K', '5') // keypad pagedown (lower R.)
+#define K_KORIGIN TERMCAP2KEY('K', '2') // keypad center
+
+#define K_KPLUS TERMCAP2KEY('K', '6') // keypad plus
+#define K_KMINUS TERMCAP2KEY('K', '7') // keypad minus
+#define K_KDIVIDE TERMCAP2KEY('K', '8') // keypad /
+#define K_KMULTIPLY TERMCAP2KEY('K', '9') // keypad *
+#define K_KENTER TERMCAP2KEY('K', 'A') // keypad Enter
+#define K_KPOINT TERMCAP2KEY('K', 'B') // keypad . or ,
+
+#define K_K0 TERMCAP2KEY('K', 'C') // keypad 0
+#define K_K1 TERMCAP2KEY('K', 'D') // keypad 1
+#define K_K2 TERMCAP2KEY('K', 'E') // keypad 2
+#define K_K3 TERMCAP2KEY('K', 'F') // keypad 3
+#define K_K4 TERMCAP2KEY('K', 'G') // keypad 4
+#define K_K5 TERMCAP2KEY('K', 'H') // keypad 5
+#define K_K6 TERMCAP2KEY('K', 'I') // keypad 6
+#define K_K7 TERMCAP2KEY('K', 'J') // keypad 7
+#define K_K8 TERMCAP2KEY('K', 'K') // keypad 8
+#define K_K9 TERMCAP2KEY('K', 'L') // keypad 9
+
+#define K_KCOMMA TERMCAP2KEY('K', 'M') // keypad comma
+#define K_KEQUAL TERMCAP2KEY('K', 'N') // keypad equal
#define K_MOUSE TERMCAP2KEY(KS_MOUSE, KE_FILLER)
#define K_MENU TERMCAP2KEY(KS_MENU, KE_FILLER)
diff --git a/src/nvim/lib/kbtree.h b/src/nvim/lib/kbtree.h
index e2688064a8..704aa26010 100644
--- a/src/nvim/lib/kbtree.h
+++ b/src/nvim/lib/kbtree.h
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
+#include <assert.h>
#include "nvim/memory.h"
@@ -317,7 +318,7 @@
#define __KB_ITR(name, key_t, kbnode_t) \
static inline void kb_itr_first_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
{ \
- itr->p = 0; \
+ itr->p = NULL; \
if (b->n_keys == 0) return; \
itr->p = itr->stack; \
itr->p->x = b->root; itr->p->i = 0; \
@@ -329,30 +330,37 @@
} \
static inline int kb_itr_next_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
{ \
- if (itr->p < itr->stack) return 0; \
+ if (itr->p == NULL) return 0; \
for (;;) { \
++itr->p->i; \
+ assert(itr->p->i <= 21); \
while (itr->p->x && itr->p->i <= itr->p->x->n) { \
itr->p[1].i = 0; \
itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \
++itr->p; \
} \
+ if (itr->p == itr->stack) { \
+ itr->p = NULL; \
+ return 0; \
+ } \
--itr->p; \
- if (itr->p < itr->stack) return 0; \
if (itr->p->x && itr->p->i < itr->p->x->n) return 1; \
} \
} \
static inline int kb_itr_prev_##name(kbtree_##name##_t *b, kbitr_##name##_t *itr) \
{ \
- if (itr->p < itr->stack) return 0; \
+ if (itr->p == NULL) return 0; \
for (;;) { \
while (itr->p->x && itr->p->i >= 0) { \
itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[itr->p->i] : 0; \
itr->p[1].i = itr->p[1].x ? itr->p[1].x->n : -1; \
++itr->p; \
} \
+ if (itr->p == itr->stack) { \
+ itr->p = NULL; \
+ return 0; \
+ } \
--itr->p; \
- if (itr->p < itr->stack) return 0; \
--itr->p->i; \
if (itr->p->x && itr->p->i >= 0) return 1; \
} \
@@ -371,9 +379,11 @@
itr->p->i = i; \
if (i >= 0 && r == 0) return 1; \
++itr->p->i; \
+ assert(itr->p->i <= 21); \
itr->p[1].x = itr->p->x->is_internal? __KB_PTR(b, itr->p->x)[i + 1] : 0; \
++itr->p; \
} \
+ itr->p->i = 0; \
return 0; \
} \
static inline int kb_itr_get_##name(kbtree_##name##_t *b, key_t k, kbitr_##name##_t *itr) \
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 8066b6e828..a2f83d4d09 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -1,6 +1,13 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+//
+// Log module
+//
+// How Linux printk() handles recursion, buffering, etc:
+// https://lwn.net/Articles/780556/
+//
+
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
@@ -99,9 +106,14 @@ void log_unlock(void)
uv_mutex_unlock(&mutex);
}
-/// @param context description of a shared context or subsystem
-/// @param func_name function name, or NULL
-/// @param line_num source line number, or -1
+/// Logs a message to $NVIM_LOG_FILE.
+///
+/// @param log_level Log level (see log.h)
+/// @param context Description of a shared context or subsystem
+/// @param func_name Function name, or NULL
+/// @param line_num Source line number, or -1
+/// @param eol Append linefeed "\n"
+/// @param fmt printf-style format string
bool logmsg(int log_level, const char *context, const char *func_name,
int line_num, bool eol, const char *fmt, ...)
FUNC_ATTR_UNUSED FUNC_ATTR_PRINTF(6, 7)
@@ -163,7 +175,8 @@ end:
FILE *open_log_file(void)
{
static bool opening_log_file = false;
- // check if it's a recursive call
+ // Disallow recursion. (This only matters for log_path_init; for logmsg and
+ // friends we use a mutex: log_lock).
if (opening_log_file) {
do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Cannot LOG() recursively.");
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 93069893cf..72b97736fc 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -24,6 +24,10 @@
#include "nvim/undo.h"
#include "nvim/ascii.h"
+#ifdef WIN32
+#include "nvim/os/os.h"
+#endif
+
#include "nvim/lua/executor.h"
#include "nvim/lua/converter.h"
@@ -118,6 +122,14 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, -2, "debug");
lua_pop(lstate, 1);
+#ifdef WIN32
+ // os.getenv
+ lua_getglobal(lstate, "os");
+ lua_pushcfunction(lstate, &nlua_getenv);
+ lua_setfield(lstate, -2, "getenv");
+ lua_pop(lstate, 1);
+#endif
+
// vim
if (luaL_dostring(lstate, (char *)&vim_module[0])) {
nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
@@ -297,7 +309,7 @@ nlua_print_error:
return 0;
}
-/// debug.debug implementation: interaction with user while debugging
+/// debug.debug: interaction with user while debugging.
///
/// @param lstate Lua interpreter state.
int nlua_debug(lua_State *lstate)
@@ -337,6 +349,19 @@ int nlua_debug(lua_State *lstate)
return 0;
}
+#ifdef WIN32
+/// os.getenv: override os.getenv to maintain coherency. #9681
+///
+/// uv_os_setenv uses SetEnvironmentVariableW which does not update _environ.
+///
+/// @param lstate Lua interpreter state.
+static int nlua_getenv(lua_State *lstate)
+{
+ lua_pushstring(lstate, os_getenv(luaL_checkstring(lstate, 1)));
+ return 1;
+}
+#endif
+
/// Evaluate lua string
///
/// Used for luaeval().
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index b0d0bfc74b..9cd8e232d5 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -1,3 +1,39 @@
+-- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib)
+--
+-- Lua code lives in one of three places:
+-- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the
+-- `inspect` and `lpeg` modules.
+-- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests.
+-- 3. src/nvim/lua/: Compiled-into Nvim itself.
+--
+-- Guideline: "If in doubt, put it in the runtime".
+--
+-- Most functions should live directly on `vim.`, not sub-modules. The only
+-- "forbidden" names are those claimed by legacy `if_lua`:
+-- $ vim
+-- :lua for k,v in pairs(vim) do print(k) end
+-- buffer
+-- open
+-- window
+-- lastline
+-- firstline
+-- type
+-- line
+-- eval
+-- dict
+-- beep
+-- list
+-- command
+--
+-- Reference (#6580):
+-- - https://github.com/luafun/luafun
+-- - https://github.com/rxi/lume
+-- - http://leafo.net/lapis/reference/utilities.html
+-- - https://github.com/torch/paths
+-- - https://github.com/bakpakin/Fennel (pretty print, repl)
+-- - https://github.com/howl-editor/howl/tree/master/lib/howl/util
+
+
-- Internal-only until comments in #8107 are addressed.
-- Returns:
-- {errcode}, {output}
@@ -118,79 +154,24 @@ local function _update_package_paths()
last_nvim_paths = cur_nvim_paths
end
-local function gsplit(s, sep, plain)
- assert(type(s) == "string")
- assert(type(sep) == "string")
- assert(type(plain) == "boolean" or type(plain) == "nil")
-
- local start = 1
- local done = false
-
- local function pass(i, j, ...)
- if i then
- assert(j+1 > start, "Infinite loop detected")
- local seg = s:sub(start, i - 1)
- start = j + 1
- return seg, ...
- else
- done = true
- return s:sub(start)
- end
- end
-
- return function()
- if done then
- return
- end
- if sep == '' then
- if start == #s then
- done = true
- end
- return pass(start+1, start)
- end
- return pass(s:find(sep, start, plain))
- end
-end
-
-local function split(s,sep,plain)
- local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end
- return t
-end
-
+--- Trim whitespace (Lua pattern "%%s") from both sides of a string.
+---
+--@see https://www.lua.org/pil/20.2.html
+--@param s String to trim
+--@returns String with whitespace removed from its beginning and end
local function trim(s)
- assert(type(s) == "string", "Only strings can be trimmed")
- local result = s:gsub("^%s+", ""):gsub("%s+$", "")
- return result
-end
-
-local deepcopy
-
-local function id(v)
- return v
-end
-
-local deepcopy_funcs = {
- table = function(orig)
- local copy = {}
- for k, v in pairs(orig) do
- copy[deepcopy(k)] = deepcopy(v)
- end
- return copy
- end,
- number = id,
- string = id,
- ['nil'] = id,
- boolean = id,
-}
-
-deepcopy = function(orig)
- return deepcopy_funcs[type(orig)](orig)
+ assert(type(s) == 'string', 'Only strings can be trimmed')
+ return s:match('^%s*(.*%S)') or ''
end
-local function __index(table, key)
- if key == "inspect" then
- table.inspect = require("vim.inspect")
- return table.inspect
+local function __index(t, key)
+ if key == 'inspect' then
+ t.inspect = require('vim.inspect')
+ return t.inspect
+ elseif require('vim.shared')[key] ~= nil then
+ -- Expose all `vim.shared` functions on the `vim` module.
+ t[key] = require('vim.shared')[key]
+ return t[key]
end
end
@@ -200,9 +181,6 @@ local module = {
_os_proc_info = _os_proc_info,
_system = _system,
trim = trim,
- split = split,
- gsplit = gsplit,
- deepcopy = deepcopy,
}
setmetatable(module, {
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 5dffca95a2..4e1c7dff57 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -114,12 +114,12 @@ typedef struct {
char *listen_addr; // --listen {address}
} mparm_T;
-/* Values for edit_type. */
-#define EDIT_NONE 0 /* no edit type yet */
-#define EDIT_FILE 1 /* file name argument[s] given, use argument list */
-#define EDIT_STDIN 2 /* read file from stdin */
-#define EDIT_TAG 3 /* tag name argument given, use tagname */
-#define EDIT_QF 4 /* start in quickfix mode */
+// Values for edit_type.
+#define EDIT_NONE 0 // no edit type yet
+#define EDIT_FILE 1 // file name argument[s] given, use argument list
+#define EDIT_STDIN 2 // read file from stdin
+#define EDIT_TAG 3 // tag name argument given, use tagname
+#define EDIT_QF 4 // start in quickfix mode
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "main.c.generated.h"
@@ -547,10 +547,11 @@ int main(int argc, char **argv)
if (params.n_commands > 0)
exe_commands(&params);
+ starting = 0;
+
RedrawingDisabled = 0;
redraw_all_later(NOT_VALID);
- no_wait_return = FALSE;
- starting = 0;
+ no_wait_return = false;
// 'autochdir' has been postponed.
do_autochdir();
@@ -806,6 +807,7 @@ static void command_line_scan(mparm_T *parmp)
if (exmode_active) {
// "nvim -e -" silent mode
silent_mode = true;
+ parmp->no_swap_file = true;
} else {
if (parmp->edit_type != EDIT_NONE
&& parmp->edit_type != EDIT_FILE
@@ -869,6 +871,9 @@ static void command_line_scan(mparm_T *parmp)
} else if (STRNICMP(argv[0] + argv_idx, "startuptime", 11) == 0) {
want_argument = true;
argv_idx += 11;
+ } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) {
+ parmp->use_vimrc = "NONE";
+ set_option_value("shadafile", 0L, "NONE", 0);
} else {
if (argv[0][argv_idx])
mainerr(err_opt_unknown, argv[0]);
@@ -989,6 +994,7 @@ static void command_line_scan(mparm_T *parmp)
case 's': {
if (exmode_active) { // "-es" silent (batch) Ex-mode
silent_mode = true;
+ parmp->no_swap_file = true;
} else { // "-s {scriptin}" read from script file
want_argument = true;
}
@@ -1126,7 +1132,7 @@ static void command_line_scan(mparm_T *parmp)
}
case 'i': { // "-i {shada}" use for shada
- used_shada_file = argv[0];
+ set_option_value("shadafile", 0L, argv[0], 0);
break;
}
@@ -1514,10 +1520,11 @@ static void create_windows(mparm_T *parmp)
dorewind = FALSE;
curbuf = curwin->w_buffer;
if (curbuf->b_ml.ml_mfp == NULL) {
- /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */
- if (p_fdls >= 0)
+ // Set 'foldlevel' to 'foldlevelstart' if it's not negative..
+ if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;
- /* When getting the ATTENTION prompt here, use a dialog */
+ }
+ // When getting the ATTENTION prompt here, use a dialog.
swap_exists_action = SEA_DIALOG;
set_buflisted(TRUE);
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 9b6f57a56f..90da27cf9f 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -1,12 +1,12 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-///
-/// map.c: khash.h wrapper
-///
-/// NOTE: Callers must manage memory (allocate) for keys and values.
-/// khash.h does not make its own copy of the key or value.
-///
+//
+// map.c: khash.h wrapper
+//
+// NOTE: Callers must manage memory (allocate) for keys and values.
+// khash.h does not make its own copy of the key or value.
+//
#include <stdlib.h>
#include <stdbool.h>
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 602648c27c..5c9367ab01 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -214,7 +214,7 @@ pos_T *movemark(int count)
pos_T *pos;
xfmark_T *jmp;
- cleanup_jumplist();
+ cleanup_jumplist(curwin, true);
if (curwin->w_jumplistlen == 0) /* nothing to jump to */
return (pos_T *)NULL;
@@ -781,13 +781,11 @@ void ex_jumps(exarg_T *eap)
int i;
char_u *name;
- cleanup_jumplist();
- /* Highlight title */
+ cleanup_jumplist(curwin, true);
+ // Highlight title
MSG_PUTS_TITLE(_("\n jump line col file/text"));
for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i) {
if (curwin->w_jumplist[i].fmark.mark.lnum != 0) {
- if (curwin->w_jumplist[i].fmark.fnum == 0)
- fname2fnum(&curwin->w_jumplist[i]);
name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
if (name == NULL) /* file name not available */
continue;
@@ -1069,19 +1067,24 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
{ \
posp->lnum += lnum_amount; \
assert(col_amount > INT_MIN && col_amount <= INT_MAX); \
- if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
+ if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) { \
posp->col = 0; \
- else \
+ } else if (posp->col < spaces_removed) { \
+ posp->col = (int)col_amount + spaces_removed; \
+ } else { \
posp->col += (colnr_T)col_amount; \
+ } \
} \
}
-/*
- * Adjust marks in line "lnum" at column "mincol" and further: add
- * "lnum_amount" to the line number and add "col_amount" to the column
- * position.
- */
-void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount)
+// Adjust marks in line "lnum" at column "mincol" and further: add
+// "lnum_amount" to the line number and add "col_amount" to the column
+// position.
+// "spaces_removed" is the number of spaces that were removed, matters when the
+// cursor is inside them.
+void mark_col_adjust(
+ linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount,
+ int spaces_removed)
{
int i;
int fnum = curbuf->b_fnum;
@@ -1151,41 +1154,69 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a
}
}
-/*
- * When deleting lines, this may create duplicate marks in the
- * jumplist. They will be removed here for the current window.
- */
-void cleanup_jumplist(void)
+// When deleting lines, this may create duplicate marks in the
+// jumplist. They will be removed here for the specified window.
+// When "loadfiles" is true first ensure entries have the "fnum" field set
+// (this may be a bit slow).
+void cleanup_jumplist(win_T *wp, bool loadfiles)
{
int i;
- int from, to;
- to = 0;
- for (from = 0; from < curwin->w_jumplistlen; ++from) {
- if (curwin->w_jumplistidx == from)
- curwin->w_jumplistidx = to;
- for (i = from + 1; i < curwin->w_jumplistlen; ++i)
- if (curwin->w_jumplist[i].fmark.fnum
- == curwin->w_jumplist[from].fmark.fnum
- && curwin->w_jumplist[from].fmark.fnum != 0
- && curwin->w_jumplist[i].fmark.mark.lnum
- == curwin->w_jumplist[from].fmark.mark.lnum)
+ if (loadfiles) {
+ // If specified, load all the files from the jump list. This is
+ // needed to properly clean up duplicate entries, but will take some
+ // time.
+ for (i = 0; i < wp->w_jumplistlen; i++) {
+ if ((wp->w_jumplist[i].fmark.fnum == 0)
+ && (wp->w_jumplist[i].fmark.mark.lnum != 0)) {
+ fname2fnum(&wp->w_jumplist[i]);
+ }
+ }
+ }
+
+ int to = 0;
+ for (int from = 0; from < wp->w_jumplistlen; from++) {
+ if (wp->w_jumplistidx == from) {
+ wp->w_jumplistidx = to;
+ }
+ for (i = from + 1; i < wp->w_jumplistlen; i++) {
+ if (wp->w_jumplist[i].fmark.fnum
+ == wp->w_jumplist[from].fmark.fnum
+ && wp->w_jumplist[from].fmark.fnum != 0
+ && wp->w_jumplist[i].fmark.mark.lnum
+ == wp->w_jumplist[from].fmark.mark.lnum) {
break;
- if (i >= curwin->w_jumplistlen) { // no duplicate
+ }
+ }
+ if (i >= wp->w_jumplistlen) { // no duplicate
if (to != from) {
- // Not using curwin->w_jumplist[to++] = curwin->w_jumplist[from] because
+ // Not using wp->w_jumplist[to++] = wp->w_jumplist[from] because
// this way valgrind complains about overlapping source and destination
// in memcpy() call. (clang-3.6.0, debug build with -DEXITFREE).
- curwin->w_jumplist[to] = curwin->w_jumplist[from];
+ wp->w_jumplist[to] = wp->w_jumplist[from];
}
to++;
} else {
- xfree(curwin->w_jumplist[from].fname);
+ xfree(wp->w_jumplist[from].fname);
+ }
+ }
+ if (wp->w_jumplistidx == wp->w_jumplistlen) {
+ wp->w_jumplistidx = to;
+ }
+ wp->w_jumplistlen = to;
+
+ // When pointer is below last jump, remove the jump if it matches the current
+ // line. This avoids useless/phantom jumps. #9805
+ if (wp->w_jumplistlen
+ && wp->w_jumplistidx == wp->w_jumplistlen) {
+ const xfmark_T *fm_last = &wp->w_jumplist[wp->w_jumplistlen - 1];
+ if (fm_last->fmark.fnum == curbuf->b_fnum
+ && fm_last->fmark.mark.lnum == wp->w_cursor.lnum) {
+ xfree(fm_last->fname);
+ wp->w_jumplistlen--;
+ wp->w_jumplistidx--;
}
}
- if (curwin->w_jumplistidx == curwin->w_jumplistlen)
- curwin->w_jumplistidx = to;
- curwin->w_jumplistlen = to;
}
/*
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 5ed2b4c564..8cc91146cc 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -555,6 +555,24 @@ size_t mb_string2cells(const char_u *str)
return clen;
}
+/// Get the number of cells occupied by string `str` with maximum length `size`
+///
+/// @param str The source string, may not be NULL, must be a NUL-terminated
+/// string.
+/// @param size maximum length of string. It will terminate on earlier NUL.
+/// @return The number of cells occupied by string `str`
+size_t mb_string2cells_len(const char_u *str, size_t size)
+{
+ size_t clen = 0;
+
+ for (const char_u *p = str; *p != NUL && p < str+size;
+ p += utf_ptr2len_len(p, size+(p-str))) {
+ clen += utf_ptr2cells(p);
+ }
+
+ return clen;
+}
+
/// Convert a UTF-8 byte sequence to a wide character
///
/// If the sequence is illegal or truncated by a NUL then the first byte is
@@ -1045,75 +1063,78 @@ int utf_class_tab(const int c, const uint64_t *const chartab)
unsigned int first;
unsigned int last;
unsigned int class;
- } classes[] =
- {
- {0x037e, 0x037e, 1}, /* Greek question mark */
- {0x0387, 0x0387, 1}, /* Greek ano teleia */
- {0x055a, 0x055f, 1}, /* Armenian punctuation */
- {0x0589, 0x0589, 1}, /* Armenian full stop */
- {0x05be, 0x05be, 1},
- {0x05c0, 0x05c0, 1},
- {0x05c3, 0x05c3, 1},
- {0x05f3, 0x05f4, 1},
- {0x060c, 0x060c, 1},
- {0x061b, 0x061b, 1},
- {0x061f, 0x061f, 1},
- {0x066a, 0x066d, 1},
- {0x06d4, 0x06d4, 1},
- {0x0700, 0x070d, 1}, /* Syriac punctuation */
- {0x0964, 0x0965, 1},
- {0x0970, 0x0970, 1},
- {0x0df4, 0x0df4, 1},
- {0x0e4f, 0x0e4f, 1},
- {0x0e5a, 0x0e5b, 1},
- {0x0f04, 0x0f12, 1},
- {0x0f3a, 0x0f3d, 1},
- {0x0f85, 0x0f85, 1},
- {0x104a, 0x104f, 1}, /* Myanmar punctuation */
- {0x10fb, 0x10fb, 1}, /* Georgian punctuation */
- {0x1361, 0x1368, 1}, /* Ethiopic punctuation */
- {0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
- {0x1680, 0x1680, 0},
- {0x169b, 0x169c, 1},
- {0x16eb, 0x16ed, 1},
- {0x1735, 0x1736, 1},
- {0x17d4, 0x17dc, 1}, /* Khmer punctuation */
- {0x1800, 0x180a, 1}, /* Mongolian punctuation */
- {0x2000, 0x200b, 0}, /* spaces */
- {0x200c, 0x2027, 1}, /* punctuation and symbols */
- {0x2028, 0x2029, 0},
- {0x202a, 0x202e, 1}, /* punctuation and symbols */
- {0x202f, 0x202f, 0},
- {0x2030, 0x205e, 1}, /* punctuation and symbols */
- {0x205f, 0x205f, 0},
- {0x2060, 0x27ff, 1}, /* punctuation and symbols */
- {0x2070, 0x207f, 0x2070}, /* superscript */
- {0x2080, 0x2094, 0x2080}, /* subscript */
- {0x20a0, 0x27ff, 1}, /* all kinds of symbols */
- {0x2800, 0x28ff, 0x2800}, /* braille */
- {0x2900, 0x2998, 1}, /* arrows, brackets, etc. */
- {0x29d8, 0x29db, 1},
- {0x29fc, 0x29fd, 1},
- {0x2e00, 0x2e7f, 1}, /* supplemental punctuation */
- {0x3000, 0x3000, 0}, /* ideographic space */
- {0x3001, 0x3020, 1}, /* ideographic punctuation */
- {0x3030, 0x3030, 1},
- {0x303d, 0x303d, 1},
- {0x3040, 0x309f, 0x3040}, /* Hiragana */
- {0x30a0, 0x30ff, 0x30a0}, /* Katakana */
- {0x3300, 0x9fff, 0x4e00}, /* CJK Ideographs */
- {0xac00, 0xd7a3, 0xac00}, /* Hangul Syllables */
- {0xf900, 0xfaff, 0x4e00}, /* CJK Ideographs */
- {0xfd3e, 0xfd3f, 1},
- {0xfe30, 0xfe6b, 1}, /* punctuation forms */
- {0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
- {0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
- {0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
- {0xff5b, 0xff65, 1}, /* half/fullwidth ASCII */
- {0x20000, 0x2a6df, 0x4e00}, /* CJK Ideographs */
- {0x2a700, 0x2b73f, 0x4e00}, /* CJK Ideographs */
- {0x2b740, 0x2b81f, 0x4e00}, /* CJK Ideographs */
- {0x2f800, 0x2fa1f, 0x4e00}, /* CJK Ideographs */
+ } classes[] = {
+ { 0x037e, 0x037e, 1 }, // Greek question mark
+ { 0x0387, 0x0387, 1 }, // Greek ano teleia
+ { 0x055a, 0x055f, 1 }, // Armenian punctuation
+ { 0x0589, 0x0589, 1 }, // Armenian full stop
+ { 0x05be, 0x05be, 1 },
+ { 0x05c0, 0x05c0, 1 },
+ { 0x05c3, 0x05c3, 1 },
+ { 0x05f3, 0x05f4, 1 },
+ { 0x060c, 0x060c, 1 },
+ { 0x061b, 0x061b, 1 },
+ { 0x061f, 0x061f, 1 },
+ { 0x066a, 0x066d, 1 },
+ { 0x06d4, 0x06d4, 1 },
+ { 0x0700, 0x070d, 1 }, // Syriac punctuation
+ { 0x0964, 0x0965, 1 },
+ { 0x0970, 0x0970, 1 },
+ { 0x0df4, 0x0df4, 1 },
+ { 0x0e4f, 0x0e4f, 1 },
+ { 0x0e5a, 0x0e5b, 1 },
+ { 0x0f04, 0x0f12, 1 },
+ { 0x0f3a, 0x0f3d, 1 },
+ { 0x0f85, 0x0f85, 1 },
+ { 0x104a, 0x104f, 1 }, // Myanmar punctuation
+ { 0x10fb, 0x10fb, 1 }, // Georgian punctuation
+ { 0x1361, 0x1368, 1 }, // Ethiopic punctuation
+ { 0x166d, 0x166e, 1 }, // Canadian Syl. punctuation
+ { 0x1680, 0x1680, 0 },
+ { 0x169b, 0x169c, 1 },
+ { 0x16eb, 0x16ed, 1 },
+ { 0x1735, 0x1736, 1 },
+ { 0x17d4, 0x17dc, 1 }, // Khmer punctuation
+ { 0x1800, 0x180a, 1 }, // Mongolian punctuation
+ { 0x2000, 0x200b, 0 }, // spaces
+ { 0x200c, 0x2027, 1 }, // punctuation and symbols
+ { 0x2028, 0x2029, 0 },
+ { 0x202a, 0x202e, 1 }, // punctuation and symbols
+ { 0x202f, 0x202f, 0 },
+ { 0x2030, 0x205e, 1 }, // punctuation and symbols
+ { 0x205f, 0x205f, 0 },
+ { 0x2060, 0x27ff, 1 }, // punctuation and symbols
+ { 0x2070, 0x207f, 0x2070 }, // superscript
+ { 0x2080, 0x2094, 0x2080 }, // subscript
+ { 0x20a0, 0x27ff, 1 }, // all kinds of symbols
+ { 0x2800, 0x28ff, 0x2800 }, // braille
+ { 0x2900, 0x2998, 1 }, // arrows, brackets, etc.
+ { 0x29d8, 0x29db, 1 },
+ { 0x29fc, 0x29fd, 1 },
+ { 0x2e00, 0x2e7f, 1 }, // supplemental punctuation
+ { 0x3000, 0x3000, 0 }, // ideographic space
+ { 0x3001, 0x3020, 1 }, // ideographic punctuation
+ { 0x3030, 0x3030, 1 },
+ { 0x303d, 0x303d, 1 },
+ { 0x3040, 0x309f, 0x3040 }, // Hiragana
+ { 0x30a0, 0x30ff, 0x30a0 }, // Katakana
+ { 0x3300, 0x9fff, 0x4e00 }, // CJK Ideographs
+ { 0xac00, 0xd7a3, 0xac00 }, // Hangul Syllables
+ { 0xf900, 0xfaff, 0x4e00 }, // CJK Ideographs
+ { 0xfd3e, 0xfd3f, 1 },
+ { 0xfe30, 0xfe6b, 1 }, // punctuation forms
+ { 0xff00, 0xff0f, 1 }, // half/fullwidth ASCII
+ { 0xff1a, 0xff20, 1 }, // half/fullwidth ASCII
+ { 0xff3b, 0xff40, 1 }, // half/fullwidth ASCII
+ { 0xff5b, 0xff65, 1 }, // half/fullwidth ASCII
+ { 0x1d000, 0x1d24f, 1 }, // Musical notation
+ { 0x1d400, 0x1d7ff, 1 }, // Mathematical Alphanumeric Symbols
+ { 0x1f000, 0x1f2ff, 1 }, // Game pieces; enclosed characters
+ { 0x1f300, 0x1f9ff, 1 }, // Many symbol blocks
+ { 0x20000, 0x2a6df, 0x4e00 }, // CJK Ideographs
+ { 0x2a700, 0x2b73f, 0x4e00 }, // CJK Ideographs
+ { 0x2b740, 0x2b81f, 0x4e00 }, // CJK Ideographs
+ { 0x2f800, 0x2fa1f, 0x4e00 }, // CJK Ideographs
};
int bot = 0;
int top = ARRAY_SIZE(classes) - 1;
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 662eda3c7c..a071314453 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -71,6 +71,7 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/os/process.h"
#include "nvim/os/input.h"
#ifndef UNIX /* it's in os/unix_defs.h for Unix */
@@ -348,7 +349,7 @@ int ml_open(buf_T *buf)
/*
* Allocate first data block and create an empty line 1.
*/
- hp = ml_new_data(mfp, FALSE, 1);
+ hp = ml_new_data(mfp, false, 1);
if (hp->bh_bnum != 2) {
IEMSG(_("E298: Didn't get block nr 2?"));
goto error;
@@ -763,7 +764,7 @@ void ml_recover(void)
long error;
int cannot_open;
linenr_T line_count;
- int has_error;
+ bool has_error;
int idx;
int top;
int txt_start;
@@ -983,8 +984,9 @@ void ml_recover(void)
* Now that we are sure that the file is going to be recovered, clear the
* contents of the current buffer.
*/
- while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
- ml_delete((linenr_T)1, FALSE);
+ while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
+ ml_delete((linenr_T)1, false);
+ }
/*
* Try reading the original file to obtain the values of 'fileformat',
@@ -1033,8 +1035,8 @@ void ml_recover(void)
}
++error;
ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
- (colnr_T)0, TRUE);
- } else { /* there is a block */
+ (colnr_T)0, true);
+ } else { // there is a block
pp = hp->bh_data;
if (pp->pb_id == PTR_ID) { /* it is a pointer block */
/* check line count when using pointer block first time */
@@ -1044,15 +1046,15 @@ void ml_recover(void)
if (line_count != 0) {
++error;
ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
}
}
if (pp->pb_count == 0) {
ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
- (colnr_T)0, TRUE);
- ++error;
- } else if (idx < (int)pp->pb_count) { /* go a block deeper */
+ (colnr_T)0, true);
+ error++;
+ } else if (idx < (int)pp->pb_count) { // go a block deeper
if (pp->pb_pointer[idx].pe_bnum < 0) {
/*
* Data block with negative block number.
@@ -1072,7 +1074,7 @@ void ml_recover(void)
if (cannot_open) {
++error;
ml_append(lnum++, (char_u *)_("???LINES MISSING"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
}
++idx; /* get same block again for next index */
continue;
@@ -1102,23 +1104,21 @@ void ml_recover(void)
}
++error;
ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
} else {
- /*
- * it is a data block
- * Append all the lines in this block
- */
- has_error = FALSE;
- /*
- * check length of block
- * if wrong, use length in pointer block
- */
+ // it is a data block
+ // Append all the lines in this block
+ has_error = false;
+ // check length of block
+ // if wrong, use length in pointer block
if (page_count * mfp->mf_page_size != dp->db_txt_end) {
- ml_append(lnum++,
- (char_u *)_("??? from here until ???END lines may be messed up"),
- (colnr_T)0, TRUE);
- ++error;
- has_error = TRUE;
+ ml_append(
+ lnum++,
+ (char_u *)_("??? from here until ???END lines"
+ " may be messed up"),
+ (colnr_T)0, true);
+ error++;
+ has_error = true;
dp->db_txt_end = page_count * mfp->mf_page_size;
}
@@ -1130,12 +1130,13 @@ void ml_recover(void)
* if wrong, use count in data block
*/
if (line_count != dp->db_line_count) {
- ml_append(lnum++,
- (char_u *)_(
- "??? from here until ???END lines may have been inserted/deleted"),
- (colnr_T)0, TRUE);
- ++error;
- has_error = TRUE;
+ ml_append(
+ lnum++,
+ (char_u *)_("??? from here until ???END lines"
+ " may have been inserted/deleted"),
+ (colnr_T)0, true);
+ error++;
+ has_error = true;
}
for (i = 0; i < dp->db_line_count; ++i) {
@@ -1146,11 +1147,11 @@ void ml_recover(void)
++error;
} else
p = (char_u *)dp + txt_start;
- ml_append(lnum++, p, (colnr_T)0, TRUE);
+ ml_append(lnum++, p, (colnr_T)0, true);
+ }
+ if (has_error) {
+ ml_append(lnum++, (char_u *)_("???END"), (colnr_T)0, true);
}
- if (has_error)
- ml_append(lnum++, (char_u *)_("???END"),
- (colnr_T)0, TRUE);
}
}
}
@@ -1201,7 +1202,7 @@ void ml_recover(void)
*/
while (curbuf->b_ml.ml_line_count > lnum
&& !(curbuf->b_ml.ml_flags & ML_EMPTY))
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
+ ml_delete(curbuf->b_ml.ml_line_count, false);
curbuf->b_flags |= BF_RECOVERED;
recoverymode = FALSE;
@@ -1453,14 +1454,47 @@ static char *make_percent_swname(const char *dir, char *name)
return d;
}
-#ifdef UNIX
static bool process_still_running;
-#endif
-/*
- * Give information about an existing swap file.
- * Returns timestamp (0 when unknown).
- */
+/// Return information found in swapfile "fname" in dictionary "d".
+/// This is used by the swapinfo() function.
+void get_b0_dict(const char *fname, dict_T *d)
+{
+ int fd;
+ struct block0 b0;
+
+ if ((fd = os_open(fname, O_RDONLY, 0)) >= 0) {
+ if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
+ if (ml_check_b0_id(&b0) == FAIL) {
+ tv_dict_add_str(d, S_LEN("error"), "Not a swap file");
+ } else if (b0_magic_wrong(&b0)) {
+ tv_dict_add_str(d, S_LEN("error"), "Magic number mismatch");
+ } else {
+ // We have swap information.
+ tv_dict_add_str_len(d, S_LEN("version"), (char *)b0.b0_version, 10);
+ tv_dict_add_str_len(d, S_LEN("user"), (char *)b0.b0_uname,
+ B0_UNAME_SIZE);
+ tv_dict_add_str_len(d, S_LEN("host"), (char *)b0.b0_hname,
+ B0_HNAME_SIZE);
+ tv_dict_add_str_len(d, S_LEN("fname"), (char *)b0.b0_fname,
+ B0_FNAME_SIZE_ORG);
+
+ tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid));
+ tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime));
+ tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0);
+ tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino));
+ }
+ } else {
+ tv_dict_add_str(d, S_LEN("error"), "Cannot read file");
+ }
+ close(fd);
+ } else {
+ tv_dict_add_str(d, S_LEN("error"), "Cannot open file");
+ }
+}
+
+/// Give information about an existing swap file.
+/// Returns timestamp (0 when unknown).
static time_t swapfile_info(char_u *fname)
{
assert(fname != NULL);
@@ -1530,12 +1564,10 @@ static time_t swapfile_info(char_u *fname)
if (char_to_long(b0.b0_pid) != 0L) {
MSG_PUTS(_("\n process ID: "));
msg_outnum(char_to_long(b0.b0_pid));
-#if defined(UNIX)
- if (kill((pid_t)char_to_long(b0.b0_pid), 0) == 0) {
+ if (os_proc_running((int)char_to_long(b0.b0_pid))) {
MSG_PUTS(_(" (STILL RUNNING)"));
process_still_running = true;
}
-#endif
}
if (b0_magic_wrong(&b0)) {
@@ -1552,6 +1584,51 @@ static time_t swapfile_info(char_u *fname)
return x;
}
+/// Returns TRUE if the swap file looks OK and there are no changes, thus it
+/// can be safely deleted.
+static time_t swapfile_unchanged(char *fname)
+{
+ struct block0 b0;
+ int ret = true;
+
+ // Swap file must exist.
+ if (!os_path_exists((char_u *)fname)) {
+ return false;
+ }
+
+ // must be able to read the first block
+ int fd = os_open(fname, O_RDONLY, 0);
+ if (fd < 0) {
+ return false;
+ }
+ if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0)) {
+ close(fd);
+ return false;
+ }
+
+ // the ID and magic number must be correct
+ if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0)) {
+ ret = false;
+ }
+
+ // must be unchanged
+ if (b0.b0_dirty) {
+ ret = false;
+ }
+
+ // process must be known and not running.
+ long pid = char_to_long(b0.b0_pid);
+ if (pid == 0L || os_proc_running((int)pid)) {
+ ret = false;
+ }
+
+ // TODO(bram): Should we check if the swap file was created on the current
+ // system? And the current user?
+
+ close(fd);
+ return ret;
+}
+
static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
FUNC_ATTR_NONNULL_ALL
{
@@ -1737,7 +1814,7 @@ char_u *
ml_get_buf (
buf_T *buf,
linenr_T lnum,
- int will_change /* line will be changed */
+ bool will_change // line will be changed
)
{
bhdr_T *hp;
@@ -1823,12 +1900,11 @@ int ml_line_alloced(void)
*
* return FAIL for failure, OK otherwise
*/
-int
-ml_append (
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of new line, including NUL, or 0 */
- int newfile /* flag, see above */
+int ml_append(
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of new line, including NUL, or 0
+ bool newfile // flag, see above
)
{
/* When starting up, we might still need to create the memfile */
@@ -1844,13 +1920,12 @@ ml_append (
* Like ml_append() but for an arbitrary buffer. The buffer must already have
* a memline.
*/
-int
-ml_append_buf (
+int ml_append_buf(
buf_T *buf,
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of new line, including NUL, or 0 */
- int newfile /* flag, see above */
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of new line, including NUL, or 0
+ bool newfile // flag, see above
)
{
if (buf->b_ml.ml_mfp == NULL)
@@ -1861,14 +1936,13 @@ ml_append_buf (
return ml_append_int(buf, lnum, line, len, newfile, FALSE);
}
-static int
-ml_append_int (
+static int ml_append_int(
buf_T *buf,
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of line, including NUL, or 0 */
- int newfile, /* flag, see above */
- int mark /* mark the new line */
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of line, including NUL, or 0
+ bool newfile, // flag, see above
+ int mark // mark the new line
)
{
int i;
@@ -2351,13 +2425,13 @@ int ml_replace(linenr_T lnum, char_u *line, bool copy)
///
/// @param message Show "--No lines in buffer--" message.
/// @return FAIL for failure, OK otherwise
-int ml_delete(linenr_T lnum, int message)
+int ml_delete(linenr_T lnum, bool message)
{
ml_flush_line(curbuf);
return ml_delete_int(curbuf, lnum, message);
}
-static int ml_delete_int(buf_T *buf, linenr_T lnum, int message)
+static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
{
bhdr_T *hp;
memfile_T *mfp;
@@ -2677,17 +2751,15 @@ static void ml_flush_line(buf_T *buf)
/* The else case is already covered by the insert and delete */
ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
} else {
- /*
- * Cannot do it in one data block: Delete and append.
- * Append first, because ml_delete_int() cannot delete the
- * last line in a buffer, which causes trouble for a buffer
- * that has only one line.
- * Don't forget to copy the mark!
- */
- /* How about handling errors??? */
- (void)ml_append_int(buf, lnum, new_line, new_len, FALSE,
- (dp->db_index[idx] & DB_MARKED));
- (void)ml_delete_int(buf, lnum, FALSE);
+ // Cannot do it in one data block: Delete and append.
+ // Append first, because ml_delete_int() cannot delete the
+ // last line in a buffer, which causes trouble for a buffer
+ // that has only one line.
+ // Don't forget to copy the mark!
+ // How about handling errors???
+ (void)ml_append_int(buf, lnum, new_line, new_len, false,
+ (dp->db_index[idx] & DB_MARKED));
+ (void)ml_delete_int(buf, lnum, false);
}
}
xfree(new_line);
@@ -2701,7 +2773,7 @@ static void ml_flush_line(buf_T *buf)
/*
* create a new, empty, data block
*/
-static bhdr_T *ml_new_data(memfile_T *mfp, int negative, int page_count)
+static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count)
{
assert(page_count >= 0);
bhdr_T *hp = mf_new(mfp, negative, (unsigned)page_count);
@@ -3353,17 +3425,24 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
-#ifdef UNIX
process_still_running = false;
-#endif
- /*
- * If there is a SwapExists autocommand and we can handle
- * the response, trigger it. It may return 0 to ask the
- * user anyway.
- */
- if (swap_exists_action != SEA_NONE
- && has_autocmd(EVENT_SWAPEXISTS, (char_u *) buf_fname, buf))
- choice = do_swapexists(buf, (char_u *) fname);
+ // It's safe to delete the swap file if all these are true:
+ // - the edited file exists
+ // - the swap file has no changes and looks OK
+ if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
+ choice = 4;
+ if (p_verbose > 0) {
+ verb_msg(_("Found a swap file that is not useful, deleting it"));
+ }
+ }
+
+ // If there is a SwapExists autocommand and we can handle the
+ // response, trigger it. It may return 0 to ask the user anyway.
+ if (choice == 0
+ && swap_exists_action != SEA_NONE
+ && has_autocmd(EVENT_SWAPEXISTS, (char_u *)buf_fname, buf)) {
+ choice = do_swapexists(buf, (char_u *)fname);
+ }
if (choice == 0) {
// Show info about the existing swap file.
@@ -3395,21 +3474,18 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
xstrlcat(name, sw_msg_2, name_len);
choice = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"),
(char_u *)name,
-# if defined(UNIX)
process_still_running
? (char_u *)_(
"&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Quit\n&Abort") :
-# endif
(char_u *)_(
"&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Delete it\n&Quit\n&Abort"),
1, NULL, false);
-# if defined(UNIX)
- if (process_still_running && choice >= 4)
- choice++; /* Skip missing "Delete it" button */
-# endif
+ if (process_still_running && choice >= 4) {
+ choice++; // Skip missing "Delete it" button.
+ }
xfree(name);
// pretend screen didn't scroll, need redraw anyway
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index aea297fce2..472481bb30 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -171,7 +171,7 @@ ex_menu(exarg_T *eap)
if (enable != kNone) {
// Change sensitivity of the menu.
// For the PopUp menu, remove a menu for each mode separately.
- // Careful: menu_nable_recurse() changes menu_path.
+ // Careful: menu_enable_recurse() changes menu_path.
if (STRCMP(menu_path, "*") == 0) { // meaning: do all menus
menu_path = (char_u *)"";
}
@@ -180,11 +180,11 @@ ex_menu(exarg_T *eap)
for (i = 0; i < MENU_INDEX_TIP; ++i)
if (modes & (1 << i)) {
p = popup_mode_name(menu_path, i);
- menu_nable_recurse(root_menu, p, MENU_ALL_MODES, enable);
+ menu_enable_recurse(root_menu, p, MENU_ALL_MODES, enable);
xfree(p);
}
}
- menu_nable_recurse(root_menu, menu_path, modes, enable);
+ menu_enable_recurse(root_menu, menu_path, modes, enable);
} else if (unmenu) {
/*
* Delete menu(s).
@@ -485,7 +485,10 @@ erret:
* Set the (sub)menu with the given name to enabled or disabled.
* Called recursively.
*/
-static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable)
+static int menu_enable_recurse(vimmenu_T *menu,
+ char_u *name,
+ int modes,
+ int enable)
{
char_u *p;
@@ -503,13 +506,14 @@ static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enab
EMSG(_(e_notsubmenu));
return FAIL;
}
- if (menu_nable_recurse(menu->children, p, modes, enable)
- == FAIL)
+ if (menu_enable_recurse(menu->children, p, modes, enable) == FAIL) {
return FAIL;
- } else if (enable)
+ }
+ } else if (enable) {
menu->enabled |= modes;
- else
+ } else {
menu->enabled &= ~modes;
+ }
/*
* When name is empty, we are doing all menu items for the given
@@ -770,15 +774,12 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
if (menu_name_equal(name, menu)) {
// Found menu
if (*p != NUL && menu->children == NULL) {
- if (*p != NUL) {
EMSG(_(e_notsubmenu));
return NULL;
- } else if ((menu->modes & modes) == 0x0) {
- EMSG(_(e_othermode));
- return NULL;
- }
- }
- if (*p == NUL) { // found a full match
+ } else if ((menu->modes & modes) == 0x0) {
+ EMSG(_(e_othermode));
+ return NULL;
+ } else if (*p == NUL) { // found a full match
return menu;
}
break;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index b4aa333a48..cb83d6482c 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -133,15 +133,11 @@ int msg(char_u *s)
return msg_attr_keep(s, 0, false, false);
}
-/*
- * Like msg() but keep it silent when 'verbosefile' is set.
- */
-int verb_msg(char_u *s)
+/// Like msg() but keep it silent when 'verbosefile' is set.
+int verb_msg(char *s)
{
- int n;
-
verbose_enter();
- n = msg_attr_keep(s, 0, false, false);
+ int n = msg_attr_keep((char_u *)s, 0, false, false);
verbose_leave();
return n;
@@ -384,7 +380,7 @@ int smsg(char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist, NULL);
+ vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
va_end(arglist);
return msg(IObuff);
}
@@ -395,11 +391,22 @@ int smsg_attr(int attr, char *s, ...)
va_list arglist;
va_start(arglist, s);
- vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist, NULL);
+ vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
va_end(arglist);
return msg_attr((const char *)IObuff, attr);
}
+int smsg_attr_keep(int attr, char *s, ...)
+ FUNC_ATTR_PRINTF(2, 3)
+{
+ va_list arglist;
+
+ va_start(arglist, s);
+ vim_vsnprintf((char *)IObuff, IOSIZE, s, arglist);
+ va_end(arglist);
+ return msg_attr_keep(IObuff, attr, true, false);
+}
+
/*
* Remember the last sourcing name/lnum used in an error message, so that it
* isn't printed each time when it didn't change.
@@ -662,7 +669,7 @@ bool emsgf_multiline(const char *const fmt, ...)
}
va_start(ap, fmt);
- vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
+ vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
va_end(ap);
ret = emsg_multiline(errbuf, true);
@@ -678,7 +685,7 @@ static bool emsgfv(const char *fmt, va_list ap)
return true;
}
- vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
+ vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
return emsg((const char_u *)errbuf);
}
@@ -726,7 +733,7 @@ void msg_schedule_emsgf(const char *const fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- vim_vsnprintf((char *)IObuff, IOSIZE, fmt, ap, NULL);
+ vim_vsnprintf((char *)IObuff, IOSIZE, fmt, ap);
va_end(ap);
char *s = xstrdup((char *)IObuff);
@@ -1826,7 +1833,7 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
va_list ap;
va_start(ap, fmt);
- const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap, NULL);
+ const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
va_end(ap);
msg_scroll = true;
@@ -1873,7 +1880,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
}
// Concat pieces with the same highlight
ga_concat_len(&msg_ext_last_chunk, (char *)str,
- strnlen((char *)str, maxlen));
+ strnlen((char *)str, maxlen)); // -V781
return;
}
@@ -3001,6 +3008,8 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1)
} else {
keep_msg_attr = 0;
}
+ msg_ext_set_kind("wmsg");
+
if (msg_attr((const char *)message, keep_msg_attr) && msg_scrolled == 0) {
set_keep_msg(message, keep_msg_attr);
}
@@ -3341,6 +3350,7 @@ void display_confirm_msg(void)
// Avoid that 'q' at the more prompt truncates the message here.
confirm_msg_used++;
if (confirm_msg != NULL) {
+ msg_ext_set_kind("confirm");
msg_puts_attr((const char *)confirm_msg, HL_ATTR(HLF_M));
}
confirm_msg_used--;
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index a8cfc2d700..0008409731 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -710,9 +710,6 @@ open_line (
less_cols_off++;
}
}
- if (*p_extra != NUL) {
- did_ai = false; // append some text, don't truncate now
- }
/* columns for marks adjusted for removed columns */
less_cols = (int)(p_extra - saved_line);
@@ -748,9 +745,9 @@ open_line (
if (dir == BACKWARD)
--curwin->w_cursor.lnum;
if (!(State & VREPLACE_FLAG) || old_cursor.lnum >= orig_line_count) {
- if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, FALSE)
- == FAIL)
+ if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) {
goto theend;
+ }
// Postpone calling changed_lines(), because it would mess up folding
// with markers.
// Skip mark_adjust when adding a line after the last one, there can't
@@ -848,10 +845,11 @@ open_line (
/* Move marks after the line break to the new line. */
if (flags & OPENLINE_MARKFIX)
mark_col_adjust(curwin->w_cursor.lnum,
- curwin->w_cursor.col + less_cols_off,
- 1L, (long)-less_cols);
- } else
+ curwin->w_cursor.col + less_cols_off,
+ 1L, (long)-less_cols, 0);
+ } else {
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
+ }
}
/*
@@ -1749,8 +1747,8 @@ del_lines (
if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
break;
- ml_delete(first, TRUE);
- ++n;
+ ml_delete(first, true);
+ n++;
/* If we delete the last line in the file, stop */
if (first > curbuf->b_ml.ml_line_count)
@@ -2177,7 +2175,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
/* when the cursor line is changed always trigger CursorMoved */
if (lnum <= curwin->w_cursor.lnum
&& lnume + (xtra < 0 ? -xtra : xtra) > curwin->w_cursor.lnum)
- last_cursormoved.lnum = 0;
+ curwin->w_last_cursormoved.lnum = 0;
}
/*
@@ -2250,6 +2248,7 @@ change_warning (
if (msg_row == Rows - 1)
msg_col = col;
msg_source(HL_ATTR(HLF_W));
+ msg_ext_set_kind("wmsg");
MSG_PUTS_ATTR(_(w_readonly), HL_ATTR(HLF_W) | MSG_HIST);
set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1);
msg_clr_eos();
@@ -2580,6 +2579,8 @@ void beep_flush(void)
// val is one of the BO_ values, e.g., BO_OPER
void vim_beep(unsigned val)
{
+ called_vim_beep = true;
+
if (emsg_silent == 0) {
if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
if (p_vb) {
@@ -2589,8 +2590,9 @@ void vim_beep(unsigned val)
}
}
- /* When 'verbose' is set and we are sourcing a script or executing a
- * function give the user a hint where the beep comes from. */
+ // When 'debug' contains "beep" produce a message. If we are sourcing
+ // a script or executing a function give the user a hint where the beep
+ // comes from.
if (vim_strchr(p_debug, 'e') != NULL) {
msg_source(HL_ATTR(HLF_W));
msg_attr(_("Beep!"), HL_ATTR(HLF_W));
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 7aa7f922c1..e076543614 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -692,7 +692,7 @@ int win_col_off(win_T *wp)
return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
+ (cmdwin_type == 0 || wp != curwin ? 0 : 1)
+ (int)wp->w_p_fdc
- + (signcolumn_on(wp) ? win_signcol_width(wp) : 0);
+ + (win_signcol_count(wp) * win_signcol_width(wp));
}
int curwin_col_off(void)
@@ -1524,9 +1524,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
/* Count screen lines that are below the window. */
scrolled += loff.height;
if (loff.lnum == curwin->w_botline
- && boff.fill == 0
- )
+ && loff.fill == 0) {
scrolled -= curwin->w_empty_rows;
+ }
}
if (boff.lnum < curbuf->b_ml.ml_line_count) {
@@ -2001,10 +2001,7 @@ static void get_scroll_overlap(lineoff_T *lp, int dir)
return;
}
-/* #define KEEP_SCREEN_LINE */
-/*
- * Scroll 'scroll' lines up or down.
- */
+// Scroll 'scroll' lines up or down.
void halfpage(bool flag, linenr_T Prenum)
{
long scrolled = 0;
@@ -2039,13 +2036,11 @@ void halfpage(bool flag, linenr_T Prenum)
++curwin->w_topline;
curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
-#ifndef KEEP_SCREEN_LINE
if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
++curwin->w_cursor.lnum;
curwin->w_valid &=
~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
-#endif
}
curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
scrolled += i;
@@ -2070,10 +2065,7 @@ void halfpage(bool flag, linenr_T Prenum)
}
}
-#ifndef KEEP_SCREEN_LINE
- /*
- * When hit bottom of the file: move cursor down.
- */
+ // When hit bottom of the file: move cursor down.
if (n > 0) {
if (hasAnyFolding(curwin)) {
while (--n >= 0
@@ -2086,18 +2078,6 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_cursor.lnum += n;
check_cursor_lnum();
}
-#else
- /* try to put the cursor in the same screen line */
- while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
- && curwin->w_cursor.lnum < curwin->w_botline - 1) {
- scrolled -= plines(curwin->w_cursor.lnum);
- if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
- break;
- (void)hasFolding(curwin->w_cursor.lnum, NULL,
- &curwin->w_cursor.lnum);
- ++curwin->w_cursor.lnum;
- }
-#endif
} else {
/*
* scroll the text down
@@ -2119,17 +2099,13 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
VALID_BOTLINE|VALID_BOTLINE_AP);
scrolled += i;
-#ifndef KEEP_SCREEN_LINE
if (curwin->w_cursor.lnum > 1) {
--curwin->w_cursor.lnum;
curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
-#endif
}
-#ifndef KEEP_SCREEN_LINE
- /*
- * When hit top of the file: move cursor up.
- */
+
+ // When hit top of the file: move cursor up.
if (n > 0) {
if (curwin->w_cursor.lnum <= (linenr_T)n)
curwin->w_cursor.lnum = 1;
@@ -2142,18 +2118,6 @@ void halfpage(bool flag, linenr_T Prenum)
} else
curwin->w_cursor.lnum -= n;
}
-#else
- /* try to put the cursor in the same screen line */
- scrolled += n; /* move cursor when topline is 1 */
- while (curwin->w_cursor.lnum > curwin->w_topline
- && (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline)) {
- scrolled -= plines(curwin->w_cursor.lnum - 1);
- if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
- break;
- --curwin->w_cursor.lnum;
- foldAdjustCursor();
- }
-#endif
}
/* Move cursor to first line of closed fold. */
foldAdjustCursor();
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 76fbe407c2..3438949e2d 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -131,7 +131,7 @@ Object rpc_send_call(uint64_t id,
channel_incref(channel);
RpcState *rpc = &channel->rpc;
- uint64_t request_id = rpc->next_request_id++;
+ uint32_t request_id = rpc->next_request_id++;
// Send the msgpack-rpc request
send_request(channel, request_id, method_name, args);
@@ -281,23 +281,26 @@ static void parse_msgpack(Channel *channel)
// A not so uncommon cause for this might be deserializing objects with
// a high nesting level: msgpack will break when its internal parse stack
// size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default)
- send_error(channel, 0, "Invalid msgpack payload. "
- "This error can also happen when deserializing "
- "an object with high level of nesting");
+ send_error(channel, kMessageTypeRequest, 0,
+ "Invalid msgpack payload. "
+ "This error can also happen when deserializing "
+ "an object with high level of nesting");
}
}
+/// Handles requests and notifications received on the channel.
static void handle_request(Channel *channel, msgpack_object *request)
FUNC_ATTR_NONNULL_ALL
{
- uint64_t request_id;
+ uint32_t request_id;
Error error = ERROR_INIT;
- msgpack_rpc_validate(&request_id, request, &error);
+ MessageType type = msgpack_rpc_validate(&request_id, request, &error);
if (ERROR_SET(&error)) {
// Validation failed, send response with error
if (channel_write(channel,
serialize_response(channel->id,
+ type,
request_id,
&error,
NIL,
@@ -311,6 +314,7 @@ static void handle_request(Channel *channel, msgpack_object *request)
api_clear_error(&error);
return;
}
+ assert(type == kMessageTypeRequest || type == kMessageTypeNotification);
MsgpackRpcRequestHandler handler;
msgpack_object *method = msgpack_rpc_method(request);
@@ -326,13 +330,14 @@ static void handle_request(Channel *channel, msgpack_object *request)
}
if (ERROR_SET(&error)) {
- send_error(channel, request_id, error.msg);
+ send_error(channel, type, request_id, error.msg);
api_clear_error(&error);
api_free_array(args);
return;
}
RequestEvent *evdata = xmalloc(sizeof(RequestEvent));
+ evdata->type = type;
evdata->channel = channel;
evdata->handler = handler;
evdata->args = args;
@@ -343,39 +348,41 @@ static void handle_request(Channel *channel, msgpack_object *request)
if (is_get_mode && !input_blocking()) {
// Defer the event to a special queue used by os/input.c. #6247
- multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata);
+ multiqueue_put(ch_before_blocking_events, request_event, 1, evdata);
} else {
// Invoke immediately.
- on_request_event((void **)&evdata);
+ request_event((void **)&evdata);
}
} else {
- multiqueue_put(channel->events, on_request_event, 1, evdata);
+ multiqueue_put(channel->events, request_event, 1, evdata);
DLOG("RPC: scheduled %.*s", method->via.bin.size, method->via.bin.ptr);
}
}
-static void on_request_event(void **argv)
+/// Handles a message, depending on the type:
+/// - Request: invokes method and writes the response (or error).
+/// - Notification: invokes method (emits `nvim_error_event` on error).
+static void request_event(void **argv)
{
RequestEvent *e = argv[0];
Channel *channel = e->channel;
MsgpackRpcRequestHandler handler = e->handler;
- Array args = e->args;
- uint64_t request_id = e->request_id;
Error error = ERROR_INIT;
- Object result = handler.fn(channel->id, args, &error);
- if (request_id != NO_RESPONSE) {
- // send the response
+ Object result = handler.fn(channel->id, e->args, &error);
+ if (e->type == kMessageTypeRequest || ERROR_SET(&error)) {
+ // Send the response.
msgpack_packer response;
msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write);
channel_write(channel, serialize_response(channel->id,
- request_id,
+ e->type,
+ e->request_id,
&error,
result,
&out_buffer));
} else {
api_free_object(result);
}
- api_free_array(args);
+ api_free_array(e->args);
channel_decref(channel);
xfree(e);
api_clear_error(&error);
@@ -430,20 +437,21 @@ static void internal_read_event(void **argv)
wstream_release_wbuffer(buffer);
}
-static void send_error(Channel *channel, uint64_t id, char *err)
+static void send_error(Channel *chan, MessageType type, uint32_t id, char *err)
{
Error e = ERROR_INIT;
api_set_error(&e, kErrorTypeException, "%s", err);
- channel_write(channel, serialize_response(channel->id,
- id,
- &e,
- NIL,
- &out_buffer));
+ channel_write(chan, serialize_response(chan->id,
+ type,
+ id,
+ &e,
+ NIL,
+ &out_buffer));
api_clear_error(&e);
}
static void send_request(Channel *channel,
- uint64_t id,
+ uint32_t id,
const char *name,
Array args)
{
@@ -576,7 +584,7 @@ static bool is_rpc_response(msgpack_object *obj)
static bool is_valid_rpc_response(msgpack_object *obj, Channel *channel)
{
- uint64_t response_id = obj->via.array.ptr[1].via.u64;
+ uint32_t response_id = (uint32_t)obj->via.array.ptr[1].via.u64;
if (kv_size(channel->rpc.call_stack) == 0) {
return false;
}
@@ -614,7 +622,7 @@ static void call_set_error(Channel *channel, char *msg, int loglevel)
}
static WBuffer *serialize_request(uint64_t channel_id,
- uint64_t request_id,
+ uint32_t request_id,
const String method,
Array args,
msgpack_sbuffer *sbuffer,
@@ -634,14 +642,15 @@ static WBuffer *serialize_request(uint64_t channel_id,
}
static WBuffer *serialize_response(uint64_t channel_id,
- uint64_t response_id,
+ MessageType type,
+ uint32_t response_id,
Error *err,
Object arg,
msgpack_sbuffer *sbuffer)
{
msgpack_packer pac;
msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write);
- if (ERROR_SET(err) && response_id == NO_RESPONSE) {
+ if (ERROR_SET(err) && type == kMessageTypeNotification) {
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(err->type));
ADD(args, STRING_OBJ(cstr_to_string(err->msg)));
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index bfa7f7b87c..6ef8c027f0 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -13,23 +13,24 @@
typedef struct Channel Channel;
typedef struct {
- uint64_t request_id;
+ uint32_t request_id;
bool returned, errored;
Object result;
} ChannelCallFrame;
typedef struct {
+ MessageType type;
Channel *channel;
MsgpackRpcRequestHandler handler;
Array args;
- uint64_t request_id;
+ uint32_t request_id;
} RequestEvent;
typedef struct {
PMap(cstr_t) *subscribed_events;
bool closed;
msgpack_unpacker *unpacker;
- uint64_t next_request_id;
+ uint32_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack;
Dictionary info;
} RpcState;
diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c
index 19cc31f6a6..3925dc546a 100644
--- a/src/nvim/msgpack_rpc/helpers.c
+++ b/src/nvim/msgpack_rpc/helpers.c
@@ -489,7 +489,7 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
}
/// Serializes a msgpack-rpc request or notification(id == 0)
-void msgpack_rpc_serialize_request(uint64_t request_id,
+void msgpack_rpc_serialize_request(uint32_t request_id,
const String method,
Array args,
msgpack_packer *pac)
@@ -499,7 +499,7 @@ void msgpack_rpc_serialize_request(uint64_t request_id,
msgpack_pack_int(pac, request_id ? 0 : 2);
if (request_id) {
- msgpack_pack_uint64(pac, request_id);
+ msgpack_pack_uint32(pac, request_id);
}
msgpack_rpc_from_string(method, pac);
@@ -507,7 +507,7 @@ void msgpack_rpc_serialize_request(uint64_t request_id,
}
/// Serializes a msgpack-rpc response
-void msgpack_rpc_serialize_response(uint64_t response_id,
+void msgpack_rpc_serialize_response(uint32_t response_id,
Error *err,
Object arg,
msgpack_packer *pac)
@@ -515,7 +515,7 @@ void msgpack_rpc_serialize_response(uint64_t response_id,
{
msgpack_pack_array(pac, 4);
msgpack_pack_int(pac, 1);
- msgpack_pack_uint64(pac, response_id);
+ msgpack_pack_uint32(pac, response_id);
if (ERROR_SET(err)) {
// error represented by a [type, message] array
@@ -561,58 +561,57 @@ static msgpack_object *msgpack_rpc_msg_id(msgpack_object *req)
return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER ? obj : NULL;
}
-void msgpack_rpc_validate(uint64_t *response_id,
- msgpack_object *req,
- Error *err)
+MessageType msgpack_rpc_validate(uint32_t *response_id, msgpack_object *req,
+ Error *err)
{
- // response id not known yet
-
- *response_id = NO_RESPONSE;
+ *response_id = 0;
// Validate the basic structure of the msgpack-rpc payload
if (req->type != MSGPACK_OBJECT_ARRAY) {
api_set_error(err, kErrorTypeValidation, "Message is not an array");
- return;
+ return kMessageTypeUnknown;
}
if (req->via.array.size == 0) {
api_set_error(err, kErrorTypeValidation, "Message is empty");
- return;
+ return kMessageTypeUnknown;
}
if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
api_set_error(err, kErrorTypeValidation, "Message type must be an integer");
- return;
+ return kMessageTypeUnknown;
}
- uint64_t type = req->via.array.ptr[0].via.u64;
+ MessageType type = (MessageType)req->via.array.ptr[0].via.u64;
if (type != kMessageTypeRequest && type != kMessageTypeNotification) {
api_set_error(err, kErrorTypeValidation, "Unknown message type");
- return;
+ return kMessageTypeUnknown;
}
if ((type == kMessageTypeRequest && req->via.array.size != 4)
|| (type == kMessageTypeNotification && req->via.array.size != 3)) {
api_set_error(err, kErrorTypeValidation,
"Request array size must be 4 (request) or 3 (notification)");
- return;
+ return type;
}
if (type == kMessageTypeRequest) {
msgpack_object *id_obj = msgpack_rpc_msg_id(req);
if (!id_obj) {
api_set_error(err, kErrorTypeValidation, "ID must be a positive integer");
- return;
+ return type;
}
- *response_id = id_obj->via.u64;
+ *response_id = (uint32_t)id_obj->via.u64;
}
if (!msgpack_rpc_method(req)) {
api_set_error(err, kErrorTypeValidation, "Method must be a string");
- return;
+ return type;
}
if (!msgpack_rpc_args(req)) {
api_set_error(err, kErrorTypeValidation, "Parameters must be an array");
- return;
+ return type;
}
+
+ return type;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f12abd362f..ca586cca29 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1187,12 +1187,12 @@ static void normal_check_cursor_moved(NormalState *s)
{
// Trigger CursorMoved if the cursor moved.
if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
- && !equalpos(last_cursormoved, curwin->w_cursor)) {
+ && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) {
if (has_event(EVENT_CURSORMOVED)) {
apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
}
- last_cursormoved = curwin->w_cursor;
+ curwin->w_last_cursormoved = curwin->w_cursor;
}
}
@@ -4642,6 +4642,9 @@ static void nv_clear(cmdarg_T *cap)
if (!checkclearop(cap->oap)) {
/* Clear all syntax states to force resyncing. */
syn_stack_free_all(curwin->w_s);
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ wp->w_s->b_syn_slow = false;
+ }
redraw_later(CLEAR);
}
}
@@ -6526,8 +6529,14 @@ static void n_start_visual_mode(int c)
*/
static void nv_window(cmdarg_T *cap)
{
- if (!checkclearop(cap->oap))
- do_window(cap->nchar, cap->count0, NUL); /* everything is in window.c */
+ if (cap->nchar == ':') {
+ // "CTRL-W :" is the same as typing ":"; useful in a terminal window
+ cap->cmdchar = ':';
+ cap->nchar = NUL;
+ nv_colon(cap);
+ } else if (!checkclearop(cap->oap)) {
+ do_window(cap->nchar, cap->count0, NUL); // everything is in window.c
+ }
}
/*
@@ -8001,7 +8010,6 @@ static void nv_event(cmdarg_T *cap)
// lists or dicts being used.
may_garbage_collect = false;
multiqueue_process_events(main_loop.events);
- cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
finish_op = false;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 99dee939fc..216bab4dda 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -153,6 +153,10 @@ int get_op_type(int char1, int char2)
if (opchars[i][0] == char1 && opchars[i][1] == char2) {
break;
}
+ if (i == (int)(ARRAY_SIZE(opchars) - 1)) {
+ internal_error("get_op_type()");
+ break;
+ }
}
return i;
}
@@ -1408,8 +1412,10 @@ int op_delete(oparg_T *oap)
free_register(&y_regs[9]); /* free register "9 */
for (n = 9; n > 1; n--)
y_regs[n] = y_regs[n - 1];
- y_previous = &y_regs[1];
- y_regs[1].y_array = NULL; /* set register "1 to empty */
+ if (!is_append_register(oap->regname)) {
+ y_previous = &y_regs[1];
+ }
+ y_regs[1].y_array = NULL; // set register "1 to empty
reg = &y_regs[1];
op_yank_reg(oap, false, reg, false);
}
@@ -2605,17 +2611,16 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
static bool recursive = false;
if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
- // No autocommand was defined
- // or we yanked from this autocommand.
+ // No autocommand was defined, or we yanked from this autocommand.
return;
}
recursive = true;
- // set v:event to a dictionary with information about the yank
+ // Set the v:event dictionary with information about the yank.
dict_T *dict = get_vim_var_dict(VV_EVENT);
- // the yanked text
+ // The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
@@ -2623,17 +2628,21 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
- // the register type
+ // Register type.
char buf[NUMBUFLEN+2];
format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
tv_dict_add_str(dict, S_LEN("regtype"), buf);
- // name of requested register or the empty string for an unnamed operation.
+ // Name of requested register, or empty string for unnamed operation.
buf[0] = (char)oap->regname;
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("regname"), buf);
- // kind of operation (yank/delete/change)
+ // Motion type: inclusive or exclusive.
+ tv_dict_add_special(dict, S_LEN("inclusive"),
+ oap->inclusive ? kSpecialVarTrue : kSpecialVarFalse);
+
+ // Kind of operation: yank, delete, change).
buf[0] = (char)get_op_char(oap->op_type);
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("operator"), buf);
@@ -2786,8 +2795,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
if (!curbuf->terminal) {
- // Autocommands may be executed when saving lines for undo, which may make
- // y_array invalid. Start undo now to avoid that.
+ // Autocommands may be executed when saving lines for undo. This might
+ // make y_array invalid, so we start undo now to avoid that.
if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) {
return;
}
@@ -2992,9 +3001,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
/* add a new line */
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
if (ml_append(curbuf->b_ml.ml_line_count, (char_u *)"",
- (colnr_T)1, FALSE) == FAIL)
+ (colnr_T)1, false) == FAIL) {
break;
- ++nr_lines;
+ }
+ nr_lines++;
}
/* get the old line and advance to the position to insert at */
oldp = get_cursor_line_ptr();
@@ -3181,8 +3191,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
newp = (char_u *) xmalloc((size_t)(STRLEN(ptr) + totlen + 1));
STRCPY(newp, y_array[y_size - 1]);
STRCAT(newp, ptr);
- /* insert second line */
- ml_append(lnum, newp, (colnr_T)0, FALSE);
+ // insert second line
+ ml_append(lnum, newp, (colnr_T)0, false);
xfree(newp);
oldp = ml_get(lnum);
@@ -3704,10 +3714,16 @@ int do_join(size_t count,
cend -= spaces[t];
memset(cend, ' ', (size_t)(spaces[t]));
}
+
+ // If deleting more spaces than adding, the cursor moves no more than
+ // what is added if it is inside these spaces.
+ const int spaces_removed = (int)((curr - curr_start) - spaces[t]);
+
mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
- (long)(cend - newp + spaces[t] - (curr - curr_start)));
- if (t == 0)
+ (long)(cend - newp - spaces_removed), spaces_removed);
+ if (t == 0) {
break;
+ }
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
if (remove_comments)
curr += comments[t - 1];
@@ -4135,14 +4151,14 @@ format_lines (
if (next_leader_len > 0) {
(void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len);
+ (long)-next_leader_len, 0);
} else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
int indent = (int)getwhitecols_curline();
if (indent > 0) {
(void)del_bytes(indent, FALSE, FALSE);
mark_col_adjust(curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-indent);
+ (colnr_T)0, 0L, (long)-indent, 0);
}
}
curwin->w_cursor.lnum--;
@@ -5601,6 +5617,9 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
if (explicit_cb_reg) {
target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
+ if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) {
+ clipboard_needs_update = false;
+ }
goto end;
} else { // unnamed register: "implicit" clipboard
if (writing && clipboard_delay_update) {
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 2346c84b54..743f6c8311 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -98,10 +98,10 @@
#define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x))
-/* WV_ and BV_ values get typecasted to this for the "indir" field */
+// WV_ and BV_ values get typecasted to this for the "indir" field
typedef enum {
PV_NONE = 0,
- PV_MAXVAL = 0xffff /* to avoid warnings for value out of range */
+ PV_MAXVAL = 0xffff // to avoid warnings for value out of range
} idopt_T;
/*
@@ -175,7 +175,7 @@ static int p_udf;
static long p_wm;
static char_u *p_keymap;
-/* Saved values for when 'bin' is set. */
+// Saved values for when 'bin' is set.
static int p_et_nobin;
static int p_ml_nobin;
static long p_tw_nobin;
@@ -202,28 +202,28 @@ typedef struct vimoption {
# define SCRIPTID_INIT , 0
} vimoption_T;
-#define VI_DEFAULT 0 /* def_val[VI_DEFAULT] is Vi default value */
-#define VIM_DEFAULT 1 /* def_val[VIM_DEFAULT] is Vim default value */
+#define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value
+#define VIM_DEFAULT 1 // def_val[VIM_DEFAULT] is Vim default value
/*
* Flags
*/
-#define P_BOOL 0x01U /* the option is boolean */
-#define P_NUM 0x02U /* the option is numeric */
-#define P_STRING 0x04U /* the option is a string */
-#define P_ALLOCED 0x08U /* the string option is in allocated memory,
- must use free_string_option() when
- assigning new value. Not set if default is
- the same. */
-#define P_EXPAND 0x10U /* environment expansion. NOTE: P_EXPAND can
- never be used for local or hidden options */
-#define P_NODEFAULT 0x40U /* don't set to default value */
-#define P_DEF_ALLOCED 0x80U /* default value is in allocated memory, must
- use free() when assigning new value */
-#define P_WAS_SET 0x100U /* option has been set/reset */
-#define P_NO_MKRC 0x200U /* don't include in :mkvimrc output */
-#define P_VI_DEF 0x400U /* Use Vi default for Vim */
-#define P_VIM 0x800U /* Vim option */
+#define P_BOOL 0x01U // the option is boolean
+#define P_NUM 0x02U // the option is numeric
+#define P_STRING 0x04U // the option is a string
+#define P_ALLOCED 0x08U // the string option is in allocated memory,
+ // must use free_string_option() when
+ // assigning new value. Not set if default is
+ // the same.
+#define P_EXPAND 0x10U // environment expansion. NOTE: P_EXPAND can
+ // never be used for local or hidden options
+#define P_NODEFAULT 0x40U // don't set to default value
+#define P_DEF_ALLOCED 0x80U // default value is in allocated memory, must
+ // use free() when assigning new value
+#define P_WAS_SET 0x100U // option has been set/reset
+#define P_NO_MKRC 0x200U // don't include in :mkvimrc output
+#define P_VI_DEF 0x400U // Use Vi default for Vim
+#define P_VIM 0x800U // Vim option
// when option changed, what to display:
#define P_RSTAT 0x1000U ///< redraw status lines
@@ -284,7 +284,6 @@ static char *(p_ambw_values[]) = { "single", "double", NULL };
static char *(p_bg_values[]) = { "light", "dark", NULL };
static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", NULL };
static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
-static char *(p_wop_values[]) = { "tagfile", NULL };
static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos",
"mac", NULL };
@@ -306,7 +305,19 @@ static char *(p_fcl_values[]) = { "all", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview",
"noinsert", "noselect", NULL };
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
-static char *(p_scl_values[]) = { "yes", "no", "auto", NULL };
+static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2",
+ "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9",
+ "yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8",
+ "yes:9", NULL };
+
+/// All possible flags for 'shm'.
+static char_u SHM_ALL[] = {
+ SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI,
+ SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER,
+ SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU,
+ SHM_RECORDING, SHM_FILEINFO,
+ 0,
+};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
@@ -421,13 +432,17 @@ static inline char *add_colon_dirs(char *dest, const char *const val,
return dest;
}
-/// Add directory to a comma-separated list of directories
+/// Adds directory `dest` to a comma-separated list of directories.
///
-/// In the added directory comma is escaped.
+/// Commas in the added directory are escaped.
+///
+/// Windows: Appends "nvim-data" instead of "nvim" if `type` is kXDGDataHome.
+///
+/// @see get_xdg_home
///
/// @param[in,out] dest Destination comma-separated array.
/// @param[in] dir Directory to append.
-/// @param[in] append_nvim If true, append "nvim" as the very first suffix.
+/// @param[in] type Decides whether to append "nvim" (Win: or "nvim-data").
/// @param[in] suf1 If not NULL, suffix appended to destination. Prior to it
/// directory separator is appended. Suffix must not contain
/// commas.
@@ -441,7 +456,7 @@ static inline char *add_colon_dirs(char *dest, const char *const val,
///
/// @return (dest + appended_characters_length)
static inline char *add_dir(char *dest, const char *const dir,
- const size_t dir_len, const bool append_nvim,
+ const size_t dir_len, const XDGVarType type,
const char *const suf1, const size_t len1,
const char *const suf2, const size_t len2)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
@@ -450,12 +465,19 @@ static inline char *add_dir(char *dest, const char *const dir,
return dest;
}
dest = strcpy_comma_escaped(dest, dir, dir_len);
+ bool append_nvim = (type == kXDGDataHome || type == kXDGConfigHome);
if (append_nvim) {
if (!after_pathsep(dest - 1, dest)) {
*dest++ = PATHSEP;
}
+#if defined(WIN32)
+ size_t size = (type == kXDGDataHome ? sizeof("nvim-data") - 1 : NVIM_SIZE);
+ memmove(dest, (type == kXDGDataHome ? "nvim-data" : "nvim"), size);
+ dest += size;
+#else
memmove(dest, "nvim", NVIM_SIZE);
dest += NVIM_SIZE;
+#endif
if (suf1 != NULL) {
*dest++ = PATHSEP;
memmove(dest, suf1, len1);
@@ -471,7 +493,10 @@ static inline char *add_dir(char *dest, const char *const dir,
return dest;
}
-/// Set &runtimepath to default value
+/// Sets &runtimepath to default value.
+///
+/// Windows: Uses "…/nvim-data" for kXDGDataHome to avoid storing
+/// configuration and data files in the same path. #4403
static void set_runtimepath_default(void)
{
size_t rtp_size = 0;
@@ -488,8 +513,13 @@ static void set_runtimepath_default(void)
if (data_home != NULL) {
data_len = strlen(data_home);
if (data_len != 0) {
+#if defined(WIN32)
+ size_t nvim_size = (sizeof("nvim-data") - 1);
+#else
+ size_t nvim_size = NVIM_SIZE;
+#endif
rtp_size += ((data_len + memcnt(data_home, ',', data_len)
- + NVIM_SIZE + 1 + SITE_SIZE + 1
+ + nvim_size + 1 + SITE_SIZE + 1
+ !after_pathsep(data_home, data_home + data_len)) * 2
+ AFTER_SIZE + 1);
}
@@ -518,21 +548,22 @@ static void set_runtimepath_default(void)
}
char *const rtp = xmalloc(rtp_size);
char *rtp_cur = rtp;
- rtp_cur = add_dir(rtp_cur, config_home, config_len, true, NULL, 0, NULL, 0);
+ rtp_cur = add_dir(rtp_cur, config_home, config_len, kXDGConfigHome,
+ NULL, 0, NULL, 0);
rtp_cur = add_colon_dirs(rtp_cur, config_dirs, NULL, 0, NULL, 0, true);
- rtp_cur = add_dir(rtp_cur, data_home, data_len, true, "site", SITE_SIZE,
- NULL, 0);
+ rtp_cur = add_dir(rtp_cur, data_home, data_len, kXDGDataHome,
+ "site", SITE_SIZE, NULL, 0);
rtp_cur = add_colon_dirs(rtp_cur, data_dirs, "site", SITE_SIZE, NULL, 0,
true);
- rtp_cur = add_dir(rtp_cur, vimruntime, vimruntime_len, false, NULL, 0,
- NULL, 0);
+ rtp_cur = add_dir(rtp_cur, vimruntime, vimruntime_len, kXDGNone,
+ NULL, 0, NULL, 0);
rtp_cur = add_colon_dirs(rtp_cur, data_dirs, "site", SITE_SIZE,
"after", AFTER_SIZE, false);
- rtp_cur = add_dir(rtp_cur, data_home, data_len, true, "site", SITE_SIZE,
- "after", AFTER_SIZE);
+ rtp_cur = add_dir(rtp_cur, data_home, data_len, kXDGDataHome,
+ "site", SITE_SIZE, "after", AFTER_SIZE);
rtp_cur = add_colon_dirs(rtp_cur, config_dirs, "after", AFTER_SIZE, NULL, 0,
false);
- rtp_cur = add_dir(rtp_cur, config_home, config_len, true,
+ rtp_cur = add_dir(rtp_cur, config_home, config_len, kXDGConfigHome,
"after", AFTER_SIZE, NULL, 0);
// Strip trailing comma.
rtp_cur[-1] = NUL;
@@ -562,8 +593,8 @@ void set_init_1(void)
langmap_init();
- /* Be nocompatible */
- p_cp = FALSE;
+ // Be nocompatible
+ p_cp = false;
/*
* Find default value for 'shell' option.
@@ -590,23 +621,25 @@ void set_init_1(void)
garray_T ga;
ga_init(&ga, 1, 100);
- for (size_t n = 0; n < ARRAY_SIZE(names); ++n) {
+ for (size_t n = 0; n < ARRAY_SIZE(names); n++) {
bool mustfree = true;
char *p;
# ifdef UNIX
if (*names[n] == NUL) {
p = "/tmp";
mustfree = false;
- }
- else
+ } else
# endif
- p = vim_getenv(names[n]);
+ {
+ p = vim_getenv(names[n]);
+ }
if (p != NULL && *p != NUL) {
// First time count the NUL, otherwise count the ','.
len = (int)strlen(p) + 3;
ga_grow(&ga, len);
- if (!GA_EMPTY(&ga))
+ if (!GA_EMPTY(&ga)) {
STRCAT(ga.ga_data, ",");
+ }
STRCAT(ga.ga_data, p);
add_pathsep(ga.ga_data);
STRCAT(ga.ga_data, "*");
@@ -627,19 +660,20 @@ void set_init_1(void)
int i;
int j;
- /* Initialize the 'cdpath' option's default value. */
+ // Initialize the 'cdpath' option's default value.
cdpath = (char_u *)vim_getenv("CDPATH");
if (cdpath != NULL) {
buf = xmalloc(2 * STRLEN(cdpath) + 2);
{
- buf[0] = ','; /* start with ",", current dir first */
+ buf[0] = ','; // start with ",", current dir first
j = 1;
- for (i = 0; cdpath[i] != NUL; ++i) {
- if (vim_ispathlistsep(cdpath[i]))
+ for (i = 0; cdpath[i] != NUL; i++) {
+ if (vim_ispathlistsep(cdpath[i])) {
buf[j++] = ',';
- else {
- if (cdpath[i] == ' ' || cdpath[i] == ',')
+ } else {
+ if (cdpath[i] == ' ' || cdpath[i] == ',') {
buf[j++] = '\\';
+ }
buf[j++] = cdpath[i];
}
}
@@ -648,15 +682,16 @@ void set_init_1(void)
if (opt_idx >= 0) {
options[opt_idx].def_val[VI_DEFAULT] = buf;
options[opt_idx].flags |= P_DEF_ALLOCED;
- } else
- xfree(buf); /* cannot happen */
+ } else {
+ xfree(buf); // cannot happen
+ }
}
xfree(cdpath);
}
}
#if defined(MSWIN) || defined(MAC)
- /* Set print encoding on platforms that don't default to latin1 */
+ // Set print encoding on platforms that don't default to latin1
set_string_default("printencoding", "hp-roman8", false);
#endif
@@ -701,19 +736,19 @@ void set_init_1(void)
curbuf->b_p_initialized = true;
- curbuf->b_p_ar = -1; /* no local 'autoread' value */
+ curbuf->b_p_ar = -1; // no local 'autoread' value
curbuf->b_p_ul = NO_LOCAL_UNDOLEVEL;
check_buf_options(curbuf);
check_win_options(curwin);
check_options();
- /* Set all options to their Vim default */
+ // Set all options to their Vim default
set_options_default(OPT_FREE);
// set 'laststatus'
last_status(false);
- /* Must be before option_expand(), because that one needs vim_isIDc() */
+ // Must be before option_expand(), because that one needs vim_isIDc()
didset_options();
// Use the current chartab for the generic chartab. This is not in
@@ -748,14 +783,15 @@ void set_init_1(void)
* and Vim. When this changes, add some code here! Also need to
* split P_DEF_ALLOCED in two.
*/
- if (options[opt_idx].flags & P_DEF_ALLOCED)
+ if (options[opt_idx].flags & P_DEF_ALLOCED) {
xfree(options[opt_idx].def_val[VI_DEFAULT]);
- options[opt_idx].def_val[VI_DEFAULT] = (char_u *) p;
+ }
+ options[opt_idx].def_val[VI_DEFAULT] = (char_u *)p;
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
- save_file_ff(curbuf); /* Buffer is unchanged */
+ save_file_ff(curbuf); // Buffer is unchanged
/* Detect use of mlterm.
* Mlterm is a terminal emulator akin to xterm that has some special
@@ -787,7 +823,7 @@ void set_init_1(void)
(void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc);
#endif
- /* Set the default for 'helplang'. */
+ // Set the default for 'helplang'.
set_helplang_default(get_mess_lang());
}
@@ -795,20 +831,20 @@ void set_init_1(void)
* Set an option to its default value.
* This does not take care of side effects!
*/
-static void
-set_option_default (
+static void
+set_option_default(
int opt_idx,
- int opt_flags, /* OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL */
- int compatible /* use Vi default value */
+ int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
+ int compatible // use Vi default value
)
{
- char_u *varp; /* pointer to variable for current option */
- int dvi; /* index in def_val[] */
+ char_u *varp; // pointer to variable for current option
+ int dvi; // index in def_val[]
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
uint32_t flags = options[opt_idx].flags;
- if (varp != NULL) { /* skip hidden option, nothing to do for it */
+ if (varp != NULL) { // skip hidden option, nothing to do for it
dvi = ((flags & P_VI_DEF) || compatible) ? VI_DEFAULT : VIM_DEFAULT;
if (flags & P_STRING) {
/* Use set_string_option_direct() for local options to handle
@@ -834,20 +870,22 @@ set_option_default (
*(long *)varp;
}
}
- } else { /* P_BOOL */
+ } else { // P_BOOL
*(int *)varp = (int)(intptr_t)options[opt_idx].def_val[dvi];
#ifdef UNIX
- /* 'modeline' defaults to off for root */
- if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID)
- *(int *)varp = FALSE;
+ // 'modeline' defaults to off for root
+ if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID) {
+ *(int *)varp = false;
+ }
#endif
- /* May also set global value for local option. */
- if (both)
+ // May also set global value for local option.
+ if (both) {
*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
*(int *)varp;
+ }
}
- /* The default value is not insecure. */
+ // The default value is not insecure.
uint32_t *flagsp = insecure_flag(opt_idx, opt_flags);
*flagsp = *flagsp & ~P_INSECURE;
}
@@ -858,9 +896,9 @@ set_option_default (
/*
* Set all options (except terminal options) to their default value.
*/
-static void
-set_options_default (
- int opt_flags /* OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL */
+static void
+set_options_default(
+ int opt_flags // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
)
{
for (int i = 0; options[i].fullname; i++) {
@@ -869,7 +907,7 @@ set_options_default (
}
}
- /* The 'scroll' option must be computed for all windows. */
+ // The 'scroll' option must be computed for all windows.
FOR_ALL_TAB_WINDOWS(tp, wp) {
win_comp_scroll(wp);
}
@@ -1056,8 +1094,9 @@ void set_helplang_default(const char *lang)
}
int idx = findoption("hlg");
if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
- if (options[idx].flags & P_ALLOCED)
+ if (options[idx].flags & P_ALLOCED) {
free_string_option(p_hlg);
+ }
p_hlg = (char_u *)xmemdupz(lang, lang_len);
// zh_CN becomes "cn", zh_TW becomes "tw".
if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) {
@@ -1102,24 +1141,21 @@ void set_title_defaults(void)
}
}
-/*
- * Parse 'arg' for option settings.
- *
- * 'arg' may be IObuff, but only when no errors can be present and option
- * does not need to be expanded with option_expand().
- * "opt_flags":
- * 0 for ":set"
- * OPT_GLOBAL for ":setglobal"
- * OPT_LOCAL for ":setlocal" and a modeline
- * OPT_MODELINE for a modeline
- * OPT_WINONLY to only set window-local options
- * OPT_NOWIN to skip setting window-local options
- *
- * returns FAIL if an error is detected, OK otherwise
- */
-int
-do_set (
- char_u *arg, /* option string (may be written to!) */
+// Parse 'arg' for option settings.
+//
+// 'arg' may be IObuff, but only when no errors can be present and option
+// does not need to be expanded with option_expand().
+// "opt_flags":
+// 0 for ":set"
+// OPT_GLOBAL for ":setglobal"
+// OPT_LOCAL for ":setlocal" and a modeline
+// OPT_MODELINE for a modeline
+// OPT_WINONLY to only set window-local options
+// OPT_NOWIN to skip setting window-local options
+//
+// returns FAIL if an error is detected, OK otherwise
+int do_set(
+ char_u *arg, // option string (may be written to!)
int opt_flags
)
{
@@ -1127,30 +1163,30 @@ do_set (
char_u *errmsg;
char_u errbuf[80];
char_u *startarg;
- int prefix; /* 1: nothing, 0: "no", 2: "inv" in front of name */
- char_u nextchar; /* next non-white char after option name */
- int afterchar; /* character just after option name */
+ int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name
+ char_u nextchar; // next non-white char after option name
+ int afterchar; // character just after option name
int len;
int i;
varnumber_T value;
int key;
- uint32_t flags; /* flags for current option */
- char_u *varp = NULL; /* pointer to variable for current option */
- int did_show = FALSE; /* already showed one value */
- int adding; /* "opt+=arg" */
- int prepending; /* "opt^=arg" */
- int removing; /* "opt-=arg" */
+ uint32_t flags; // flags for current option
+ char_u *varp = NULL; // pointer to variable for current option
+ int did_show = false; // already showed one value
+ int adding; // "opt+=arg"
+ int prepending; // "opt^=arg"
+ int removing; // "opt-=arg"
int cp_val = 0;
if (*arg == NUL) {
showoptions(0, opt_flags);
- did_show = TRUE;
+ did_show = true;
goto theend;
}
- while (*arg != NUL) { /* loop to process all options */
+ while (*arg != NUL) { // loop to process all options
errmsg = NULL;
- startarg = arg; /* remember for error message */
+ startarg = arg; // remember for error message
if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
&& !(opt_flags & OPT_MODELINE)) {
@@ -1169,7 +1205,7 @@ do_set (
redraw_all_later(CLEAR);
} else {
showoptions(1, opt_flags);
- did_show = TRUE;
+ did_show = true;
}
} else {
prefix = 1;
@@ -1181,17 +1217,18 @@ do_set (
arg += 3;
}
- /* find end of name */
+ // find end of name
key = 0;
if (*arg == '<') {
opt_idx = -1;
- /* look out for <t_>;> */
- if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4])
+ // look out for <t_>;>
+ if (arg[1] == 't' && arg[2] == '_' && arg[3] && arg[4]) {
len = 5;
- else {
+ } else {
len = 1;
- while (arg[len] != NUL && arg[len] != '>')
- ++len;
+ while (arg[len] != NUL && arg[len] != '>') {
+ len++;
+ }
}
if (arg[len] != '>') {
errmsg = e_invarg;
@@ -1220,43 +1257,45 @@ do_set (
}
}
- /* remember character after option name */
+ // remember character after option name
afterchar = arg[len];
- /* skip white space, allow ":set ai ?" */
- while (ascii_iswhite(arg[len]))
- ++len;
+ // skip white space, allow ":set ai ?"
+ while (ascii_iswhite(arg[len])) {
+ len++;
+ }
- adding = FALSE;
- prepending = FALSE;
- removing = FALSE;
+ adding = false;
+ prepending = false;
+ removing = false;
if (arg[len] != NUL && arg[len + 1] == '=') {
if (arg[len] == '+') {
- adding = TRUE; /* "+=" */
- ++len;
+ adding = true; // "+="
+ len++;
} else if (arg[len] == '^') {
- prepending = TRUE; /* "^=" */
- ++len;
+ prepending = true; // "^="
+ len++;
} else if (arg[len] == '-') {
- removing = TRUE; /* "-=" */
- ++len;
+ removing = true; // "-="
+ len++;
}
}
nextchar = arg[len];
- if (opt_idx == -1 && key == 0) { /* found a mismatch: skip */
+ if (opt_idx == -1 && key == 0) { // found a mismatch: skip
errmsg = (char_u *)N_("E518: Unknown option");
goto skip;
}
if (opt_idx >= 0) {
- if (options[opt_idx].var == NULL) { /* hidden option: skip */
- /* Only give an error message when requesting the value of
- * a hidden option, ignore setting it. */
+ if (options[opt_idx].var == NULL) { // hidden option: skip
+ // Only give an error message when requesting the value of
+ // a hidden option, ignore setting it.
if (vim_strchr((char_u *)"=:!&<", nextchar) == NULL
&& (!(options[opt_idx].flags & P_BOOL)
- || nextchar == '?'))
+ || nextchar == '?')) {
errmsg = (char_u *)_(e_unsupportedoption);
+ }
goto skip;
}
@@ -1272,28 +1311,30 @@ do_set (
&& (opt_idx < 0 || options[opt_idx].var != VAR_WIN))
goto skip;
- /* Skip all options that are window-local (used for :vimgrep). */
+ // Skip all options that are window-local (used for :vimgrep).
if ((opt_flags & OPT_NOWIN) && opt_idx >= 0
- && options[opt_idx].var == VAR_WIN)
+ && options[opt_idx].var == VAR_WIN) {
goto skip;
+ }
- /* Disallow changing some options from modelines. */
+ // Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
if (flags & (P_SECURE | P_NO_ML)) {
errmsg = (char_u *)_("E520: Not allowed in a modeline");
goto skip;
}
- /* In diff mode some options are overruled. This avoids that
- * 'foldmethod' becomes "marker" instead of "diff" and that
- * "wrap" gets set. */
+ // In diff mode some options are overruled. This avoids that
+ // 'foldmethod' becomes "marker" instead of "diff" and that
+ // "wrap" gets set.
if (curwin->w_p_diff
- && opt_idx >= 0 /* shut up coverity warning */
+ && opt_idx >= 0 // shut up coverity warning
&& (options[opt_idx].indir == PV_FDM
- || options[opt_idx].indir == PV_WRAP))
+ || options[opt_idx].indir == PV_WRAP)) {
goto skip;
+ }
}
- /* Disallow changing some options in the sandbox */
+ // Disallow changing some options in the sandbox
if (sandbox != 0 && (flags & P_SECURE)) {
errmsg = (char_u *)_(e_sandbox);
goto skip;
@@ -1303,11 +1344,11 @@ do_set (
arg += len;
cp_val = p_cp;
if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') {
- if (arg[3] == 'm') { /* "opt&vim": set to Vim default */
- cp_val = FALSE;
+ if (arg[3] == 'm') { // "opt&vim": set to Vim default
+ cp_val = false;
arg += 3;
- } else { /* "opt&vi": set to Vi default */
- cp_val = TRUE;
+ } else { // "opt&vi": set to Vi default
+ cp_val = true;
arg += 2;
}
}
@@ -1329,11 +1370,11 @@ do_set (
/*
* print value
*/
- if (did_show)
- msg_putchar('\n'); /* cursor below last one */
- else {
- gotocmdline(TRUE); /* cursor at status line */
- did_show = TRUE; /* remember that we did a line */
+ if (did_show) {
+ msg_putchar('\n'); // cursor below last one
+ } else {
+ gotocmdline(true); // cursor at status line
+ did_show = true; // remember that we did a line
}
if (opt_idx >= 0) {
showoneopt(&options[opt_idx], opt_flags);
@@ -1357,7 +1398,10 @@ do_set (
&& nextchar != NUL && !ascii_iswhite(afterchar))
errmsg = e_trailing;
} else {
- if (flags & P_BOOL) { /* boolean */
+ int value_is_replaced = !prepending && !adding && !removing;
+ int value_checked = false;
+
+ if (flags & P_BOOL) { // boolean
if (nextchar == '=' || nextchar == ':') {
errmsg = e_invarg;
goto skip;
@@ -1368,20 +1412,21 @@ do_set (
* ":set opt&": reset to default value
* ":set opt<": reset to global value
*/
- if (nextchar == '!')
+ if (nextchar == '!') {
value = *(int *)(varp) ^ 1;
- else if (nextchar == '&')
+ } else if (nextchar == '&') {
value = (int)(intptr_t)options[opt_idx].def_val[
- ((flags & P_VI_DEF) || cp_val)
- ? VI_DEFAULT : VIM_DEFAULT];
- else if (nextchar == '<') {
- /* For 'autoread' -1 means to use global value. */
+ ((flags & P_VI_DEF) || cp_val)
+ ? VI_DEFAULT : VIM_DEFAULT];
+ } else if (nextchar == '<') {
+ // For 'autoread' -1 means to use global value.
if ((int *)varp == &curbuf->b_p_ar
- && opt_flags == OPT_LOCAL)
+ && opt_flags == OPT_LOCAL) {
value = -1;
- else
+ } else {
value = *(int *)get_varp_scope(&(options[opt_idx]),
- OPT_GLOBAL);
+ OPT_GLOBAL);
+ }
} else {
/*
* ":set invopt": invert
@@ -1391,10 +1436,11 @@ do_set (
errmsg = e_trailing;
goto skip;
}
- if (prefix == 2) /* inv */
+ if (prefix == 2) { // inv
value = *(int *)(varp) ^ 1;
- else
+ } else {
value = prefix;
+ }
}
errmsg = (char_u *)set_bool_option(opt_idx, varp, (int)value,
@@ -1406,16 +1452,14 @@ do_set (
goto skip;
}
- if (flags & P_NUM) { /* numeric */
- /*
- * Different ways to set a number option:
- * & set to default value
- * < set to global value
- * <xx> accept special key codes for 'wildchar'
- * c accept any non-digit for 'wildchar'
- * [-]0-9 set number
- * other error
- */
+ if (flags & P_NUM) { // numeric
+ // Different ways to set a number option:
+ // & set to default value
+ // < set to global value
+ // <xx> accept special key codes for 'wildchar'
+ // c accept any non-digit for 'wildchar'
+ // [-]0-9 set number
+ // other error
arg++;
if (nextchar == '&') {
value = (long)(intptr_t)options[opt_idx].def_val[
@@ -1520,10 +1564,10 @@ do_set (
}
} else if (nextchar == '<') { // set to global val
newval = vim_strsave(*(char_u **)get_varp_scope(
- &(options[opt_idx]), OPT_GLOBAL));
- new_value_alloced = TRUE;
+ &(options[opt_idx]), OPT_GLOBAL));
+ new_value_alloced = true;
} else {
- ++arg; /* jump to after the '=' or ':' */
+ arg++; // jump to after the '=' or ':'
/*
* Set 'keywordprg' to ":help" if an empty
@@ -1571,18 +1615,24 @@ do_set (
&& ascii_isdigit(*arg)) {
*errbuf = NUL;
i = getdigits_int(&arg);
- if (i & 1)
+ if (i & 1) {
STRCAT(errbuf, "b,");
- if (i & 2)
+ }
+ if (i & 2) {
STRCAT(errbuf, "s,");
- if (i & 4)
+ }
+ if (i & 4) {
STRCAT(errbuf, "h,l,");
- if (i & 8)
+ }
+ if (i & 8) {
STRCAT(errbuf, "<,>,");
- if (i & 16)
+ }
+ if (i & 16) {
STRCAT(errbuf, "[,],");
- if (*errbuf != NUL) /* remove trailing , */
+ }
+ if (*errbuf != NUL) { // remove trailing ,
errbuf[STRLEN(errbuf) - 1] = NUL;
+ }
save_arg = arg;
arg = errbuf;
}
@@ -1593,7 +1643,7 @@ do_set (
else if ( *arg == '>'
&& (varp == (char_u *)&p_dir
|| varp == (char_u *)&p_bdir)) {
- ++arg;
+ arg++;
}
/*
@@ -1601,10 +1651,11 @@ do_set (
* Can't use set_string_option_direct(), because
* we need to remove the backslashes.
*/
- /* get a bit too much */
+ // get a bit too much
newlen = (unsigned)STRLEN(arg) + 1;
- if (adding || prepending || removing)
+ if (adding || prepending || removing) {
newlen += (unsigned)STRLEN(origval) + 1;
+ }
newval = xmalloc(newlen);
s = newval;
@@ -1626,10 +1677,10 @@ do_set (
&& arg[2] != '\\')))
#endif
)
- ++arg; /* remove backslash */
+ arg++; // remove backslash
if (has_mbyte
&& (i = (*mb_ptr2len)(arg)) > 1) {
- /* copy multibyte char */
+ // copy multibyte char
memmove(s, arg, (size_t)i);
arg += i;
s += i;
@@ -1649,8 +1700,9 @@ do_set (
if (s != NULL) {
xfree(newval);
newlen = (unsigned)STRLEN(s) + 1;
- if (adding || prepending || removing)
+ if (adding || prepending || removing) {
newlen += (unsigned)STRLEN(origval) + 1;
+ }
newval = xmalloc(newlen);
STRCPY(newval, s);
}
@@ -1658,11 +1710,11 @@ do_set (
/* locate newval[] in origval[] when removing it
* and when adding to avoid duplicates */
- i = 0; /* init for GCC */
+ i = 0; // init for GCC
if (removing || (flags & P_NODUP)) {
i = (int)STRLEN(newval);
bs = 0;
- for (s = origval; *s; ++s) {
+ for (s = origval; *s; s++) {
if ((!(flags & P_COMMA)
|| s == origval
|| (s[-1] == ',' && !(bs & 1)))
@@ -1685,8 +1737,8 @@ do_set (
// do not add if already there
if ((adding || prepending) && *s) {
- prepending = FALSE;
- adding = FALSE;
+ prepending = false;
+ adding = false;
STRCPY(newval, origval);
}
}
@@ -1712,8 +1764,9 @@ do_set (
i = (int)STRLEN(newval);
STRMOVE(newval + i + comma, origval);
}
- if (comma)
+ if (comma) {
newval[i] = ',';
+ }
}
/* Remove newval[] from origval[]. (Note: "i" has
@@ -1721,16 +1774,17 @@ do_set (
if (removing) {
STRCPY(newval, origval);
if (*s) {
- /* may need to remove a comma */
+ // may need to remove a comma
if (flags & P_COMMA) {
if (s == origval) {
- /* include comma after string */
- if (s[i] == ',')
- ++i;
+ // include comma after string
+ if (s[i] == ',') {
+ i++;
+ }
} else {
- /* include comma before string */
- --s;
- ++i;
+ // include comma before string
+ s--;
+ i++;
}
}
STRMOVE(newval + (s - origval), s + i);
@@ -1760,9 +1814,10 @@ do_set (
}
}
- if (save_arg != NULL) /* number for 'whichwrap' */
+ if (save_arg != NULL) { // number for 'whichwrap'
arg = save_arg;
- new_value_alloced = TRUE;
+ }
+ new_value_alloced = true;
}
// Set the new value.
@@ -1776,12 +1831,32 @@ do_set (
// buffer is closed by autocommands.
saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0;
- // Handle side effects, and set the global value for
- // ":set" on local options. Note: when setting 'syntax'
- // or 'filetype' autocommands may be triggered that can
- // cause havoc.
- errmsg = did_set_string_option(opt_idx, (char_u **)varp,
- new_value_alloced, oldval, errbuf, opt_flags);
+ {
+ uint32_t *p = insecure_flag(opt_idx, opt_flags);
+ const int secure_saved = secure;
+
+ // When an option is set in the sandbox, from a
+ // modeline or in secure mode, then deal with side
+ // effects in secure mode. Also when the value was
+ // set with the P_INSECURE flag and is not
+ // completely replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0
+ || (!value_is_replaced && (*p & P_INSECURE))) {
+ secure = 1;
+ }
+
+ // Handle side effects, and set the global value
+ // for ":set" on local options. Note: when setting
+ // 'syntax' or 'filetype' autocommands may be
+ // triggered that can cause havoc.
+ errmsg = did_set_string_option(opt_idx, (char_u **)varp,
+ new_value_alloced, oldval,
+ errbuf, sizeof(errbuf),
+ opt_flags, &value_checked);
+
+ secure = secure_saved;
+ }
if (errmsg == NULL) {
if (!starting) {
@@ -1807,9 +1882,9 @@ do_set (
}
}
- if (opt_idx >= 0)
- did_set_option(opt_idx, opt_flags,
- !prepending && !adding && !removing);
+ if (opt_idx >= 0) {
+ did_set_option(opt_idx, opt_flags, value_is_replaced, value_checked);
+ }
}
skip:
@@ -1819,13 +1894,16 @@ skip:
* - skip blanks
* - skip one "=val" argument (for hidden options ":set gfn =xx")
*/
- for (i = 0; i < 2; ++i) {
- while (*arg != NUL && !ascii_iswhite(*arg))
- if (*arg++ == '\\' && *arg != NUL)
- ++arg;
+ for (i = 0; i < 2; i++) {
+ while (*arg != NUL && !ascii_iswhite(*arg)) {
+ if (*arg++ == '\\' && *arg != NUL) {
+ arg++;
+ }
+ }
arg = skipwhite(arg);
- if (*arg != '=')
+ if (*arg != '=') {
break;
+ }
}
}
@@ -1833,18 +1911,18 @@ skip:
STRLCPY(IObuff, _(errmsg), IOSIZE);
i = (int)STRLEN(IObuff) + 2;
if (i + (arg - startarg) < IOSIZE) {
- /* append the argument with the error */
+ // append the argument with the error
STRCAT(IObuff, ": ");
assert(arg >= startarg);
memmove(IObuff + i, startarg, (size_t)(arg - startarg));
IObuff[i + (arg - startarg)] = NUL;
}
- /* make sure all characters are printable */
+ // make sure all characters are printable
trans_characters(IObuff, IOSIZE);
- ++no_wait_return; /* wait_return done later */
- emsg(IObuff); /* show error highlighted */
- --no_wait_return;
+ no_wait_return++; // wait_return done later
+ emsg(IObuff); // show error highlighted
+ no_wait_return--;
return FAIL;
}
@@ -1854,27 +1932,26 @@ skip:
theend:
if (silent_mode && did_show) {
- /* After displaying option values in silent mode. */
- silent_mode = FALSE;
- info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
+ // After displaying option values in silent mode.
+ silent_mode = false;
+ info_message = true; // use mch_msg(), not mch_errmsg()
msg_putchar('\n');
ui_flush();
- silent_mode = TRUE;
- info_message = FALSE; /* use mch_msg(), not mch_errmsg() */
+ silent_mode = true;
+ info_message = false; // use mch_msg(), not mch_errmsg()
}
return OK;
}
-/*
- * Call this when an option has been given a new value through a user command.
- * Sets the P_WAS_SET flag and takes care of the P_INSECURE flag.
- */
-static void
-did_set_option (
+// Call this when an option has been given a new value through a user command.
+// Sets the P_WAS_SET flag and takes care of the P_INSECURE flag.
+static void did_set_option(
int opt_idx,
- int opt_flags, /* possibly with OPT_MODELINE */
- int new_value /* value was replaced completely */
+ int opt_flags, // possibly with OPT_MODELINE
+ int new_value, // value was replaced completely
+ int value_checked // value was checked to be safe, no need to
+ // set P_INSECURE
)
{
options[opt_idx].flags |= P_WAS_SET;
@@ -1883,20 +1960,22 @@ did_set_option (
* set the P_INSECURE flag. Otherwise, if a new value is stored reset the
* flag. */
uint32_t *p = insecure_flag(opt_idx, opt_flags);
- if (secure
- || sandbox != 0
- || (opt_flags & OPT_MODELINE))
+ if (!value_checked && (secure
+ || sandbox != 0
+ || (opt_flags & OPT_MODELINE))) {
*p = *p | P_INSECURE;
- else if (new_value)
+ } else if (new_value) {
*p = *p & ~P_INSECURE;
+ }
}
-static char_u *illegal_char(char_u *errbuf, int c)
+static char_u *illegal_char(char_u *errbuf, size_t errbuflen, int c)
{
- if (errbuf == NULL)
+ if (errbuf == NULL) {
return (char_u *)"";
- sprintf((char *)errbuf, _("E539: Illegal character <%s>"),
- (char *)transchar(c));
+ }
+ vim_snprintf((char *)errbuf, errbuflen, _("E539: Illegal character <%s>"),
+ (char *)transchar(c));
return errbuf;
}
@@ -1906,10 +1985,12 @@ static char_u *illegal_char(char_u *errbuf, int c)
*/
static int string_to_key(char_u *arg)
{
- if (*arg == '<')
+ if (*arg == '<') {
return find_key_option(arg + 1);
- if (*arg == '^')
+ }
+ if (*arg == '^') {
return Ctrl_chr(arg[1]);
+ }
return *arg;
}
@@ -1921,26 +2002,24 @@ static char_u *check_cedit(void)
{
int n;
- if (*p_cedit == NUL)
+ if (*p_cedit == NUL) {
cedit_key = -1;
- else {
+ } else {
n = string_to_key(p_cedit);
- if (vim_isprintc(n))
+ if (vim_isprintc(n)) {
return e_invarg;
+ }
cedit_key = n;
}
return NULL;
}
-/*
- * When changing 'title', 'titlestring', 'icon' or 'iconstring', call
- * maketitle() to create and display it.
- * When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
- * the old value back.
- */
-static void
-did_set_title (
- int icon /* Did set icon instead of title */
+// When changing 'title', 'titlestring', 'icon' or 'iconstring', call
+// maketitle() to create and display it.
+// When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
+// the old value back.
+static void did_set_title(
+ int icon // Did set icon instead of title
)
{
if (starting != NO_SCREEN) {
@@ -1949,14 +2028,11 @@ did_set_title (
}
}
-/*
- * set_options_bin - called when 'bin' changes value.
- */
-void
-set_options_bin (
+// set_options_bin - called when 'bin' changes value.
+void set_options_bin(
int oldval,
int newval,
- int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
+ int opt_flags // OPT_LOCAL and/or OPT_GLOBAL
)
{
/*
@@ -1964,7 +2040,7 @@ set_options_bin (
* copied when 'bin is set and restored when 'bin' is reset.
*/
if (newval) {
- if (!oldval) { /* switched on */
+ if (!oldval) { // switched on
if (!(opt_flags & OPT_GLOBAL)) {
curbuf->b_p_tw_nobin = curbuf->b_p_tw;
curbuf->b_p_wm_nobin = curbuf->b_p_wm;
@@ -1980,19 +2056,19 @@ set_options_bin (
}
if (!(opt_flags & OPT_GLOBAL)) {
- curbuf->b_p_tw = 0; /* no automatic line wrap */
- curbuf->b_p_wm = 0; /* no automatic line wrap */
- curbuf->b_p_ml = 0; /* no modelines */
- curbuf->b_p_et = 0; /* no expandtab */
+ curbuf->b_p_tw = 0; // no automatic line wrap
+ curbuf->b_p_wm = 0; // no automatic line wrap
+ curbuf->b_p_ml = 0; // no modelines
+ curbuf->b_p_et = 0; // no expandtab
}
if (!(opt_flags & OPT_LOCAL)) {
p_tw = 0;
p_wm = 0;
- p_ml = FALSE;
- p_et = FALSE;
- p_bin = TRUE; /* needed when called for the "-b" argument */
+ p_ml = false;
+ p_et = false;
+ p_bin = true; // needed when called for the "-b" argument
}
- } else if (oldval) { /* switched off */
+ } else if (oldval) { // switched off
if (!(opt_flags & OPT_GLOBAL)) {
curbuf->b_p_tw = curbuf->b_p_tw_nobin;
curbuf->b_p_wm = curbuf->b_p_wm_nobin;
@@ -2020,8 +2096,9 @@ int get_shada_parameter(int type)
char_u *p;
p = find_shada_parameter(type);
- if (p != NULL && ascii_isdigit(*p))
+ if (p != NULL && ascii_isdigit(*p)) {
return atoi((char *)p);
+ }
return -1;
}
@@ -2034,14 +2111,17 @@ char_u *find_shada_parameter(int type)
{
char_u *p;
- for (p = p_shada; *p; ++p) {
- if (*p == type)
+ for (p = p_shada; *p; p++) {
+ if (*p == type) {
return p + 1;
- if (*p == 'n') /* 'n' is always the last one */
+ }
+ if (*p == 'n') { // 'n' is always the last one
break;
- p = vim_strchr(p, ','); /* skip until next ',' */
- if (p == NULL) /* hit the end without finding parameter */
+ }
+ p = vim_strchr(p, ','); // skip until next ','
+ if (p == NULL) { // hit the end without finding parameter
break;
+ }
}
return NULL;
}
@@ -2054,9 +2134,10 @@ char_u *find_shada_parameter(int type)
*/
static char_u *option_expand(int opt_idx, char_u *val)
{
- /* if option doesn't need expansion nothing to do */
- if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL)
+ // if option doesn't need expansion nothing to do
+ if (!(options[opt_idx].flags & P_EXPAND) || options[opt_idx].var == NULL) {
return NULL;
+ }
if (val == NULL) {
val = *(char_u **)options[opt_idx].var;
@@ -2075,11 +2156,12 @@ static char_u *option_expand(int opt_idx, char_u *val)
* For 'spellsuggest' expand after "file:".
*/
expand_env_esc(val, NameBuff, MAXPATHL,
- (char_u **)options[opt_idx].var == &p_tags, FALSE,
- (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
- NULL);
- if (STRCMP(NameBuff, val) == 0) /* they are the same */
+ (char_u **)options[opt_idx].var == &p_tags, false,
+ (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
+ NULL);
+ if (STRCMP(NameBuff, val) == 0) { // they are the same
return NULL;
+ }
return NameBuff;
}
@@ -2090,7 +2172,7 @@ static char_u *option_expand(int opt_idx, char_u *val)
*/
static void didset_options(void)
{
- /* initialize the table for 'iskeyword' et.al. */
+ // initialize the table for 'iskeyword' et.al.
(void)init_chartab();
(void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
@@ -2139,9 +2221,11 @@ void check_options(void)
{
int opt_idx;
- for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++)
- if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL)
+ for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) {
+ if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) {
check_string_option((char_u **)get_varp(&(options[opt_idx])));
+ }
+ }
}
/*
@@ -2207,28 +2291,29 @@ void check_buf_options(buf_T *buf)
*/
void free_string_option(char_u *p)
{
- if (p != empty_option)
+ if (p != empty_option) {
xfree(p);
+ }
}
void clear_string_option(char_u **pp)
{
- if (*pp != empty_option)
+ if (*pp != empty_option) {
xfree(*pp);
+ }
*pp = empty_option;
}
static void check_string_option(char_u **pp)
{
- if (*pp == NULL)
+ if (*pp == NULL) {
*pp = empty_option;
+ }
}
-/*
- * Return TRUE when option "opt" was set from a modeline or in secure mode.
- * Return FALSE when it wasn't.
- * Return -1 for an unknown option.
- */
+/// Return true when option "opt" was set from a modeline or in secure mode.
+/// Return false when it wasn't.
+/// Return -1 for an unknown option.
int was_set_insecurely(char_u *opt, int opt_flags)
{
int idx = findoption((const char *)opt);
@@ -2257,7 +2342,7 @@ static uint32_t *insecure_flag(int opt_idx, int opt_flags)
case PV_INEX: return &curbuf->b_p_inex_flags;
}
- /* Nothing special, return global flags field. */
+ // Nothing special, return global flags field.
return &options[opt_idx].flags;
}
@@ -2265,9 +2350,10 @@ static uint32_t *insecure_flag(int opt_idx, int opt_flags)
/*
* Redraw the window title and/or tab page text later.
*/
-static void redraw_titles(void) {
- need_maketitle = TRUE;
- redraw_tabline = TRUE;
+static void redraw_titles(void)
+{
+ need_maketitle = true;
+ redraw_tabline = true;
}
static int shada_idx = -1;
@@ -2279,12 +2365,12 @@ static int shada_idx = -1;
* When "set_sid" is zero set the scriptID to current_SID. When "set_sid" is
* SID_NONE don't set the scriptID. Otherwise set the scriptID to "set_sid".
*/
-void
-set_string_option_direct (
+void
+set_string_option_direct(
char_u *name,
int opt_idx,
char_u *val,
- int opt_flags, /* OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL */
+ int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
int set_sid
)
{
@@ -2302,22 +2388,25 @@ set_string_option_direct (
}
}
- if (options[idx].var == NULL) /* can't set hidden option */
+ if (options[idx].var == NULL) { // can't set hidden option
return;
+ }
assert((void *) options[idx].var != (void *) &p_shada);
s = vim_strsave(val);
{
varp = (char_u **)get_varp_scope(&(options[idx]),
- both ? OPT_LOCAL : opt_flags);
- if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED))
+ both ? OPT_LOCAL : opt_flags);
+ if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED)) {
free_string_option(*varp);
+ }
*varp = s;
- /* For buffer/window local option may also set the global value. */
- if (both)
+ // For buffer/window local option may also set the global value.
+ if (both) {
set_string_option_global(idx, varp);
+ }
options[idx].flags |= P_ALLOCED;
@@ -2336,19 +2425,20 @@ set_string_option_direct (
/*
* Set global value for string option when it's a local option.
*/
-static void
-set_string_option_global (
- int opt_idx, /* option index */
- char_u **varp /* pointer to option variable */
+static void
+set_string_option_global(
+ int opt_idx, // option index
+ char_u **varp // pointer to option variable
)
{
char_u **p, *s;
- /* the global value is always allocated */
- if (options[opt_idx].var == VAR_WIN)
+ // the global value is always allocated
+ if (options[opt_idx].var == VAR_WIN) {
p = (char_u **)GLOBAL_WO(varp);
- else
+ } else {
p = (char_u **)options[opt_idx].var;
+ }
if (options[opt_idx].indir != PV_NONE && p != varp) {
s = vim_strsave(*varp);
free_string_option(*p);
@@ -2385,10 +2475,12 @@ static char *set_string_option(const int opt_idx, const char *const value,
char *const saved_oldval = xstrdup(oldval);
char *const saved_newval = xstrdup(s);
+ int value_checked = false;
char *const r = (char *)did_set_string_option(
- opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags);
+ opt_idx, (char_u **)varp, (int)true, (char_u *)oldval,
+ NULL, 0, opt_flags, &value_checked);
if (r == NULL) {
- did_set_option(opt_idx, opt_flags, true);
+ did_set_option(opt_idx, opt_flags, true, value_checked);
}
// call autocommand after handling side effects
@@ -2419,28 +2511,24 @@ static bool valid_filetype(char_u *val)
return true;
}
-#ifdef _MSC_VER
-// MSVC optimizations are disabled for this function because it
-// incorrectly generates an empty string for SHM_ALL.
-#pragma optimize("", off)
-#endif
-/*
- * Handle string options that need some action to perform when changed.
- * Returns NULL for success, or an error message for an error.
- */
+/// Handle string options that need some action to perform when changed.
+/// Returns NULL for success, or an error message for an error.
static char_u *
-did_set_string_option (
- int opt_idx, /* index in options[] table */
- char_u **varp, /* pointer to the option variable */
- int new_value_alloced, /* new value was allocated */
- char_u *oldval, /* previous value of the option */
- char_u *errbuf, /* buffer for errors, or NULL */
- int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
+did_set_string_option(
+ int opt_idx, // index in options[] table
+ char_u **varp, // pointer to the option variable
+ int new_value_alloced, // new value was allocated
+ char_u *oldval, // previous value of the option
+ char_u *errbuf, // buffer for errors, or NULL
+ size_t errbuflen, // length of errors buffer
+ int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL
+ int *value_checked // value was checked to be safe, no
+ // need to set P_INSECURE
)
{
char_u *errmsg = NULL;
char_u *s, *p;
- int did_chartab = FALSE;
+ int did_chartab = false;
char_u **gvarp;
bool free_oldval = (options[opt_idx].flags & P_ALLOCED);
bool value_changed = false;
@@ -2449,7 +2537,7 @@ did_set_string_option (
* two values for all local options. */
gvarp = (char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- /* Disallow changing some options from secure mode */
+ // Disallow changing some options from secure mode
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
errmsg = e_secure;
@@ -2462,9 +2550,7 @@ did_set_string_option (
// path separator (slash and/or backslash), wildcards and characters that
// are often illegal in a file name. Be more permissive if "secure" is off.
errmsg = e_invarg;
- }
- /* 'backupcopy' */
- else if (gvarp == &p_bkc) {
+ } else if (gvarp == &p_bkc) { // 'backupcopy'
char_u *bkc = p_bkc;
unsigned int *flags = &bkc_flags;
@@ -2489,17 +2575,15 @@ did_set_string_option (
errmsg = e_invarg;
}
}
- }
- /* 'backupext' and 'patchmode' */
- else if (varp == &p_bex || varp == &p_pm) {
+ } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
- *p_pm == '.' ? p_pm + 1 : p_pm) == 0)
+ *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
errmsg = (char_u *)N_("E589: 'backupext' and 'patchmode' are equal");
- }
- /* 'breakindentopt' */
- else if (varp == &curwin->w_p_briopt) {
- if (briopt_check(curwin) == FAIL)
+ }
+ } else if (varp == &curwin->w_p_briopt) { // 'breakindentopt'
+ if (briopt_check(curwin) == FAIL) {
errmsg = e_invarg;
+ }
} else if (varp == &p_isi
|| varp == &(curbuf->b_p_isk)
|| varp == &p_isp
@@ -2508,63 +2592,58 @@ did_set_string_option (
// If the new option is invalid, use old value. 'lisp' option: refill
// g_chartab[] for '-' char
if (init_chartab() == FAIL) {
- did_chartab = TRUE; /* need to restore it below */
- errmsg = e_invarg; /* error in value */
+ did_chartab = true; // need to restore it below
+ errmsg = e_invarg; // error in value
}
- }
- /* 'helpfile' */
- else if (varp == &p_hf) {
- /* May compute new values for $VIM and $VIMRUNTIME */
+ } else if (varp == &p_hf) { // 'helpfile'
+ // May compute new values for $VIM and $VIMRUNTIME
if (didset_vim) {
vim_setenv("VIM", "");
- didset_vim = FALSE;
+ didset_vim = false;
}
if (didset_vimruntime) {
vim_setenv("VIMRUNTIME", "");
- didset_vimruntime = FALSE;
+ didset_vimruntime = false;
}
- }
- /* 'colorcolumn' */
- else if (varp == &curwin->w_p_cc)
+ } else if (varp == &curwin->w_p_cc) { // 'colorcolumn'
errmsg = check_colorcolumn(curwin);
-
- /* 'helplang' */
- else if (varp == &p_hlg) {
- /* Check for "", "ab", "ab,cd", etc. */
+ } else if (varp == &p_hlg) { // 'helplang'
+ // Check for "", "ab", "ab,cd", etc.
for (s = p_hlg; *s != NUL; s += 3) {
if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
errmsg = e_invarg;
break;
}
- if (s[2] == NUL)
+ if (s[2] == NUL) {
break;
+ }
}
} else if (varp == &p_hl) {
// 'highlight'
if (strcmp((char *)(*varp), HIGHLIGHT_INIT) != 0) {
errmsg = e_unsupportedoption;
}
- }
- /* 'nrformats' */
- else if (gvarp == &p_nf) {
- if (check_opt_strings(*varp, p_nf_values, TRUE) != OK)
+ } else if (gvarp == &p_nf) { // 'nrformats'
+ if (check_opt_strings(*varp, p_nf_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_ssop) { // 'sessionoptions'
- if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK)
+ if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
errmsg = e_invarg;
+ }
if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
- /* Don't allow both "sesdir" and "curdir". */
+ // Don't allow both "sesdir" and "curdir".
(void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
errmsg = e_invarg;
}
} else if (varp == &p_vop) { // 'viewoptions'
- if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK)
+ if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'scrollopt' */
- else if (varp == &p_sbo) {
- if (check_opt_strings(p_sbo, p_scbopt_values, TRUE) != OK)
+ }
+ } else if (varp == &p_sbo) { // 'scrollopt'
+ if (check_opt_strings(p_sbo, p_scbopt_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_ambw || (int *)varp == &p_emoji) {
// 'ambiwidth'
if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
@@ -2583,13 +2662,11 @@ did_set_string_option (
ambw_end:
{} // clint prefers {} over ; as an empty statement
}
- }
- /* 'background' */
- else if (varp == &p_bg) {
- if (check_opt_strings(p_bg, p_bg_values, FALSE) == OK) {
+ } else if (varp == &p_bg) { // 'background'
+ if (check_opt_strings(p_bg, p_bg_values, false) == OK) {
int dark = (*p_bg == 'd');
- init_highlight(FALSE, FALSE);
+ init_highlight(false, false);
if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
// The color scheme must have set 'background' back to another
@@ -2599,31 +2676,28 @@ ambw_end:
free_string_option(p_bg);
p_bg = vim_strsave((char_u *)(dark ? "dark" : "light"));
check_string_option(&p_bg);
- init_highlight(FALSE, FALSE);
+ init_highlight(false, false);
}
} else
errmsg = e_invarg;
- }
- /* 'wildmode' */
- else if (varp == &p_wim) {
- if (check_opt_wim() == FAIL)
+ } else if (varp == &p_wim) { // 'wildmode'
+ if (check_opt_wim() == FAIL) {
errmsg = e_invarg;
- }
- /* 'wildoptions' */
- else if (varp == &p_wop) {
- if (check_opt_strings(p_wop, p_wop_values, TRUE) != OK)
+ }
+ // 'wildoptions'
+ } else if (varp == &p_wop) {
+ if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'winaltkeys' */
- else if (varp == &p_wak) {
+ }
+ } else if (varp == &p_wak) { // 'winaltkeys'
if (*p_wak == NUL
- || check_opt_strings(p_wak, p_wak_values, FALSE) != OK)
+ || check_opt_strings(p_wak, p_wak_values, false) != OK) {
errmsg = e_invarg;
- }
- /* 'eventignore' */
- else if (varp == &p_ei) {
- if (check_ei() == FAIL)
+ }
+ } else if (varp == &p_ei) { // 'eventignore'
+ if (check_ei() == FAIL) {
errmsg = e_invarg;
+ }
// 'encoding', 'fileencoding' and 'makeencoding'
} else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
if (gvarp == &p_fenc) {
@@ -2642,7 +2716,7 @@ ambw_end:
}
if (errmsg == NULL) {
- /* canonize the value, so that STRCMP() can be used on it */
+ // canonize the value, so that STRCMP() can be used on it
p = enc_canonize(*varp);
xfree(*varp);
*varp = p;
@@ -2654,7 +2728,7 @@ ambw_end:
}
}
} else if (varp == &p_penc) {
- /* Canonize printencoding if VIM standard one */
+ // Canonize printencoding if VIM standard one
p = enc_canonize(p_penc);
xfree(p_penc);
p_penc = p;
@@ -2662,22 +2736,37 @@ ambw_end:
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'keymap' has
+ // been checked to be safe.
+ secure = 0;
+
// load or unload key mapping tables
errmsg = keymap_init();
+
+ secure = secure_save;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
}
if (errmsg == NULL) {
if (*curbuf->b_p_keymap != NUL) {
- /* Installed a new keymap, switch on using it. */
+ // Installed a new keymap, switch on using it.
curbuf->b_p_iminsert = B_IMODE_LMAP;
- if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT)
+ if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT) {
curbuf->b_p_imsearch = B_IMODE_LMAP;
+ }
} else {
- /* Cleared the keymap, may reset 'iminsert' and 'imsearch'. */
- if (curbuf->b_p_iminsert == B_IMODE_LMAP)
+ // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
+ if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
curbuf->b_p_iminsert = B_IMODE_NONE;
- if (curbuf->b_p_imsearch == B_IMODE_LMAP)
+ }
+ if (curbuf->b_p_imsearch == B_IMODE_LMAP) {
curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
+ }
}
if ((opt_flags & OPT_LOCAL) == 0) {
set_iminsert_global();
@@ -2685,41 +2774,37 @@ ambw_end:
}
status_redraw_curbuf();
}
- }
- /* 'fileformat' */
- else if (gvarp == &p_ff) {
- if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL))
+ } else if (gvarp == &p_ff) { // 'fileformat'
+ if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL)) {
errmsg = e_modifiable;
- else if (check_opt_strings(*varp, p_ff_values, FALSE) != OK)
+ } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
errmsg = e_invarg;
- else {
+ } else {
redraw_titles();
- /* update flag in swap file */
+ // update flag in swap file
ml_setflags(curbuf);
/* Redraw needed when switching to/from "mac": a CR in the text
* will be displayed differently. */
- if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm')
+ if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm') {
redraw_curbuf_later(NOT_VALID);
+ }
}
- }
- /* 'fileformats' */
- else if (varp == &p_ffs) {
- if (check_opt_strings(p_ffs, p_ff_values, TRUE) != OK) {
+ } else if (varp == &p_ffs) { // 'fileformats'
+ if (check_opt_strings(p_ffs, p_ff_values, true) != OK) {
errmsg = e_invarg;
}
- }
-
- /* 'matchpairs' */
- else if (gvarp == &p_mps) {
+ } else if (gvarp == &p_mps) { // 'matchpairs'
if (has_mbyte) {
- for (p = *varp; *p != NUL; ++p) {
+ for (p = *varp; *p != NUL; p++) {
int x2 = -1;
int x3 = -1;
- if (*p != NUL)
+ if (*p != NUL) {
p += mb_ptr2len(p);
- if (*p != NUL)
+ }
+ if (*p != NUL) {
x2 = *p++;
+ }
if (*p != NUL) {
x3 = utf_ptr2char(p);
p += mb_ptr2len(p);
@@ -2728,42 +2813,45 @@ ambw_end:
errmsg = e_invarg;
break;
}
- if (*p == NUL)
+ if (*p == NUL) {
break;
+ }
}
} else {
- /* Check for "x:y,x:y" */
+ // Check for "x:y,x:y"
for (p = *varp; *p != NUL; p += 4) {
if (p[1] != ':' || p[2] == NUL || (p[3] != NUL && p[3] != ',')) {
errmsg = e_invarg;
break;
}
- if (p[3] == NUL)
+ if (p[3] == NUL) {
break;
+ }
}
}
- }
- /* 'comments' */
- else if (gvarp == &p_com) {
+ } else if (gvarp == &p_com) { // 'comments'
for (s = *varp; *s; ) {
while (*s && *s != ':') {
if (vim_strchr((char_u *)COM_ALL, *s) == NULL
&& !ascii_isdigit(*s) && *s != '-') {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
- ++s;
+ s++;
}
- if (*s++ == NUL)
+ if (*s++ == NUL) {
errmsg = (char_u *)N_("E524: Missing colon");
- else if (*s == ',' || *s == NUL)
+ } else if (*s == ',' || *s == NUL) {
errmsg = (char_u *)N_("E525: Zero length string");
- if (errmsg != NULL)
+ }
+ if (errmsg != NULL) {
break;
+ }
while (*s && *s != ',') {
- if (*s == '\\' && s[1] != NUL)
- ++s;
- ++s;
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
+ }
+ s++;
}
s = skip_to_option_part(s);
}
@@ -2771,17 +2859,14 @@ ambw_end:
errmsg = set_chars_option(curwin, varp);
} else if (varp == &curwin->w_p_fcs) { // 'fillchars'
errmsg = set_chars_option(curwin, varp);
- }
- /* 'cedit' */
- else if (varp == &p_cedit) {
+ } else if (varp == &p_cedit) { // 'cedit'
errmsg = check_cedit();
- }
- /* 'verbosefile' */
- else if (varp == &p_vfile) {
+ } else if (varp == &p_vfile) { // 'verbosefile'
verbose_stop();
- if (*p_vfile != NUL && verbose_open() == FAIL)
+ if (*p_vfile != NUL && verbose_open() == FAIL) {
errmsg = e_invarg;
- /* 'shada' */
+ }
+ // 'shada'
} else if (varp == &p_shada) {
// TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
// option.
@@ -2795,133 +2880,115 @@ ambw_end:
// of the function and the set of P_ALLOCED at the end of the fuction.
free_oldval = (options[opt_idx].flags & P_ALLOCED);
for (s = p_shada; *s; ) {
- /* Check it's a valid character */
+ // Check it's a valid character
if (vim_strchr((char_u *)"!\"%'/:<@cfhnrs", *s) == NULL) {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
- if (*s == 'n') { /* name is always last one */
+ if (*s == 'n') { // name is always last one
break;
- } else if (*s == 'r') { /* skip until next ',' */
- while (*++s && *s != ',')
- ;
+ } else if (*s == 'r') { // skip until next ','
+ while (*++s && *s != ',') {}
} else if (*s == '%') {
- /* optional number */
- while (ascii_isdigit(*++s))
- ;
- } else if (*s == '!' || *s == 'h' || *s == 'c')
- ++s; /* no extra chars */
- else { /* must have a number */
- while (ascii_isdigit(*++s))
- ;
+ // optional number
+ while (ascii_isdigit(*++s)) {}
+ } else if (*s == '!' || *s == 'h' || *s == 'c') {
+ s++; // no extra chars
+ } else { // must have a number
+ while (ascii_isdigit(*++s)) {}
if (!ascii_isdigit(*(s - 1))) {
if (errbuf != NULL) {
- sprintf((char *)errbuf,
- _("E526: Missing number after <%s>"),
- transchar_byte(*(s - 1)));
+ vim_snprintf((char *)errbuf, errbuflen,
+ _("E526: Missing number after <%s>"),
+ transchar_byte(*(s - 1)));
errmsg = errbuf;
} else
errmsg = (char_u *)"";
break;
}
}
- if (*s == ',')
- ++s;
- else if (*s) {
- if (errbuf != NULL)
+ if (*s == ',') {
+ s++;
+ } else if (*s) {
+ if (errbuf != NULL) {
errmsg = (char_u *)N_("E527: Missing comma");
- else
+ } else {
errmsg = (char_u *)"";
+ }
break;
}
}
- if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0)
+ if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) {
errmsg = (char_u *)N_("E528: Must specify a ' value");
- }
- /* 'showbreak' */
- else if (varp == &p_sbr) {
+ }
+ } else if (varp == &p_sbr) { // 'showbreak'
for (s = p_sbr; *s; ) {
- if (ptr2cells(s) != 1)
+ if (ptr2cells(s) != 1) {
errmsg = (char_u *)N_("E595: contains unprintable or wide character");
+ }
MB_PTR_ADV(s);
}
- }
-
- // 'guicursor'
- else if (varp == &p_guicursor) {
+ } else if (varp == &p_guicursor) { // 'guicursor'
errmsg = parse_shape_opt(SHAPE_CURSOR);
- }
-
- else if (varp == &p_popt)
+ } else if (varp == &p_popt) {
errmsg = parse_printoptions();
- else if (varp == &p_pmfn)
+ } else if (varp == &p_pmfn) {
errmsg = parse_printmbfont();
-
- /* 'langmap' */
- else if (varp == &p_langmap)
+ } else if (varp == &p_langmap) { // 'langmap'
langmap_set();
-
- /* 'breakat' */
- else if (varp == &p_breakat)
+ } else if (varp == &p_breakat) { // 'breakat'
fill_breakat_flags();
-
- /* 'titlestring' and 'iconstring' */
- else if (varp == &p_titlestring || varp == &p_iconstring) {
+ } else if (varp == &p_titlestring || varp == &p_iconstring) {
+ // 'titlestring' and 'iconstring'
int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
- /* NULL => statusline syntax */
- if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL)
+ // NULL => statusline syntax
+ if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) {
stl_syntax |= flagval;
- else
+ } else {
stl_syntax &= ~flagval;
+ }
did_set_title(varp == &p_iconstring);
- }
-
- /* 'selection' */
- else if (varp == &p_sel) {
+ } else if (varp == &p_sel) { // 'selection'
if (*p_sel == NUL
- || check_opt_strings(p_sel, p_sel_values, FALSE) != OK)
+ || check_opt_strings(p_sel, p_sel_values, false) != OK) {
errmsg = e_invarg;
- }
- /* 'selectmode' */
- else if (varp == &p_slm) {
- if (check_opt_strings(p_slm, p_slm_values, TRUE) != OK)
+ }
+ } else if (varp == &p_slm) { // 'selectmode'
+ if (check_opt_strings(p_slm, p_slm_values, true) != OK) {
errmsg = e_invarg;
- }
- /* 'keymodel' */
- else if (varp == &p_km) {
- if (check_opt_strings(p_km, p_km_values, TRUE) != OK)
+ }
+ } else if (varp == &p_km) { // 'keymodel'
+ if (check_opt_strings(p_km, p_km_values, true) != OK) {
errmsg = e_invarg;
- else {
+ } else {
km_stopsel = (vim_strchr(p_km, 'o') != NULL);
km_startsel = (vim_strchr(p_km, 'a') != NULL);
}
- }
- /* 'mousemodel' */
- else if (varp == &p_mousem) {
- if (check_opt_strings(p_mousem, p_mousem_values, FALSE) != OK)
+ } else if (varp == &p_mousem) { // 'mousemodel'
+ if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_swb) { // 'switchbuf'
- if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK)
+ if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'debug' */
- else if (varp == &p_debug) {
- if (check_opt_strings(p_debug, p_debug_values, TRUE) != OK)
+ }
+ } else if (varp == &p_debug) { // 'debug'
+ if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_dy) { // 'display'
- if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK)
+ if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
errmsg = e_invarg;
- else
+ } else {
(void)init_chartab();
-
- }
- /* 'eadirection' */
- else if (varp == &p_ead) {
- if (check_opt_strings(p_ead, p_ead_values, FALSE) != OK)
+ }
+ } else if (varp == &p_ead) { // 'eadirection'
+ if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_cb) { // 'clipboard'
if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, true) != OK) {
errmsg = e_invarg;
@@ -2931,78 +2998,77 @@ ambw_end:
// When 'spelllang' or 'spellfile' is set and there is a window for this
// buffer in which 'spell' is set load the wordlists.
errmsg = did_set_spell_option(varp == &(curwin->w_s->b_p_spf));
- }
- /* When 'spellcapcheck' is set compile the regexp program. */
- else if (varp == &(curwin->w_s->b_p_spc)) {
+ } else if (varp == &(curwin->w_s->b_p_spc)) {
+ // When 'spellcapcheck' is set compile the regexp program.
errmsg = compile_cap_prog(curwin->w_s);
- }
- /* 'spellsuggest' */
- else if (varp == &p_sps) {
- if (spell_check_sps() != OK)
+ } else if (varp == &p_sps) { // 'spellsuggest'
+ if (spell_check_sps() != OK) {
errmsg = e_invarg;
- }
- /* 'mkspellmem' */
- else if (varp == &p_msm) {
- if (spell_check_msm() != OK)
+ }
+ } else if (varp == &p_msm) { // 'mkspellmem'
+ if (spell_check_msm() != OK) {
errmsg = e_invarg;
- }
- /* When 'bufhidden' is set, check for valid value. */
- else if (gvarp == &p_bh) {
- if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, FALSE) != OK)
+ }
+ } else if (gvarp == &p_bh) {
+ // When 'bufhidden' is set, check for valid value.
+ if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, false) != OK) {
errmsg = e_invarg;
- }
- /* When 'buftype' is set, check for valid value. */
- else if (gvarp == &p_bt) {
+ }
+ } else if (gvarp == &p_bt) {
+ // When 'buftype' is set, check for valid value.
if ((curbuf->terminal && curbuf->b_p_bt[0] != 't')
|| (!curbuf->terminal && curbuf->b_p_bt[0] == 't')
- || check_opt_strings(curbuf->b_p_bt, p_buftype_values, FALSE) != OK) {
+ || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
errmsg = e_invarg;
} else {
if (curwin->w_status_height) {
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
redraw_later(VALID);
}
curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
redraw_titles();
}
- }
- /* 'statusline' or 'rulerformat' */
- else if (gvarp == &p_stl || varp == &p_ruf) {
+ } else if (gvarp == &p_stl || varp == &p_ruf) {
+ // 'statusline' or 'rulerformat'
int wid;
- if (varp == &p_ruf) /* reset ru_wid first */
+ if (varp == &p_ruf) { // reset ru_wid first
ru_wid = 0;
+ }
s = *varp;
if (varp == &p_ruf && *s == '%') {
- /* set ru_wid if 'ruf' starts with "%99(" */
- if (*++s == '-') /* ignore a '-' */
+ // set ru_wid if 'ruf' starts with "%99("
+ if (*++s == '-') { // ignore a '-'
s++;
+ }
wid = getdigits_int(&s);
- if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL)
+ if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
ru_wid = wid;
- else
+ } else {
errmsg = check_stl_option(p_ruf);
- }
- /* check 'statusline' only if it doesn't start with "%!" */
- else if (varp == &p_ruf || s[0] != '%' || s[1] != '!')
+ }
+ } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
+ // check 'statusline' only if it doesn't start with "%!"
errmsg = check_stl_option(s);
- if (varp == &p_ruf && errmsg == NULL)
+ }
+ if (varp == &p_ruf && errmsg == NULL) {
comp_col();
- }
- /* check if it is a valid value for 'complete' -- Acevedo */
- else if (gvarp == &p_cpt) {
+ }
+ } else if (gvarp == &p_cpt) {
+ // check if it is a valid value for 'complete' -- Acevedo
for (s = *varp; *s; ) {
while (*s == ',' || *s == ' ')
s++;
- if (!*s)
+ if (!*s) {
break;
+ }
if (vim_strchr((char_u *)".wbuksid]tU", *s) == NULL) {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
if (*++s != NUL && *s != ',' && *s != ' ') {
if (s[-1] == 'k' || s[-1] == 's') {
- /* skip optional filename after 'k' and 's' */
+ // skip optional filename after 'k' and 's'
while (*s && *s != ',' && *s != ' ') {
if (*s == '\\' && s[1] != NUL) {
s++;
@@ -3011,9 +3077,9 @@ ambw_end:
}
} else {
if (errbuf != NULL) {
- sprintf((char *)errbuf,
- _("E535: Illegal character after <%c>"),
- *--s);
+ vim_snprintf((char *)errbuf, errbuflen,
+ _("E535: Illegal character after <%c>"),
+ *--s);
errmsg = errbuf;
} else
errmsg = (char_u *)"";
@@ -3021,9 +3087,7 @@ ambw_end:
}
}
}
- }
- /* 'completeopt' */
- else if (varp == &p_cot) {
+ } else if (varp == &p_cot) { // 'completeopt'
if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
errmsg = e_invarg;
} else {
@@ -3034,27 +3098,27 @@ ambw_end:
if (check_opt_strings(*varp, p_scl_values, false) != OK) {
errmsg = e_invarg;
}
- }
- /* 'pastetoggle': translate key codes like in a mapping */
- else if (varp == &p_pt) {
+ } else if (varp == &p_pt) {
+ // 'pastetoggle': translate key codes like in a mapping
if (*p_pt) {
(void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true,
CPO_TO_CPO_FLAGS);
if (p != NULL) {
- if (new_value_alloced)
+ if (new_value_alloced) {
free_string_option(p_pt);
+ }
p_pt = p;
- new_value_alloced = TRUE;
+ new_value_alloced = true;
}
}
- }
- /* 'backspace' */
- else if (varp == &p_bs) {
+ } else if (varp == &p_bs) { // 'backspace'
if (ascii_isdigit(*p_bs)) {
- if (*p_bs >'2' || p_bs[1] != NUL)
+ if (*p_bs >'2' || p_bs[1] != NUL) {
errmsg = e_invarg;
- } else if (check_opt_strings(p_bs, p_bs_values, TRUE) != OK)
+ }
+ } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
errmsg = e_invarg;
+ }
} else if (varp == &p_bo) {
if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
errmsg = e_invarg;
@@ -3078,64 +3142,59 @@ ambw_end:
errmsg = e_invarg;
}
} else if (varp == &p_cmp) { // 'casemap'
- if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK)
+ if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'diffopt' */
- else if (varp == &p_dip) {
- if (diffopt_changed() == FAIL)
+ }
+ } else if (varp == &p_dip) { // 'diffopt'
+ if (diffopt_changed() == FAIL) {
errmsg = e_invarg;
- }
- /* 'foldmethod' */
- else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) {
- if (check_opt_strings(*varp, p_fdm_values, FALSE) != OK
- || *curwin->w_p_fdm == NUL)
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) { // 'foldmethod'
+ if (check_opt_strings(*varp, p_fdm_values, false) != OK
+ || *curwin->w_p_fdm == NUL) {
errmsg = e_invarg;
- else {
+ } else {
foldUpdateAll(curwin);
- if (foldmethodIsDiff(curwin))
+ if (foldmethodIsDiff(curwin)) {
newFoldLevel();
+ }
}
- }
- /* 'foldexpr' */
- else if (varp == &curwin->w_p_fde) {
- if (foldmethodIsExpr(curwin))
+ } else if (varp == &curwin->w_p_fde) { // 'foldexpr'
+ if (foldmethodIsExpr(curwin)) {
foldUpdateAll(curwin);
- }
- /* 'foldmarker' */
- else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) {
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) { // 'foldmarker'
p = vim_strchr(*varp, ',');
- if (p == NULL)
+ if (p == NULL) {
errmsg = (char_u *)N_("E536: comma required");
- else if (p == *varp || p[1] == NUL)
+ } else if (p == *varp || p[1] == NUL) {
errmsg = e_invarg;
- else if (foldmethodIsMarker(curwin))
+ } else if (foldmethodIsMarker(curwin)) {
foldUpdateAll(curwin);
- }
- /* 'commentstring' */
- else if (gvarp == &p_cms) {
- if (**varp != NUL && strstr((char *)*varp, "%s") == NULL)
+ }
+ } else if (gvarp == &p_cms) { // 'commentstring'
+ if (**varp != NUL && strstr((char *)(*varp), "%s") == NULL) {
errmsg = (char_u *)N_(
- "E537: 'commentstring' must be empty or contain %s");
+ "E537: 'commentstring' must be empty or contain %s");
+ }
} else if (varp == &p_fdo) { // 'foldopen'
- if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK)
+ if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK) {
errmsg = e_invarg;
- }
- /* 'foldclose' */
- else if (varp == &p_fcl) {
- if (check_opt_strings(p_fcl, p_fcl_values, TRUE) != OK)
+ }
+ } else if (varp == &p_fcl) { // 'foldclose'
+ if (check_opt_strings(p_fcl, p_fcl_values, true) != OK) {
errmsg = e_invarg;
- }
- /* 'foldignore' */
- else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) {
- if (foldmethodIsIndent(curwin))
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) { // 'foldignore'
+ if (foldmethodIsIndent(curwin)) {
foldUpdateAll(curwin);
+ }
} else if (varp == &p_ve) { // 'virtualedit'
- if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK)
+ if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK) {
errmsg = e_invarg;
- else if (STRCMP(p_ve, oldval) != 0) {
- /* Recompute cursor position in case the new 've' setting
- * changes something. */
+ } else if (STRCMP(p_ve, oldval) != 0) {
+ // Recompute cursor position in case the new 've' setting
+ // changes something.
validate_virtcol();
coladvance(curwin->w_virtcol);
}
@@ -3149,16 +3208,15 @@ ambw_end:
|| (p[2] != NUL && p[2] != ',')) {
errmsg = e_invarg;
break;
- } else if (p[2] == NUL)
+ } else if (p[2] == NUL) {
break;
- else
+ } else {
p += 3;
+ }
}
}
- }
- /* 'cinoptions' */
- else if (gvarp == &p_cino) {
- /* TODO: recognize errors */
+ } else if (gvarp == &p_cino) { // 'cinoptions'
+ // TODO(vim): recognize errors
parse_cino(curbuf);
// inccommand
} else if (varp == &p_icm) {
@@ -3170,12 +3228,20 @@ ambw_end:
errmsg = e_invarg;
} else {
value_changed = STRCMP(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
}
} else if (gvarp == &p_syn) {
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
value_changed = STRCMP(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
}
} else if (varp == &curwin->w_p_winhl) {
if (!parse_winhl_opt(curwin)) {
@@ -3199,11 +3265,12 @@ ambw_end:
p = (char_u *)MOUSE_ALL;
}
if (p != NULL) {
- for (s = *varp; *s; ++s)
+ for (s = *varp; *s; s++) {
if (vim_strchr(p, *s) == NULL) {
- errmsg = illegal_char(errbuf, *s);
+ errmsg = illegal_char(errbuf, errbuflen, *s);
break;
}
+ }
}
}
@@ -3211,28 +3278,32 @@ ambw_end:
* If error detected, restore the previous value.
*/
if (errmsg != NULL) {
- if (new_value_alloced)
+ if (new_value_alloced) {
free_string_option(*varp);
+ }
*varp = oldval;
/*
* When resetting some values, need to act on it.
*/
- if (did_chartab)
+ if (did_chartab) {
(void)init_chartab();
+ }
} else {
- /* Remember where the option was set. */
+ // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID);
/*
* Free string options that are in allocated memory.
* Use "free_oldval", because recursiveness may change the flags under
* our fingers (esp. init_highlight()).
*/
- if (free_oldval)
+ if (free_oldval) {
free_string_option(oldval);
- if (new_value_alloced)
+ }
+ if (new_value_alloced) {
options[opt_idx].flags |= P_ALLOCED;
- else
+ } else {
options[opt_idx].flags &= ~P_ALLOCED;
+ }
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
&& ((int)options[opt_idx].indir & PV_BOTH)) {
@@ -3241,15 +3312,15 @@ ambw_end:
p = get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
free_string_option(*(char_u **)p);
*(char_u **)p = empty_option;
- }
- /* May set global value for local option. */
- else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL)
+ } else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
+ // May set global value for local option.
set_string_option_global(opt_idx, varp);
+ }
/*
* Trigger the autocommand only after setting the flags.
*/
- /* When 'syntax' is set, load the syntax of that name */
+ // When 'syntax' is set, load the syntax of that name
if (varp == &(curbuf->b_p_syn)) {
static int syn_recursive = 0;
@@ -3265,6 +3336,11 @@ ambw_end:
// already set to this value.
if (!(opt_flags & OPT_MODELINE) || value_changed) {
static int ft_recursive = 0;
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'filetype' has
+ // been checked to be safe.
+ secure = 0;
ft_recursive++;
did_filetype = true;
@@ -3277,15 +3353,17 @@ ambw_end:
if (varp != &(curbuf->b_p_ft)) {
varp = NULL;
}
+ secure = secure_save;
}
}
if (varp == &(curwin->w_s->b_p_spl)) {
char_u fname[200];
char_u *q = curwin->w_s->b_p_spl;
- /* Skip the first name if it is "cjk". */
- if (STRNCMP(q, "cjk,", 4) == 0)
+ // Skip the first name if it is "cjk".
+ if (STRNCMP(q, "cjk,", 4) == 0) {
q += 4;
+ }
/*
* Source the spell/LANG.vim in 'runtimepath'.
@@ -3293,12 +3371,16 @@ ambw_end:
* Use the first name in 'spelllang' up to '_region' or
* '.encoding'.
*/
- for (p = q; *p != NUL; ++p)
- if (vim_strchr((char_u *)"_.,", *p) != NULL)
+ for (p = q; *p != NUL; p++) {
+ if (!ASCII_ISALNUM(*p) && *p != '-') {
break;
- vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
- (int)(p - q), q);
- source_runtime(fname, DIP_ALL);
+ }
+ }
+ if (p > q) {
+ vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
+ (int)(p - q), q);
+ source_runtime(fname, DIP_ALL);
+ }
}
}
@@ -3312,28 +3394,22 @@ ambw_end:
if (curwin->w_curswant != MAXCOL
&& (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0)
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
check_redraw(options[opt_idx].flags);
return errmsg;
-}
-#ifdef _MSC_VER
-#pragma optimize("", on)
-#endif
+} // NOLINT(readability/fn_size)
-/*
- * Simple int comparison function for use with qsort()
- */
+/// Simple int comparison function for use with qsort()
static int int_cmp(const void *a, const void *b)
{
return *(const int *)a - *(const int *)b;
}
-/*
- * Handle setting 'colorcolumn' or 'textwidth' in window "wp".
- * Returns error message, NULL if it's OK.
- */
+/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
+///
+/// @return error message, NULL if it's OK.
char_u *check_colorcolumn(win_T *wp)
{
char_u *s;
@@ -3342,19 +3418,22 @@ char_u *check_colorcolumn(win_T *wp)
int color_cols[256];
int j = 0;
- if (wp->w_buffer == NULL)
- return NULL; /* buffer was closed */
+ if (wp->w_buffer == NULL) {
+ return NULL; // buffer was closed
+ }
for (s = wp->w_p_cc; *s != NUL && count < 255; ) {
if (*s == '-' || *s == '+') {
- /* -N and +N: add to 'textwidth' */
+ // -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
- ++s;
- if (!ascii_isdigit(*s))
+ s++;
+ if (!ascii_isdigit(*s)) {
return e_invarg;
+ }
col = col * getdigits_int(&s);
- if (wp->w_buffer->b_p_tw == 0)
- goto skip; /* 'textwidth' not set, skip this item */
+ if (wp->w_buffer->b_p_tw == 0) {
+ goto skip; // 'textwidth' not set, skip this item
+ }
assert((col >= 0
&& wp->w_buffer->b_p_tw <= INT_MAX - col
&& wp->w_buffer->b_p_tw + col >= INT_MIN)
@@ -3362,39 +3441,46 @@ char_u *check_colorcolumn(win_T *wp)
&& wp->w_buffer->b_p_tw >= INT_MIN - col
&& wp->w_buffer->b_p_tw + col <= INT_MAX));
col += (int)wp->w_buffer->b_p_tw;
- if (col < 0)
+ if (col < 0) {
goto skip;
- } else if (ascii_isdigit(*s))
+ }
+ } else if (ascii_isdigit(*s)) {
col = getdigits_int(&s);
- else
+ } else {
return e_invarg;
- color_cols[count++] = col - 1; /* 1-based to 0-based */
+ }
+ color_cols[count++] = col - 1; // 1-based to 0-based
skip:
- if (*s == NUL)
+ if (*s == NUL) {
break;
- if (*s != ',')
+ }
+ if (*s != ',') {
return e_invarg;
- if (*++s == NUL)
- return e_invarg; /* illegal trailing comma as in "set cc=80," */
+ }
+ if (*++s == NUL) {
+ return e_invarg; // illegal trailing comma as in "set cc=80,"
+ }
}
xfree(wp->w_p_cc_cols);
- if (count == 0)
+ if (count == 0) {
wp->w_p_cc_cols = NULL;
- else {
+ } else {
wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
/* sort the columns for faster usage on screen redraw inside
* win_line() */
qsort(color_cols, count, sizeof(int), int_cmp);
- for (unsigned int i = 0; i < count; ++i)
- /* skip duplicates */
- if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i])
+ for (unsigned int i = 0; i < count; i++) {
+ // skip duplicates
+ if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
wp->w_p_cc_cols[j++] = color_cols[i];
- wp->w_p_cc_cols[j] = -1; /* end marker */
+ }
+ }
+ wp->w_p_cc_cols[j] = -1; // end marker
}
- return NULL; /* no error */
+ return NULL; // no error
}
@@ -3469,7 +3555,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
}
p = *varp;
while (*p) {
- for (i = 0; i < entries; ++i) {
+ for (i = 0; i < entries; i++) {
len = (int)STRLEN(tab[i].name);
if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':'
@@ -3516,14 +3602,16 @@ static char_u *set_chars_option(win_T *wp, char_u **varp)
}
}
- if (i == entries)
+ if (i == entries) {
return e_invarg;
- if (*p == ',')
- ++p;
+ }
+ if (*p == ',') {
+ p++;
+ }
}
}
- return NULL; /* no error */
+ return NULL; // no error
}
/*
@@ -3537,11 +3625,13 @@ char_u *check_stl_option(char_u *s)
static char_u errbuf[80];
while (*s && itemcnt < STL_MAX_ITEM) {
- /* Check for valid keys after % sequences */
- while (*s && *s != '%')
+ // Check for valid keys after % sequences
+ while (*s && *s != '%') {
s++;
- if (!*s)
+ }
+ if (!*s) {
break;
+ }
s++;
if (*s != '%' && *s != ')') {
itemcnt++;
@@ -3552,16 +3642,20 @@ char_u *check_stl_option(char_u *s)
}
if (*s == ')') {
s++;
- if (--groupdepth < 0)
+ if (--groupdepth < 0) {
break;
+ }
continue;
}
- if (*s == '-')
+ if (*s == '-') {
s++;
- while (ascii_isdigit(*s))
+ }
+ while (ascii_isdigit(*s)) {
s++;
- if (*s == STL_USER_HL)
+ }
+ if (*s == STL_USER_HL) {
continue;
+ }
if (*s == '.') {
s++;
while (*s && ascii_isdigit(*s))
@@ -3572,20 +3666,23 @@ char_u *check_stl_option(char_u *s)
continue;
}
if (vim_strchr(STL_ALL, *s) == NULL) {
- return illegal_char(errbuf, *s);
+ return illegal_char(errbuf, sizeof(errbuf), *s);
}
if (*s == '{') {
s++;
while (*s != '}' && *s)
s++;
- if (*s != '}')
+ if (*s != '}') {
return (char_u *)N_("E540: Unclosed expression sequence");
+ }
}
}
- if (itemcnt >= STL_MAX_ITEM)
+ if (itemcnt >= STL_MAX_ITEM) {
return (char_u *)N_("E541: too many items");
- if (groupdepth != 0)
+ }
+ if (groupdepth != 0) {
return (char_u *)N_("E542: unbalanced groups");
+ }
return NULL;
}
@@ -3622,15 +3719,15 @@ static char_u *compile_cap_prog(synblock_T *synblock)
regprog_T *rp = synblock->b_cap_prog;
char_u *re;
- if (*synblock->b_p_spc == NUL)
+ if (*synblock->b_p_spc == NUL) {
synblock->b_cap_prog = NULL;
- else {
- /* Prepend a ^ so that we only match at one column */
+ } else {
+ // Prepend a ^ so that we only match at one column
re = concat_str((char_u *)"^", synblock->b_p_spc);
synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
xfree(re);
if (synblock->b_cap_prog == NULL) {
- synblock->b_cap_prog = rp; /* restore the previous program */
+ synblock->b_cap_prog = rp; // restore the previous program
return e_invarg;
}
}
@@ -3718,20 +3815,21 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
{
int old_value = *(int *)varp;
- /* Disallow changing some options from secure mode */
+ // Disallow changing some options from secure mode
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
return (char *)e_secure;
}
- *(int *)varp = value; /* set the new value */
- /* Remember where the option was set. */
+ *(int *)varp = value; // set the new value
+ // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID);
- /* May set global value for local option. */
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0)
+ // May set global value for local option.
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
*(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value;
+ }
// Ensure that options set to p_force_on cannot be disabled.
if ((int *)varp == &p_force_on && p_force_on == false) {
@@ -3775,72 +3873,65 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
curbuf = save_curbuf;
}
} else if ((int *)varp == &curbuf->b_p_ro) {
- /* when 'readonly' is reset globally, also reset readonlymode */
- if (!curbuf->b_p_ro && (opt_flags & OPT_LOCAL) == 0)
- readonlymode = FALSE;
+ // when 'readonly' is reset globally, also reset readonlymode
+ if (!curbuf->b_p_ro && (opt_flags & OPT_LOCAL) == 0) {
+ readonlymode = false;
+ }
- /* when 'readonly' is set may give W10 again */
- if (curbuf->b_p_ro)
+ // when 'readonly' is set may give W10 again
+ if (curbuf->b_p_ro) {
curbuf->b_did_warn = false;
+ }
redraw_titles();
- }
- /* when 'modifiable' is changed, redraw the window title */
- else if ((int *)varp == &curbuf->b_p_ma) {
+ } else if ((int *)varp == &curbuf->b_p_ma) {
+ // when 'modifiable' is changed, redraw the window title
redraw_titles();
- }
- /* when 'endofline' is changed, redraw the window title */
- else if ((int *)varp == &curbuf->b_p_eol) {
+ } else if ((int *)varp == &curbuf->b_p_eol) {
+ // when 'endofline' is changed, redraw the window title
redraw_titles();
} else if ((int *)varp == &curbuf->b_p_fixeol) {
// when 'fixeol' is changed, redraw the window title
redraw_titles();
- }
- /* when 'bomb' is changed, redraw the window title and tab page text */
- else if ((int *)varp == &curbuf->b_p_bomb) {
+ } else if ((int *)varp == &curbuf->b_p_bomb) {
+ // when 'bomb' is changed, redraw the window title and tab page text
redraw_titles();
- }
- /* when 'bin' is set also set some other options */
- else if ((int *)varp == &curbuf->b_p_bin) {
+ } else if ((int *)varp == &curbuf->b_p_bin) {
+ // when 'bin' is set also set some other options
set_options_bin(old_value, curbuf->b_p_bin, opt_flags);
redraw_titles();
- }
- /* when 'buflisted' changes, trigger autocommands */
- else if ((int *)varp == &curbuf->b_p_bl && old_value != curbuf->b_p_bl) {
+ } else if ((int *)varp == &curbuf->b_p_bl && old_value != curbuf->b_p_bl) {
+ // when 'buflisted' changes, trigger autocommands
apply_autocmds(curbuf->b_p_bl ? EVENT_BUFADD : EVENT_BUFDELETE,
- NULL, NULL, TRUE, curbuf);
- }
- /* when 'swf' is set, create swapfile, when reset remove swapfile */
- else if ((int *)varp == (int *)&curbuf->b_p_swf) {
- if (curbuf->b_p_swf && p_uc)
- ml_open_file(curbuf); /* create the swap file */
- else
- /* no need to reset curbuf->b_may_swap, ml_open_file() will check
- * buf->b_p_swf */
- mf_close_file(curbuf, true); /* remove the swap file */
- }
- /* when 'terse' is set change 'shortmess' */
- else if ((int *)varp == &p_terse) {
+ NULL, NULL, true, curbuf);
+ } else if ((int *)varp == (int *)&curbuf->b_p_swf) {
+ // when 'swf' is set, create swapfile, when reset remove swapfile
+ if (curbuf->b_p_swf && p_uc) {
+ ml_open_file(curbuf); // create the swap file
+ } else {
+ // no need to reset curbuf->b_may_swap, ml_open_file() will check
+ // buf->b_p_swf
+ mf_close_file(curbuf, true); // remove the swap file
+ }
+ } else if ((int *)varp == &p_terse) {
+ // when 'terse' is set change 'shortmess'
char_u *p;
p = vim_strchr(p_shm, SHM_SEARCH);
- /* insert 's' in p_shm */
+ // insert 's' in p_shm
if (p_terse && p == NULL) {
STRCPY(IObuff, p_shm);
STRCAT(IObuff, "s");
set_string_option_direct((char_u *)"shm", -1, IObuff, OPT_FREE, 0);
- }
- /* remove 's' from p_shm */
- else if (!p_terse && p != NULL)
+ } else if (!p_terse && p != NULL) { // remove 's' from p_shm
STRMOVE(p, p + 1);
- }
- /* when 'paste' is set or reset also change other options */
- else if ((int *)varp == &p_paste) {
+ }
+ } else if ((int *)varp == &p_paste) {
+ // when 'paste' is set or reset also change other options
paste_option_changed();
- }
- /* when 'insertmode' is set from an autocommand need to do work here */
- else if ((int *)varp == &p_im) {
+ } else if ((int *)varp == &p_im) {
+ // when 'insertmode' is set from an autocommand need to do work here
if (p_im) {
if ((State & INSERT) == 0) {
need_start_insertmode = true;
@@ -3854,25 +3945,21 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
}
restart_edit = 0;
}
- }
- /* when 'ignorecase' is set or reset and 'hlsearch' is set, redraw */
- else if ((int *)varp == &p_ic && p_hls) {
+ } else if ((int *)varp == &p_ic && p_hls) {
+ // when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
redraw_all_later(SOME_VALID);
- }
- /* when 'hlsearch' is set or reset: reset no_hlsearch */
- else if ((int *)varp == &p_hls) {
- SET_NO_HLSEARCH(FALSE);
- }
- /* when 'scrollbind' is set: snapshot the current position to avoid a jump
- * at the end of normal_cmd() */
- else if ((int *)varp == &curwin->w_p_scb) {
+ } else if ((int *)varp == &p_hls) {
+ // when 'hlsearch' is set or reset: reset no_hlsearch
+ SET_NO_HLSEARCH(false);
+ } else if ((int *)varp == &curwin->w_p_scb) {
+ // when 'scrollbind' is set: snapshot the current position to avoid a jump
+ // at the end of normal_cmd()
if (curwin->w_p_scb) {
- do_check_scrollbind(FALSE);
+ do_check_scrollbind(false);
curwin->w_scbind_pos = curwin->w_topline;
}
- }
- /* There can be only one window with 'previewwindow' set. */
- else if ((int *)varp == &curwin->w_p_pvw) {
+ } else if ((int *)varp == &curwin->w_p_pvw) {
+ // There can be only one window with 'previewwindow' set.
if (curwin->w_p_pvw) {
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
if (win->w_p_pvw && win != curwin) {
@@ -3910,17 +3997,17 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
pseps[0] = '\\';
}
- /* need to adjust the file name arguments and buffer names. */
+ // need to adjust the file name arguments and buffer names.
buflist_slash_adjust();
alist_slash_adjust();
scriptnames_slash_adjust();
}
#endif
-
- /* If 'wrap' is set, set w_leftcol to zero. */
else if ((int *)varp == &curwin->w_p_wrap) {
- if (curwin->w_p_wrap)
+ // If 'wrap' is set, set w_leftcol to zero.
+ if (curwin->w_p_wrap) {
curwin->w_leftcol = 0;
+ }
} else if ((int *)varp == &p_ea) {
if (p_ea && !old_value) {
win_equal(curwin, false, 0);
@@ -3928,22 +4015,18 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
} else if ((int *)varp == &p_acd) {
// Change directories when the 'acd' option is set now.
do_autochdir();
- }
- /* 'diff' */
- else if ((int *)varp == &curwin->w_p_diff) {
- /* May add or remove the buffer from the list of diff buffers. */
+ } else if ((int *)varp == &curwin->w_p_diff) { // 'diff'
+ // May add or remove the buffer from the list of diff buffers.
diff_buf_adjust(curwin);
- if (foldmethodIsDiff(curwin))
+ if (foldmethodIsDiff(curwin)) {
foldUpdateAll(curwin);
- }
-
-
- /* 'spell' */
- else if ((int *)varp == &curwin->w_p_spell) {
+ }
+ } else if ((int *)varp == &curwin->w_p_spell) { // 'spell'
if (curwin->w_p_spell) {
char_u *errmsg = did_set_spelllang(curwin);
- if (errmsg != NULL)
+ if (errmsg != NULL) {
EMSG(_(errmsg));
+ }
}
}
@@ -3953,13 +4036,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
* 'arabic' is set, handle various sub-settings.
*/
if (!p_tbidi) {
- /* set rightleft mode */
+ // set rightleft mode
if (!curwin->w_p_rl) {
- curwin->w_p_rl = TRUE;
+ curwin->w_p_rl = true;
changed_window_setting();
}
- /* Enable Arabic shaping (major part of what Arabic requires) */
+ // Enable Arabic shaping (major part of what Arabic requires)
if (!p_arshape) {
p_arshape = true;
redraw_all_later(NOT_VALID);
@@ -3977,8 +4060,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
set_vim_var_string(VV_WARNINGMSG, _(w_arabic), -1);
}
- /* set 'delcombine' */
- p_deco = TRUE;
+ // set 'delcombine'
+ p_deco = true;
// Force-set the necessary keymap for arabic.
set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
@@ -3987,9 +4070,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
* 'arabic' is reset, handle various sub-settings.
*/
if (!p_tbidi) {
- /* reset rightleft mode */
+ // reset rightleft mode
if (curwin->w_p_rl) {
- curwin->w_p_rl = FALSE;
+ curwin->w_p_rl = false;
changed_window_setting();
}
@@ -4000,7 +4083,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
/* 'delcombine' isn't reset, it is a global option and another
* window may still want it "on". */
- /* Revert to the default keymap */
+ // Revert to the default keymap
curbuf->b_p_iminsert = B_IMODE_NONE;
curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
}
@@ -4040,10 +4123,11 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
BOOLEAN_OBJ(value));
}
- comp_col(); /* in case 'ruler' or 'showcmd' changed */
+ comp_col(); // in case 'ruler' or 'showcmd' changed
if (curwin->w_curswant != MAXCOL
- && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0)
- curwin->w_set_curswant = TRUE;
+ && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
+ curwin->w_set_curswant = true;
+ }
check_redraw(options[opt_idx].flags);
return NULL;
@@ -4064,11 +4148,11 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
{
char_u *errmsg = NULL;
long old_value = *(long *)varp;
- long old_Rows = Rows; /* remember old Rows */
- long old_Columns = Columns; /* remember old Columns */
+ long old_Rows = Rows; // remember old Rows
+ long old_Columns = Columns; // remember old Columns
long *pp = (long *)varp;
- /* Disallow changing some options from secure mode. */
+ // Disallow changing some options from secure mode.
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
return (char *)e_secure;
@@ -4374,21 +4458,21 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
&& curwin->w_height > 0))
&& full_screen) {
if (pp == &(curwin->w_p_scr)) {
- if (curwin->w_p_scr != 0)
+ if (curwin->w_p_scr != 0) {
errmsg = e_scroll;
+ }
win_comp_scroll(curwin);
- }
- /* If 'scroll' became invalid because of a side effect silently adjust
- * it. */
- else if (curwin->w_p_scr <= 0)
+ } else if (curwin->w_p_scr <= 0) {
+ // If 'scroll' became invalid because of a side effect silently adjust it.
curwin->w_p_scr = 1;
- else /* curwin->w_p_scr > curwin->w_height */
+ } else { // curwin->w_p_scr > curwin->w_height
curwin->w_p_scr = curwin->w_height;
+ }
}
if ((p_sj < -100 || p_sj >= Rows) && full_screen) {
- if (Rows != old_Rows) /* Rows changed, just adjust p_sj */
+ if (Rows != old_Rows) { // Rows changed, just adjust p_sj
p_sj = Rows / 2;
- else {
+ } else {
errmsg = e_scroll;
p_sj = 1;
}
@@ -4424,10 +4508,11 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
INTEGER_OBJ(value));
}
- comp_col(); /* in case 'columns' or 'ls' changed */
+ comp_col(); // in case 'columns' or 'ls' changed
if (curwin->w_curswant != MAXCOL
- && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0)
- curwin->w_set_curswant = TRUE;
+ && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
+ curwin->w_set_curswant = true;
+ }
check_redraw(options[opt_idx].flags);
return (char *)errmsg;
@@ -4458,7 +4543,7 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags,
*/
static void check_redraw(uint32_t flags)
{
- /* Careful: P_RCLR and P_RALL are a combination of other P_ flags */
+ // Careful: P_RCLR and P_RALL are a combination of other P_ flags
bool doclear = (flags & P_RCLR) == P_RCLR;
bool all = ((flags & P_RALL) == P_RALL || doclear);
@@ -4691,7 +4776,7 @@ int get_option_value(
// opt_type).
//
// Returned flags:
-// 0 hidden or unknown option, also option that does not have requested
+// 0 hidden or unknown option, also option that does not have requested
// type (see SREQ_* in option_defs.h)
// see SOPT_* in option_defs.h for other flags
//
@@ -4845,7 +4930,7 @@ char *set_option_value(const char *const name, const long number,
return set_string_option(opt_idx, s, opt_flags);
} else {
varp = get_varp_scope(&(options[opt_idx]), opt_flags);
- if (varp != NULL) { /* hidden option is not changed */
+ if (varp != NULL) { // hidden option is not changed
if (number == 0 && string != NULL) {
int idx;
@@ -4904,10 +4989,10 @@ static int find_key_option(const char_u *arg)
* if 'all' == 0: show changed options
* if 'all' == 1: show all normal options
*/
-static void
-showoptions (
+static void
+showoptions(
int all,
- int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */
+ int opt_flags // OPT_LOCAL and/or OPT_GLOBAL
)
{
vimoption_T *p;
@@ -4934,28 +5019,26 @@ showoptions (
MSG_PUTS_TITLE(_("\n--- Options ---"));
}
- /*
- * do the loop two times:
- * 1. display the short items
- * 2. display the long items (only strings and numbers)
- */
- for (run = 1; run <= 2 && !got_int; ++run) {
- /*
- * collect the items in items[]
- */
+ // Do the loop two times:
+ // 1. display the short items
+ // 2. display the long items (only strings and numbers)
+ for (run = 1; run <= 2 && !got_int; run++) {
+ // collect the items in items[]
item_count = 0;
for (p = &options[0]; p->fullname != NULL; p++) {
varp = NULL;
if (opt_flags != 0) {
- if (p->indir != PV_NONE)
+ if (p->indir != PV_NONE) {
varp = get_varp_scope(p, opt_flags);
- } else
+ }
+ } else {
varp = get_varp(p);
+ }
if (varp != NULL
&& (all == 1 || (all == 0 && !optval_default(p, varp)))) {
- if (p->flags & P_BOOL)
- len = 1; /* a toggle option fits always */
- else {
+ if (p->flags & P_BOOL) {
+ len = 1; // a toggle option fits always
+ } else {
option_value2string(p, opt_flags);
len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1;
}
@@ -4975,18 +5058,21 @@ showoptions (
&& (Columns + GAP - 3) / INC >= INT_MIN
&& (Columns + GAP - 3) / INC <= INT_MAX);
cols = (int)((Columns + GAP - 3) / INC);
- if (cols == 0)
+ if (cols == 0) {
cols = 1;
+ }
rows = (item_count + cols - 1) / cols;
- } else /* run == 2 */
+ } else { // run == 2
rows = item_count;
- for (row = 0; row < rows && !got_int; ++row) {
- msg_putchar('\n'); /* go to next line */
- if (got_int) /* 'q' typed in more */
+ }
+ for (row = 0; row < rows && !got_int; row++) {
+ msg_putchar('\n'); // go to next line
+ if (got_int) { // 'q' typed in more
break;
+ }
col = 0;
for (i = row; i < item_count; i += rows) {
- msg_col = col; /* make columns */
+ msg_col = col; // make columns
showoneopt(items[i], opt_flags);
col += INC;
}
@@ -4997,15 +5083,14 @@ showoptions (
xfree(items);
}
-/*
- * Return TRUE if option "p" has its default value.
- */
+/// Return true if option "p" has its default value.
static int optval_default(vimoption_T *p, char_u *varp)
{
int dvi;
- if (varp == NULL)
- return TRUE; /* hidden option is always at default */
+ if (varp == NULL) {
+ return true; // hidden option is always at default
+ }
dvi = ((p->flags & P_VI_DEF) || p_cp) ? VI_DEFAULT : VIM_DEFAULT;
if (p->flags & P_NUM) {
return *(long *)varp == (long)(intptr_t)p->def_val[dvi];
@@ -5044,17 +5129,17 @@ void ui_refresh_options(void)
* showoneopt: show the value of one option
* must not be called with a hidden option!
*/
-static void
-showoneopt (
+static void
+showoneopt(
vimoption_T *p,
- int opt_flags /* OPT_LOCAL or OPT_GLOBAL */
+ int opt_flags // OPT_LOCAL or OPT_GLOBAL
)
{
char_u *varp;
int save_silent = silent_mode;
- silent_mode = FALSE;
- info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
+ silent_mode = false;
+ info_message = true; // use mch_msg(), not mch_errmsg()
varp = get_varp_scope(p, opt_flags);
@@ -5070,13 +5155,13 @@ showoneopt (
MSG_PUTS(p->fullname);
if (!(p->flags & P_BOOL)) {
msg_putchar('=');
- /* put value string in NameBuff */
+ // put value string in NameBuff
option_value2string(p, opt_flags);
msg_outtrans(NameBuff);
}
silent_mode = save_silent;
- info_message = FALSE;
+ info_message = false;
}
/*
@@ -5095,7 +5180,7 @@ showoneopt (
* may have set them when doing ":edit file" and the
* user has set them back at the default or fresh
* value.
- * When "local_only" is TRUE, don't write fresh
+ * When "local_only" is true, don't write fresh
* values, only local values (for ":mkview").
* (fresh value = value used for a new buffer or window for a local option).
*
@@ -5104,9 +5189,9 @@ showoneopt (
int makeset(FILE *fd, int opt_flags, int local_only)
{
vimoption_T *p;
- char_u *varp; /* currently used value */
- char_u *varp_fresh; /* local value */
- char_u *varp_local = NULL; /* fresh value */
+ char_u *varp; // currently used value
+ char_u *varp_fresh; // local value
+ char_u *varp_local = NULL; // fresh value
char *cmd;
int round;
int pri;
@@ -5120,35 +5205,40 @@ int makeset(FILE *fd, int opt_flags, int local_only)
* Do the loop over "options[]" twice: once for options with the
* P_PRI_MKRC flag and once without.
*/
- for (pri = 1; pri >= 0; --pri) {
- for (p = &options[0]; p->fullname; p++)
+ for (pri = 1; pri >= 0; pri--) {
+ for (p = &options[0]; p->fullname; p++) {
if (!(p->flags & P_NO_MKRC)
&& ((pri == 1) == ((p->flags & P_PRI_MKRC) != 0))) {
- /* skip global option when only doing locals */
- if (p->indir == PV_NONE && !(opt_flags & OPT_GLOBAL))
+ // skip global option when only doing locals
+ if (p->indir == PV_NONE && !(opt_flags & OPT_GLOBAL)) {
continue;
+ }
/* Do not store options like 'bufhidden' and 'syntax' in a vimrc
* file, they are always buffer-specific. */
- if ((opt_flags & OPT_GLOBAL) && (p->flags & P_NOGLOB))
+ if ((opt_flags & OPT_GLOBAL) && (p->flags & P_NOGLOB)) {
continue;
+ }
varp = get_varp_scope(p, opt_flags);
- /* Hidden options are never written. */
- if (!varp)
+ // Hidden options are never written.
+ if (!varp) {
continue;
- /* Global values are only written when not at the default value. */
- if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp))
+ }
+ // Global values are only written when not at the default value.
+ if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp)) {
continue;
+ }
round = 2;
if (p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
- /* skip window-local option when only doing globals */
- if (!(opt_flags & OPT_LOCAL))
+ // skip window-local option when only doing globals
+ if (!(opt_flags & OPT_LOCAL)) {
continue;
- /* When fresh value of window-local option is not at the
- * default, need to write it too. */
+ }
+ // When fresh value of window-local option is not at the
+ // default, need to write it too.
if (!(opt_flags & OPT_GLOBAL) && !local_only) {
varp_fresh = get_varp_scope(p, OPT_GLOBAL);
if (!optval_default(p, varp_fresh)) {
@@ -5162,20 +5252,23 @@ int makeset(FILE *fd, int opt_flags, int local_only)
/* Round 1: fresh value for window-local options.
* Round 2: other values */
- for (; round <= 2; varp = varp_local, ++round) {
- if (round == 1 || (opt_flags & OPT_GLOBAL))
+ for (; round <= 2; varp = varp_local, round++) {
+ if (round == 1 || (opt_flags & OPT_GLOBAL)) {
cmd = "set";
- else
+ } else {
cmd = "setlocal";
+ }
if (p->flags & P_BOOL) {
- if (put_setbool(fd, cmd, p->fullname, *(int *)varp) == FAIL)
+ if (put_setbool(fd, cmd, p->fullname, *(int *)varp) == FAIL) {
return FAIL;
+ }
} else if (p->flags & P_NUM) {
- if (put_setnum(fd, cmd, p->fullname, (long *)varp) == FAIL)
+ if (put_setnum(fd, cmd, p->fullname, (long *)varp) == FAIL) {
return FAIL;
- } else { /* P_STRING */
- int do_endif = FALSE;
+ }
+ } else { // P_STRING
+ int do_endif = false;
// Don't set 'syntax' and 'filetype' again if the value is
// already right, avoids reloading the syntax file.
@@ -5191,12 +5284,14 @@ int makeset(FILE *fd, int opt_flags, int local_only)
(p->flags & P_EXPAND) != 0) == FAIL)
return FAIL;
if (do_endif) {
- if (put_line(fd, "endif") == FAIL)
+ if (put_line(fd, "endif") == FAIL) {
return FAIL;
+ }
}
}
}
}
+ }
}
return OK;
}
@@ -5207,19 +5302,20 @@ int makeset(FILE *fd, int opt_flags, int local_only)
*/
int makefoldset(FILE *fd)
{
- if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, FALSE) == FAIL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, FALSE)
+ if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, false) == FAIL
+ || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, false)
== FAIL
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, FALSE)
+ || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, false)
== FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, FALSE)
+ || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, false)
== FAIL
|| put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
|| put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
|| put_setnum(fd, "setlocal", "fdn", &curwin->w_p_fdn) == FAIL
|| put_setbool(fd, "setlocal", "fen", curwin->w_p_fen) == FAIL
- )
+ ) {
return FAIL;
+ }
return OK;
}
@@ -5229,8 +5325,9 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
char_u *s;
char_u *buf;
- if (fprintf(fd, "%s %s=", cmd, name) < 0)
+ if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
+ }
if (*valuep != NULL) {
/* Output 'pastetoggle' as key names. For other
* options some characters have to be escaped with
@@ -5246,17 +5343,19 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
}
} else if (expand) {
buf = xmalloc(MAXPATHL);
- home_replace(NULL, *valuep, buf, MAXPATHL, FALSE);
+ home_replace(NULL, *valuep, buf, MAXPATHL, false);
if (put_escstr(fd, buf, 2) == FAIL) {
xfree(buf);
return FAIL;
}
xfree(buf);
- } else if (put_escstr(fd, *valuep, 2) == FAIL)
+ } else if (put_escstr(fd, *valuep, 2) == FAIL) {
return FAIL;
+ }
}
- if (put_eol(fd) < 0)
+ if (put_eol(fd) < 0) {
return FAIL;
+ }
return OK;
}
@@ -5264,26 +5363,32 @@ static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep)
{
long wc;
- if (fprintf(fd, "%s %s=", cmd, name) < 0)
+ if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
+ }
if (wc_use_keyname((char_u *)valuep, &wc)) {
- /* print 'wildchar' and 'wildcharm' as a key name */
- if (fputs((char *)get_special_key_name((int)wc, 0), fd) < 0)
+ // print 'wildchar' and 'wildcharm' as a key name
+ if (fputs((char *)get_special_key_name((int)wc, 0), fd) < 0) {
return FAIL;
- } else if (fprintf(fd, "%" PRId64, (int64_t)*valuep) < 0)
+ }
+ } else if (fprintf(fd, "%" PRId64, (int64_t)(*valuep)) < 0) {
return FAIL;
- if (put_eol(fd) < 0)
+ }
+ if (put_eol(fd) < 0) {
return FAIL;
+ }
return OK;
}
static int put_setbool(FILE *fd, char *cmd, char *name, int value)
{
- if (value < 0) /* global/local option using global value */
+ if (value < 0) { // global/local option using global value
return OK;
+ }
if (fprintf(fd, "%s %s%s", cmd, value ? "" : "no", name) < 0
- || put_eol(fd) < 0)
+ || put_eol(fd) < 0) {
return FAIL;
+ }
return OK;
}
@@ -5294,7 +5399,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
* of 'ru_col'.
*/
-#define COL_RULER 17 /* columns needed by standard ruler */
+#define COL_RULER 17 // columns needed by standard ruler
void comp_col(void)
{
@@ -5304,14 +5409,16 @@ void comp_col(void)
ru_col = 0;
if (p_ru) {
ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
- /* no last status line, adjust sc_col */
- if (!last_has_status)
+ // no last status line, adjust sc_col
+ if (!last_has_status) {
sc_col = ru_col;
+ }
}
if (p_sc) {
sc_col += SHOWCMD_COLS;
- if (!p_ru || last_has_status) /* no need for separating space */
- ++sc_col;
+ if (!p_ru || last_has_status) { // no need for separating space
+ sc_col++;
+ }
}
assert(sc_col >= 0
&& INT_MIN + sc_col <= Columns
@@ -5321,10 +5428,12 @@ void comp_col(void)
&& INT_MIN + ru_col <= Columns
&& Columns - ru_col <= INT_MAX);
ru_col = (int)(Columns - ru_col);
- if (sc_col <= 0) /* screen too narrow, will become a mess */
+ if (sc_col <= 0) { // screen too narrow, will become a mess
sc_col = 1;
- if (ru_col <= 0)
+ }
+ if (ru_col <= 0) {
ru_col = 1;
+ }
}
// Unset local option value, similar to ":set opt<".
@@ -5411,8 +5520,9 @@ void unset_global_local_option(char *name, void *from)
static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
{
if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
- if (p->var == VAR_WIN)
+ if (p->var == VAR_WIN) {
return (char_u *)GLOBAL_WO(get_varp(p));
+ }
return p->var;
}
if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
@@ -5437,7 +5547,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
case PV_BKC: return (char_u *)&(curbuf->b_p_bkc);
case PV_MENC: return (char_u *)&(curbuf->b_p_menc);
}
- return NULL; /* "cannot happen" */
+ return NULL; // "cannot happen"
}
return get_varp(p);
}
@@ -5447,14 +5557,15 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
*/
static char_u *get_varp(vimoption_T *p)
{
- /* hidden option, always return NULL */
- if (p->var == NULL)
+ // hidden option, always return NULL
+ if (p->var == NULL) {
return NULL;
+ }
switch ((int)p->indir) {
case PV_NONE: return p->var;
- /* global option with local value: use local value if it's been set */
+ // global option with local value: use local value if it's been set
case PV_EP: return *curbuf->b_p_ep != NUL
? (char_u *)&curbuf->b_p_ep : p->var;
case PV_KP: return *curbuf->b_p_kp != NUL
@@ -5593,7 +5704,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_LCS: return (char_u *)&(curwin->w_p_lcs);
default: IEMSG(_("E356: get_varp ERROR"));
}
- /* always return a valid pointer to avoid a crash! */
+ // always return a valid pointer to avoid a crash!
return (char_u *)&(curbuf->b_p_wm);
}
@@ -5602,8 +5713,9 @@ static char_u *get_varp(vimoption_T *p)
*/
char_u *get_equalprg(void)
{
- if (*curbuf->b_p_ep == NUL)
+ if (*curbuf->b_p_ep == NUL) {
return p_ep;
+ }
return curbuf->b_p_ep;
}
@@ -5746,33 +5858,34 @@ void didset_window_options(win_T *wp)
*/
void buf_copy_options(buf_T *buf, int flags)
{
- int should_copy = TRUE;
- char_u *save_p_isk = NULL; /* init for GCC */
+ int should_copy = true;
+ char_u *save_p_isk = NULL; // init for GCC
int dont_do_help;
- int did_isk = FALSE;
+ int did_isk = false;
/*
* Skip this when the option defaults have not been set yet. Happens when
* main() allocates the first buffer.
*/
if (p_cpo != NULL) {
- /*
- * Always copy when entering and 'cpo' contains 'S'.
- * Don't copy when already initialized.
- * Don't copy when 'cpo' contains 's' and not entering.
- * 'S' BCO_ENTER initialized 's' should_copy
- * yes yes X X TRUE
- * yes no yes X FALSE
- * no X yes X FALSE
- * X no no yes FALSE
- * X no no no TRUE
- * no yes no X TRUE
- */
+ //
+ // Always copy when entering and 'cpo' contains 'S'.
+ // Don't copy when already initialized.
+ // Don't copy when 'cpo' contains 's' and not entering.
+ // 'S' BCO_ENTER initialized 's' should_copy
+ // yes yes X X true
+ // yes no yes X false
+ // no X yes X false
+ // X no no yes false
+ // X no no no true
+ // no yes no X true
+ ///
if ((vim_strchr(p_cpo, CPO_BUFOPTGLOB) == NULL || !(flags & BCO_ENTER))
&& (buf->b_p_initialized
|| (!(flags & BCO_ENTER)
- && vim_strchr(p_cpo, CPO_BUFOPT) != NULL)))
- should_copy = FALSE;
+ && vim_strchr(p_cpo, CPO_BUFOPT) != NULL))) {
+ should_copy = false;
+ }
if (should_copy || (flags & BCO_ALWAYS)) {
/* Don't copy the options specific to a help buffer when
@@ -5780,7 +5893,7 @@ void buf_copy_options(buf_T *buf, int flags)
* (jumping back to a help file with CTRL-T or CTRL-O) */
dont_do_help = ((flags & BCO_NOHELP) && buf->b_help)
|| buf->b_p_initialized;
- if (dont_do_help) { /* don't free b_p_isk */
+ if (dont_do_help) { // don't free b_p_isk
save_p_isk = buf->b_p_isk;
buf->b_p_isk = NULL;
}
@@ -5789,8 +5902,8 @@ void buf_copy_options(buf_T *buf, int flags)
* If not already initialized, set 'readonly' and copy 'fileformat'.
*/
if (!buf->b_p_initialized) {
- free_buf_options(buf, TRUE);
- buf->b_p_ro = FALSE; /* don't copy readonly */
+ free_buf_options(buf, true);
+ buf->b_p_ro = false; // don't copy readonly
buf->b_p_fenc = vim_strsave(p_fenc);
switch (*p_ffs) {
case 'm': {
@@ -5853,12 +5966,12 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_cin = p_cin;
buf->b_p_cink = vim_strsave(p_cink);
buf->b_p_cino = vim_strsave(p_cino);
- /* Don't copy 'filetype', it must be detected */
+ // Don't copy 'filetype', it must be detected
buf->b_p_ft = empty_option;
buf->b_p_pi = p_pi;
buf->b_p_cinw = vim_strsave(p_cinw);
buf->b_p_lisp = p_lisp;
- /* Don't copy 'syntax', it must be set */
+ // Don't copy 'syntax', it must be set
buf->b_p_syn = empty_option;
buf->b_p_smc = p_smc;
buf->b_s.b_syn_isk = empty_option;
@@ -5909,15 +6022,16 @@ void buf_copy_options(buf_T *buf, int flags)
* Don't touch these at all when BCO_NOHELP is used and going from
* or to a help buffer.
*/
- if (dont_do_help)
+ if (dont_do_help) {
buf->b_p_isk = save_p_isk;
- else {
+ } else {
buf->b_p_isk = vim_strsave(p_isk);
did_isk = true;
buf->b_p_ts = p_ts;
buf->b_help = false;
- if (buf->b_p_bt[0] == 'h')
+ if (buf->b_p_bt[0] == 'h') {
clear_string_option(&buf->b_p_bt);
+ }
buf->b_p_ma = p_ma;
}
}
@@ -5926,13 +6040,15 @@ void buf_copy_options(buf_T *buf, int flags)
* When the options should be copied (ignoring BCO_ALWAYS), set the
* flag that indicates that the options have been initialized.
*/
- if (should_copy)
+ if (should_copy) {
buf->b_p_initialized = true;
+ }
}
- check_buf_options(buf); /* make sure we don't have NULLs */
- if (did_isk)
- (void)buf_init_chartab(buf, FALSE);
+ check_buf_options(buf); // make sure we don't have NULLs
+ if (did_isk) {
+ (void)buf_init_chartab(buf, false);
+ }
}
/*
@@ -5970,19 +6086,19 @@ static int expand_option_idx = -1;
static char_u expand_option_name[5] = {'t', '_', NUL, NUL, NUL};
static int expand_option_flags = 0;
-void
-set_context_in_set_cmd (
+void
+set_context_in_set_cmd(
expand_T *xp,
char_u *arg,
- int opt_flags /* OPT_GLOBAL and/or OPT_LOCAL */
+ int opt_flags // OPT_GLOBAL and/or OPT_LOCAL
)
{
char_u nextchar;
- uint32_t flags = 0; /* init for GCC */
- int opt_idx = 0; /* init for GCC */
+ uint32_t flags = 0; // init for GCC
+ int opt_idx = 0; // init for GCC
char_u *p;
char_u *s;
- int is_term_option = FALSE;
+ int is_term_option = false;
int key;
expand_option_flags = opt_flags;
@@ -5999,17 +6115,18 @@ set_context_in_set_cmd (
}
while (p > arg) {
s = p;
- /* count number of backslashes before ' ' or ',' */
+ // count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
- while (s > arg && *(s - 1) == '\\')
- --s;
+ while (s > arg && *(s - 1) == '\\') {
+ s--;
+ }
}
- /* break at a space with an even number of backslashes */
+ // break at a space with an even number of backslashes
if (*p == ' ' && ((p - s) & 1) == 0) {
- ++p;
+ p++;
break;
}
- --p;
+ p--;
}
if (STRNCMP(p, "no", 2) == 0) {
xp->xp_context = EXPAND_BOOL_SETTINGS;
@@ -6021,27 +6138,31 @@ set_context_in_set_cmd (
}
xp->xp_pattern = arg = p;
if (*arg == '<') {
- while (*p != '>')
- if (*p++ == NUL) /* expand terminal option name */
+ while (*p != '>') {
+ if (*p++ == NUL) { // expand terminal option name
return;
+ }
+ }
key = get_special_key_code(arg + 1);
- if (key == 0) { /* unknown name */
+ if (key == 0) { // unknown name
xp->xp_context = EXPAND_NOTHING;
return;
}
nextchar = *++p;
- is_term_option = TRUE;
+ is_term_option = true;
expand_option_name[2] = (char_u)KEY2TERMCAP0(key);
expand_option_name[3] = KEY2TERMCAP1(key);
} else {
if (p[0] == 't' && p[1] == '_') {
p += 2;
- if (*p != NUL)
- ++p;
- if (*p == NUL)
- return; /* expand option name */
+ if (*p != NUL) {
+ p++;
+ }
+ if (*p == NUL) {
+ return; // expand option name
+ }
nextchar = *++p;
- is_term_option = TRUE;
+ is_term_option = true;
expand_option_name[2] = p[-2];
expand_option_name[3] = p[-1];
} else {
@@ -6065,9 +6186,9 @@ set_context_in_set_cmd (
}
}
}
- /* handle "-=" and "+=" */
+ // handle "-=" and "+="
if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=') {
- ++p;
+ p++;
nextchar = '=';
}
if ((nextchar != '=' && nextchar != ':')
@@ -6077,16 +6198,18 @@ set_context_in_set_cmd (
}
if (p[1] == NUL) {
xp->xp_context = EXPAND_OLD_SETTING;
- if (is_term_option)
+ if (is_term_option) {
expand_option_idx = -1;
- else
+ } else {
expand_option_idx = opt_idx;
+ }
xp->xp_pattern = p + 1;
return;
}
xp->xp_context = EXPAND_NOTHING;
- if (is_term_option || (flags & P_NUM))
+ if (is_term_option || (flags & P_NUM)) {
return;
+ }
xp->xp_pattern = p + 1;
@@ -6109,22 +6232,24 @@ set_context_in_set_cmd (
xp->xp_backslash = XP_BS_ONE;
} else {
xp->xp_context = EXPAND_FILES;
- /* for 'tags' need three backslashes for a space */
- if (p == (char_u *)&p_tags)
+ // for 'tags' need three backslashes for a space
+ if (p == (char_u *)&p_tags) {
xp->xp_backslash = XP_BS_THREE;
- else
+ } else {
xp->xp_backslash = XP_BS_ONE;
+ }
}
}
/* For an option that is a list of file names, find the start of the
* last file name. */
- for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; --p) {
- /* count number of backslashes before ' ' or ',' */
+ for (p = arg + STRLEN(arg) - 1; p > xp->xp_pattern; p--) {
+ // count number of backslashes before ' ' or ','
if (*p == ' ' || *p == ',') {
s = p;
- while (s > xp->xp_pattern && *(s - 1) == '\\')
- --s;
+ while (s > xp->xp_pattern && *(s - 1) == '\\') {
+ s--;
+ }
if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3))
|| (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0)) {
xp->xp_pattern = p + 1;
@@ -6132,7 +6257,7 @@ set_context_in_set_cmd (
}
}
- /* for 'spellsuggest' start at "file:" */
+ // for 'spellsuggest' start at "file:"
if (options[opt_idx].var == (char_u *)&p_sps
&& STRNCMP(p, "file:", 5) == 0) {
xp->xp_pattern = p + 5;
@@ -6157,31 +6282,36 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***
* loop == 0: count the number of matching options
* loop == 1: copy the matching options into allocated memory
*/
- for (loop = 0; loop <= 1; ++loop) {
+ for (loop = 0; loop <= 1; loop++) {
regmatch->rm_ic = ic;
if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
for (match = 0; match < (int)ARRAY_SIZE(names);
- ++match)
+ match++) {
if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0)) {
- if (loop == 0)
+ if (loop == 0) {
num_normal++;
- else
+ } else {
(*file)[count++] = vim_strsave((char_u *)names[match]);
+ }
}
+ }
}
for (size_t opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
opt_idx++) {
- if (options[opt_idx].var == NULL)
+ if (options[opt_idx].var == NULL) {
continue;
+ }
if (xp->xp_context == EXPAND_BOOL_SETTINGS
- && !(options[opt_idx].flags & P_BOOL))
+ && !(options[opt_idx].flags & P_BOOL)) {
continue;
- match = FALSE;
+ }
+ match = false;
if (vim_regexec(regmatch, str, (colnr_T)0)
|| (options[opt_idx].shortname != NULL
&& vim_regexec(regmatch,
- (char_u *)options[opt_idx].shortname, (colnr_T)0))){
- match = TRUE;
+ (char_u *)options[opt_idx].shortname,
+ (colnr_T)0))) {
+ match = true;
}
if (match) {
@@ -6252,10 +6382,10 @@ void ExpandOldSetting(int *num_file, char_u ***file)
* Get the value for the numeric or string option *opp in a nice format into
* NameBuff[]. Must not be called with a hidden option!
*/
-static void
-option_value2string (
+static void
+option_value2string(
vimoption_T *opp,
- int opt_flags /* OPT_GLOBAL and/or OPT_LOCAL */
+ int opt_flags // OPT_GLOBAL and/or OPT_LOCAL
)
{
char_u *varp;
@@ -6290,19 +6420,18 @@ option_value2string (
}
}
-/*
- * Return TRUE if "varp" points to 'wildchar' or 'wildcharm' and it can be
- * printed as a keyname.
- * "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
- */
+/// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be
+/// printed as a keyname.
+/// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'.
static int wc_use_keyname(char_u *varp, long *wcp)
{
if (((long *)varp == &p_wc) || ((long *)varp == &p_wcm)) {
*wcp = *(long *)varp;
- if (IS_SPECIAL(*wcp) || find_special_key_in_table((int)*wcp) >= 0)
- return TRUE;
+ if (IS_SPECIAL(*wcp) || find_special_key_in_table((int)(*wcp)) >= 0) {
+ return true;
+ }
}
- return FALSE;
+ return false;
}
/*
@@ -6314,7 +6443,7 @@ static int wc_use_keyname(char_u *varp, long *wcp)
*
* langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
* commands.
- * langmap_mapga.ga_data is a sorted table of langmap_entry_T.
+ * langmap_mapga.ga_data is a sorted table of langmap_entry_T.
* This does the same as langmap_mapchar[] for characters >= 256.
*/
/*
@@ -6338,7 +6467,7 @@ static void langmap_set_entry(int from, int to)
assert(langmap_mapga.ga_len >= 0);
unsigned int b = (unsigned int)langmap_mapga.ga_len;
- /* Do a binary search for an existing entry. */
+ // Do a binary search for an existing entry.
while (a != b) {
unsigned int i = (a + b) / 2;
int d = entries[i].from - from;
@@ -6347,19 +6476,20 @@ static void langmap_set_entry(int from, int to)
entries[i].to = to;
return;
}
- if (d < 0)
+ if (d < 0) {
a = i + 1;
- else
+ } else {
b = i;
+ }
}
ga_grow(&langmap_mapga, 1);
- /* insert new entry at position "a" */
+ // insert new entry at position "a"
entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
memmove(entries + 1, entries,
((unsigned int)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
- ++langmap_mapga.ga_len;
+ langmap_mapga.ga_len++;
entries[0].from = from;
entries[0].to = to;
}
@@ -6377,20 +6507,23 @@ int langmap_adjust_mb(int c)
int i = (a + b) / 2;
int d = entries[i].from - c;
- if (d == 0)
- return entries[i].to; /* found matching entry */
- if (d < 0)
+ if (d == 0) {
+ return entries[i].to; // found matching entry
+ }
+ if (d < 0) {
a = i + 1;
- else
+ } else {
b = i;
+ }
}
- return c; /* no entry found, return "c" unmodified */
+ return c; // no entry found, return "c" unmodified
}
static void langmap_init(void)
{
- for (int i = 0; i < 256; i++)
- langmap_mapchar[i] = (char_u)i; /* we init with a one-to-one map */
+ for (int i = 0; i < 256; i++) {
+ langmap_mapchar[i] = (char_u)i; // we init with a one-to-one map
+ }
ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
}
@@ -6404,8 +6537,8 @@ static void langmap_set(void)
char_u *p2;
int from, to;
- ga_clear(&langmap_mapga); /* clear the previous map first */
- langmap_init(); /* back to one-to-one map */
+ ga_clear(&langmap_mapga); // clear the previous map first
+ langmap_init(); // back to one-to-one map
for (p = p_langmap; p[0] != NUL; ) {
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
@@ -6414,13 +6547,14 @@ static void langmap_set(void)
p2++;
}
}
- if (p2[0] == ';')
- ++p2; /* abcd;ABCD form, p2 points to A */
- else
- p2 = NULL; /* aAbBcCdD form, p2 is NULL */
+ if (p2[0] == ';') {
+ p2++; // abcd;ABCD form, p2 points to A
+ } else {
+ p2 = NULL; // aAbBcCdD form, p2 is NULL
+ }
while (p[0]) {
if (p[0] == ',') {
- ++p;
+ p++;
break;
}
if (p[0] == '\\' && p[1] != NUL) {
@@ -6450,9 +6584,9 @@ static void langmap_set(void)
return;
}
- if (from >= 256)
+ if (from >= 256) {
langmap_set_entry(from, to);
- else {
+ } else {
assert(to <= UCHAR_MAX);
langmap_mapchar[from & 255] = (char_u)to;
}
@@ -6470,7 +6604,7 @@ static void langmap_set(void)
p);
return;
}
- ++p;
+ p++;
}
break;
}
@@ -6479,14 +6613,13 @@ static void langmap_set(void)
}
}
-/*
- * Return TRUE if format option 'x' is in effect.
- * Take care of no formatting when 'paste' is set.
- */
+/// Return true if format option 'x' is in effect.
+/// Take care of no formatting when 'paste' is set.
int has_format_option(int x)
{
- if (p_paste)
- return FALSE;
+ if (p_paste) {
+ return false;
+ }
return vim_strchr(curbuf->b_p_fo, x) != NULL;
}
@@ -6505,7 +6638,7 @@ bool shortmess(int x)
*/
static void paste_option_changed(void)
{
- static int old_p_paste = FALSE;
+ static int old_p_paste = false;
static int save_sm = 0;
static int save_sta = 0;
static int save_ru = 0;
@@ -6518,7 +6651,7 @@ static void paste_option_changed(void)
* Save the current values, so they can be restored later.
*/
if (!old_p_paste) {
- /* save options for each buffer */
+ // save options for each buffer
FOR_ALL_BUFFERS(buf) {
buf->b_p_tw_nopaste = buf->b_p_tw;
buf->b_p_wm_nopaste = buf->b_p_wm;
@@ -6566,12 +6699,10 @@ static void paste_option_changed(void)
p_wm = 0;
p_sts = 0;
p_ai = 0;
- }
- /*
- * Paste switched from on to off: Restore saved values.
- */
- else if (old_p_paste) {
- /* restore options for each buffer */
+ } else if (old_p_paste) {
+ // Paste switched from on to off: Restore saved values.
+
+ // restore options for each buffer
FOR_ALL_BUFFERS(buf) {
buf->b_p_tw = buf->b_p_tw_nopaste;
buf->b_p_wm = buf->b_p_wm_nopaste;
@@ -6580,7 +6711,7 @@ static void paste_option_changed(void)
buf->b_p_et = buf->b_p_et_nopaste;
}
- /* restore global options */
+ // restore global options
p_sm = save_sm;
p_sta = save_sta;
if (p_ru != save_ru) {
@@ -6659,12 +6790,15 @@ static void fill_breakat_flags(void)
char_u *p;
int i;
- for (i = 0; i < 256; i++)
- breakat_flags[i] = FALSE;
+ for (i = 0; i < 256; i++) {
+ breakat_flags[i] = false;
+ }
- if (p_breakat != NULL)
- for (p = p_breakat; *p; p++)
- breakat_flags[*p] = TRUE;
+ if (p_breakat != NULL) {
+ for (p = p_breakat; *p; p++) {
+ breakat_flags[*p] = true;
+ }
+ }
}
/*
@@ -6676,7 +6810,7 @@ static void fill_breakat_flags(void)
static int check_opt_strings(
char_u *val,
char **values,
- int list /* when TRUE: accept a list of values */
+ int list // when true: accept a list of values
)
{
return opt_strings_flags(val, values, NULL, list);
@@ -6690,18 +6824,19 @@ static int check_opt_strings(
* Empty is always OK.
*/
static int opt_strings_flags(
- char_u *val, /* new value */
- char **values, /* array of valid string values */
+ char_u *val, // new value
+ char **values, // array of valid string values
unsigned *flagp,
- bool list /* when TRUE: accept a list of values */
+ bool list // when true: accept a list of values
)
{
unsigned int new_flags = 0;
while (*val) {
- for (unsigned int i = 0;; ++i) {
- if (values[i] == NULL) /* val not found in values[] */
+ for (unsigned int i = 0;; i++) {
+ if (values[i] == NULL) { // val not found in values[]
return FAIL;
+ }
size_t len = STRLEN(values[i]);
if (STRNCMP(values[i], val, len) == 0
@@ -6709,12 +6844,13 @@ static int opt_strings_flags(
val += len + (val[len] == ',');
assert(i < sizeof(1U) * 8);
new_flags |= (1U << i);
- break; /* check next item in val list */
+ break; // check next item in val list
}
}
}
- if (flagp != NULL)
+ if (flagp != NULL) {
*flagp = new_flags;
+ }
return OK;
}
@@ -6729,55 +6865,60 @@ static int check_opt_wim(void)
int i;
int idx = 0;
- for (i = 0; i < 4; ++i)
+ for (i = 0; i < 4; i++) {
new_wim_flags[i] = 0;
+ }
- for (p = p_wim; *p; ++p) {
- for (i = 0; ASCII_ISALPHA(p[i]); ++i)
- ;
- if (p[i] != NUL && p[i] != ',' && p[i] != ':')
+ for (p = p_wim; *p; p++) {
+ for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
+ if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
return FAIL;
- if (i == 7 && STRNCMP(p, "longest", 7) == 0)
+ }
+ if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
new_wim_flags[idx] |= WIM_LONGEST;
- else if (i == 4 && STRNCMP(p, "full", 4) == 0)
+ } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
new_wim_flags[idx] |= WIM_FULL;
- else if (i == 4 && STRNCMP(p, "list", 4) == 0)
+ } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
new_wim_flags[idx] |= WIM_LIST;
- else
+ } else {
return FAIL;
+ }
p += i;
- if (*p == NUL)
+ if (*p == NUL) {
break;
+ }
if (*p == ',') {
- if (idx == 3)
+ if (idx == 3) {
return FAIL;
- ++idx;
+ }
+ idx++;
}
}
- /* fill remaining entries with last flag */
+ // fill remaining entries with last flag
while (idx < 3) {
new_wim_flags[idx + 1] = new_wim_flags[idx];
- ++idx;
+ idx++;
}
- /* only when there are no errors, wim_flags[] is changed */
- for (i = 0; i < 4; ++i)
+ // only when there are no errors, wim_flags[] is changed
+ for (i = 0; i < 4; i++) {
wim_flags[i] = new_wim_flags[i];
+ }
return OK;
}
/*
* Check if backspacing over something is allowed.
- * The parameter what is one of the following: whatBS_INDENT, BS_EOL
+ * The parameter what is one of the following: whatBS_INDENT, BS_EOL
* or BS_START
*/
bool can_bs(int what)
{
switch (*p_bs) {
- case '2': return TRUE;
+ case '2': return true;
case '1': return what != BS_START;
- case '0': return FALSE;
+ case '0': return false;
}
return vim_strchr(p_bs, what) != NULL;
}
@@ -6792,7 +6933,7 @@ void save_file_ff(buf_T *buf)
buf->b_start_eol = buf->b_p_eol;
buf->b_start_bomb = buf->b_p_bomb;
- /* Only use free/alloc when necessary, they take time. */
+ // Only use free/alloc when necessary, they take time.
if (buf->b_start_fenc == NULL
|| STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
xfree(buf->b_start_fenc);
@@ -6800,33 +6941,37 @@ void save_file_ff(buf_T *buf)
}
}
-/*
- * Return TRUE if 'fileformat' and/or 'fileencoding' has a different value
- * from when editing started (save_file_ff() called).
- * Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
- * changed and 'binary' is not set.
- * Also when 'endofline' was changed and 'fixeol' is not set.
- * When "ignore_empty" is true don't consider a new, empty buffer to be
- * changed.
- */
+/// Return true if 'fileformat' and/or 'fileencoding' has a different value
+/// from when editing started (save_file_ff() called).
+/// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
+/// changed and 'binary' is not set.
+/// Also when 'endofline' was changed and 'fixeol' is not set.
+/// When "ignore_empty" is true don't consider a new, empty buffer to be
+/// changed.
bool file_ff_differs(buf_T *buf, bool ignore_empty)
{
- /* In a buffer that was never loaded the options are not valid. */
- if (buf->b_flags & BF_NEVERLOADED)
- return FALSE;
+ // In a buffer that was never loaded the options are not valid.
+ if (buf->b_flags & BF_NEVERLOADED) {
+ return false;
+ }
if (ignore_empty
&& (buf->b_flags & BF_NEW)
&& buf->b_ml.ml_line_count == 1
- && *ml_get_buf(buf, (linenr_T)1, FALSE) == NUL)
- return FALSE;
- if (buf->b_start_ffc != *buf->b_p_ff)
+ && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
+ return false;
+ }
+ if (buf->b_start_ffc != *buf->b_p_ff) {
+ return true;
+ }
+ if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
return true;
- if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol)
+ }
+ if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
return true;
- if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb)
- return TRUE;
- if (buf->b_start_fenc == NULL)
+ }
+ if (buf->b_start_fenc == NULL) {
return *buf->b_p_fenc != NUL;
+ }
return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
}
@@ -6835,7 +6980,7 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty)
*/
int check_ff_value(char_u *p)
{
- return check_opt_strings(p, p_ff_values, FALSE);
+ return check_opt_strings(p, p_ff_values, false);
}
/*
@@ -6862,7 +7007,7 @@ int get_sts_value(void)
* Check matchpairs option for "*initc".
* If there is a match set "*initc" to the matching character and "*findc" to
* the opposite character. Set "*backwards" to the direction.
- * When "switchit" is TRUE swap the direction.
+ * When "switchit" is true swap the direction.
*/
void find_mps_values(int *initc, int *findc, int *backwards, int switchit)
{
@@ -6927,10 +7072,12 @@ static bool briopt_check(win_T *wp)
p += 3;
bri_sbr = true;
}
- if (*p != ',' && *p != NUL)
+ if (*p != ',' && *p != NUL) {
return false;
- if (*p == ',')
- ++p;
+ }
+ if (*p == ',') {
+ p++;
+ }
}
wp->w_p_brishift = bri_shift;
@@ -7086,22 +7233,40 @@ size_t copy_option_part(char_u **option, char_u *buf, size_t maxlen,
return len;
}
-/// Return TRUE when 'shell' has "csh" in the tail.
+/// Return true when 'shell' has "csh" in the tail.
int csh_like_shell(void)
{
return strstr((char *)path_tail(p_sh), "csh") != NULL;
}
-/// Return true when window "wp" has a column to draw signs in.
-bool signcolumn_on(win_T *wp)
+/// Return the number of requested sign columns, based on current
+/// buffer signs and on user configuration.
+int win_signcol_count(win_T *wp)
{
- if (*wp->w_p_scl == 'n') {
- return false;
- }
- if (*wp->w_p_scl == 'y') {
- return true;
- }
- return wp->w_buffer->b_signlist != NULL;
+ int maximum = 1, needed_signcols;
+ const char *scl = (const char *)wp->w_p_scl;
+
+ if (*scl == 'n') {
+ return 0;
+ }
+ needed_signcols = buf_signcols(wp->w_buffer);
+
+ // yes or yes
+ if (!strncmp(scl, "yes:", 4)) {
+ // Fixed amount of columns
+ return scl[4] - '0';
+ }
+ if (*scl == 'y') {
+ return 1;
+ }
+
+ // auto or auto:<NUM>
+ if (!strncmp(scl, "auto:", 5)) {
+ // Variable depending on a configuration
+ maximum = scl[5] - '0';
+ }
+
+ return MIN(maximum, needed_signcols);
}
/// Get window or buffer local options
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 6dc5e418fc..3e116095fd 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -74,13 +74,14 @@
#define FO_MBYTE_JOIN 'M' /* no space before/after multi-byte char */
#define FO_MBYTE_JOIN2 'B' /* no space between multi-byte chars */
#define FO_ONE_LETTER '1'
-#define FO_WHITE_PAR 'w' /* trailing white space continues paragr. */
-#define FO_AUTO 'a' /* automatic formatting */
-#define FO_REMOVE_COMS 'j' /* remove comment leaders when joining lines */
+#define FO_WHITE_PAR 'w' // trailing white space continues paragr.
+#define FO_AUTO 'a' // automatic formatting
+#define FO_REMOVE_COMS 'j' // remove comment leaders when joining lines
+#define FO_PERIOD_ABBR 'p' // don't break a single space after a period
#define DFLT_FO_VI "vt"
#define DFLT_FO_VIM "tcqj"
-#define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */
+#define FO_ALL "tcroq2vlb1mMBn,awjp" // for do_set()
// characters for the p_cpo option:
#define CPO_ALTREAD 'a' // ":read" sets alternate file name
@@ -178,14 +179,6 @@ enum {
SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
0, \
})
-/// All possible flags for 'shm'.
-#define SHM_ALL ((char_u[]) { \
- SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
- SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \
- SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \
- SHM_RECORDING, SHM_FILEINFO, \
- 0, \
-})
/* characters for p_go: */
#define GO_ASEL 'a' /* autoselect */
@@ -637,6 +630,7 @@ EXTERN long p_ur; ///< 'undoreload'
EXTERN long p_uc; ///< 'updatecount'
EXTERN long p_ut; ///< 'updatetime'
EXTERN char_u *p_shada; ///< 'shada'
+EXTERN char *p_shadafile; ///< 'shadafile'
EXTERN char_u *p_vdir; ///< 'viewdir'
EXTERN char_u *p_vop; ///< 'viewoptions'
EXTERN unsigned vop_flags; ///< uses SSOP_ flags
@@ -658,6 +652,12 @@ extern char_u *p_vfile; /* 'verbosefile' */
#endif
EXTERN int p_warn; // 'warn'
EXTERN char_u *p_wop; // 'wildoptions'
+EXTERN unsigned wop_flags;
+# ifdef IN_OPTION_C
+static char *(p_wop_values[]) = { "tagfile", "pum", NULL };
+#endif
+#define WOP_TAGFILE 0x01
+#define WOP_PUM 0x02
EXTERN long p_window; // 'window'
EXTERN char_u *p_wak; // 'winaltkeys'
EXTERN char_u *p_wig; // 'wildignore'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 2398f9d61c..affddd8084 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -44,6 +44,8 @@ local N_=function(s)
return 'N_(' .. cstr(s) .. ')'
end
end
+-- used for 'cinkeys' and 'indentkeys'
+local indentkeys_default = '0{,0},0),0],:,0#,!^F,o,O,e';
return {
cstr=cstr,
options={
@@ -179,6 +181,7 @@ return {
{
full_name='backupskip', abbreviation='bsk',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_bsk',
defaults={if_true={vi=""}}
@@ -187,6 +190,7 @@ return {
full_name='belloff', abbreviation='bo',
deny_duplicates=true,
type='string', list='comma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_bo',
defaults={if_true={vi="all"}}
@@ -319,7 +323,7 @@ return {
vi_def=true,
alloced=true,
varname='p_cink',
- defaults={if_true={vi="0{,0},0),:,0#,!^F,o,O,e"}}
+ defaults={if_true={vi=indentkeys_default}}
},
{
full_name='cinoptions', abbreviation='cino',
@@ -754,6 +758,7 @@ return {
{
full_name='fileencodings', abbreviation='fencs',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_fencs',
defaults={if_true={vi="ucs-bom,utf-8,default,latin1"}}
@@ -1014,6 +1019,7 @@ return {
{
full_name='guifontset', abbreviation='gfs',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_guifontset',
redraw={'ui_option'},
@@ -1068,6 +1074,7 @@ return {
{
full_name='helplang', abbreviation='hlg',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_hlg',
defaults={if_true={vi=""}}
@@ -1218,7 +1225,7 @@ return {
vi_def=true,
alloced=true,
varname='p_indk',
- defaults={if_true={vi="0{,0},:,0#,!^F,o,O,e"}}
+ defaults={if_true={vi=indentkeys_default}}
},
{
full_name='infercase', abbreviation='inf',
@@ -2008,6 +2015,15 @@ return {
defaults={if_true={vi="", vim="!,'100,<50,s10,h"}}
},
{
+ full_name='shadafile', abbreviation='sdf',
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
+ vi_def=true,
+ secure=true,
+ varname='p_shadafile',
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='shell', abbreviation='sh',
type='string', scope={'global'},
secure=true,
@@ -2238,6 +2254,7 @@ return {
{
full_name='spellfile', abbreviation='spf',
type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
secure=true,
vi_def=true,
alloced=true,
@@ -2248,6 +2265,7 @@ return {
{
full_name='spelllang', abbreviation='spl',
type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
vi_def=true,
alloced=true,
expand=true,
@@ -2258,6 +2276,7 @@ return {
{
full_name='spellsuggest', abbreviation='sps',
type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
secure=true,
vi_def=true,
expand=true,
@@ -2623,6 +2642,15 @@ return {
defaults={if_true={vi="", vim="!,'100,<50,s10,h"}}
},
{
+ full_name='viminfofile', abbreviation='vif',
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
+ vi_def=true,
+ secure=true,
+ varname='p_shadafile',
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='virtualedit', abbreviation='ve',
type='string', list='onecomma', scope={'global'},
deny_duplicates=true,
@@ -2699,7 +2727,8 @@ return {
},
{
full_name='wildoptions', abbreviation='wop',
- type='string', scope={'global'},
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
vi_def=true,
varname='p_wop',
defaults={if_true={vi=""}}
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index e7bfbc8240..7d1021962c 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -151,25 +151,36 @@ int os_unsetenv(const char *name)
char *os_getenvname_at_index(size_t index)
{
#ifdef _WIN32
- // Check if index is inside the environ array and is not the last element.
- for (size_t i = 0; i <= index; i++) {
- if (_wenviron[i] == NULL) {
- return NULL;
- }
- }
- wchar_t *utf16_str = _wenviron[index];
- char *utf8_str;
- int conversion_result = utf16_to_utf8(utf16_str, &utf8_str);
- if (conversion_result != 0) {
- EMSG2("utf16_to_utf8 failed: %d", conversion_result);
+ wchar_t *env = GetEnvironmentStringsW();
+ if (!env) {
return NULL;
}
- size_t namesize = 0;
- while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) {
- namesize++;
+ char *name = NULL;
+ size_t current_index = 0;
+ // GetEnvironmentStringsW() result has this format:
+ // var1=value1\0var2=value2\0...varN=valueN\0\0
+ for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) {
+ if (index == current_index) {
+ char *utf8_str;
+ int conversion_result = utf16_to_utf8(it, &utf8_str);
+ if (conversion_result != 0) {
+ EMSG2("utf16_to_utf8 failed: %d", conversion_result);
+ break;
+ }
+ size_t namesize = 0;
+ while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) {
+ namesize++;
+ }
+ name = (char *)vim_strnsave((char_u *)utf8_str, namesize);
+ xfree(utf8_str);
+ break;
+ }
+ if (*it == L'\0') {
+ current_index++;
+ }
}
- char *name = (char *)vim_strnsave((char_u *)utf8_str, namesize);
- xfree(utf8_str);
+
+ FreeEnvironmentStringsW(env);
return name;
#else
# if defined(HAVE__NSGETENVIRON)
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 9b9b4581f4..8c7dfcdee7 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -226,13 +226,13 @@ int os_exepath(char *buffer, size_t *size)
return uv_exepath(buffer, size);
}
-/// Checks if the given path represents an executable file.
+/// Checks if the file `name` is executable.
///
-/// @param[in] name Name of the executable.
-/// @param[out] abspath Path of the executable, if found and not `NULL`.
-/// @param[in] use_path If 'false', only check if "name" is executable
+/// @param[in] name Filename to check.
+/// @param[out] abspath Returns resolved executable path, if not NULL.
+/// @param[in] use_path Also search $PATH.
///
-/// @return `true` if `name` is executable and
+/// @return true if `name` is executable and
/// - can be found in $PATH,
/// - is relative to current dir or
/// - is absolute.
@@ -242,40 +242,36 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
FUNC_ATTR_NONNULL_ARG(1)
{
bool no_path = !use_path || path_is_absolute(name);
-#ifndef WIN32
// If the filename is "qualified" (relative or absolute) do not check $PATH.
+#ifdef WIN32
+ no_path |= (name[0] == '.'
+ && ((name[1] == '/' || name[1] == '\\')
+ || (name[1] == '.' && (name[2] == '/' || name[2] == '\\'))));
+#else
no_path |= (name[0] == '.'
&& (name[1] == '/' || (name[1] == '.' && name[2] == '/')));
#endif
if (no_path) {
#ifdef WIN32
- const char *pathext = os_getenv("PATHEXT");
- if (!pathext) {
- pathext = ".com;.exe;.bat;.cmd";
- }
- bool ok = is_executable((char *)name) || is_executable_ext((char *)name,
- pathext);
+ if (is_executable_ext((char *)name, abspath)) {
#else
// Must have path separator, cannot execute files in the current directory.
- const bool ok = ((const char_u *)gettail_dir((const char *)name) != name
- && is_executable((char *)name));
+ if ((const char_u *)gettail_dir((const char *)name) != name
+ && is_executable((char *)name, abspath)) {
#endif
- if (ok) {
- if (abspath != NULL) {
- *abspath = save_abs_path(name);
- }
return true;
+ } else {
+ return false;
}
- return false;
}
return is_executable_in_path(name, abspath);
}
/// Returns true if `name` is an executable file.
-static bool is_executable(const char *name)
- FUNC_ATTR_NONNULL_ALL
+static bool is_executable(const char *name, char_u **abspath)
+ FUNC_ATTR_NONNULL_ARG(1)
{
int32_t mode = os_getperm((const char *)name);
@@ -286,40 +282,58 @@ static bool is_executable(const char *name)
#ifdef WIN32
// Windows does not have exec bit; just check if the file exists and is not
// a directory.
- return (S_ISREG(mode));
+ const bool ok = S_ISREG(mode);
#else
int r = -1;
if (S_ISREG(mode)) {
RUN_UV_FS_FUNC(r, uv_fs_access, name, X_OK, NULL);
}
- return (r == 0);
+ const bool ok = (r == 0);
#endif
+ if (ok && abspath != NULL) {
+ *abspath = save_abs_path((char_u *)name);
+ }
+ return ok;
}
#ifdef WIN32
-/// Appends file extensions from `pathext` to `name` and returns true if any
-/// such combination is executable.
-static bool is_executable_ext(char *name, const char *pathext)
- FUNC_ATTR_NONNULL_ALL
+/// Checks if file `name` is executable under any of these conditions:
+/// - extension is in $PATHEXT and `name` is executable
+/// - result of any $PATHEXT extension appended to `name` is executable
+static bool is_executable_ext(char *name, char_u **abspath)
+ FUNC_ATTR_NONNULL_ARG(1)
{
+ const bool is_unix_shell = strstr((char *)path_tail(p_sh), "sh") != NULL;
+ char *nameext = strrchr(name, '.');
+ size_t nameext_len = nameext ? strlen(nameext) : 0;
xstrlcpy(os_buf, name, sizeof(os_buf));
char *buf_end = xstrchrnul(os_buf, '\0');
+ const char *pathext = os_getenv("PATHEXT");
+ if (!pathext) {
+ pathext = ".com;.exe;.bat;.cmd";
+ }
for (const char *ext = pathext; *ext; ext++) {
- // Skip the extension if there is no suffix after a '.'.
+ // If $PATHEXT itself contains dot:
if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) {
+ if (is_executable(name, abspath)) {
+ return true;
+ }
+ // Skip it.
ext++;
continue;
}
const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR);
- STRLCPY(buf_end, ext, ext_end - ext + 1);
-
- if (is_executable(os_buf)) {
- return true;
- }
-
- if (*ext_end != ENV_SEPCHAR) {
- break;
+ size_t ext_len = (size_t)(ext_end - ext);
+ if (ext_len != 0) {
+ STRLCPY(buf_end, ext, ext_len + 1);
+ bool in_pathext = nameext_len == ext_len
+ && 0 == mb_strnicmp((char_u *)nameext, (char_u *)ext, ext_len);
+
+ if (((in_pathext || is_unix_shell) && is_executable(name, abspath))
+ || is_executable(os_buf, abspath)) {
+ return true;
+ }
}
ext = ext_end;
}
@@ -327,10 +341,10 @@ static bool is_executable_ext(char *name, const char *pathext)
}
#endif
-/// Checks if a file is inside the `$PATH` and is executable.
+/// Checks if a file is in `$PATH` and is executable.
///
-/// @param[in] name The name of the executable.
-/// @param[out] abspath Path of the executable, if found and not `NULL`.
+/// @param[in] name Filename to check.
+/// @param[out] abspath Returns resolved executable path, if not NULL.
///
/// @return `true` if `name` is an executable inside `$PATH`.
static bool is_executable_in_path(const char_u *name, char_u **abspath)
@@ -351,15 +365,6 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
#endif
size_t buf_len = STRLEN(name) + strlen(path) + 2;
-
-#ifdef WIN32
- const char *pathext = os_getenv("PATHEXT");
- if (!pathext) {
- pathext = ".com;.exe;.bat;.cmd";
- }
- buf_len += strlen(pathext);
-#endif
-
char *buf = xmalloc(buf_len);
// Walk through all entries in $PATH to check if "name" exists there and
@@ -374,15 +379,10 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
append_path(buf, (char *)name, buf_len);
#ifdef WIN32
- bool ok = is_executable(buf) || is_executable_ext(buf, pathext);
+ if (is_executable_ext(buf, abspath)) {
#else
- bool ok = is_executable(buf);
+ if (is_executable(buf, abspath)) {
#endif
- if (ok) {
- if (abspath != NULL) { // Caller asked for a copy of the path.
- *abspath = save_abs_path((char_u *)buf);
- }
-
rv = true;
goto end;
}
@@ -643,8 +643,9 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size,
return (ptrdiff_t)written_bytes;
}
-/// Copies a file from path to new_path. Currently this passes
-/// the arguments through to uv_fs_copyfile.
+/// Copies a file from `path` to `new_path`.
+///
+/// @see http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_copyfile
///
/// @param path Path of file to be copied
/// @param path_new Path of new file
diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c
index a67e7487eb..a1020be215 100644
--- a/src/nvim/os/process.c
+++ b/src/nvim/os/process.c
@@ -265,3 +265,9 @@ Dictionary os_proc_info(int pid)
return pinfo;
}
#endif
+
+/// Return true if process `pid` is running.
+bool os_proc_running(int pid)
+{
+ return uv_kill(pid, 0) == 0;
+}
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index f6503dfdb0..91ee45d3a9 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -80,14 +80,14 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
return ret;
}
-/// Return nvim-specific XDG directory subpath
+/// Return Nvim-specific XDG directory subpath.
///
-/// @param[in] idx XDG directory to use.
+/// Windows: Uses "…/nvim-data" for kXDGDataHome to avoid storing
+/// configuration and data files in the same path. #4403
///
-/// @return [allocated] `{xdg_directory}/nvim`
+/// @param[in] idx XDG directory to use.
///
-/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to
-/// avoid storing configuration and data files in the same path.
+/// @return [allocated] "{xdg_directory}/nvim"
char *get_xdg_home(const XDGVarType idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
diff --git a/src/nvim/os/stdpaths_defs.h b/src/nvim/os/stdpaths_defs.h
index 1433e0bc62..44c30df373 100644
--- a/src/nvim/os/stdpaths_defs.h
+++ b/src/nvim/os/stdpaths_defs.h
@@ -3,6 +3,7 @@
/// List of possible XDG variables
typedef enum {
+ kXDGNone = -1,
kXDGConfigHome, ///< XDG_CONFIG_HOME
kXDGDataHome, ///< XDG_DATA_HOME
kXDGCacheHome, ///< XDG_CACHE_HOME
diff --git a/src/nvim/path.c b/src/nvim/path.c
index a706e32773..67b88a861a 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1346,6 +1346,15 @@ void slash_adjust(char_u *p)
if (path_with_url((const char *)p)) {
return;
}
+
+ if (*p == '`') {
+ // don't replace backslash in backtick quoted strings
+ const size_t len = STRLEN(p);
+ if (len > 2 && *(p + len - 1) == '`') {
+ return;
+ }
+ }
+
while (*p) {
if (*p == (char_u)psepcN) {
*p = (char_u)psepc;
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 499ee11cad..58a0008e04 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -12,6 +12,7 @@
#include "nvim/vim.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/eval/typval.h"
#include "nvim/popupmnu.h"
#include "nvim/charset.h"
#include "nvim/ex_cmds.h"
@@ -65,7 +66,9 @@ static bool pum_invalid = false; // the screen was just cleared
/// @param array_changed if true, array contains different items since last call
/// if false, a new item is selected, but the array
/// is the same
-void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
+/// @param cmd_startcol only for cmdline mode: column of completed match
+void pum_display(pumitem_T *array, int size, int selected, bool array_changed,
+ int cmd_startcol)
{
int w;
int def_width;
@@ -83,7 +86,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
if (!pum_is_visible) {
// To keep the code simple, we only allow changing the
// draw mode when the popup menu is not being displayed
- pum_external = ui_has(kUIPopupmenu);
+ pum_external = ui_has(kUIPopupmenu)
+ || (State == CMDLINE && ui_has(kUIWildmenu));
}
do {
@@ -95,19 +99,26 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
above_row = 0;
below_row = cmdline_row;
- // anchor position: the start of the completed word
- row = curwin->w_wrow;
- if (curwin->w_p_rl) {
- col = curwin->w_width - curwin->w_wcol - 1;
+ // wildoptions=pum
+ if (State == CMDLINE) {
+ row = ui_has(kUICmdline) ? 0 : cmdline_row;
+ col = cmd_startcol;
+ pum_anchor_grid = ui_has(kUICmdline) ? -1 : DEFAULT_GRID_HANDLE;
} else {
- col = curwin->w_wcol;
- }
+ // anchor position: the start of the completed word
+ row = curwin->w_wrow;
+ if (curwin->w_p_rl) {
+ col = curwin->w_width - curwin->w_wcol - 1;
+ } else {
+ col = curwin->w_wcol;
+ }
- pum_anchor_grid = (int)curwin->w_grid.handle;
- if (!ui_has(kUIMultigrid)) {
- pum_anchor_grid = (int)default_grid.handle;
- row += curwin->w_winrow;
- col += curwin->w_wincol;
+ pum_anchor_grid = (int)curwin->w_grid.handle;
+ if (!ui_has(kUIMultigrid)) {
+ pum_anchor_grid = (int)default_grid.handle;
+ row += curwin->w_winrow;
+ col += curwin->w_wincol;
+ }
}
if (pum_external) {
@@ -661,7 +672,7 @@ static int pum_set_selected(int n, int repeat)
&& (curbuf->b_p_bh[0] == 'w')) {
// Already a "wipeout" buffer, make it empty.
while (!BUFEMPTY()) {
- ml_delete((linenr_T)1, FALSE);
+ ml_delete((linenr_T)1, false);
}
} else {
// Don't want to sync undo in the current buffer.
@@ -686,11 +697,11 @@ static int pum_set_selected(int n, int repeat)
for (p = pum_array[pum_selected].pum_info; *p != NUL;) {
e = vim_strchr(p, '\n');
if (e == NULL) {
- ml_append(lnum++, p, 0, FALSE);
+ ml_append(lnum++, p, 0, false);
break;
} else {
*e = NUL;
- ml_append(lnum++, p, (int)(e - p + 1), FALSE);
+ ml_append(lnum++, p, (int)(e - p + 1), false);
*e = '\n';
p = e + 1;
}
@@ -841,3 +852,17 @@ int pum_get_height(void)
{
return pum_height;
}
+
+void pum_set_boundings(dict_T *dict)
+{
+ if (!pum_visible()) {
+ return;
+ }
+ tv_dict_add_nr(dict, S_LEN("height"), pum_height);
+ tv_dict_add_nr(dict, S_LEN("width"), pum_width);
+ tv_dict_add_nr(dict, S_LEN("row"), pum_row);
+ tv_dict_add_nr(dict, S_LEN("col"), pum_col);
+ tv_dict_add_nr(dict, S_LEN("size"), pum_size);
+ tv_dict_add_special(dict, S_LEN("scrollbar"),
+ pum_scrollbar ? kSpecialVarTrue : kSpecialVarFalse);
+}
diff --git a/src/nvim/popupmnu.h b/src/nvim/popupmnu.h
index 42e6ef5653..7956e50a93 100644
--- a/src/nvim/popupmnu.h
+++ b/src/nvim/popupmnu.h
@@ -1,6 +1,7 @@
#ifndef NVIM_POPUPMNU_H
#define NVIM_POPUPMNU_H
+#include "nvim/vim.h"
#include "nvim/macros.h"
#include "nvim/grid_defs.h"
#include "nvim/types.h"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index ee1cf2182f..29b3d19f52 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -57,19 +57,20 @@ struct dir_stack_T {
*/
typedef struct qfline_S qfline_T;
struct qfline_S {
- qfline_T *qf_next; /* pointer to next error in the list */
- qfline_T *qf_prev; /* pointer to previous error in the list */
- linenr_T qf_lnum; /* line number where the error occurred */
- int qf_fnum; /* file number for the line */
- int qf_col; /* column where the error occurred */
- int qf_nr; /* error number */
- char_u *qf_pattern; /* search pattern for the error */
- char_u *qf_text; /* description of the error */
- char_u qf_viscol; /* set to TRUE if qf_col is screen column */
- char_u qf_cleared; /* set to TRUE if line has been deleted */
- char_u qf_type; /* type of the error (mostly 'E'); 1 for
- :helpgrep */
- char_u qf_valid; /* valid error message detected */
+ qfline_T *qf_next; ///< pointer to next error in the list
+ qfline_T *qf_prev; ///< pointer to previous error in the list
+ linenr_T qf_lnum; ///< line number where the error occurred
+ int qf_fnum; ///< file number for the line
+ int qf_col; ///< column where the error occurred
+ int qf_nr; ///< error number
+ char_u *qf_module; ///< module name for this error
+ char_u *qf_pattern; ///< search pattern for the error
+ char_u *qf_text; ///< description of the error
+ char_u qf_viscol; ///< set to TRUE if qf_col is screen column
+ char_u qf_cleared; ///< set to TRUE if line has been deleted
+ char_u qf_type; ///< type of the error (mostly 'E'); 1 for
+ // :helpgrep
+ char_u qf_valid; ///< valid error message detected
};
/*
@@ -101,6 +102,7 @@ typedef struct qf_list_S {
bool qf_multiline;
bool qf_multiignore;
bool qf_multiscan;
+ long qf_changedtick;
} qf_list_T;
/// Quickfix/Location list stack definition
@@ -121,7 +123,7 @@ struct qf_info_S {
static qf_info_T ql_info; // global quickfix list
static unsigned last_qf_id = 0; // Last Used quickfix list id
-#define FMT_PATTERNS 10 /* maximum number of % recognized */
+#define FMT_PATTERNS 11 // maximum number of % recognized
/*
* Structure used to hold the info of one part of 'errorformat'
@@ -155,7 +157,8 @@ enum {
QF_OK = 1,
QF_END_OF_INPUT = 2,
QF_NOMEM = 3,
- QF_IGNORE_LINE = 4
+ QF_IGNORE_LINE = 4,
+ QF_MULTISCAN = 5,
};
typedef struct {
@@ -176,6 +179,7 @@ typedef struct {
typedef struct {
char_u *namebuf;
+ char_u *module;
char_u *errmsg;
size_t errmsglen;
long lnum;
@@ -205,6 +209,8 @@ typedef struct {
static char_u *qf_last_bufname = NULL;
static bufref_T qf_last_bufref = { NULL, 0, 0 };
+static char *e_loc_list_changed = N_("E926: Current location list was changed");
+
/// Read the errorfile "efile" into memory, line by line, building the error
/// list. Set the error list's title to qf_title.
///
@@ -247,7 +253,8 @@ static struct fmtpattern
{ 'r', ".*" },
{ 'p', "[- .]*" }, // NOLINT(whitespace/tab)
{ 'v', "\\d\\+" },
- { 's', ".\\+" }
+ { 's', ".\\+" },
+ { 'o', ".\\+" }
};
// Converts a 'errorformat' string to regular expression pattern
@@ -423,7 +430,11 @@ static efm_T * parse_efm_option(char_u *efm)
for (int round = FMT_PATTERNS - 1; round >= 0; ) {
i += STRLEN(fmt_pat[round--].pattern);
}
+#ifdef BACKSLASH_IN_FILENAME
+ i += 12; // "%f" can become twelve chars longer (see efm_to_regpat)
+#else
i += 2; // "%f" can become two chars longer
+#endif
char_u *fmtstr = xmalloc(i);
while (efm[0] != NUL) {
@@ -717,20 +728,17 @@ static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf,
size_t linelen, efm_T *fmt_first, qffields_T *fields)
{
efm_T *fmt_ptr;
- size_t len;
- int i;
int idx = 0;
char_u *tail = NULL;
- regmatch_T regmatch;
qf_list_T *qfl = &qi->qf_lists[qf_idx];
+ int status;
- // Always ignore case when looking for a matching error.
- regmatch.rm_ic = true;
-
+restofline:
// If there was no %> item start at the first pattern
if (fmt_start == NULL) {
fmt_ptr = fmt_first;
} else {
+ // Otherwise start from the last used pattern.
fmt_ptr = fmt_start;
fmt_start = NULL;
}
@@ -738,143 +746,15 @@ static int qf_parse_line(qf_info_T *qi, int qf_idx, char_u *linebuf,
// Try to match each part of 'errorformat' until we find a complete
// match or no match.
fields->valid = true;
-restofline:
for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) {
idx = fmt_ptr->prefix;
- if (qfl->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL) {
- continue;
- }
- fields->namebuf[0] = NUL;
- fields->pattern[0] = NUL;
- if (!qfl->qf_multiscan) {
- fields->errmsg[0] = NUL;
+ status = qf_parse_get_fields(linebuf, linelen, fmt_ptr, fields,
+ qfl->qf_multiline, qfl->qf_multiscan,
+ &tail);
+ if (status == QF_NOMEM) {
+ return status;
}
- fields->lnum = 0;
- fields->col = 0;
- fields->use_viscol = false;
- fields->enr = -1;
- fields->type = 0;
- tail = NULL;
-
- regmatch.regprog = fmt_ptr->prog;
- int r = vim_regexec(&regmatch, linebuf, (colnr_T)0);
- fmt_ptr->prog = regmatch.regprog;
- if (r) {
- if ((idx == 'C' || idx == 'Z') && !qfl->qf_multiline) {
- continue;
- }
- if (vim_strchr((char_u *)"EWI", idx) != NULL) {
- fields->type = (char_u)idx;
- } else {
- fields->type = 0;
- }
- // Extract error message data from matched line.
- // We check for an actual submatch, because "\[" and "\]" in
- // the 'errorformat' may cause the wrong submatch to be used.
- if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- // Expand ~/file and $HOME/file to full path.
- char_u c = *regmatch.endp[i];
- *regmatch.endp[i] = NUL;
- expand_env(regmatch.startp[i], fields->namebuf, CMDBUFFSIZE);
- *regmatch.endp[i] = c;
-
- if (vim_strchr((char_u *)"OPQ", idx) != NULL
- && !os_path_exists(fields->namebuf)) {
- continue;
- }
- }
- if ((i = (int)fmt_ptr->addr[1]) > 0) { // %n
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->enr = (int)atol((char *)regmatch.startp[i]);
- }
- if ((i = (int)fmt_ptr->addr[2]) > 0) { // %l
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->lnum = atol((char *)regmatch.startp[i]);
- }
- if ((i = (int)fmt_ptr->addr[3]) > 0) { // %c
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->col = (int)atol((char *)regmatch.startp[i]);
- }
- if ((i = (int)fmt_ptr->addr[4]) > 0) { // %t
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->type = *regmatch.startp[i];
- }
- if (fmt_ptr->flags == '+' && !qfl->qf_multiscan) { // %+
- if (linelen >= fields->errmsglen) {
- // linelen + null terminator
- fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
- fields->errmsglen = linelen + 1;
- }
- STRLCPY(fields->errmsg, linebuf, linelen + 1);
- } else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- len = (size_t)(regmatch.endp[i] - regmatch.startp[i]);
- if (len >= fields->errmsglen) {
- // len + null terminator
- fields->errmsg = xrealloc(fields->errmsg, len + 1);
- fields->errmsglen = len + 1;
- }
- STRLCPY(fields->errmsg, regmatch.startp[i], len + 1);
- }
- if ((i = (int)fmt_ptr->addr[6]) > 0) { // %r
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- tail = regmatch.startp[i];
- }
- if ((i = (int)fmt_ptr->addr[7]) > 0) { // %p
- char_u *match_ptr;
-
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- fields->col = 0;
- for (match_ptr = regmatch.startp[i];
- match_ptr != regmatch.endp[i]; match_ptr++) {
- fields->col++;
- if (*match_ptr == TAB) {
- fields->col += 7;
- fields->col -= fields->col % 8;
- }
- }
- fields->col++;
- fields->use_viscol = true;
- }
- if ((i = (int)fmt_ptr->addr[8]) > 0) { // %v
- if (regmatch.startp[i] == NULL) {
- continue;
- }
- fields->col = (int)atol((char *)regmatch.startp[i]);
- fields->use_viscol = true;
- }
- if ((i = (int)fmt_ptr->addr[9]) > 0) { // %s
- if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) {
- continue;
- }
- len = (size_t)(regmatch.endp[i] - regmatch.startp[i]);
- if (len > CMDBUFFSIZE - 5) {
- len = CMDBUFFSIZE - 5;
- }
- STRCPY(fields->pattern, "^\\V");
- xstrlcat((char *)fields->pattern, (char *)regmatch.startp[i],
- CMDBUFFSIZE+1);
- fields->pattern[len + 3] = '\\';
- fields->pattern[len + 4] = '$';
- fields->pattern[len + 5] = NUL;
- }
+ if (status == QF_OK) {
break;
}
}
@@ -882,30 +762,16 @@ restofline:
if (fmt_ptr == NULL || idx == 'D' || idx == 'X') {
if (fmt_ptr != NULL) {
- if (idx == 'D') { // enter directory
- if (*fields->namebuf == NUL) {
- EMSG(_("E379: Missing or empty directory name"));
- return QF_FAIL;
- }
- qfl->qf_directory = qf_push_dir(fields->namebuf, &qfl->qf_dir_stack,
- false);
- if (qfl->qf_directory == NULL) {
- return QF_FAIL;
- }
- } else if (idx == 'X') { // leave directory
- qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack);
+ // 'D' and 'X' directory specifiers.
+ status = qf_parse_dir_pfx(idx, fields, qfl);
+ if (status != QF_OK) {
+ return status;
}
}
- fields->namebuf[0] = NUL; // no match found, remove file name
- fields->lnum = 0; // don't jump to this line
- fields->valid = false;
- if (linelen >= fields->errmsglen) {
- // linelen + null terminator
- fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
- fields->errmsglen = linelen + 1;
+ status = qf_parse_line_nomatch(linebuf, linelen, fields);
+ if (status != QF_OK) {
+ return status;
}
- // copy whole line to error message
- STRLCPY(fields->errmsg, linebuf, linelen + 1);
if (fmt_ptr == NULL) {
qfl->qf_multiline = qfl->qf_multiignore = false;
}
@@ -918,63 +784,17 @@ restofline:
if (vim_strchr((char_u *)"AEWI", idx) != NULL) {
qfl->qf_multiline = true; // start of a multi-line message
qfl->qf_multiignore = false; // reset continuation
- } else if (vim_strchr((char_u *)"CZ", idx)
- != NULL) { // continuation of multi-line msg
- if (!qfl->qf_multiignore) {
- qfline_T *qfprev = qfl->qf_last;
- if (qfprev == NULL) {
- return QF_FAIL;
- }
- if (*fields->errmsg) {
- size_t textlen = STRLEN(qfprev->qf_text);
- qfprev->qf_text = xrealloc(qfprev->qf_text,
- textlen + STRLEN(fields->errmsg) + 2);
- qfprev->qf_text[textlen] = '\n';
- STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg);
- }
- if (qfprev->qf_nr == -1) {
- qfprev->qf_nr = fields->enr;
- }
- if (vim_isprintc(fields->type) && !qfprev->qf_type) {
- qfprev->qf_type = fields->type; // only printable chars allowed
- }
- if (!qfprev->qf_lnum) {
- qfprev->qf_lnum = fields->lnum;
- }
- if (!qfprev->qf_col) {
- qfprev->qf_col = fields->col;
- }
- qfprev->qf_viscol = fields->use_viscol;
- if (!qfprev->qf_fnum) {
- qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory,
- *fields->namebuf || qfl->qf_directory
- ? fields->namebuf
- : qfl->qf_currfile && fields->valid
- ? qfl->qf_currfile : 0);
- }
- }
- if (idx == 'Z') {
- qfl->qf_multiline = qfl->qf_multiignore = false;
+ } else if (vim_strchr((char_u *)"CZ", idx) != NULL) {
+ // continuation of multi-line msg
+ status = qf_parse_multiline_pfx(qi, qf_idx, idx, qfl, fields);
+ if (status != QF_OK) {
+ return status;
}
-
- line_breakcheck();
- return QF_IGNORE_LINE;
} else if (vim_strchr((char_u *)"OPQ", idx) != NULL) {
// global file names
- fields->valid = false;
- if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) {
- if (*fields->namebuf && idx == 'P') {
- qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack,
- true);
- } else if (idx == 'Q') {
- qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack);
- }
- *fields->namebuf = NUL;
- if (tail && *tail) {
- STRMOVE(IObuff, skipwhite(tail));
- qfl->qf_multiscan = true;
- goto restofline;
- }
+ status = qf_parse_file_pfx(idx, fields, qfl, tail);
+ if (status == QF_MULTISCAN) {
+ goto restofline;
}
}
if (fmt_ptr->flags == '-') { // generally exclude this line
@@ -1034,6 +854,7 @@ qf_init_ext(
}
fields.namebuf = xmalloc(CMDBUFFSIZE + 1);
+ fields.module = xmalloc(CMDBUFFSIZE + 1);
fields.errmsglen = CMDBUFFSIZE + 1;
fields.errmsg = xmalloc(fields.errmsglen);
fields.pattern = xmalloc(CMDBUFFSIZE + 1);
@@ -1128,6 +949,7 @@ qf_init_ext(
(*fields.namebuf || qfl->qf_directory)
? fields.namebuf : ((qfl->qf_currfile && fields.valid)
? qfl->qf_currfile : (char_u *)NULL),
+ fields.module,
0,
fields.errmsg,
fields.lnum,
@@ -1172,6 +994,7 @@ qf_init_end:
fclose(state.fd);
}
xfree(fields.namebuf);
+ xfree(fields.module);
xfree(fields.errmsg);
xfree(fields.pattern);
xfree(state.growbuf);
@@ -1187,6 +1010,8 @@ qf_init_end:
return retval;
}
+/// Set the title of the specified quickfix list. Frees the previous title.
+/// Prepends ':' to the title.
static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title)
{
xfree(qi->qf_lists[qf_idx].qf_title);
@@ -1197,10 +1022,23 @@ static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title)
char_u *p = xmallocz(len);
qi->qf_lists[qf_idx].qf_title = p;
- snprintf((char *)p, len + 1, ":%s", (char *)title);
+ xstrlcpy((char *)p, (char *)title, len + 1);
}
}
+/// The title of a quickfix/location list is set, by default, to the command
+/// that created the quickfix list with the ":" prefix.
+/// Create a quickfix list title string by prepending ":" to a user command.
+/// Returns a pointer to a static buffer with the title.
+static char_u * qf_cmdtitle(char_u *cmd)
+{
+ static char_u qftitle_str[IOSIZE];
+
+ snprintf((char *)qftitle_str, IOSIZE, ":%s", (char *)cmd);
+
+ return qftitle_str;
+}
+
// Prepare for adding a new quickfix list. If the current list is in the
// middle of the stack, then all the following lists are freed and then
// the new list is added.
@@ -1230,9 +1068,301 @@ static void qf_new_list(qf_info_T *qi, char_u *qf_title)
qi->qf_lists[qi->qf_curlist].qf_id = ++last_qf_id;
}
-/*
- * Free a location list
- */
+/// Parse the error format matches in 'regmatch' and set the values in 'fields'.
+/// fmt_ptr contains the 'efm' format specifiers/prefixes that have a match.
+/// Returns QF_OK if all the matches are successfully parsed. On failure,
+/// returns QF_FAIL or QF_NOMEM.
+static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr,
+ regmatch_T *regmatch, qffields_T *fields,
+ int qf_multiline, int qf_multiscan, char_u **tail)
+{
+ char_u idx = fmt_ptr->prefix;
+ int i;
+ size_t len;
+
+ if ((idx == 'C' || idx == 'Z') && !qf_multiline) {
+ return QF_FAIL;
+ }
+ if (vim_strchr((char_u *)"EWI", idx) != NULL) {
+ fields->type = idx;
+ } else {
+ fields->type = 0;
+ }
+
+ // Extract error message data from matched line.
+ // We check for an actual submatch, because "\[" and "\]" in
+ // the 'errorformat' may cause the wrong submatch to be used.
+ if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+
+ // Expand ~/file and $HOME/file to full path.
+ char_u c = *regmatch->endp[i];
+ *regmatch->endp[i] = NUL;
+ expand_env(regmatch->startp[i], fields->namebuf, CMDBUFFSIZE);
+ *regmatch->endp[i] = c;
+
+ if (vim_strchr((char_u *)"OPQ", idx) != NULL
+ && !os_path_exists(fields->namebuf)) {
+ return QF_FAIL;
+ }
+ }
+ if ((i = (int)fmt_ptr->addr[1]) > 0) { // %n
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->enr = (int)atol((char *)regmatch->startp[i]);
+ }
+ if ((i = (int)fmt_ptr->addr[2]) > 0) { // %l
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->lnum = atol((char *)regmatch->startp[i]);
+ }
+ if ((i = (int)fmt_ptr->addr[3]) > 0) { // %c
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->col = (int)atol((char *)regmatch->startp[i]);
+ }
+ if ((i = (int)fmt_ptr->addr[4]) > 0) { // %t
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->type = *regmatch->startp[i];
+ }
+ if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+
+ if (linelen >= fields->errmsglen) {
+ // linelen + null terminator
+ fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
+ fields->errmsglen = linelen + 1;
+ }
+ STRLCPY(fields->errmsg, linebuf, linelen + 1);
+ } else if ((i = (int)fmt_ptr->addr[5]) > 0) { // %m
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
+ if (len >= fields->errmsglen) {
+ // len + null terminator
+ fields->errmsg = xrealloc(fields->errmsg, len + 1);
+ fields->errmsglen = len + 1;
+ }
+ STRLCPY(fields->errmsg, regmatch->startp[i], len + 1);
+ }
+ if ((i = (int)fmt_ptr->addr[6]) > 0) { // %r
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ *tail = regmatch->startp[i];
+ }
+ if ((i = (int)fmt_ptr->addr[7]) > 0) { // %p
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->col = 0;
+ char_u *match_ptr;
+ for (match_ptr = regmatch->startp[i]; match_ptr != regmatch->endp[i];
+ match_ptr++) {
+ fields->col++;
+ if (*match_ptr == TAB) {
+ fields->col += 7;
+ fields->col -= fields->col % 8;
+ }
+ }
+ fields->col++;
+ fields->use_viscol = true;
+ }
+ if ((i = (int)fmt_ptr->addr[8]) > 0) { // %v
+ if (regmatch->startp[i] == NULL) {
+ return QF_FAIL;
+ }
+ fields->col = (int)atol((char *)regmatch->startp[i]);
+ fields->use_viscol = true;
+ }
+ if ((i = (int)fmt_ptr->addr[9]) > 0) { // %s
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
+ if (len > CMDBUFFSIZE - 5) {
+ len = CMDBUFFSIZE - 5;
+ }
+ STRCPY(fields->pattern, "^\\V");
+ STRNCAT(fields->pattern, regmatch->startp[i], len);
+ fields->pattern[len + 3] = '\\';
+ fields->pattern[len + 4] = '$';
+ fields->pattern[len + 5] = NUL;
+ }
+ if ((i = (int)fmt_ptr->addr[10]) > 0) { // %o
+ if (regmatch->startp[i] == NULL || regmatch->endp[i] == NULL) {
+ return QF_FAIL;
+ }
+ len = (size_t)(regmatch->endp[i] - regmatch->startp[i]);
+ if (len > CMDBUFFSIZE) {
+ len = CMDBUFFSIZE;
+ }
+ STRNCAT(fields->module, regmatch->startp[i], len);
+ }
+
+ return QF_OK;
+}
+
+/// Parse an error line in 'linebuf' using a single error format string in
+/// 'fmt_ptr->prog' and return the matching values in 'fields'.
+/// Returns QF_OK if the efm format matches completely and the fields are
+/// successfully copied. Otherwise returns QF_FAIL or QF_NOMEM.
+static int qf_parse_get_fields(char_u *linebuf, size_t linelen, efm_T *fmt_ptr,
+ qffields_T *fields, int qf_multiline,
+ int qf_multiscan, char_u **tail)
+{
+ regmatch_T regmatch;
+ int status = QF_FAIL;
+ int r;
+
+ if (qf_multiscan && vim_strchr((char_u *)"OPQ", fmt_ptr->prefix) == NULL) {
+ return QF_FAIL;
+ }
+
+ fields->namebuf[0] = NUL;
+ fields->module[0] = NUL;
+ fields->pattern[0] = NUL;
+ if (!qf_multiscan) {
+ fields->errmsg[0] = NUL;
+ }
+ fields->lnum = 0;
+ fields->col = 0;
+ fields->use_viscol = false;
+ fields->enr = -1;
+ fields->type = 0;
+ *tail = NULL;
+
+ // Always ignore case when looking for a matching error.
+ regmatch.rm_ic = true;
+ regmatch.regprog = fmt_ptr->prog;
+ r = vim_regexec(&regmatch, linebuf, (colnr_T)0);
+ fmt_ptr->prog = regmatch.regprog;
+ if (r) {
+ status = qf_parse_match(linebuf, linelen, fmt_ptr, &regmatch, fields,
+ qf_multiline, qf_multiscan, tail);
+ }
+
+ return status;
+}
+
+/// Parse directory error format prefixes (%D and %X).
+/// Push and pop directories from the directory stack when scanning directory
+/// names.
+static int qf_parse_dir_pfx(int idx, qffields_T *fields, qf_list_T *qfl)
+{
+ if (idx == 'D') { // enter directory
+ if (*fields->namebuf == NUL) {
+ EMSG(_("E379: Missing or empty directory name"));
+ return QF_FAIL;
+ }
+ qfl->qf_directory = qf_push_dir(fields->namebuf, &qfl->qf_dir_stack, false);
+ if (qfl->qf_directory == NULL) {
+ return QF_FAIL;
+ }
+ } else if (idx == 'X') { // leave directory
+ qfl->qf_directory = qf_pop_dir(&qfl->qf_dir_stack);
+ }
+
+ return QF_OK;
+}
+
+/// Parse global file name error format prefixes (%O, %P and %Q).
+static int qf_parse_file_pfx(int idx, qffields_T *fields, qf_list_T *qfl,
+ char_u *tail)
+{
+ fields->valid = false;
+ if (*fields->namebuf == NUL || os_path_exists(fields->namebuf)) {
+ if (*fields->namebuf && idx == 'P') {
+ qfl->qf_currfile = qf_push_dir(fields->namebuf, &qfl->qf_file_stack,
+ true);
+ } else if (idx == 'Q') {
+ qfl->qf_currfile = qf_pop_dir(&qfl->qf_file_stack);
+ }
+ *fields->namebuf = NUL;
+ if (tail && *tail) {
+ STRMOVE(IObuff, skipwhite(tail));
+ qfl->qf_multiscan = true;
+ return QF_MULTISCAN;
+ }
+ }
+
+ return QF_OK;
+}
+
+/// Parse a non-error line (a line which doesn't match any of the error
+/// format in 'efm').
+static int qf_parse_line_nomatch(char_u *linebuf, size_t linelen,
+ qffields_T *fields)
+{
+ fields->namebuf[0] = NUL; // no match found, remove file name
+ fields->lnum = 0; // don't jump to this line
+ fields->valid = false;
+ if (linelen >= fields->errmsglen) {
+ // linelen + null terminator
+ fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
+ fields->errmsglen = linelen + 1;
+ }
+ // copy whole line to error message
+ STRLCPY(fields->errmsg, linebuf, linelen + 1);
+
+ return QF_OK;
+}
+
+/// Parse multi-line error format prefixes (%C and %Z)
+static int qf_parse_multiline_pfx(qf_info_T *qi, int qf_idx, int idx,
+ qf_list_T *qfl, qffields_T *fields)
+{
+ if (!qfl->qf_multiignore) {
+ qfline_T *qfprev = qfl->qf_last;
+
+ if (qfprev == NULL) {
+ return QF_FAIL;
+ }
+ if (*fields->errmsg && !qfl->qf_multiignore) {
+ size_t textlen = strlen((char *)qfprev->qf_text);
+ size_t errlen = strlen((char *)fields->errmsg);
+ qfprev->qf_text = xrealloc(qfprev->qf_text, textlen + errlen + 2);
+ qfprev->qf_text[textlen] = '\n';
+ STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg);
+ }
+ if (qfprev->qf_nr == -1) {
+ qfprev->qf_nr = fields->enr;
+ }
+ if (vim_isprintc(fields->type) && !qfprev->qf_type) {
+ // only printable chars allowed
+ qfprev->qf_type = fields->type;
+ }
+
+ if (!qfprev->qf_lnum) {
+ qfprev->qf_lnum = fields->lnum;
+ }
+ if (!qfprev->qf_col) {
+ qfprev->qf_col = fields->col;
+ }
+ qfprev->qf_viscol = fields->use_viscol;
+ if (!qfprev->qf_fnum) {
+ qfprev->qf_fnum = qf_get_fnum(qi, qf_idx, qfl->qf_directory,
+ *fields->namebuf || qfl->qf_directory
+ ? fields->namebuf
+ : qfl->qf_currfile && fields->valid
+ ? qfl->qf_currfile : 0);
+ }
+ }
+ if (idx == 'Z') {
+ qfl->qf_multiline = qfl->qf_multiignore = false;
+ }
+ line_breakcheck();
+
+ return QF_IGNORE_LINE;
+}
+
+/// Free a location list.
static void ll_free_all(qf_info_T **pqi)
{
int i;
@@ -1252,6 +1382,7 @@ static void ll_free_all(qf_info_T **pqi)
}
}
+/// Free all the quickfix/location lists in the stack.
void qf_free_all(win_T *wp)
{
int i;
@@ -1273,6 +1404,7 @@ void qf_free_all(win_T *wp)
/// @param qf_idx list index
/// @param dir optional directory name
/// @param fname file name or NULL
+/// @param module module name or NULL
/// @param bufnum buffer number or zero
/// @param mesg message
/// @param lnum line number
@@ -1285,9 +1417,9 @@ void qf_free_all(win_T *wp)
///
/// @returns OK or FAIL.
static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
- int bufnum, char_u *mesg, long lnum, int col,
- char_u vis_col, char_u *pattern, int nr, char_u type,
- char_u valid)
+ char_u *module, int bufnum, char_u *mesg, long lnum,
+ int col, char_u vis_col, char_u *pattern, int nr,
+ char_u type, char_u valid)
{
qfline_T *qfp = xmalloc(sizeof(qfline_T));
qfline_T **lastp; // pointer to qf_last or NULL
@@ -1312,9 +1444,15 @@ static int qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname,
} else {
qfp->qf_pattern = vim_strsave(pattern);
}
+ if (module == NULL || *module == NUL) {
+ qfp->qf_module = NULL;
+ } else {
+ qfp->qf_module = vim_strsave(module);
+ }
qfp->qf_nr = nr;
- if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */
+ if (type != 1 && !vim_isprintc(type)) { // only printable chars allowed
type = 0;
+ }
qfp->qf_type = (char_u)type;
qfp->qf_valid = valid;
@@ -1443,6 +1581,7 @@ void copy_loclist(win_T *from, win_T *to)
to->w_llist->qf_curlist,
NULL,
NULL,
+ from_qfp->qf_module,
0,
from_qfp->qf_text,
from_qfp->qf_lnum,
@@ -1473,6 +1612,7 @@ void copy_loclist(win_T *from, win_T *to)
// Assign a new ID for the location list
to_qfl->qf_id = ++last_qf_id;
+ to_qfl->qf_changedtick = 0L;
/* When no valid entries are present in the list, qf_ptr points to
* the first item in the list */
@@ -1699,6 +1839,27 @@ static char_u *qf_guess_filepath(qf_info_T *qi, int qf_idx, char_u *filename)
return ds_ptr == NULL ? NULL : ds_ptr->dirname;
}
+/// Returns true, if a quickfix/location list with the given identifier exists.
+static bool qflist_valid(win_T *wp, unsigned int qf_id)
+{
+ qf_info_T *qi = &ql_info;
+
+ if (wp) {
+ qi = GET_LOC_LIST(wp);
+ if (!qi) {
+ return false;
+ }
+ }
+
+ for (int i = 0; i < qi->qf_listcount; i++) {
+ if (qi->qf_lists[i].qf_id == qf_id) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/// When loading a file from the quickfix, the auto commands may modify it.
/// This may invalidate the current quickfix entry. This function checks
/// whether a entry is still present in the quickfix.
@@ -1725,314 +1886,520 @@ static bool is_qf_entry_present(qf_info_T *qi, qfline_T *qf_ptr)
return true;
}
-/*
- * jump to a quickfix line
- * if dir == FORWARD go "errornr" valid entries forward
- * if dir == BACKWARD go "errornr" valid entries backward
- * if dir == FORWARD_FILE go "errornr" valid entries files backward
- * if dir == BACKWARD_FILE go "errornr" valid entries files backward
- * else if "errornr" is zero, redisplay the same line
- * else go to entry "errornr"
- */
-void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
+/// Get the next valid entry in the current quickfix/location list. The search
+/// starts from the current entry. Returns NULL on failure.
+static qfline_T *get_next_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
+ int *qf_index, int dir)
{
- qf_info_T *ll_ref;
- qfline_T *qf_ptr;
- qfline_T *old_qf_ptr;
- int qf_index;
- int old_qf_fnum;
- int old_qf_index;
- int prev_index;
- static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
- char_u *err = e_no_more_items;
- linenr_T i;
- buf_T *old_curbuf;
- linenr_T old_lnum;
- colnr_T screen_col;
- colnr_T char_col;
- char_u *line;
- char_u *old_swb = p_swb;
- unsigned old_swb_flags = swb_flags;
- int opened_window = FALSE;
- win_T *win;
- win_T *altwin;
- int flags;
- win_T *oldwin = curwin;
- int print_message = TRUE;
- int len;
- const bool old_KeyTyped = KeyTyped; // getting file may reset it
- int ok = OK;
- bool usable_win;
+ int idx = *qf_index;
+ int old_qf_fnum = qf_ptr->qf_fnum;
- if (qi == NULL)
- qi = &ql_info;
+ do {
+ if (idx == qi->qf_lists[qi->qf_curlist].qf_count
+ || qf_ptr->qf_next == NULL) {
+ return NULL;
+ }
+ idx++;
+ qf_ptr = qf_ptr->qf_next;
+ } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid)
+ || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
- if (qi->qf_curlist >= qi->qf_listcount
- || qi->qf_lists[qi->qf_curlist].qf_count == 0) {
- EMSG(_(e_quickfix));
- return;
- }
+ *qf_index = idx;
+ return qf_ptr;
+}
- qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr;
- old_qf_ptr = qf_ptr;
- qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
- old_qf_index = qf_index;
- if (dir == FORWARD || dir == FORWARD_FILE) { /* next valid entry */
- while (errornr--) {
- old_qf_ptr = qf_ptr;
- prev_index = qf_index;
- old_qf_fnum = qf_ptr->qf_fnum;
- do {
- if (qf_index == qi->qf_lists[qi->qf_curlist].qf_count
- || qf_ptr->qf_next == NULL) {
- qf_ptr = old_qf_ptr;
- qf_index = prev_index;
- if (err != NULL) {
- EMSG(_(err));
- goto theend;
- }
- errornr = 0;
- break;
- }
- ++qf_index;
- qf_ptr = qf_ptr->qf_next;
- } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid
- && !qf_ptr->qf_valid)
- || (dir == FORWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
- err = NULL;
- }
- } else if (dir == BACKWARD || dir == BACKWARD_FILE) { /* prev. valid entry */
- while (errornr--) {
- old_qf_ptr = qf_ptr;
- prev_index = qf_index;
- old_qf_fnum = qf_ptr->qf_fnum;
- do {
- if (qf_index == 1 || qf_ptr->qf_prev == NULL) {
- qf_ptr = old_qf_ptr;
- qf_index = prev_index;
- if (err != NULL) {
- EMSG(_(err));
- goto theend;
- }
- errornr = 0;
- break;
- }
- --qf_index;
- qf_ptr = qf_ptr->qf_prev;
- } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid
- && !qf_ptr->qf_valid)
- || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
- err = NULL;
+/// Get the previous valid entry in the current quickfix/location list. The
+/// search starts from the current entry. Returns NULL on failure.
+static qfline_T *get_prev_valid_entry(qf_info_T *qi, qfline_T *qf_ptr,
+ int *qf_index, int dir)
+{
+ int idx = *qf_index;
+ int old_qf_fnum = qf_ptr->qf_fnum;
+
+ do {
+ if (idx == 1 || qf_ptr->qf_prev == NULL) {
+ return NULL;
}
- } else if (errornr != 0) { /* go to specified number */
- while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL) {
- --qf_index;
- qf_ptr = qf_ptr->qf_prev;
+ idx--;
+ qf_ptr = qf_ptr->qf_prev;
+ } while ((!qi->qf_lists[qi->qf_curlist].qf_nonevalid && !qf_ptr->qf_valid)
+ || (dir == BACKWARD_FILE && qf_ptr->qf_fnum == old_qf_fnum));
+
+ *qf_index = idx;
+ return qf_ptr;
+}
+
+/// Get the n'th (errornr) previous/next valid entry from the current entry in
+/// the quickfix list.
+/// dir == FORWARD or FORWARD_FILE: next valid entry
+/// dir == BACKWARD or BACKWARD_FILE: previous valid entry
+static qfline_T *get_nth_valid_entry(qf_info_T *qi, int errornr,
+ qfline_T *qf_ptr, int *qf_index, int dir)
+{
+ qfline_T *prev_qf_ptr;
+ int prev_index;
+ static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
+ char_u *err = e_no_more_items;
+
+ while (errornr--) {
+ prev_qf_ptr = qf_ptr;
+ prev_index = *qf_index;
+
+ if (dir == FORWARD || dir == FORWARD_FILE) {
+ qf_ptr = get_next_valid_entry(qi, qf_ptr, qf_index, dir);
+ } else {
+ qf_ptr = get_prev_valid_entry(qi, qf_ptr, qf_index, dir);
}
- while (errornr > qf_index && qf_index <
- qi->qf_lists[qi->qf_curlist].qf_count
- && qf_ptr->qf_next != NULL) {
- ++qf_index;
- qf_ptr = qf_ptr->qf_next;
+
+ if (qf_ptr == NULL) {
+ qf_ptr = prev_qf_ptr;
+ *qf_index = prev_index;
+ if (err != NULL) {
+ EMSG(_(err));
+ return NULL;
+ }
+ break;
}
+
+ err = NULL;
}
- qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
- if (qf_win_pos_update(qi, old_qf_index))
- /* No need to print the error message if it's visible in the error
- * window */
- print_message = FALSE;
+ return qf_ptr;
+}
- /*
- * For ":helpgrep" find a help window or open one.
- */
- if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) {
- win_T *wp = NULL;
+/// Get n'th (errornr) quickfix entry
+static qfline_T *get_nth_entry(qf_info_T *qi, int errornr, qfline_T *qf_ptr,
+ int *cur_qfidx)
+{
+ int qf_idx = *cur_qfidx;
- if (cmdmod.tab == 0) {
- FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
- if (bt_help(wp2->w_buffer)) {
- wp = wp2;
- break;
- }
+ // New error number is less than the current error number
+ while (errornr < qf_idx && qf_idx > 1 && qf_ptr->qf_prev != NULL) {
+ qf_idx--;
+ qf_ptr = qf_ptr->qf_prev;
+ }
+
+ // New error number is greater than the current error number
+ while (errornr > qf_idx
+ && qf_idx < qi->qf_lists[qi->qf_curlist].qf_count
+ && qf_ptr->qf_next != NULL) {
+ qf_idx++;
+ qf_ptr = qf_ptr->qf_next;
+ }
+
+ *cur_qfidx = qf_idx;
+ return qf_ptr;
+}
+
+/// Find a help window or open one.
+static int jump_to_help_window(qf_info_T *qi, int *opened_window)
+{
+ win_T *wp = NULL;
+
+ if (cmdmod.tab != 0) {
+ wp = NULL;
+ } else {
+ FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
+ if (bt_help(wp2->w_buffer)) {
+ wp = wp2;
+ break;
}
}
- if (wp != NULL && wp->w_buffer->b_nwindows > 0)
- win_enter(wp, true);
- else {
- /*
- * Split off help window; put it at far top if no position
- * specified, the current window is vertically split and narrow.
- */
- flags = WSP_HELP;
- if (cmdmod.split == 0 && curwin->w_width != Columns
- && curwin->w_width < 80)
- flags |= WSP_TOP;
- if (qi != &ql_info)
- flags |= WSP_NEWLOC; /* don't copy the location list */
-
- if (win_split(0, flags) == FAIL)
- goto theend;
- opened_window = TRUE; /* close it when fail */
-
- if (curwin->w_height < p_hh)
- win_setheight((int)p_hh);
-
- if (qi != &ql_info) { /* not a quickfix list */
- /* The new window should use the supplied location list */
- curwin->w_llist = qi;
- qi->qf_refcount++;
- }
+ }
+
+ if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
+ win_enter(wp, true);
+ } else {
+ // Split off help window; put it at far top if no position
+ // specified, the current window is vertically split and narrow.
+ int flags = WSP_HELP;
+ if (cmdmod.split == 0
+ && curwin->w_width != Columns
+ && curwin->w_width < 80) {
+ flags |= WSP_TOP;
}
- if (!p_im)
- restart_edit = 0; /* don't want insert mode in help file */
- }
+ if (qi != &ql_info) {
+ flags |= WSP_NEWLOC; // don't copy the location list
+ }
- /*
- * If currently in the quickfix window, find another window to show the
- * file in.
- */
- if (bt_quickfix(curbuf) && !opened_window) {
- win_T *usable_win_ptr = NULL;
+ if (win_split(0, flags) == FAIL) {
+ return FAIL;
+ }
- /*
- * If there is no file specified, we don't know where to go.
- * But do advance, otherwise ":cn" gets stuck.
- */
- if (qf_ptr->qf_fnum == 0)
- goto theend;
+ *opened_window = true;
- usable_win = false;
+ if (curwin->w_height < p_hh) {
+ win_setheight((int)p_hh);
+ }
- ll_ref = curwin->w_llist_ref;
- if (ll_ref != NULL) {
- /* Find a window using the same location list that is not a
- * quickfix window. */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_llist == ll_ref
- && wp->w_buffer->b_p_bt[0] != 'q') {
- usable_win = true;
- usable_win_ptr = wp;
- break;
- }
+ if (qi != &ql_info) { // not a quickfix list
+ // The new window should use the supplied location list
+ curwin->w_llist = qi;
+ qi->qf_refcount++;
+ }
+ }
+
+ if (!p_im) {
+ restart_edit = 0; // don't want insert mode in help file
+ }
+
+ return OK;
+}
+
+/// Find a suitable window for opening a file (qf_fnum) and jump to it.
+/// If the file is already opened in a window, jump to it.
+static int qf_jump_to_usable_window(int qf_fnum, int *opened_window)
+{
+ win_T *usable_win_ptr = NULL;
+ int usable_win;
+ int flags;
+ win_T *win = NULL;
+ win_T *altwin;
+
+ usable_win = 0;
+
+ qf_info_T *ll_ref = curwin->w_llist_ref;
+ if (ll_ref != NULL) {
+ // Find a window using the same location list that is not a
+ // quickfix window.
+ FOR_ALL_WINDOWS_IN_TAB(usable_win_ptr2, curtab) {
+ if (usable_win_ptr2->w_llist == ll_ref
+ && !bt_quickfix(usable_win_ptr2->w_buffer)) {
+ usable_win_ptr = usable_win_ptr2;
+ usable_win = 1;
+ break;
}
}
+ }
- if (!usable_win) {
- /* Locate a window showing a normal buffer */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->b_p_bt[0] == NUL) {
- usable_win = true;
- break;
- }
+ if (!usable_win) {
+ // Locate a window showing a normal buffer
+ FOR_ALL_WINDOWS_IN_TAB(win2, curtab) {
+ if (win2->w_buffer->b_p_bt[0] == NUL) {
+ win = win2;
+ usable_win = 1;
+ break;
}
}
+ }
- /*
- * If no usable window is found and 'switchbuf' contains "usetab"
- * then search in other tabs.
- */
- if (!usable_win && (swb_flags & SWB_USETAB)) {
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum) {
- goto_tabpage_win(tp, wp);
- usable_win = true;
- goto win_found;
- }
+ // If no usable window is found and 'switchbuf' contains "usetab"
+ // then search in other tabs.
+ if (!usable_win && (swb_flags & SWB_USETAB)) {
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer->b_fnum == qf_fnum) {
+ goto_tabpage_win(tp, wp);
+ usable_win = 1;
+ goto win_found;
}
}
+ }
win_found:
- /*
- * If there is only one window and it is the quickfix window, create a
- * new one above the quickfix window.
- */
- if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) {
- flags = WSP_ABOVE;
- if (ll_ref != NULL)
- flags |= WSP_NEWLOC;
- if (win_split(0, flags) == FAIL)
- goto failed; /* not enough room for window */
- opened_window = TRUE; /* close it when fail */
- p_swb = empty_option; /* don't split again */
- swb_flags = 0;
- RESET_BINDING(curwin);
- if (ll_ref != NULL) {
- /* The new window should use the location list from the
- * location list window */
- curwin->w_llist = ll_ref;
- ll_ref->qf_refcount++;
- }
- } else {
- if (curwin->w_llist_ref != NULL) {
- /* In a location window */
- win = usable_win_ptr;
+ // If there is only one window and it is the quickfix window, create a
+ // new one above the quickfix window.
+ if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) {
+ flags = WSP_ABOVE;
+ if (ll_ref != NULL) {
+ flags |= WSP_NEWLOC;
+ }
+ if (win_split(0, flags) == FAIL) {
+ return FAIL; // not enough room for window
+ }
+ *opened_window = true; // close it when fail
+ p_swb = empty_option; // don't split again
+ swb_flags = 0;
+ RESET_BINDING(curwin);
+ if (ll_ref != NULL) {
+ // The new window should use the location list from the
+ // location list window
+ curwin->w_llist = ll_ref;
+ ll_ref->qf_refcount++;
+ }
+ } else {
+ if (curwin->w_llist_ref != NULL) {
+ // In a location window
+ win = usable_win_ptr;
+ if (win == NULL) {
+ // Find the window showing the selected file
+ FOR_ALL_WINDOWS_IN_TAB(win2, curtab) {
+ if (win2->w_buffer->b_fnum == qf_fnum) {
+ win = win2;
+ break;
+ }
+ }
if (win == NULL) {
- /* Find the window showing the selected file */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum) {
- win = wp;
+ // Find a previous usable window
+ win = curwin;
+ do {
+ if (win->w_buffer->b_p_bt[0] == NUL) {
break;
}
+ if (win->w_prev == NULL) {
+ win = lastwin; // wrap around the top
+ } else {
+ win = win->w_prev; // go to previous window
+ }
+ } while (win != curwin);
+ }
+ }
+ win_goto(win);
+
+ // If the location list for the window is not set, then set it
+ // to the location list from the location window
+ if (win->w_llist == NULL) {
+ win->w_llist = ll_ref;
+ ll_ref->qf_refcount++;
+ }
+ } else {
+ // Try to find a window that shows the right buffer.
+ // Default to the window just above the quickfix buffer.
+ win = curwin;
+ altwin = NULL;
+ for (;;) {
+ if (win->w_buffer->b_fnum == qf_fnum) {
+ break;
+ }
+ if (win->w_prev == NULL) {
+ win = lastwin; // wrap around the top
+ } else {
+ win = win->w_prev; // go to previous window
+ }
+ if (IS_QF_WINDOW(win)) {
+ // Didn't find it, go to the window before the quickfix window.
+ if (altwin != NULL) {
+ win = altwin;
+ } else if (curwin->w_prev != NULL) {
+ win = curwin->w_prev;
+ } else {
+ win = curwin->w_next;
}
- if (win == NULL) {
- /* Find a previous usable window */
- win = curwin;
- do {
- if (win->w_buffer->b_p_bt[0] == NUL)
- break;
- if (win->w_prev == NULL)
- win = lastwin; /* wrap around the top */
- else
- win = win->w_prev; /* go to previous window */
- } while (win != curwin);
- }
+ break;
}
- win_goto(win);
- /* If the location list for the window is not set, then set it
- * to the location list from the location window */
- if (win->w_llist == NULL) {
- win->w_llist = ll_ref;
- ll_ref->qf_refcount++;
+ // Remember a usable window.
+ if (altwin == NULL && !win->w_p_pvw
+ && win->w_buffer->b_p_bt[0] == NUL) {
+ altwin = win;
}
+ }
+
+ win_goto(win);
+ }
+ }
+
+ return OK;
+}
+
+/// Edit the selected file or help file.
+static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit,
+ win_T *oldwin, int *opened_window, int *abort)
+{
+ int retval = OK;
+
+ if (qf_ptr->qf_type == 1) {
+ // Open help file (do_ecmd() will set b_help flag, readfile() will
+ // set b_p_ro flag).
+ if (!can_abandon(curbuf, forceit)) {
+ EMSG(_(e_nowrtmsg));
+ retval = false;
+ } else {
+ retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
+ ECMD_HIDE + ECMD_SET_HELP,
+ oldwin == curwin ? curwin : NULL);
+ }
+ } else {
+ int old_qf_curlist = qi->qf_curlist;
+ unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+
+ retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
+ GETF_SETMARK | GETF_SWITCH, forceit);
+
+ if (qi != &ql_info) {
+ // Location list. Check whether the associated window is still
+ // present and the list is still valid.
+ if (!win_valid_any_tab(oldwin)) {
+ EMSG(_("E924: Current window was closed"));
+ *abort = true;
+ *opened_window = false;
+ } else if (!qflist_valid(oldwin, save_qfid)) {
+ EMSG(_(e_loc_list_changed));
+ *abort = true;
+ }
+ } else if (old_qf_curlist != qi->qf_curlist
+ || !is_qf_entry_present(qi, qf_ptr)) {
+ if (qi == &ql_info) {
+ EMSG(_("E925: Current quickfix was changed"));
} else {
+ EMSG(_(e_loc_list_changed));
+ }
+ *abort = true;
+ }
- /*
- * Try to find a window that shows the right buffer.
- * Default to the window just above the quickfix buffer.
- */
- win = curwin;
- altwin = NULL;
- for (;; ) {
- if (win->w_buffer->b_fnum == qf_ptr->qf_fnum)
- break;
- if (win->w_prev == NULL)
- win = lastwin; /* wrap around the top */
- else
- win = win->w_prev; /* go to previous window */
-
- if (IS_QF_WINDOW(win)) {
- /* Didn't find it, go to the window before the quickfix
- * window. */
- if (altwin != NULL)
- win = altwin;
- else if (curwin->w_prev != NULL)
- win = curwin->w_prev;
- else
- win = curwin->w_next;
+ if (*abort) {
+ retval = false;
+ }
+ }
+
+ return retval;
+}
+
+/// Goto the error line in the current file using either line/column number or a
+/// search pattern.
+static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char_u qf_viscol,
+ char_u *qf_pattern)
+{
+ linenr_T i;
+ char_u *line;
+ colnr_T screen_col;
+ colnr_T char_col;
+
+ if (qf_pattern == NULL) {
+ // Go to line with error, unless qf_lnum is 0.
+ i = qf_lnum;
+ if (i > 0) {
+ if (i > curbuf->b_ml.ml_line_count) {
+ i = curbuf->b_ml.ml_line_count;
+ }
+ curwin->w_cursor.lnum = i;
+ }
+ if (qf_col > 0) {
+ curwin->w_cursor.col = qf_col - 1;
+ curwin->w_cursor.coladd = 0;
+ if (qf_viscol == true) {
+ // Check each character from the beginning of the error
+ // line up to the error column. For each tab character
+ // found, reduce the error column value by the length of
+ // a tab character.
+ line = get_cursor_line_ptr();
+ screen_col = 0;
+ for (char_col = 0; char_col < curwin->w_cursor.col; char_col++) {
+ if (*line == NUL) {
break;
}
-
- /* Remember a usable window. */
- if (altwin == NULL && !win->w_p_pvw
- && win->w_buffer->b_p_bt[0] == NUL)
- altwin = win;
+ if (*line++ == '\t') {
+ curwin->w_cursor.col -= 7 - (screen_col % 8);
+ screen_col += 8 - (screen_col % 8);
+ } else {
+ screen_col++;
+ }
}
-
- win_goto(win);
}
+ check_cursor();
+ } else {
+ beginline(BL_WHITE | BL_FIX);
+ }
+ } else {
+ // Move the cursor to the first line in the buffer
+ pos_T save_cursor = curwin->w_cursor;
+ curwin->w_cursor.lnum = 0;
+ if (!do_search(NULL, '/', qf_pattern, (long)1, SEARCH_KEEP, NULL, NULL)) {
+ curwin->w_cursor = save_cursor;
+ }
+ }
+}
+
+/// Display quickfix list index and size message
+static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr,
+ buf_T *old_curbuf, linenr_T old_lnum)
+{
+ // Update the screen before showing the message, unless the screen
+ // scrolled up.
+ if (!msg_scrolled) {
+ update_topline_redraw();
+ }
+ snprintf((char *)IObuff, IOSIZE, _("(%d of %d)%s%s: "), qf_index,
+ qi->qf_lists[qi->qf_curlist].qf_count,
+ qf_ptr->qf_cleared ? _(" (line deleted)") : "",
+ (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
+ // Add the message, skipping leading whitespace and newlines.
+ int len = (int)STRLEN(IObuff);
+ qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len);
+
+ // Output the message. Overwrite to avoid scrolling when the 'O'
+ // flag is present in 'shortmess'; But when not jumping, print the
+ // whole message.
+ linenr_T i = msg_scroll;
+ if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) {
+ msg_scroll = true;
+ } else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
+ msg_scroll = false;
+ }
+ msg_attr_keep(IObuff, 0, true, false);
+ msg_scroll = (int)i;
+}
+
+/// jump to a quickfix line
+/// if dir == FORWARD go "errornr" valid entries forward
+/// if dir == BACKWARD go "errornr" valid entries backward
+/// if dir == FORWARD_FILE go "errornr" valid entries files backward
+/// if dir == BACKWARD_FILE go "errornr" valid entries files backward
+/// else if "errornr" is zero, redisplay the same line
+/// else go to entry "errornr"
+void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
+{
+ qfline_T *qf_ptr;
+ qfline_T *old_qf_ptr;
+ int qf_index;
+ int old_qf_index;
+ buf_T *old_curbuf;
+ linenr_T old_lnum;
+ char_u *old_swb = p_swb;
+ unsigned old_swb_flags = swb_flags;
+ int opened_window = false;
+ win_T *oldwin = curwin;
+ int print_message = true;
+ const bool old_KeyTyped = KeyTyped; // getting file may reset it
+ int retval = OK;
+
+ if (qi == NULL)
+ qi = &ql_info;
+
+ if (qi->qf_curlist >= qi->qf_listcount
+ || qi->qf_lists[qi->qf_curlist].qf_count == 0) {
+ EMSG(_(e_quickfix));
+ return;
+ }
+
+ qf_ptr = qi->qf_lists[qi->qf_curlist].qf_ptr;
+ old_qf_ptr = qf_ptr;
+ qf_index = qi->qf_lists[qi->qf_curlist].qf_index;
+ old_qf_index = qf_index;
+ if (dir != 0) { // next/prev valid entry
+ qf_ptr = get_nth_valid_entry(qi, errornr, qf_ptr, &qf_index, dir);
+ if (qf_ptr == NULL) {
+ qf_ptr = old_qf_ptr;
+ qf_index = old_qf_index;
+ goto theend; // The horror... the horror...
+ }
+ } else if (errornr != 0) { // go to specified number
+ qf_ptr = get_nth_entry(qi, errornr, qf_ptr, &qf_index);
+ }
+
+ qi->qf_lists[qi->qf_curlist].qf_index = qf_index;
+ if (qf_win_pos_update(qi, old_qf_index))
+ /* No need to print the error message if it's visible in the error
+ * window */
+ print_message = FALSE;
+
+ // For ":helpgrep" find a help window or open one.
+ if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) {
+ if (jump_to_help_window(qi, &opened_window) == FAIL) {
+ goto theend;
+ }
+ }
+
+ // If currently in the quickfix window, find another window to show the
+ // file in.
+ if (bt_quickfix(curbuf) && !opened_window) {
+ // If there is no file specified, we don't know where to go.
+ // But do advance, otherwise ":cn" gets stuck.
+ if (qf_ptr->qf_fnum == 0) {
+ goto theend;
+ }
+ if (qf_jump_to_usable_window(qf_ptr->qf_fnum, &opened_window) == FAIL) {
+ goto failed;
}
}
@@ -2044,122 +2411,28 @@ win_found:
old_lnum = curwin->w_cursor.lnum;
if (qf_ptr->qf_fnum != 0) {
- if (qf_ptr->qf_type == 1) {
- /* Open help file (do_ecmd() will set b_help flag, readfile() will
- * set b_p_ro flag). */
- if (!can_abandon(curbuf, forceit)) {
- EMSG(_(e_nowrtmsg));
- ok = false;
- } else {
- ok = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1,
- ECMD_HIDE + ECMD_SET_HELP,
- oldwin == curwin ? curwin : NULL);
- }
- } else {
- int old_qf_curlist = qi->qf_curlist;
- bool is_abort = false;
-
- ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1,
- GETF_SETMARK | GETF_SWITCH, forceit);
- if (qi != &ql_info && !win_valid_any_tab(oldwin)) {
- EMSG(_("E924: Current window was closed"));
- is_abort = true;
- opened_window = false;
- } else if (old_qf_curlist != qi->qf_curlist // -V560
- || !is_qf_entry_present(qi, qf_ptr)) {
- if (qi == &ql_info) {
- EMSG(_("E925: Current quickfix was changed"));
- } else {
- EMSG(_("E926: Current location list was changed"));
- }
- is_abort = true;
- }
-
- if (is_abort) {
- ok = false;
- qi = NULL;
- qf_ptr = NULL;
- }
+ int abort = false;
+ retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, oldwin, &opened_window,
+ &abort);
+ if (abort) {
+ qi = NULL;
+ qf_ptr = NULL;
}
}
- if (ok == OK) {
- /* When not switched to another buffer, still need to set pc mark */
- if (curbuf == old_curbuf)
+ if (retval == OK) {
+ // When not switched to another buffer, still need to set pc mark
+ if (curbuf == old_curbuf) {
setpcmark();
-
- if (qf_ptr->qf_pattern == NULL) {
- /*
- * Go to line with error, unless qf_lnum is 0.
- */
- i = qf_ptr->qf_lnum;
- if (i > 0) {
- if (i > curbuf->b_ml.ml_line_count)
- i = curbuf->b_ml.ml_line_count;
- curwin->w_cursor.lnum = i;
- }
- if (qf_ptr->qf_col > 0) {
- curwin->w_cursor.col = qf_ptr->qf_col - 1;
- curwin->w_cursor.coladd = 0;
- if (qf_ptr->qf_viscol == true) {
- // Check each character from the beginning of the error
- // line up to the error column. For each tab character
- // found, reduce the error column value by the length of
- // a tab character.
- line = get_cursor_line_ptr();
- screen_col = 0;
- for (char_col = 0; char_col < curwin->w_cursor.col; ++char_col) {
- if (*line == NUL)
- break;
- if (*line++ == '\t') {
- curwin->w_cursor.col -= 7 - (screen_col % 8);
- screen_col += 8 - (screen_col % 8);
- } else
- ++screen_col;
- }
- }
- check_cursor();
- } else
- beginline(BL_WHITE | BL_FIX);
- } else {
- pos_T save_cursor;
-
- /* Move the cursor to the first line in the buffer */
- save_cursor = curwin->w_cursor;
- curwin->w_cursor.lnum = 0;
- if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1,
- SEARCH_KEEP, NULL, NULL)) {
- curwin->w_cursor = save_cursor;
- }
}
+ qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol,
+ qf_ptr->qf_pattern);
+
if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped)
foldOpenCursor();
if (print_message) {
- /* Update the screen before showing the message, unless the screen
- * scrolled up. */
- if (!msg_scrolled)
- update_topline_redraw();
- sprintf((char *)IObuff, _("(%d of %d)%s%s: "), qf_index,
- qi->qf_lists[qi->qf_curlist].qf_count,
- qf_ptr->qf_cleared ? _(" (line deleted)") : "",
- (char *)qf_types(qf_ptr->qf_type, qf_ptr->qf_nr));
- /* Add the message, skipping leading whitespace and newlines. */
- len = (int)STRLEN(IObuff);
- qf_fmt_text(skipwhite(qf_ptr->qf_text), IObuff + len, IOSIZE - len);
-
- /* Output the message. Overwrite to avoid scrolling when the 'O'
- * flag is present in 'shortmess'; But when not jumping, print the
- * whole message. */
- i = msg_scroll;
- if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) {
- msg_scroll = true;
- } else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
- msg_scroll = false;
- }
- msg_ext_set_kind("quickfix");
- msg_attr_keep(IObuff, 0, true, false);
- msg_scroll = (int)i;
+ qf_jump_print_msg(qi, qf_index, qf_ptr, old_curbuf, old_lnum);
}
} else {
if (opened_window) {
@@ -2257,17 +2530,22 @@ void qf_list(exarg_T *eap)
break;
fname = NULL;
- if (qfp->qf_fnum != 0
- && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
- fname = buf->b_fname;
- if (qfp->qf_type == 1) /* :helpgrep */
- fname = path_tail(fname);
+ if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i,
+ (char *)qfp->qf_module);
+ } else {
+ if (qfp->qf_fnum != 0 && (buf = buflist_findnr(qfp->qf_fnum)) != NULL) {
+ fname = buf->b_fname;
+ if (qfp->qf_type == 1) { // :helpgrep
+ fname = path_tail(fname);
+ }
+ }
+ if (fname == NULL) {
+ snprintf((char *)IObuff, IOSIZE, "%2d", i);
+ } else {
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i, (char *)fname);
+ }
}
- if (fname == NULL)
- sprintf((char *)IObuff, "%2d", i);
- else
- vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
- i, (char *)fname);
msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index
? HL_ATTR(HLF_QFL) : HL_ATTR(HLF_D));
if (qfp->qf_lnum == 0) {
@@ -2328,6 +2606,8 @@ static void qf_fmt_text(char_u *text, char_u *buf, int bufsize)
buf[i] = NUL;
}
+/// Display information (list number, list size and the title) about a
+/// quickfix/location list.
static void qf_msg(qf_info_T *qi, int which, char *lead)
{
char *title = (char *)qi->qf_lists[which].qf_title;
@@ -2397,6 +2677,7 @@ void qf_age(exarg_T *eap)
qf_update_buffer(qi, NULL);
}
+/// Display the information about all the quickfix/location lists in the stack.
void qf_history(exarg_T *eap)
{
qf_info_T *qi = &ql_info;
@@ -2428,9 +2709,10 @@ static void qf_free_items(qf_info_T *qi, int idx)
qfp = qfl->qf_start;
qfpnext = qfp->qf_next;
if (!stop) {
+ xfree(qfp->qf_module);
xfree(qfp->qf_text);
- stop = (qfp == qfpnext);
xfree(qfp->qf_pattern);
+ stop = (qfp == qfpnext);
xfree(qfp);
if (stop) {
// Somehow qf_count may have an incorrect value, set it to 1
@@ -2472,6 +2754,7 @@ static void qf_free(qf_info_T *qi, int idx)
tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL;
qfl->qf_id = 0;
+ qfl->qf_changedtick = 0L;
}
/*
@@ -2841,10 +3124,8 @@ static int is_qf_win(win_T *win, qf_info_T *qi)
return FALSE;
}
-/*
- * Find a window displaying the quickfix/location list 'qi'
- * Searches in only the windows opened in the current tab.
- */
+/// Find a window displaying the quickfix/location list 'qi'
+/// Only searches in the current tabpage.
static win_T *qf_find_win(qf_info_T *qi)
{
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
@@ -2972,9 +3253,12 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
lnum = buf->b_ml.ml_line_count;
}
while (lnum < qi->qf_lists[qi->qf_curlist].qf_count) {
- if (qfp->qf_fnum != 0
- && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
- && errbuf->b_fname != NULL) {
+ if (qfp->qf_module != NULL) {
+ STRCPY(IObuff, qfp->qf_module);
+ len = (int)STRLEN(IObuff);
+ } else if (qfp->qf_fnum != 0
+ && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
+ && errbuf->b_fname != NULL) {
if (qfp->qf_type == 1) { // :helpgrep
STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff));
} else {
@@ -3062,51 +3346,9 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last)
KeyTyped = old_KeyTyped;
}
-/*
- * Return TRUE if "buf" is the quickfix buffer.
- */
-int bt_quickfix(const buf_T *const buf)
-{
- return buf != NULL && buf->b_p_bt[0] == 'q';
-}
-
-// Return TRUE if "buf" is a "nofile", "acwrite" or "terminal" buffer.
-// This means the buffer name is not a file name.
-int bt_nofile(buf_T *buf)
-{
- return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
- || buf->b_p_bt[0] == 'a' || buf->terminal);
-}
-
-// Return TRUE if "buf" is a "nowrite", "nofile" or "terminal" buffer.
-int bt_dontwrite(buf_T *buf)
-{
- return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->terminal);
-}
-
-int bt_dontwrite_msg(buf_T *buf)
-{
- if (bt_dontwrite(buf)) {
- EMSG(_("E382: Cannot write, 'buftype' option is set"));
- return TRUE;
- }
- return FALSE;
-}
-
-/*
- * Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
- * and 'bufhidden'.
- */
-int buf_hide(buf_T *buf)
+static void qf_list_changed(qf_info_T *qi, int qf_idx)
{
- /* 'bufhidden' overrules 'hidden' and ":hide", check it first */
- switch (buf->b_p_bh[0]) {
- case 'u': /* "unload" */
- case 'w': /* "wipe" */
- case 'd': return FALSE; /* "delete" */
- case 'h': return TRUE; /* "hide" */
- }
- return p_hid || cmdmod.hide;
+ qi->qf_lists[qf_idx].qf_changedtick++;
}
/*
@@ -3192,17 +3434,21 @@ void ex_make(exarg_T *eap)
res = qf_init(wp, fname, (eap->cmdidx != CMD_make
&& eap->cmdidx != CMD_lmake) ? p_gefm : p_efm,
(eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd),
- *eap->cmdlinep, enc);
+ qf_cmdtitle(*eap->cmdlinep), enc);
if (wp != NULL) {
qi = GET_LOC_LIST(wp);
}
+ if (res >= 0 && qi != NULL) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
- curbuf->b_fname, TRUE, curbuf);
- if (qi->qf_curlist < qi->qf_listcount)
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true,
+ curbuf);
+ if (qi != NULL && qi->qf_curlist < qi->qf_listcount) {
res = qi->qf_lists[qi->qf_curlist].qf_count;
- else
+ } else {
res = 0;
+ }
}
if (res > 0 && !eap->forceit)
qf_jump(qi, 0, 0, FALSE); /* display first error */
@@ -3546,19 +3792,218 @@ void ex_cfile(exarg_T *eap)
// first error.
// :caddfile adds to an existing quickfix list. If there is no
// quickfix list then a new list is created.
- if (qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
- && eap->cmdidx != CMD_laddfile),
- *eap->cmdlinep, enc) > 0
- && (eap->cmdidx == CMD_cfile
- || eap->cmdidx == CMD_lfile)) {
- if (au_name != NULL)
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
- if (wp != NULL)
- qi = GET_LOC_LIST(wp);
- qf_jump(qi, 0, 0, eap->forceit); /* display first error */
+ int res = qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile
+ && eap->cmdidx != CMD_laddfile),
+ qf_cmdtitle(*eap->cmdlinep), enc);
+ if (wp != NULL) {
+ qi = GET_LOC_LIST(wp);
+ }
+ if (res >= 0 && qi != NULL) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
+ unsigned save_qfid = 0;
+ if (qi != NULL) {
+ save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
+ }
+ if (au_name != NULL) {
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, false, curbuf);
+ }
+ // Autocmd might have freed the quickfix/location list. Check whether it is
+ // still valid
+ if (qi != NULL && !qflist_valid(wp, save_qfid)) {
+ return;
+ }
+ if (res > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)) {
+ qf_jump(qi, 0, 0, eap->forceit); // display first error
+ }
+}
+
+/// Return the vimgrep autocmd name.
+static char_u *vgr_get_auname(cmdidx_T cmdidx)
+{
+ switch (cmdidx) {
+ case CMD_vimgrep: return (char_u *)"vimgrep";
+ case CMD_lvimgrep: return (char_u *)"lvimgrep";
+ case CMD_vimgrepadd: return (char_u *)"vimgrepadd";
+ case CMD_lvimgrepadd: return (char_u *)"lvimgrepadd";
+ case CMD_grep: return (char_u *)"grep";
+ case CMD_lgrep: return (char_u *)"lgrep";
+ case CMD_grepadd: return (char_u *)"grepadd";
+ case CMD_lgrepadd: return (char_u *)"lgrepadd";
+ default: return NULL;
+ }
+}
+
+/// Initialize the regmatch used by vimgrep for pattern "s".
+static void vgr_init_regmatch(regmmatch_T *regmatch, char_u *s)
+{
+ // Get the search pattern: either white-separated or enclosed in //.
+ regmatch->regprog = NULL;
+
+ if (s == NULL || *s == NUL) {
+ // Pattern is empty, use last search pattern.
+ if (last_search_pat() == NULL) {
+ EMSG(_(e_noprevre));
+ return;
+ }
+ regmatch->regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
} else {
- if (au_name != NULL)
- apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, NULL, FALSE, curbuf);
+ regmatch->regprog = vim_regcomp(s, RE_MAGIC);
+ }
+
+ regmatch->rmm_ic = p_ic;
+ regmatch->rmm_maxcol = 0;
+}
+
+
+/// Display a file name when vimgrep is running.
+static void vgr_display_fname(char_u *fname)
+{
+ msg_start();
+ char_u *p = msg_strtrunc(fname, true);
+ if (p == NULL) {
+ msg_outtrans(fname);
+ } else {
+ msg_outtrans(p);
+ xfree(p);
+ }
+ msg_clr_eos();
+ msg_didout = false; // overwrite this message
+ msg_nowait = true; // don't wait for this message
+ msg_col = 0;
+ ui_flush();
+}
+
+/// Load a dummy buffer to search for a pattern using vimgrep.
+static buf_T *vgr_load_dummy_buf(char_u *fname, char_u *dirname_start,
+ char_u *dirname_now)
+{
+ char_u *save_ei = NULL;
+
+ // Don't do Filetype autocommands to avoid loading syntax and
+ // indent scripts, a great speed improvement.
+ long save_mls = p_mls;
+ p_mls = 0;
+
+ // Load file into a buffer, so that 'fileencoding' is detected,
+ // autocommands applied, etc.
+ buf_T *buf = load_dummy_buffer(fname, dirname_start, dirname_now);
+
+ p_mls = save_mls;
+ au_event_restore(save_ei);
+
+ return buf;
+}
+
+/// Check whether a quickfix/location list is valid. Autocmds may remove or
+/// change a quickfix list when vimgrep is running. If the list is not found,
+/// create a new list.
+static bool vgr_qflist_valid(qf_info_T *qi, unsigned save_qfid,
+ qfline_T *cur_qf_start, int loclist_cmd,
+ char_u *title)
+{
+ if (loclist_cmd) {
+ // Verify that the location list is still valid. An autocmd might have
+ // freed the location list.
+ if (!qflist_valid(curwin, save_qfid)) {
+ EMSG(_(e_loc_list_changed));
+ return false;
+ }
+ }
+ if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start) {
+ int idx;
+ // Autocommands changed the quickfix list. Find the one we were using
+ // and restore it.
+ for (idx = 0; idx < LISTCOUNT; idx++) {
+ if (cur_qf_start == qi->qf_lists[idx].qf_start) {
+ qi->qf_curlist = idx;
+ break;
+ }
+ }
+ if (idx == LISTCOUNT) {
+ // List cannot be found, create a new one.
+ qf_new_list(qi, title);
+ }
+ }
+
+ return true;
+}
+
+
+/// Search for a pattern in all the lines in a buffer and add the matching lines
+/// to a quickfix list.
+static bool vgr_match_buflines(qf_info_T *qi, char_u *fname, buf_T *buf,
+ regmmatch_T *regmatch, long tomatch,
+ int duplicate_name, int flags)
+{
+ bool found_match = false;
+
+ for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0; lnum++) {
+ colnr_T col = 0;
+ while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL,
+ NULL) > 0) {
+ // Pass the buffer number so that it gets used even for a
+ // dummy buffer, unless duplicate_name is set, then the
+ // buffer will be wiped out below.
+ if (qf_add_entry(qi,
+ qi->qf_curlist,
+ NULL, // dir
+ fname,
+ NULL,
+ duplicate_name ? 0 : buf->b_fnum,
+ ml_get_buf(buf, regmatch->startpos[0].lnum + lnum,
+ false),
+ regmatch->startpos[0].lnum + lnum,
+ regmatch->startpos[0].col + 1,
+ false, // vis_col
+ NULL, // search pattern
+ 0, // nr
+ 0, // type
+ true // valid
+ ) == FAIL) {
+ got_int = true;
+ break;
+ }
+ found_match = true;
+ if (--tomatch == 0) {
+ break;
+ }
+ if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) {
+ break;
+ }
+ col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col);
+ if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) {
+ break;
+ }
+ }
+ line_breakcheck();
+ if (got_int) {
+ break;
+ }
+ }
+
+ return found_match;
+}
+
+/// Jump to the first match and update the directory.
+static void vgr_jump_to_match(qf_info_T *qi, int forceit, int *redraw_for_dummy,
+ buf_T *first_match_buf, char_u *target_dir)
+{
+ buf_T *buf = curbuf;
+ qf_jump(qi, 0, 0, forceit);
+ if (buf != curbuf) {
+ // If we jumped to another buffer redrawing will already be
+ // taken care of.
+ *redraw_for_dummy = false;
+ }
+
+ // Jump to the directory used after loading the buffer.
+ if (curbuf == first_match_buf && target_dir != NULL) {
+ exarg_T ea = {
+ .arg = target_dir,
+ .cmdidx = CMD_lcd,
+ };
+ ex_cd(&ea);
}
}
@@ -3578,8 +4023,9 @@ void ex_vimgrep(exarg_T *eap)
char_u *p;
int fi;
qf_info_T *qi = &ql_info;
+ int loclist_cmd = false;
qfline_T *cur_qf_start;
- long lnum;
+ win_T *wp;
buf_T *buf;
int duplicate_name = FALSE;
int using_dummy;
@@ -3587,28 +4033,15 @@ void ex_vimgrep(exarg_T *eap)
int found_match;
buf_T *first_match_buf = NULL;
time_t seconds = 0;
- long save_mls;
- char_u *save_ei = NULL;
aco_save_T aco;
int flags = 0;
- colnr_T col;
long tomatch;
char_u *dirname_start = NULL;
char_u *dirname_now = NULL;
char_u *target_dir = NULL;
char_u *au_name = NULL;
- switch (eap->cmdidx) {
- case CMD_vimgrep: au_name = (char_u *)"vimgrep"; break;
- case CMD_lvimgrep: au_name = (char_u *)"lvimgrep"; break;
- case CMD_vimgrepadd: au_name = (char_u *)"vimgrepadd"; break;
- case CMD_lvimgrepadd: au_name = (char_u *)"lvimgrepadd"; break;
- case CMD_grep: au_name = (char_u *)"grep"; break;
- case CMD_lgrep: au_name = (char_u *)"lgrep"; break;
- case CMD_grepadd: au_name = (char_u *)"grepadd"; break;
- case CMD_lgrepadd: au_name = (char_u *)"lgrepadd"; break;
- default: break;
- }
+ au_name = vgr_get_auname(eap->cmdidx);
if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
if (aborting()) {
@@ -3621,6 +4054,7 @@ void ex_vimgrep(exarg_T *eap)
|| eap->cmdidx == CMD_lgrepadd
|| eap->cmdidx == CMD_lvimgrepadd) {
qi = ll_get_or_alloc_list(curwin);
+ loclist_cmd = true;
}
if (eap->addr_count > 0)
@@ -3630,28 +4064,17 @@ void ex_vimgrep(exarg_T *eap)
/* Get the search pattern: either white-separated or enclosed in // */
regmatch.regprog = NULL;
- char_u *title = vim_strsave(*eap->cmdlinep);
+ char_u *title = vim_strsave(qf_cmdtitle(*eap->cmdlinep));
p = skip_vimgrep_pat(eap->arg, &s, &flags);
if (p == NULL) {
EMSG(_(e_invalpat));
goto theend;
}
- if (s == NULL || *s == NUL) {
- // Pattern is empty, use last search pattern.
- if (last_search_pat() == NULL) {
- EMSG(_(e_noprevre));
- goto theend;
- }
- regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
- } else {
- regmatch.regprog = vim_regcomp(s, RE_MAGIC);
- }
-
- if (regmatch.regprog == NULL)
+ vgr_init_regmatch(&regmatch, s);
+ if (regmatch.regprog == NULL) {
goto theend;
- regmatch.rmm_ic = p_ic;
- regmatch.rmm_maxcol = 0;
+ }
p = skipwhite(p);
if (*p == NUL) {
@@ -3681,8 +4104,9 @@ void ex_vimgrep(exarg_T *eap)
* ":lcd %:p:h" changes the meaning of short path names. */
os_dirname(dirname_start, MAXPATHL);
- /* Remember the value of qf_start, so that we can check for autocommands
- * changing the current quickfix list. */
+ // Remember the current values of the quickfix list and qf_start, so that
+ // we can check for autocommands changing the current quickfix list.
+ unsigned save_qfid = qi->qf_lists[qi->qf_curlist].qf_id;
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
seconds = (time_t)0;
@@ -3692,19 +4116,7 @@ void ex_vimgrep(exarg_T *eap)
/* Display the file name every second or so, show the user we are
* working on it. */
seconds = time(NULL);
- msg_start();
- p = msg_strtrunc(fname, TRUE);
- if (p == NULL)
- msg_outtrans(fname);
- else {
- msg_outtrans(p);
- xfree(p);
- }
- msg_clr_eos();
- msg_didout = FALSE; /* overwrite this message */
- msg_nowait = TRUE; /* don't wait for this message */
- msg_col = 0;
- ui_flush();
+ vgr_display_fname(fname);
}
buf = buflist_findname_exp(fnames[fi]);
@@ -3714,88 +4126,28 @@ void ex_vimgrep(exarg_T *eap)
using_dummy = TRUE;
redraw_for_dummy = TRUE;
- /* Don't do Filetype autocommands to avoid loading syntax and
- * indent scripts, a great speed improvement. */
- save_ei = au_event_disable(",Filetype");
- /* Don't use modelines here, it's useless. */
- save_mls = p_mls;
- p_mls = 0;
-
- /* Load file into a buffer, so that 'fileencoding' is detected,
- * autocommands applied, etc. */
- buf = load_dummy_buffer(fname, dirname_start, dirname_now);
-
- p_mls = save_mls;
- au_event_restore(save_ei);
- } else
- /* Use existing, loaded buffer. */
- using_dummy = FALSE;
-
- if (cur_qf_start != qi->qf_lists[qi->qf_curlist].qf_start) {
- int idx;
+ buf = vgr_load_dummy_buf(fname, dirname_start, dirname_now);
+ } else {
+ // Use existing, loaded buffer.
+ using_dummy = false;
+ }
- /* Autocommands changed the quickfix list. Find the one we were
- * using and restore it. */
- for (idx = 0; idx < LISTCOUNT; ++idx)
- if (cur_qf_start == qi->qf_lists[idx].qf_start) {
- qi->qf_curlist = idx;
- break;
- }
- if (idx == LISTCOUNT) {
- /* List cannot be found, create a new one. */
- qf_new_list(qi, *eap->cmdlinep);
- cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
- }
+ // Check whether the quickfix list is still valid
+ if (!vgr_qflist_valid(qi, save_qfid, cur_qf_start, loclist_cmd,
+ *eap->cmdlinep)) {
+ goto theend;
}
+ cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
if (buf == NULL) {
if (!got_int)
smsg(_("Cannot open file \"%s\""), fname);
} else {
- /* Try for a match in all lines of the buffer.
- * For ":1vimgrep" look for first match only. */
- found_match = FALSE;
- for (lnum = 1; lnum <= buf->b_ml.ml_line_count && tomatch > 0;
- ++lnum) {
- col = 0;
- while (vim_regexec_multi(&regmatch, curwin, buf, lnum,
- col, NULL, NULL) > 0) {
- // Pass the buffer number so that it gets used even for a
- // dummy buffer, unless duplicate_name is set, then the
- // buffer will be wiped out below.
- if (qf_add_entry(qi,
- qi->qf_curlist,
- NULL, // dir
- fname,
- duplicate_name ? 0 : buf->b_fnum,
- ml_get_buf(buf,
- regmatch.startpos[0].lnum + lnum, false),
- regmatch.startpos[0].lnum + lnum,
- regmatch.startpos[0].col + 1,
- false, // vis_col
- NULL, // search pattern
- 0, // nr
- 0, // type
- true) // valid
- == FAIL) {
- got_int = true;
- break;
- }
- found_match = TRUE;
- if (--tomatch == 0)
- break;
- if ((flags & VGR_GLOBAL) == 0
- || regmatch.endpos[0].lnum > 0)
- break;
- col = regmatch.endpos[0].col
- + (col == regmatch.endpos[0].col);
- if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, FALSE)))
- break;
- }
- line_breakcheck();
- if (got_int)
- break;
- }
+ // Try for a match in all lines of the buffer.
+ // For ":1vimgrep" look for first match only.
+ found_match = vgr_match_buflines(qi, fname, buf, &regmatch, tomatch,
+ duplicate_name, flags);
+
cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start;
if (using_dummy) {
@@ -3858,6 +4210,7 @@ void ex_vimgrep(exarg_T *eap)
qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE;
qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start;
qi->qf_lists[qi->qf_curlist].qf_index = 1;
+ qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL);
@@ -3865,24 +4218,18 @@ void ex_vimgrep(exarg_T *eap)
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, TRUE, curbuf);
+ // The QuickFixCmdPost autocmd may free the quickfix list. Check the list
+ // is still valid.
+ wp = loclist_cmd ? curwin : NULL;
+ if (!qflist_valid(wp, save_qfid)) {
+ goto theend;
+ }
+
/* Jump to first match. */
if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
if ((flags & VGR_NOJUMP) == 0) {
- buf = curbuf;
- qf_jump(qi, 0, 0, eap->forceit);
- if (buf != curbuf)
- /* If we jumped to another buffer redrawing will already be
- * taken care of. */
- redraw_for_dummy = FALSE;
-
- /* Jump to the directory used after loading the buffer. */
- if (curbuf == first_match_buf && target_dir != NULL) {
- exarg_T ea;
-
- ea.arg = target_dir;
- ea.cmdidx = CMD_lcd;
- ex_cd(&ea);
- }
+ vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf,
+ target_dir);
}
} else
EMSG2(_(e_nomatch2), s);
@@ -3913,10 +4260,10 @@ static void restore_start_dir(char_u *dirname_start)
if (STRCMP(dirname_start, dirname_now) != 0) {
/* If the directory has changed, change it back by building up an
* appropriate ex command and executing it. */
- exarg_T ea;
-
- ea.arg = dirname_start;
- ea.cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd;
+ exarg_T ea = {
+ .arg = dirname_start,
+ .cmdidx = (curwin->w_localdir == NULL) ? CMD_cd : CMD_lcd,
+ };
ex_cd(&ea);
}
xfree(dirname_now);
@@ -4112,6 +4459,10 @@ int get_errorlist(const qf_info_T *qi_arg, win_T *wp, int qf_idx, list_T *list)
|| (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol)
== FAIL)
|| (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL)
+ || tv_dict_add_str(dict, S_LEN("module"),
+ (qfp->qf_module == NULL
+ ? ""
+ : (const char *)qfp->qf_module)) == FAIL
|| tv_dict_add_str(dict, S_LEN("pattern"),
(qfp->qf_pattern == NULL
? ""
@@ -4145,10 +4496,14 @@ enum {
QF_GETLIST_WINID = 0x8,
QF_GETLIST_CONTEXT = 0x10,
QF_GETLIST_ID = 0x20,
- QF_GETLIST_ALL = 0xFF
+ QF_GETLIST_IDX = 0x40,
+ QF_GETLIST_SIZE = 0x80,
+ QF_GETLIST_TICK = 0x100,
+ QF_GETLIST_ALL = 0x1FF
};
-// Parse text from 'di' and return the quickfix list items
+/// Parse text from 'di' and return the quickfix list items.
+/// Existing quickfix lists are not modified.
static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
{
int status = FAIL;
@@ -4186,35 +4541,85 @@ static int qf_get_list_from_lines(dict_T *what, dictitem_T *di, dict_T *retdict)
return status;
}
-/// Return quickfix/location list details (title) as a
-/// dictionary. 'what' contains the details to return. If 'list_idx' is -1,
-/// then current list is used. Otherwise the specified list is used.
-int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
+// Return the quickfix/location list number with the given identifier.
+// Returns -1 if list is not found.
+static int qf_id2nr(const qf_info_T *const qi, const unsigned qfid)
{
- qf_info_T *qi = &ql_info;
- dictitem_T *di;
-
- if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) {
- return qf_get_list_from_lines(what, di, retdict);
+ for (int qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) {
+ if (qi->qf_lists[qf_idx].qf_id == qfid) {
+ return qf_idx;
+ }
}
+ return -1;
+}
- if (wp != NULL) {
- qi = GET_LOC_LIST(wp);
+/// Return the quickfix/location list window identifier in the current tabpage.
+static int qf_winid(qf_info_T *qi)
+{
+ // The quickfix window can be opened even if the quickfix list is not set
+ // using ":copen". This is not true for location lists.
+ if (qi == NULL) {
+ return 0;
}
- // List is not present or is empty
- if (qi == NULL || qi->qf_listcount == 0) {
- // If querying for the size of the list, return 0
- if (((di = tv_dict_find(what, S_LEN("nr"))) != NULL)
- && (di->di_tv.v_type == VAR_STRING)
- && (STRCMP(di->di_tv.vval.v_string, "$") == 0)) {
- return tv_dict_add_nr(retdict, S_LEN("nr"), 0);
- }
- return FAIL;
+ win_T *win = qf_find_win(qi);
+ if (win != NULL) {
+ return win->handle;
}
+ return 0;
+}
- int status = OK;
+/// Convert the keys in 'what' to quickfix list property flags.
+static int qf_getprop_keys2flags(dict_T *what)
+{
int flags = QF_GETLIST_NONE;
+ if (tv_dict_find(what, S_LEN("all")) != NULL) {
+ flags |= QF_GETLIST_ALL;
+ }
+ if (tv_dict_find(what, S_LEN("title")) != NULL) {
+ flags |= QF_GETLIST_TITLE;
+ }
+ if (tv_dict_find(what, S_LEN("nr")) != NULL) {
+ flags |= QF_GETLIST_NR;
+ }
+ if (tv_dict_find(what, S_LEN("winid")) != NULL) {
+ flags |= QF_GETLIST_WINID;
+ }
+ if (tv_dict_find(what, S_LEN("context")) != NULL) {
+ flags |= QF_GETLIST_CONTEXT;
+ }
+ if (tv_dict_find(what, S_LEN("id")) != NULL) {
+ flags |= QF_GETLIST_ID;
+ }
+ if (tv_dict_find(what, S_LEN("items")) != NULL) {
+ flags |= QF_GETLIST_ITEMS;
+ }
+ if (tv_dict_find(what, S_LEN("idx")) != NULL) {
+ flags |= QF_GETLIST_IDX;
+ }
+ if (tv_dict_find(what, S_LEN("size")) != NULL) {
+ flags |= QF_GETLIST_SIZE;
+ }
+ if (tv_dict_find(what, S_LEN("changedtick")) != NULL) {
+ flags |= QF_GETLIST_TICK;
+ }
+
+ return flags;
+}
+
+/// Return the quickfix list index based on 'nr' or 'id' in 'what'.
+///
+/// If 'nr' and 'id' are not present in 'what' then return the current
+/// quickfix list index.
+/// If 'nr' is zero then return the current quickfix list index.
+/// If 'nr' is '$' then return the last quickfix list index.
+/// If 'id' is present then return the index of the quickfix list with that id.
+/// If 'id' is zero then return the quickfix list index specified by 'nr'.
+/// Return -1, if quickfix list is not present or if the stack is empty.
+static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what)
+{
+ dictitem_T *di = NULL;
+
int qf_idx = qi->qf_curlist; // default is the current list
if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {
// Use the specified quickfix/location list
@@ -4223,7 +4628,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
if (di->di_tv.vval.v_number != 0) {
qf_idx = (int)di->di_tv.vval.v_number - 1;
if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
- return FAIL;
+ qf_idx = -1;
}
}
} else if (di->di_tv.v_type == VAR_STRING
@@ -4231,9 +4636,8 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
// Get the last quickfix list number
qf_idx = qi->qf_listcount - 1;
} else {
- return FAIL;
+ qf_idx = -1;
}
- flags |= QF_GETLIST_NR;
}
if ((di = tv_dict_find(what, S_LEN("id"))) != NULL) {
@@ -4241,75 +4645,161 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
if (di->di_tv.v_type == VAR_NUMBER) {
// For zero, use the current list or the list specifed by 'nr'
if (di->di_tv.vval.v_number != 0) {
- for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) {
- if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) {
- break;
- }
- }
- if (qf_idx == qi->qf_listcount) {
- return FAIL; // List not found
- }
+ qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number);
}
- flags |= QF_GETLIST_ID;
} else {
- return FAIL;
+ qf_idx = -1;
}
}
- if (tv_dict_find(what, S_LEN("all")) != NULL) {
- flags |= QF_GETLIST_ALL;
+ return qf_idx;
+}
+
+/// Return default values for quickfix list properties in retdict.
+static int qf_getprop_defaults(qf_info_T *qi, int flags, dict_T *retdict)
+{
+ int status = OK;
+
+ if (flags & QF_GETLIST_TITLE) {
+ status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)"");
}
- if (tv_dict_find(what, S_LEN("title")) != NULL) {
- flags |= QF_GETLIST_TITLE;
+ if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
+ list_T *l = tv_list_alloc(kListLenMayKnow);
+ status = tv_dict_add_list(retdict, S_LEN("items"), l);
}
- if (tv_dict_find(what, S_LEN("winid")) != NULL) {
- flags |= QF_GETLIST_WINID;
+ if ((status == OK) && (flags & QF_GETLIST_NR)) {
+ status = tv_dict_add_nr(retdict, S_LEN("nr"), 0);
}
- if (tv_dict_find(what, S_LEN("context")) != NULL) {
- flags |= QF_GETLIST_CONTEXT;
+ if ((status == OK) && (flags & QF_GETLIST_WINID)) {
+ status = tv_dict_add_nr(retdict, S_LEN("winid"), qf_winid(qi));
}
- if (tv_dict_find(what, S_LEN("items")) != NULL) {
- flags |= QF_GETLIST_ITEMS;
+ if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
+ status = tv_dict_add_str(retdict, S_LEN("context"), (const char *)"");
+ }
+ if ((status == OK) && (flags & QF_GETLIST_ID)) {
+ status = tv_dict_add_nr(retdict, S_LEN("id"), 0);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_IDX)) {
+ status = tv_dict_add_nr(retdict, S_LEN("idx"), 0);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_SIZE)) {
+ status = tv_dict_add_nr(retdict, S_LEN("size"), 0);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_TICK)) {
+ status = tv_dict_add_nr(retdict, S_LEN("changedtick"), 0);
}
- if (flags & QF_GETLIST_TITLE) {
+ return status;
+}
+
+/// Return the quickfix list title as 'title' in retdict
+static int qf_getprop_title(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
char_u *t = qi->qf_lists[qf_idx].qf_title;
if (t == NULL) {
t = (char_u *)"";
}
- status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)t);
+ return tv_dict_add_str(retdict, S_LEN("title"), (const char *)t);
+}
+
+/// Return the quickfix list items/entries as 'items' in retdict
+static int qf_getprop_items(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
+ list_T *l = tv_list_alloc(kListLenMayKnow);
+ get_errorlist(qi, NULL, qf_idx, l);
+ tv_dict_add_list(retdict, S_LEN("items"), l);
+
+ return OK;
+}
+
+/// Return the quickfix list context (if any) as 'context' in retdict.
+static int qf_getprop_ctx(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
+ int status;
+
+ if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
+ dictitem_T *di = tv_dict_item_alloc_len(S_LEN("context"));
+ tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
+ status = tv_dict_add(retdict, di);
+ if (status == FAIL) {
+ tv_dict_item_free(di);
+ }
+ } else {
+ status = tv_dict_add_str(retdict, S_LEN("context"), "");
+ }
+
+ return status;
+}
+
+/// Return the quickfix list index as 'idx' in retdict
+static int qf_getprop_idx(qf_info_T *qi, int qf_idx, dict_T *retdict)
+{
+ int idx = qi->qf_lists[qf_idx].qf_index;
+ if (qi->qf_lists[qf_idx].qf_count == 0) {
+ // For empty lists, qf_index is set to 1
+ idx = 0;
+ }
+ return tv_dict_add_nr(retdict, S_LEN("idx"), idx);
+}
+
+/// Return quickfix/location list details (title) as a dictionary.
+/// 'what' contains the details to return. If 'list_idx' is -1,
+/// then current list is used. Otherwise the specified list is used.
+int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
+{
+ qf_info_T *qi = &ql_info;
+ dictitem_T *di = NULL;
+ int status = OK;
+ int qf_idx;
+
+ if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) {
+ return qf_get_list_from_lines(what, di, retdict);
+ }
+
+ if (wp != NULL) {
+ qi = GET_LOC_LIST(wp);
+ }
+
+ int flags = qf_getprop_keys2flags(what);
+
+ if (qi != NULL && qi->qf_listcount != 0) {
+ qf_idx = qf_getprop_qfidx(qi, what);
+ }
+
+ // List is not present or is empty
+ if (qi == NULL || qi->qf_listcount == 0 || qf_idx == -1) {
+ return qf_getprop_defaults(qi, flags, retdict);
+ }
+
+ if (flags & QF_GETLIST_TITLE) {
+ status = qf_getprop_title(qi, qf_idx, retdict);
}
if ((status == OK) && (flags & QF_GETLIST_NR)) {
status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1);
}
if ((status == OK) && (flags & QF_GETLIST_WINID)) {
- win_T *win = qf_find_win(qi);
- if (win != NULL) {
- status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle);
- }
+ status = tv_dict_add_nr(retdict, S_LEN("winid"), qf_winid(qi));
}
if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
- list_T *l = tv_list_alloc(kListLenMayKnow);
- (void)get_errorlist(qi, NULL, qf_idx, l);
- tv_dict_add_list(retdict, S_LEN("items"), l);
+ status = qf_getprop_items(qi, qf_idx, retdict);
}
-
if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
- if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
- di = tv_dict_item_alloc_len(S_LEN("context"));
- tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
- status = tv_dict_add(retdict, di);
- if (status == FAIL) {
- tv_dict_item_free(di);
- }
- } else {
- status = tv_dict_add_str(retdict, S_LEN("context"), "");
- }
+ status = qf_getprop_ctx(qi, qf_idx, retdict);
}
-
if ((status == OK) && (flags & QF_GETLIST_ID)) {
status = tv_dict_add_nr(retdict, S_LEN("id"), qi->qf_lists[qf_idx].qf_id);
}
+ if ((status == OK) && (flags & QF_GETLIST_IDX)) {
+ status = qf_getprop_idx(qi, qf_idx, retdict);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_SIZE)) {
+ status = tv_dict_add_nr(retdict, S_LEN("size"),
+ qi->qf_lists[qf_idx].qf_count);
+ }
+ if ((status == OK) && (flags & QF_GETLIST_TICK)) {
+ status = tv_dict_add_nr(retdict, S_LEN("changedtick"),
+ qi->qf_lists[qf_idx].qf_changedtick);
+ }
return status;
}
@@ -4347,6 +4837,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
}
char *const filename = tv_dict_get_string(d, "filename", true);
+ char *const module = tv_dict_get_string(d, "module", true);
int bufnum = (int)tv_dict_get_number(d, "bufnr");
long lnum = (long)tv_dict_get_number(d, "lnum");
int col = (int)tv_dict_get_number(d, "col");
@@ -4384,6 +4875,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
qf_idx,
NULL, // dir
(char_u *)filename,
+ (char_u *)module,
bufnum,
(char_u *)text,
lnum,
@@ -4395,6 +4887,7 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list,
valid);
xfree(filename);
+ xfree(module);
xfree(pattern);
xfree(text);
@@ -4471,12 +4964,8 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action,
if (!newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) {
// Use the quickfix/location list with the specified id
if (di->di_tv.v_type == VAR_NUMBER) {
- for (qf_idx = 0; qf_idx < qi->qf_listcount; qf_idx++) {
- if (qi->qf_lists[qf_idx].qf_id == di->di_tv.vval.v_number) {
- break;
- }
- }
- if (qf_idx == qi->qf_listcount) {
+ qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number);
+ if (qf_idx == -1) {
return FAIL; // List not found
}
} else {
@@ -4508,6 +4997,13 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action,
retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list,
title_save, action == ' ' ? 'a' : action);
+ if (action == 'r') {
+ // When replacing the quickfix list entries using
+ // qf_add_entries(), the title is set with a ':' prefix.
+ // Restore the title with the saved title.
+ xfree(qi->qf_lists[qf_idx].qf_title);
+ qi->qf_lists[qf_idx].qf_title = vim_strsave(title_save);
+ }
xfree(title_save);
}
}
@@ -4543,6 +5039,10 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action,
retval = OK;
}
+ if (retval == OK) {
+ qf_list_changed(qi, qf_idx);
+ }
+
return retval;
}
@@ -4624,11 +5124,15 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,
retval = qf_set_properties(qi, what, action, title);
} else {
retval = qf_add_entries(qi, qi->qf_curlist, list, title, action);
+ if (retval == OK) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
}
return retval;
}
+/// Mark the context as in use for all the lists in a quickfix stack.
static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
{
bool abort = false;
@@ -4645,7 +5149,7 @@ static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
}
/// Mark the context of the quickfix list and the location lists (if present) as
-/// "in use". So that garabage collection doesn't free the context.
+/// "in use". So that garbage collection doesn't free the context.
bool set_ref_in_quickfix(int copyID)
{
bool abort = mark_quickfix_ctx(&ql_info, copyID);
@@ -4730,10 +5234,10 @@ void ex_cbuffer(exarg_T *eap)
eap->line2 = buf->b_ml.ml_line_count;
}
if (eap->line1 < 1 || eap->line1 > buf->b_ml.ml_line_count
- || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count)
+ || eap->line2 < 1 || eap->line2 > buf->b_ml.ml_line_count) {
EMSG(_(e_invrange));
- else {
- char_u *qf_title = *eap->cmdlinep;
+ } else {
+ char_u *qf_title = qf_cmdtitle(*eap->cmdlinep);
if (buf->b_sfname) {
vim_snprintf((char *)IObuff, IOSIZE, "%s (%s)",
@@ -4741,17 +5245,20 @@ void ex_cbuffer(exarg_T *eap)
qf_title = IObuff;
}
- if (qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
- (eap->cmdidx != CMD_caddbuffer
- && eap->cmdidx != CMD_laddbuffer),
- eap->line1, eap->line2, qf_title, NULL) > 0) {
- if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
- curbuf->b_fname, true, curbuf);
- }
- if (eap->cmdidx == CMD_cbuffer || eap->cmdidx == CMD_lbuffer) {
- qf_jump(qi, 0, 0, eap->forceit); // display first error
- }
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, buf, NULL, p_efm,
+ (eap->cmdidx != CMD_caddbuffer
+ && eap->cmdidx != CMD_laddbuffer),
+ eap->line1, eap->line2, qf_title, NULL);
+ if (res >= 0) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
+ if (au_name != NULL) {
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
+ curbuf->b_fname, true, curbuf);
+ }
+ if (res > 0 && (eap->cmdidx == CMD_cbuffer
+ || eap->cmdidx == CMD_lbuffer)) {
+ qf_jump(qi, 0, 0, eap->forceit); // display first error
}
}
}
@@ -4766,11 +5273,6 @@ void ex_cexpr(exarg_T *eap)
qf_info_T *qi = &ql_info;
const char *au_name = NULL;
- if (eap->cmdidx == CMD_lexpr || eap->cmdidx == CMD_lgetexpr
- || eap->cmdidx == CMD_laddexpr) {
- qi = ll_get_or_alloc_list(curwin);
- }
-
switch (eap->cmdidx) {
case CMD_cexpr:
au_name = "cexpr";
@@ -4800,23 +5302,32 @@ void ex_cexpr(exarg_T *eap)
}
}
+ if (eap->cmdidx == CMD_lexpr
+ || eap->cmdidx == CMD_lgetexpr
+ || eap->cmdidx == CMD_laddexpr) {
+ qi = ll_get_or_alloc_list(curwin);
+ }
+
/* Evaluate the expression. When the result is a string or a list we can
* use it to fill the errorlist. */
typval_T tv;
if (eval0(eap->arg, &tv, NULL, true) != FAIL) {
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
|| tv.v_type == VAR_LIST) {
- if (qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
- (eap->cmdidx != CMD_caddexpr
- && eap->cmdidx != CMD_laddexpr),
- (linenr_T)0, (linenr_T)0, *eap->cmdlinep, NULL) > 0) {
- if (au_name != NULL) {
- apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
- curbuf->b_fname, true, curbuf);
- }
- if (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr) {
- qf_jump(qi, 0, 0, eap->forceit); // display first error
- }
+ int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
+ (eap->cmdidx != CMD_caddexpr
+ && eap->cmdidx != CMD_laddexpr),
+ (linenr_T)0, (linenr_T)0,
+ qf_cmdtitle(*eap->cmdlinep), NULL);
+ if (res >= 0) {
+ qf_list_changed(qi, qi->qf_curlist);
+ }
+ if (au_name != NULL) {
+ apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name,
+ curbuf->b_fname, true, curbuf);
+ }
+ if (res > 0 && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)) {
+ qf_jump(qi, 0, 0, eap->forceit); // display first error
}
} else {
EMSG(_("E777: String or List expected"));
@@ -4890,16 +5401,13 @@ void ex_helpgrep(exarg_T *eap)
}
}
- // Autocommands may change the list. Save it for later comparison
- qf_info_T *save_qi = qi;
-
regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING);
regmatch.rm_ic = FALSE;
if (regmatch.regprog != NULL) {
// Create a new quickfix list.
- qf_new_list(qi, *eap->cmdlinep);
+ qf_new_list(qi, qf_cmdtitle(*eap->cmdlinep));
- /* Go through all directories in 'runtimepath' */
+ // Go through all the directories in 'runtimepath'
p = p_rtp;
while (*p != NUL && !got_int) {
copy_option_part(&p, NameBuff, MAXPATHL, ",");
@@ -4914,15 +5422,15 @@ void ex_helpgrep(exarg_T *eap)
if (gen_expand_wildcards(1, buff_list, &fcount,
&fnames, EW_FILE|EW_SILENT) == OK
&& fcount > 0) {
- for (fi = 0; fi < fcount && !got_int; ++fi) {
- /* Skip files for a different language. */
+ for (fi = 0; fi < fcount && !got_int; fi++) {
+ // Skip files for a different language.
if (lang != NULL
- && STRNICMP(lang, fnames[fi]
- + STRLEN(fnames[fi]) - 3, 2) != 0
+ && STRNICMP(lang, fnames[fi] + STRLEN(fnames[fi]) - 3, 2) != 0
&& !(STRNICMP(lang, "en", 2) == 0
&& STRNICMP("txt", fnames[fi]
- + STRLEN(fnames[fi]) - 3, 3) == 0))
+ + STRLEN(fnames[fi]) - 3, 3) == 0)) {
continue;
+ }
fd = mch_fopen((char *)fnames[fi], "r");
if (fd != NULL) {
lnum = 1;
@@ -4939,6 +5447,7 @@ void ex_helpgrep(exarg_T *eap)
qi->qf_curlist,
NULL, // dir
fnames[fi],
+ NULL,
0,
line,
lnum,
@@ -4983,12 +5492,13 @@ void ex_helpgrep(exarg_T *eap)
/* Darn, some plugin changed the value. */
free_string_option(save_cpo);
+ qf_list_changed(qi, qi->qf_curlist);
qf_update_buffer(qi, NULL);
if (au_name != NULL) {
apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
curbuf->b_fname, true, curbuf);
- if (!new_qi && qi != save_qi && qf_find_buf(qi) == NULL) {
+ if (!new_qi && qi != &ql_info && qf_find_buf(qi) == NULL) {
// autocommands made "qi" invalid
return;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index ab1a7c7b3e..39ce7ff844 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -5098,8 +5098,6 @@ static int regmatch(
printf("Premature EOL\n");
#endif
}
- if (status == RA_FAIL)
- got_int = TRUE;
return status == RA_MATCH;
}
@@ -6963,10 +6961,11 @@ char_u *reg_submatch(int no)
return NULL;
}
- s = reg_getline_submatch(lnum) + rsm.sm_mmatch->startpos[no].col;
+ s = reg_getline_submatch(lnum);
if (s == NULL) { // anti-crash check, cannot happen?
break;
}
+ s += rsm.sm_mmatch->startpos[no].col;
if (rsm.sm_mmatch->endpos[no].lnum == lnum) {
// Within one line: take form start to end col.
len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col;
@@ -7225,7 +7224,8 @@ static void report_re_switch(char_u *pat)
/// @param nl
///
/// @return TRUE if there is a match, FALSE if not.
-static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
+static int vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col,
+ bool nl)
{
regexec_T rex_save;
bool rex_in_use_save = rex_in_use;
@@ -7274,8 +7274,8 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
int vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line,
colnr_T col)
{
- regmatch_T regmatch = {.regprog = *prog, .rm_ic = ignore_case};
- int r = vim_regexec_both(&regmatch, line, col, false);
+ regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case };
+ int r = vim_regexec_string(&regmatch, line, col, false);
*prog = regmatch.regprog;
return r;
}
@@ -7284,7 +7284,7 @@ int vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line,
// Return TRUE if there is a match, FALSE if not.
int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
{
- return vim_regexec_both(rmp, line, col, false);
+ return vim_regexec_string(rmp, line, col, false);
}
// Like vim_regexec(), but consider a "\n" in "line" to be a line break.
@@ -7292,7 +7292,7 @@ int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
// Return TRUE if there is a match, FALSE if not.
int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
{
- return vim_regexec_both(rmp, line, col, true);
+ return vim_regexec_string(rmp, line, col, true);
}
/// Match a regexp against multiple lines.
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index 298d82fc6b..5e5b19b63f 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -94,10 +94,8 @@ typedef struct {
char_u program[1]; /* actually longer.. */
} bt_regprog_T;
-/*
- * Structure representing a NFA state.
- * A NFA state may have no outgoing edge, when it is a NFA_MATCH state.
- */
+// Structure representing a NFA state.
+// An NFA state may have no outgoing edge, when it is a NFA_MATCH state.
typedef struct nfa_state nfa_state_T;
struct nfa_state {
int c;
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index e0e8820b87..ce7270ae65 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -4582,7 +4582,9 @@ static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos)
* "pim" is NULL or contains info about a Postponed Invisible Match (start
* position).
*/
-static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog, regsubs_T *submatch, regsubs_T *m, int **listids)
+static int recursive_regmatch(
+ nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog,
+ regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len)
{
int save_reginput_col = (int)(reginput - regline);
int save_reglnum = reglnum;
@@ -4665,8 +4667,10 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
if (nfa_ll_index == 1) {
/* Already calling nfa_regmatch() recursively. Save the lastlist[1]
* values and clear them. */
- if (*listids == NULL) {
+ if (*listids == NULL || *listids_len < nstate) {
+ xfree(*listids);
*listids = xmalloc(sizeof(**listids) * nstate);
+ *listids_len = nstate;
}
nfa_save_listids(prog, *listids);
need_restore = TRUE;
@@ -4964,13 +4968,15 @@ static int nfa_did_time_out(void)
///
/// When "nfa_endp" is not NULL it is a required end-of-match position.
///
-/// Return TRUE if there is a match, FALSE otherwise.
+/// Return TRUE if there is a match, FALSE if there is no match,
+/// NFA_TOO_EXPENSIVE if we end up with too many states.
/// When there is a match "submatch" contains the positions.
+///
/// Note: Caller must ensure that: start != NULL.
static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
regsubs_T *submatch, regsubs_T *m)
{
- int result;
+ int result = false;
int flag = 0;
bool go_to_nextline = false;
nfa_thread_T *t;
@@ -4979,6 +4985,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
nfa_list_T *thislist;
nfa_list_T *nextlist;
int *listids = NULL;
+ int listids_len = 0;
nfa_state_T *add_state;
bool add_here;
int add_count;
@@ -5271,7 +5278,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// First try matching the invisible match, then what
// follows.
result = recursive_regmatch(t->state, NULL, prog, submatch, m,
- &listids);
+ &listids, &listids_len);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -5372,7 +5379,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// First try matching the pattern.
result = recursive_regmatch(t->state, NULL, prog, submatch, m,
- &listids);
+ &listids, &listids_len);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -6079,8 +6086,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
fprintf(log_fd, "Postponed recursive nfa_regmatch()\n");
fprintf(log_fd, "\n");
#endif
- result = recursive_regmatch(pim->state, pim,
- prog, submatch, m, &listids);
+ result = recursive_regmatch(pim->state, pim, prog, submatch, m,
+ &listids, &listids_len);
pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH;
// for \@! and \@<! it is a match when the result is
// FALSE
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 5255fd2a51..4c830bb256 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -363,7 +363,7 @@ void update_screen(int type)
need_wait_return = FALSE;
}
- if (type >= NOT_VALID) {
+ if (type >= CLEAR || !default_grid.valid) {
ui_comp_set_screen_valid(false);
}
win_ui_flush_positions();
@@ -621,6 +621,11 @@ static void win_update(win_T *wp)
linenr_T mod_bot = 0;
int save_got_int;
+ // If we can compute a change in the automatic sizing of the sign column
+ // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
+ // figuring it out here so we can redraw the entire screen for it.
+ buf_signcols(buf);
+
type = wp->w_redr_type;
win_grid_alloc(wp);
@@ -673,8 +678,6 @@ static void win_update(win_T *wp)
mod_bot = wp->w_redraw_bot + 1;
else
mod_bot = 0;
- wp->w_redraw_top = 0; /* reset for next time */
- wp->w_redraw_bot = 0;
if (buf->b_mod_set) {
if (mod_top == 0 || mod_top > buf->b_mod_top) {
mod_top = buf->b_mod_top;
@@ -771,6 +774,8 @@ static void win_update(win_T *wp)
if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu)
mod_bot = MAXLNUM;
}
+ wp->w_redraw_top = 0; // reset for next time
+ wp->w_redraw_bot = 0;
/*
* When only displaying the lines at the top, set top_end. Used when
@@ -1130,6 +1135,9 @@ static void win_update(win_T *wp)
/* reset got_int, otherwise regexp won't work */
save_got_int = got_int;
got_int = 0;
+ // Set the time limit to 'redrawtime'.
+ proftime_T syntax_tm = profile_setlimit(p_rdt);
+ syn_set_timeout(&syntax_tm);
win_foldinfo.fi_level = 0;
/*
@@ -1456,7 +1464,7 @@ static void win_update(win_T *wp)
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} else {
- win_draw_end(wp, '@', ' ', srow, wp->w_grid.Rows, HLF_AT);
+ win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.Rows, at_attr);
wp->w_botline = lnum;
}
} else {
@@ -1473,7 +1481,7 @@ static void win_update(win_T *wp)
if (row + j > wp->w_grid.Rows) {
j = wp->w_grid.Rows - row;
}
- win_draw_end(wp, i, i, row, row + (int)j, HLF_DED);
+ win_draw_end(wp, i, i, true, row, row + (int)j, HLF_DED);
row += j;
}
} else if (dollar_vcol == -1)
@@ -1481,12 +1489,14 @@ static void win_update(win_T *wp)
// make sure the rest of the screen is blank
// write the 'eob' character to rows that aren't part of the file.
- win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', row, wp->w_grid.Rows, HLF_EOB);
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.Rows,
+ HLF_EOB);
}
if (wp->w_redr_type >= REDRAW_TOP) {
draw_vsep_win(wp, 0);
}
+ syn_set_timeout(NULL);
/* Reset the type of redrawing required, the window has been updated. */
wp->w_redr_type = 0;
@@ -1543,85 +1553,66 @@ int win_signcol_width(win_T *wp)
return 2;
}
-/*
- * Clear the rest of the window and mark the unused lines with "c1". use "c2"
- * as the filler character.
- */
-static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T hl)
+/// Call grid_fill() with columns adjusted for 'rightleft' if needed.
+/// Return the new offset.
+static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
+ int endrow, int attr)
{
- int n = 0;
-# define FDC_OFF n
- int fdc = compute_foldcolumn(wp, 0);
+ int nn = off + width;
- int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl));
+ if (nn > wp->w_grid.Columns) {
+ nn = wp->w_grid.Columns;
+ }
if (wp->w_p_rl) {
- // No check for cmdline window: should never be right-left.
- n = fdc;
+ grid_fill(&wp->w_grid, row, endrow, W_ENDCOL(wp) - nn, W_ENDCOL(wp) - off,
+ c1, c2, attr);
+ } else {
+ grid_fill(&wp->w_grid, row, endrow, off, nn, c1, c2, attr);
+ }
- if (n > 0) {
- // draw the fold column at the right
- if (n > wp->w_grid.Columns) {
- n = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, wp->w_grid.Columns - n,
- wp->w_grid.Columns, ' ', ' ', win_hl_attr(wp, HLF_FC));
- }
+ return nn;
+}
- if (signcolumn_on(wp)) {
- int nn = n + win_signcol_width(wp);
+/// Clear lines near the end of the window and mark the unused lines with "c1".
+/// Use "c2" as filler character.
+/// When "draw_margin" is true, then draw the sign/fold/number columns.
+static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row,
+ int endrow, hlf_T hl)
+{
+ int n = 0;
- // draw the sign column left of the fold column
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, wp->w_grid.Columns - nn,
- wp->w_grid.Columns - n, ' ', ' ', win_hl_attr(wp, HLF_SC));
- n = nn;
+ if (draw_margin) {
+ // draw the fold column
+ int fdc = compute_foldcolumn(wp, 0);
+ if (fdc > 0) {
+ n = win_fill_end(wp, ' ', ' ', n, fdc, row, endrow,
+ win_hl_attr(wp, HLF_FC));
}
-
- grid_fill(&wp->w_grid, row, endrow, 0, wp->w_grid.Columns - 1 - FDC_OFF,
- c2, c2, attr);
- grid_fill(&wp->w_grid, row, endrow,
- wp->w_grid.Columns - 1 - FDC_OFF, wp->w_grid.Columns - FDC_OFF,
- c1, c2, attr);
- } else {
- if (cmdwin_type != 0 && wp == curwin) {
- /* draw the cmdline character in the leftmost column */
- n = 1;
- if (n > wp->w_grid.Columns) {
- n = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, 0, n, cmdwin_type, ' ',
- win_hl_attr(wp, HLF_AT));
+ // draw the sign column
+ int count = win_signcol_count(wp);
+ if (count > 0) {
+ n = win_fill_end(wp, ' ', ' ', n, win_signcol_width(wp) * count, row,
+ endrow, win_hl_attr(wp, HLF_SC));
}
- if (fdc > 0) {
- int nn = n + fdc;
-
- // draw the fold column at the left
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, n, nn, ' ', ' ',
- win_hl_attr(wp, HLF_FC));
- n = nn;
+ // draw the number column
+ if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) {
+ n = win_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, row, endrow,
+ win_hl_attr(wp, HLF_N));
}
+ }
- if (signcolumn_on(wp)) {
- int nn = n + win_signcol_width(wp);
-
- // draw the sign column after the fold column
- if (nn > wp->w_grid.Columns) {
- nn = wp->w_grid.Columns;
- }
- grid_fill(&wp->w_grid, row, endrow, n, nn, ' ', ' ',
- win_hl_attr(wp, HLF_SC));
- n = nn;
- }
+ int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl));
- grid_fill(&wp->w_grid, row, endrow, FDC_OFF, wp->w_grid.Columns, c1, c2,
- attr);
+ if (wp->w_p_rl) {
+ grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n,
+ c2, c2, attr);
+ grid_fill(&wp->w_grid, row, endrow, W_ENDCOL(wp) - 1 - n, W_ENDCOL(wp) - n,
+ c1, c2, attr);
+ } else {
+ grid_fill(&wp->w_grid, row, endrow, n, wp->w_grid.Columns, c1, c2, attr);
}
+
set_empty_rows(wp, row);
}
@@ -1773,10 +1764,10 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col);
// If signs are being displayed, add spaces.
- if (signcolumn_on(wp)) {
+ if (win_signcol_count(wp) > 0) {
len = wp->w_grid.Columns - col;
if (len > 0) {
- int len_max = win_signcol_width(wp);
+ int len_max = win_signcol_width(wp) * win_signcol_count(wp);
if (len > len_max) {
len = len_max;
}
@@ -2180,7 +2171,6 @@ win_line (
int vcol_off = 0; ///< offset for concealed characters
int did_wcol = false;
int match_conc = 0; ///< cchar for match functions
- int has_match_conc = 0; ///< match wants to conceal
int old_boguscols = 0;
# define VCOL_HLC (vcol - vcol_off)
# define FIX_FOR_BOGUSCOLS \
@@ -2202,7 +2192,7 @@ win_line (
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
extra_check = wp->w_p_lbr;
- if (syntax_present(wp) && !wp->w_s->b_syn_error) {
+ if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) {
// Prepare for syntax highlighting in this line. When there is an
// error, stop syntax highlighting.
save_did_emsg = did_emsg;
@@ -2212,8 +2202,10 @@ win_line (
wp->w_s->b_syn_error = true;
} else {
did_emsg = save_did_emsg;
- has_syntax = true;
- extra_check = true;
+ if (!wp->w_s->b_syn_slow) {
+ has_syntax = true;
+ extra_check = true;
+ }
}
}
@@ -2377,31 +2369,34 @@ win_line (
filler_lines = wp->w_topfill;
filler_todo = filler_lines;
- // Cursor line highlighting for 'cursorline' in the current window. Not
- // when Visual mode is active, because it's not clear what is selected
- // then.
- if (wp->w_p_cul && lnum == wp->w_cursor.lnum
- && !(wp == curwin && VIsual_active)) {
- int cul_attr = win_hl_attr(wp, HLF_CUL);
- HlAttrs ae = syn_attr2entry(cul_attr);
-
- // We make a compromise here (#7383):
- // * low-priority CursorLine if fg is not set
- // * high-priority ("same as Vim" priority) CursorLine if fg is set
- if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
- line_attr_lowprio = cul_attr;
- } else {
- if (!(State & INSERT) && bt_quickfix(wp->w_buffer)
- && qf_current_entry(wp) == lnum) {
- line_attr = hl_combine_attr(cul_attr, line_attr);
+ // Cursor line highlighting for 'cursorline' in the current window.
+ if (wp->w_p_cul && lnum == wp->w_cursor.lnum) {
+ // Do not show the cursor line when Visual mode is active, because it's
+ // not clear what is selected then.
+ if (!(wp == curwin && VIsual_active)) {
+ int cul_attr = win_hl_attr(wp, HLF_CUL);
+ HlAttrs ae = syn_attr2entry(cul_attr);
+
+ // We make a compromise here (#7383):
+ // * low-priority CursorLine if fg is not set
+ // * high-priority ("same as Vim" priority) CursorLine if fg is set
+ if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
+ line_attr_lowprio = cul_attr;
} else {
- line_attr = cul_attr;
+ if (!(State & INSERT) && bt_quickfix(wp->w_buffer)
+ && qf_current_entry(wp) == lnum) {
+ line_attr = hl_combine_attr(cul_attr, line_attr);
+ } else {
+ line_attr = cul_attr;
+ }
}
}
+ // Update w_last_cursorline even if Visual mode is active.
+ wp->w_last_cursorline = wp->w_cursor.lnum;
}
// If this line has a sign with line highlighting set line_attr.
- v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
+ v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL, 0, 1);
if (v != 0) {
line_attr = sign_get_attr((int)v, SIGN_LINEHL);
}
@@ -2450,7 +2445,9 @@ win_line (
}
if (wp->w_p_list) {
- if (curwin->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail) {
+ if (curwin->w_p_lcs_chars.space
+ || wp->w_p_lcs_chars.trail
+ || wp->w_p_lcs_chars.nbsp) {
extra_check = true;
}
// find start of trailing whitespace
@@ -2549,9 +2546,10 @@ win_line (
}
wp->w_cursor = pos;
- /* Need to restart syntax highlighting for this line. */
- if (has_syntax)
+ // Need to restart syntax highlighting for this line.
+ if (has_syntax) {
syntax_start(wp, lnum);
+ }
}
}
@@ -2597,6 +2595,9 @@ win_line (
}
next_search_hl(wp, shl, lnum, (colnr_T)v,
shl == &search_hl ? NULL : cur);
+ if (wp->w_s->b_syn_slow) {
+ has_syntax = false;
+ }
// Need to get the line again, a multi-line regexp may have made it
// invalid.
@@ -2651,9 +2652,11 @@ win_line (
extra_check = true;
}
+ int sign_idx = 0;
// Repeat for the whole displayed line.
for (;; ) {
- has_match_conc = 0;
+ int has_match_conc = 0; ///< match wants to conceal
+ bool did_decrement_ptr = false;
// Skip this quickly when working on the text.
if (draw_state != WL_LINE) {
if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
@@ -2691,7 +2694,8 @@ win_line (
draw_state = WL_SIGN;
/* Show the sign column when there are any signs in this
* buffer or when using Netbeans. */
- if (signcolumn_on(wp)) {
+ int count = win_signcol_count(wp);
+ if (count > 0) {
int text_sign;
// Draw cells with the sign value or blank.
c_extra = ' ';
@@ -2700,7 +2704,8 @@ win_line (
n_extra = win_signcol_width(wp);
if (row == startrow + filler_lines && filler_todo <= 0) {
- text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT);
+ text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT,
+ sign_idx, count);
if (text_sign != 0) {
p_extra = sign_get_text(text_sign);
int symbol_blen = (int)STRLEN(p_extra);
@@ -2718,6 +2723,11 @@ win_line (
char_attr = sign_get_attr(text_sign, SIGN_TEXT);
}
}
+
+ sign_idx++;
+ if (sign_idx < count) {
+ draw_state = WL_SIGN - 1;
+ }
}
}
@@ -2754,8 +2764,15 @@ win_line (
if (wp->w_skipcol > 0)
for (p_extra = extra; *p_extra == ' '; ++p_extra)
*p_extra = '-';
- if (wp->w_p_rl) /* reverse line numbers */
- rl_mirror(extra);
+ if (wp->w_p_rl) { // reverse line numbers
+ // like rl_mirror(), but keep the space at the end
+ char_u *p2 = skiptowhite(extra) - 1;
+ for (char_u *p1 = extra; p1 < p2; p1++, p2--) {
+ const int t = *p1;
+ *p1 = *p2;
+ *p2 = t;
+ }
+ }
p_extra = extra;
c_extra = NUL;
c_final = NUL;
@@ -2766,7 +2783,8 @@ win_line (
n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N);
- int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL);
+ int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL,
+ 0, 1);
if (num_sign != 0) {
// :sign defined with "numhl" highlight.
char_attr = sign_get_attr(num_sign, SIGN_NUMHL);
@@ -2853,6 +2871,7 @@ win_line (
}
if (draw_state == WL_LINE - 1 && n_extra == 0) {
+ sign_idx = 0;
draw_state = WL_LINE;
if (saved_n_extra) {
/* Continue item from end of wrapped line. */
@@ -2936,8 +2955,11 @@ win_line (
shl->endcol = tmp_col;
}
shl->attr_cur = shl->attr;
- if (cur != NULL && syn_name2id((char_u *)"Conceal")
- == cur->hlg_id) {
+ // Match with the "Conceal" group results in hiding
+ // the match.
+ if (cur != NULL
+ && shl != &search_hl
+ && syn_name2id((char_u *)"Conceal") == cur->hlg_id) {
has_match_conc = v == (long)shl->startcol ? 2 : 1;
match_conc = cur->conceal_char;
} else {
@@ -3207,6 +3229,7 @@ win_line (
// Put pointer back so that the character will be
// displayed at the start of the next line.
ptr--;
+ did_decrement_ptr = true;
} else if (*ptr != NUL) {
ptr += mb_l - 1;
}
@@ -3255,16 +3278,18 @@ win_line (
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
ptr = line + v;
- if (!attr_pri)
+ if (!attr_pri) {
char_attr = syntax_attr;
- else
+ } else {
char_attr = hl_combine_attr(syntax_attr, char_attr);
- /* no concealing past the end of the line, it interferes
- * with line highlighting */
- if (c == NUL)
+ }
+ // no concealing past the end of the line, it interferes
+ // with line highlighting.
+ if (c == NUL) {
syntax_flags = 0;
- else
+ } else {
syntax_flags = get_syntax_info(&syntax_seqnr);
+ }
} else if (!attr_pri) {
char_attr = 0;
}
@@ -3356,7 +3381,7 @@ win_line (
}
if (wp->w_buffer->terminal) {
- char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);
+ char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
}
// Found last space before word: check for line break.
@@ -3663,6 +3688,11 @@ win_line (
prev_syntax_id = 0;
is_concealing = FALSE;
}
+
+ if (n_skip > 0 && did_decrement_ptr) {
+ // not showing the '>', put pointer back to avoid getting stuck
+ ptr++;
+ }
}
/* In the cursor line and we may be concealing characters: correct
@@ -3971,8 +4001,10 @@ win_line (
break;
}
- // line continues beyond line end
- if (wp->w_p_lcs_chars.ext
+ // Show "extends" character from 'listchars' if beyond the line end and
+ // 'list' is set.
+ if (wp->w_p_lcs_chars.ext != NUL
+ && wp->w_p_list
&& !wp->w_p_wrap
&& filler_todo <= 0
&& (wp->w_p_rl ? col == 0 : col == grid->Columns - 1)
@@ -4192,11 +4224,9 @@ win_line (
) || lcs_eol_one == -1)
break;
- /* When the window is too narrow draw all "@" lines. */
- if (draw_state != WL_LINE
- && filler_todo <= 0
- ) {
- win_draw_end(wp, '@', ' ', row, wp->w_grid.Rows, HLF_AT);
+ // When the window is too narrow draw all "@" lines.
+ if (draw_state != WL_LINE && filler_todo <= 0) {
+ win_draw_end(wp, '@', ' ', true, row, wp->w_grid.Rows, HLF_AT);
row = endrow;
}
@@ -4813,13 +4843,13 @@ static void win_redr_status(win_T *wp)
p = NameBuff;
len = (int)STRLEN(p);
- if (wp->w_buffer->b_help
+ if (bt_help(wp->w_buffer)
|| wp->w_p_pvw
|| bufIsChanged(wp->w_buffer)
|| wp->w_buffer->b_p_ro) {
*(p + len++) = ' ';
}
- if (wp->w_buffer->b_help) {
+ if (bt_help(wp->w_buffer)) {
STRCPY(p + len, _("[Help]"));
len += (int)STRLEN(p + len);
}
@@ -6351,14 +6381,12 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
}
-/*
- * show the current mode and ruler
- *
- * If clear_cmdline is TRUE, clear the rest of the cmdline.
- * If clear_cmdline is FALSE there may be a message there that needs to be
- * cleared only if a mode is shown.
- * Return the length of the message (0 if no message).
- */
+// Show the current mode and ruler.
+//
+// If clear_cmdline is TRUE, clear the rest of the cmdline.
+// If clear_cmdline is FALSE there may be a message there that needs to be
+// cleared only if a mode is shown.
+// Return the length of the message (0 if no message).
int showmode(void)
{
int need_clear;
@@ -7151,8 +7179,12 @@ void screen_resize(int width, int height)
if (curwin->w_p_scb)
do_check_scrollbind(TRUE);
if (State & CMDLINE) {
+ redraw_popupmenu = false;
update_screen(NOT_VALID);
redrawcmdline();
+ if (pum_drawn()) {
+ cmdline_pum_display(false);
+ }
} else {
update_topline();
if (pum_drawn()) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 777ea07a21..6e00602e66 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -310,7 +310,7 @@ void free_search_patterns(void)
/// Save and restore the search pattern for incremental highlight search
/// feature.
///
-/// It's similar but different from save_search_patterns() and
+/// It's similar to but different from save_search_patterns() and
/// restore_search_patterns(), because the search pattern must be restored when
/// cancelling incremental searching even if it's called inside user functions.
void save_last_search_pattern(void)
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 8864301e4c..4440d3905f 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -856,13 +856,13 @@ static int msgpack_sd_writer_write(void *data, const char *buf, size_t len)
return 0;
}
-/// Check whether writing to shada file was disabled with -i NONE
+/// Check whether writing to shada file was disabled ("-i NONE" or "--clean").
///
/// @return true if it was disabled, false otherwise.
static bool shada_disabled(void)
FUNC_ATTR_PURE
{
- return used_shada_file != NULL && STRCMP(used_shada_file, "NONE") == 0;
+ return strequal(p_shadafile, "NONE");
}
/// Read ShaDa file
@@ -1542,14 +1542,14 @@ static char *shada_filename(const char *file)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
{
if (file == NULL || *file == NUL) {
- if (used_shada_file != NULL) {
- file = used_shada_file;
+ if (p_shadafile != NULL && *p_shadafile != NUL) {
+ file = p_shadafile;
} else {
if ((file = find_shada_parameter('n')) == NULL || *file == NUL) {
file = shada_get_default_file();
}
// XXX It used to be one level lower, so that whatever is in
- // `used_shada_file` was expanded. I intentionally moved it here
+ // `p_shadafile` was expanded. I intentionally moved it here
// because various expansions must have already be done by the shell.
// If shell is not performing them then they should be done in main.c
// where arguments are parsed, *not here*.
@@ -2739,8 +2739,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
// Initialize jump list
const void *jump_iter = NULL;
+ cleanup_jumplist(curwin, false);
setpcmark();
- cleanup_jumplist();
do {
xfmark_T fm;
jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 4443fd8a2e..b4f2709d45 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -13,6 +13,7 @@ struct signlist
linenr_T lnum; // line number which has this sign
int typenr; // typenr of sign
signlist_T *next; // next signlist entry
+ signlist_T *prev; // previous entry -- for easy reordering
};
// type argument for buf_getsigntype() and sign_get_attr()
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index ec4da88ea1..0fc33bec81 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -2294,7 +2294,7 @@ static void use_midword(slang_T *lp, win_T *wp)
}
// Find the region "region[2]" in "rp" (points to "sl_regions").
-// Each region is simply stored as the two characters of it's name.
+// Each region is simply stored as the two characters of its name.
// Returns the index if found (first is 0), REGION_ALL if not found.
static int find_region(char_u *rp, char_u *region)
{
@@ -7100,9 +7100,9 @@ void ex_spelldump(exarg_T *eap)
spell_dump_compl(NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0);
// Delete the empty line that we started with.
- if (curbuf->b_ml.ml_line_count > 1)
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
-
+ if (curbuf->b_ml.ml_line_count > 1) {
+ ml_delete(curbuf->b_ml.ml_line_count, false);
+ }
redraw_later(NOT_VALID);
}
@@ -7171,7 +7171,7 @@ spell_dump_compl (
if (do_region && region_names != NULL) {
if (pat == NULL) {
vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names);
- ml_append(lnum++, IObuff, (colnr_T)0, FALSE);
+ ml_append(lnum++, IObuff, (colnr_T)0, false);
}
} else
do_region = false;
@@ -7185,7 +7185,7 @@ spell_dump_compl (
if (pat == NULL) {
vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname);
- ml_append(lnum++, IObuff, (colnr_T)0, FALSE);
+ ml_append(lnum++, IObuff, (colnr_T)0, false);
}
// When matching with a pattern and there are no prefixes only use
@@ -7347,14 +7347,15 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int d
}
}
- ml_append(lnum, p, (colnr_T)0, FALSE);
+ ml_append(lnum, p, (colnr_T)0, false);
} else if (((dumpflags & DUMPFLAG_ICASE)
? mb_strnicmp(p, pat, STRLEN(pat)) == 0
: STRNCMP(p, pat, STRLEN(pat)) == 0)
&& ins_compl_add_infercase(p, (int)STRLEN(p),
- p_ic, NULL, *dir, 0) == OK)
+ p_ic, NULL, *dir, 0) == OK) {
// if dir was BACKWARD then honor it just once
*dir = FORWARD;
+ }
}
// For ":spelldump": Find matching prefixes for "word". Prepend each to
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 7a6f2fce39..117939e7e9 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -955,8 +955,9 @@ someerror:
break;
}
if (ml_append_buf(slang->sl_sugbuf, (linenr_T)wordnr,
- ga.ga_data, ga.ga_len, TRUE) == FAIL)
+ ga.ga_data, ga.ga_len, true) == FAIL) {
goto someerror;
+ }
}
ga_clear(&ga);
@@ -3227,7 +3228,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
prevp = p;
if (get_affitem(affile->af_flagtype, &p) != 0) {
// A flag is a postponed prefix flag if it appears in "af_pref"
- // and it's ID is not zero.
+ // and its ID is not zero.
STRLCPY(key, prevp, p - prevp + 1);
hi = hash_find(&affile->af_pref, key);
if (!HASHITEM_EMPTY(hi)) {
@@ -4920,9 +4921,10 @@ sug_filltable (
((char_u *)gap->ga_data)[gap->ga_len++] = NUL;
if (ml_append_buf(spin->si_spellbuf, (linenr_T)wordnr,
- gap->ga_data, gap->ga_len, TRUE) == FAIL)
+ gap->ga_data, gap->ga_len, true) == FAIL) {
return -1;
- ++wordnr;
+ }
+ wordnr++;
// Remove extra NUL entries, we no longer need them. We don't
// bother freeing the nodes, the won't be reused anyway.
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 96a8dfd295..3ba9354c67 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -678,12 +678,12 @@ static float_T tv_float(typval_T *const tvs, int *const idxp)
// are discarded. If "str_m" is greater than zero it is guaranteed
// the resulting string will be NUL-terminated.
-// vim_vsnprintf() can be invoked with either "va_list" or a list of
+// vim_vsnprintf_typval() can be invoked with either "va_list" or a list of
// "typval_T". When the latter is not used it must be NULL.
/// Append a formatted value to the string
///
-/// @see vim_vsnprintf().
+/// @see vim_vsnprintf_typval().
int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
FUNC_ATTR_PRINTF(3, 4)
{
@@ -697,7 +697,7 @@ int vim_snprintf_add(char *str, size_t str_m, char *fmt, ...)
}
va_list ap;
va_start(ap, fmt);
- const int str_l = vim_vsnprintf(str + len, space, fmt, ap, NULL);
+ const int str_l = vim_vsnprintf(str + len, space, fmt, ap);
va_end(ap);
return str_l;
}
@@ -715,7 +715,7 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
- const int str_l = vim_vsnprintf(str, str_m, fmt, ap, NULL);
+ const int str_l = vim_vsnprintf(str, str_m, fmt, ap);
va_end(ap);
return str_l;
}
@@ -736,6 +736,10 @@ static const char *infinity_str(bool positive, char fmt_spec,
return table[idx];
}
+int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
+{
+ return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
+}
/// Write formatted value to the string
///
@@ -748,8 +752,8 @@ static const char *infinity_str(bool positive, char fmt_spec,
///
/// @return Number of bytes excluding NUL byte that would be written to the
/// string if str_m was greater or equal to the return value.
-int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
- typval_T *const tvs)
+int vim_vsnprintf_typval(
+ char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs)
{
size_t str_l = 0;
bool str_avail = str_l < str_m;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 0104f0d834..1b30161e94 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -356,15 +356,16 @@ static reg_extmatch_T *next_match_extmatch = NULL;
* The current state (within the line) of the recognition engine.
* When current_state.ga_itemsize is 0 the current state is invalid.
*/
-static win_T *syn_win; /* current window for highlighting */
-static buf_T *syn_buf; /* current buffer for highlighting */
-static synblock_T *syn_block; /* current buffer for highlighting */
-static linenr_T current_lnum = 0; /* lnum of current state */
-static colnr_T current_col = 0; /* column of current state */
-static int current_state_stored = 0; /* TRUE if stored current state
- * after setting current_finished */
-static int current_finished = 0; /* current line has been finished */
-static garray_T current_state /* current stack of state_items */
+static win_T *syn_win; // current window for highlighting
+static buf_T *syn_buf; // current buffer for highlighting
+static synblock_T *syn_block; // current buffer for highlighting
+static proftime_T *syn_tm; // timeout limit
+static linenr_T current_lnum = 0; // lnum of current state
+static colnr_T current_col = 0; // column of current state
+static int current_state_stored = 0; // TRUE if stored current state
+ // after setting current_finished
+static int current_finished = 0; // current line has been finished
+static garray_T current_state // current stack of state_items
= GA_EMPTY_INIT_VALUE;
static int16_t *current_next_list = NULL; // when non-zero, nextgroup list
static int current_next_flags = 0; // flags for current_next_list
@@ -375,7 +376,12 @@ static int current_line_id = 0; // unique number for current line
static int syn_time_on = FALSE;
# define IF_SYN_TIME(p) (p)
-
+// Set the timeout used for syntax highlighting.
+// Use NULL to reset, no timeout.
+void syn_set_timeout(proftime_T *tm)
+{
+ syn_tm = tm;
+}
/*
* Start the syntax recognition for a line. This function is normally called
@@ -2887,6 +2893,7 @@ static char_u *syn_getcurline(void)
static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st)
{
int r;
+ int timed_out = 0;
proftime_T pt;
const int l_syn_time_on = syn_time_on;
@@ -2902,7 +2909,8 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
}
rmp->rmm_maxcol = syn_buf->b_p_smc;
- r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL, NULL);
+ r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
+ syn_tm, &timed_out);
if (l_syn_time_on) {
pt = profile_end(pt);
@@ -2914,6 +2922,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
if (r > 0)
++st->match;
}
+ if (timed_out) {
+ syn_win->w_s->b_syn_slow = true;
+ }
if (r > 0) {
rmp->startpos[0].lnum += lnum;
@@ -3144,6 +3155,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
void syntax_clear(synblock_T *block)
{
block->b_syn_error = false; // clear previous error
+ block->b_syn_slow = false; // clear previous timeout
block->b_syn_ic = false; // Use case, by default
block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
block->b_syn_containedin = false;
@@ -5756,8 +5768,10 @@ int syn_get_foldlevel(win_T *wp, long lnum)
{
int level = 0;
- /* Return quickly when there are no fold items at all. */
- if (wp->w_s->b_syn_folditems != 0) {
+ // Return quickly when there are no fold items at all.
+ if (wp->w_s->b_syn_folditems != 0
+ && !wp->w_s->b_syn_error
+ && !wp->w_s->b_syn_slow) {
syntax_start(wp, lnum);
for (int i = 0; i < current_state.ga_len; ++i) {
@@ -5956,6 +5970,7 @@ static const char *highlight_init_both[] = {
"default link Substitute Search",
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
+ "default link NormalFloat Pmenu",
NULL
};
@@ -7445,7 +7460,7 @@ void highlight_attr_set_all(void)
void highlight_changed(void)
{
int id;
- char_u userhl[10];
+ char_u userhl[30]; // use 30 to avoid compiler warning
int id_SNC = -1;
int id_S = -1;
int hlcnt;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 410b9dfcbd..81af23f911 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -436,11 +436,15 @@ do_tag (
tagmatchname = vim_strsave(name);
}
- if (type == DT_TAG || type == DT_SELECT || type == DT_JUMP
+ if (type == DT_SELECT || type == DT_JUMP
|| type == DT_LTAG) {
cur_match = MAXCOL - 1;
}
- max_num_matches = cur_match + 1;
+ if (type == DT_TAG) {
+ max_num_matches = MAXCOL;
+ } else {
+ max_num_matches = cur_match + 1;
+ }
/* when the argument starts with '/', use it as a regexp */
if (!no_regexp && *name == '/') {
@@ -495,7 +499,7 @@ do_tag (
if (type == DT_CSCOPE && num_matches > 1) {
cs_print_tags();
ask_for_selection = true;
- } else if (type == DT_TAG) {
+ } else if (type == DT_TAG && *tag != NUL) {
// If a count is supplied to the ":tag <name>" command, then
// jump to count'th matching tag.
cur_match = count > 0 ? count - 1 : 0;
@@ -1523,58 +1527,38 @@ line_read_in:
}
parse_line:
- /*
- * Figure out where the different strings are in this line.
- * For "normal" tags: Do a quick check if the tag matches.
- * This speeds up tag searching a lot!
- */
- if (orgpat.headlen
- ) {
+ // When the line is too long the NUL will not be in the
+ // last-but-one byte (see vim_fgets()).
+ // Has been reported for Mozilla JS with extremely long names.
+ // In that case we can't parse it and we ignore the line.
+ if (lbuf[LSIZE - 2] != NUL && !use_cscope) {
+ if (p_verbose >= 5) {
+ verbose_enter();
+ MSG(_("Ignoring long line in tags file"));
+ verbose_leave();
+ }
+ if (state != TS_LINEAR) {
+ // Avoid getting stuck.
+ linear = true;
+ state = TS_LINEAR;
+ vim_fseek(fp, search_info.low_offset, SEEK_SET);
+ }
+ continue;
+ }
+
+ // Figure out where the different strings are in this line.
+ // For "normal" tags: Do a quick check if the tag matches.
+ // This speeds up tag searching a lot!
+ if (orgpat.headlen) {
tagp.tagname = lbuf;
tagp.tagname_end = vim_strchr(lbuf, TAB);
- if (tagp.tagname_end == NULL)
- {
- if (vim_strchr(lbuf, NL) == NULL) {
- /* Truncated line, ignore it. Has been reported for
- * Mozilla JS with extremely long names. */
- if (p_verbose >= 5) {
- verbose_enter();
- MSG(_("Ignoring long line in tags file"));
- verbose_leave();
- }
- if (state != TS_LINEAR) {
- /* Avoid getting stuck. */
- linear = TRUE;
- state = TS_LINEAR;
- vim_fseek(fp, search_info.low_offset, SEEK_SET);
- }
- continue;
- }
-
- /* Corrupted tag line. */
- line_error = TRUE;
+ if (tagp.tagname_end == NULL) {
+ // Corrupted tag line.
+ line_error = true;
break;
}
/*
- * Check for old style static tag: "file:tag file .."
- */
- tagp.fname = NULL;
- for (p = lbuf; p < tagp.tagname_end; ++p) {
- if (*p == ':') {
- if (tagp.fname == NULL)
- tagp.fname = tagp.tagname_end + 1;
- if ( fnamencmp(lbuf, tagp.fname, p - lbuf) == 0
- && tagp.fname[p - lbuf] == TAB
- ) {
- /* found one */
- tagp.tagname = p + 1;
- break;
- }
- }
- }
-
- /*
* Skip this line if the length of the tag is different and
* there is no regexp, or the tag is too short.
*/
@@ -1673,11 +1657,8 @@ parse_line:
if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0)
continue;
- /*
- * Can be a matching tag, isolate the file name and command.
- */
- if (tagp.fname == NULL)
- tagp.fname = tagp.tagname_end + 1;
+ // Can be a matching tag, isolate the file name and command.
+ tagp.fname = tagp.tagname_end + 1;
tagp.fname_end = vim_strchr(tagp.fname, TAB);
tagp.command = tagp.fname_end + 1;
if (tagp.fname_end == NULL)
@@ -1744,19 +1725,12 @@ parse_line:
/* Don't change the ordering, always use the same table. */
mtt = MT_GL_OTH;
} else {
- /* Decide in which array to store this match. */
- is_current = test_for_current(
- tagp.fname, tagp.fname_end, tag_fname,
- buf_ffname);
- {
- if (tagp.tagname != lbuf)
- is_static = TRUE; /* detected static tag before */
- else
- is_static = test_for_static(&tagp);
- }
+ // Decide in which array to store this match.
+ is_current = test_for_current(tagp.fname, tagp.fname_end, tag_fname,
+ buf_ffname);
+ is_static = test_for_static(&tagp);
- /* decide in which of the sixteen tables to store this
- * match */
+ // Decide in which of the sixteen tables to store this match.
if (is_static) {
if (is_current)
mtt = MT_ST_CUR;
@@ -1858,9 +1832,9 @@ parse_line:
// Don't add identical matches.
// Add all cscope tags, because they are all listed.
// "mfp" is used as a hash key, there is a NUL byte to end
- // the part matters for comparing, more bytes may follow
- // after it. E.g. help tags store the priority after the
- // NUL.
+ // the part that matters for comparing, more bytes may
+ // follow after it. E.g. help tags store the priority
+ // after the NUL.
if (use_cscope) {
hash++;
} else {
@@ -1996,7 +1970,13 @@ static garray_T tag_fnames = GA_EMPTY_INIT_VALUE;
*/
static void found_tagfile_cb(char_u *fname, void *cookie)
{
- GA_APPEND(char_u *, &tag_fnames, vim_strsave(fname));
+ char_u *const tag_fname = vim_strsave(fname);
+
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(tag_fname);
+#endif
+ simplify_filename(tag_fname);
+ GA_APPEND(char_u *, &tag_fnames, tag_fname);
}
#if defined(EXITFREE)
@@ -2054,9 +2034,20 @@ get_tagfname (
++tnp->tn_hf_idx;
STRCPY(buf, p_hf);
STRCPY(path_tail(buf), "tags");
- } else
- STRLCPY(buf, ((char_u **)(tag_fnames.ga_data))[
- tnp->tn_hf_idx++], MAXPATHL);
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(buf);
+#endif
+ simplify_filename(buf);
+
+ for (int i = 0; i < tag_fnames.ga_len; i++) {
+ if (STRCMP(buf, ((char_u **)(tag_fnames.ga_data))[i]) == 0) {
+ return FAIL; // avoid duplicate file names
+ }
+ }
+ } else {
+ STRLCPY(buf, ((char_u **)(tag_fnames.ga_data))[tnp->tn_hf_idx++],
+ MAXPATHL);
+ }
return OK;
}
@@ -2188,25 +2179,9 @@ parse_tag_line (
*/
static bool test_for_static(tagptrs_T *tagp)
{
- char_u *p;
+ char_u *p;
- int len;
-
- /*
- * Check for old style static tag: "file:tag file .."
- */
- len = (int)(tagp->fname_end - tagp->fname);
- p = tagp->tagname + len;
- if ( p < tagp->tagname_end
- && *p == ':'
- && fnamencmp(tagp->tagname, tagp->fname, len) == 0) {
- tagp->tagname = p + 1;
- return TRUE;
- }
-
- /*
- * Check for new style static tag ":...<Tab>file:[<Tab>...]"
- */
+ // Check for new style static tag ":...<Tab>file:[<Tab>...]"
p = tagp->command;
while ((p = vim_strchr(p, '\t')) != NULL) {
++p;
@@ -2905,3 +2880,177 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
}
return ret;
}
+
+// Return information about 'tag' in dict 'retdict'.
+static void get_tag_details(taggy_T *tag, dict_T *retdict)
+{
+ list_T *pos;
+ fmark_T *fmark;
+
+ tv_dict_add_str(retdict, S_LEN("tagname"), (const char *)tag->tagname);
+ tv_dict_add_nr(retdict, S_LEN("matchnr"), tag->cur_match + 1);
+ tv_dict_add_nr(retdict, S_LEN("bufnr"), tag->cur_fnum);
+
+ pos = tv_list_alloc(4);
+ tv_dict_add_list(retdict, S_LEN("from"), pos);
+
+ fmark = &tag->fmark;
+ tv_list_append_number(pos,
+ (varnumber_T)(fmark->fnum != -1 ? fmark->fnum : 0));
+ tv_list_append_number(pos, (varnumber_T)fmark->mark.lnum);
+ tv_list_append_number(pos, (varnumber_T)(fmark->mark.col == MAXCOL
+ ? MAXCOL : fmark->mark.col + 1));
+ tv_list_append_number(pos, (varnumber_T)fmark->mark.coladd);
+}
+
+// Return the tag stack entries of the specified window 'wp' in dictionary
+// 'retdict'.
+void get_tagstack(win_T *wp, dict_T *retdict)
+{
+ list_T *l;
+ int i;
+ dict_T *d;
+
+ tv_dict_add_nr(retdict, S_LEN("length"), wp->w_tagstacklen);
+ tv_dict_add_nr(retdict, S_LEN("curidx"), wp->w_tagstackidx + 1);
+ l = tv_list_alloc(2);
+ tv_dict_add_list(retdict, S_LEN("items"), l);
+
+ for (i = 0; i < wp->w_tagstacklen; i++) {
+ d = tv_dict_alloc();
+ tv_list_append_dict(l, d);
+ get_tag_details(&wp->w_tagstack[i], d);
+ }
+}
+
+// Free all the entries in the tag stack of the specified window
+static void tagstack_clear(win_T *wp)
+{
+ // Free the current tag stack
+ for (int i = 0; i < wp->w_tagstacklen; i++) {
+ xfree(wp->w_tagstack[i].tagname);
+ }
+ wp->w_tagstacklen = 0;
+ wp->w_tagstackidx = 0;
+}
+
+// Remove the oldest entry from the tag stack and shift the rest of
+// the entires to free up the top of the stack.
+static void tagstack_shift(win_T *wp)
+{
+ taggy_T *tagstack = wp->w_tagstack;
+ xfree(tagstack[0].tagname);
+ for (int i = 1; i < wp->w_tagstacklen; i++) {
+ tagstack[i - 1] = tagstack[i];
+ }
+ wp->w_tagstacklen--;
+}
+
+// Push a new item to the tag stack
+static void tagstack_push_item(
+ win_T *wp,
+ char_u *tagname,
+ int cur_fnum,
+ int cur_match,
+ pos_T mark,
+ int fnum)
+{
+ taggy_T *tagstack = wp->w_tagstack;
+ int idx = wp->w_tagstacklen; // top of the stack
+
+ // if the tagstack is full: remove the oldest entry
+ if (idx >= TAGSTACKSIZE) {
+ tagstack_shift(wp);
+ idx = TAGSTACKSIZE - 1;
+ }
+
+ wp->w_tagstacklen++;
+ tagstack[idx].tagname = tagname;
+ tagstack[idx].cur_fnum = cur_fnum;
+ tagstack[idx].cur_match = cur_match;
+ if (tagstack[idx].cur_match < 0) {
+ tagstack[idx].cur_match = 0;
+ }
+ tagstack[idx].fmark.mark = mark;
+ tagstack[idx].fmark.fnum = fnum;
+}
+
+// Add a list of items to the tag stack in the specified window
+static void tagstack_push_items(win_T *wp, list_T *l)
+{
+ listitem_T *li;
+ dictitem_T *di;
+ dict_T *itemdict;
+ char_u *tagname;
+ pos_T mark;
+ int fnum;
+
+ // Add one entry at a time to the tag stack
+ for (li = tv_list_first(l); li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT
+ || TV_LIST_ITEM_TV(li)->vval.v_dict == NULL) {
+ continue; // Skip non-dict items
+ }
+ itemdict = TV_LIST_ITEM_TV(li)->vval.v_dict;
+
+ // parse 'from' for the cursor position before the tag jump
+ if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) {
+ continue;
+ }
+ if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK) {
+ continue;
+ }
+ if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true))
+ == NULL) {
+ continue;
+ }
+
+ if (mark.col > 0) {
+ mark.col--;
+ }
+ tagstack_push_item(wp, tagname,
+ (int)tv_dict_get_number(itemdict, "bufnr"),
+ (int)tv_dict_get_number(itemdict, "matchnr") - 1,
+ mark, fnum);
+ }
+}
+
+// Set the current index in the tag stack. Valid values are between 0
+// and the stack length (inclusive).
+static void tagstack_set_curidx(win_T *wp, int curidx)
+{
+ wp->w_tagstackidx = curidx;
+ if (wp->w_tagstackidx < 0) { // sanity check
+ wp->w_tagstackidx = 0;
+ }
+ if (wp->w_tagstackidx > wp->w_tagstacklen) {
+ wp->w_tagstackidx = wp->w_tagstacklen;
+ }
+}
+
+// Set the tag stack entries of the specified window.
+// 'action' is set to either 'a' for append or 'r' for replace.
+int set_tagstack(win_T *wp, dict_T *d, int action)
+{
+ dictitem_T *di;
+ list_T *l;
+
+ if ((di = tv_dict_find(d, "items", -1)) != NULL) {
+ if (di->di_tv.v_type != VAR_LIST) {
+ return FAIL;
+ }
+ l = di->di_tv.vval.v_list;
+
+ if (action == 'r') {
+ tagstack_clear(wp);
+ }
+
+ tagstack_push_items(wp, l);
+ }
+
+ if ((di = tv_dict_find(d, "curidx", -1)) != NULL) {
+ tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1);
+ }
+
+ return OK;
+}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index ffe650f416..d8d529d0f6 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -374,6 +374,7 @@ void terminal_enter(void)
TerminalState state, *s = &state;
memset(s, 0, sizeof(TerminalState));
s->term = buf->terminal;
+ stop_insert_mode = false;
// Ensure the terminal is properly sized. Ideally window size management
// code should always have resized the terminal already, but check here to
@@ -400,9 +401,9 @@ void terminal_enter(void)
showmode();
curwin->w_redr_status = true; // For mode() in statusline. #8323
ui_busy_start();
- redraw(false);
s->state.execute = terminal_execute;
+ s->state.check = terminal_check;
state_enter(&s->state);
restart_edit = 0;
@@ -415,8 +416,10 @@ void terminal_enter(void)
// draw the unfocused cursor
invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
+ if (curbuf->terminal == s->term && !s->close) {
+ terminal_check_cursor();
+ }
unshowmode(true);
- redraw(curbuf->handle != s->term->buf_handle);
ui_busy_stop();
if (s->close) {
bool wipe = s->term->buf_handle != 0;
@@ -427,6 +430,45 @@ void terminal_enter(void)
}
}
+static void terminal_check_cursor(void)
+{
+ Terminal *term = curbuf->terminal;
+ curwin->w_wrow = term->cursor.row;
+ curwin->w_wcol = term->cursor.col + win_col_off(curwin);
+ curwin->w_cursor.lnum = MIN(curbuf->b_ml.ml_line_count,
+ row_to_linenr(term, term->cursor.row));
+ // Nudge cursor when returning to normal-mode.
+ int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1);
+ curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off);
+ curwin->w_cursor.coladd = 0;
+ mb_check_adjust_col(curwin);
+}
+
+// Function executed before each iteration of terminal mode.
+// Return:
+// 1 if the iteration should continue normally
+// 0 if the main loop must exit
+static int terminal_check(VimState *state)
+{
+ if (stop_insert_mode) {
+ return 0;
+ }
+
+ terminal_check_cursor();
+
+ if (must_redraw) {
+ update_screen(0);
+ }
+
+ if (need_maketitle) { // Update title in terminal-mode. #7248
+ maketitle();
+ }
+
+ setcursor();
+ ui_flush();
+ return 1;
+}
+
static int terminal_execute(VimState *state, int key)
{
TerminalState *s = (TerminalState *)state;
@@ -878,15 +920,20 @@ static VTermKey convert_key(int key, VTermModifier *statep)
case K_KINS: return VTERM_KEY_KP_0;
case K_K1: FALLTHROUGH;
case K_KEND: return VTERM_KEY_KP_1;
- case K_K2: return VTERM_KEY_KP_2;
+ case K_K2: FALLTHROUGH;
+ case K_KDOWN: return VTERM_KEY_KP_2;
case K_K3: FALLTHROUGH;
case K_KPAGEDOWN: return VTERM_KEY_KP_3;
- case K_K4: return VTERM_KEY_KP_4;
- case K_K5: return VTERM_KEY_KP_5;
- case K_K6: return VTERM_KEY_KP_6;
+ case K_K4: FALLTHROUGH;
+ case K_KLEFT: return VTERM_KEY_KP_4;
+ case K_K5: FALLTHROUGH;
+ case K_KORIGIN: return VTERM_KEY_KP_5;
+ case K_K6: FALLTHROUGH;
+ case K_KRIGHT: return VTERM_KEY_KP_6;
case K_K7: FALLTHROUGH;
case K_KHOME: return VTERM_KEY_KP_7;
- case K_K8: return VTERM_KEY_KP_8;
+ case K_K8: FALLTHROUGH;
+ case K_KUP: return VTERM_KEY_KP_8;
case K_K9: FALLTHROUGH;
case K_KPAGEUP: return VTERM_KEY_KP_9;
case K_KDEL: FALLTHROUGH;
@@ -977,6 +1024,9 @@ static bool send_mouse_event(Terminal *term, int c)
{
int row = mouse_row, col = mouse_col, grid = mouse_grid;
win_T *mouse_win = mouse_find_win(&grid, &row, &col);
+ if (mouse_win == NULL) {
+ goto end;
+ }
if (term->forward_mouse && mouse_win->w_buffer->terminal == term) {
// event in the terminal window and mouse events was enabled by the
@@ -1024,6 +1074,7 @@ static bool send_mouse_event(Terminal *term, int c)
return mouse_win == curwin;
}
+end:
ins_char_typebuf(c);
return true;
}
@@ -1128,11 +1179,7 @@ static void refresh_terminal(Terminal *term)
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
refresh_pending = false;
- if (exiting // Cannot redraw (requires event loop) during teardown/exit.
- || (State & CMDPREVIEW)
- // WM_LIST (^D) is not redrawn, unlike the normal wildmenu. So we must
- // skip redraws to keep it visible.
- || wild_menu_showing == WM_LIST) {
+ if (exiting) { // Cannot redraw (requires event loop) during teardown/exit.
return;
}
Terminal *term;
@@ -1142,12 +1189,8 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
map_foreach(invalidated_terminals, term, stub, {
refresh_terminal(term);
});
- bool any_visible = is_term_visible();
pmap_clear(ptr_t)(invalidated_terminals);
unblock_autocmds();
- if (any_visible) {
- redraw(true);
- }
}
static void refresh_size(Terminal *term, buf_T *buf)
@@ -1261,61 +1304,6 @@ static void refresh_screen(Terminal *term, buf_T *buf)
term->invalid_end = -1;
}
-/// @return true if any invalidated terminal buffer is visible to the user
-static bool is_term_visible(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->terminal
- && pmap_has(ptr_t)(invalidated_terminals, wp->w_buffer->terminal)) {
- return true;
- }
- }
- return false;
-}
-
-static void redraw(bool restore_cursor)
-{
- Terminal *term = curbuf->terminal;
- if (!term) {
- restore_cursor = true;
- }
-
- int save_row = 0;
- int save_col = 0;
- if (restore_cursor) {
- // save the current row/col to restore after updating screen when not
- // focused
- save_row = ui_current_row();
- save_col = ui_current_col();
- }
- block_autocmds();
-
- if (must_redraw) {
- update_screen(0);
- }
-
- if (need_maketitle) { // Update title in terminal-mode. #7248
- maketitle();
- }
-
- if (restore_cursor) {
- ui_cursor_goto(save_row, save_col);
- } else if (term) {
- curwin->w_wrow = term->cursor.row;
- curwin->w_wcol = term->cursor.col + win_col_off(curwin);
- curwin->w_cursor.lnum = MIN(curbuf->b_ml.ml_line_count,
- row_to_linenr(term, term->cursor.row));
- // Nudge cursor when returning to normal-mode.
- int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1);
- curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off);
- curwin->w_cursor.coladd = 0;
- mb_check_adjust_col(curwin);
- }
-
- unblock_autocmds();
- ui_flush();
-}
-
static void adjust_topline(Terminal *term, buf_T *buf, long added)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -1351,23 +1339,16 @@ static bool is_focused(Terminal *term)
return State & TERM_FOCUS && curbuf->terminal == term;
}
-#define GET_CONFIG_VALUE(k, o) \
- do { \
- Error err = ERROR_INIT; \
- /* Only called from terminal_open where curbuf->terminal is the */ \
- /* context */ \
- o = dict_get_value(curbuf->b_vars, cstr_as_string(k), &err); \
- api_clear_error(&err); \
- if (o.type == kObjectTypeNil) { \
- o = dict_get_value(&globvardict, cstr_as_string(k), &err); \
- api_clear_error(&err); \
- } \
- } while (0)
-
static char *get_config_string(char *key)
{
- Object obj;
- GET_CONFIG_VALUE(key, obj);
+ Error err = ERROR_INIT;
+ // Only called from terminal_open where curbuf->terminal is the context.
+ Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err);
+ api_clear_error(&err);
+ if (obj.type == kObjectTypeNil) {
+ obj = dict_get_value(&globvardict, cstr_as_string(key), &err);
+ api_clear_error(&err);
+ }
if (obj.type == kObjectTypeString) {
return obj.data.string.data;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index fac27aa346..7b1f0f59cc 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -14,8 +14,6 @@ export NVIM_PRG := $(NVIM_PRG)
export TMPDIR := $(abspath ../../../Xtest-tmpdir)
SCRIPTS_DEFAULT = \
- test14.out \
- test37.out \
test42.out \
test48.out \
test52.out \
@@ -23,7 +21,6 @@ SCRIPTS_DEFAULT = \
ifneq ($(OS),Windows_NT)
SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
- test17.out \
test49.out \
endif
@@ -50,8 +47,6 @@ NEW_TESTS_IGNORE := $(NEW_TESTS_IN_ALOT) $(NEW_TESTS_ALOT) \
NEW_TESTS ?= $(addsuffix .res,$(sort $(filter-out $(NEW_TESTS_IGNORE),$(basename $(notdir $(wildcard test_*.vim))))) $(NEW_TESTS_ALOT))
-SCRIPTS_GUI := test16.out
-
ifdef VALGRIND_GDB
VGDB := --vgdb=yes \
@@ -83,8 +78,6 @@ endif
nongui: nolog $(SCRIPTS) newtests report
-gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report
-
.gdbinit:
@echo "[OLDTEST-PREP] Setting up .gdbinit"
@echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit
@@ -102,7 +95,7 @@ report:
test1.out: $(NVIM_PRG)
-$(SCRIPTS) $(SCRIPTS_GUI): $(NVIM_PRG) test1.out
+$(SCRIPTS): $(NVIM_PRG) test1.out
RM_ON_RUN := test.out X* viminfo
RM_ON_START := test.ok
@@ -164,5 +157,6 @@ newtestssilent: $(NEW_TESTS)
%.res: %.vim .gdbinit
@echo "[OLDTEST] Running" $*
+ @rm -rf $*.failed test.ok $(RM_ON_RUN)
@mkdir -p $(TMPDIR)
@/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim
diff --git a/src/nvim/testdir/load.vim b/src/nvim/testdir/load.vim
index 2e01338dd0..6369b8f45e 100644
--- a/src/nvim/testdir/load.vim
+++ b/src/nvim/testdir/load.vim
@@ -1,3 +1,5 @@
+" Also used by: test/functional/helpers.lua
+
function! s:load_factor() abort
let timeout = 200
let times = []
@@ -23,8 +25,8 @@ function! s:load_factor() abort
endfunction
" Compute load factor only once.
-let s:load_factor = s:load_factor()
+let g:test_load_factor = s:load_factor()
function! LoadAdjust(num) abort
- return float2nr(ceil(a:num * s:load_factor))
+ return float2nr(ceil(a:num * g:test_load_factor))
endfunction
diff --git a/src/nvim/testdir/test14.in b/src/nvim/testdir/test14.in
deleted file mode 100644
index bef2e45431..0000000000
--- a/src/nvim/testdir/test14.in
+++ /dev/null
@@ -1,94 +0,0 @@
-Tests for "vaBiB", end could be wrong.
-Also test ":s/pat/sub/" with different ~s in sub.
-Also test for ^Vxff and ^Vo123 in Insert mode.
-Also test "[m", "]m", "[M" and "]M"
-Also test search()
-
-STARTTEST
-/Start cursor here
-vaBiBD:?Bug?,/Piece/-2w! test.out
-/^- Bug
-:s/u/~u~/
-:s/i/~u~/
-:s/o/~~~/
-:.w >>test.out
-:let tt = "o\<C-V>65\<C-V>x42\<C-V>o103 \<C-V>33a\<C-V>xfg\<C-V>o78\<Esc>"
-:exe "normal " . tt
-:unlet tt
-:.w >>test.out
-:set vb
-/^Piece
-2]maA:.w >>test.out
-j]maB:.w >>test.out
-]maC:.w >>test.out
-[maD:.w >>test.out
-k2[maE:.w >>test.out
-3[maF:.w >>test.out
-]MaG:.w >>test.out
-j2]MaH:.w >>test.out
-]M]MaI:.w >>test.out
-2[MaJ:.w >>test.out
-k[MaK:.w >>test.out
-3[MaL:.w >>test.out
-:"
-/^foobar
-:let startline = line('.')
-:call search('foobar', 'c')
-:call append(line('$'), line('.') - startline)
-j:call search('^$', 'c')
-:call append(line('$'), line('.') - startline)
-:call search('^$', 'bc')
-:call append(line('$'), line('.') - startline)
-/two
-:call search('.', 'c')
-:call append(line('$'), getline('.')[col('.') - 1:])
-:"
-/^substitute
-:s/foo/bar/
-:$put =@/
-/^substitute
-:keeppatterns s/asdf/xyz/
-:$put =@/
-/^substitute
-Y:$put =@0
-/bar /e
-:$put =@0
--:keeppatterns /xyz
-0dn:/^search()/,$w >>test.out
-:qa!
-ENDTEST
-
-- Bug in "vPPPP" on this text (Webb):
- {
- cmd;
- {
- cmd; /* <-- Start cursor here */
- {
- }
- }
- }
-
-Piece of Java
-{
- tt m1 {
- t1;
- } e1
-
- tt m2 {
- t2;
- } e2
-
- tt m3 {
- if (x)
- {
- t3;
- }
- } e3
-}
-
-foobar
-
-substitute foo asdf
-
-one two
-search()
diff --git a/src/nvim/testdir/test14.ok b/src/nvim/testdir/test14.ok
deleted file mode 100644
index 0aa2db3f97..0000000000
--- a/src/nvim/testdir/test14.ok
+++ /dev/null
@@ -1,26 +0,0 @@
-- Bug in "vPPPP" on this text (Webb):
- {
- }
-- Bug uuun "vPPPP" uuuuuuuuun this text (Webb):
-ABC !ag8
- tt m1 {A
- tt m2 {B
- tt m3 {C
- tt m3 {DC
- tt m1 {EA
-{F
- }G e1
- }H e3
-}I
- }JH e3
- }K e2
-{LF
-search()
-0
-1
-1
-two
-foo
-^substitute
-substitute bar xyz
-xyz
diff --git a/src/nvim/testdir/test16.in b/src/nvim/testdir/test16.in
deleted file mode 100644
index b2cd159a8c..0000000000
--- a/src/nvim/testdir/test16.in
+++ /dev/null
@@ -1,15 +0,0 @@
-Tests for resetting "secure" flag after GUI has started.
-For KDE set a font, empty 'guifont' may cause a hang.
-
-STARTTEST
-:if $DISPLAY == "" | e! test.ok | wq! test.out | endif
-:set exrc secure
-:if has("gui_kde")
-: set guifont=Courier\ 10\ Pitch/8/-1/5/50/0/0/0/0/0
-:endif
-:gui -f
-:.,$w! test.out
-:qa!
-ENDTEST
-
- just some text
diff --git a/src/nvim/testdir/test16.ok b/src/nvim/testdir/test16.ok
deleted file mode 100644
index 25e2eea5c0..0000000000
--- a/src/nvim/testdir/test16.ok
+++ /dev/null
@@ -1,2 +0,0 @@
-
- just some text
diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in
deleted file mode 100644
index 1a4ac6b6d1..0000000000
--- a/src/nvim/testdir/test17.in
+++ /dev/null
@@ -1,126 +0,0 @@
-Tests for:
-- "gf" on ${VAR},
-- ":checkpath!" with various 'include' settings.
-
-STARTTEST
-:set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
-:"
-:if has("unix")
-:let $CDIR = "."
-/CDIR
-:else
-:let $TDIR = "."
-/TDIR
-:endif
-:" Dummy writing for making that sure gf doesn't fail even if the current
-:" file is modified. It can be occurred when executing the following command
-:" directly on Windows without fixing the 'fileformat':
-:" > nmake -f Make_dos.mak test17.out
-:w! test.out
-gf
-:set ff=unix
-:w! test.out
-:brewind
-ENDTEST
-
- ${CDIR}/test17a.in
- $TDIR/test17a.in
-
-STARTTEST
-:" check for 'include' without \zs or \ze
-:lang C
-:call delete("./Xbase.a")
-:call delete("Xdir1", "rf")
-:!mkdir Xdir1
-:!mkdir "Xdir1/dir2"
-:e! Xdir1/dir2/foo.a
-i#include "bar.a":
-:w
-:e Xdir1/dir2/bar.a
-i#include "baz.a":
-:w
-:e Xdir1/dir2/baz.a
-i#include "foo.a":
-:w
-:e Xbase.a
-:set path=Xdir1/dir2
-i#include <foo.a>:
-:w
-:redir! >>test.out
-:checkpath!
-:redir END
-:brewind
-ENDTEST
-
-STARTTEST
-:" check for 'include' with \zs and \ze
-:call delete("./Xbase.b")
-:call delete("Xdir1", "rf")
-:!mkdir Xdir1
-:!mkdir "Xdir1/dir2"
-:let &include='^\s*%inc\s*/\zs[^/]\+\ze'
-:function! DotsToSlashes()
-: return substitute(v:fname, '\.', '/', 'g') . '.b'
-:endfunction
-:let &includeexpr='DotsToSlashes()'
-:e! Xdir1/dir2/foo.b
-i%inc /bar/:
-:w
-:e Xdir1/dir2/bar.b
-i%inc /baz/:
-:w
-:e Xdir1/dir2/baz.b
-i%inc /foo/:
-:w
-:e Xbase.b
-:set path=Xdir1/dir2
-i%inc /foo/:
-:w
-:redir! >>test.out
-:checkpath!
-:redir END
-:brewind
-ENDTEST
-
-STARTTEST
-:" check for 'include' with \zs and no \ze
-:call delete("./Xbase.c")
-:call delete("Xdir1", "rf")
-:!mkdir Xdir1
-:!mkdir "Xdir1/dir2"
-:let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze'
-:function! StripNewlineChar()
-: if v:fname =~ '\n$'
-: return v:fname[:-2]
-: endif
-: return v:fname
-:endfunction
-:let &includeexpr='StripNewlineChar()'
-:e! Xdir1/dir2/foo.c
-i%inc bar.c:
-:w
-:e Xdir1/dir2/bar.c
-i%inc baz.c:
-:w
-:e Xdir1/dir2/baz.c
-i%inc foo.c:
-:w
-:e Xdir1/dir2/FALSE.c
-i%inc foo.c:
-:w
-:e Xbase.c
-:set path=Xdir1/dir2
-i%inc FALSE.c foo.c:
-:w
-:redir! >>test.out
-:checkpath!
-:redir END
-:brewind
-:" change "\" to "/" for Windows and fix 'fileformat'
-:e test.out
-:%s#\\#/#g
-:set ff&
-:w
-:q
-ENDTEST
-
diff --git a/src/nvim/testdir/test17.ok b/src/nvim/testdir/test17.ok
deleted file mode 100644
index b2a66d5f85..0000000000
--- a/src/nvim/testdir/test17.ok
+++ /dev/null
@@ -1,33 +0,0 @@
-This file is just to test "gf" in test 17.
-The contents is not important.
-Just testing!
-
-
---- Included files in path ---
-Xdir1/dir2/foo.a
-Xdir1/dir2/foo.a -->
- Xdir1/dir2/bar.a
- Xdir1/dir2/bar.a -->
- Xdir1/dir2/baz.a
- Xdir1/dir2/baz.a -->
- "foo.a" (Already listed)
-
-
---- Included files in path ---
-Xdir1/dir2/foo.b
-Xdir1/dir2/foo.b -->
- Xdir1/dir2/bar.b
- Xdir1/dir2/bar.b -->
- Xdir1/dir2/baz.b
- Xdir1/dir2/baz.b -->
- foo (Already listed)
-
-
---- Included files in path ---
-Xdir1/dir2/foo.c
-Xdir1/dir2/foo.c -->
- Xdir1/dir2/bar.c
- Xdir1/dir2/bar.c -->
- Xdir1/dir2/baz.c
- Xdir1/dir2/baz.c -->
- foo.c (Already listed)
diff --git a/src/nvim/testdir/test17a.in b/src/nvim/testdir/test17a.in
deleted file mode 100644
index 7e89364797..0000000000
--- a/src/nvim/testdir/test17a.in
+++ /dev/null
@@ -1,3 +0,0 @@
-This file is just to test "gf" in test 17.
-The contents is not important.
-Just testing!
diff --git a/src/nvim/testdir/test37.in b/src/nvim/testdir/test37.in
deleted file mode 100644
index 156bf74a10..0000000000
--- a/src/nvim/testdir/test37.in
+++ /dev/null
@@ -1,116 +0,0 @@
-Test for 'scrollbind'. <eralston@computer.org> Do not add a line below!
-STARTTEST
-:
-:set noscrollbind
-:set scrollopt=ver,jump
-:set scrolloff=2
-:set nowrap
-:set noequalalways
-:set splitbelow
-:" TEST using two windows open to one buffer, one extra empty window
-:split
-:new
-t:
-:resize 8
-/^start of window 1$/
-zt:
-:set scrollbind
-j:
-:resize 7
-/^start of window 2$/
-zt:
-:set scrollbind
-:" -- start of tests --
-:" TEST scrolling down
-L5jHyybpr0tHyybpr1tL6jHyybpr2kHyybpr3:
-:" TEST scrolling up
-tH4kjHtHyybpr4kHyybpr5k3ktHjHyybpr6tHyybpr7:
-:" TEST horizontal scrolling
-:set scrollopt+=hor
-gg"zyyG"zpGt015zly$bp"zpGky$bp"zpG:
-k10jH7zhg0y$bp"zpGtHg0y$bp"zpG:
-:set scrollopt-=hor
-:" ****** tests using two different buffers *****
-tj:
-:close
-t:
-:set noscrollbind
-:/^start of window 2$/,/^end of window 2$/y
-:new
-tj4"zpGp:
-t/^start of window 1$/
-zt:
-:set scrollbind
-j:
-/^start of window 2$/
-zt:
-:set scrollbind
-:" -- start of tests --
-:" TEST scrolling down
-L5jHyybpr0tHyybpr1tL6jHyybpr2kHyybpr3:
-:" TEST scrolling up
-tH4kjHtHyybpr4kHyybpr5k3ktHjHyybpr6tHyybpr7:
-:" TEST horizontal scrolling
-:set scrollopt+=hor
-gg"zyyG"zpGt015zly$bp"zpGky$bp"zpG:
-k10jH7zhg0y$bp"zpGtHg0y$bp"zpG:
-:set scrollopt-=hor
-:" TEST syncbind
-t:set noscb
-ggLj:set noscb
-ggL:set scb
-t:set scb
-GjG:syncbind
-HktHjHyybptyybp:
-t:set noscb
-ggLj:set noscb
-ggL:set scb
-t:set scb
-tGjGt:syncbind
-HkjHtHyybptjyybp:
-tH3kjHtHyybptjyybp:
-:" ***** done with tests *****
-:w! test.out " Write contents of this file
-:qa!
-ENDTEST
-
-
-start of window 1
-. line 01 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 01
-. line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-. line 03 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 03
-. line 04 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 04
-. line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
-. line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
-. line 07 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 07
-. line 08 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 08
-. line 09 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 09
-. line 10 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 10
-. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-. line 12 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 12
-. line 13 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 13
-. line 14 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 14
-. line 15 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 15
-end of window 1
-
-
-start of window 2
-. line 01 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 01
-. line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
-. line 03 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 03
-. line 04 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 04
-. line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
-. line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
-. line 07 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 07
-. line 08 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 08
-. line 09 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 09
-. line 10 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 10
-. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12
-. line 13 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 13
-. line 14 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 14
-. line 15 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 15
-. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
-end of window 2
-
-end of test37.in (please don't delete this line)
diff --git a/src/nvim/testdir/test37.ok b/src/nvim/testdir/test37.ok
deleted file mode 100644
index d0b74853b3..0000000000
--- a/src/nvim/testdir/test37.ok
+++ /dev/null
@@ -1,33 +0,0 @@
-
-0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
-1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
-2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
-5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
-6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
-7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-UTSRQPONMLKJIHGREDCBA9876543210 02
-. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-
-0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
-1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
-2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
-5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
-6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
-7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
-UTSRQPONMLKJIHGREDCBA9876543210 02
-. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
-. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
-
-. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
-:set scrollbind
-:set scrollbind
-. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
-j:
-. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12
diff --git a/src/nvim/testdir/test50.in b/src/nvim/testdir/test50.in
deleted file mode 100644
index 392177b808..0000000000
--- a/src/nvim/testdir/test50.in
+++ /dev/null
@@ -1,89 +0,0 @@
-Test for shortpathname ':8' extension.
-Only for use on Win32 systems!
-
-STARTTEST
-:fun! TestIt(file, bits, expected)
- let res=fnamemodify(a:file,a:bits)
- if a:expected == ''
- echo "'".a:file."'->(".a:bits.")->'".res."'"
- else
- if substitute(res,'/','\\', 'g') != substitute( a:expected, '/','\\', 'g')
- echo "FAILED: '".a:file."'->(".a:bits.")->'".res."'"
- echo "Expected: '".a:expected."'"
- else
- echo "OK"
- endif
- endif
-endfun
-:fun! MakeDir( dirname )
- "exe '!mkdir '.substitute(a:dirname,'/','\\','g')
- call system('mkdir '.substitute(a:dirname,'/','\\','g'))
-endfun
-:fun! RMDir( dirname)
- "exe '!rmdir '.substitute(a:dirname,'/','\\','g')
- call system('rmdir '.substitute(a:dirname,'/','\\','g'))
-endfun
-:fun! MakeFile( filename)
- "exe '!copy nul '.substitute(a:filename,'/','\\','g')
- call system('copy nul '.substitute(a:filename,'/','\\','g'))
-endfun
-:fun! TestColonEight()
- redir! >test.out
- " This could change for CygWin to //cygdrive/c
- let dir1='c:/x.x.y'
- if filereadable(dir1) || isdirectory(dir1)
- echo "FATAL: '".dir1."' exists, cannot run test"
- return
- endif
- let file1=dir1.'/zz.y.txt'
- let nofile1=dir1.'/z.y.txt'
- let dir2=dir1.'/VimIsTheGreatestSinceSlicedBread'
- let file2=dir2.'/z.txt'
- let nofile2=dir2.'/zz.txt'
- call MakeDir( dir1 )
- let resdir1 = substitute(fnamemodify(dir1, ':p:8'), '\\$', '', '')
- if resdir1 !~ '\V\^c:/XX\x\x\x\x~1.Y\$'
- echo "FATAL: unexpected short name: " . resdir1
- echo "INFO: please report your OS to vim-dev"
- return
- endif
- let resfile1=resdir1.'/ZZY~1.TXT'
- let resnofile1=resdir1.'/z.y.txt'
- let resdir2=resdir1.'/VIMIST~1'
- let resfile2=resdir2.'/z.txt'
- let resnofile2=resdir2.'/zz.txt'
- call MakeDir( dir2 )
- call MakeFile( file1 )
- call MakeFile( file2 )
- call TestIt(file1, ':p:8', resfile1)
- call TestIt(nofile1, ':p:8', resnofile1)
- call TestIt(file2, ':p:8', resfile2)
- call TestIt(nofile2, ':p:8', resnofile2)
- call TestIt(nofile2, ':p:8:h', fnamemodify(resnofile2,':h'))
- exe 'cd '.dir1
- call TestIt(file1, ':.:8', strpart(resfile1,strlen(resdir1)+1))
- call TestIt(nofile1, ':.:8', strpart(resnofile1,strlen(resdir1)+1))
- call TestIt(file2, ':.:8', strpart(resfile2,strlen(resdir1)+1))
- call TestIt(nofile2, ':.:8', strpart(resnofile2,strlen(resdir1)+1))
- let $HOME=dir1
- call TestIt(file1, ':~:8', '~'.strpart(resfile1,strlen(resdir1)))
- call TestIt(nofile1, ':~:8', '~'.strpart(resnofile1,strlen(resdir1)))
- call TestIt(file2, ':~:8', '~'.strpart(resfile2,strlen(resdir1)))
- call TestIt(nofile2, ':~:8', '~'.strpart(resnofile2,strlen(resdir1)))
- cd c:/
- call delete( file2 )
- call delete( file1 )
- call RMDir( dir2 )
- call RMDir( dir1 )
- echo
- redir END
-endfun
-:let dir = getcwd()
-:call TestColonEight()
-:exe "cd " . dir
-:edit! test.out
-:set ff=dos
-:w
-:qa!
-ENDTEST
-
diff --git a/src/nvim/testdir/test50.ok b/src/nvim/testdir/test50.ok
deleted file mode 100644
index 91ef1d6604..0000000000
--- a/src/nvim/testdir/test50.ok
+++ /dev/null
@@ -1,14 +0,0 @@
-
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 368fc9810d..3a9ffbdbf3 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -240,13 +240,53 @@ func Test_arglistid()
call assert_equal(0, arglistid())
endfunc
-" Test for argv()
+" Tests for argv() and argc()
func Test_argv()
call Reset_arglist()
call assert_equal([], argv())
call assert_equal("", argv(2))
+ call assert_equal(0, argc())
argadd a b c d
+ call assert_equal(4, argc())
call assert_equal('c', argv(2))
+
+ let w1_id = win_getid()
+ split
+ let w2_id = win_getid()
+ arglocal
+ args e f g
+ tabnew
+ let w3_id = win_getid()
+ split
+ let w4_id = win_getid()
+ argglobal
+ tabfirst
+ call assert_equal(4, argc(w1_id))
+ call assert_equal('b', argv(1, w1_id))
+ call assert_equal(['a', 'b', 'c', 'd'], argv(-1, w1_id))
+
+ call assert_equal(3, argc(w2_id))
+ call assert_equal('f', argv(1, w2_id))
+ call assert_equal(['e', 'f', 'g'], argv(-1, w2_id))
+
+ call assert_equal(3, argc(w3_id))
+ call assert_equal('e', argv(0, w3_id))
+ call assert_equal(['e', 'f', 'g'], argv(-1, w3_id))
+
+ call assert_equal(4, argc(w4_id))
+ call assert_equal('c', argv(2, w4_id))
+ call assert_equal(['a', 'b', 'c', 'd'], argv(-1, w4_id))
+
+ call assert_equal(4, argc(-1))
+ call assert_equal(3, argc())
+ call assert_equal('d', argv(3, -1))
+ call assert_equal(['a', 'b', 'c', 'd'], argv(-1, -1))
+ tabonly | only | enew!
+ " Negative test cases
+ call assert_equal(-1, argc(100))
+ call assert_equal('', argv(1, 100))
+ call assert_equal([], argv(-1, 100))
+ call assert_equal('', argv(10, -1))
endfunc
" Test for the :argedit command
@@ -289,6 +329,18 @@ func Test_argedit()
%argd
bwipe! C
bwipe! D
+
+ " :argedit reuses the current buffer if it is empty
+ %argd
+ " make sure to use a new buffer number for x when it is loaded
+ bw! x
+ new
+ let a = bufnr('')
+ argedit x
+ call assert_equal(a, bufnr(''))
+ call assert_equal('x', bufname(''))
+ %argd
+ bw! x
endfunc
" Test for the :argdelete command
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index f1fb8e67b9..8182c6973b 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -34,6 +34,28 @@ if has('timers')
call timer_start(LoadAdjust(100), 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
+ unlet g:triggered
+ au! CursorHoldI
+ set updatetime&
+ endfunc
+
+ func Test_cursorhold_insert_with_timer_interrupt()
+ if !has('job')
+ return
+ endif
+ " Need to move the cursor.
+ call feedkeys("ggG", "xt")
+
+ " Confirm the timer invoked in exit_cb of the job doesn't disturb
+ " CursorHoldI event.
+ let g:triggered = 0
+ au CursorHoldI * let g:triggered += 1
+ set updatetime=500
+ call job_start(has('win32') ? 'cmd /c echo:' : 'echo',
+ \ {'exit_cb': {j, s -> timer_start(1000, 'ExitInsertMode')}})
+ call feedkeys('a', 'x!')
+ call assert_equal(1, g:triggered)
+ unlet g:triggered
au! CursorHoldI
set updatetime&
endfunc
@@ -46,10 +68,35 @@ if has('timers')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
+ unlet g:triggered
au! CursorHoldI
set updatetime&
endfunc
-endif
+
+ func Test_OptionSet_modeline()
+ throw 'skipped: Nvim does not support test_override()'
+ call test_override('starting', 1)
+ au! OptionSet
+ augroup set_tabstop
+ au OptionSet tabstop call timer_start(1, {-> execute("echo 'Handler called'", "")})
+ augroup END
+ call writefile(['vim: set ts=7 sw=5 :', 'something'], 'XoptionsetModeline')
+ set modeline
+ let v:errmsg = ''
+ call assert_fails('split XoptionsetModeline', 'E12:')
+ call assert_equal(7, &ts)
+ call assert_equal('', v:errmsg)
+
+ augroup set_tabstop
+ au!
+ augroup END
+ bwipe!
+ set ts&
+ call delete('XoptionsetModeline')
+ call test_override('starting', 0)
+ endfunc
+
+endif "has('timers')
func Test_bufunload()
augroup test_bufunload_group
@@ -457,7 +504,7 @@ endfunc
func Test_OptionSet()
throw 'skipped: Nvim does not support test_override()'
- if !has("eval") || !has("autocmd") || !exists("+autochdir")
+ if !has("eval") || !exists("+autochdir")
return
endif
@@ -598,7 +645,7 @@ endfunc
func Test_OptionSet_diffmode()
throw 'skipped: Nvim does not support test_override()'
call test_override('starting', 1)
- " 18: Changing an option when enetering diff mode
+ " 18: Changing an option when entering diff mode
new
au OptionSet diff :let &l:cul=v:option_new
@@ -1161,23 +1208,23 @@ func Test_TextYankPost()
norm "ayiw
call assert_equal(
- \{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
\g:event)
norm y_
call assert_equal(
- \{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'},
+ \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'},
\g:event)
call feedkeys("\<C-V>y", 'x')
call assert_equal(
- \{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"},
+ \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'regtype': "\x161"},
\g:event)
norm "xciwbar
call assert_equal(
- \{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
\g:event)
norm "bdiw
call assert_equal(
- \{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
+ \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
\g:event)
call assert_equal({}, v:event)
@@ -1308,3 +1355,319 @@ func Test_Changed_FirstTime()
call delete('Xchanged.txt')
bwipe!
endfunc
+
+func Test_autocmd_nested()
+ let g:did_nested = 0
+ augroup Testing
+ au WinNew * edit somefile
+ au BufNew * let g:did_nested = 1
+ augroup END
+ split
+ call assert_equal(0, g:did_nested)
+ close
+ bwipe! somefile
+
+ " old nested argument still works
+ augroup Testing
+ au!
+ au WinNew * nested edit somefile
+ au BufNew * let g:did_nested = 1
+ augroup END
+ split
+ call assert_equal(1, g:did_nested)
+ close
+ bwipe! somefile
+
+ " New ++nested argument works
+ augroup Testing
+ au!
+ au WinNew * ++nested edit somefile
+ au BufNew * let g:did_nested = 1
+ augroup END
+ split
+ call assert_equal(1, g:did_nested)
+ close
+ bwipe! somefile
+
+ augroup Testing
+ au!
+ augroup END
+
+ call assert_fails('au WinNew * ++nested ++nested echo bad', 'E983:')
+ call assert_fails('au WinNew * nested nested echo bad', 'E983:')
+endfunc
+
+func Test_autocmd_once()
+ " Without ++once WinNew triggers twice
+ let g:did_split = 0
+ augroup Testing
+ au WinNew * let g:did_split += 1
+ augroup END
+ split
+ split
+ call assert_equal(2, g:did_split)
+ call assert_true(exists('#WinNew'))
+ close
+ close
+
+ " With ++once WinNew triggers once
+ let g:did_split = 0
+ augroup Testing
+ au!
+ au WinNew * ++once let g:did_split += 1
+ augroup END
+ split
+ split
+ call assert_equal(1, g:did_split)
+ call assert_false(exists('#WinNew'))
+ close
+ close
+
+ call assert_fails('au WinNew * ++once ++once echo bad', 'E983:')
+endfunc
+
+func Test_autocmd_bufreadpre()
+ new
+ let b:bufreadpre = 1
+ call append(0, range(100))
+ w! XAutocmdBufReadPre.txt
+ autocmd BufReadPre <buffer> :let b:bufreadpre += 1
+ norm! 50gg
+ sp
+ norm! 100gg
+ wincmd p
+ let g:wsv1 = winsaveview()
+ wincmd p
+ let g:wsv2 = winsaveview()
+ " triggers BufReadPre, should not move the cursor in either window
+ " The topline may change one line in a large window.
+ edit
+ call assert_inrange(g:wsv2.topline - 1, g:wsv2.topline + 1, winsaveview().topline)
+ call assert_equal(g:wsv2.lnum, winsaveview().lnum)
+ call assert_equal(2, b:bufreadpre)
+ wincmd p
+ call assert_equal(g:wsv1.topline, winsaveview().topline)
+ call assert_equal(g:wsv1.lnum, winsaveview().lnum)
+ call assert_equal(2, b:bufreadpre)
+ " Now set the cursor position in an BufReadPre autocommand
+ " (even though the position will be invalid, this should make Vim reset the
+ " cursor position in the other window.
+ wincmd p
+ 1
+ " won't do anything, but try to set the cursor on an invalid lnum
+ autocmd BufReadPre <buffer> :norm! 70gg
+ " triggers BufReadPre, should not move the cursor in either window
+ e
+ call assert_equal(1, winsaveview().topline)
+ call assert_equal(1, winsaveview().lnum)
+ call assert_equal(3, b:bufreadpre)
+ wincmd p
+ call assert_equal(g:wsv1.topline, winsaveview().topline)
+ call assert_equal(g:wsv1.lnum, winsaveview().lnum)
+ call assert_equal(3, b:bufreadpre)
+ close
+ close
+ call delete('XAutocmdBufReadPre.txt')
+endfunc
+
+" Tests for the following autocommands:
+" - FileWritePre writing a compressed file
+" - FileReadPost reading a compressed file
+" - BufNewFile reading a file template
+" - BufReadPre decompressing the file to be read
+" - FilterReadPre substituting characters in the temp file
+" - FilterReadPost substituting characters after filtering
+" - FileReadPre set options for decompression
+" - FileReadPost decompress the file
+func Test_ReadWrite_Autocmds()
+ " Run this test only on Unix-like systems and if gzip is available
+ if !has('unix') || !executable("gzip")
+ return
+ endif
+
+ " Make $GZIP empty, "-v" would cause trouble.
+ let $GZIP = ""
+
+ " Use a FileChangedShell autocommand to avoid a prompt for 'Xtestfile.gz'
+ " being modified outside of Vim (noticed on Solaris).
+ au FileChangedShell * echo 'caught FileChangedShell'
+
+ " Test for the FileReadPost, FileWritePre and FileWritePost autocmds
+ augroup Test1
+ au!
+ au FileWritePre *.gz '[,']!gzip
+ au FileWritePost *.gz undo
+ au FileReadPost *.gz '[,']!gzip -d
+ augroup END
+
+ new
+ set bin
+ call append(0, [
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ])
+ 1,9write! Xtestfile.gz
+ enew! | close
+
+ new
+ " Read and decompress the testfile
+ 0read Xtestfile.gz
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], getline(1, 9))
+ enew! | close
+
+ augroup Test1
+ au!
+ augroup END
+
+ " Test for the FileAppendPre and FileAppendPost autocmds
+ augroup Test2
+ au!
+ au BufNewFile *.c read Xtest.c
+ au FileAppendPre *.out '[,']s/new/NEW/
+ au FileAppendPost *.out !cat Xtest.c >> test.out
+ augroup END
+
+ call writefile(['/*', ' * Here is a new .c file', ' */'], 'Xtest.c')
+ new foo.c " should load Xtest.c
+ call assert_equal(['/*', ' * Here is a new .c file', ' */'], getline(2, 4))
+ w! >> test.out " append it to the output file
+
+ let contents = readfile('test.out')
+ call assert_equal(' * Here is a NEW .c file', contents[2])
+ call assert_equal(' * Here is a new .c file', contents[5])
+
+ call delete('test.out')
+ enew! | close
+ augroup Test2
+ au!
+ augroup END
+
+ " Test for the BufReadPre and BufReadPost autocmds
+ augroup Test3
+ au!
+ " setup autocommands to decompress before reading and re-compress
+ " afterwards
+ au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand("<afile>"))
+ au BufReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))
+ au BufReadPost *.gz call rename(expand("<afile>"), expand("<afile>:r"))
+ au BufReadPost *.gz exe '!gzip ' . shellescape(expand("<afile>:r"))
+ augroup END
+
+ e! Xtestfile.gz " Edit compressed file
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], getline(1, 9))
+
+ w! >> test.out " Append it to the output file
+
+ augroup Test3
+ au!
+ augroup END
+
+ " Test for the FilterReadPre and FilterReadPost autocmds.
+ set shelltemp " need temp files here
+ augroup Test4
+ au!
+ au FilterReadPre *.out call rename(expand("<afile>"), expand("<afile>") . ".t")
+ au FilterReadPre *.out exe 'silent !sed s/e/E/ ' . shellescape(expand("<afile>")) . ".t >" . shellescape(expand("<afile>"))
+ au FilterReadPre *.out exe 'silent !rm ' . shellescape(expand("<afile>")) . '.t'
+ au FilterReadPost *.out '[,']s/x/X/g
+ augroup END
+
+ e! test.out " Edit the output file
+ 1,$!cat
+ call assert_equal([
+ \ 'linE 2 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 4 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 6 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 7 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 8 AbcdefghijklmnopqrstuvwXyz',
+ \ 'linE 9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ \ 'linE 10 AbcdefghijklmnopqrstuvwXyz'
+ \ ], getline(1, 9))
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], readfile('test.out'))
+
+ augroup Test4
+ au!
+ augroup END
+ set shelltemp&vim
+
+ " Test for the FileReadPre and FileReadPost autocmds.
+ augroup Test5
+ au!
+ au FileReadPre *.gz exe 'silent !gzip -d ' . shellescape(expand("<afile>"))
+ au FileReadPre *.gz call rename(expand("<afile>:r"), expand("<afile>"))
+ au FileReadPost *.gz '[,']s/l/L/
+ augroup END
+
+ new
+ 0r Xtestfile.gz " Read compressed file
+ call assert_equal([
+ \ 'Line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'Line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'Line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], getline(1, 9))
+ call assert_equal([
+ \ 'line 2 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 4 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 6 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 8 Abcdefghijklmnopqrstuvwxyz',
+ \ 'line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ \ 'line 10 Abcdefghijklmnopqrstuvwxyz'
+ \ ], readfile('Xtestfile.gz'))
+
+ augroup Test5
+ au!
+ augroup END
+
+ au! FileChangedShell
+ call delete('Xtestfile.gz')
+ call delete('Xtest.c')
+ call delete('test.out')
+endfunc
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index 770ed55b8d..4436ebbf31 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -24,7 +24,10 @@ func Test_cd_no_arg()
call assert_equal(path, getcwd())
else
" Test that cd without argument echoes cwd on non-Unix systems.
+ let shellslash = &shellslash
+ set shellslash
call assert_match(getcwd(), execute('cd'))
+ let &shellslash = shellslash
endif
endfunc
diff --git a/src/nvim/testdir/test_checkpath.vim b/src/nvim/testdir/test_checkpath.vim
new file mode 100644
index 0000000000..a7557a107d
--- /dev/null
+++ b/src/nvim/testdir/test_checkpath.vim
@@ -0,0 +1,113 @@
+" Tests for the :checkpath command
+
+" Test for 'include' without \zs or \ze
+func Test_checkpath1()
+ let save_shellslash = &shellslash
+ set shellslash
+ call mkdir("Xdir1/dir2", "p")
+ call writefile(['#include "bar.a"'], 'Xdir1/dir2/foo.a')
+ call writefile(['#include "baz.a"'], 'Xdir1/dir2/bar.a')
+ call writefile(['#include "foo.a"'], 'Xdir1/dir2/baz.a')
+ call writefile(['#include <foo.a>'], 'Xbase.a')
+
+ edit Xbase.a
+ set path=Xdir1/dir2
+ let res = split(execute("checkpath!"), "\n")
+ call assert_equal([
+ \ '--- Included files in path ---',
+ \ 'Xdir1/dir2/foo.a',
+ \ 'Xdir1/dir2/foo.a -->',
+ \ ' Xdir1/dir2/bar.a',
+ \ ' Xdir1/dir2/bar.a -->',
+ \ ' Xdir1/dir2/baz.a',
+ \ ' Xdir1/dir2/baz.a -->',
+ \ ' "foo.a" (Already listed)'], res)
+
+ enew
+ call delete("./Xbase.a")
+ call delete("Xdir1", "rf")
+ set path&
+ let &shellslash = save_shellslash
+endfunc
+
+func DotsToSlashes()
+ return substitute(v:fname, '\.', '/', 'g') . '.b'
+endfunc
+
+" Test for 'include' with \zs and \ze
+func Test_checkpath2()
+ let save_shellslash = &shellslash
+ set shellslash
+ call mkdir("Xdir1/dir2", "p")
+ call writefile(['%inc /bar/'], 'Xdir1/dir2/foo.b')
+ call writefile(['%inc /baz/'], 'Xdir1/dir2/bar.b')
+ call writefile(['%inc /foo/'], 'Xdir1/dir2/baz.b')
+ call writefile(['%inc /foo/'], 'Xbase.b')
+
+ let &include='^\s*%inc\s*/\zs[^/]\+\ze'
+ let &includeexpr='DotsToSlashes()'
+
+ edit Xbase.b
+ set path=Xdir1/dir2
+ let res = split(execute("checkpath!"), "\n")
+ call assert_equal([
+ \ '--- Included files in path ---',
+ \ 'Xdir1/dir2/foo.b',
+ \ 'Xdir1/dir2/foo.b -->',
+ \ ' Xdir1/dir2/bar.b',
+ \ ' Xdir1/dir2/bar.b -->',
+ \ ' Xdir1/dir2/baz.b',
+ \ ' Xdir1/dir2/baz.b -->',
+ \ ' foo (Already listed)'], res)
+
+ enew
+ call delete("./Xbase.b")
+ call delete("Xdir1", "rf")
+ set path&
+ set include&
+ set includeexpr&
+ let &shellslash = save_shellslash
+endfunc
+
+func StripNewlineChar()
+ if v:fname =~ '\n$'
+ return v:fname[:-2]
+ endif
+ return v:fname
+endfunc
+
+" Test for 'include' with \zs and no \ze
+func Test_checkpath3()
+ let save_shellslash = &shellslash
+ set shellslash
+ call mkdir("Xdir1/dir2", "p")
+ call writefile(['%inc bar.c'], 'Xdir1/dir2/foo.c')
+ call writefile(['%inc baz.c'], 'Xdir1/dir2/bar.c')
+ call writefile(['%inc foo.c'], 'Xdir1/dir2/baz.c')
+ call writefile(['%inc foo.c'], 'Xdir1/dir2/FALSE.c')
+ call writefile(['%inc FALSE.c foo.c'], 'Xbase.c')
+
+ let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze'
+ let &includeexpr='StripNewlineChar()'
+
+ edit Xbase.c
+ set path=Xdir1/dir2
+ let res = split(execute("checkpath!"), "\n")
+ call assert_equal([
+ \ '--- Included files in path ---',
+ \ 'Xdir1/dir2/foo.c',
+ \ 'Xdir1/dir2/foo.c -->',
+ \ ' Xdir1/dir2/bar.c',
+ \ ' Xdir1/dir2/bar.c -->',
+ \ ' Xdir1/dir2/baz.c',
+ \ ' Xdir1/dir2/baz.c -->',
+ \ ' foo.c (Already listed)'], res)
+
+ enew
+ call delete("./Xbase.c")
+ call delete("Xdir1", "rf")
+ set path&
+ set include&
+ set includeexpr&
+ let &shellslash = save_shellslash
+endfunc
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 60ef20e0b2..02840de743 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -35,7 +35,8 @@ func Test_client_server()
endif
" Takes a short while for the server to be active.
- call WaitFor('serverlist() =~ "' . name . '"')
+ " When using valgrind it takes much longer.
+ call WaitFor('serverlist() =~ "' . name . '"', 5000)
call assert_match(name, serverlist())
call remote_foreground(name)
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index 2d793ed88f..7262789ab4 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -173,7 +173,6 @@ func Test_command_count_4()
only!
exe bufnr . 'buf'
- bnext
let bufnr = bufnr('%')
let buffers = []
.,$-bufdo call add(buffers, bufnr('%'))
diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim
new file mode 100644
index 0000000000..db18f7695d
--- /dev/null
+++ b/src/nvim/testdir/test_debugger.vim
@@ -0,0 +1,232 @@
+" Tests for the Vim script debug commands
+
+source shared.vim
+" source screendump.vim
+
+" Run a Vim debugger command
+" If the expected output argument is supplied, then check for it.
+func RunDbgCmd(buf, cmd, ...)
+ call term_sendkeys(a:buf, a:cmd . "\r")
+ call term_wait(a:buf)
+
+ if a:0 != 0
+ " Verify the expected output
+ let lnum = 20 - len(a:1)
+ for l in a:1
+ call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))})
+ let lnum += 1
+ endfor
+ endif
+endfunc
+
+" Debugger tests
+func Test_Debugger()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ " Create a Vim script with some functions
+ call writefile([
+ \ 'func Foo()',
+ \ ' let var1 = 1',
+ \ ' let var2 = Bar(var1) + 9',
+ \ ' return var2',
+ \ 'endfunc',
+ \ 'func Bar(var)',
+ \ ' let var1 = 2 + a:var',
+ \ ' let var2 = Bazz(var1) + 4',
+ \ ' return var2',
+ \ 'endfunc',
+ \ 'func Bazz(var)',
+ \ ' let var1 = 3 + a:var',
+ \ ' let var3 = "another var"',
+ \ ' let var3 = "value2"',
+ \ ' let var3 = "value3"',
+ \ ' return var1',
+ \ 'endfunc'], 'Xtest.vim')
+
+ " Start Vim in a terminal
+ let buf = RunVimInTerminal('-S Xtest.vim', {})
+
+ " Start the Vim debugger
+ call RunDbgCmd(buf, ':debug echo Foo()')
+
+ " Create a few stack frames by stepping through functions
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+
+ " check backtrace
+ call RunDbgCmd(buf, 'backtrace', [
+ \ ' 2 function Foo[2]',
+ \ ' 1 Bar[2]',
+ \ '->0 Bazz',
+ \ 'line 2: let var3 = "another var"'])
+
+ " Check variables in different stack frames
+ call RunDbgCmd(buf, 'echo var1', ['6'])
+
+ call RunDbgCmd(buf, 'up')
+ call RunDbgCmd(buf, 'back', [
+ \ ' 2 function Foo[2]',
+ \ '->1 Bar[2]',
+ \ ' 0 Bazz',
+ \ 'line 2: let var3 = "another var"'])
+ call RunDbgCmd(buf, 'echo var1', ['3'])
+
+ call RunDbgCmd(buf, 'u')
+ call RunDbgCmd(buf, 'bt', [
+ \ '->2 function Foo[2]',
+ \ ' 1 Bar[2]',
+ \ ' 0 Bazz',
+ \ 'line 2: let var3 = "another var"'])
+ call RunDbgCmd(buf, 'echo var1', ['1'])
+
+ " Undefined variables
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'frame 2')
+ call RunDbgCmd(buf, 'echo var3', [
+ \ 'Error detected while processing function Foo[2]..Bar[2]..Bazz:',
+ \ 'line 3:',
+ \ 'E121: Undefined variable: var3'])
+
+ " var3 is defined in this level with some other value
+ call RunDbgCmd(buf, 'fr 0')
+ call RunDbgCmd(buf, 'echo var3', ['another var'])
+
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'step', [
+ \ 'function Foo[2]..Bar',
+ \ 'line 3: End of function'])
+ call RunDbgCmd(buf, 'up')
+
+ " Undefined var2
+ call RunDbgCmd(buf, 'echo var2', [
+ \ 'Error detected while processing function Foo[2]..Bar:',
+ \ 'line 3:',
+ \ 'E121: Undefined variable: var2'])
+
+ " Var2 is defined with 10
+ call RunDbgCmd(buf, 'down')
+ call RunDbgCmd(buf, 'echo var2', ['10'])
+
+ " Backtrace movements
+ call RunDbgCmd(buf, 'b', [
+ \ ' 1 function Foo[2]',
+ \ '->0 Bar',
+ \ 'line 3: End of function'])
+
+ " next command cannot go down, we are on bottom
+ call RunDbgCmd(buf, 'down', ['frame is zero'])
+ call RunDbgCmd(buf, 'up')
+
+ " next command cannot go up, we are on top
+ call RunDbgCmd(buf, 'up', ['frame at highest level: 1'])
+ call RunDbgCmd(buf, 'where', [
+ \ '->1 function Foo[2]',
+ \ ' 0 Bar',
+ \ 'line 3: End of function'])
+
+ " fil is not frame or finish, it is file
+ call RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--'])
+
+ " relative backtrace movement
+ call RunDbgCmd(buf, 'fr -1')
+ call RunDbgCmd(buf, 'frame', [
+ \ ' 1 function Foo[2]',
+ \ '->0 Bar',
+ \ 'line 3: End of function'])
+
+ call RunDbgCmd(buf, 'fr +1')
+ call RunDbgCmd(buf, 'fram', [
+ \ '->1 function Foo[2]',
+ \ ' 0 Bar',
+ \ 'line 3: End of function'])
+
+ " go beyond limits does not crash
+ call RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1'])
+ call RunDbgCmd(buf, 'fra', [
+ \ '->1 function Foo[2]',
+ \ ' 0 Bar',
+ \ 'line 3: End of function'])
+
+ call RunDbgCmd(buf, 'frame -40', ['frame is zero'])
+ call RunDbgCmd(buf, 'fram', [
+ \ ' 1 function Foo[2]',
+ \ '->0 Bar',
+ \ 'line 3: End of function'])
+
+ " final result 19
+ call RunDbgCmd(buf, 'cont', ['19'])
+
+ " breakpoints tests
+
+ " Start a debug session, so that reading the last line from the terminal
+ " works properly.
+ call RunDbgCmd(buf, ':debug echo Foo()')
+
+ " No breakpoints
+ call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
+
+ " Place some breakpoints
+ call RunDbgCmd(buf, 'breaka func Bar')
+ call RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1'])
+ call RunDbgCmd(buf, 'breakadd func 3 Bazz')
+ call RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1',
+ \ ' 2 func Bazz line 3'])
+
+ " Check whether the breakpoints are hit
+ call RunDbgCmd(buf, 'cont', [
+ \ 'Breakpoint in "Bar" line 1',
+ \ 'function Foo[2]..Bar',
+ \ 'line 1: let var1 = 2 + a:var'])
+ call RunDbgCmd(buf, 'cont', [
+ \ 'Breakpoint in "Bazz" line 3',
+ \ 'function Foo[2]..Bar[2]..Bazz',
+ \ 'line 3: let var3 = "value2"'])
+
+ " Delete the breakpoints
+ call RunDbgCmd(buf, 'breakd 1')
+ call RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3'])
+ call RunDbgCmd(buf, 'breakdel func 3 Bazz')
+ call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
+
+ call RunDbgCmd(buf, 'cont')
+
+ " Make sure the breakpoints are removed
+ call RunDbgCmd(buf, ':echo Foo()', ['19'])
+
+ " Delete a non-existing breakpoint
+ call RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2'])
+
+ " Expression breakpoint
+ call RunDbgCmd(buf, ':breakadd func 2 Bazz')
+ call RunDbgCmd(buf, ':echo Bazz(1)')
+ call RunDbgCmd(buf, 'step')
+ call RunDbgCmd(buf, 'breaka expr var3')
+ call RunDbgCmd(buf, 'breakl', [' 4 expr var3'])
+ call RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 4',
+ \ 'Oldval = "''another var''"',
+ \ 'Newval = "''value2''"',
+ \ 'function Bazz',
+ \ 'line 4: let var3 = "value3"'])
+
+ call RunDbgCmd(buf, 'breakdel *')
+ call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
+
+ " finish the current function
+ call RunDbgCmd(buf, 'finish', [
+ \ 'function Bazz',
+ \ 'line 5: End of function'])
+ call RunDbgCmd(buf, 'cont')
+
+ call StopVimInTerminal(buf)
+
+ call delete('Xtest.vim')
+endfunc
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index ad3eec3274..00f4563f3d 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -527,7 +527,7 @@ func Test_setting_cursor()
new Xtest2
put =range(1,100)
wq
-
+
tabe Xtest2
$
diffsp Xtest1
@@ -672,6 +672,35 @@ func Test_diff_filler()
%bwipe!
endfunc
+func Test_diff_hlID()
+ new
+ call setline(1, [1, 2, 3])
+ diffthis
+ vnew
+ call setline(1, ['1x', 2, 'x', 3])
+ diffthis
+ redraw
+
+ call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "")
+
+ call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
+ call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
+ call assert_equal(diff_hlID(1, 2), hlID("DiffText"))
+ call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText")
+ call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
+ call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
+ call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd")
+ call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "")
+
+ wincmd w
+ call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
+ call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
+ call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
+ call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "")
+
+ %bwipe!
+endfunc
+
func Test_diff_lastline()
enew!
only!
@@ -734,6 +763,9 @@ func Test_diff_of_diff()
call VerifyScreenDump(buf, 'Test_diff_of_diff_01', {})
+ call term_sendkeys(buf, ":set rightleft\<cr>")
+ call VerifyScreenDump(buf, 'Test_diff_of_diff_02', {})
+
" clean up
call StopVimInTerminal(buf)
call delete('Xtest_diff_diff')
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index f8946c6929..7f3994300f 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -402,8 +402,19 @@ func! Test_edit_13()
call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix')
call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$'))
set smartindent& autoindent&
- bw!
+ bwipe!
endif
+
+ " Test autoindent removing indent of blank line.
+ new
+ call setline(1, ' foo bar baz')
+ set autoindent
+ exe "normal 0eea\<CR>\<CR>\<Esc>"
+ call assert_equal(" foo bar", getline(1))
+ call assert_equal("", getline(2))
+ call assert_equal(" baz", getline(3))
+ set autoindent&
+ bwipe!
endfunc
func! Test_edit_CR()
@@ -1374,9 +1385,26 @@ func Test_edit_complete_very_long_name()
return
endtry
- " Try to get the Vim window position before setting 'columns'.
+ " Try to get the Vim window position before setting 'columns', so that we can
+ " move the window back to where it was.
let winposx = getwinposx()
let winposy = getwinposy()
+
+ if winposx >= 0 && winposy >= 0 && !has('gui_running')
+ " We did get the window position, but xterm may report the wrong numbers.
+ " Move the window to the reported position and compute any offset.
+ exe 'winpos ' . winposx . ' ' . winposy
+ sleep 100m
+ let x = getwinposx()
+ if x >= 0
+ let winposx += winposx - x
+ endif
+ let y = getwinposy()
+ if y >= 0
+ let winposy += winposy - y
+ endif
+ endif
+
let save_columns = &columns
" Need at least about 1100 columns to reproduce the problem.
set columns=2000
@@ -1443,3 +1471,19 @@ func Test_leave_insert_autocmd()
au! InsertLeave
iunmap x
endfunc
+
+" Test for inserting characters using CTRL-V followed by a number.
+func Test_edit_special_chars()
+ new
+
+ if has("ebcdic")
+ let t = "o\<C-V>193\<C-V>xc2\<C-V>o303 \<C-V>90a\<C-V>xfg\<C-V>o578\<Esc>"
+ else
+ let t = "o\<C-V>65\<C-V>x42\<C-V>o103 \<C-V>33a\<C-V>xfg\<C-V>o78\<Esc>"
+ endif
+
+ exe "normal " . t
+ call assert_equal("ABC !a\<C-O>g\<C-G>8", getline(2))
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 111c85bb95..1850fb0cf1 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -21,3 +21,31 @@ func Test_E963()
call assert_fails("let v:oldfiles=''", 'E963:')
call assert_equal(v_o, v:oldfiles)
endfunc
+
+func Test_mkdir_p()
+ call mkdir('Xmkdir/nested', 'p')
+ call assert_true(isdirectory('Xmkdir/nested'))
+ try
+ " Trying to make existing directories doesn't error
+ call mkdir('Xmkdir', 'p')
+ call mkdir('Xmkdir/nested', 'p')
+ catch /E739:/
+ call assert_report('mkdir(..., "p") failed for an existing directory')
+ endtry
+ " 'p' doesn't suppress real errors
+ call writefile([], 'Xfile')
+ call assert_fails('call mkdir("Xfile", "p")', 'E739')
+ call delete('Xfile')
+ call delete('Xmkdir', 'rf')
+endfunc
+
+func Test_line_continuation()
+ let array = [5,
+ "\ ignore this
+ \ 6,
+ "\ more to ignore
+ "\ more moreto ignore
+ \ ]
+ "\ and some more
+ call assert_equal([5, 6], array)
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 5d4a0ff3cb..7a99a37be4 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -200,7 +200,7 @@ let s:filename_checks = {
\ 'hog': ['file.hog', 'snort.conf', 'vision.conf'],
\ 'hostconf': ['/etc/host.conf'],
\ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny'],
- \ 'htmlcheetah': ['file.tmpl'],
+ \ 'template': ['file.tmpl'],
\ 'htmlm4': ['file.html.m4'],
\ 'httest': ['file.htt', 'file.htb'],
\ 'ibasic': ['file.iba', 'file.ibi'],
@@ -450,6 +450,7 @@ let s:filename_checks = {
\ 'tssgm': ['file.tssgm'],
\ 'tssop': ['file.tssop'],
\ 'twig': ['file.twig'],
+ \ 'typescript': ['file.ts'],
\ 'uc': ['file.uc'],
\ 'udevconf': ['/etc/udev/udev.conf'],
\ 'udevperm': ['/etc/udev/permissions.d/file.permissions'],
@@ -484,7 +485,7 @@ let s:filename_checks = {
\ 'xhtml': ['file.xhtml', 'file.xht'],
\ 'xinetd': ['/etc/xinetd.conf'],
\ 'xmath': ['file.msc', 'file.msf'],
- \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ts', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl'],
+ \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl'],
\ 'xmodmap': ['anyXmodmap'],
\ 'xf86conf': ['xorg.conf', 'xorg.conf-4'],
\ 'xpm2': ['file.xpm2'],
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
index 0bae161a8b..f5488a6a27 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -133,6 +133,7 @@ func Test_finddir()
let save_shellslash = &shellslash
let save_dir = getcwd()
set path=,,
+ set shellslash
call CreateFiles()
cd Xdir1
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 824baffbc9..46c2d0f4cd 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -881,14 +881,25 @@ func Test_Executable()
call assert_equal(1, executable('notepad'))
call assert_equal(1, executable('notepad.exe'))
call assert_equal(0, executable('notepad.exe.exe'))
- call assert_equal(1, executable('shell32.dll'))
- call assert_equal(1, executable('win.ini'))
+ call assert_equal(0, executable('shell32.dll'))
+ call assert_equal(0, executable('win.ini'))
elseif has('unix')
call assert_equal(1, executable('cat'))
call assert_equal(0, executable('nodogshere'))
endif
endfunc
+func Test_executable_longname()
+ if !has('win32')
+ return
+ endif
+
+ let fname = 'X' . repeat('あ', 200) . '.bat'
+ call writefile([], fname)
+ call assert_equal(1, executable(fname))
+ call delete(fname)
+endfunc
+
func Test_hostname()
let hostname_vim = hostname()
if has('unix')
@@ -1049,3 +1060,19 @@ func Test_func_range_with_edit()
call delete('Xfuncrange2')
bwipe!
endfunc
+
+sandbox function Fsandbox()
+ normal ix
+endfunc
+
+func Test_func_sandbox()
+ sandbox let F = {-> 'hello'}
+ call assert_equal('hello', F())
+
+ sandbox let F = {-> execute("normal ix\<Esc>")}
+ call assert_fails('call F()', 'E48:')
+ unlet F
+
+ call assert_fails('call Fsandbox()', 'E48:')
+ delfunc Fsandbox
+endfunc
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index d233046d2b..accd21e9a3 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -64,3 +64,38 @@ func Test_gF()
bwipe Xfile
bwipe Xfile2
endfunc
+
+" Test for invoking 'gf' on a ${VAR} variable
+func Test_gf()
+ if has("ebcdic")
+ set isfname=@,240-249,/,.,-,_,+,,,$,:,~,{,}
+ else
+ set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
+ endif
+
+ call writefile(["Test for gf command"], "Xtest1")
+ if has("unix")
+ call writefile([" ${CDIR}/Xtest1"], "Xtestgf")
+ else
+ call writefile([" $TDIR/Xtest1"], "Xtestgf")
+ endif
+ new Xtestgf
+ if has("unix")
+ let $CDIR = "."
+ /CDIR
+ else
+ if has("amiga")
+ let $TDIR = "/testdir"
+ else
+ let $TDIR = "."
+ endif
+ /TDIR
+ endif
+
+ normal gf
+ call assert_equal('Xtest1', fnamemodify(bufname(''), ":t"))
+ close!
+
+ call delete('Xtest1')
+ call delete('Xtestgf')
+endfunc
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 33df79581c..e751fb5d16 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -129,10 +129,6 @@ func Test_highlight_eol_with_cursorline()
endfunc
func Test_highlight_eol_with_cursorline_vertsplit()
- if !has('vertsplit')
- return
- endif
-
let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine()
call NewWindow('topleft 5', 5)
@@ -533,3 +529,45 @@ func Test_termguicolors()
set t_Co=0
redraw
endfunc
+
+func Test_cursorline_after_yank()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call writefile([
+ \ 'set cul rnu',
+ \ 'call setline(1, ["","1","2","3",""])',
+ \ ], 'Xtest_cursorline_yank')
+ let buf = RunVimInTerminal('-S Xtest_cursorline_yank', {'rows': 8})
+ call term_wait(buf)
+ call term_sendkeys(buf, "Gy3k")
+ call term_wait(buf)
+ call term_sendkeys(buf, "jj")
+
+ call VerifyScreenDump(buf, 'Test_cursorline_yank_01', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_cursorline_yank')
+endfunc
+
+func Test_cursorline_with_visualmode()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call writefile([
+ \ 'set cul',
+ \ 'call setline(1, repeat(["abc"], 50))',
+ \ ], 'Xtest_cursorline_with_visualmode')
+ let buf = RunVimInTerminal('-S Xtest_cursorline_with_visualmode', {'rows': 12})
+ call term_wait(buf)
+ call term_sendkeys(buf, "V\<C-f>kkkjk")
+
+ call VerifyScreenDump(buf, 'Test_cursorline_with_visualmode_01', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_cursorline_with_visualmode')
+endfunc
diff --git a/src/nvim/testdir/test_jumplist.vim b/src/nvim/testdir/test_jumplist.vim
new file mode 100644
index 0000000000..be1af5e705
--- /dev/null
+++ b/src/nvim/testdir/test_jumplist.vim
@@ -0,0 +1,66 @@
+" Tests for the jumplist functionality
+
+" Tests for the getjumplist() function
+func Test_getjumplist()
+ if !has("jumplist")
+ return
+ endif
+
+ %bwipe
+ clearjumps
+ call assert_equal([[], 0], getjumplist())
+ call assert_equal([[], 0], getjumplist(1))
+ call assert_equal([[], 0], getjumplist(1, 1))
+
+ call assert_equal([], getjumplist(100))
+ call assert_equal([], getjumplist(1, 100))
+
+ let lines = []
+ for i in range(1, 100)
+ call add(lines, "Line " . i)
+ endfor
+ call writefile(lines, "Xtest")
+
+ " Jump around and create a jump list
+ edit Xtest
+ let bnr = bufnr('%')
+ normal 50%
+ normal G
+ normal gg
+
+ let expected = [[
+ \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 50, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 100, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 3]
+ call assert_equal(expected, getjumplist())
+ " jumplist doesn't change in between calls
+ call assert_equal(expected, getjumplist())
+
+ " Traverse the jump list and verify the results
+ 5
+ exe "normal \<C-O>"
+ call assert_equal(2, getjumplist(1)[1])
+ exe "normal 2\<C-O>"
+ call assert_equal(0, getjumplist(1, 1)[1])
+ exe "normal 3\<C-I>"
+ call assert_equal(3, getjumplist()[1])
+ exe "normal \<C-O>"
+ normal 20%
+ let expected = [[
+ \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 50, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 5, 'bufnr': bnr, 'col': 0, 'coladd': 0},
+ \ {'lnum': 100, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 4]
+ call assert_equal(expected, getjumplist())
+ " jumplist doesn't change in between calls
+ call assert_equal(expected, getjumplist())
+
+ let l = getjumplist()
+ call test_garbagecollect_now()
+ call assert_equal(4, l[1])
+ clearjumps
+ call test_garbagecollect_now()
+ call assert_equal(4, l[1])
+
+ call delete("Xtest")
+endfunc
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index ada25da4a8..bc7817cef8 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -291,3 +291,9 @@ func Test_named_function_closure()
call garbagecollect()
call assert_equal(14, s:Abar())
endfunc
+
+func Test_lambda_with_index()
+ let List = {x -> [x]}
+ let Extract = {-> function(List, ['foobar'])()[0]}
+ call assert_equal('foobar', Extract())
+endfunc
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 4899f59910..57cfaa298e 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -90,6 +90,45 @@ func Test_listchars()
\ '.....h>-$',
\ 'iii<<<<><<$', '$'], l)
+
+ " test nbsp
+ normal ggdG
+ set listchars=nbsp:X,trail:Y
+ set list
+ " Non-breaking space
+ let nbsp = nr2char(0xa0)
+ call append(0, [ ">".nbsp."<" ])
+
+ let expected = '>X< '
+
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, virtcol('$')))
+
+ set listchars=nbsp:X
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, virtcol('$')))
+
+ " test extends
+ normal ggdG
+ set listchars=extends:Z
+ set nowrap
+ set nolist
+ call append(0, [ repeat('A', &columns + 1) ])
+
+ let expected = repeat('A', &columns)
+
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, &columns))
+
+ set list
+ let expected = expected[:-2] . 'Z'
+ redraw!
+ call cursor(1, 1)
+ call assert_equal([expected], ScreenLines(1, &columns))
+
enew!
set listchars& ff&
endfunc
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 32593a423a..67f07ff6de 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -4,6 +4,8 @@ if !has('multi_byte')
finish
endif
+source shared.vim
+
func Test_abbreviation()
" abbreviation with 0x80 should work
inoreab чкпр vim
@@ -173,6 +175,9 @@ func Test_abbr_after_line_join()
endfunc
func Test_map_timeout()
+ if !has('timers')
+ return
+ endif
nnoremap aaaa :let got_aaaa = 1<CR>
nnoremap bb :let got_bb = 1<CR>
nmap b aaa
@@ -182,7 +187,7 @@ func Test_map_timeout()
call feedkeys("\<Esc>", "t")
endfunc
set timeout timeoutlen=200
- call timer_start(300, 'ExitInsert')
+ let timer = timer_start(300, 'ExitInsert')
" After the 'b' Vim waits for another character to see if it matches 'bb'.
" When it times out it is expanded to "aaa", but there is no wait for
" "aaaa". Can't check that reliably though.
@@ -197,6 +202,39 @@ func Test_map_timeout()
nunmap b
set timeoutlen&
delfunc ExitInsert
+ call timer_stop(timer)
+endfunc
+
+func Test_map_timeout_with_timer_interrupt()
+ if !has('job') || !has('timers')
+ return
+ endif
+
+ " Confirm the timer invoked in exit_cb of the job doesn't disturb mapped key
+ " sequence.
+ new
+ let g:val = 0
+ nnoremap \12 :let g:val = 1<CR>
+ nnoremap \123 :let g:val = 2<CR>
+ set timeout timeoutlen=1000
+
+ func ExitCb(job, status)
+ let g:timer = timer_start(1, {_ -> feedkeys("3\<Esc>", 't')})
+ endfunc
+
+ call job_start([&shell, &shellcmdflag, 'echo'], {'exit_cb': 'ExitCb'})
+ call feedkeys('\12', 'xt!')
+ call assert_equal(2, g:val)
+
+ bwipe!
+ nunmap \12
+ nunmap \123
+ set timeoutlen&
+ call WaitFor({-> exists('g:timer')})
+ call timer_stop(g:timer)
+ unlet g:timer
+ unlet g:val
+ delfunc ExitCb
endfunc
func Test_cabbr_visual_mode()
diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim
index e608a2e58b..e926b946a9 100644
--- a/src/nvim/testdir/test_match.vim
+++ b/src/nvim/testdir/test_match.vim
@@ -202,6 +202,28 @@ func Test_matchaddpos()
set hlsearch&
endfunc
+func Test_matchaddpos_otherwin()
+ syntax on
+ new
+ call setline(1, ['12345', 'NP'])
+ let winid = win_getid()
+
+ wincmd w
+ call matchadd('Search', '4', 10, -1, {'window': winid})
+ call matchaddpos('Error', [[1,2], [2,2]], 10, -1, {'window': winid})
+ redraw!
+ call assert_notequal(screenattr(1,2), 0)
+ call assert_notequal(screenattr(1,4), 0)
+ call assert_notequal(screenattr(2,2), 0)
+ call assert_equal(screenattr(1,2), screenattr(2,2))
+ call assert_notequal(screenattr(1,2), screenattr(1,4))
+
+ wincmd w
+ bwipe!
+ call clearmatches()
+ syntax off
+endfunc
+
func Test_matchaddpos_using_negative_priority()
set hlsearch
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index c2e7bb7bf9..c790bd32e3 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -17,9 +17,9 @@ func Test_mksession()
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
- \ 'one multibyteCharacter',
- \ 'a two multiByte characters',
- \ 'A three mulTibyte characters',
+ \ 'one ä multibyteCharacter',
+ \ 'aä Ä two multiByte characters',
+ \ 'Aäöü three mulTibyte characters',
\ 'short line',
\ ])
let tmpfile = 'Xtemp'
@@ -240,13 +240,14 @@ endfunc
func Test_mksession_quote_in_filename()
let v:errmsg = ''
+ let filename = has('win32') ? 'x''y' : 'x''y"z'
%bwipe!
split another
- split x'y\"z
+ execute 'split' escape(filename, '"')
mksession! Xtest_mks_quoted.out
%bwipe!
source Xtest_mks_quoted.out
- call assert_true(bufexists("x'y\"z"))
+ call assert_true(bufexists(filename))
%bwipe!
call delete('Xtest_mks_quoted.out')
diff --git a/src/nvim/testdir/test_modeline.vim b/src/nvim/testdir/test_modeline.vim
new file mode 100644
index 0000000000..75fe1d993c
--- /dev/null
+++ b/src/nvim/testdir/test_modeline.vim
@@ -0,0 +1,84 @@
+func Test_modeline_invalid()
+ let modeline = &modeline
+ set modeline
+ call assert_fails('set Xmodeline', 'E518:')
+
+ let &modeline = modeline
+ bwipe!
+ call delete('Xmodeline')
+ endfunc
+
+func Test_modeline_filetype()
+ call writefile(['vim: set ft=c :', 'nothing'], 'Xmodeline_filetype')
+ let modeline = &modeline
+ set modeline
+ filetype plugin on
+ split Xmodeline_filetype
+ call assert_equal("c", &filetype)
+ call assert_equal(1, b:did_ftplugin)
+ call assert_equal("ccomplete#Complete", &ofu)
+
+ bwipe!
+ call delete('Xmodeline_filetype')
+ let &modeline = modeline
+ filetype plugin off
+endfunc
+
+func Test_modeline_syntax()
+ call writefile(['vim: set syn=c :', 'nothing'], 'Xmodeline_syntax')
+ let modeline = &modeline
+ set modeline
+ syntax enable
+ split Xmodeline_syntax
+ call assert_equal("c", &syntax)
+ call assert_equal("c", b:current_syntax)
+
+ bwipe!
+ call delete('Xmodeline_syntax')
+ let &modeline = modeline
+ syntax off
+endfunc
+
+func Test_modeline_keymap()
+ call writefile(['vim: set keymap=greek :', 'nothing'], 'Xmodeline_keymap')
+ let modeline = &modeline
+ set modeline
+ split Xmodeline_keymap
+ call assert_equal("greek", &keymap)
+ call assert_match('greek\|grk', b:keymap_name)
+
+ bwipe!
+ call delete('Xmodeline_keymap')
+ let &modeline = modeline
+ set keymap= iminsert=0 imsearch=-1
+endfunc
+
+func s:modeline_fails(what, text)
+ let fname = "Xmodeline_fails_" . a:what
+ call writefile(['vim: set ' . a:text . ' :', 'nothing'], fname)
+ let modeline = &modeline
+ set modeline
+ filetype plugin on
+ syntax enable
+ call assert_fails('split ' . fname, 'E474:')
+ call assert_equal("", &filetype)
+ call assert_equal("", &syntax)
+
+ bwipe!
+ call delete(fname)
+ let &modeline = modeline
+ filetype plugin off
+ syntax off
+endfunc
+
+func Test_modeline_filetype_fails()
+ call s:modeline_fails('filetype', 'ft=evil$CMD')
+endfunc
+
+func Test_modeline_syntax_fails()
+ call s:modeline_fails('syntax', 'syn=evil$CMD')
+endfunc
+
+func Test_modeline_keymap_fails()
+ call s:modeline_fails('keymap', 'keymap=evil$CMD')
+endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index d07b3fdbce..ef17209f74 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -82,7 +82,6 @@ fun! Test_normal00_optrans()
endfunc
func! Test_normal01_keymodel()
- throw "skipped: Nvim regression: 'keymodel'"
call Setup_NewWindow()
" Test 1: depending on 'keymodel' <s-down> does something different
50
@@ -1353,11 +1352,21 @@ func! Test_normal23_K()
bw!
return
endif
- set keywordprg=man\ --pager=cat
+
+ if has('mac')
+ " In MacOS, the option for specifying a pager is different
+ set keywordprg=man\ -P\ cat
+ else
+ set keywordprg=man\ --pager=cat
+ endif
" Test for using man
2
let a = execute('unsilent norm! K')
- call assert_match("man --pager=cat 'man'", a)
+ if has('mac')
+ call assert_match("man -P cat 'man'", a)
+ else
+ call assert_match("man --pager=cat 'man'", a)
+ endif
" clean up
let &keywordprg = k
@@ -2272,6 +2281,8 @@ endfunc
func! Test_normal45_drop()
if !has('dnd')
+ " The ~ register does not exist
+ call assert_beeps('norm! "~')
return
endif
@@ -2540,3 +2551,81 @@ func Test_delete_until_paragraph()
call assert_equal('', getline(1))
bwipe!
endfunc
+
+" Test for '[m', ']m', '[M' and ']M'
+" Jumping to beginning and end of methods in Java-like languages
+func Test_java_motion()
+ new
+ a
+Piece of Java
+{
+ tt m1 {
+ t1;
+ } e1
+
+ tt m2 {
+ t2;
+ } e2
+
+ tt m3 {
+ if (x)
+ {
+ t3;
+ }
+ } e3
+}
+.
+
+ normal gg
+
+ normal 2]maA
+ call assert_equal("\ttt m1 {A", getline('.'))
+ call assert_equal([3, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal j]maB
+ call assert_equal("\ttt m2 {B", getline('.'))
+ call assert_equal([7, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal ]maC
+ call assert_equal("\ttt m3 {C", getline('.'))
+ call assert_equal([11, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal [maD
+ call assert_equal("\ttt m3 {DC", getline('.'))
+ call assert_equal([11, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal k2[maE
+ call assert_equal("\ttt m1 {EA", getline('.'))
+ call assert_equal([3, 9, 16], [line('.'), col('.'), virtcol('.')])
+
+ normal 3[maF
+ call assert_equal("{F", getline('.'))
+ call assert_equal([2, 2, 2], [line('.'), col('.'), virtcol('.')])
+
+ normal ]MaG
+ call assert_equal("\t}G e1", getline('.'))
+ call assert_equal([5, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal j2]MaH
+ call assert_equal("\t}H e3", getline('.'))
+ call assert_equal([16, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal ]M]M
+ normal aI
+ call assert_equal("}I", getline('.'))
+ call assert_equal([17, 2, 2], [line('.'), col('.'), virtcol('.')])
+
+ normal 2[MaJ
+ call assert_equal("\t}JH e3", getline('.'))
+ call assert_equal([16, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal k[MaK
+ call assert_equal("\t}K e2", getline('.'))
+ call assert_equal([9, 3, 10], [line('.'), col('.'), virtcol('.')])
+
+ normal 3[MaL
+ call assert_equal("{LF", getline('.'))
+ call assert_equal([2, 2, 2], [line('.'), col('.'), virtcol('.')])
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
index 6c43cbc1dc..96f4bfc71b 100644
--- a/src/nvim/testdir/test_popup.vim
+++ b/src/nvim/testdir/test_popup.vim
@@ -5,7 +5,7 @@ source shared.vim
let g:months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
let g:setting = ''
-func! ListMonths()
+func ListMonths()
if g:setting != ''
exe ":set" g:setting
endif
@@ -18,7 +18,7 @@ func! ListMonths()
return ''
endfunc
-func! Test_popup_complete2()
+func Test_popup_complete2()
" Although the popupmenu is not visible, this does not mean completion mode
" has ended. After pressing <f5> to complete the currently typed char, Vim
" still stays in the first state of the completion (:h ins-completion-menu),
@@ -33,9 +33,9 @@ func! Test_popup_complete2()
call assert_equal(["Dece", "", "December2015"], getline(1,3))
%d
bw!
-endfu
+endfunc
-func! Test_popup_complete()
+func Test_popup_complete()
new
inoremap <f5> <c-r>=ListMonths()<cr>
@@ -214,10 +214,10 @@ func! Test_popup_complete()
call feedkeys("aM\<f5>\<enter>\<esc>", 'tx')
call assert_equal(["March", "M", "March"], getline(1,4))
%d
-endfu
+endfunc
-func! Test_popup_completion_insertmode()
+func Test_popup_completion_insertmode()
new
inoremap <F5> <C-R>=ListMonths()<CR>
@@ -246,20 +246,16 @@ func! Test_popup_completion_insertmode()
iunmap <F5>
endfunc
-" TODO: Fix what breaks after this line.
-" - Do not use "q!", it may exit Vim if there is an error
-finish
-
func Test_noinsert_complete()
- function! s:complTest1() abort
+ func! s:complTest1() abort
call complete(1, ['source', 'soundfold'])
return ''
- endfunction
+ endfunc
- function! s:complTest2() abort
+ func! s:complTest2() abort
call complete(1, ['source', 'soundfold'])
return ''
- endfunction
+ endfunc
new
set completeopt+=noinsert
@@ -279,10 +275,42 @@ func Test_noinsert_complete()
iunmap <F5>
endfunc
+func Test_complete_no_filter()
+ func! s:complTest1() abort
+ call complete(1, [{'word': 'foobar'}])
+ return ''
+ endfunc
+ func! s:complTest2() abort
+ call complete(1, [{'word': 'foobar', 'equal': 1}])
+ return ''
+ endfunc
+
+ let completeopt = &completeopt
+
+ " without equal=1
+ new
+ set completeopt=menuone,noinsert,menu
+ inoremap <F5> <C-R>=s:complTest1()<CR>
+ call feedkeys("i\<F5>z\<CR>\<CR>\<ESC>.", 'tx')
+ call assert_equal('z', getline(1))
+ bwipe!
+
+ " with equal=1
+ new
+ set completeopt=menuone,noinsert,menu
+ inoremap <F5> <C-R>=s:complTest2()<CR>
+ call feedkeys("i\<F5>z\<CR>\<CR>\<ESC>.", 'tx')
+ call assert_equal('foobar', getline(1))
+ bwipe!
+
+ let &completeopt = completeopt
+ iunmap <F5>
+endfunc
+
func Test_compl_vim_cmds_after_register_expr()
- function! s:test_func()
+ func! s:test_func()
return 'autocmd '
- endfunction
+ endfunc
augroup AAAAA_Group
au!
augroup END
@@ -329,7 +357,7 @@ func DummyCompleteTwo(findstart, base)
else
return ['twodef', 'twoDEF']
endif
-endfunction
+endfunc
" Test that nothing happens if the 'completefunc' opens
" a new window (no completion, no crash)
@@ -406,7 +434,7 @@ func Test_omnifunc_with_check()
q!
endfunc
-function UndoComplete()
+func UndoComplete()
call complete(1, ['January', 'February', 'March',
\ 'April', 'May', 'June', 'July', 'August', 'September',
\ 'October', 'November', 'December'])
@@ -443,7 +471,7 @@ func Test_complete_no_undo()
q!
endfunc
-function! DummyCompleteFive(findstart, base)
+func DummyCompleteFive(findstart, base)
if a:findstart
return 0
else
@@ -547,7 +575,7 @@ func Test_completion_comment_formatting()
bwipe!
endfunc
-function! DummyCompleteSix()
+func DummyCompleteSix()
call complete(1, ['Hello', 'World'])
return ''
endfunction
@@ -623,7 +651,7 @@ func Test_popup_and_preview_autocommand()
bw!
endfunc
-fun MessCompleteMonths()
+func MessCompleteMonths()
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep")
call complete_add(m)
if complete_check()
@@ -631,14 +659,14 @@ fun MessCompleteMonths()
endif
endfor
return []
-endfun
+endfunc
-fun MessCompleteMore()
+func MessCompleteMore()
call complete(1, split("Oct Nov Dec"))
return []
-endfun
+endfunc
-fun MessComplete(findstart, base)
+func MessComplete(findstart, base)
if a:findstart
let line = getline('.')
let start = col('.') - 1
@@ -651,7 +679,7 @@ fun MessComplete(findstart, base)
call MessCompleteMore()
return []
endif
-endf
+endfunc
func Test_complete_func_mess()
" Calling complete() after complete_add() in 'completefunc' is wrong, but it
@@ -712,4 +740,105 @@ func Test_popup_and_window_resize()
bwipe!
endfunc
+func Test_popup_complete_info_01()
+ new
+ inoremap <buffer><F5> <C-R>=complete_info().mode<CR>
+ func s:complTestEval() abort
+ call complete(1, ['aa', 'ab'])
+ return ''
+ endfunc
+ inoremap <buffer><F6> <C-R>=s:complTestEval()<CR>
+ call writefile([
+ \ 'dummy dummy.txt 1',
+ \], 'Xdummy.txt')
+ setlocal tags=Xdummy.txt
+ setlocal dictionary=Xdummy.txt
+ setlocal thesaurus=Xdummy.txt
+ setlocal omnifunc=syntaxcomplete#Complete
+ setlocal completefunc=syntaxcomplete#Complete
+ setlocal spell
+ for [keys, mode_name] in [
+ \ ["", ''],
+ \ ["\<C-X>", 'ctrl_x'],
+ \ ["\<C-X>\<C-N>", 'keyword'],
+ \ ["\<C-X>\<C-P>", 'keyword'],
+ \ ["\<C-X>\<C-L>", 'whole_line'],
+ \ ["\<C-X>\<C-F>", 'files'],
+ \ ["\<C-X>\<C-]>", 'tags'],
+ \ ["\<C-X>\<C-D>", 'path_defines'],
+ \ ["\<C-X>\<C-I>", 'path_patterns'],
+ \ ["\<C-X>\<C-K>", 'dictionary'],
+ \ ["\<C-X>\<C-T>", 'thesaurus'],
+ \ ["\<C-X>\<C-V>", 'cmdline'],
+ \ ["\<C-X>\<C-U>", 'function'],
+ \ ["\<C-X>\<C-O>", 'omni'],
+ \ ["\<C-X>s", 'spell'],
+ \ ["\<F6>", 'eval'],
+ \]
+ call feedkeys("i" . keys . "\<F5>\<Esc>", 'tx')
+ call assert_equal(mode_name, getline('.'))
+ %d
+ endfor
+ call delete('Xdummy.txt')
+ bwipe!
+endfunc
+
+func UserDefinedComplete(findstart, base)
+ if a:findstart
+ return 0
+ else
+ return [
+ \ { 'word': 'Jan', 'menu': 'January' },
+ \ { 'word': 'Feb', 'menu': 'February' },
+ \ { 'word': 'Mar', 'menu': 'March' },
+ \ { 'word': 'Apr', 'menu': 'April' },
+ \ { 'word': 'May', 'menu': 'May' },
+ \ ]
+ endif
+endfunc
+
+func GetCompleteInfo()
+ if empty(g:compl_what)
+ let g:compl_info = complete_info()
+ else
+ let g:compl_info = complete_info(g:compl_what)
+ endif
+ return ''
+endfunc
+
+func Test_popup_complete_info_02()
+ new
+ inoremap <buffer><F5> <C-R>=GetCompleteInfo()<CR>
+ setlocal completefunc=UserDefinedComplete
+
+ let d = {
+ \ 'mode': 'function',
+ \ 'pum_visible': 1,
+ \ 'items': [
+ \ {'word': 'Jan', 'menu': 'January', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'Feb', 'menu': 'February', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'Mar', 'menu': 'March', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'Apr', 'menu': 'April', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'May', 'menu': 'May', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}
+ \ ],
+ \ 'selected': 0,
+ \ }
+
+ let g:compl_what = []
+ call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
+ call assert_equal(d, g:compl_info)
+
+ let g:compl_what = ['mode', 'pum_visible', 'selected']
+ call remove(d, 'items')
+ call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
+ call assert_equal(d, g:compl_info)
+
+ let g:compl_what = ['mode']
+ call remove(d, 'selected')
+ call remove(d, 'pum_visible')
+ call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
+ call assert_equal(d, g:compl_info)
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim
index 0b8961c52b..43a5d18cb3 100644
--- a/src/nvim/testdir/test_put.vim
+++ b/src/nvim/testdir/test_put.vim
@@ -1,3 +1,4 @@
+" Tests for put commands, e.g. ":put", "p", "gp", "P", "gP", etc.
func Test_put_block()
if !has('multi_byte')
@@ -47,3 +48,59 @@ func Test_put_expr()
call assert_equal(['A1','A2','A3','4A','5A','6A'], getline(1,'$'))
bw!
endfunc
+
+func Test_put_lines()
+ new
+ let a = [ getreg('a'), getregtype('a') ]
+ call setline(1, ['Line 1', 'Line2', 'Line 3', ''])
+ exe 'norm! gg"add"AddG""p'
+ call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1,'$'))
+ " clean up
+ bw!
+ call setreg('a', a[0], a[1])
+endfunc
+
+func Test_put_fails_when_nomodifiable()
+ new
+ setlocal nomodifiable
+
+ normal! yy
+ call assert_fails(':put', 'E21')
+ call assert_fails(':put!', 'E21')
+ call assert_fails(':normal! p', 'E21')
+ call assert_fails(':normal! gp', 'E21')
+ call assert_fails(':normal! P', 'E21')
+ call assert_fails(':normal! gP', 'E21')
+
+ if has('mouse')
+ set mouse=n
+ call assert_fails('execute "normal! \<MiddleMouse>"', 'E21')
+ set mouse&
+ endif
+
+ bwipeout!
+endfunc
+
+" A bug was discovered where the Normal mode put commands (e.g., "p") would
+" output duplicate error messages when invoked in a non-modifiable buffer.
+func Test_put_p_errmsg_nodup()
+ new
+ setlocal nomodifiable
+
+ normal! yy
+
+ func Capture_p_error()
+ redir => s:p_err
+ normal! p
+ redir END
+ endfunc
+
+ silent! call Capture_p_error()
+
+ " Error message output within a function should be three lines (the function
+ " name, the line number, and the error message).
+ call assert_equal(3, count(s:p_err, "\n"))
+
+ delfunction Capture_p_error
+ bwipeout!
+endfunc
diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim
index 5ba9fd68cf..0c5809f7a4 100644
--- a/src/nvim/testdir/test_python2.vim
+++ b/src/nvim/testdir/test_python2.vim
@@ -52,3 +52,92 @@ func Test_vim_function()
py del f
delfunc s:foo
endfunc
+
+func _SetUpHiddenBuffer()
+ py import vim
+ new
+ edit hidden
+ setlocal bufhidden=hide
+
+ enew
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func _CleanUpHiddenBuffer()
+ bwipe! hidden
+ bwipe!
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Clear()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_List()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = [ 'test' ]
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Str()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = 'test'
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_ClearLine()
+ call _SetUpHiddenBuffer()
+ py vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func _SetUpVisibleBuffer()
+ py import vim
+ new
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Clear()
+ call _SetUpVisibleBuffer()
+
+ py vim.current.buffer[:] = None
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_List()
+ call _SetUpVisibleBuffer()
+
+ py vim.current.buffer[:] = [ 'test' ]
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunction
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Str()
+ call _SetUpVisibleBuffer()
+
+ py vim.current.buffer[-1] = None
+ call assert_equal( line( '.' ), 10 )
+
+ bwipe!
+endfunction
diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim
index 2e3fc93674..d090dcc99c 100644
--- a/src/nvim/testdir/test_python3.vim
+++ b/src/nvim/testdir/test_python3.vim
@@ -52,3 +52,92 @@ func Test_vim_function()
py3 del f
delfunc s:foo
endfunc
+
+func _SetUpHiddenBuffer()
+ py3 import vim
+ new
+ edit hidden
+ setlocal bufhidden=hide
+
+ enew
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func _CleanUpHiddenBuffer()
+ bwipe! hidden
+ bwipe!
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Clear()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_List()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][:] = [ 'test' ]
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_Str()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = 'test'
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func Test_Write_To_HiddenBuffer_Does_Not_Fix_Cursor_ClearLine()
+ call _SetUpHiddenBuffer()
+ py3 vim.buffers[ int( vim.eval( 'bufnr("hidden")' ) ) ][0] = None
+ call assert_equal( line( '.' ), 11 )
+ call _CleanUpHiddenBuffer()
+endfunc
+
+func _SetUpVisibleBuffer()
+ py3 import vim
+ new
+ let lnum = 0
+ while lnum < 10
+ call append( 1, string( lnum ) )
+ let lnum = lnum + 1
+ endwhile
+ normal G
+ call assert_equal( line( '.' ), 11 )
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Clear()
+ call _SetUpVisibleBuffer()
+
+ py3 vim.current.buffer[:] = None
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunc
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_List()
+ call _SetUpVisibleBuffer()
+
+ py3 vim.current.buffer[:] = [ 'test' ]
+ call assert_equal( line( '.' ), 1 )
+
+ bwipe!
+endfunction
+
+func Test_Write_To_Current_Buffer_Fixes_Cursor_Str()
+ call _SetUpVisibleBuffer()
+
+ py3 vim.current.buffer[-1] = None
+ call assert_equal( line( '.' ), 10 )
+
+ bwipe!
+endfunction
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index cb3e7ca8f6..c6d083dfcc 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -136,6 +136,19 @@ func XlistTests(cchar)
\ ' 4:40 col 20 x 44: Other',
\ ' 5:50 col 25 55: one'], l)
+ " Test for module names, one needs to explicitly set `'valid':v:true` so
+ let save_shellslash = &shellslash
+ set shellslash
+ call g:Xsetlist([
+ \ {'lnum':10,'col':5,'type':'W','module':'Data.Text','text':'ModuleWarning','nr':11,'valid':v:true},
+ \ {'lnum':20,'col':10,'type':'W','module':'Data.Text','filename':'Data/Text.hs','text':'ModuleWarning','nr':22,'valid':v:true},
+ \ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}])
+ let l = split(execute('Xlist', ""), "\n")
+ call assert_equal([' 1 Data.Text:10 col 5 warning 11: ModuleWarning',
+ \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning',
+ \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l)
+ let &shellslash = save_shellslash
+
" Error cases
call assert_fails('Xlist abc', 'E488:')
endfunc
@@ -430,6 +443,19 @@ func Xtest_browse(cchar)
call delete('Xqftestfile1')
call delete('Xqftestfile2')
+
+ " Should be able to use next/prev with invalid entries
+ Xexpr ""
+ call assert_equal(0, g:Xgetlist({'idx' : 0}).idx)
+ call assert_equal(0, g:Xgetlist({'size' : 0}).size)
+ Xaddexpr ['foo', 'bar', 'baz', 'quux', 'shmoo']
+ call assert_equal(5, g:Xgetlist({'size' : 0}).size)
+ Xlast
+ call assert_equal(5, g:Xgetlist({'idx' : 0}).idx)
+ Xfirst
+ call assert_equal(1, g:Xgetlist({'idx' : 0}).idx)
+ 2Xnext
+ call assert_equal(3, g:Xgetlist({'idx' : 0}).idx)
endfunc
func Test_browse()
@@ -469,6 +495,19 @@ func s:test_xhelpgrep(cchar)
" This wipes out the buffer, make sure that doesn't cause trouble.
Xclose
+ if a:cchar == 'l'
+ " When a help window is present, running :lhelpgrep should reuse the
+ " help window and not the current window
+ new | only
+ call g:Xsetlist([], 'f')
+ help index.txt
+ wincmd w
+ lhelpgrep quickfix
+ call assert_equal(1, winnr())
+ call assert_notequal([], getloclist(1))
+ call assert_equal([], getloclist(2))
+ endif
+
new | only
" Search for non existing help string
@@ -1067,6 +1106,21 @@ func Test_efm2()
call assert_equal(1, l[4].valid)
call assert_equal(expand('unittests/dbfacadeTest.py'), bufname(l[4].bufnr))
+ " Test for %o
+ set efm=%f(%o):%l\ %m
+ cgetexpr ['Xtestfile(Language.PureScript.Types):20 Error']
+ call writefile(['Line1'], 'Xtestfile')
+ let l = getqflist()
+ call assert_equal(1, len(l), string(l))
+ call assert_equal('Language.PureScript.Types', l[0].module)
+ copen
+ call assert_equal('Language.PureScript.Types|20| Error', getline(1))
+ call feedkeys("\<CR>", 'xn')
+ call assert_equal('Xtestfile', expand('%:t'))
+ cclose
+ bd
+ call delete("Xtestfile")
+
" The following sequence of commands used to crash Vim
set efm=%W%m
cgetexpr ['msg1']
@@ -1353,6 +1407,11 @@ func XquickfixSetListWithAct(cchar)
call assert_fails("call g:Xsetlist(list1, 0)", 'E928:')
endfunc
+func Test_setqflist_invalid_nr()
+ " The following command used to crash Vim
+ call setqflist([], ' ', {'nr' : $XXX_DOES_NOT_EXIST})
+endfunc
+
func Test_quickfix_set_list_with_act()
call XquickfixSetListWithAct('c')
call XquickfixSetListWithAct('l')
@@ -1666,6 +1725,10 @@ func HistoryTest(cchar)
call assert_equal(' error list 1 of 3; 1 ' . common, res[0])
call assert_equal(' error list 2 of 3; 2 ' . common, res[1])
call assert_equal('> error list 3 of 3; 3 ' . common, res[2])
+
+ call g:Xsetlist([], 'f')
+ let l = split(execute(a:cchar . 'hist'), "\n")
+ call assert_equal('No entries', l[0])
endfunc
func Test_history()
@@ -1749,8 +1812,8 @@ func Xproperty_tests(cchar)
call assert_equal(-1, s)
call assert_equal({}, g:Xgetlist({'abc':1}))
- call assert_equal({}, g:Xgetlist({'nr':99, 'title':1}))
- call assert_equal({}, g:Xgetlist({'nr':[], 'title':1}))
+ call assert_equal('', g:Xgetlist({'nr':99, 'title':1}).title)
+ call assert_equal('', g:Xgetlist({'nr':[], 'title':1}).title)
if a:cchar == 'l'
call assert_equal({}, getloclist(99, {'title': 1}))
@@ -1786,7 +1849,7 @@ func Xproperty_tests(cchar)
call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context)
only
call setloclist(0, [], 'f')
- call assert_equal({}, getloclist(0, {'context':1}))
+ call assert_equal('', getloclist(0, {'context':1}).context)
endif
" Test for changing the context of previous quickfix lists
@@ -1844,6 +1907,11 @@ func Xproperty_tests(cchar)
let l = g:Xgetlist({'items':1})
call assert_equal(0, len(l.items))
+ call g:Xsetlist([], 'r', {'title' : 'TestTitle'})
+ call g:Xsetlist([], 'r', {'items' : [{'filename' : 'F1', 'lnum' : 10, 'text' : 'L10'}]})
+ call g:Xsetlist([], 'r', {'items' : [{'filename' : 'F1', 'lnum' : 10, 'text' : 'L10'}]})
+ call assert_equal('TestTitle', g:Xgetlist({'title' : 1}).title)
+
" The following used to crash Vim with address sanitizer
call g:Xsetlist([], 'f')
call g:Xsetlist([], 'a', {'items' : [{'filename':'F1', 'lnum':10}]})
@@ -1886,10 +1954,10 @@ func Xproperty_tests(cchar)
call g:Xsetlist([], 'r', l2)
let newl1=g:Xgetlist({'nr':1,'all':1})
let newl2=g:Xgetlist({'nr':2,'all':1})
- call assert_equal(':Fruits', newl1.title)
+ call assert_equal('Fruits', newl1.title)
call assert_equal(['Fruits'], newl1.context)
call assert_equal('Line20', newl1.items[0].text)
- call assert_equal(':Colors', newl2.title)
+ call assert_equal('Colors', newl2.title)
call assert_equal(['Colors'], newl2.context)
call assert_equal('Line10', newl2.items[0].text)
call g:Xsetlist([], 'f')
@@ -1913,6 +1981,30 @@ func Test_Autocmd()
cexpr "F1:10:Line 10"
caddexpr "F1:20:Line 20"
cgetexpr "F1:30:Line 30"
+ cexpr ""
+ caddexpr ""
+ cgetexpr ""
+ silent! cexpr non_existing_func()
+ silent! caddexpr non_existing_func()
+ silent! cgetexpr non_existing_func()
+ let l = ['precexpr',
+ \ 'postcexpr',
+ \ 'precaddexpr',
+ \ 'postcaddexpr',
+ \ 'precgetexpr',
+ \ 'postcgetexpr',
+ \ 'precexpr',
+ \ 'postcexpr',
+ \ 'precaddexpr',
+ \ 'postcaddexpr',
+ \ 'precgetexpr',
+ \ 'postcgetexpr',
+ \ 'precexpr',
+ \ 'precaddexpr',
+ \ 'precgetexpr']
+ call assert_equal(l, g:acmds)
+
+ let g:acmds = []
enew! | call append(0, "F2:10:Line 10")
cbuffer!
enew! | call append(0, "F2:20:Line 20")
@@ -1920,19 +2012,108 @@ func Test_Autocmd()
enew! | call append(0, "F2:30:Line 30")
caddbuffer
- let l = ['precexpr',
- \ 'postcexpr',
- \ 'precaddexpr',
- \ 'postcaddexpr',
- \ 'precgetexpr',
- \ 'postcgetexpr',
- \ 'precbuffer',
+ new
+ let bnum = bufnr('%')
+ bunload
+ exe 'silent! cbuffer! ' . bnum
+ exe 'silent! cgetbuffer ' . bnum
+ exe 'silent! caddbuffer ' . bnum
+ enew!
+ let l = ['precbuffer',
\ 'postcbuffer',
\ 'precgetbuffer',
\ 'postcgetbuffer',
\ 'precaddbuffer',
- \ 'postcaddbuffer']
+ \ 'postcaddbuffer',
+ \ 'precbuffer',
+ \ 'precgetbuffer',
+ \ 'precaddbuffer']
+ call assert_equal(l, g:acmds)
+
+ call writefile(['Xtest:1:Line1'], 'Xtest')
+ call writefile([], 'Xempty')
+ let g:acmds = []
+ cfile Xtest
+ caddfile Xtest
+ cgetfile Xtest
+ cfile Xempty
+ caddfile Xempty
+ cgetfile Xempty
+ silent! cfile do_not_exist
+ silent! caddfile do_not_exist
+ silent! cgetfile do_not_exist
+ let l = ['precfile',
+ \ 'postcfile',
+ \ 'precaddfile',
+ \ 'postcaddfile',
+ \ 'precgetfile',
+ \ 'postcgetfile',
+ \ 'precfile',
+ \ 'postcfile',
+ \ 'precaddfile',
+ \ 'postcaddfile',
+ \ 'precgetfile',
+ \ 'postcgetfile',
+ \ 'precfile',
+ \ 'postcfile',
+ \ 'precaddfile',
+ \ 'postcaddfile',
+ \ 'precgetfile',
+ \ 'postcgetfile']
+ call assert_equal(l, g:acmds)
+
+ let g:acmds = []
+ helpgrep quickfix
+ silent! helpgrep non_existing_help_topic
+ vimgrep test Xtest
+ vimgrepadd test Xtest
+ silent! vimgrep non_existing_test Xtest
+ silent! vimgrepadd non_existing_test Xtest
+ set makeprg=
+ silent! make
+ set makeprg&
+ let l = ['prehelpgrep',
+ \ 'posthelpgrep',
+ \ 'prehelpgrep',
+ \ 'posthelpgrep',
+ \ 'previmgrep',
+ \ 'postvimgrep',
+ \ 'previmgrepadd',
+ \ 'postvimgrepadd',
+ \ 'previmgrep',
+ \ 'postvimgrep',
+ \ 'previmgrepadd',
+ \ 'postvimgrepadd',
+ \ 'premake',
+ \ 'postmake']
call assert_equal(l, g:acmds)
+
+ if has('unix')
+ " Run this test only on Unix-like systems. The grepprg may not be set on
+ " non-Unix systems.
+ " The following lines are used for the grep test. Don't remove.
+ " Grep_Autocmd_Text: Match 1
+ " GrepAdd_Autocmd_Text: Match 2
+ let g:acmds = []
+ silent grep Grep_Autocmd_Text test_quickfix.vim
+ silent grepadd GrepAdd_Autocmd_Text test_quickfix.vim
+ silent grep abc123def Xtest
+ silent grepadd abc123def Xtest
+ let l = ['pregrep',
+ \ 'postgrep',
+ \ 'pregrepadd',
+ \ 'postgrepadd',
+ \ 'pregrep',
+ \ 'postgrep',
+ \ 'pregrepadd',
+ \ 'postgrepadd']
+ call assert_equal(l, g:acmds)
+ endif
+
+ call delete('Xtest')
+ call delete('Xempty')
+ au! QuickFixCmdPre
+ au! QuickFixCmdPost
endfunc
func Test_Autocmd_Exception()
@@ -2184,8 +2365,8 @@ func XsizeTests(cchar)
call g:Xsetlist([], 'f')
call assert_equal(0, g:Xgetlist({'nr':'$'}).nr)
- call assert_equal(1, len(g:Xgetlist({'nr':'$', 'all':1})))
- call assert_equal(0, len(g:Xgetlist({'nr':0})))
+ call assert_equal('', g:Xgetlist({'nr':'$', 'all':1}).title)
+ call assert_equal(0, g:Xgetlist({'nr':0}).nr)
Xexpr "File1:10:Line1"
Xexpr "File2:20:Line2"
@@ -2405,6 +2586,29 @@ func Xmultifilestack_tests(cchar)
call assert_equal(3, l1.items[1].lnum)
call assert_equal('two.txt', bufname(l2.items[1].bufnr))
call assert_equal(5, l2.items[1].lnum)
+
+ " Test for start of a new error line in the same line where a previous
+ " error line ends with a file stack.
+ let efm_val = 'Error\ l%l\ in\ %f,'
+ let efm_val .= '%-P%>(%f%r,Error\ l%l\ in\ %m,%-Q)%r'
+ let l = g:Xgetlist({'lines' : [
+ \ '(one.txt',
+ \ 'Error l4 in one.txt',
+ \ ') (two.txt',
+ \ 'Error l6 in two.txt',
+ \ ')',
+ \ 'Error l8 in one.txt'
+ \ ], 'efm' : efm_val})
+ call assert_equal(3, len(l.items))
+ call assert_equal('one.txt', bufname(l.items[0].bufnr))
+ call assert_equal(4, l.items[0].lnum)
+ call assert_equal('one.txt', l.items[0].text)
+ call assert_equal('two.txt', bufname(l.items[1].bufnr))
+ call assert_equal(6, l.items[1].lnum)
+ call assert_equal('two.txt', l.items[1].text)
+ call assert_equal('one.txt', bufname(l.items[2].bufnr))
+ call assert_equal(8, l.items[2].lnum)
+ call assert_equal('', l.items[2].text)
endfunc
func Test_multifilestack()
@@ -2584,7 +2788,7 @@ func Xqfid_tests(cchar)
call s:setup_commands(a:cchar)
call g:Xsetlist([], 'f')
- call assert_equal({}, g:Xgetlist({'id':0}))
+ call assert_equal(0, g:Xgetlist({'id':0}).id)
Xexpr ''
let start_id = g:Xgetlist({'id' : 0}).id
Xexpr '' | Xexpr ''
@@ -2592,10 +2796,10 @@ func Xqfid_tests(cchar)
call assert_equal(start_id, g:Xgetlist({'id':0, 'nr':1}).id)
call assert_equal(start_id + 1, g:Xgetlist({'id':0, 'nr':0}).id)
call assert_equal(start_id + 2, g:Xgetlist({'id':0, 'nr':'$'}).id)
- call assert_equal({}, g:Xgetlist({'id':0, 'nr':99}))
+ call assert_equal(0, g:Xgetlist({'id':0, 'nr':99}).id)
call assert_equal(2, g:Xgetlist({'id':start_id + 1, 'nr':0}).nr)
- call assert_equal({}, g:Xgetlist({'id':99, 'nr':0}))
- call assert_equal({}, g:Xgetlist({'id':"abc", 'nr':0}))
+ call assert_equal(0, g:Xgetlist({'id':99, 'nr':0}).id)
+ call assert_equal(0, g:Xgetlist({'id':"abc", 'nr':0}).id)
call g:Xsetlist([], 'a', {'id':start_id, 'context':[1,2]})
call assert_equal([1,2], g:Xgetlist({'nr':1, 'context':1}).context)
@@ -2606,7 +2810,7 @@ func Xqfid_tests(cchar)
let qfid = g:Xgetlist({'id':0, 'nr':0})
call g:Xsetlist([], 'f')
- call assert_equal({}, g:Xgetlist({'id':qfid, 'nr':0}))
+ call assert_equal(0, g:Xgetlist({'id':qfid, 'nr':0}).id)
endfunc
func Test_qf_id()
@@ -2614,6 +2818,15 @@ func Test_qf_id()
call Xqfid_tests('l')
endfunc
+func Test_getqflist_invalid_nr()
+ " The following commands used to crash Vim
+ cexpr ""
+ call getqflist({'nr' : $XXX_DOES_NOT_EXIST_XXX})
+
+ " Cleanup
+ call setqflist([], 'r')
+endfunc
+
" Test for shortening/simplifying the file name when opening the
" quickfix window or when displaying the quickfix list
func Test_shorten_fname()
@@ -2638,6 +2851,99 @@ func Test_shorten_fname()
call assert_equal('test_quickfix.vim', bufname('test_quickfix.vim'))
endfunc
+" Quickfix title tests
+" In the below tests, 'exe "cmd"' is used to invoke the quickfix commands.
+" Otherwise due to indentation, the title is set with spaces at the beginning
+" of the command.
+func Test_qftitle()
+ call writefile(["F1:1:Line1"], 'Xerr')
+
+ " :cexpr
+ exe "cexpr readfile('Xerr')"
+ call assert_equal(":cexpr readfile('Xerr')", getqflist({'title' : 1}).title)
+
+ " :cgetexpr
+ exe "cgetexpr readfile('Xerr')"
+ call assert_equal(":cgetexpr readfile('Xerr')",
+ \ getqflist({'title' : 1}).title)
+
+ " :caddexpr
+ call setqflist([], 'f')
+ exe "caddexpr readfile('Xerr')"
+ call assert_equal(":caddexpr readfile('Xerr')",
+ \ getqflist({'title' : 1}).title)
+
+ " :cbuffer
+ new Xerr
+ exe "cbuffer"
+ call assert_equal(':cbuffer (Xerr)', getqflist({'title' : 1}).title)
+
+ " :cgetbuffer
+ edit Xerr
+ exe "cgetbuffer"
+ call assert_equal(':cgetbuffer (Xerr)', getqflist({'title' : 1}).title)
+
+ " :caddbuffer
+ call setqflist([], 'f')
+ edit Xerr
+ exe "caddbuffer"
+ call assert_equal(':caddbuffer (Xerr)', getqflist({'title' : 1}).title)
+
+ " :cfile
+ exe "cfile Xerr"
+ call assert_equal(':cfile Xerr', getqflist({'title' : 1}).title)
+
+ " :cgetfile
+ exe "cgetfile Xerr"
+ call assert_equal(':cgetfile Xerr', getqflist({'title' : 1}).title)
+
+ " :caddfile
+ call setqflist([], 'f')
+ exe "caddfile Xerr"
+ call assert_equal(':caddfile Xerr', getqflist({'title' : 1}).title)
+
+ " :grep
+ set grepprg=internal
+ exe "grep F1 Xerr"
+ call assert_equal(':grep F1 Xerr', getqflist({'title' : 1}).title)
+
+ " :grepadd
+ call setqflist([], 'f')
+ exe "grepadd F1 Xerr"
+ call assert_equal(':grepadd F1 Xerr', getqflist({'title' : 1}).title)
+ set grepprg&vim
+
+ " :vimgrep
+ exe "vimgrep F1 Xerr"
+ call assert_equal(':vimgrep F1 Xerr', getqflist({'title' : 1}).title)
+
+ " :vimgrepadd
+ call setqflist([], 'f')
+ exe "vimgrepadd F1 Xerr"
+ call assert_equal(':vimgrepadd F1 Xerr', getqflist({'title' : 1}).title)
+
+ call setqflist(['F1:10:L10'], ' ')
+ call assert_equal(':setqflist()', getqflist({'title' : 1}).title)
+
+ call setqflist([], 'f')
+ call setqflist(['F1:10:L10'], 'a')
+ call assert_equal(':setqflist()', getqflist({'title' : 1}).title)
+
+ call setqflist([], 'f')
+ call setqflist(['F1:10:L10'], 'r')
+ call assert_equal(':setqflist()', getqflist({'title' : 1}).title)
+
+ close
+ call delete('Xerr')
+
+ call setqflist([], ' ', {'title' : 'Errors'})
+ copen
+ call assert_equal('Errors', w:quickfix_title)
+ call setqflist([], 'r', {'items' : [{'filename' : 'a.c', 'lnum' : 10}]})
+ call assert_equal('Errors', w:quickfix_title)
+ cclose
+endfunc
+
" Test for the position of the quickfix and location list window
func Test_qfwin_pos()
" Open two windows
@@ -2664,3 +2970,198 @@ func Test_qfwin_pos()
call assert_equal(3, winnr())
close
endfunc
+
+" The following test used to crash Vim
+func Test_lhelpgrep_autocmd()
+ lhelpgrep quickfix
+ autocmd QuickFixCmdPost * call setloclist(0, [], 'f')
+ lhelpgrep buffer
+ call assert_equal('help', &filetype)
+ call assert_equal(0, getloclist(0, {'nr' : '$'}).nr)
+ lhelpgrep tabpage
+ call assert_equal('help', &filetype)
+ call assert_equal(1, getloclist(0, {'nr' : '$'}).nr)
+ au! QuickFixCmdPost
+ new | only
+endfunc
+
+" Test to make sure that an empty quickfix buffer is not reused for loading
+" a normal buffer.
+func Test_empty_qfbuf()
+ enew | only
+ call writefile(["Test"], 'Xfile1')
+ call setqflist([], 'f')
+ copen | only
+ let qfbuf = bufnr('')
+ edit Xfile1
+ call assert_notequal(qfbuf, bufnr(''))
+ enew
+ call delete('Xfile1')
+endfunc
+
+" Tests for the getqflist() and getloclist() functions when the list is not
+" present or is empty
+func Xgetlist_empty_tests(cchar)
+ call s:setup_commands(a:cchar)
+
+ " Empty quickfix stack
+ call g:Xsetlist([], 'f')
+ call assert_equal('', g:Xgetlist({'context' : 0}).context)
+ call assert_equal(0, g:Xgetlist({'id' : 0}).id)
+ call assert_equal(0, g:Xgetlist({'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'items' : 0}).items)
+ call assert_equal(0, g:Xgetlist({'nr' : 0}).nr)
+ call assert_equal(0, g:Xgetlist({'size' : 0}).size)
+ call assert_equal('', g:Xgetlist({'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
+ call assert_equal(0, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick': 0}, g:Xgetlist({'all' : 0}))
+
+ " Quickfix window with empty stack
+ silent! Xopen
+ let qfwinid = (a:cchar == 'c') ? win_getid() : 0
+ call assert_equal(qfwinid, g:Xgetlist({'winid' : 0}).winid)
+ Xclose
+
+ " Empty quickfix list
+ Xexpr ""
+ call assert_equal('', g:Xgetlist({'context' : 0}).context)
+ call assert_notequal(0, g:Xgetlist({'id' : 0}).id)
+ call assert_equal(0, g:Xgetlist({'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'items' : 0}).items)
+ call assert_notequal(0, g:Xgetlist({'nr' : 0}).nr)
+ call assert_equal(0, g:Xgetlist({'size' : 0}).size)
+ call assert_notequal('', g:Xgetlist({'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'winid' : 0}).winid)
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ let qfid = g:Xgetlist({'id' : 0}).id
+ call g:Xsetlist([], 'f')
+
+ " Non-existing quickfix identifier
+ call assert_equal('', g:Xgetlist({'id' : qfid, 'context' : 0}).context)
+ call assert_equal(0, g:Xgetlist({'id' : qfid}).id)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'id' : qfid, 'items' : 0}).items)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'nr' : 0}).nr)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'size' : 0}).size)
+ call assert_equal('', g:Xgetlist({'id' : qfid, 'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'winid' : 0}).winid)
+ call assert_equal(0, g:Xgetlist({'id' : qfid, 'changedtick' : 0}).changedtick)
+ call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
+
+ " Non-existing quickfix list number
+ call assert_equal('', g:Xgetlist({'nr' : 5, 'context' : 0}).context)
+ call assert_equal(0, g:Xgetlist({'nr' : 5}).nr)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'idx' : 0}).idx)
+ call assert_equal([], g:Xgetlist({'nr' : 5, 'items' : 0}).items)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'id' : 0}).id)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'size' : 0}).size)
+ call assert_equal('', g:Xgetlist({'nr' : 5, 'title' : 0}).title)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'winid' : 0}).winid)
+ call assert_equal(0, g:Xgetlist({'nr' : 5, 'changedtick' : 0}).changedtick)
+ call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
+endfunc
+
+func Test_getqflist()
+ call Xgetlist_empty_tests('c')
+ call Xgetlist_empty_tests('l')
+endfunc
+
+" Tests for the quickfix/location list changedtick
+func Xqftick_tests(cchar)
+ call s:setup_commands(a:cchar)
+
+ call g:Xsetlist([], 'f')
+
+ Xexpr "F1:10:Line10"
+ let qfid = g:Xgetlist({'id' : 0}).id
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ Xaddexpr "F2:20:Line20\nF2:21:Line21"
+ call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([], 'a', {'lines' : ["F3:30:Line30", "F3:31:Line31"]})
+ call assert_equal(3, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([], 'r', {'lines' : ["F4:40:Line40"]})
+ call assert_equal(4, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([], 'a', {'title' : 'New Title'})
+ call assert_equal(5, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ enew!
+ call append(0, ["F5:50:L50", "F6:60:L60"])
+ Xaddbuffer
+ call assert_equal(6, g:Xgetlist({'changedtick' : 0}).changedtick)
+ enew!
+
+ call g:Xsetlist([], 'a', {'context' : {'bus' : 'pci'}})
+ call assert_equal(7, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
+ \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'a')
+ call assert_equal(8, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
+ \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], ' ')
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call g:Xsetlist([{'filename' : 'F7', 'lnum' : 10, 'text' : 'L7'},
+ \ {'filename' : 'F7', 'lnum' : 11, 'text' : 'L11'}], 'r')
+ call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ call writefile(["F8:80:L80", "F8:81:L81"], "Xone")
+ Xfile Xone
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ Xaddfile Xone
+ call assert_equal(2, g:Xgetlist({'changedtick' : 0}).changedtick)
+
+ " Test case for updating a non-current quickfix list
+ call g:Xsetlist([], 'f')
+ Xexpr "F1:1:L1"
+ Xexpr "F2:2:L2"
+ call g:Xsetlist([], 'a', {'nr' : 1, "lines" : ["F10:10:L10"]})
+ call assert_equal(1, g:Xgetlist({'changedtick' : 0}).changedtick)
+ call assert_equal(2, g:Xgetlist({'nr' : 1, 'changedtick' : 0}).changedtick)
+
+ call delete("Xone")
+endfunc
+
+func Test_qf_tick()
+ call Xqftick_tests('c')
+ call Xqftick_tests('l')
+endfunc
+
+" The following test used to crash vim
+func Test_lbuffer_crash()
+ sv Xtest
+ augroup QF_Test
+ au!
+ au * * bw
+ augroup END
+ lbuffer
+ augroup QF_Test
+ au!
+ augroup END
+endfunc
+
+" The following test used to crash vim
+func Test_lexpr_crash()
+ augroup QF_Test
+ au!
+ au * * call setloclist(0, [], 'f')
+ augroup END
+ lexpr ""
+ augroup QF_Test
+ au!
+ augroup END
+ enew | only
+endfunc
+
+" The following test used to crash Vim
+func Test_lvimgrep_crash()
+ sv Xtest
+ augroup QF_Test
+ au!
+ au * * call setloclist(0, [], 'f')
+ augroup END
+ lvimgrep quickfix test_quickfix.vim
+ augroup QF_Test
+ au!
+ augroup END
+ enew | only
+endfunc
diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim
index 2192b5e8fc..7873502943 100644
--- a/src/nvim/testdir/test_regex_char_classes.vim
+++ b/src/nvim/testdir/test_regex_char_classes.vim
@@ -1,5 +1,11 @@
" Tests for regexp with backslash and other special characters inside []
" Also test backslash for hex/octal numbered character.
+"
+if !has('multi_byte')
+ finish
+endif
+
+scriptencoding utf-8
function RunSTest(value, calls, expected)
new
@@ -56,3 +62,237 @@ function Test_s_search()
call RunSTest(" xyz", "s/~/bcd/", " bcd")
call RunSTest(" bcdbcdbcd", "s/~\\+/BB/", " BB")
endfunction
+
+" Test character classes in regexp using regexpengine 0, 1, 2.
+func Test_regex_char_classes()
+ new
+ let save_enc = &encoding
+ set encoding=utf-8
+
+ let input = "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"
+
+ " Format is [cmd_to_run, expected_output]
+ let tests = [
+ \ [':s/\%#=0\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=1\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=2\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=0[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=1[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=2[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=0\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\O//g',
+ \ "01234567"],
+ \ [':s/\%#=1\O//g',
+ \ "01234567"],
+ \ [':s/\%#=2\O//g',
+ \ "01234567"],
+ \ [':s/\%#=0[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=1[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=2[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=0\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=1\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=2\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=0[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=1[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=2[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=0\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=1\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=2\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=0[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=1[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=2[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=0\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=1\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=2\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=0[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=1[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=2[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=0\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=1\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=2\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=0[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=1[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=2[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=0\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=1[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=2[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009b"],
+ \ [':s/\%#=0[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"],
+ \ [':s/\%#=1[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"],
+ \ [':s/\%#=2[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"]
+ \]
+
+ for [cmd, expected] in tests
+ call append(0, input)
+ call cursor(1, 1)
+ exe cmd
+ call assert_equal(expected, getline(1), cmd)
+ endfor
+
+ let &encoding = save_enc
+ enew!
+ close
+endfunc
diff --git a/src/nvim/testdir/test_scrollbind.vim b/src/nvim/testdir/test_scrollbind.vim
index baa24f1979..6c5488be05 100644
--- a/src/nvim/testdir/test_scrollbind.vim
+++ b/src/nvim/testdir/test_scrollbind.vim
@@ -30,3 +30,243 @@ func Test_scrollbind()
setl noscrollbind
call assert_equal(0, topLineLeft - topLineRight)
endfunc
+
+" Test for 'scrollbind'
+func Test_scrollbind_opt()
+ new | only
+ set noscrollbind
+ set scrollopt=ver,jump scrolloff=2 nowrap noequalalways splitbelow
+
+ " Insert the text used for the test
+ append
+
+
+start of window 1
+. line 01 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 01
+. line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02
+. line 03 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 03
+. line 04 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 04
+. line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05
+. line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06
+. line 07 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 07
+. line 08 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 08
+. line 09 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 09
+. line 10 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 10
+. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11
+. line 12 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 12
+. line 13 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 13
+. line 14 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 14
+. line 15 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 15
+end of window 1
+
+
+start of window 2
+. line 01 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 01
+. line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02
+. line 03 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 03
+. line 04 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 04
+. line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05
+. line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06
+. line 07 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 07
+. line 08 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 08
+. line 09 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 09
+. line 10 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 10
+. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11
+. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12
+. line 13 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 13
+. line 14 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 14
+. line 15 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 15
+. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16
+end of window 2
+
+.
+
+ " Test using two windows open to one buffer, one extra empty window
+ split
+ new
+ wincmd t
+ resize 8
+ call search('^start of window 1$')
+ normal zt
+ set scrollbind
+ wincmd j
+ resize 7
+ call search('^start of window 2$')
+ normal zt
+ set scrollbind
+
+ " -- start of tests --
+ " Test scrolling down
+ normal L5jHyy
+ wincmd b | normal pr0
+ wincmd t | normal Hyy
+ wincmd b | normal pr1
+ wincmd t | normal L6jHyy
+ wincmd b | normal pr2
+ wincmd k | normal Hyy
+ wincmd b | normal pr3
+
+ " Test scrolling up
+ wincmd t | normal H4k
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal pr4
+ wincmd k | normal Hyy
+ wincmd b | normal pr5
+ wincmd k | normal 3k
+ wincmd t | normal H
+ wincmd j | normal Hyy
+ wincmd b | normal pr6
+ wincmd t | normal Hyy
+ wincmd b | normal pr7
+
+ " Test horizontal scrolling
+ set scrollopt+=hor
+ normal gg"zyyG"zpG
+ wincmd t | normal 015zly$
+ wincmd b | normal p"zpG
+ wincmd k | normal y$
+ wincmd b | normal p"zpG
+ wincmd k | normal 10jH7zhg0y$
+ wincmd b | normal p"zpG
+ wincmd t | normal Hg0y$
+ wincmd b | normal p"zpG
+ set scrollopt-=hor
+
+ wincmd b
+ call assert_equal([
+ \ '',
+ \ '0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05',
+ \ '1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05',
+ \ '2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ '3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06',
+ \ '5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06',
+ \ '6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ '56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ 'UTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ ''], getline(1, '$'))
+ enew!
+
+ " ****** tests using two different buffers *****
+ wincmd t | wincmd j | close
+ wincmd t | set noscrollbind
+ /start of window 2$/,/^end of window 2$/y
+ new
+ wincmd t | wincmd j | normal 4"zpGp
+ wincmd t
+ call search('^start of window 1$')
+ normal zt
+ set scrollbind
+ wincmd j
+ call search('^start of window 2$')
+ normal zt
+ set scrollbind
+
+ " -- start of tests --
+ " Test scrolling down
+ normal L5jHyy
+ wincmd b | normal pr0
+ wincmd t | normal Hyy
+ wincmd b | normal pr1
+ wincmd t | normal L6jHyy
+ wincmd b | normal pr2
+ wincmd k | normal Hyy
+ wincmd b | normal pr3
+
+ " Test scrolling up
+ wincmd t | normal H4k
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal pr4
+ wincmd k | normal Hyy
+ wincmd b | normal pr5
+ wincmd k | normal 3k
+ wincmd t | normal H
+ wincmd j | normal Hyy
+ wincmd b | normal pr6
+ wincmd t | normal Hyy
+ wincmd b | normal pr7
+
+ " Test horizontal scrolling
+ set scrollopt+=hor
+ normal gg"zyyG"zpG
+ wincmd t | normal 015zly$
+ wincmd b | normal p"zpG
+ wincmd k | normal y$
+ wincmd b | normal p"zpG
+ wincmd k | normal 10jH7zhg0y$
+ wincmd b | normal p"zpG
+ wincmd t | normal Hg0y$
+ wincmd b | normal p"zpG
+ set scrollopt-=hor
+
+ wincmd b
+ call assert_equal([
+ \ '',
+ \ '0 line 05 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 05',
+ \ '1 line 05 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 05',
+ \ '2 line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ '3 line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '4 line 06 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 06',
+ \ '5 line 06 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 06',
+ \ '6 line 02 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '7 line 02 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ '56789ABCDEFGHIJKLMNOPQRSTUVWXYZ 02',
+ \ 'UTSRQPONMLKJIHGREDCBA9876543210 02',
+ \ '. line 11 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 11',
+ \ '. line 11 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 11',
+ \ ''], getline(1, '$'))
+ enew!
+
+ " Test 'syncbind'
+ wincmd t | set noscrollbind | normal ggL
+ wincmd j | set noscrollbind | normal ggL
+ set scrollbind
+ wincmd t | set scrollbind | normal G
+ wincmd j | normal G
+ syncbind
+ normal Hk
+ wincmd t | normal H
+ wincmd j | normal Hyy
+ wincmd b | normal p
+ wincmd t | normal yy
+ wincmd b | normal p
+ wincmd t | set noscrollbind | normal ggL
+ wincmd j | set noscrollbind
+ normal ggL
+ set scrollbind
+ wincmd t | set scrollbind
+ wincmd t | normal G
+ wincmd j | normal G
+ wincmd t | syncbind | normal Hk
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal p
+ wincmd t | wincmd j | normal yy
+ wincmd b | normal p
+ wincmd t | normal H3k
+ wincmd j | normal H
+ wincmd t | normal Hyy
+ wincmd b | normal p
+ wincmd t | wincmd j | normal yy
+ wincmd b | normal p
+
+ wincmd b
+ call assert_equal([
+ \ '',
+ \ '. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16',
+ \ 'start of window 2',
+ \ 'start of window 2',
+ \ '. line 16 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 16',
+ \ '. line 15 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 15',
+ \ '. line 12 ZYXWVUTSRQPONMLKJIHGREDCBA9876543210 12',
+ \ ], getline(1, '$'))
+ enew!
+
+ new | only!
+ set scrollbind& scrollopt& scrolloff& wrap& equalalways& splitbelow&
+endfunc
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 08ccc8d4fe..f4fe4051e3 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -585,3 +585,27 @@ func Test_one_error_msg()
" This was also giving an internal error
call assert_fails('call search(" \\((\\v[[=P=]]){185}+ ")', 'E871:')
endfunc
+
+" Test for the search() function with match at the cursor position
+func Test_search_match_at_curpos()
+ new
+ call append(0, ['foobar', '', 'one two', ''])
+
+ normal gg
+
+ call search('foobar', 'c')
+ call assert_equal([1, 1], [line('.'), col('.')])
+
+ normal j
+ call search('^$', 'c')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ call search('^$', 'bc')
+ call assert_equal([2, 1], [line('.'), col('.')])
+
+ exe "normal /two\<CR>"
+ call search('.', 'c')
+ call assert_equal([3, 5], [line('.'), col('.')])
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index d02454fbf0..8b306192f0 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -107,6 +107,32 @@ function! Test_substitute_variants()
endfor
endfunction
+" Test the l, p, # flags.
+func Test_substitute_flags_lp()
+ new
+ call setline(1, "abc\tdef\<C-h>ghi")
+
+ let a = execute('s/a/a/p')
+ call assert_equal("\nabc def^Hghi", a)
+
+ let a = execute('s/a/a/l')
+ call assert_equal("\nabc^Idef^Hghi$", a)
+
+ let a = execute('s/a/a/#')
+ call assert_equal("\n 1 abc def^Hghi", a)
+
+ let a = execute('s/a/a/p#')
+ call assert_equal("\n 1 abc def^Hghi", a)
+
+ let a = execute('s/a/a/l#')
+ call assert_equal("\n 1 abc^Idef^Hghi$", a)
+
+ let a = execute('s/a/a/')
+ call assert_equal("", a)
+
+ bwipe!
+endfunc
+
func Test_substitute_repeat()
" This caused an invalid memory access.
split Xfile
@@ -585,3 +611,93 @@ func Test_sub_replace_10()
call assert_equal('aa2a3a', substitute('123', '1\|\ze', 'a', 'g'))
call assert_equal('1aaa', substitute('123', '1\zs\|[23]', 'a', 'g'))
endfunc
+
+func Test_nocatch_sub_failure_handling()
+ " normal error results in all replacements
+ func! Foo()
+ foobar
+ endfunc
+ new
+ call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
+ %s/aaa/\=Foo()/g
+ call assert_equal(['1 0', '2 0', '3 0'], getline(1, 3))
+
+ " Trow without try-catch causes abort after the first line.
+ " We cannot test this, since it would stop executing the test script.
+
+ " try/catch does not result in any changes
+ func! Foo()
+ throw 'error'
+ endfunc
+ call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
+ let error_caught = 0
+ try
+ %s/aaa/\=Foo()/g
+ catch
+ let error_caught = 1
+ endtry
+ call assert_equal(1, error_caught)
+ call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
+
+ " Same, but using "n" flag so that "sandbox" gets set
+ call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
+ let error_caught = 0
+ try
+ %s/aaa/\=Foo()/gn
+ catch
+ let error_caught = 1
+ endtry
+ call assert_equal(1, error_caught)
+ call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
+
+ bwipe!
+endfunc
+
+" Test ":s/pat/sub/" with different ~s in sub.
+func Test_replace_with_tilde()
+ new
+ " Set the last replace string to empty
+ s/^$//
+ call append(0, ['- Bug in "vPPPP" on this text:'])
+ normal gg
+ s/u/~u~/
+ call assert_equal('- Bug in "vPPPP" on this text:', getline(1))
+ s/i/~u~/
+ call assert_equal('- Bug uuun "vPPPP" on this text:', getline(1))
+ s/o/~~~/
+ call assert_equal('- Bug uuun "vPPPP" uuuuuuuuun this text:', getline(1))
+ close!
+endfunc
+
+func Test_replace_keeppatterns()
+ new
+ a
+foobar
+
+substitute foo asdf
+
+one two
+.
+
+ normal gg
+ /^substitute
+ s/foo/bar/
+ call assert_equal('foo', @/)
+ call assert_equal('substitute bar asdf', getline('.'))
+
+ /^substitute
+ keeppatterns s/asdf/xyz/
+ call assert_equal('^substitute', @/)
+ call assert_equal('substitute bar xyz', getline('.'))
+
+ exe "normal /bar /e\<CR>"
+ call assert_equal(15, col('.'))
+ normal -
+ keeppatterns /xyz
+ call assert_equal('bar ', @/)
+ call assert_equal('substitute bar xyz', getline('.'))
+ exe "normal 0dn"
+ call assert_equal('xyz', getline('.'))
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index bc7b7c00d3..3db438cf4b 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -1,5 +1,9 @@
" Tests for the swap feature
+func s:swapname()
+ return trim(execute('swapname'))
+endfunc
+
" Tests for 'directory' option.
func Test_swap_directory()
if !has("unix")
@@ -17,7 +21,7 @@ func Test_swap_directory()
" Verify that the swap file doesn't exist in the current directory
call assert_equal([], glob(".Xtest1*.swp", 1, 1, 1))
edit Xtest1
- let swfname = split(execute("swapname"))[0]
+ let swfname = s:swapname()
call assert_equal([swfname], glob(swfname, 1, 1, 1))
" './dir', swap file in a directory relative to the file
@@ -27,7 +31,7 @@ func Test_swap_directory()
edit Xtest1
call assert_equal([], glob(swfname, 1, 1, 1))
let swfname = "Xtest2/Xtest1.swp"
- call assert_equal(swfname, split(execute("swapname"))[0])
+ call assert_equal(swfname, s:swapname())
call assert_equal([swfname], glob("Xtest2/*", 1, 1, 1))
" 'dir', swap file in directory relative to the current dir
@@ -38,7 +42,7 @@ func Test_swap_directory()
edit Xtest2/Xtest3
call assert_equal(["Xtest2/Xtest3"], glob("Xtest2/*", 1, 1, 1))
let swfname = "Xtest.je/Xtest3.swp"
- call assert_equal(swfname, split(execute("swapname"))[0])
+ call assert_equal(swfname, s:swapname())
call assert_equal([swfname], glob("Xtest.je/*", 1, 1, 1))
set dir&
@@ -47,6 +51,42 @@ func Test_swap_directory()
call delete("Xtest.je", "rf")
endfunc
+func Test_swap_group()
+ if !has("unix")
+ return
+ endif
+ let groups = split(system('groups'))
+ if len(groups) <= 1
+ throw 'Skipped: need at least two groups, got ' . string(groups)
+ endif
+
+ try
+ call delete('Xtest')
+ split Xtest
+ call setline(1, 'just some text')
+ wq
+ if system('ls -l Xtest') !~ ' ' . groups[0] . ' \d'
+ throw 'Skipped: test file does not have the first group'
+ else
+ silent !chmod 640 Xtest
+ call system('chgrp ' . groups[1] . ' Xtest')
+ if system('ls -l Xtest') !~ ' ' . groups[1] . ' \d'
+ throw 'Skipped: cannot set second group on test file'
+ else
+ split Xtest
+ let swapname = substitute(execute('swapname'), '[[:space:]]', '', 'g')
+ call assert_match('Xtest', swapname)
+ " Group of swapfile must now match original file.
+ call assert_match(' ' . groups[1] . ' \d', system('ls -l ' . swapname))
+
+ bwipe!
+ endif
+ endif
+ finally
+ call delete('Xtest')
+ endtry
+endfunc
+
func Test_missing_dir()
call mkdir('Xswapdir')
exe 'set directory=' . getcwd() . '/Xswapdir'
@@ -61,3 +101,120 @@ func Test_missing_dir()
set directory&
call delete('Xswapdir', 'rf')
endfunc
+
+func Test_swapinfo()
+ new Xswapinfo
+ call setline(1, ['one', 'two', 'three'])
+ w
+ let fname = s:swapname()
+ call assert_match('Xswapinfo', fname)
+ let info = swapinfo(fname)
+
+ let ver = printf('VIM %d.%d', v:version / 100, v:version % 100)
+ call assert_equal(ver, info.version)
+
+ call assert_match('\w', info.user)
+ " host name is truncated to 39 bytes in the swap file
+ call assert_equal(hostname()[:38], info.host)
+ call assert_match('Xswapinfo', info.fname)
+ call assert_match(0, info.dirty)
+ call assert_equal(getpid(), info.pid)
+ call assert_match('^\d*$', info.mtime)
+ if has_key(info, 'inode')
+ call assert_match('\d', info.inode)
+ endif
+ bwipe!
+ call delete(fname)
+ call delete('Xswapinfo')
+
+ let info = swapinfo('doesnotexist')
+ call assert_equal('Cannot open file', info.error)
+
+ call writefile(['burp'], 'Xnotaswapfile')
+ let info = swapinfo('Xnotaswapfile')
+ call assert_equal('Cannot read file', info.error)
+ call delete('Xnotaswapfile')
+
+ call writefile([repeat('x', 10000)], 'Xnotaswapfile')
+ let info = swapinfo('Xnotaswapfile')
+ call assert_equal('Not a swap file', info.error)
+ call delete('Xnotaswapfile')
+endfunc
+
+func Test_swapname()
+ edit Xtest1
+ let expected = s:swapname()
+ call assert_equal(expected, swapname('%'))
+
+ new Xtest2
+ let buf = bufnr('%')
+ let expected = s:swapname()
+ wincmd p
+ call assert_equal(expected, swapname(buf))
+
+ new Xtest3
+ setlocal noswapfile
+ call assert_equal('', swapname('%'))
+
+ bwipe!
+ call delete('Xtest1')
+ call delete('Xtest2')
+ call delete('Xtest3')
+endfunc
+
+func Test_swapfile_delete()
+ throw 'skipped: need the "blob" feature for this test'
+ autocmd! SwapExists
+ function s:swap_exists()
+ let v:swapchoice = s:swap_choice
+ let s:swapname = v:swapname
+ let s:filename = expand('<afile>')
+ endfunc
+ augroup test_swapfile_delete
+ autocmd!
+ autocmd SwapExists * call s:swap_exists()
+ augroup END
+
+
+ " Create a valid swapfile by editing a file.
+ split XswapfileText
+ call setline(1, ['one', 'two', 'three'])
+ write " file is written, not modified
+ " read the swapfile as a Blob
+ let swapfile_name = swapname('%')
+ let swapfile_bytes = readfile(swapfile_name, 'B')
+
+ " Close the file and recreate the swap file.
+ " Now editing the file will run into the process still existing
+ quit
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swap_choice = 'e'
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
+
+ " Write the swapfile with a modified PID, now it will be automatically
+ " deleted. Process one should never be Vim.
+ let swapfile_bytes[24:27] = 0z01000000
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal('', s:swapname)
+
+ " Now set the modified flag, the swap file will not be deleted
+ let swapfile_bytes[28 + 80 + 899] = 0x55
+ call writefile(swapfile_bytes, swapfile_name)
+ let s:swapname = ''
+ split XswapfileText
+ quit
+ call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t'))
+
+ call delete('XswapfileText')
+ call delete(swapfile_name)
+ augroup test_swapfile_delete
+ autocmd!
+ augroup END
+ augroup! test_swapfile_delete
+endfunc
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 6978faeb7b..850947f89f 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -503,3 +503,37 @@ func Test_syn_wrong_z_one()
" call test_override("ALL", 0)
bwipe!
endfunc
+
+func Test_syntax_hangs()
+ if !has('reltime') || !has('float') || !has('syntax')
+ return
+ endif
+
+ " This pattern takes a long time to match, it should timeout.
+ new
+ call setline(1, ['aaa', repeat('abc ', 1000), 'ccc'])
+ let start = reltime()
+ set nolazyredraw redrawtime=101
+ syn match Error /\%#=1a*.*X\@<=b*/
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed > 0.1)
+ call assert_true(elapsed < 1.0)
+
+ " second time syntax HL is disabled
+ let start = reltime()
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed < 0.1)
+
+ " after CTRL-L the timeout flag is reset
+ let start = reltime()
+ exe "normal \<C-L>"
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed > 0.1)
+ call assert_true(elapsed < 1.0)
+
+ set redrawtime&
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index add9b3d7cf..8043d13433 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -1,5 +1,6 @@
" Tests for tabpage
+" source screendump.vim
function Test_tabpage()
bw!
@@ -105,6 +106,14 @@ function Test_tabpage()
call assert_equal(4, tabpagenr())
7tabmove 5
call assert_equal(5, tabpagenr())
+ -tabmove
+ call assert_equal(4, tabpagenr())
+ +tabmove
+ call assert_equal(5, tabpagenr())
+ -2tabmove
+ call assert_equal(3, tabpagenr())
+ +3tabmove
+ call assert_equal(6, tabpagenr())
" The following are a no-op
norm! 2gt
@@ -547,4 +556,27 @@ func Test_tabs()
bw!
endfunc
+func Test_tabpage_cmdheight()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: only works with terminal'
+ endif
+ call writefile([
+ \ 'set laststatus=2',
+ \ 'set cmdheight=2',
+ \ 'tabnew',
+ \ 'set cmdheight=3',
+ \ 'tabnext',
+ \ 'redraw!',
+ \ 'echo "hello\nthere"',
+ \ 'tabnext',
+ \ 'redraw',
+ \ ], 'XTest_tabpage_cmdheight')
+ " Check that cursor line is concealed
+ let buf = RunVimInTerminal('-S XTest_tabpage_cmdheight', {'statusoff': 3})
+ call VerifyScreenDump(buf, 'Test_tabpage_cmdheight', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XTest_tabpage_cmdheight')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index f9bd8b5246..21ea00cab7 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -258,4 +258,209 @@ func Test_tagjump_etags()
bwipe!
endfunc
+" Test for getting and modifying the tag stack
+func Test_getsettagstack()
+ call writefile(['line1', 'line2', 'line3'], 'Xfile1')
+ call writefile(['line1', 'line2', 'line3'], 'Xfile2')
+ call writefile(['line1', 'line2', 'line3'], 'Xfile3')
+
+ enew | only
+ call settagstack(1, {'items' : []})
+ call assert_equal(0, gettagstack(1).length)
+ call assert_equal([], gettagstack(1).items)
+ " Error cases
+ call assert_equal({}, gettagstack(100))
+ call assert_equal(-1, settagstack(100, {'items' : []}))
+ call assert_fails('call settagstack(1, [1, 10])', 'E715')
+ call assert_fails("call settagstack(1, {'items' : 10})", 'E714')
+ call assert_fails("call settagstack(1, {'items' : []}, 10)", 'E928')
+ call assert_fails("call settagstack(1, {'items' : []}, 'b')", 'E962')
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile1\t1",
+ \ "three\tXfile3\t3",
+ \ "two\tXfile2\t2"],
+ \ 'Xtags')
+
+ let stk = []
+ call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'one',
+ \ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
+ tag one
+ call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'two',
+ \ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
+ tag two
+ call add(stk, {'bufnr' : bufnr('%'), 'tagname' : 'three',
+ \ 'from' : [bufnr('%'), line('.'), col('.'), 0], 'matchnr' : 1})
+ tag three
+ call assert_equal(3, gettagstack(1).length)
+ call assert_equal(stk, gettagstack(1).items)
+ " Check for default - current window
+ call assert_equal(3, gettagstack().length)
+ call assert_equal(stk, gettagstack().items)
+
+ " Try to set current index to invalid values
+ call settagstack(1, {'curidx' : -1})
+ call assert_equal(1, gettagstack().curidx)
+ call settagstack(1, {'curidx' : 50})
+ call assert_equal(4, gettagstack().curidx)
+
+ " Try pushing invalid items onto the stack
+ call settagstack(1, {'items' : []})
+ call settagstack(1, {'items' : ["plate"]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+ call settagstack(1, {'items' : [{"tagname" : "abc"}]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+ call settagstack(1, {'items' : [{"from" : 100}]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+ call settagstack(1, {'items' : [{"from" : [2, 1, 0, 0]}]}, 'a')
+ call assert_equal(0, gettagstack().length)
+ call assert_equal([], gettagstack().items)
+
+ " Push one item at a time to the stack
+ call settagstack(1, {'items' : []})
+ call settagstack(1, {'items' : [stk[0]]}, 'a')
+ call settagstack(1, {'items' : [stk[1]]}, 'a')
+ call settagstack(1, {'items' : [stk[2]]}, 'a')
+ call settagstack(1, {'curidx' : 4})
+ call assert_equal({'length' : 3, 'curidx' : 4, 'items' : stk},
+ \ gettagstack(1))
+
+ " Try pushing items onto a full stack
+ for i in range(7)
+ call settagstack(1, {'items' : stk}, 'a')
+ endfor
+ call assert_equal(20, gettagstack().length)
+ call settagstack(1,
+ \ {'items' : [{'tagname' : 'abc', 'from' : [1, 10, 1, 0]}]}, 'a')
+ call assert_equal('abc', gettagstack().items[19].tagname)
+
+ " Tag with multiple matches
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "two\tXfile1\t1",
+ \ "two\tXfile2\t3",
+ \ "two\tXfile3\t2"],
+ \ 'Xtags')
+ call settagstack(1, {'items' : []})
+ tag two
+ tnext
+ tnext
+ call assert_equal(1, gettagstack().length)
+ call assert_equal(3, gettagstack().items[0].matchnr)
+
+ call settagstack(1, {'items' : []})
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ call delete('Xtags')
+ set tags&
+endfunc
+
+func Test_tag_with_count()
+ call writefile([
+ \ 'test Xtest.h /^void test();$/;" p typeref:typename:void signature:()',
+ \ ], 'Xtags')
+ call writefile([
+ \ 'main Xtest.c /^int main()$/;" f typeref:typename:int signature:()',
+ \ 'test Xtest.c /^void test()$/;" f typeref:typename:void signature:()',
+ \ ], 'Ytags')
+ cal writefile([
+ \ 'int main()',
+ \ 'void test()',
+ \ ], 'Xtest.c')
+ cal writefile([
+ \ 'void test();',
+ \ ], 'Xtest.h')
+ set tags=Xtags,Ytags
+
+ new Xtest.c
+ let tl = taglist('test', 'Xtest.c')
+ call assert_equal(tl[0].filename, 'Xtest.c')
+ call assert_equal(tl[1].filename, 'Xtest.h')
+
+ tag test
+ call assert_equal(bufname('%'), 'Xtest.c')
+ 1tag test
+ call assert_equal(bufname('%'), 'Xtest.c')
+ 2tag test
+ call assert_equal(bufname('%'), 'Xtest.h')
+
+ set tags&
+ call delete('Xtags')
+ call delete('Ytags')
+ bwipe Xtest.h
+ bwipe Xtest.c
+ call delete('Xtest.h')
+ call delete('Xtest.c')
+endfunc
+
+func Test_tagnr_recall()
+ call writefile([
+ \ 'test Xtest.h /^void test();$/;" p',
+ \ 'main Xtest.c /^int main()$/;" f',
+ \ 'test Xtest.c /^void test()$/;" f',
+ \ ], 'Xtags')
+ cal writefile([
+ \ 'int main()',
+ \ 'void test()',
+ \ ], 'Xtest.c')
+ cal writefile([
+ \ 'void test();',
+ \ ], 'Xtest.h')
+ set tags=Xtags
+
+ new Xtest.c
+ let tl = taglist('test', 'Xtest.c')
+ call assert_equal(tl[0].filename, 'Xtest.c')
+ call assert_equal(tl[1].filename, 'Xtest.h')
+
+ 2tag test
+ call assert_equal(bufname('%'), 'Xtest.h')
+ pop
+ call assert_equal(bufname('%'), 'Xtest.c')
+ tag
+ call assert_equal(bufname('%'), 'Xtest.h')
+
+ set tag&
+ call delete('Xtags')
+ bwipe Xtest.h
+ bwipe Xtest.c
+ call delete('Xtest.h')
+ call delete('Xtest.c')
+endfunc
+
+func Test_tag_line_toolong()
+ call writefile([
+ \ '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 django/contrib/admin/templates/admin/edit_inline/stacked.html 16;" j line:16 language:HTML'
+ \ ], 'Xtags')
+ set tags=Xtags
+ let old_vbs = &verbose
+ set verbose=5
+ " ":tjump" should give "tag not found" not "Format error in tags file"
+ call assert_fails('tj /foo', 'E426')
+ try
+ tj /foo
+ catch /^Vim\%((\a\+)\)\=:E431/
+ call assert_report(v:exception)
+ catch /.*/
+ endtry
+ call assert_equal('Ignoring long line in tags file', split(execute('messages'), '\n')[-1])
+ call writefile([
+ \ '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567 django/contrib/admin/templates/admin/edit_inline/stacked.html 16;" j line:16 language:HTML'
+ \ ], 'Xtags')
+ call assert_fails('tj /foo', 'E426')
+ try
+ tj /foo
+ catch /^Vim\%((\a\+)\)\=:E431/
+ call assert_report(v:exception)
+ catch /.*/
+ endtry
+ call assert_equal('Ignoring long line in tags file', split(execute('messages'), '\n')[-1])
+ call delete('Xtags')
+ let &verbose = old_vbs
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index 3ad2025915..ea0a6b9678 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -1,4 +1,4 @@
-" test 'taglist' function and :tags command
+" test taglist(), tagfiles() functions and :tags command
func Test_taglist()
call writefile([
@@ -61,3 +61,39 @@ func Test_tags_too_long()
call assert_fails('tag ' . repeat('x', 1020), 'E426')
tags
endfunc
+
+" For historical reasons we support a tags file where the last line is missing
+" the newline.
+func Test_tagsfile_without_trailing_newline()
+ call writefile(["Foo\tfoo\t1"], 'Xtags', 'b')
+ set tags=Xtags
+
+ let tl = taglist('.*')
+ call assert_equal(1, len(tl))
+ call assert_equal('Foo', tl[0].name)
+
+ call delete('Xtags')
+endfunc
+
+func Test_tagfiles()
+ call assert_equal([], tagfiles())
+
+ call writefile(["FFoo\tXfoo\t1"], 'Xtags1')
+ call writefile(["FBar\tXbar\t1"], 'Xtags2')
+ set tags=Xtags1,Xtags2
+ call assert_equal(['Xtags1', 'Xtags2'], tagfiles())
+
+ help
+ let tf = tagfiles()
+ call assert_equal(1, len(tf))
+ call assert_equal(fnamemodify(expand('$VIMRUNTIME/doc/tags'), ':p:gs?\\?/?'),
+ \ fnamemodify(tf[0], ':p:gs?\\?/?'))
+ helpclose
+ call assert_equal(['Xtags1', 'Xtags2'], tagfiles())
+ set tags&
+ call assert_equal([], tagfiles())
+
+ call delete('Xtags1')
+ call delete('Xtags2')
+ bd
+endfunc
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 999566c6ac..13fb50b985 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -163,6 +163,329 @@ func Test_text_format()
\ '# 1 xxxxx',
\ '# foobar'], getline(1, 2))
+ " Test the 'p' flag for 'formatoptions'
+ " First test without the flag: that it will break "Mr. Feynman" at the space
+ normal ggdG
+ setl tw=28 fo=tcq
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking, Mr.',
+ \ 'Feynman!'], getline(1, 2))
+ " Now test with the flag: that it will push the name with the title onto the
+ " next line
+ normal ggdG
+ setl fo+=p
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking,',
+ \ 'Mr. Feynman!'], getline(1, 2))
+ " Ensure that it will still break if two spaces are entered
+ normal ggdG
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking, Mr.',
+ \ 'Feynman!'], getline(1, 2))
+
setl ai& tw& fo& si& comments&
enew!
endfunc
+
+" Tests for :right, :center and :left on text with embedded TAB.
+func Test_format_align()
+ enew!
+ set tw=65
+
+ " :left alignment
+ call append(0, [
+ \ " test for :left",
+ \ " a a",
+ \ " fa a",
+ \ " dfa a",
+ \ " sdfa a",
+ \ " asdfa a",
+ \ " xasdfa a",
+ \ "asxxdfa a",
+ \ ])
+ %left
+ call assert_equal([
+ \ "test for :left",
+ \ "a a",
+ \ "fa a",
+ \ "dfa a",
+ \ "sdfa a",
+ \ "asdfa a",
+ \ "xasdfa a",
+ \ "asxxdfa a",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ " :center alignment
+ call append(0, [
+ \ " test for :center",
+ \ " a a",
+ \ " fa afd asdf",
+ \ " dfa a",
+ \ " sdfa afd asdf",
+ \ " asdfa a",
+ \ " xasdfa asdfasdfasdfasdfasdf",
+ \ "asxxdfa a"
+ \ ])
+ %center
+ call assert_equal([
+ \ " test for :center",
+ \ " a a",
+ \ " fa afd asdf",
+ \ " dfa a",
+ \ " sdfa afd asdf",
+ \ " asdfa a",
+ \ " xasdfa asdfasdfasdfasdfasdf",
+ \ " asxxdfa a",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ " :right alignment
+ call append(0, [
+ \ " test for :right",
+ \ " a a",
+ \ " fa a",
+ \ " dfa a",
+ \ " sdfa a",
+ \ " asdfa a",
+ \ " xasdfa a",
+ \ " asxxdfa a",
+ \ " asxa;ofa a",
+ \ " asdfaqwer a",
+ \ " a ax",
+ \ " fa ax",
+ \ " dfa ax",
+ \ " sdfa ax",
+ \ " asdfa ax",
+ \ " xasdfa ax",
+ \ " asxxdfa ax",
+ \ " asxa;ofa ax",
+ \ " asdfaqwer ax",
+ \ " a axx",
+ \ " fa axx",
+ \ " dfa axx",
+ \ " sdfa axx",
+ \ " asdfa axx",
+ \ " xasdfa axx",
+ \ " asxxdfa axx",
+ \ " asxa;ofa axx",
+ \ " asdfaqwer axx",
+ \ " a axxx",
+ \ " fa axxx",
+ \ " dfa axxx",
+ \ " sdfa axxx",
+ \ " asdfa axxx",
+ \ " xasdfa axxx",
+ \ " asxxdfa axxx",
+ \ " asxa;ofa axxx",
+ \ " asdfaqwer axxx",
+ \ " a axxxo",
+ \ " fa axxxo",
+ \ " dfa axxxo",
+ \ " sdfa axxxo",
+ \ " asdfa axxxo",
+ \ " xasdfa axxxo",
+ \ " asxxdfa axxxo",
+ \ " asxa;ofa axxxo",
+ \ " asdfaqwer axxxo",
+ \ " a axxxoi",
+ \ " fa axxxoi",
+ \ " dfa axxxoi",
+ \ " sdfa axxxoi",
+ \ " asdfa axxxoi",
+ \ " xasdfa axxxoi",
+ \ " asxxdfa axxxoi",
+ \ " asxa;ofa axxxoi",
+ \ " asdfaqwer axxxoi",
+ \ " a axxxoik",
+ \ " fa axxxoik",
+ \ " dfa axxxoik",
+ \ " sdfa axxxoik",
+ \ " asdfa axxxoik",
+ \ " xasdfa axxxoik",
+ \ " asxxdfa axxxoik",
+ \ " asxa;ofa axxxoik",
+ \ " asdfaqwer axxxoik",
+ \ " a axxxoike",
+ \ " fa axxxoike",
+ \ " dfa axxxoike",
+ \ " sdfa axxxoike",
+ \ " asdfa axxxoike",
+ \ " xasdfa axxxoike",
+ \ " asxxdfa axxxoike",
+ \ " asxa;ofa axxxoike",
+ \ " asdfaqwer axxxoike",
+ \ " a axxxoikey",
+ \ " fa axxxoikey",
+ \ " dfa axxxoikey",
+ \ " sdfa axxxoikey",
+ \ " asdfa axxxoikey",
+ \ " xasdfa axxxoikey",
+ \ " asxxdfa axxxoikey",
+ \ " asxa;ofa axxxoikey",
+ \ " asdfaqwer axxxoikey",
+ \ ])
+ %right
+ call assert_equal([
+ \ "\t\t\t\t test for :right",
+ \ "\t\t\t\t a a",
+ \ "\t\t\t\t fa a",
+ \ "\t\t\t\t dfa a",
+ \ "\t\t\t\t sdfa a",
+ \ "\t\t\t\t asdfa a",
+ \ "\t\t\t\t xasdfa a",
+ \ "\t\t\t\t asxxdfa a",
+ \ "\t\t\t\t asxa;ofa a",
+ \ "\t\t\t\t asdfaqwer a",
+ \ "\t\t\t\t a ax",
+ \ "\t\t\t\t fa ax",
+ \ "\t\t\t\t dfa ax",
+ \ "\t\t\t\t sdfa ax",
+ \ "\t\t\t\t asdfa ax",
+ \ "\t\t\t\t xasdfa ax",
+ \ "\t\t\t\t asxxdfa ax",
+ \ "\t\t\t\t asxa;ofa ax",
+ \ "\t\t\t\t asdfaqwer ax",
+ \ "\t\t\t\t a axx",
+ \ "\t\t\t\t fa axx",
+ \ "\t\t\t\t dfa axx",
+ \ "\t\t\t\t sdfa axx",
+ \ "\t\t\t\t asdfa axx",
+ \ "\t\t\t\t xasdfa axx",
+ \ "\t\t\t\t asxxdfa axx",
+ \ "\t\t\t\t asxa;ofa axx",
+ \ "\t\t\t\t asdfaqwer axx",
+ \ "\t\t\t\t a axxx",
+ \ "\t\t\t\t fa axxx",
+ \ "\t\t\t\t dfa axxx",
+ \ "\t\t\t\t sdfa axxx",
+ \ "\t\t\t\t asdfa axxx",
+ \ "\t\t\t\t xasdfa axxx",
+ \ "\t\t\t\t asxxdfa axxx",
+ \ "\t\t\t\t asxa;ofa axxx",
+ \ "\t\t\t\t asdfaqwer axxx",
+ \ "\t\t\t\t a axxxo",
+ \ "\t\t\t\t fa axxxo",
+ \ "\t\t\t\t dfa axxxo",
+ \ "\t\t\t\t sdfa axxxo",
+ \ "\t\t\t\t asdfa axxxo",
+ \ "\t\t\t\t xasdfa axxxo",
+ \ "\t\t\t\t asxxdfa axxxo",
+ \ "\t\t\t\t asxa;ofa axxxo",
+ \ "\t\t\t\t asdfaqwer axxxo",
+ \ "\t\t\t\t a axxxoi",
+ \ "\t\t\t\t fa axxxoi",
+ \ "\t\t\t\t dfa axxxoi",
+ \ "\t\t\t\t sdfa axxxoi",
+ \ "\t\t\t\t asdfa axxxoi",
+ \ "\t\t\t\t xasdfa axxxoi",
+ \ "\t\t\t\t asxxdfa axxxoi",
+ \ "\t\t\t\t asxa;ofa axxxoi",
+ \ "\t\t\t\t asdfaqwer axxxoi",
+ \ "\t\t\t\t a axxxoik",
+ \ "\t\t\t\t fa axxxoik",
+ \ "\t\t\t\t dfa axxxoik",
+ \ "\t\t\t\t sdfa axxxoik",
+ \ "\t\t\t\t asdfa axxxoik",
+ \ "\t\t\t\t xasdfa axxxoik",
+ \ "\t\t\t\t asxxdfa axxxoik",
+ \ "\t\t\t\t asxa;ofa axxxoik",
+ \ "\t\t\t\t asdfaqwer axxxoik",
+ \ "\t\t\t\t a axxxoike",
+ \ "\t\t\t\t fa axxxoike",
+ \ "\t\t\t\t dfa axxxoike",
+ \ "\t\t\t\t sdfa axxxoike",
+ \ "\t\t\t\t asdfa axxxoike",
+ \ "\t\t\t\t xasdfa axxxoike",
+ \ "\t\t\t\t asxxdfa axxxoike",
+ \ "\t\t\t\t asxa;ofa axxxoike",
+ \ "\t\t\t\t asdfaqwer axxxoike",
+ \ "\t\t\t\t a axxxoikey",
+ \ "\t\t\t\t fa axxxoikey",
+ \ "\t\t\t\t dfa axxxoikey",
+ \ "\t\t\t\t sdfa axxxoikey",
+ \ "\t\t\t\t asdfa axxxoikey",
+ \ "\t\t\t\t xasdfa axxxoikey",
+ \ "\t\t\t\t asxxdfa axxxoikey",
+ \ "\t\t\t\t asxa;ofa axxxoikey",
+ \ "\t\t\t\t asdfaqwer axxxoikey",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ set tw&
+endfunc
+
+" Test formatting a paragraph.
+func Test_format_para()
+ enew!
+ set fo+=tcroql tw=72
+
+ call append(0, [
+ \ "xxxxx xx xxxxxx ",
+ \ "xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx",
+ \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx",
+ \ "xx xxxxxxx. xxxx xxxx.",
+ \ "",
+ \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx",
+ \ "> xxxxxx xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx"
+ \ ])
+ exe "normal /xxxxxxxx$\<CR>"
+ normal 0gq6kk
+ call assert_equal([
+ \ "xxxxx xx xxxxxx xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx",
+ \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx xx xxxxxxx.",
+ \ "xxxx xxxx.",
+ \ "",
+ \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx xxxxxx",
+ \ "> xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx",
+ \ ""
+ \ ], getline(1, '$'))
+
+ set fo& tw&
+ enew!
+endfunc
+
+" Test undo after ":%s" and formatting.
+func Test_format_undo()
+ enew!
+ map gg :.,.+2s/^/x/<CR>kk:set tw=3<CR>gqq
+
+ call append(0, [
+ \ "aa aa aa aa",
+ \ "bb bb bb bb",
+ \ "cc cc cc cc"
+ \ ])
+ " undo/redo here to make the next undo only work on the following changes
+ exe "normal i\<C-G>u"
+ call cursor(1,1)
+ normal ggu
+ call assert_equal([
+ \ "aa aa aa aa",
+ \ "bb bb bb bb",
+ \ "cc cc cc cc",
+ \ ""
+ \ ], getline(1, '$'))
+
+ unmap gg
+ set tw&
+ enew!
+endfunc
+
+func Test_format_list_auto()
+ new
+ call setline(1, ['1. abc', '2. def', '3. ghi'])
+ set fo=tan ai bs=2
+ call feedkeys("3G0lli\<BS>\<BS>x\<Esc>", 'tx')
+ call assert_equal('2. defx ghi', getline(2))
+ bwipe!
+ set fo& ai& bs&
+endfunc
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index 62ddad5dce..9384989a35 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -15,17 +15,13 @@ func MyHandlerWithLists(lists, timer)
let x = string(a:lists)
endfunc
-func s:assert_inrange(lower, upper, actual)
- return assert_inrange(a:lower, LoadAdjust(a:upper), a:actual)
-endfunc
-
func Test_oneshot()
let g:val = 0
let timer = timer_start(50, 'MyHandler')
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call s:assert_inrange(40, 120, slept)
+ call assert_inrange(40, LoadAdjust(120), slept)
else
call assert_inrange(20, 120, slept)
endif
@@ -37,7 +33,7 @@ func Test_repeat_three()
let slept = WaitFor('g:val == 3')
call assert_equal(3, g:val)
if has('reltime')
- call s:assert_inrange(120, 250, slept)
+ call assert_inrange(120, LoadAdjust(250), slept)
else
call assert_inrange(80, 200, slept)
endif
@@ -47,9 +43,12 @@ func Test_repeat_many()
call timer_stopall()
let g:val = 0
let timer = timer_start(50, 'MyHandler', {'repeat': -1})
+ if has('mac')
+ sleep 200m
+ endif
sleep 200m
call timer_stop(timer)
- call assert_inrange((has('mac') ? 1 : 2), 4, g:val)
+ call assert_inrange((has('mac') ? 1 : 2), LoadAdjust(4), g:val)
endfunc
func Test_with_partial_callback()
@@ -63,7 +62,7 @@ func Test_with_partial_callback()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call s:assert_inrange(40, 130, slept)
+ call assert_inrange(40, LoadAdjust(130), slept)
else
call assert_inrange(20, 100, slept)
endif
@@ -126,7 +125,7 @@ func Test_paused()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call s:assert_inrange(0, 140, slept)
+ call assert_inrange(0, LoadAdjust(140), slept)
else
call assert_inrange(0, 10, slept)
endif
@@ -197,6 +196,23 @@ func Test_input_in_timer()
call assert_equal('hello', g:val)
endfunc
+func FuncWithError(timer)
+ let g:call_count += 1
+ if g:call_count == 4
+ return
+ endif
+ doesnotexist
+endfunc
+
+func Test_timer_errors()
+ let g:call_count = 0
+ let timer = timer_start(10, 'FuncWithError', {'repeat': -1})
+ " Timer will be stopped after failing 3 out of 3 times.
+ call WaitFor('g:call_count == 3')
+ sleep 50m
+ call assert_equal(3, g:call_count)
+endfunc
+
func FuncWithCaughtError(timer)
let g:call_count += 1
try
diff --git a/src/nvim/testdir/test_true_false.vim b/src/nvim/testdir/test_true_false.vim
index 4a5d47471d..ad865bb113 100644
--- a/src/nvim/testdir/test_true_false.vim
+++ b/src/nvim/testdir/test_true_false.vim
@@ -134,6 +134,8 @@ func Test_non_zero_arg()
" call test_settime(93784)
" call Try_arg_non_zero("mode(%v%)", 'x', 'x!')
" call test_settime(0)
+ let shellslash = &shellslash
+ set shellslash
call Try_arg_non_zero("shellescape('foo%', %v%)", "'foo%'", "'foo\\%'")
@@ -152,4 +154,6 @@ func Test_non_zero_arg()
let r = visualmode(v)
call assert_equal('', r, 'result for ' . v . ' is not "" but ' . r)
endfor
+
+ let &shellslash = shellslash
endfunc
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index 9729ca9f57..0cb5dc4033 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -242,6 +242,7 @@ func Test_undojoin()
endfunc
func Test_undo_write()
+ call delete('Xtest')
split Xtest
call feedkeys("ione one one\<Esc>", 'xt')
w!
@@ -388,6 +389,15 @@ funct Test_undofile()
" Test undofile() with 'undodir' set to a non-existing directory.
" call assert_equal('', undofile('Xundofoo'))
+ if isdirectory('/tmp')
+ set undodir=/tmp
+ if has('osx')
+ call assert_equal('/tmp/%private%tmp%file', undofile('///tmp/file'))
+ else
+ call assert_equal('/tmp/%tmp%file', undofile('///tmp/file'))
+ endif
+ endif
+
set undodir&
endfunc
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 74afc72f03..f69273635c 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -411,3 +411,27 @@ func Test_Visual_paragraph_textobject()
bwipe!
endfunc
+
+" Tests for "vaBiB", end could be wrong.
+func Test_Visual_Block()
+ new
+ a
+- Bug in "vPPPP" on this text:
+ {
+ cmd;
+ {
+ cmd;\t/* <-- Start cursor here */
+ {
+ }
+ }
+ }
+.
+ normal gg
+ call search('Start cursor here')
+ normal vaBiBD
+ call assert_equal(['- Bug in "vPPPP" on this text:',
+ \ "\t{",
+ \ "\t}"], getline(1, '$'))
+
+ close!
+endfunc
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index e4618610cd..ee43540fdd 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -158,3 +158,29 @@ func Test_winfixwidth_on_close()
%bwipeout!
setlocal nowinfixwidth splitbelow& splitright&
endfunction
+
+" Test that 'winfixheight' will be respected even there is non-leaf frame
+fun! Test_winfixheight_non_leaf_frame()
+ vsplit
+ botright 11new
+ let l:wid = win_getid()
+ setlocal winfixheight
+ call assert_equal(11, winheight(l:wid))
+ botright new
+ bwipe!
+ call assert_equal(11, winheight(l:wid))
+ %bwipe!
+endf
+
+" Test that 'winfixwidth' will be respected even there is non-leaf frame
+fun! Test_winfixwidth_non_leaf_frame()
+ split
+ topleft 11vnew
+ let l:wid = win_getid()
+ setlocal winfixwidth
+ call assert_equal(11, winwidth(l:wid))
+ topleft new
+ bwipe!
+ call assert_equal(11, winwidth(l:wid))
+ %bwipe!
+endf
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 57fb36abb8..003a23ea7b 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -646,4 +646,54 @@ func Test_relative_cursor_second_line_after_resize()
let &so = so_save
endfunc
+" Tests for the winnr() function
+func Test_winnr()
+ only | tabonly
+ call assert_equal(1, winnr('j'))
+ call assert_equal(1, winnr('k'))
+ call assert_equal(1, winnr('h'))
+ call assert_equal(1, winnr('l'))
+
+ " create a set of horizontally and vertically split windows
+ leftabove new | wincmd p
+ leftabove new | wincmd p
+ rightbelow new | wincmd p
+ rightbelow new | wincmd p
+ leftabove vnew | wincmd p
+ leftabove vnew | wincmd p
+ rightbelow vnew | wincmd p
+ rightbelow vnew | wincmd p
+
+ call assert_equal(8, winnr('j'))
+ call assert_equal(2, winnr('k'))
+ call assert_equal(4, winnr('h'))
+ call assert_equal(6, winnr('l'))
+ call assert_equal(9, winnr('2j'))
+ call assert_equal(1, winnr('2k'))
+ call assert_equal(3, winnr('2h'))
+ call assert_equal(7, winnr('2l'))
+
+ " Error cases
+ call assert_fails("echo winnr('0.2k')", 'E15:')
+ call assert_equal(2, winnr('-2k'))
+ call assert_fails("echo winnr('-2xj')", 'E15:')
+ call assert_fails("echo winnr('j2j')", 'E15:')
+ call assert_fails("echo winnr('ll')", 'E15:')
+ call assert_fails("echo winnr('5')", 'E15:')
+ call assert_equal(4, winnr('0h'))
+
+ tabnew
+ call assert_equal(8, tabpagewinnr(1, 'j'))
+ call assert_equal(2, tabpagewinnr(1, 'k'))
+ call assert_equal(4, tabpagewinnr(1, 'h'))
+ call assert_equal(6, tabpagewinnr(1, 'l'))
+
+ only | tabonly
+endfunc
+
+func Test_window_colon_command()
+ " This was reading invalid memory.
+ exe "norm! v\<C-W>:\<C-U>echo v:version"
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 1cd5fb306a..b4585a72ef 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -36,13 +36,15 @@ func Test_writefile_fails_conversion()
if !has('multi_byte') || !has('iconv')
return
endif
+ " Without a backup file the write won't happen if there is a conversion
+ " error.
set nobackup nowritebackup
new
let contents = ["line one", "line two"]
call writefile(contents, 'Xfile')
edit Xfile
call setline(1, ["first line", "cannot convert \u010b", "third line"])
- call assert_fails('write ++enc=cp932')
+ call assert_fails('write ++enc=cp932', 'E513:')
call assert_equal(contents, readfile('Xfile'))
call delete('Xfile')
@@ -50,6 +52,27 @@ func Test_writefile_fails_conversion()
set backup& writebackup&
endfunc
+func Test_writefile_fails_conversion2()
+ if !has('iconv') || has('sun')
+ return
+ endif
+ " With a backup file the write happens even if there is a conversion error,
+ " but then the backup file must remain
+ set nobackup writebackup
+ let contents = ["line one", "line two"]
+ call writefile(contents, 'Xfile_conversion_err')
+ edit Xfile_conversion_err
+ call setline(1, ["first line", "cannot convert \u010b", "third line"])
+ set fileencoding=latin1
+ let output = execute('write')
+ call assert_match('CONVERSION ERROR', output)
+ call assert_equal(contents, readfile('Xfile_conversion_err~'))
+
+ call delete('Xfile_conversion_err')
+ call delete('Xfile_conversion_err~')
+ bwipe!
+endfunc
+
func SetFlag(timer)
let g:flag = 1
endfunc
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 3eb88366d6..6d9023bd79 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -10,6 +10,7 @@
#include "nvim/charset.h"
#include "nvim/main.h"
#include "nvim/aucmd.h"
+#include "nvim/ex_docmd.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -22,7 +23,7 @@
# include "tui/input.c.generated.h"
#endif
-void term_input_init(TermInput *input, Loop *loop)
+void tinput_init(TermInput *input, Loop *loop)
{
input->loop = loop;
input->paste_enabled = false;
@@ -48,20 +49,12 @@ void term_input_init(TermInput *input, Loop *loop)
int curflags = termkey_get_canonflags(input->tk);
termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
// setup input handle
-#ifdef WIN32
- uv_tty_init(&loop->uv, &input->tty_in, 0, 1);
- uv_tty_set_mode(&input->tty_in, UV_TTY_MODE_RAW);
- rstream_init_stream(&input->read_stream,
- (uv_stream_t *)&input->tty_in,
- 0xfff);
-#else
rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff);
-#endif
// initialize a timer handle for handling ESC with libtermkey
time_watcher_init(loop, &input->timer_handle, input);
}
-void term_input_destroy(TermInput *input)
+void tinput_destroy(TermInput *input)
{
rbuffer_free(input->key_buffer);
uv_mutex_destroy(&input->key_buffer_mutex);
@@ -71,23 +64,23 @@ void term_input_destroy(TermInput *input)
termkey_destroy(input->tk);
}
-void term_input_start(TermInput *input)
+void tinput_start(TermInput *input)
{
- rstream_start(&input->read_stream, read_cb, input);
+ rstream_start(&input->read_stream, tinput_read_cb, input);
}
-void term_input_stop(TermInput *input)
+void tinput_stop(TermInput *input)
{
rstream_stop(&input->read_stream);
time_watcher_stop(&input->timer_handle);
}
-static void input_done_event(void **argv)
+static void tinput_done_event(void **argv)
{
input_done();
}
-static void wait_input_enqueue(void **argv)
+static void tinput_wait_enqueue(void **argv)
{
TermInput *input = argv[0];
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
@@ -106,12 +99,12 @@ static void wait_input_enqueue(void **argv)
uv_mutex_unlock(&input->key_buffer_mutex);
}
-static void flush_input(TermInput *input, bool wait_until_empty)
+static void tinput_flush(TermInput *input, bool wait_until_empty)
{
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
do {
uv_mutex_lock(&input->key_buffer_mutex);
- loop_schedule(&main_loop, event_create(wait_input_enqueue, 1, input));
+ loop_schedule(&main_loop, event_create(tinput_wait_enqueue, 1, input));
input->waiting = true;
while (input->waiting) {
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
@@ -120,13 +113,13 @@ static void flush_input(TermInput *input, bool wait_until_empty)
} while (rbuffer_size(input->key_buffer) > drain_boundary);
}
-static void enqueue_input(TermInput *input, char *buf, size_t size)
+static void tinput_enqueue(TermInput *input, char *buf, size_t size)
{
if (rbuffer_size(input->key_buffer) >
rbuffer_capacity(input->key_buffer) - 0xff) {
// don't ever let the buffer get too full or we risk putting incomplete keys
// into it
- flush_input(input, false);
+ tinput_flush(input, false);
}
rbuffer_write(input->key_buffer, buf, size);
}
@@ -146,7 +139,7 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key)
ptr++;
}
- enqueue_input(input, buf, len);
+ tinput_enqueue(input, buf, len);
}
static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
@@ -164,7 +157,7 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key)
len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
}
- enqueue_input(input, buf, len);
+ tinput_enqueue(input, buf, len);
}
static void forward_mouse_event(TermInput *input, TermKeyKey *key)
@@ -236,7 +229,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
}
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
- enqueue_input(input, buf, len);
+ tinput_enqueue(input, buf, len);
}
static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
@@ -244,7 +237,7 @@ static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key);
}
-static void timer_cb(TimeWatcher *watcher, void *data);
+static void tinput_timer_cb(TimeWatcher *watcher, void *data);
static int get_key_code_timeout(void)
{
@@ -287,16 +280,16 @@ static void tk_getkeys(TermInput *input, bool force)
if (ms > 0) {
// Stop the current timer if already running
time_watcher_stop(&input->timer_handle);
- time_watcher_start(&input->timer_handle, timer_cb, (uint32_t)ms, 0);
+ time_watcher_start(&input->timer_handle, tinput_timer_cb, (uint32_t)ms, 0);
} else {
tk_getkeys(input, true);
}
}
-static void timer_cb(TimeWatcher *watcher, void *data)
+static void tinput_timer_cb(TimeWatcher *watcher, void *data)
{
tk_getkeys(data, true);
- flush_input(data, true);
+ tinput_flush(data, true);
}
/// Handle focus events.
@@ -332,7 +325,7 @@ static bool handle_bracketed_paste(TermInput *input)
if (input->paste_enabled == enable) {
return true;
}
- enqueue_input(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
+ tinput_enqueue(input, PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
input->paste_enabled = enable;
return true;
}
@@ -357,15 +350,17 @@ static bool handle_forced_escape(TermInput *input)
static void set_bg_deferred(void **argv)
{
char *bgvalue = argv[0];
- if (starting) {
- // Wait until after startup, so OptionSet is triggered.
- loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue));
- return;
- }
if (!option_was_set("bg") && !strequal((char *)p_bg, bgvalue)) {
// Value differs, apply it.
- set_option_value("bg", 0L, bgvalue, 0);
- reset_option_was_set("bg");
+ if (starting) {
+ // Wait until after startup, so OptionSet is triggered.
+ do_cmdline_cmd((bgvalue[0] == 'l')
+ ? "autocmd VimEnter * ++once ++nested set bg=light"
+ : "autocmd VimEnter * ++once ++nested set bg=dark");
+ } else {
+ set_option_value("bg", 0L, bgvalue, 0);
+ reset_option_was_set("bg");
+ }
}
}
@@ -424,7 +419,8 @@ static bool handle_background_color(TermInput *input)
double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601
char *bgvalue = luminance < 0.5 ? "dark" : "light";
DLOG("bg response: %s", bgvalue);
- loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue));
+ loop_schedule_deferred(&main_loop,
+ event_create(set_bg_deferred, 1, bgvalue));
} else {
DLOG("failed to parse bg response");
}
@@ -433,8 +429,8 @@ static bool handle_background_color(TermInput *input)
return false;
}
-static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data,
- bool eof)
+static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_,
+ void *data, bool eof)
{
TermInput *input = data;
@@ -452,9 +448,10 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data,
// ls *.md | xargs nvim
input->in_fd = 2;
stream_close(&input->read_stream, NULL, NULL);
- multiqueue_put(input->loop->fast_events, restart_reading, 1, input);
+ multiqueue_put(input->loop->fast_events, tinput_restart_reading, 1,
+ input);
} else {
- loop_schedule(&main_loop, event_create(input_done_event, 0));
+ loop_schedule(&main_loop, event_create(tinput_done_event, 0));
}
return;
}
@@ -494,15 +491,15 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data,
}
}
} while (rbuffer_size(input->read_stream.buffer));
- flush_input(input, true);
+ tinput_flush(input, true);
// Make sure the next input escape sequence fits into the ring buffer
// without wrap around, otherwise it could be misinterpreted.
rbuffer_reset(input->read_stream.buffer);
}
-static void restart_reading(void **argv)
+static void tinput_restart_reading(void **argv)
{
TermInput *input = argv[0];
rstream_init_fd(input->loop, &input->read_stream, input->in_fd, 0xfff);
- rstream_start(&input->read_stream, read_cb, input);
+ rstream_start(&input->read_stream, tinput_read_cb, input);
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 573cc9d683..7d59cf5c6a 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -17,9 +17,6 @@ typedef struct term_input {
#endif
TimeWatcher timer_handle;
Loop *loop;
-#ifdef WIN32
- uv_tty_t tty_in;
-#endif
Stream read_stream;
RBuffer *key_buffer;
uv_mutex_t key_buffer_mutex;
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 3e7a3b1ba1..42e5b9b270 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -342,7 +342,7 @@ static void tui_terminal_start(UI *ui)
terminfo_start(ui);
tui_guess_size(ui);
signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
- term_input_start(&data->input);
+ tinput_start(&data->input);
}
static void tui_terminal_after_startup(UI *ui)
@@ -364,7 +364,7 @@ static void tui_terminal_stop(UI *ui)
ui->data = NULL; // Flag UI as "stopped".
return;
}
- term_input_stop(&data->input);
+ tinput_stop(&data->input);
signal_watcher_stop(&data->winch_handle);
terminfo_stop(ui);
ugrid_free(&data->grid);
@@ -404,7 +404,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
#if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18
data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
#endif
- term_input_init(&data->input, &tui_loop);
+ tinput_init(&data->input, &tui_loop);
tui_terminal_start(ui);
// Allow main thread to continue, we are ready to handle UI callbacks.
@@ -429,7 +429,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
}
ui_bridge_stopped(bridge);
- term_input_destroy(&data->input);
+ tinput_destroy(&data->input);
signal_watcher_stop(&data->cont_handle);
signal_watcher_close(&data->cont_handle, NULL);
signal_watcher_close(&data->winch_handle, NULL);
@@ -704,14 +704,6 @@ static void cursor_goto(UI *ui, int row, int col)
// even less expensive than using BSes or CUB.
unibi_out(ui, unibi_carriage_return);
ugrid_goto(grid, grid->row, 0);
- } else if (col > grid->col) {
- int n = col - grid->col;
- if (n <= (row == grid->row ? 4 : 2)
- && cheap_to_print(ui, grid->row, grid->col, n)) {
- UGRID_FOREACH_CELL(grid, grid->row, grid->col, col, {
- print_cell(ui, cell);
- });
- }
}
if (row == grid->row) {
if (col < grid->col
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index d07ea3179e..7dbb8ec790 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -141,6 +141,17 @@ bool ui_rgb_attached(void)
return false;
}
+/// Returns true if any UI requested `override=true`.
+bool ui_override(void)
+{
+ for (size_t i = 1; i < ui_count; i++) {
+ if (uis[i]->override) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool ui_active(void)
{
return ui_count > 1;
@@ -173,12 +184,13 @@ void ui_refresh(void)
ext_widgets[i] = true;
}
+ bool inclusive = ui_override();
for (size_t i = 0; i < ui_count; i++) {
UI *ui = uis[i];
width = MIN(ui->width, width);
height = MIN(ui->height, height);
for (UIExtension j = 0; (int)j < kUIExtCount; j++) {
- ext_widgets[j] &= ui->ui_ext[j];
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive);
}
}
@@ -431,6 +443,7 @@ Array ui_array(void)
PUT(info, "width", INTEGER_OBJ(ui->width));
PUT(info, "height", INTEGER_OBJ(ui->height));
PUT(info, "rgb", BOOLEAN_OBJ(ui->rgb));
+ PUT(info, "override", BOOLEAN_OBJ(ui->override));
for (UIExtension j = 0; j < kUIExtCount; j++) {
if (ui_ext_names[j][0] != '_' || ui->ui_ext[j]) {
PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j]));
@@ -460,7 +473,9 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
if (wp->w_floating) {
if (width != wp->w_width && height != wp->w_height) {
- win_config_float(wp, (int)width, (int)height, wp->w_float_config);
+ wp->w_float_config.width = width;
+ wp->w_float_config.height = height;
+ win_config_float(wp, wp->w_float_config);
}
} else {
// non-positive indicates no request
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 3f6b3babad..e1dd18a289 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -48,9 +48,11 @@ typedef int LineFlags;
struct ui_t {
bool rgb;
+ bool override; ///< Force highest-requested UI capabilities.
bool composed;
- bool ui_ext[kUIExtCount]; ///< Externalized widgets
- int width, height;
+ bool ui_ext[kUIExtCount]; ///< Externalized UI capabilities.
+ int width;
+ int height;
void *data;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 9ad3f851ad..e24ab11a3a 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -3,6 +3,8 @@
// Compositor: merge floating grids with the main grid for display in
// TUI and non-multigrid UIs.
+//
+// Layer-based compositing: https://en.wikipedia.org/wiki/Digital_compositing
#include <assert.h>
#include <stdbool.h>
@@ -104,6 +106,9 @@ bool ui_comp_should_draw(void)
return composed_uis != 0 && valid_screen;
}
+/// Places `grid` at (col,row) position with (width * height) size.
+/// Adds `grid` as the top layer if it is a new layer.
+///
/// TODO(bfredl): later on the compositor should just use win_float_pos events,
/// though that will require slight event order adjustment: emit the win_pos
/// events in the beginning of update_screen(0), rather than in ui_flush()
@@ -158,6 +163,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
if (insert_at < kv_size(layers)-1) {
for (size_t i = kv_size(layers)-1; i > insert_at; i--) {
kv_A(layers, i) = kv_A(layers, i-1);
+ kv_A(layers, i)->comp_index = i;
}
kv_A(layers, insert_at) = grid;
}
diff --git a/src/nvim/ui_compositor.h b/src/nvim/ui_compositor.h
index b3780db532..70e01c3a21 100644
--- a/src/nvim/ui_compositor.h
+++ b/src/nvim/ui_compositor.h
@@ -1,5 +1,3 @@
-// Bridge for communication between a UI thread and nvim core.
-// Used by the built-in TUI and libnvim-based UIs.
#ifndef NVIM_UI_COMPOSITOR_H
#define NVIM_UI_COMPOSITOR_H
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index df0507ed41..10996d99d5 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -83,13 +83,11 @@
#include <string.h>
#include <fcntl.h>
-#include "nvim/vim.h"
+#include "nvim/buffer.h"
#include "nvim/ascii.h"
#include "nvim/undo.h"
-#include "nvim/macros.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
-#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/buffer_updates.h"
@@ -102,8 +100,6 @@
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/quickfix.h"
-#include "nvim/screen.h"
#include "nvim/sha256.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -1135,8 +1131,9 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
/* If there is no undo information at all, quit here after deleting any
* existing undo file. */
if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL) {
- if (p_verbose > 0)
- verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
+ if (p_verbose > 0) {
+ verb_msg(_("Skipping undo file write, nothing to undo"));
+ }
goto theend;
}
@@ -1822,7 +1819,7 @@ void undo_time(long step, bool sec, bool file, bool absolute)
u_header_T *uhp = NULL;
u_header_T *last;
int mark;
- int nomark;
+ int nomark = 0; // shut up compiler
int round;
bool dosec = sec;
bool dofile = file;
@@ -2154,7 +2151,7 @@ static void u_undoredo(int undo, bool do_buf_event)
int new_flags;
fmark_T namedm[NMARKS];
visualinfo_T visualinfo;
- int empty_buffer; /* buffer became empty */
+ bool empty_buffer; // buffer became empty
u_header_T *curhead = curbuf->b_u_curhead;
/* Don't want autocommands using the undo structures here, they are
@@ -2221,7 +2218,7 @@ static void u_undoredo(int undo, bool do_buf_event)
}
}
- empty_buffer = FALSE;
+ empty_buffer = false;
/* delete the lines between top and bot and save them in newarray */
if (oldsize > 0) {
@@ -2232,9 +2229,10 @@ static void u_undoredo(int undo, bool do_buf_event)
newarray[i] = u_save_line(lnum);
/* remember we deleted the last line in the buffer, and a
* dummy empty line will be inserted */
- if (curbuf->b_ml.ml_line_count == 1)
- empty_buffer = TRUE;
- ml_delete(lnum, FALSE);
+ if (curbuf->b_ml.ml_line_count == 1) {
+ empty_buffer = true;
+ }
+ ml_delete(lnum, false);
}
} else
newarray = NULL;
@@ -2249,7 +2247,7 @@ static void u_undoredo(int undo, bool do_buf_event)
if (empty_buffer && lnum == 0) {
ml_replace((linenr_T)1, uep->ue_array[i], true);
} else {
- ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
+ ml_append(lnum, uep->ue_array[i], (colnr_T)0, false);
}
xfree(uep->ue_array[i]);
}
@@ -2452,7 +2450,9 @@ static void u_undo_end(
}
}
- smsg(_("%" PRId64 " %s; %s #%" PRId64 " %s"),
+ smsg_attr_keep(
+ 0,
+ _("%" PRId64 " %s; %s #%" PRId64 " %s"),
u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
_(msgstr),
did_undo ? _("before") : _("after"),
@@ -2585,9 +2585,13 @@ static void u_add_time(char_u *buf, size_t buflen, time_t tt)
else
/* longer ago */
(void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime);
- } else
- vim_snprintf((char *)buf, buflen, _("%" PRId64 " seconds ago"),
- (int64_t)(time(NULL) - tt));
+ } else {
+ int64_t seconds = time(NULL) - tt;
+ vim_snprintf((char *)buf, buflen,
+ NGETTEXT("%" PRId64 " second ago",
+ "%" PRId64 " seconds ago", (uint32_t)seconds),
+ seconds);
+ }
}
/*
diff --git a/src/nvim/version.c b/src/nvim/version.c
index b7c9140b7f..b18cfc6dde 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -158,7 +158,7 @@ static const int included_patches[] = {
1766,
1765,
1764,
- // 1763,
+ 1763,
// 1762,
// 1761,
1760,
@@ -393,7 +393,7 @@ static const int included_patches[] = {
// 1531,
1530,
// 1529,
- // 1528,
+ 1528,
// 1527,
// 1526,
// 1525,
@@ -474,7 +474,7 @@ static const int included_patches[] = {
1450,
// 1449,
// 1448,
- // 1447,
+ 1447,
1446,
1445,
1444,
@@ -549,7 +549,7 @@ static const int included_patches[] = {
// 1375,
1374,
1373,
- // 1372,
+ 1372,
// 1371,
1370,
1369,
@@ -585,7 +585,7 @@ static const int included_patches[] = {
// 1339,
1338,
1337,
- // 1336,
+ 1336,
// 1335,
// 1334,
1333,
@@ -768,7 +768,7 @@ static const int included_patches[] = {
1156,
1155,
1154,
- // 1153,
+ 1153,
1152,
1151,
1150,
@@ -807,7 +807,7 @@ static const int included_patches[] = {
// 1117,
// 1116,
1115,
- // 1114,
+ 1114,
// 1113,
// 1112,
1111,
@@ -844,11 +844,11 @@ static const int included_patches[] = {
// 1080,
// 1079,
1078,
- // 1077,
+ 1077,
// 1076,
// 1075,
// 1074,
- // 1073,
+ 1073,
1072,
1071,
// 1070,
@@ -876,7 +876,7 @@ static const int included_patches[] = {
1048,
1047,
1046,
- // 1045,
+ 1045,
1044,
1043,
1042,
@@ -1193,7 +1193,7 @@ static const int included_patches[] = {
731,
// 730,
729,
- // 728,
+ 728,
727,
726,
// 725,
@@ -1212,11 +1212,11 @@ static const int included_patches[] = {
// 712,
711,
710,
- // 709,
+ 709,
708,
707,
706,
- // 705,
+ 705,
704,
703,
// 702,
@@ -1240,7 +1240,7 @@ static const int included_patches[] = {
684,
// 683,
682,
- // 681,
+ 681,
680,
679,
678,
@@ -1275,10 +1275,10 @@ static const int included_patches[] = {
649,
648,
// 647,
- // 646,
- // 645,
- // 644,
- // 643,
+ 646,
+ 645,
+ 644,
+ 643,
642,
641,
640,
@@ -1292,7 +1292,7 @@ static const int included_patches[] = {
632,
631,
630,
- // 629,
+ 629,
628,
627,
626,
@@ -1329,7 +1329,7 @@ static const int included_patches[] = {
595,
594,
593,
- // 592,
+ 592,
591,
590,
589,
@@ -1349,7 +1349,7 @@ static const int included_patches[] = {
575,
574,
573,
- // 572,
+ 572,
571,
570,
569,
@@ -1415,9 +1415,9 @@ static const int included_patches[] = {
509,
508,
507,
- // 506,
+ 506,
505,
- // 504,
+ 504,
503,
502,
501,
@@ -1508,9 +1508,9 @@ static const int included_patches[] = {
416,
415,
414,
- // 413,
- // 412,
- // 411,
+ 413,
+ 412,
+ 411,
410,
409,
408,
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 767936ecee..3e0a5907be 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -277,7 +277,6 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// Enums need a typecast to be used as array index (for Ultrix).
#define HL_ATTR(n) highlight_attr[(int)(n)]
-#define TERM_STR(n) term_strings[(int)(n)]
/// Maximum number of bytes in a multi-byte character. It can be one 32-bit
/// character of up to 6 bytes, or one 16-bit character of up to three bytes
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 83ddf534cf..6bc082ffb2 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -502,10 +502,11 @@ wingotofile:
break;
}
FloatConfig config = FLOAT_CONFIG_INIT;
+ config.width = curwin->w_width;
+ config.height = curwin->w_height;
config.external = true;
Error err = ERROR_INIT;
- if (!win_new_float(curwin, curwin->w_width, curwin->w_height, config,
- &err)) {
+ if (!win_new_float(curwin, config, &err)) {
EMSG(err.msg);
api_clear_error(&err);
beep_flush();
@@ -538,12 +539,9 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize,
/// float. It must then already belong to the current tabpage!
///
/// config must already have been validated!
-win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
- Error *err)
+win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
{
- bool new = false;
if (wp == NULL) {
- new = true;
wp = win_alloc(lastwin_nofloating(), false);
win_init(wp, curwin, 0);
} else {
@@ -569,29 +567,30 @@ win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
wp->w_floating = 1;
wp->w_status_height = 0;
wp->w_vsep_width = 0;
- win_config_float(wp, width, height, config);
+
+ // TODO(bfredl): use set_option_to() after merging #9110 ?
+ wp->w_p_nu = false;
+ wp->w_allbuf_opt.wo_nu = false;
+ win_config_float(wp, fconfig);
wp->w_pos_changed = true;
redraw_win_later(wp, VALID);
- if (new) {
- win_enter(wp, false);
- }
return wp;
}
-void win_config_float(win_T *wp, int width, int height,
- FloatConfig config)
+void win_config_float(win_T *wp, FloatConfig fconfig)
{
- wp->w_height = MAX(height, 1);
- wp->w_width = MAX(width, 2);
+ wp->w_width = MAX(fconfig.width, 1);
+ wp->w_height = MAX(fconfig.height, 1);
- if (config.relative == kFloatRelativeCursor) {
- config.relative = kFloatRelativeWindow;
- config.row += curwin->w_wrow;
- config.col += curwin->w_wcol;
- config.window = curwin->handle;
+ if (fconfig.relative == kFloatRelativeCursor) {
+ fconfig.relative = kFloatRelativeWindow;
+ fconfig.row += curwin->w_wrow;
+ fconfig.col += curwin->w_wcol;
+ fconfig.window = curwin->handle;
}
- wp->w_float_config = config;
+ bool change_external = fconfig.external != wp->w_float_config.external;
+ wp->w_float_config = fconfig;
if (!ui_has(kUIMultigrid)) {
wp->w_height = MIN(wp->w_height, Rows-1);
@@ -601,6 +600,10 @@ void win_config_float(win_T *wp, int width, int height,
win_set_inner_size(wp);
must_redraw = MAX(must_redraw, VALID);
wp->w_pos_changed = true;
+ if (change_external) {
+ wp->w_hl_needs_update = true;
+ redraw_win_later(wp, NOT_VALID);
+ }
}
static void ui_ext_win_position(win_T *wp)
@@ -610,28 +613,25 @@ static void ui_ext_win_position(win_T *wp)
wp->w_wincol, wp->w_width, wp->w_height);
return;
}
- const char *const anchor_str[] = {
- "NW",
- "NE",
- "SW",
- "SE"
- };
FloatConfig c = wp->w_float_config;
if (!c.external) {
ScreenGrid *grid = &default_grid;
- int row = c.row, col = c.col;
+ float row = c.row, col = c.col;
if (c.relative == kFloatRelativeWindow) {
Error dummy = ERROR_INIT;
win_T *win = find_window_by_handle(c.window, &dummy);
if (win) {
grid = &win->w_grid;
- screen_adjust_grid(&grid, &row, &col);
+ int row_off = 0, col_off = 0;
+ screen_adjust_grid(&grid, &row_off, &col_off);
+ row += row_off;
+ col += col_off;
}
api_clear_error(&dummy);
}
if (ui_has(kUIMultigrid)) {
- String anchor = cstr_to_string(anchor_str[c.anchor]);
+ String anchor = cstr_to_string(float_anchor_str[c.anchor]);
ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle,
row, col, c.focusable);
} else {
@@ -640,16 +640,16 @@ static void ui_ext_win_position(win_T *wp)
bool east = c.anchor & kFloatAnchorEast;
bool south = c.anchor & kFloatAnchorSouth;
- row -= (south ? wp->w_height : 0);
- col -= (east ? wp->w_width : 0);
- row = MAX(MIN(row, Rows-wp->w_height-1), 0);
- col = MAX(MIN(col, Columns-wp->w_width), 0);
- wp->w_winrow = row;
- wp->w_wincol = col;
+ int comp_row = (int)row - (south ? wp->w_height : 0);
+ int comp_col = (int)col - (east ? wp->w_width : 0);
+ comp_row = MAX(MIN(comp_row, Rows-wp->w_height-1), 0);
+ comp_col = MAX(MIN(comp_col, Columns-wp->w_width), 0);
+ wp->w_winrow = comp_row;
+ wp->w_wincol = comp_col;
bool valid = (wp->w_redr_type == 0);
bool on_top = (curwin == wp) || !curwin->w_floating;
- ui_comp_put_grid(&wp->w_grid, row, col, wp->w_height, wp->w_width,
- valid, on_top);
+ ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height,
+ wp->w_width, valid, on_top);
if (!valid) {
wp->w_grid.valid = false;
redraw_win_later(wp, NOT_VALID);
@@ -668,14 +668,14 @@ static bool parse_float_anchor(String anchor, FloatAnchor *out)
*out = (FloatAnchor)0;
}
char *str = anchor.data;
- if (!STRICMP(str, "NW")) {
- *out = kFloatAnchorNW;
- } else if (!STRICMP(str, "NE")) {
- *out = kFloatAnchorNE;
- } else if (!STRICMP(str, "SW")) {
- *out = kFloatAnchorSW;
- } else if (!STRICMP(str, "SE")) {
- *out = kFloatAnchorSE;
+ if (striequal(str, "NW")) {
+ *out = 0; // NW is the default
+ } else if (striequal(str, "NE")) {
+ *out = kFloatAnchorEast;
+ } else if (striequal(str, "SW")) {
+ *out = kFloatAnchorSouth;
+ } else if (striequal(str, "SE")) {
+ *out = kFloatAnchorSouth | kFloatAnchorEast;
} else {
return false;
}
@@ -684,15 +684,12 @@ static bool parse_float_anchor(String anchor, FloatAnchor *out)
static bool parse_float_relative(String relative, FloatRelative *out)
{
- if (relative.size == 0) {
- *out = (FloatRelative)0;
- }
char *str = relative.data;
- if (!STRICMP(str, "editor")) {
+ if (striequal(str, "editor")) {
*out = kFloatRelativeEditor;
- } else if (!STRICMP(str, "win")) {
+ } else if (striequal(str, "win")) {
*out = kFloatRelativeWindow;
- } else if (!STRICMP(str, "cursor")) {
+ } else if (striequal(str, "cursor")) {
*out = kFloatRelativeCursor;
} else {
return false;
@@ -700,11 +697,14 @@ static bool parse_float_relative(String relative, FloatRelative *out)
return true;
}
-bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
+bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
Error *err)
{
+ // TODO(bfredl): use a get/has_key interface instead and get rid of extra
+ // flags
bool has_row = false, has_col = false, has_relative = false;
bool has_external = false, has_window = false;
+ bool has_width = false, has_height = false;
for (size_t i = 0; i < config.size; i++) {
char *key = config.items[i].key.data;
@@ -712,109 +712,137 @@ bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
if (!strcmp(key, "row")) {
has_row = true;
if (val.type == kObjectTypeInteger) {
- out->row = val.data.integer;
+ fconfig->row = val.data.integer;
} else if (val.type == kObjectTypeFloat) {
- out->row = val.data.floating;
+ fconfig->row = val.data.floating;
} else {
api_set_error(err, kErrorTypeValidation,
- "'row' option has to be Integer or Float");
+ "'row' key must be Integer or Float");
return false;
}
} else if (!strcmp(key, "col")) {
has_col = true;
if (val.type == kObjectTypeInteger) {
- out->col = val.data.integer;
+ fconfig->col = val.data.integer;
} else if (val.type == kObjectTypeFloat) {
- out->col = val.data.floating;
+ fconfig->col = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'col' key must be Integer or Float");
+ return false;
+ }
+ } else if (strequal(key, "width")) {
+ has_width = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->width = val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'width' key must be a positive Integer");
+ return false;
+ }
+ } else if (strequal(key, "height")) {
+ has_height = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->height= val.data.integer;
} else {
api_set_error(err, kErrorTypeValidation,
- "'col' option has to be Integer or Float");
+ "'height' key must be a positive Integer");
return false;
}
} else if (!strcmp(key, "anchor")) {
if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
- "'anchor' option has to be String");
+ "'anchor' key must be String");
return false;
}
- if (!parse_float_anchor(val.data.string, &out->anchor)) {
+ if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'anchor' option");
+ "Invalid value of 'anchor' key");
return false;
}
} else if (!strcmp(key, "relative")) {
- has_relative = true;
if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
- "'relative' option has to be String");
+ "'relative' key must be String");
return false;
}
- if (!parse_float_relative(val.data.string, &out->relative)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'relative' option");
- return false;
+ // ignore empty string, to match nvim_win_get_config
+ if (val.data.string.size > 0) {
+ has_relative = true;
+ if (!parse_float_relative(val.data.string, &fconfig->relative)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'relative' key");
+ return false;
+ }
}
} else if (!strcmp(key, "win")) {
has_window = true;
if (val.type != kObjectTypeInteger
&& val.type != kObjectTypeWindow) {
api_set_error(err, kErrorTypeValidation,
- "'win' option has to be Integer or Window");
+ "'win' key must be Integer or Window");
return false;
}
- out->window = val.data.integer;
+ fconfig->window = val.data.integer;
} else if (!strcmp(key, "external")) {
if (val.type == kObjectTypeInteger) {
- out->external = val.data.integer;
+ fconfig->external = val.data.integer;
} else if (val.type == kObjectTypeBoolean) {
- out->external = val.data.boolean;
+ fconfig->external = val.data.boolean;
} else {
api_set_error(err, kErrorTypeValidation,
- "'external' option has to be Boolean");
+ "'external' key must be Boolean");
return false;
}
- has_external = out->external;
+ has_external = fconfig->external;
} else if (!strcmp(key, "focusable")) {
if (val.type == kObjectTypeInteger) {
- out->focusable = val.data.integer;
+ fconfig->focusable = val.data.integer;
} else if (val.type == kObjectTypeBoolean) {
- out->focusable = val.data.boolean;
+ fconfig->focusable = val.data.boolean;
} else {
api_set_error(err, kErrorTypeValidation,
- "'focusable' option has to be Boolean");
+ "'focusable' key must be Boolean");
return false;
}
} else {
api_set_error(err, kErrorTypeValidation,
- "Invalid options key '%s'", key);
+ "Invalid key '%s'", key);
return false;
}
}
- if (has_window && !(has_relative && out->relative == kFloatRelativeWindow)) {
+ if (has_window && !(has_relative
+ && fconfig->relative == kFloatRelativeWindow)) {
api_set_error(err, kErrorTypeValidation,
- "'win' option is only valid with relative='win'");
+ "'win' key is only valid with relative='win'");
return false;
}
- if ((has_relative && out->relative == kFloatRelativeWindow)
- && (!has_window || out->window == 0)) {
- out->window = curwin->handle;
+ if ((has_relative && fconfig->relative == kFloatRelativeWindow)
+ && (!has_window || fconfig->window == 0)) {
+ fconfig->window = curwin->handle;
}
if (has_relative && has_external) {
api_set_error(err, kErrorTypeValidation,
- "Only one of 'relative' and 'external' should be used");
+ "Only one of 'relative' and 'external' must be used");
return false;
} else if (!reconf && !has_relative && !has_external) {
api_set_error(err, kErrorTypeValidation,
"One of 'relative' and 'external' must be used");
return false;
} else if (has_relative) {
- out->external = false;
+ fconfig->external = false;
}
- if (out->external && !ui_has(kUIMultigrid)) {
+ if (!reconf && !(has_height && has_width)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Must specify 'width' and 'height'");
+ return false;
+ }
+
+ if (fconfig->external && !ui_has(kUIMultigrid)) {
api_set_error(err, kErrorTypeValidation,
"UI doesn't support external windows");
return false;
@@ -1121,6 +1149,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else if (wp->w_floating) {
new_frame(wp);
wp->w_floating = false;
+ // non-floating window doesn't store float config.
+ wp->w_float_config = FLOAT_CONFIG_INIT;
}
/*
@@ -2265,8 +2295,7 @@ int win_close(win_T *win, bool free_buf)
EMSG(_("E813: Cannot close autocmd window"));
return FAIL;
}
- if ((firstwin == aucmd_win || lastwin_nofloating() == aucmd_win)
- && one_window()) {
+ if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) {
EMSG(_("E814: Cannot close window, only autocmd window would remain"));
return FAIL;
}
@@ -2299,10 +2328,10 @@ int win_close(win_T *win, bool free_buf)
if (!win->w_floating) {
wp = frame2win(win_altframe(win, NULL));
} else {
- if (win_valid(prevwin)) {
+ if (win_valid(prevwin) && prevwin != win) {
wp = prevwin;
} else {
- wp = curtab->tp_firstwin;
+ wp = firstwin;
}
}
@@ -2448,7 +2477,7 @@ int win_close(win_T *win, bool free_buf)
}
if (!was_floating) {
- if (p_ea && (*p_ead == 'b' || *p_ead == dir)) {
+ if (!curwin->w_floating && p_ea && (*p_ead == 'b' || *p_ead == dir)) {
// If the frame of the closed window contains the new current window,
// only resize that frame. Otherwise resize all windows.
win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
@@ -2561,15 +2590,12 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
free_tabpage(tp);
}
-/*
- * Free the memory used for a window.
- * Returns a pointer to the window that got the freed up space.
- */
-static win_T *
-win_free_mem (
+// Free the memory used for a window.
+// Returns a pointer to the window that got the freed up space.
+static win_T *win_free_mem(
win_T *win,
- int *dirp, /* set to 'v' or 'h' for direction if 'ea' */
- tabpage_T *tp /* tab page "win" is in, NULL for current */
+ int *dirp, // set to 'v' or 'h' for direction if 'ea'
+ tabpage_T *tp // tab page "win" is in, NULL for current
)
{
frame_T *frp;
@@ -2581,18 +2607,20 @@ win_free_mem (
wp = winframe_remove(win, dirp, tp);
xfree(frp);
} else {
- if (win_valid(prevwin)) {
+ *dirp = 'h'; // Dummy value.
+ if (win_valid(prevwin) && prevwin != win) {
wp = prevwin;
} else {
- wp = curtab->tp_firstwin;
+ wp = firstwin;
}
}
win_free(win, tp);
- /* When deleting the current window of another tab page select a new
- * current window. */
- if (tp != NULL && win == tp->tp_curwin)
+ // When deleting the current window of another tab page select a new
+ // current window.
+ if (tp != NULL && win == tp->tp_curwin) {
tp->tp_curwin = wp;
+ }
return wp;
}
@@ -2665,9 +2693,9 @@ winframe_remove (
frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL) {
if (frp != NULL) {
- if (frp->fr_win != NULL && !frp->fr_win->w_p_wfh) {
+ if (!frame_fixed_height(frp)) {
frp2 = frp;
- wp = frp->fr_win;
+ wp = frame2win(frp2);
break;
}
frp = frp->fr_prev;
@@ -2694,9 +2722,9 @@ winframe_remove (
frp3 = frp_close->fr_next;
while (frp != NULL || frp3 != NULL) {
if (frp != NULL) {
- if (frp->fr_win != NULL && !frp->fr_win->w_p_wfw) {
+ if (!frame_fixed_width(frp)) {
frp2 = frp;
- wp = frp->fr_win;
+ wp = frame2win(frp2);
break;
}
frp = frp->fr_prev;
@@ -3384,16 +3412,18 @@ int win_alloc_first(void)
return OK;
}
-/*
- * Init "aucmd_win". This can only be done after the first
- * window is fully initialized, thus it can't be in win_alloc_first().
- */
+// Init `aucmd_win`. This can only be done after the first window
+// is fully initialized, thus it can't be in win_alloc_first().
void win_alloc_aucmd_win(void)
{
- aucmd_win = win_alloc(NULL, TRUE);
- win_init_some(aucmd_win, curwin);
+ Error err = ERROR_INIT;
+ FloatConfig fconfig = FLOAT_CONFIG_INIT;
+ fconfig.width = Columns;
+ fconfig.height = 5;
+ fconfig.focusable = false;
+ aucmd_win = win_new_float(NULL, fconfig, &err);
+ aucmd_win->w_buffer->b_nwindows--;
RESET_BINDING(aucmd_win);
- new_frame(aucmd_win);
}
/*
@@ -3764,6 +3794,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
* the frames for that. When the Vim window was resized need to update
* frame sizes too. Use the stored value of p_ch, so that it can be
* different for each tab page. */
+ if (p_ch != curtab->tp_ch_used) {
+ clear_cmdline = true;
+ }
p_ch = curtab->tp_ch_used;
if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
))
@@ -3806,7 +3839,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
for (win_T *wp = firstwin; wp; wp = wp->w_next) {
if (wp->w_floating && !wp->w_float_config.external) {
- win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config);
+ win_config_float(wp, wp->w_float_config);
}
wp->w_pos_changed = true;
}
@@ -3818,7 +3851,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
*/
void goto_tabpage(int n)
{
- tabpage_T *tp;
+ tabpage_T *tp = NULL; // shut up compiler
tabpage_T *ttp;
int i;
@@ -4006,24 +4039,25 @@ tabpage_T *win_find_tabpage(win_T *win)
return NULL;
}
-/*
- * Move to window above or below "count" times.
- */
-static void
-win_goto_ver (
- int up, /* TRUE to go to win above */
- long count
-)
+/// Get the above or below neighbor window of the specified window.
+///
+/// Returns the specified window if the neighbor is not found.
+/// Returns the previous window if the specifiecied window is a floating window.
+///
+/// @param up true for the above neighbor
+/// @param count nth neighbor window
+///
+/// @return found window
+win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count)
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
- foundfr = curwin->w_frame;
+ foundfr = wp->w_frame;
- if (curwin->w_floating) {
- win_goto(prevwin);
- return;
+ if (wp->w_floating) {
+ return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin;
}
while (count--) {
@@ -4033,14 +4067,17 @@ win_goto_ver (
*/
fr = foundfr;
for (;; ) {
- if (fr == topframe)
+ if (fr == tp->tp_topframe) {
goto end;
- if (up)
+ }
+ if (up) {
nfr = fr->fr_prev;
- else
+ } else {
nfr = fr->fr_next;
- if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
+ }
+ if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL) {
break;
+ }
fr = fr->fr_parent;
}
@@ -4054,11 +4091,12 @@ win_goto_ver (
}
fr = nfr->fr_child;
if (nfr->fr_layout == FR_ROW) {
- /* Find the frame at the cursor row. */
+ // Find the frame at the cursor row.
while (fr->fr_next != NULL
&& frame2win(fr)->w_wincol + fr->fr_width
- <= curwin->w_wincol + curwin->w_wcol)
+ <= wp->w_wincol + wp->w_wcol) {
fr = fr->fr_next;
+ }
}
if (nfr->fr_layout == FR_COL && up)
while (fr->fr_next != NULL)
@@ -4067,28 +4105,40 @@ win_goto_ver (
}
}
end:
- if (foundfr != NULL)
- win_goto(foundfr->fr_win);
+ return foundfr != NULL ? foundfr->fr_win : NULL;
}
-/*
- * Move to left or right window.
- */
-static void
-win_goto_hor (
- int left, /* TRUE to go to left win */
- long count
-)
+/// Move to window above or below "count" times.
+///
+/// @param up true to go to win above
+/// @param count go count times into direction
+static void win_goto_ver(bool up, long count)
+{
+ win_T *win = win_vert_neighbor(curtab, curwin, up, count);
+ if (win != NULL) {
+ win_goto(win);
+ }
+}
+
+/// Get the left or right neighbor window of the specified window.
+///
+/// Returns the specified window if the neighbor is not found.
+/// Returns the previous window if the specifiecied window is a floating window.
+///
+/// @param left true for the left neighbor
+/// @param count nth neighbor window
+///
+/// @return found window
+win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count)
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
- foundfr = curwin->w_frame;
+ foundfr = wp->w_frame;
- if (curwin->w_floating) {
- win_goto(prevwin);
- return;
+ if (wp->w_floating) {
+ return win_valid(prevwin) && !prevwin->w_floating ? prevwin : firstwin;
}
while (count--) {
@@ -4098,14 +4148,17 @@ win_goto_hor (
*/
fr = foundfr;
for (;; ) {
- if (fr == topframe)
+ if (fr == tp->tp_topframe) {
goto end;
- if (left)
+ }
+ if (left) {
nfr = fr->fr_prev;
- else
+ } else {
nfr = fr->fr_next;
- if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
+ }
+ if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL) {
break;
+ }
fr = fr->fr_parent;
}
@@ -4122,7 +4175,7 @@ win_goto_hor (
/* Find the frame at the cursor row. */
while (fr->fr_next != NULL
&& frame2win(fr)->w_winrow + fr->fr_height
- <= curwin->w_winrow + curwin->w_wrow)
+ <= wp->w_winrow + wp->w_wrow)
fr = fr->fr_next;
}
if (nfr->fr_layout == FR_ROW && left)
@@ -4132,8 +4185,19 @@ win_goto_hor (
}
}
end:
- if (foundfr != NULL)
- win_goto(foundfr->fr_win);
+ return foundfr != NULL ? foundfr->fr_win : NULL;
+}
+
+/// Move to left or right window.
+///
+/// @param left true to go to left window
+/// @param count go count times into direction
+static void win_goto_hor(bool left, long count)
+{
+ win_T *win = win_horz_neighbor(curtab, curwin, left, count);
+ if (win != NULL) {
+ win_goto(win);
+ }
}
/*
@@ -4242,9 +4306,12 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid,
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
}
if (trigger_enter_autocmds) {
- apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
- if (other_buffer)
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
+ apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
+ if (other_buffer) {
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
+ }
+ apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
+ curwin->w_last_cursormoved = curwin->w_cursor;
}
maketitle();
@@ -4374,6 +4441,7 @@ static win_T *win_alloc(win_T *after, int hidden)
new_wp->w_cursor.lnum = 1;
new_wp->w_scbind_pos = 1;
new_wp->w_floating = 0;
+ new_wp->w_float_config = FLOAT_CONFIG_INIT;
/* We won't calculate w_fraction until resizing the window */
new_wp->w_fraction = 0;
@@ -4642,8 +4710,12 @@ void win_size_restore(garray_T *gap)
{
int i = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]);
- win_setheight_win(((int *)gap->ga_data)[i++], wp);
+ int width = ((int *)gap->ga_data)[i++];
+ int height = ((int *)gap->ga_data)[i++];
+ if (!wp->w_floating) {
+ frame_setwidth(wp->w_frame, width);
+ win_setheight_win(height, wp);
+ }
}
}
/* recompute the window positions */
@@ -4666,7 +4738,7 @@ int win_comp_pos(void)
// Too often, but when we support anchoring floats to split windows,
// this will be needed
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
- win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config);
+ win_config_float(wp, wp->w_float_config);
}
return row;
@@ -4739,7 +4811,8 @@ void win_setheight_win(int height, win_T *win)
if (win->w_floating) {
if (win->w_float_config.external) {
- win_config_float(win, win->w_width, height, win->w_float_config);
+ win->w_float_config.height = height;
+ win_config_float(win, win->w_float_config);
} else {
beep_flush();
return;
@@ -4944,7 +5017,8 @@ void win_setwidth_win(int width, win_T *wp)
}
if (wp->w_floating) {
if (wp->w_float_config.external) {
- win_config_float(wp, width, wp->w_height, wp->w_float_config);
+ wp->w_float_config.width = width;
+ win_config_float(wp, wp->w_float_config);
} else {
beep_flush();
return;
@@ -5770,7 +5844,7 @@ last_status (
{
/* Don't make a difference between horizontal or vertical split. */
last_status_rec(topframe, (p_ls == 2
- || (p_ls == 1 && (morewin || !ONE_WINDOW))));
+ || (p_ls == 1 && (morewin || !one_window()))));
}
static void last_status_rec(frame_T *fr, int statusline)
@@ -5889,16 +5963,40 @@ void check_lnums(int do_curwin)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) {
+ // save the original cursor position and topline
+ wp->w_save_cursor.w_cursor_save = wp->w_cursor;
+ wp->w_save_cursor.w_topline_save = wp->w_topline;
+
if (wp->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
wp->w_cursor.lnum = curbuf->b_ml.ml_line_count;
}
if (wp->w_topline > curbuf->b_ml.ml_line_count) {
wp->w_topline = curbuf->b_ml.ml_line_count;
}
+
+ // save the corrected cursor position and topline
+ wp->w_save_cursor.w_cursor_corr = wp->w_cursor;
+ wp->w_save_cursor.w_topline_corr = wp->w_topline;
}
}
}
+/// Reset cursor and topline to its stored values from check_lnums().
+/// check_lnums() must have been called first!
+void reset_lnums(void)
+{
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == curbuf) {
+ // Restore the value if the autocommand didn't change it.
+ if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor)) {
+ wp->w_cursor = wp->w_save_cursor.w_cursor_save;
+ }
+ if (wp->w_save_cursor.w_topline_corr == wp->w_topline) {
+ wp->w_topline = wp->w_save_cursor.w_topline_save;
+ }
+ }
+ }
+}
/*
* A snapshot of the window sizes, to restore them after closing the help
@@ -6044,7 +6142,7 @@ static win_T *get_snapshot_focus(int idx)
}
}
- return sn->fr_win;
+ return win_valid(sn->fr_win) ? sn->fr_win : NULL;
}
/*
diff --git a/test/README.md b/test/README.md
index d3f421e8fc..c87f835f79 100644
--- a/test/README.md
+++ b/test/README.md
@@ -88,10 +88,25 @@ To run a *single* legacy test set `TEST_FILE`, for example:
Debugging tests
---------------
-You can set `$GDB` to [run tests under gdbserver](https://github.com/neovim/neovim/pull/1527).
-And if `$VALGRIND` is set it will pass `--vgdb=yes` to valgrind instead of
-starting gdbserver directly.
-
+- You can set `$GDB` to [run tests under gdbserver](https://github.com/neovim/neovim/pull/1527).
+ And if `$VALGRIND` is set it will pass `--vgdb=yes` to valgrind instead of
+ starting gdbserver directly.
+- Hanging tests often happen due to unexpected `:h press-enter` prompts. The
+ default screen width is 50 columns. Commands that try to print lines longer
+ than 50 columns in the command-line, e.g. `:edit very...long...path`, will
+ trigger the prompt. In this case, a shorter path or `:silent edit` should be
+ used.
+- If you can't figure out what is going on, try to visualize the screen. Put
+ this at the beginning of your test:
+
+ ```lua
+ local Screen = require('test.functional.ui.screen')
+ local screen = Screen.new()
+ screen:attach()
+ ```
+
+ Afterwards, put `screen:snapshot_util()` at any position in your test. See the
+ comment at the top of `test/functional/ui/screen.lua` for more.
Filtering Tests
---------------
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 93599c04f1..9d6cfb99ab 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -24,8 +24,8 @@ describe('api/buf', function()
end
- describe('line_count, insert and del_line', function()
- it('works', function()
+ describe('nvim_buf_set_lines, nvim_buf_line_count', function()
+ it('deprecated forms', function()
eq(1, curbuf_depr('line_count'))
curbuf_depr('insert', -1, {'line'})
eq(2, curbuf_depr('line_count'))
@@ -39,6 +39,41 @@ describe('api/buf', function()
eq(1, curbuf_depr('line_count'))
end)
+ it('cursor position is maintained after lines are inserted #9961', function()
+ -- replace the buffer contents with these three lines.
+ request('nvim_buf_set_lines', 0, 0, -1, 1, {"line1", "line2", "line3", "line4"})
+ -- Set the current cursor to {3, 2}.
+ curwin('set_cursor', {3, 2})
+
+ -- add 2 lines and delete 1 line above the current cursor position.
+ request('nvim_buf_set_lines', 0, 1, 2, 1, {"line5", "line6"})
+ -- check the current set of lines in the buffer.
+ eq({"line1", "line5", "line6", "line3", "line4"}, buffer('get_lines', 0, 0, -1, 1))
+ -- cursor should be moved below by 1 line.
+ eq({4, 2}, curwin('get_cursor'))
+
+ -- add a line after the current cursor position.
+ request('nvim_buf_set_lines', 0, 5, 5, 1, {"line7"})
+ -- check the current set of lines in the buffer.
+ eq({"line1", "line5", "line6", "line3", "line4", "line7"}, buffer('get_lines', 0, 0, -1, 1))
+ -- cursor position is unchanged.
+ eq({4, 2}, curwin('get_cursor'))
+
+ -- overwrite current cursor line.
+ request('nvim_buf_set_lines', 0, 3, 5, 1, {"line8", "line9"})
+ -- check the current set of lines in the buffer.
+ eq({"line1", "line5", "line6", "line8", "line9", "line7"}, buffer('get_lines', 0, 0, -1, 1))
+ -- cursor position is unchanged.
+ eq({4, 2}, curwin('get_cursor'))
+
+ -- delete current cursor line.
+ request('nvim_buf_set_lines', 0, 3, 5, 1, {})
+ -- check the current set of lines in the buffer.
+ eq({"line1", "line5", "line6", "line7"}, buffer('get_lines', 0, 0, -1, 1))
+ -- cursor position is unchanged.
+ eq({4, 2}, curwin('get_cursor'))
+ end)
+
it('line_count has defined behaviour for unloaded buffers', function()
-- we'll need to know our bufnr for when it gets unloaded
local bufnr = curbuf('get_number')
@@ -70,7 +105,7 @@ describe('api/buf', function()
end)
end)
- describe('{get,set,del}_line', function()
+ describe('deprecated: {get,set,del}_line', function()
it('works', function()
eq('', curbuf_depr('get_line', 0))
curbuf_depr('set_line', 0, 'line1')
@@ -102,7 +137,7 @@ describe('api/buf', function()
end)
end)
- describe('{get,set}_line_slice', function()
+ describe('deprecated: {get,set}_line_slice', function()
it('get_line_slice: out-of-bounds returns empty array', function()
curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'})
eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, 2, true, true)) --sanity
@@ -149,7 +184,7 @@ describe('api/buf', function()
end)
end)
- describe('{get,set}_lines', function()
+ describe('nvim_buf_get_lines, nvim_buf_set_lines', function()
local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines
local line_count = curbufmeths.line_count
@@ -272,7 +307,7 @@ describe('api/buf', function()
eq({}, get_lines(-3, -4, true))
end)
- it('set_line_slice: out-of-bounds can extend past end', function()
+ it('set_lines: out-of-bounds can extend past end', function()
set_lines(0, -1, true, {'a', 'b', 'c'})
eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity
@@ -286,7 +321,7 @@ describe('api/buf', function()
eq({'e', 'a', 'b', 'c', 'd'}, get_lines(0, -1, true))
end)
- it("set_line on alternate buffer does not access invalid line (E315)", function()
+ it("set_lines on alternate buffer does not access invalid line (E315)", function()
feed_command('set hidden')
insert('Initial file')
command('enew')
@@ -334,9 +369,27 @@ describe('api/buf', function()
{2:-- INSERT --} |
]])
end)
+
+ it('set_lines on hidden buffer preserves "previous window" #9741', function()
+ insert([[
+ visible buffer line 1
+ line 2
+ ]])
+ local hiddenbuf = meths.create_buf(false,true)
+ command('vsplit')
+ command('vsplit')
+ feed('<c-w>l<c-w>l<c-w>l')
+ eq(3, funcs.winnr())
+ feed('<c-w>h')
+ eq(2, funcs.winnr())
+ meths.buf_set_lines(hiddenbuf, 0, -1, true,
+ {'hidden buffer line 1', 'line 2'})
+ feed('<c-w>p')
+ eq(3, funcs.winnr())
+ end)
end)
- describe('get_offset', function()
+ describe('nvim_buf_get_offset', function()
local get_offset = curbufmeths.get_offset
it('works', function()
curbufmeths.set_lines(0,-1,true,{'Some\r','exa\000mple', '', 'buf\rfer', 'text'})
@@ -373,7 +426,7 @@ describe('api/buf', function()
end)
end)
- describe('{get,set,del}_var', function()
+ describe('nvim_buf_get_var, nvim_buf_set_var, nvim_buf_del_var', function()
it('works', function()
curbuf('set_var', 'lua', {1, 2, {['3'] = 1}})
eq({1, 2, {['3'] = 1}}, curbuf('get_var', 'lua'))
@@ -393,7 +446,7 @@ describe('api/buf', function()
end)
end)
- describe('get_changedtick', function()
+ describe('nvim_buf_get_changedtick', function()
it('works', function()
eq(2, curbufmeths.get_changedtick())
curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'})
@@ -417,7 +470,7 @@ describe('api/buf', function()
end)
end)
- describe('{get,set}_option', function()
+ describe('nvim_buf_get_option, nvim_buf_set_option', function()
it('works', function()
eq(8, curbuf('get_option', 'shiftwidth'))
curbuf('set_option', 'shiftwidth', 4)
@@ -430,7 +483,7 @@ describe('api/buf', function()
end)
end)
- describe('{get,set}_name', function()
+ describe('nvim_buf_get_name, nvim_buf_set_name', function()
it('works', function()
nvim('command', 'new')
eq('', curbuf('get_name'))
@@ -438,14 +491,12 @@ describe('api/buf', function()
curbuf('set_name', new_name)
eq(new_name, curbuf('get_name'))
nvim('command', 'w!')
- local f = io.open(new_name)
- ok(f ~= nil)
- f:close()
+ eq(1, funcs.filereadable(new_name))
os.remove(new_name)
end)
end)
- describe('is_loaded', function()
+ describe('nvim_buf_is_loaded', function()
it('works', function()
-- record our buffer number for when we unload it
local bufnr = curbuf('get_number')
@@ -470,7 +521,7 @@ describe('api/buf', function()
end)
end)
- describe('is_valid', function()
+ describe('nvim_buf_is_valid', function()
it('works', function()
nvim('command', 'new')
local b = nvim('get_current_buf')
@@ -480,12 +531,12 @@ describe('api/buf', function()
end)
end)
- describe('get_mark', function()
+ describe('nvim_buf_get_mark', function()
it('works', function()
curbuf('set_lines', -1, -1, true, {'a', 'bit of', 'text'})
curwin('set_cursor', {3, 4})
- nvim('command', 'mark V')
- eq({3, 0}, curbuf('get_mark', 'V'))
+ nvim('command', 'mark v')
+ eq({3, 0}, curbuf('get_mark', 'v'))
end)
end)
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index f52372bee3..b3f6bb7895 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -1,15 +1,18 @@
local helpers = require('test.functional.helpers')(after_each)
-local global_helpers = require('test.helpers')
+local bufmeths = helpers.bufmeths
local clear = helpers.clear
local command = helpers.command
local curbufmeths = helpers.curbufmeths
-local eq = helpers.eq
+local eq, neq = helpers.eq, helpers.neq
+local expect_err = helpers.expect_err
+local feed = helpers.feed
local funcs = helpers.funcs
local meths = helpers.meths
local source = helpers.source
-local shallowcopy = global_helpers.shallowcopy
+local shallowcopy = helpers.shallowcopy
+local sleep = helpers.sleep
describe('nvim_get_keymap', function()
before_each(clear)
@@ -308,3 +311,502 @@ describe('nvim_get_keymap', function()
eq({space_table}, meths.get_keymap('n'))
end)
end)
+
+describe('nvim_set_keymap, nvim_del_keymap', function()
+ before_each(clear)
+
+ -- `generate_expected` is truthy: for generating an expected output for
+ -- maparg(), which does not accept "!" (though it returns "!" in its output
+ -- if getting a mapping set with |:map!|).
+ local function normalize_mapmode(mode, generate_expected)
+ if not generate_expected and mode == '!' then
+ -- Cannot retrieve mapmode-ic mappings with "!", but can with "i" or "c".
+ mode = 'i'
+ elseif mode == '' then
+ mode = generate_expected and ' ' or mode
+ end
+ return mode
+ end
+
+ -- Generate a mapargs dict, for comparison against the mapping that was
+ -- actually set
+ local function generate_mapargs(mode, lhs, rhs, opts)
+ if not opts then
+ opts = {}
+ end
+
+ local to_return = {}
+ to_return.mode = normalize_mapmode(mode, true)
+ to_return.noremap = not opts.noremap and 0 or 1
+ to_return.lhs = lhs
+ to_return.rhs = rhs
+ to_return.silent = not opts.silent and 0 or 1
+ to_return.nowait = not opts.nowait and 0 or 1
+ to_return.expr = not opts.expr and 0 or 1
+ to_return.sid = not opts.sid and 0 or opts.sid
+ to_return.buffer = not opts.buffer and 0 or opts.buffer
+
+ -- mode 't' doesn't print when calling maparg
+ if mode == 't' then
+ to_return.mode = ''
+ end
+
+ return to_return
+ end
+
+ -- Gets a maparg() dict from Nvim, if one exists.
+ local function get_mapargs(mode, lhs)
+ return funcs.maparg(lhs, normalize_mapmode(mode), false, true)
+ end
+
+ it('error on empty LHS', function()
+ -- escape parentheses in lua string, else comparison fails erroneously
+ expect_err('Invalid %(empty%) LHS',
+ meths.set_keymap, '', '', 'rhs', {})
+ expect_err('Invalid %(empty%) LHS',
+ meths.set_keymap, '', '', '', {})
+
+ expect_err('Invalid %(empty%) LHS', meths.del_keymap, '', '')
+ end)
+
+ it('error if LHS longer than MAXMAPLEN', function()
+ -- assume MAXMAPLEN of 50 chars, as declared in vim.h
+ local MAXMAPLEN = 50
+ local lhs = ''
+ for i=1,MAXMAPLEN do
+ lhs = lhs..(i % 10)
+ end
+
+ -- exactly 50 chars should be fine
+ meths.set_keymap('', lhs, 'rhs', {})
+
+ -- del_keymap should unmap successfully
+ meths.del_keymap('', lhs)
+ eq({}, get_mapargs('', lhs))
+
+ -- 51 chars should produce an error
+ lhs = lhs..'1'
+ expect_err('LHS exceeds maximum map length: '..lhs,
+ meths.set_keymap, '', lhs, 'rhs', {})
+ expect_err('LHS exceeds maximum map length: '..lhs,
+ meths.del_keymap, '', lhs)
+ end)
+
+ it('does not throw errors when rhs is longer than MAXMAPLEN', function()
+ local MAXMAPLEN = 50
+ local rhs = ''
+ for i=1,MAXMAPLEN do
+ rhs = rhs..(i % 10)
+ end
+ rhs = rhs..'1'
+ meths.set_keymap('', 'lhs', rhs, {})
+ eq(generate_mapargs('', 'lhs', rhs),
+ get_mapargs('', 'lhs'))
+ end)
+
+ it('throws errors when given too-long mode shortnames', function()
+ expect_err('Shortname is too long: map',
+ meths.set_keymap, 'map', 'lhs', 'rhs', {})
+
+ expect_err('Shortname is too long: vmap',
+ meths.set_keymap, 'vmap', 'lhs', 'rhs', {})
+
+ expect_err('Shortname is too long: xnoremap',
+ meths.set_keymap, 'xnoremap', 'lhs', 'rhs', {})
+
+ expect_err('Shortname is too long: map', meths.del_keymap, 'map', 'lhs')
+ expect_err('Shortname is too long: vmap', meths.del_keymap, 'vmap', 'lhs')
+ expect_err('Shortname is too long: xnoremap', meths.del_keymap, 'xnoremap', 'lhs')
+ end)
+
+ it('error on invalid mode shortname', function()
+ expect_err('Invalid mode shortname: " "',
+ meths.set_keymap, ' ', 'lhs', 'rhs', {})
+ expect_err('Invalid mode shortname: "m"',
+ meths.set_keymap, 'm', 'lhs', 'rhs', {})
+ expect_err('Invalid mode shortname: "?"',
+ meths.set_keymap, '?', 'lhs', 'rhs', {})
+ expect_err('Invalid mode shortname: "y"',
+ meths.set_keymap, 'y', 'lhs', 'rhs', {})
+ expect_err('Invalid mode shortname: "p"',
+ meths.set_keymap, 'p', 'lhs', 'rhs', {})
+ expect_err('Invalid mode shortname: "?"', meths.del_keymap, '?', 'lhs')
+ expect_err('Invalid mode shortname: "y"', meths.del_keymap, 'y', 'lhs')
+ expect_err('Invalid mode shortname: "p"', meths.del_keymap, 'p', 'lhs')
+ end)
+
+ it('error on invalid optnames', function()
+ expect_err('Invalid key: silentt',
+ meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true})
+ expect_err('Invalid key: sidd',
+ meths.set_keymap, 'n', 'lhs', 'rhs', {sidd = false})
+ expect_err('Invalid key: nowaiT',
+ meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false})
+ end)
+
+ it('error on <buffer> option key', function()
+ expect_err('Invalid key: buffer',
+ meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true})
+ end)
+
+ local optnames = {'nowait', 'silent', 'script', 'expr', 'unique'}
+ for _, opt in ipairs(optnames) do
+ -- note: need '%' to escape hyphens, which have special meaning in lua
+ it('throws an error when given non-boolean value for '..opt, function()
+ local opts = {}
+ opts[opt] = 2
+ expect_err('Gave non%-boolean value for an opt: '..opt,
+ meths.set_keymap, 'n', 'lhs', 'rhs', opts)
+ end)
+ end
+
+ -- Perform tests of basic functionality
+ it('sets ordinary mappings', function()
+ meths.set_keymap('n', 'lhs', 'rhs', {})
+ eq(generate_mapargs('n', 'lhs', 'rhs'), get_mapargs('n', 'lhs'))
+
+ meths.set_keymap('v', 'lhs', 'rhs', {})
+ eq(generate_mapargs('v', 'lhs', 'rhs'), get_mapargs('v', 'lhs'))
+ end)
+
+ it('does not throw when LHS or RHS have leading/trailing whitespace', function()
+ meths.set_keymap('n', ' lhs', 'rhs', {})
+ eq(generate_mapargs('n', '<Space><Space><Space>lhs', 'rhs'),
+ get_mapargs('n', ' lhs'))
+
+ meths.set_keymap('n', 'lhs ', 'rhs', {})
+ eq(generate_mapargs('n', 'lhs<Space><Space><Space><Space>', 'rhs'),
+ get_mapargs('n', 'lhs '))
+
+ meths.set_keymap('v', ' lhs ', '\trhs\t\f', {})
+ eq(generate_mapargs('v', '<Space>lhs<Space><Space>', '\trhs\t\f'),
+ get_mapargs('v', ' lhs '))
+ end)
+
+ it('can set noremap mappings', function()
+ meths.set_keymap('x', 'lhs', 'rhs', {noremap = true})
+ eq(generate_mapargs('x', 'lhs', 'rhs', {noremap = true}),
+ get_mapargs('x', 'lhs'))
+
+ meths.set_keymap('t', 'lhs', 'rhs', {noremap = true})
+ eq(generate_mapargs('t', 'lhs', 'rhs', {noremap = true}),
+ get_mapargs('t', 'lhs'))
+ end)
+
+ it('can unmap mappings', function()
+ meths.set_keymap('v', 'lhs', 'rhs', {})
+ meths.del_keymap('v', 'lhs')
+ eq({}, get_mapargs('v', 'lhs'))
+
+ meths.set_keymap('t', 'lhs', 'rhs', {noremap = true})
+ meths.del_keymap('t', 'lhs')
+ eq({}, get_mapargs('t', 'lhs'))
+ end)
+
+ -- Test some edge cases
+ it('"!" and empty string are synonyms for mapmode-nvo', function()
+ local nvo_shortnames = {'', '!'}
+ for _, name in ipairs(nvo_shortnames) do
+ meths.set_keymap(name, 'lhs', 'rhs', {})
+ meths.del_keymap(name, 'lhs')
+ eq({}, get_mapargs(name, 'lhs'))
+ end
+ end)
+
+ local special_chars = {'<C-U>', '<S-Left>', '<F12><F2><Tab>', '<Space><Tab>'}
+ for _, lhs in ipairs(special_chars) do
+ for _, rhs in ipairs(special_chars) do
+ local mapmode = '!'
+ it('can set mappings with special characters, lhs: '..lhs..', rhs: '..rhs,
+ function()
+ meths.set_keymap(mapmode, lhs, rhs, {})
+ eq(generate_mapargs(mapmode, lhs, rhs), get_mapargs(mapmode, lhs))
+ end)
+ end
+ end
+
+ it('can set mappings containing literal keycodes', function()
+ meths.set_keymap('n', '\n\r\n', 'rhs', {})
+ local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs')
+ eq(expected, get_mapargs('n', '<C-j><CR><C-j>'))
+ end)
+
+ it('can set mappings whose RHS is a <Nop>', function()
+ meths.set_keymap('i', 'lhs', '<Nop>', {})
+ command('normal ilhs')
+ eq({''}, curbufmeths.get_lines(0, -1, 0)) -- imap to <Nop> does nothing
+ eq(generate_mapargs('i', 'lhs', '<Nop>', {}),
+ get_mapargs('i', 'lhs'))
+
+ -- also test for case insensitivity
+ meths.set_keymap('i', 'lhs', '<nOp>', {})
+ command('normal ilhs')
+ eq({''}, curbufmeths.get_lines(0, -1, 0))
+ -- note: RHS in returned mapargs() dict reflects the original RHS
+ -- provided by the user
+ eq(generate_mapargs('i', 'lhs', '<nOp>', {}),
+ get_mapargs('i', 'lhs'))
+
+ meths.set_keymap('i', 'lhs', '<NOP>', {})
+ command('normal ilhs')
+ eq({''}, curbufmeths.get_lines(0, -1, 0))
+ eq(generate_mapargs('i', 'lhs', '<NOP>', {}),
+ get_mapargs('i', 'lhs'))
+ end)
+
+ it('treats an empty RHS in a mapping like a <Nop>', function()
+ meths.set_keymap('i', 'lhs', '', {})
+ command('normal ilhs')
+ eq({''}, curbufmeths.get_lines(0, -1, 0))
+ eq(generate_mapargs('i', 'lhs', '', {}),
+ get_mapargs('i', 'lhs'))
+ end)
+
+ it('can set and unset <M-">', function()
+ -- Taken from the legacy test: test_mapping.vim. Exposes a bug in which
+ -- replace_termcodes changes the length of the mapping's LHS, but
+ -- do_map continues to use the *old* length of LHS.
+ meths.set_keymap('i', '<M-">', 'foo', {})
+ meths.del_keymap('i', '<M-">')
+ eq({}, get_mapargs('i', '<M-">'))
+ end)
+
+ it('interprets control sequences in expr-quotes correctly when called '
+ ..'inside vim', function()
+ command([[call nvim_set_keymap('i', "\<space>", "\<tab>", {})]])
+ eq(generate_mapargs('i', '<Space>', '\t', {}),
+ get_mapargs('i', '<Space>'))
+ feed('i ')
+ eq({'\t'}, curbufmeths.get_lines(0, -1, 0))
+ end)
+
+ it('throws appropriate error messages when setting <unique> maps', function()
+ meths.set_keymap('l', 'lhs', 'rhs', {})
+ expect_err('E227: mapping already exists for lhs',
+ meths.set_keymap, 'l', 'lhs', 'rhs', {unique = true})
+ -- different mapmode, no error should be thrown
+ meths.set_keymap('t', 'lhs', 'rhs', {unique = true})
+ end)
+
+ it('can set <expr> mappings whose RHS change dynamically', function()
+ meths.command_output([[
+ function! FlipFlop() abort
+ if !exists('g:flip') | let g:flip = 0 | endif
+ let g:flip = !g:flip
+ return g:flip
+ endfunction
+ ]])
+ eq(1, meths.call_function('FlipFlop', {}))
+ eq(0, meths.call_function('FlipFlop', {}))
+ eq(1, meths.call_function('FlipFlop', {}))
+ eq(0, meths.call_function('FlipFlop', {}))
+
+ meths.set_keymap('i', 'lhs', 'FlipFlop()', {expr = true})
+ command('normal ilhs')
+ eq({'1'}, curbufmeths.get_lines(0, -1, 0))
+
+ command('normal! ggVGd')
+
+ command('normal ilhs')
+ eq({'0'}, curbufmeths.get_lines(0, -1, 0))
+ end)
+
+ it('can set mappings that do trigger other mappings', function()
+ meths.set_keymap('i', 'mhs', 'rhs', {})
+ meths.set_keymap('i', 'lhs', 'mhs', {})
+
+ command('normal imhs')
+ eq({'rhs'}, curbufmeths.get_lines(0, -1, 0))
+
+ command('normal! ggVGd')
+
+ command('normal ilhs')
+ eq({'rhs'}, curbufmeths.get_lines(0, -1, 0))
+ end)
+
+ it("can set noremap mappings that don't trigger other mappings", function()
+ meths.set_keymap('i', 'mhs', 'rhs', {})
+ meths.set_keymap('i', 'lhs', 'mhs', {noremap = true})
+
+ command('normal imhs')
+ eq({'rhs'}, curbufmeths.get_lines(0, -1, 0))
+
+ command('normal! ggVGd')
+
+ command('normal ilhs') -- shouldn't trigger mhs-to-rhs mapping
+ eq({'mhs'}, curbufmeths.get_lines(0, -1, 0))
+ end)
+
+ it("can set nowait mappings that fire without waiting", function()
+ meths.set_keymap('i', '123456', 'longer', {})
+ meths.set_keymap('i', '123', 'shorter', {nowait = true})
+
+ -- feed keys one at a time; if all keys arrive atomically, the longer
+ -- mapping will trigger
+ local keys = 'i123456'
+ for c in string.gmatch(keys, '.') do
+ feed(c)
+ sleep(5)
+ end
+ eq({'shorter456'}, curbufmeths.get_lines(0, -1, 0))
+ end)
+
+ -- Perform exhaustive tests of basic functionality
+ local mapmodes = {'n', 'v', 'x', 's', 'o', '!', 'i', 'l', 'c', 't', ''}
+ for _, mapmode in ipairs(mapmodes) do
+ it('can set/unset normal mappings in mapmode '..mapmode, function()
+ meths.set_keymap(mapmode, 'lhs', 'rhs', {})
+ eq(generate_mapargs(mapmode, 'lhs', 'rhs'),
+ get_mapargs(mapmode, 'lhs'))
+
+ -- some mapmodes (like 'o') will prevent other mapmodes (like '!') from
+ -- taking effect, so unmap after each mapping
+ meths.del_keymap(mapmode, 'lhs')
+ eq({}, get_mapargs(mapmode, 'lhs'))
+ end)
+ end
+
+ for _, mapmode in ipairs(mapmodes) do
+ it('can set/unset noremap mappings using mapmode '..mapmode, function()
+ meths.set_keymap(mapmode, 'lhs', 'rhs', {noremap = true})
+ eq(generate_mapargs(mapmode, 'lhs', 'rhs', {noremap = true}),
+ get_mapargs(mapmode, 'lhs'))
+
+ meths.del_keymap(mapmode, 'lhs')
+ eq({}, get_mapargs(mapmode, 'lhs'))
+ end)
+ end
+
+ -- Test map-arguments, using optnames from above
+ -- remove some map arguments that are harder to test, or were already tested
+ optnames = {'nowait', 'silent', 'expr', 'noremap'}
+ for _, mapmode in ipairs(mapmodes) do
+ local printable_mode = normalize_mapmode(mapmode)
+
+ -- Test with single mappings
+ for _, maparg in ipairs(optnames) do
+ it('can set/unset '..printable_mode..'-mappings with maparg: '..maparg,
+ function()
+ meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = true})
+ eq(generate_mapargs(mapmode, 'lhs', 'rhs', {[maparg] = true}),
+ get_mapargs(mapmode, 'lhs'))
+ meths.del_keymap(mapmode, 'lhs')
+ eq({}, get_mapargs(mapmode, 'lhs'))
+ end)
+ it ('can set/unset '..printable_mode..'-mode mappings with maparg '..
+ maparg..', whose value is false', function()
+ meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = false})
+ eq(generate_mapargs(mapmode, 'lhs', 'rhs'),
+ get_mapargs(mapmode, 'lhs'))
+ meths.del_keymap(mapmode, 'lhs')
+ eq({}, get_mapargs(mapmode, 'lhs'))
+ end)
+ end
+
+ -- Test with triplets of mappings, one of which is false
+ for i = 1, (#optnames - 2) do
+ local opt1, opt2, opt3 = optnames[i], optnames[i + 1], optnames[i + 2]
+ it('can set/unset '..printable_mode..'-mode mappings with mapargs '..
+ opt1..', '..opt2..', '..opt3, function()
+ local opts = {[opt1] = true, [opt2] = false, [opt3] = true}
+ meths.set_keymap(mapmode, 'lhs', 'rhs', opts)
+ eq(generate_mapargs(mapmode, 'lhs', 'rhs', opts),
+ get_mapargs(mapmode, 'lhs'))
+ meths.del_keymap(mapmode, 'lhs')
+ eq({}, get_mapargs(mapmode, 'lhs'))
+ end)
+ end
+ end
+end)
+
+describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
+ before_each(clear)
+
+ -- nvim_set_keymap is implemented as a wrapped call to nvim_buf_set_keymap,
+ -- so its tests also effectively test nvim_buf_set_keymap
+
+ -- here, we mainly test for buffer specificity and other special cases
+
+ -- switch to the given buffer, abandoning any changes in the current buffer
+ local function switch_to_buf(bufnr)
+ command(bufnr..'buffer!')
+ end
+
+ -- `set hidden`, then create two buffers and return their bufnr's
+ -- If start_from_first is truthy, the first buffer will be open when
+ -- the function returns; if falsy, the second buffer will be open.
+ local function make_two_buffers(start_from_first)
+ command('set hidden')
+
+ local first_buf = meths.call_function('bufnr', {'%'})
+ command('new')
+ local second_buf = meths.call_function('bufnr', {'%'})
+ neq(second_buf, first_buf) -- sanity check
+
+ if start_from_first then
+ switch_to_buf(first_buf)
+ end
+
+ return first_buf, second_buf
+ end
+
+ it('rejects negative bufnr values', function()
+ expect_err('Wrong type for argument 1, expecting Buffer',
+ bufmeths.set_keymap, -1, '', 'lhs', 'rhs', {})
+ end)
+
+ it('can set mappings active in the current buffer but not others', function()
+ local first, second = make_two_buffers(true)
+
+ bufmeths.set_keymap(0, '', 'lhs', 'irhs<Esc>', {})
+ command('normal lhs')
+ eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
+
+ -- mapping should have no effect in new buffer
+ switch_to_buf(second)
+ command('normal lhs')
+ eq({''}, bufmeths.get_lines(0, 0, 1, 1))
+
+ -- mapping should remain active in old buffer
+ switch_to_buf(first)
+ command('normal ^lhs')
+ eq({'rhsrhs'}, bufmeths.get_lines(0, 0, 1, 1))
+ end)
+
+ it('can set local mappings in buffer other than current', function()
+ local first = make_two_buffers(false)
+ bufmeths.set_keymap(first, '', 'lhs', 'irhs<Esc>', {})
+
+ -- shouldn't do anything
+ command('normal lhs')
+ eq({''}, bufmeths.get_lines(0, 0, 1, 1))
+
+ -- should take effect
+ switch_to_buf(first)
+ command('normal lhs')
+ eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
+ end)
+
+ it('can disable mappings made in another buffer, inside that buffer', function()
+ local first = make_two_buffers(false)
+ bufmeths.set_keymap(first, '', 'lhs', 'irhs<Esc>', {})
+ bufmeths.del_keymap(first, '', 'lhs')
+ switch_to_buf(first)
+
+ -- shouldn't do anything
+ command('normal lhs')
+ eq({''}, bufmeths.get_lines(0, 0, 1, 1))
+ end)
+
+ it("can't disable mappings given wrong buffer handle", function()
+ local first, second = make_two_buffers(false)
+ bufmeths.set_keymap(first, '', 'lhs', 'irhs<Esc>', {})
+ expect_err('E31: No such mapping',
+ bufmeths.del_keymap, second, '', 'lhs')
+
+ -- should still work
+ switch_to_buf(first)
+ command('normal lhs')
+ eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
+ end)
+end)
diff --git a/test/functional/api/rpc_fixture.lua b/test/functional/api/rpc_fixture.lua
index e885a525af..87f5a91115 100644
--- a/test/functional/api/rpc_fixture.lua
+++ b/test/functional/api/rpc_fixture.lua
@@ -1,7 +1,5 @@
-local deps_prefix = './.deps/usr'
-if os.getenv('DEPS_PREFIX') then
- deps_prefix = os.getenv('DEPS_PREFIX')
-end
+local deps_prefix = (os.getenv('DEPS_PREFIX') and os.getenv('DEPS_PREFIX')
+ or './.deps/usr')
package.path = deps_prefix .. '/share/lua/5.1/?.lua;' ..
deps_prefix .. '/share/lua/5.1/?/init.lua;' ..
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 4d25ba0819..7d9a8269d9 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -1,7 +1,6 @@
-- Test server -> client RPC scenarios. Note: unlike `rpcnotify`, to evaluate
-- `rpcrequest` calls we need the client event loop to be running.
local helpers = require('test.functional.helpers')(after_each)
-local Paths = require('test.config.paths')
local clear, nvim, eval = helpers.clear, helpers.nvim, helpers.eval
local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop
@@ -243,8 +242,8 @@ describe('server -> client', function()
\ 'rpc': v:true
\ }
]])
- local lua_prog = Paths.test_lua_prg
- meths.set_var("args", {lua_prog, 'test/functional/api/rpc_fixture.lua'})
+ meths.set_var("args", {helpers.test_lua_prg,
+ 'test/functional/api/rpc_fixture.lua'})
jobid = eval("jobstart(g:args, g:job_opts)")
neq(0, 'jobid')
end)
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 75b9fb71c9..a7d8dc59ec 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1,6 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local global_helpers = require('test.helpers')
local NIL = helpers.NIL
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
@@ -16,10 +15,10 @@ local request = helpers.request
local source = helpers.source
local next_msg = helpers.next_msg
-local expect_err = global_helpers.expect_err
-local format_string = global_helpers.format_string
-local intchar2lua = global_helpers.intchar2lua
-local mergedicts_copy = global_helpers.mergedicts_copy
+local expect_err = helpers.expect_err
+local format_string = helpers.format_string
+local intchar2lua = helpers.intchar2lua
+local mergedicts_copy = helpers.mergedicts_copy
describe('API', function()
before_each(clear)
@@ -49,13 +48,30 @@ describe('API', function()
it('handles errors in async requests', function()
local error_types = meths.get_api_info()[2].error_types
- nvim_async("bogus")
+ nvim_async('bogus')
eq({'notification', 'nvim_error_event',
{error_types.Exception.id, 'Invalid method: nvim_bogus'}}, next_msg())
-- error didn't close channel.
eq(2, eval('1+1'))
end)
+ it('failed async request emits nvim_error_event', function()
+ local error_types = meths.get_api_info()[2].error_types
+ nvim_async('command', 'bogus')
+ eq({'notification', 'nvim_error_event',
+ {error_types.Exception.id, 'Vim:E492: Not an editor command: bogus'}},
+ next_msg())
+ -- error didn't close channel.
+ eq(2, eval('1+1'))
+ end)
+
+ it('does not set CA_COMMAND_BUSY #7254', function()
+ nvim('command', 'split')
+ nvim('command', 'autocmd WinEnter * startinsert')
+ nvim('command', 'wincmd w')
+ eq({mode='i', blocking=false}, nvim("get_mode"))
+ end)
+
describe('nvim_command', function()
it('works', function()
local fname = helpers.tmpname()
@@ -83,7 +99,7 @@ describe('API', function()
end)
it('VimL execution error: fails with specific error', function()
- local status, rv = pcall(nvim, "command_output", "buffer 23487")
+ local status, rv = pcall(nvim, "command", "buffer 23487")
eq(false, status) -- nvim_command() failed.
eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*"))
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
@@ -1267,7 +1283,7 @@ describe('API', function()
end)
it('returns attached UIs', function()
local screen = Screen.new(20, 4)
- screen:attach()
+ screen:attach({override=true})
local expected = {
{
chan = 1,
@@ -1282,6 +1298,7 @@ describe('API', function()
ext_messages = false,
height = 4,
rgb = true,
+ override = true,
width = 20,
}
}
@@ -1291,6 +1308,7 @@ describe('API', function()
screen = Screen.new(44, 99)
screen:attach({ rgb = false })
expected[1].rgb = false
+ expected[1].override = false
expected[1].width = 44
expected[1].height = 99
eq(expected, nvim("list_uis"))
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index 42e5c46fa5..337c5442ef 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local dedent = helpers.dedent
local eq = helpers.eq
@@ -6,11 +7,13 @@ local eval = helpers.eval
local feed = helpers.feed
local clear = helpers.clear
local meths = helpers.meths
+local meth_pcall = helpers.meth_pcall
local funcs = helpers.funcs
local expect = helpers.expect
local command = helpers.command
local exc_exec = helpers.exc_exec
local curbufmeths = helpers.curbufmeths
+local source = helpers.source
describe('autocmd', function()
before_each(clear)
@@ -59,9 +62,9 @@ describe('autocmd', function()
end)
end)
- it('once', function() -- :help autocmd-once
+ it('++once', function() -- :help autocmd-once
--
- -- ":autocmd ... once" executes its handler once, then removes the handler.
+ -- ":autocmd ... ++once" executes its handler once, then removes the handler.
--
local expected = {
'Many1',
@@ -76,10 +79,10 @@ describe('autocmd', function()
}
command('let g:foo = []')
command('autocmd TabNew * :call add(g:foo, "Many1")')
- command('autocmd TabNew * once :call add(g:foo, "Once1")')
- command('autocmd TabNew * once :call add(g:foo, "Once2")')
+ command('autocmd TabNew * ++once :call add(g:foo, "Once1")')
+ command('autocmd TabNew * ++once :call add(g:foo, "Once2")')
command('autocmd TabNew * :call add(g:foo, "Many2")')
- command('autocmd TabNew * once :call add(g:foo, "Once3")')
+ command('autocmd TabNew * ++once :call add(g:foo, "Once3")')
eq(dedent([[
--- Autocommands ---
@@ -103,27 +106,158 @@ describe('autocmd', function()
funcs.execute('autocmd Tabnew'))
--
- -- ":autocmd ... once" handlers can be deleted.
+ -- ":autocmd ... ++once" handlers can be deleted.
--
expected = {}
command('let g:foo = []')
- command('autocmd TabNew * once :call add(g:foo, "Once1")')
+ command('autocmd TabNew * ++once :call add(g:foo, "Once1")')
command('autocmd! TabNew')
command('tabnew')
eq(expected, eval('g:foo'))
--
- -- ":autocmd ... <buffer> once nested"
+ -- ":autocmd ... <buffer> ++once ++nested"
--
expected = {
'OptionSet-Once',
'CursorMoved-Once',
}
command('let g:foo = []')
- command('autocmd OptionSet binary once nested :call add(g:foo, "OptionSet-Once")')
- command('autocmd CursorMoved <buffer> once nested setlocal binary|:call add(g:foo, "CursorMoved-Once")')
+ command('autocmd OptionSet binary ++nested ++once :call add(g:foo, "OptionSet-Once")')
+ command('autocmd CursorMoved <buffer> ++once ++nested setlocal binary|:call add(g:foo, "CursorMoved-Once")')
command("put ='foo bar baz'")
feed('0llhlh')
eq(expected, eval('g:foo'))
+
+ --
+ -- :autocmd should not show empty section after ++once handlers expire.
+ --
+ expected = {
+ 'Once1',
+ 'Once2',
+ }
+ command('let g:foo = []')
+ command('autocmd! TabNew') -- Clear all TabNew handlers.
+ command('autocmd TabNew * ++once :call add(g:foo, "Once1")')
+ command('autocmd TabNew * ++once :call add(g:foo, "Once2")')
+ command('tabnew')
+ eq(expected, eval('g:foo'))
+ eq(dedent([[
+
+ --- Autocommands ---]]),
+ funcs.execute('autocmd Tabnew'))
+ end)
+
+ it('window works', function()
+ -- Nvim uses a special window to execute certain actions for an invisible buffer,
+ -- internally called autcmd_win and mentioned in the docs at :help E813
+ -- Do some safety checks for redrawing and api accesses to this window.
+
+ local screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {background = Screen.colors.LightMagenta},
+ [3] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1},
+ })
+
+ source([[
+ function! Doit()
+ let g:winid = nvim_get_current_win()
+ redraw!
+ echo getchar()
+ " API functions work when aucmd_win is in scope
+ let g:had_value = has_key(w:, "testvar")
+ call nvim_win_set_var(g:winid, "testvar", 7)
+ let g:test = w:testvar
+ endfunction
+ set hidden
+ " add dummy text to not discard the buffer
+ call setline(1,"bb")
+ autocmd User <buffer> call Doit()
+ ]])
+ screen:expect([[
+ ^bb |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed(":enew | doautoall User<cr>")
+ screen:expect([[
+ {2:bb }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ^:enew | doautoall User |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 13 |
+ ]])
+ eq(7, eval('g:test'))
+
+ -- API calls are blocked when aucmd_win is not in scope
+ eq({false, 'Vim(call):Invalid window id'},
+ meth_pcall(command, "call nvim_set_current_win(g:winid)"))
+
+ -- second time aucmd_win is needed, a different code path is invoked
+ -- to reuse the same window, so check again
+ command("let g:test = v:null")
+ command("let g:had_value = v:null")
+ feed(":doautoall User<cr>")
+ screen:expect([[
+ {2:bb }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ^:doautoall User |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 13 |
+ ]])
+ -- win vars in aucmd_win should have been reset
+ eq(0, eval('g:had_value'))
+ eq(7, eval('g:test'))
+
+ eq({false, 'Vim(call):Invalid window id'},
+ meth_pcall(command, "call nvim_set_current_win(g:winid)"))
end)
end)
diff --git a/test/functional/autocmd/cursormoved_spec.lua b/test/functional/autocmd/cursormoved_spec.lua
new file mode 100644
index 0000000000..d0f46e689b
--- /dev/null
+++ b/test/functional/autocmd/cursormoved_spec.lua
@@ -0,0 +1,34 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local eq = helpers.eq
+local eval = helpers.eval
+local funcs = helpers.funcs
+local source = helpers.source
+
+describe('CursorMoved', function()
+ before_each(clear)
+
+ it('is triggered by changing windows', function()
+ source([[
+ let g:cursormoved = 0
+ vsplit
+ autocmd CursorMoved * let g:cursormoved += 1
+ wincmd w
+ wincmd w
+ ]])
+ eq(2, eval('g:cursormoved'))
+ end)
+
+ it("is not triggered by functions that don't change the window", function()
+ source([[
+ let g:cursormoved = 0
+ let g:buf = bufnr('%')
+ vsplit foo
+ autocmd CursorMoved * let g:cursormoved += 1
+ call nvim_buf_set_lines(g:buf, 0, -1, v:true, ['aaa'])
+ ]])
+ eq({'aaa'}, funcs.nvim_buf_get_lines(eval('g:buf'), 0, -1, true))
+ eq(0, eval('g:cursormoved'))
+ end)
+end)
diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua
index 486a3346b1..5849679dd2 100644
--- a/test/functional/autocmd/textyankpost_spec.lua
+++ b/test/functional/autocmd/textyankpost_spec.lua
@@ -23,6 +23,7 @@ describe('TextYankPost', function()
it('is executed after yank and handles register types', function()
feed('yy')
eq({
+ inclusive = false,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
@@ -35,6 +36,7 @@ describe('TextYankPost', function()
feed('+yw')
eq({
+ inclusive = false,
operator = 'y',
regcontents = { 'baz ' },
regname = '',
@@ -44,6 +46,7 @@ describe('TextYankPost', function()
feed('<c-v>eky')
eq({
+ inclusive = true,
operator = 'y',
regcontents = { 'foo', 'baz' },
regname = '',
@@ -55,6 +58,7 @@ describe('TextYankPost', function()
it('makes v:event immutable', function()
feed('yy')
eq({
+ inclusive = false,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
@@ -84,6 +88,7 @@ describe('TextYankPost', function()
command('autocmd TextYankPost * normal "+yy')
feed('yy')
eq({
+ inclusive = false,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
@@ -96,6 +101,7 @@ describe('TextYankPost', function()
it('is executed after delete and change', function()
feed('dw')
eq({
+ inclusive = false,
operator = 'd',
regcontents = { 'foo' },
regname = '',
@@ -105,6 +111,7 @@ describe('TextYankPost', function()
feed('dd')
eq({
+ inclusive = false,
operator = 'd',
regcontents = { '\nbar' },
regname = '',
@@ -114,6 +121,7 @@ describe('TextYankPost', function()
feed('cwspam<esc>')
eq({
+ inclusive = true,
operator = 'c',
regcontents = { 'baz' },
regname = '',
@@ -141,6 +149,7 @@ describe('TextYankPost', function()
it('gives the correct register name', function()
feed('$"byiw')
eq({
+ inclusive = true,
operator = 'y',
regcontents = { 'bar' },
regname = 'b',
@@ -149,6 +158,7 @@ describe('TextYankPost', function()
feed('"*yy')
eq({
+ inclusive = true,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '*',
@@ -160,6 +170,7 @@ describe('TextYankPost', function()
-- regname still shows the name the user requested
feed('yy')
eq({
+ inclusive = true,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '',
@@ -168,6 +179,7 @@ describe('TextYankPost', function()
feed('"*yy')
eq({
+ inclusive = true,
operator = 'y',
regcontents = { 'foo\nbar' },
regname = '*',
@@ -178,6 +190,7 @@ describe('TextYankPost', function()
it('works with Ex commands', function()
command('1delete +')
eq({
+ inclusive = false,
operator = 'd',
regcontents = { 'foo\nbar' },
regname = '+',
@@ -187,6 +200,7 @@ describe('TextYankPost', function()
command('yank')
eq({
+ inclusive = false,
operator = 'y',
regcontents = { 'baz text' },
regname = '',
@@ -196,6 +210,7 @@ describe('TextYankPost', function()
command('normal yw')
eq({
+ inclusive = false,
operator = 'y',
regcontents = { 'baz ' },
regname = '',
@@ -205,6 +220,7 @@ describe('TextYankPost', function()
command('normal! dd')
eq({
+ inclusive = false,
operator = 'd',
regcontents = { 'baz text' },
regname = '',
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index 7b89172f92..ddaed1c448 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -1,5 +1,5 @@
-
local helpers = require('test.functional.helpers')(after_each)
+local uname = helpers.uname
local clear, eq, eval, next_msg, ok, source = helpers.clear, helpers.eq,
helpers.eval, helpers.next_msg, helpers.ok, helpers.source
local command, funcs, meths = helpers.command, helpers.funcs, helpers.meths
@@ -7,6 +7,7 @@ local sleep = helpers.sleep
local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv
local set_session = helpers.set_session
local nvim_prog = helpers.nvim_prog
+local os_name = helpers.os_name
local retry = helpers.retry
local expect_twostreams = helpers.expect_twostreams
@@ -138,9 +139,8 @@ describe('channels', function()
command("call chansend(id, 'incomplet\004')")
- local is_freebsd = eval("system('uname') =~? 'FreeBSD'") == 1
- local bsdlike = is_freebsd or (helpers.os_name() == "osx")
- print("bsdlike:", bsdlike)
+ local is_freebsd = (string.lower(uname()) == 'freebsd')
+ local bsdlike = is_freebsd or (os_name() == "osx")
local extra = bsdlike and "^D\008\008" or ""
expect_twoline(id, "stdout",
"incomplet"..extra, "[1, ['incomplet'], 'stdin']", true)
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index 6468aa9d0a..c74eb3bb02 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -68,7 +68,7 @@ describe('fileio', function()
eq(4, request('nvim__stats').fsync)
end)
- it('creates a backup', function()
+ it('backup #9709', function()
clear({ args={ '-i', 'Xtest_startup_shada',
'--cmd', 'set directory=Xtest_startup_swapdir' } })
diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua
index a0981e9207..b793e531c9 100644
--- a/test/functional/core/main_spec.lua
+++ b/test/functional/core/main_spec.lua
@@ -7,19 +7,9 @@ local feed = helpers.feed
local eval = helpers.eval
local clear = helpers.clear
local funcs = helpers.funcs
-local nvim_prog = helpers.nvim_prog
+local nvim_prog_abs = helpers.nvim_prog_abs
local write_file = helpers.write_file
-local function nvim_prog_abs()
- -- system(['build/bin/nvim']) does not work for whatever reason. It needs to
- -- either be executable searched in $PATH or something starting with / or ./.
- if nvim_prog:match('[/\\]') then
- return funcs.fnamemodify(nvim_prog, ':p')
- else
- return nvim_prog
- end
-end
-
describe('Command-line option', function()
describe('-s', function()
local fname = 'Xtest-functional-core-main-s'
diff --git a/test/functional/spell/spellfile_spec.lua b/test/functional/core/spellfile_spec.lua
index afd2c1bce4..afd2c1bce4 100644
--- a/test/functional/spell/spellfile_spec.lua
+++ b/test/functional/core/spellfile_spec.lua
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index 8edb8fc014..f77af836a6 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -13,9 +13,7 @@ local nvim_set = helpers.nvim_set
local read_file = helpers.read_file
local retry = helpers.retry
local rmdir = helpers.rmdir
-local set_session = helpers.set_session
local sleep = helpers.sleep
-local spawn = helpers.spawn
local iswin = helpers.iswin
local write_file = helpers.write_file
@@ -203,6 +201,20 @@ describe('startup', function()
{ 'set encoding', '' }))
end)
+ it('-es/-Es disables swapfile, user config #8540', function()
+ for _,arg in ipairs({'-es', '-Es'}) do
+ local out = funcs.system({nvim_prog, arg,
+ '+set swapfile? updatecount? shada?',
+ "+put =execute('scriptnames')", '+%print'})
+ local line1 = string.match(out, '^.-\n')
+ -- updatecount=0 means swapfile was disabled.
+ eq(" swapfile updatecount=0 shada=!,'100,<50,s10,h\n", line1)
+ -- Standard plugins were loaded, but not user config.
+ eq('health.vim', string.match(out, 'health.vim'))
+ eq(nil, string.match(out, 'init.vim'))
+ end
+ end)
+
it('does not crash if --embed is given twice', function()
clear{args={'--embed'}}
eq(2, eval('1+1'))
@@ -214,10 +226,6 @@ describe('sysinit', function()
local vimdir = 'Xvim'
local xhome = 'Xhome'
local pathsep = helpers.get_pathsep()
- local argv = {
- nvim_prog, '--headless', '--embed', '-i', 'NONE', '-n',
- '--cmd', 'set nomore undodir=. directory=. belloff='
- }
before_each(function()
rmdir(xdgdir)
@@ -246,19 +254,21 @@ describe('sysinit', function()
end)
it('prefers XDG_CONFIG_DIRS over VIM', function()
- set_session(spawn(argv, nil,
- { 'HOME='..xhome,
- 'XDG_CONFIG_DIRS='..xdgdir,
- 'VIM='..vimdir }))
+ clear{args={'--cmd', 'set nomore undodir=. directory=. belloff='},
+ args_rm={'-u', '--cmd'},
+ env={ HOME=xhome,
+ XDG_CONFIG_DIRS=xdgdir,
+ VIM=vimdir }}
eq('loaded 1 xdg 1 vim 0',
eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))'))
end)
it('uses VIM if XDG_CONFIG_DIRS unset', function()
- set_session(spawn(argv, nil,
- { 'HOME='..xhome,
- 'XDG_CONFIG_DIRS=',
- 'VIM='..vimdir }))
+ clear{args={'--cmd', 'set nomore undodir=. directory=. belloff='},
+ args_rm={'-u', '--cmd'},
+ env={ HOME=xhome,
+ XDG_CONFIG_DIRS='',
+ VIM=vimdir }}
eq('loaded 1 xdg 0 vim 1',
eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))'))
end)
diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua
index 6f440c7d82..40d06b599f 100644
--- a/test/functional/eval/api_functions_spec.lua
+++ b/test/functional/eval/api_functions_spec.lua
@@ -6,7 +6,7 @@ local clear, curbufmeths = helpers.clear, helpers.curbufmeths
local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval
local insert = helpers.insert
-describe('api functions', function()
+describe('eval-API', function()
before_each(clear)
it("work", function()
diff --git a/test/functional/eval/executable_spec.lua b/test/functional/eval/executable_spec.lua
index c931b47221..6a95128a4d 100644
--- a/test/functional/eval/executable_spec.lua
+++ b/test/functional/eval/executable_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, call, iswin, write_file =
- helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file
+local eq, clear, call, iswin, write_file, command =
+ helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file,
+ helpers.command
describe('executable()', function()
before_each(clear)
@@ -48,18 +49,17 @@ describe('executable()', function()
end)
it('not set', function()
- local expected = iswin() and 1 or 0
- eq(expected, call('executable', 'Xtest_not_executable'))
- eq(expected, call('executable', './Xtest_not_executable'))
+ eq(0, call('executable', 'Xtest_not_executable'))
+ eq(0, call('executable', './Xtest_not_executable'))
end)
it('set, unqualified and not in $PATH', function()
- local expected = iswin() and 1 or 0
- eq(expected, call('executable', 'Xtest_executable'))
+ eq(0, call('executable', 'Xtest_executable'))
end)
it('set, qualified as a path', function()
- eq(1, call('executable', './Xtest_executable'))
+ local expected = iswin() and 0 or 1
+ eq(expected, call('executable', './Xtest_executable'))
end)
end)
end)
@@ -136,16 +136,48 @@ describe('executable() (Windows)', function()
eq(1, call('executable', '.\\test_executable_zzz'))
end)
- it('returns 1 for any existing filename', function()
+ it("with weird $PATHEXT", function()
+ clear({env={PATHEXT=';'}})
+ eq(0, call('executable', '.\\test_executable_zzz'))
+ clear({env={PATHEXT=';;;.zzz;;'}})
+ eq(1, call('executable', '.\\test_executable_zzz'))
+ end)
+
+ it("unqualified filename, Unix-style 'shell'", function()
clear({env={PATHEXT=''}})
+ command('set shell=sh')
for _,ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
end
eq(1, call('executable', 'test_executable_zzz.zzz'))
end)
- it('returns 1 for any existing path (backslashes)', function()
+ it("relative path, Unix-style 'shell' (backslashes)", function()
clear({env={PATHEXT=''}})
+ command('set shell=bash.exe')
+ for _,ext in ipairs(exts) do
+ eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext))
+ eq(1, call('executable', './test_executable_'..ext..'.'..ext))
+ end
+ eq(1, call('executable', '.\\test_executable_zzz.zzz'))
+ eq(1, call('executable', './test_executable_zzz.zzz'))
+ end)
+
+ it('unqualified filename, $PATHEXT contains dot', function()
+ clear({env={PATHEXT='.;.zzz'}})
+ for _,ext in ipairs(exts) do
+ eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
+ end
+ eq(1, call('executable', 'test_executable_zzz.zzz'))
+ clear({env={PATHEXT='.zzz;.'}})
+ for _,ext in ipairs(exts) do
+ eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
+ end
+ eq(1, call('executable', 'test_executable_zzz.zzz'))
+ end)
+
+ it('relative path, $PATHEXT contains dot (backslashes)', function()
+ clear({env={PATHEXT='.;.zzz'}})
for _,ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext))
eq(1, call('executable', './test_executable_'..ext..'.'..ext))
@@ -153,4 +185,17 @@ describe('executable() (Windows)', function()
eq(1, call('executable', '.\\test_executable_zzz.zzz'))
eq(1, call('executable', './test_executable_zzz.zzz'))
end)
+
+ it('ignores case of extension', function()
+ clear({env={PATHEXT='.ZZZ'}})
+ eq(1, call('executable', 'test_executable_zzz.zzz'))
+ end)
+
+ it('relative path does not search $PATH', function()
+ clear({env={PATHEXT=''}})
+ eq(0, call('executable', './System32/notepad.exe'))
+ eq(0, call('executable', '.\\System32\\notepad.exe'))
+ eq(0, call('executable', '../notepad.exe'))
+ eq(0, call('executable', '..\\notepad.exe'))
+ end)
end)
diff --git a/test/functional/eval/exepath_spec.lua b/test/functional/eval/exepath_spec.lua
new file mode 100644
index 0000000000..10a11aeacc
--- /dev/null
+++ b/test/functional/eval/exepath_spec.lua
@@ -0,0 +1,14 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq, clear, call, iswin =
+ helpers.eq, helpers.clear, helpers.call, helpers.iswin
+
+describe('exepath() (Windows)', function()
+ if not iswin() then return end -- N/A for Unix.
+
+ it('append extension if omitted', function()
+ local filename = 'cmd'
+ local pathext = '.exe'
+ clear({env={PATHEXT=pathext}})
+ eq(call('exepath', filename..pathext), call('exepath', filename))
+ end)
+end)
diff --git a/test/functional/eval/let_spec.lua b/test/functional/eval/let_spec.lua
index 0cbf40137e..63e18f943f 100644
--- a/test/functional/eval/let_spec.lua
+++ b/test/functional/eval/let_spec.lua
@@ -59,7 +59,7 @@ describe(':let', function()
end)
it("multibyte env var to child process #8398 #9267", function()
- if (not helpers.iswin()) and require('test.helpers').isCI() then
+ if (not helpers.iswin()) and helpers.isCI() then
-- Fails on non-Windows CI. Buffering/timing issue?
pending('fails on unix CI', function() end)
end
diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua
index bc2b365b30..283fcf9672 100644
--- a/test/functional/ex_cmds/cd_spec.lua
+++ b/test/functional/ex_cmds/cd_spec.lua
@@ -286,6 +286,15 @@ describe("getcwd()", function ()
command("call delete('../"..directories.global.."', 'd')")
eq("", helpers.eval("getcwd()"))
end)
+
+ it("works with 'autochdir' after local directory was set (#9892)", function()
+ local curdir = cwd()
+ command('lcd ' .. directories.global)
+ command('lcd -')
+ command('set autochdir')
+ command('edit ' .. directories.global .. '/foo')
+ eq(curdir .. pathsep .. directories.global, cwd())
+ end)
end)
diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index 3d550588e7..48e7e05e4c 100644
--- a/test/functional/ex_cmds/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
@@ -1,12 +1,13 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
+local insert = helpers.insert
local eq, next_msg = helpers.eq, helpers.next_msg
local exc_exec = helpers.exc_exec
local command = helpers.command
local eval = helpers.eval
-describe('dictionary change notifications', function()
+describe('VimL dictionary notifications', function()
local channel
before_each(function()
@@ -338,4 +339,22 @@ describe('dictionary change notifications', function()
eq({'notification', '2', {'foo', {old = 'baz', new = 'bar'}}}, next_msg())
end)
end)
+
+ it('for b:changedtick', function()
+ source([[
+ function! OnTickChanged(dict, key, value)
+ call rpcnotify(g:channel, 'SendChangeTick', a:key, a:value)
+ endfunction
+ call dictwatcheradd(b:, 'changedtick', 'OnTickChanged')
+ ]])
+
+ insert('t');
+ eq({'notification', 'SendChangeTick', {'changedtick', {old = 2, new = 3}}},
+ next_msg())
+
+ command([[call dictwatcherdel(b:, 'changedtick', 'OnTickChanged')]])
+ insert('t');
+ eq(2, eval('1+1')) -- Still alive?
+ end)
+
end)
diff --git a/test/functional/ex_cmds/help_spec.lua b/test/functional/ex_cmds/help_spec.lua
new file mode 100644
index 0000000000..66d7d7d89f
--- /dev/null
+++ b/test/functional/ex_cmds/help_spec.lua
@@ -0,0 +1,27 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local funcs = helpers.funcs
+
+describe(':help', function()
+ before_each(clear)
+
+ it('window closed makes cursor return to a valid win/buf #9773', function()
+ command('help help')
+ eq(1001, funcs.win_getid())
+ command('quit')
+ eq(1000, funcs.win_getid())
+
+ command('autocmd WinNew * wincmd p')
+
+ command('help help')
+ -- Window 1002 is opened, but the autocmd switches back to 1000 and
+ -- creates the help buffer there instead.
+ eq(1000, funcs.win_getid())
+ command('quit')
+ -- Before #9773, Nvim would crash on quitting the help window.
+ eq(1002, funcs.win_getid())
+ end)
+end)
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
index a5b327095e..0f7860740e 100644
--- a/test/functional/ex_cmds/mksession_spec.lua
+++ b/test/functional/ex_cmds/mksession_spec.lua
@@ -47,4 +47,27 @@ describe(':mksession', function()
command('tabnext 2')
eq(cwd_dir .. get_pathsep() .. tab_dir, funcs.getcwd())
end)
+
+ it('restores buffers when using tab-local working directories', function()
+ local tmpfile_base = file_prefix .. '-tmpfile'
+ local cwd_dir = funcs.getcwd()
+ local session_path = cwd_dir .. get_pathsep() .. session_file
+
+ command('edit ' .. tmpfile_base .. '1')
+ command('tcd ' .. tab_dir)
+ command('tabnew')
+ command('edit ' .. cwd_dir .. get_pathsep() .. tmpfile_base .. '2')
+ command('tabfirst')
+ command('mksession ' .. session_path)
+
+ -- Create a new test instance of Nvim.
+ clear()
+
+ -- Use :silent to avoid press-enter prompt due to long path
+ command('silent source ' .. session_path)
+ command('tabnext 1')
+ eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '1', funcs.expand('%:p'))
+ command('tabnext 2')
+ eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p'))
+ end)
end)
diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua
index e2958c2924..802c3f68c6 100644
--- a/test/functional/ex_cmds/oldfiles_spec.lua
+++ b/test/functional/ex_cmds/oldfiles_spec.lua
@@ -1,18 +1,18 @@
local Screen = require('test.functional.ui.screen')
local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
local buf, eq, feed_command = helpers.curbufmeths, helpers.eq, helpers.feed_command
-local feed, nvim_prog, wait = helpers.feed, helpers.nvim_prog, helpers.wait
-local ok, set_session, spawn = helpers.ok, helpers.set_session, helpers.spawn
+local feed, wait = helpers.feed, helpers.wait
+local ok = helpers.ok
local eval = helpers.eval
local shada_file = 'Xtest.shada'
local function _clear()
- set_session(spawn({nvim_prog, '--embed', '--headless', '-u', 'NONE',
- -- Need shada for these tests.
- '-i', shada_file,
- '--cmd', 'set noswapfile undodir=. directory=. viewdir=. backupdir=. belloff= noshowcmd noruler'}))
+ clear{args={'-i', shada_file, -- Need shada for these tests.
+ '--cmd', 'set noswapfile undodir=. directory=. viewdir=. backupdir=. belloff= noshowcmd noruler'},
+ args_rm={'-i', '--cmd'}}
end
describe(':oldfiles', function()
diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua
index bf10f80401..3392a90270 100644
--- a/test/functional/ex_cmds/quickfix_commands_spec.lua
+++ b/test/functional/ex_cmds/quickfix_commands_spec.lua
@@ -37,9 +37,9 @@ for _, c in ipairs({'l', 'c'}) do
-- Second line of each entry (i.e. `nr=-1, …`) was obtained from actual
-- results. First line (i.e. `{lnum=…`) was obtained from legacy test.
local list = {
- {lnum=700, col=10, text='Line 700',
+ {lnum=700, col=10, text='Line 700', module='',
nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''},
- {lnum=800, col=15, text='Line 800',
+ {lnum=800, col=15, text='Line 800', module='',
nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''},
}
eq(list, getlist())
@@ -58,7 +58,7 @@ for _, c in ipairs({'l', 'c'}) do
]]):format(file))
command(('%s %s'):format(addfcmd, file))
list[#list + 1] = {
- lnum=900, col=30, text='Line 900',
+ lnum=900, col=30, text='Line 900', module='',
nr=-1, bufnr=5, valid=1, pattern='', vcol=0, ['type']='',
}
eq(list, getlist())
@@ -71,9 +71,9 @@ for _, c in ipairs({'l', 'c'}) do
command('enew!')
command(('%s %s'):format(getfcmd, file))
list = {
- {lnum=222, col=77, text='Line 222',
+ {lnum=222, col=77, text='Line 222', module='',
nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''},
- {lnum=333, col=88, text='Line 333',
+ {lnum=333, col=88, text='Line 333', module='',
nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''},
}
eq(list, getlist())
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index bbab1471f6..73cbb1d54e 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -13,6 +13,7 @@ local rmdir = helpers.rmdir
local set_session = helpers.set_session
local spawn = helpers.spawn
local nvim_async = helpers.nvim_async
+local expect_msg_seq = helpers.expect_msg_seq
describe(':recover', function()
before_each(clear)
@@ -163,6 +164,13 @@ describe('swapfile detection', function()
screen2:expect{any=[[Found a swap file by the name ".*]]
..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
feed('e') -- Chose "Edit" at the swap dialog.
- feed('<c-c>')
+ expect_msg_seq({
+ ignore={'redraw'},
+ seqs={
+ { {'notification', 'nvim_error_event', {0, 'Vim(edit):E325: ATTENTION'}},
+ }
+ }
+ })
+ feed('<cr>')
end)
end)
diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua
index df0b9df5dd..7c00daf1d7 100644
--- a/test/functional/ex_cmds/wviminfo_spec.lua
+++ b/test/functional/ex_cmds/wviminfo_spec.lua
@@ -1,23 +1,21 @@
local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs')
-local command, eq, neq, spawn, nvim_prog, set_session, write_file =
- helpers.command, helpers.eq, helpers.neq, helpers.spawn,
- helpers.nvim_prog, helpers.set_session, helpers.write_file
+local clear = helpers.clear
+local command, eq, neq, write_file =
+ helpers.command, helpers.eq, helpers.neq, helpers.write_file
local iswin = helpers.iswin
local read_file = helpers.read_file
describe(':wshada', function()
local shada_file = 'wshada_test'
- local session
before_each(function()
- -- Override the default session because we need 'swapfile' for these tests.
- session = spawn({nvim_prog, '-u', 'NONE', '-i', iswin() and 'nul' or '/dev/null', '--embed',
- '--cmd', 'set swapfile'})
- set_session(session)
+ clear{args={'-i', iswin() and 'nul' or '/dev/null',
+ -- Need 'swapfile' for these tests.
+ '--cmd', 'set swapfile undodir=. directory=. viewdir=. backupdir=. belloff= noshowcmd noruler'},
+ args_rm={'-n', '-i', '--cmd'}}
end)
after_each(function ()
- session:close()
os.remove(shada_file)
end)
diff --git a/test/functional/example_spec.lua b/test/functional/example_spec.lua
new file mode 100644
index 0000000000..883fe4ba63
--- /dev/null
+++ b/test/functional/example_spec.lua
@@ -0,0 +1,36 @@
+-- To run this test:
+-- TEST_FILE=test/functional/example_spec.lua make functionaltest
+
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed = helpers.clear, helpers.feed
+
+describe('example', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(20,5)
+ screen:attach()
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {bold=true, foreground=Screen.colors.Brown}
+ } )
+ end)
+
+ it('screen test', function()
+ -- Do some stuff.
+ feed('iline1<cr>line2<esc>')
+
+ -- For debugging only: prints the current screen.
+ -- screen:snapshot_util()
+
+ -- Assert the expected state.
+ screen:expect([[
+ line1 |
+ line^2 |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 9d82ce9c0d..35084f6cff 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -8,37 +8,28 @@ local Session = require('nvim.session')
local TcpStream = require('nvim.tcp_stream')
local SocketStream = require('nvim.socket_stream')
local ChildProcessStream = require('nvim.child_process_stream')
-local Paths = require('test.config.paths')
local check_cores = global_helpers.check_cores
local check_logs = global_helpers.check_logs
local dedent = global_helpers.dedent
local eq = global_helpers.eq
-local expect_err = global_helpers.expect_err
-local filter = global_helpers.filter
-local map = global_helpers.map
-local matches = global_helpers.matches
-local near = global_helpers.near
-local neq = global_helpers.neq
local ok = global_helpers.ok
-local read_file = global_helpers.read_file
local sleep = global_helpers.sleep
-local table_flatten = global_helpers.table_flatten
+local tbl_contains = global_helpers.tbl_contains
local write_file = global_helpers.write_file
-local trim = global_helpers.trim
local start_dir = lfs.currentdir()
-- XXX: NVIM_PROG takes precedence, QuickBuild sets it.
local nvim_prog = (
os.getenv('NVIM_PROG')
or os.getenv('NVIM_PRG')
- or Paths.test_build_dir .. '/bin/nvim'
+ or global_helpers.test_build_dir .. '/bin/nvim'
)
-- Default settings for the test session.
local nvim_set = 'set shortmess+=I background=light noswapfile noautoindent'
..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.'
..' belloff= noshowcmd noruler nomore'
-local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
+local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE',
'--cmd', nvim_set, '--embed'}
-- Directory containing nvim.
local nvim_dir = nvim_prog:gsub("[/\\][^/\\]+$", "")
@@ -130,16 +121,33 @@ end
-- Expects a sequence of next_msg() results. If multiple sequences are
-- passed they are tried until one succeeds, in order of shortest to longest.
+--
+-- Can be called with positional args (list of sequences only):
+-- expect_msg_seq(seq1, seq2, ...)
+-- or keyword args:
+-- expect_msg_seq{ignore={...}, seqs={seq1, seq2, ...}}
+--
+-- ignore: List of ignored event names.
+-- seqs: List of one or more potential event sequences.
local function expect_msg_seq(...)
if select('#', ...) < 1 then
error('need at least 1 argument')
end
- local seqs = {...}
+ local arg1 = select(1, ...)
+ if (arg1['seqs'] and select('#', ...) > 1) or type(arg1) ~= 'table' then
+ error('invalid args')
+ end
+ local ignore = arg1['ignore'] and arg1['ignore'] or {}
+ local seqs = arg1['seqs'] and arg1['seqs'] or {...}
+ if type(ignore) ~= 'table' then
+ error("'ignore' arg must be a list of strings")
+ end
table.sort(seqs, function(a, b) -- Sort ascending, by (shallow) length.
return #a < #b
end)
local actual_seq = {}
+ local nr_ignored = 0
local final_error = ''
local function cat_err(err1, err2)
if err1 == nil then
@@ -152,12 +160,16 @@ local function expect_msg_seq(...)
-- Collect enough messages to compare the next expected sequence.
while #actual_seq < #expected_seq do
local msg = next_msg(10000) -- Big timeout for ASAN/valgrind.
+ local msg_type = msg and msg[2] or nil
if msg == nil then
error(cat_err(final_error,
- string.format('got %d messages, expected %d',
- #actual_seq, #expected_seq)))
+ string.format('got %d messages (ignored %d), expected %d',
+ #actual_seq, nr_ignored, #expected_seq)))
+ elseif tbl_contains(ignore, msg_type) then
+ nr_ignored = nr_ignored + 1
+ else
+ table.insert(actual_seq, msg)
end
- table.insert(actual_seq, msg)
end
local status, result = pcall(eq, expected_seq, actual_seq)
if status then
@@ -217,6 +229,16 @@ local function stop()
session:stop()
end
+local function nvim_prog_abs()
+ -- system(['build/bin/nvim']) does not work for whatever reason. It must
+ -- be executable searched in $PATH or something starting with / or ./.
+ if nvim_prog:match('[/\\]') then
+ return request('nvim_call_function', 'fnamemodify', {nvim_prog, ':p'})
+ else
+ return nvim_prog
+ end
+end
+
-- Executes an ex-command. VimL errors manifest as client (lua) errors, but
-- v:errmsg will not be updated.
local function nvim_command(cmd)
@@ -291,6 +313,43 @@ local function merge_args(...)
return argv
end
+-- Removes Nvim startup args from `args` matching items in `args_rm`.
+--
+-- "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
+-- Example:
+-- args={'--headless', '-u', 'NONE'}
+-- args_rm={'--cmd', '-u'}
+-- Result:
+-- {'--headless'}
+--
+-- All cases are removed.
+-- Example:
+-- args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
+-- args_rm={'--cmd', '-u'}
+-- Result:
+-- {'-N'}
+local function remove_args(args, args_rm)
+ local new_args = {}
+ local skip_following = {'-u', '-i', '-c', '--cmd', '-s', '--listen'}
+ if not args_rm or #args_rm == 0 then
+ return {unpack(args)}
+ end
+ for _, v in ipairs(args_rm) do
+ assert(type(v) == 'string')
+ end
+ local last = ''
+ for _, arg in ipairs(args) do
+ if tbl_contains(skip_following, last) then
+ last = ''
+ elseif tbl_contains(args_rm, arg) then
+ last = arg
+ else
+ table.insert(new_args, arg)
+ end
+ end
+ return new_args
+end
+
local function spawn(argv, merge, env)
local child_stream = ChildProcessStream.spawn(
merge and merge_args(prepend_argv, argv) or argv,
@@ -329,20 +388,25 @@ local function retry(max, max_ms, fn)
end
-- Starts a new global Nvim session.
+--
-- Parameters are interpreted as startup args, OR a map with these keys:
--- args: Merged with the default `nvim_argv` set.
--- env : Defines the environment of the new session.
+-- args: List: Args appended to the default `nvim_argv` set.
+-- args_rm: List: Args removed from the default set. All cases are
+-- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
+-- (and its value) from the default set.
+-- env: Map: Defines the environment of the new session.
--
-- Example:
-- clear('-e')
--- clear({args={'-e'}, env={TERM=term}})
+-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
local function clear(...)
local args = {unpack(nvim_argv)}
+ table.insert(args, '--headless')
local new_args
local env = nil
local opts = select(1, ...)
- local headless = true
if type(opts) == 'table' then
+ args = remove_args(args, opts.args_rm)
if opts.env then
local env_tbl = {}
for k, v in pairs(opts.env) do
@@ -353,7 +417,8 @@ local function clear(...)
for _, k in ipairs({
'HOME',
'ASAN_OPTIONS',
- 'LD_LIBRARY_PATH', 'PATH',
+ 'LD_LIBRARY_PATH',
+ 'PATH',
'NVIM_LOG_FILE',
'NVIM_RPLUGIN_MANIFEST',
}) do
@@ -367,15 +432,9 @@ local function clear(...)
end
end
new_args = opts.args or {}
- if opts.headless == false then
- headless = false
- end
else
new_args = {...}
end
- if headless then
- table.insert(args, '--headless')
- end
for _, arg in ipairs(new_args) do
table.insert(args, arg)
end
@@ -679,41 +738,14 @@ local function alter_slashes(obj)
end
end
-local function compute_load_factor()
- local timeout = 200
- local times = {}
-
- clear()
-
- for _ = 1, 5 do
- source([[
- let g:val = 0
- call timer_start(200, {-> nvim_set_var('val', 1)})
- let start = reltime()
- while 1
- sleep 10m
- if g:val == 1
- let g:waited_in_ms = float2nr(reltimefloat(reltime(start)) * 1000)
- break
- endif
- endwhile
- ]])
- table.insert(times, nvim_eval('g:waited_in_ms'))
- end
-
- session:close()
- session = nil
-
- local longest = math.max(unpack(times))
- local factor = (longest + 50.0) / timeout
-
- return factor
-end
-
--- Compute load factor only once.
-local load_factor = compute_load_factor()
+local load_factor = nil
local function load_adjust(num)
+ if load_factor == nil then -- Compute load factor only once.
+ clear()
+ request('nvim_command', 'source src/nvim/testdir/load.vim')
+ load_factor = request('nvim_eval', 'g:test_load_factor')
+ end
return math.ceil(num * load_factor)
end
@@ -734,33 +766,25 @@ local module = {
curtabmeths = curtabmeths,
curwin = curwin,
curwinmeths = curwinmeths,
- dedent = dedent,
- eq = eq,
eval = nvim_eval,
exc_exec = exc_exec,
expect = expect,
expect_any = expect_any,
- expect_err = expect_err,
expect_msg_seq = expect_msg_seq,
expect_twostreams = expect_twostreams,
feed = feed,
feed_command = feed_command,
- filter = filter,
funcs = funcs,
get_pathsep = get_pathsep,
get_session = get_session,
insert = insert,
iswin = iswin,
- map = map,
- matches = matches,
merge_args = merge_args,
meth_pcall = meth_pcall,
meths = meths,
missing_provider = missing_provider,
mkdir = lfs.mkdir,
load_adjust = load_adjust,
- near = near,
- neq = neq,
new_pipename = new_pipename,
next_msg = next_msg,
nvim = nvim,
@@ -768,14 +792,13 @@ local module = {
nvim_async = nvim_async,
nvim_dir = nvim_dir,
nvim_prog = nvim_prog,
+ nvim_prog_abs = nvim_prog_abs,
nvim_set = nvim_set,
- ok = ok,
os_name = os_name,
pathroot = pathroot,
pending_win32 = pending_win32,
prepend_argv = prepend_argv,
rawfeed = rawfeed,
- read_file = read_file,
redir_exec = redir_exec,
request = request,
retry = retry,
@@ -785,21 +808,17 @@ local module = {
set_session = set_session,
set_shell_powershell = set_shell_powershell,
skip_fragile = skip_fragile,
- sleep = sleep,
source = source,
spawn = spawn,
stop = stop,
- table_flatten = table_flatten,
tabmeths = tabmeths,
tabpage = tabpage,
- tmpname = tmpname,
uimeths = uimeths,
wait = wait,
window = window,
winmeths = winmeths,
- write_file = write_file,
- trim = trim,
}
+module = global_helpers.tbl_extend('error', module, global_helpers)
return function(after_each)
if after_each then
diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua
index e17b463e30..f7f074c61a 100644
--- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua
+++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua
@@ -2,9 +2,9 @@
local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs')
-local clear, command, eq, neq, eval, wait, spawn =
+local clear, command, eq, neq, eval, wait =
helpers.clear, helpers.command, helpers.eq, helpers.neq, helpers.eval,
- helpers.wait, helpers.spawn
+ helpers.wait
describe('storing global variables in ShaDa files', function()
local tempname = 'Xtest-functional-legacy-074'
@@ -14,9 +14,7 @@ describe('storing global variables in ShaDa files', function()
end)
it('is working', function()
- local nvim2 = spawn({helpers.nvim_prog, '-u', 'NONE',
- '-i', 'Xviminfo', '--embed'})
- helpers.set_session(nvim2)
+ clear{args_rm={'-i'}, args={'-i', 'Xviminfo'}}
local test_dict = {foo = 1, bar = 0, longvarible = 1000}
local test_list = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua
index 10703465aa..8df2d89b70 100644
--- a/test/functional/legacy/assert_spec.lua
+++ b/test/functional/legacy/assert_spec.lua
@@ -18,6 +18,15 @@ describe('assert function:', function()
clear()
end)
+ describe('assert_beeps', function()
+ it('works', function()
+ call('assert_beeps', 'normal h')
+ expected_empty()
+ call('assert_beeps', 'normal 0')
+ expected_errors({'command did not beep: normal 0'})
+ end)
+ end)
+
-- assert_equal({expected}, {actual}, [, {msg}])
describe('assert_equal', function()
it('should not change v:errors when expected is equal to actual', function()
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 007d40874f..8f318e3503 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -300,3 +300,19 @@ describe('package.path/package.cpath', function()
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
end)
end)
+
+describe('os.getenv', function()
+ it('returns nothing for undefined env var', function()
+ eq(NIL, funcs.luaeval('os.getenv("XTEST_1")'))
+ end)
+ it('returns env var set by the parent process', function()
+ local value = 'foo'
+ clear({env = {['XTEST_1']=value}})
+ eq(value, funcs.luaeval('os.getenv("XTEST_1")'))
+ end)
+ it('returns env var set by let', function()
+ local value = 'foo'
+ meths.command('let $XTEST_1 = "'..value..'"')
+ eq(value, funcs.luaeval('os.getenv("XTEST_1")'))
+ end)
+end)
diff --git a/test/functional/normal/jump_spec.lua b/test/functional/normal/jump_spec.lua
new file mode 100644
index 0000000000..5bed541752
--- /dev/null
+++ b/test/functional/normal/jump_spec.lua
@@ -0,0 +1,48 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local funcs = helpers.funcs
+local feed = helpers.feed
+local write_file = helpers.write_file
+
+describe('jumplist', function()
+ local fname1 = 'Xtest-functional-normal-jump'
+ local fname2 = fname1..'2'
+ before_each(clear)
+ after_each(function()
+ os.remove(fname1)
+ os.remove(fname2)
+ end)
+
+ it('does not add a new entry on startup', function()
+ eq('\n jump line col file/text\n>', funcs.execute('jumps'))
+ end)
+
+ it('does not require two <C-O> strokes to jump back', function()
+ write_file(fname1, 'first file contents')
+ write_file(fname2, 'second file contents')
+
+ command('args '..fname1..' '..fname2)
+ local buf1 = funcs.bufnr(fname1)
+ local buf2 = funcs.bufnr(fname2)
+
+ command('next')
+ feed('<C-O>')
+ eq(buf1, funcs.bufnr('%'))
+
+ command('first')
+ command('snext')
+ feed('<C-O>')
+ eq(buf1, funcs.bufnr('%'))
+ feed('<C-I>')
+ eq(buf2, funcs.bufnr('%'))
+ feed('<C-O>')
+ eq(buf1, funcs.bufnr('%'))
+
+ command('drop '..fname2)
+ feed('<C-O>')
+ eq(buf1, funcs.bufnr('%'))
+ end)
+end)
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index f6f3f02f45..415b526051 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -15,6 +15,7 @@ local neq = helpers.neq
local mkdir = helpers.mkdir
local rmdir = helpers.rmdir
local alter_slashes = helpers.alter_slashes
+local tbl_contains = helpers.tbl_contains
describe('startup defaults', function()
describe(':filetype', function()
@@ -160,20 +161,39 @@ describe('startup defaults', function()
end)
end)
- describe("'packpath'", function()
- it('defaults to &runtimepath', function()
- eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
- end)
+ it("'shadafile' ('viminfofile')", function()
+ local env = {XDG_DATA_HOME='Xtest-userdata', XDG_CONFIG_HOME='Xtest-userconfig'}
+ clear{args={}, args_rm={'-i'}, env=env}
+ -- Default 'shadafile' is empty.
+ -- This means use the default location. :help shada-file-name
+ eq('', meths.get_option('shadafile'))
+ eq('', meths.get_option('viminfofile'))
+ -- Check that shada data (such as v:oldfiles) is saved/restored.
+ command('edit Xtest-foo')
+ command('write')
+ local f = eval('fnamemodify(@%,":p")')
+ assert(string.len(f) > 3)
+ command('qall')
+ clear{args={}, args_rm={'-i'}, env=env}
+ eq({ f }, eval('v:oldfiles'))
+ os.remove('Xtest-foo')
+ rmdir('Xtest-userdata')
+ end)
- it('does not follow modifications to runtimepath', function()
- meths.command('set runtimepath+=foo')
- neq(meths.get_option('runtimepath'), meths.get_option('packpath'))
- meths.command('set packpath+=foo')
- eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
- end)
+ it("'packpath'", function()
+ clear()
+ -- Defaults to &runtimepath.
+ eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
+
+ -- Does not follow modifications to runtimepath.
+ meths.command('set runtimepath+=foo')
+ neq(meths.get_option('runtimepath'), meths.get_option('packpath'))
+ meths.command('set packpath+=foo')
+ eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
end)
it('v:progpath is set to the absolute path', function()
+ clear()
eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath'))
end)
@@ -231,6 +251,23 @@ describe('XDG-based defaults', function()
-- Need separate describe() blocks to not run clear() twice.
-- Do not put before_each() here for the same reasons.
+ it("&runtimepath data-dir matches stdpath('data') #9910", function()
+ clear()
+ local rtp = eval('split(&runtimepath, ",")')
+ local rv = {}
+ local expected = (iswin()
+ and { [[\nvim-data\site]], [[\nvim-data\site\after]], }
+ or { '/nvim/site', '/nvim/site/after', })
+
+ for _,v in ipairs(rtp) do
+ local m = string.match(v, [=[[/\]nvim[^/\]*[/\]site.*$]=])
+ if m and not tbl_contains(rv, m) then
+ table.insert(rv, m)
+ end
+ end
+ eq(expected, rv)
+ end)
+
describe('with empty/broken environment', function()
it('sets correct defaults', function()
clear({env={
diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/provider/clipboard_spec.lua
index 2bbc678a02..b2d75db745 100644
--- a/test/functional/clipboard/clipboard_provider_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -236,7 +236,7 @@ describe('clipboard', function()
end)
end)
-describe('clipboard', function()
+describe('clipboard (with fake clipboard.vim)', function()
local function reset(...)
clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...)
end
@@ -664,4 +664,20 @@ describe('clipboard', function()
the a sourcetarget]])
end)
+ it('setreg("*") with clipboard=unnamed #5646', function()
+ source([=[
+ function! Paste_without_yank(direction) range
+ let [reg_save,regtype_save] = [getreg('*'), getregtype('*')]
+ normal! gvp
+ call setreg('*', reg_save, regtype_save)
+ endfunction
+ xnoremap p :call Paste_without_yank('p')<CR>
+ set clipboard=unnamed
+ ]=])
+ insert('some words')
+ feed('gg0yiw')
+ feed('wviwp')
+ expect('some some')
+ eq('some', eval('getreg("*")'))
+ end)
end)
diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua
index c2f6351e00..e6450e68b3 100644
--- a/test/functional/shada/marks_spec.lua
+++ b/test/functional/shada/marks_spec.lua
@@ -189,9 +189,6 @@ describe('ShaDa support code', function()
eq(1, nvim_current_line())
nvim_command('execute "normal! \\<C-o>"')
eq(testfilename, funcs.bufname('%'))
- eq(1, nvim_current_line())
- nvim_command('execute "normal! \\<C-o>"')
- eq(testfilename, funcs.bufname('%'))
eq(2, nvim_current_line())
nvim_command('execute "normal! \\<C-o>"')
eq(testfilename_2, funcs.bufname('%'))
@@ -199,6 +196,9 @@ describe('ShaDa support code', function()
nvim_command('execute "normal! \\<C-o>"')
eq(testfilename_2, funcs.bufname('%'))
eq(2, nvim_current_line())
+ nvim_command('execute "normal! \\<C-o>"')
+ eq(testfilename_2, funcs.bufname('%'))
+ eq(2, nvim_current_line())
end)
it('is able to dump and restore change list', function()
diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua
index a628baff53..22f2b8348d 100644
--- a/test/functional/shada/merging_spec.lua
+++ b/test/functional/shada/merging_spec.lua
@@ -912,12 +912,11 @@ describe('ShaDa jumps support code', function()
eq('', curbufmeths.get_name())
eq('\n'
.. ' jump line col file/text\n'
- .. ' 6 2 0 ' .. mock_file_path .. 'c\n'
- .. ' 5 2 0 ' .. mock_file_path .. 'd\n'
- .. ' 4 3 0 ' .. mock_file_path .. 'd\n'
- .. ' 3 2 0 ' .. mock_file_path .. 'e\n'
- .. ' 2 2 0 ' .. mock_file_path .. 'f\n'
- .. ' 1 1 0 \n'
+ .. ' 5 2 0 ' .. mock_file_path .. 'c\n'
+ .. ' 4 2 0 ' .. mock_file_path .. 'd\n'
+ .. ' 3 3 0 ' .. mock_file_path .. 'd\n'
+ .. ' 2 2 0 ' .. mock_file_path .. 'e\n'
+ .. ' 1 2 0 ' .. mock_file_path .. 'f\n'
.. '>', redir_exec('jumps'))
end)
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index dbee9bdb49..f3849709e3 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -99,6 +99,22 @@ describe(':terminal', function()
eq(3, #jumps)
end)
+ it(':stopinsert RPC request exits terminal-mode #7807', function()
+ command(':terminal')
+ feed('i[tui] insert-mode')
+ eq({ blocking=false, mode='t' }, nvim('get_mode'))
+ command('stopinsert')
+ eq({ blocking=false, mode='n' }, nvim('get_mode'))
+ end)
+
+ it(':stopinsert in normal mode doesn\'t break insert mode #9889', function()
+ command(':terminal')
+ eq({ blocking=false, mode='n' }, nvim('get_mode'))
+ command(':stopinsert')
+ eq({ blocking=false, mode='n' }, nvim('get_mode'))
+ feed('a')
+ eq({ blocking=false, mode='t' }, nvim('get_mode'))
+ end)
end)
describe(':terminal (with fake shell)', function()
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
index 9579e0ea0b..48fedd5927 100644
--- a/test/functional/terminal/highlight_spec.lua
+++ b/test/functional/terminal/highlight_spec.lua
@@ -3,9 +3,12 @@ local Screen = require('test.functional.ui.screen')
local thelpers = require('test.functional.terminal.helpers')
local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
local nvim_dir, command = helpers.nvim_dir, helpers.command
+local nvim_prog_abs = helpers.nvim_prog_abs
local eq, eval = helpers.eq, helpers.eval
+local funcs = helpers.funcs
+local nvim_set = helpers.nvim_set
-describe(':terminal window highlighting', function()
+describe(':terminal highlight', function()
local screen
before_each(function()
@@ -112,8 +115,51 @@ describe(':terminal window highlighting', function()
end)
end)
+it(':terminal highlight has lower precedence than editor #9964', function()
+ clear()
+ local screen = Screen.new(30, 4)
+ screen:set_default_attr_ids({
+ -- "Normal" highlight emitted by the child nvim process.
+ N_child = {foreground = tonumber('0x4040ff'), background = tonumber('0xffff40')},
+ -- "Search" highlight emitted by the child nvim process.
+ S_child = {background = tonumber('0xffff40'), italic = true, foreground = tonumber('0x4040ff')},
+ -- "Search" highlight in the parent nvim process.
+ S = {background = Screen.colors.Green, italic = true, foreground = Screen.colors.Red},
+ -- "Question" highlight in the parent nvim process.
+ Q = {background = tonumber('0xffff40'), bold = true, foreground = Screen.colors.SeaGreen4},
+ })
+ screen:attach({rgb=true})
+ -- Child nvim process in :terminal (with cterm colors).
+ funcs.termopen({
+ nvim_prog_abs(), '-n', '-u', 'NORC', '-i', 'NONE', '--cmd', nvim_set,
+ '+hi Normal ctermfg=Blue ctermbg=Yellow',
+ '+norm! ichild nvim',
+ '+norm! oline 2',
+ })
+ screen:expect([[
+ {N_child:^child nvim }|
+ {N_child:line 2 }|
+ {N_child: }|
+ |
+ ]])
+ command('hi Search gui=italic guifg=Red guibg=Green cterm=italic ctermfg=Red ctermbg=Green')
+ feed('/nvim<cr>')
+ screen:expect([[
+ {N_child:child }{S:^nvim}{N_child: }|
+ {N_child:line 2 }|
+ {N_child: }|
+ /nvim |
+ ]])
+ command('syntax keyword Question line')
+ screen:expect([[
+ {N_child:child }{S:^nvim}{N_child: }|
+ {Q:line}{N_child: 2 }|
+ {N_child: }|
+ /nvim |
+ ]])
+end)
-describe('terminal window highlighting with custom palette', function()
+describe(':terminal highlight with custom palette', function()
local screen
before_each(function()
diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua
index 75bb89a1ab..544325e746 100644
--- a/test/functional/terminal/scrollback_spec.lua
+++ b/test/functional/terminal/scrollback_spec.lua
@@ -142,15 +142,15 @@ describe(':terminal scrollback', function()
describe('and height decreased by 1', function()
if helpers.pending_win32(pending) then return end
local function will_hide_top_line()
- feed([[<C-\><C-N>:]]) -- Go to cmdline-mode, so cursor is at bottom.
+ feed([[<C-\><C-N>]])
screen:try_resize(screen._width - 2, screen._height - 1)
screen:expect([[
line2 |
line3 |
line4 |
rows: 5, cols: 28 |
- {2: } |
- :^ |
+ {2:^ } |
+ |
]])
end
@@ -166,11 +166,11 @@ describe(':terminal scrollback', function()
screen:expect([[
rows: 5, cols: 28 |
rows: 3, cols: 26 |
- {2: } |
- :^ |
+ {2:^ } |
+ |
]])
eq(8, curbuf('line_count'))
- feed([[<C-\><C-N>3k]])
+ feed([[3k]])
screen:expect([[
^line4 |
rows: 5, cols: 28 |
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index a0adb45630..56d6f68b7a 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -1,8 +1,7 @@
-- TUI acceptance tests.
-- Uses :terminal as a way to send keys and assert screen state.
-local global_helpers = require('test.helpers')
-local uname = global_helpers.uname
local helpers = require('test.functional.helpers')(after_each)
+local uname = helpers.uname
local thelpers = require('test.functional.terminal.helpers')
local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
@@ -255,14 +254,14 @@ describe('TUI', function()
]])
end)
- it('shows up in nvim_list_uis', function()
+ it('is included in nvim_list_uis()', function()
feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(filter(v, {k,v -> k[:3] !=# "ext_" })))})\013')
screen:expect([=[
|
{4:~ }|
{5: }|
- [[['height', 6], ['rgb', v:false], ['width', 50]]]|
- |
+ [[['height', 6], ['override', v:false], ['rgb', v:|
+ false], ['width', 50]]] |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
]=])
@@ -839,8 +838,7 @@ describe('TUI background color', function()
it("triggers OptionSet event on terminal-response", function()
feed_data('\027:autocmd OptionSet background echo "did OptionSet, yay!"\n')
- -- The child Nvim is running asynchronously; wait for it to register the
- -- OptionSet handler.
+ -- Wait for the child Nvim to register the OptionSet handler.
feed_data('\027:autocmd OptionSet\n')
screen:expect({any='--- Autocommands ---'})
@@ -860,16 +858,23 @@ describe('TUI background color', function()
local function assert_bg(color, bg)
it('handles '..color..' as '..bg, function()
- feed_data('\027]11;rgb:'..color..'\007:echo &background\n')
- screen:expect(string.format([[
- {1: } |
- {4:~ }|
- {4:~ }|
- {4:~ }|
- {5:[No Name] 0,0-1 All}|
- %-5s |
- {3:-- TERMINAL --} |
- ]], bg))
+ feed_data('\027]11;rgb:'..color..'\007')
+ -- Retry until the terminal response is handled.
+ retry(100, nil, function()
+ feed_data(':echo &background\n')
+ screen:expect({
+ timeout=40,
+ grid=string.format([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] 0,0-1 All}|
+ %-5s |
+ {3:-- TERMINAL --} |
+ ]], bg)
+ })
+ end)
end)
end
diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua
index c0ce656bb1..ad70b3d14f 100644
--- a/test/functional/terminal/window_split_tab_spec.lua
+++ b/test/functional/terminal/window_split_tab_spec.lua
@@ -69,7 +69,7 @@ describe(':terminal', function()
end)
it('forwards resize request to the program', function()
- feed([[<C-\><C-N>G:]]) -- Go to cmdline-mode, so cursor is at bottom.
+ feed([[<C-\><C-N>G]])
local w1, h1 = screen._width - 3, screen._height - 2
local w2, h2 = w1 - 6, h1 - 3
@@ -92,16 +92,16 @@ describe(':terminal', function()
|
|
|
+ ^ |
|
- :^ |
]])
screen:try_resize(w2, h2)
screen:expect([[
tty ready |
rows: 7, cols: 47 |
rows: 4, cols: 41 |
- {2: } |
- :^ |
+ {2:^ } |
+ |
]])
end)
end)
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index 1568b7816e..45808b3b1b 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -975,8 +975,6 @@ describe('Expressions coloring support', function()
]])
funcs.setreg('a', {'\192'})
feed('<C-r>="<C-r><C-r>a"<C-r><C-r>a"foo"')
- -- TODO(ZyX-I): Parser highlighting should not override special character
- -- highlighting.
screen:expect([[
|
{EOB:~ }|
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index 5d112d7f35..5d563895d6 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -116,6 +116,31 @@ local function test_cmdline(linegrid)
}}}
end)
+ it('from normal mode when : is mapped', function()
+ command('nnoremap ; :')
+
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {3:n }|
+ |
+ ]]}
+
+ feed(';')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {3:c }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}}
+ end)
+
it('but not with scrolled messages', function()
screen:try_resize(35,10)
feed(':echoerr doesnotexist<cr>')
@@ -600,6 +625,137 @@ local function test_cmdline(linegrid)
pos = 12,
}}}
end)
+
+ it('works together with ext_popupmenu', function()
+ local expected = {
+ {'define', '', '', ''},
+ {'jump', '', '', ''},
+ {'list', '', '', ''},
+ {'place', '', '', ''},
+ {'undefine', '', '', ''},
+ {'unplace', '', '', ''},
+ }
+
+ command('set wildmode=full')
+ command('set wildmenu')
+ screen:set_option('ext_popupmenu', true)
+ feed(':sign <tab>')
+
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign define"}},
+ pos = 11,
+ }}, popupmenu={items=expected, pos=0, anchor={-1, 0, 5}}}
+
+ feed('<tab>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign jump"}},
+ pos = 9,
+ }}, popupmenu={items=expected, pos=1, anchor={-1, 0, 5}}}
+
+ feed('<left><left>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign "}},
+ pos = 5,
+ }}, popupmenu={items=expected, pos=-1, anchor={-1, 0, 5}}}
+
+ feed('<right>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign define"}},
+ pos = 11,
+ }}, popupmenu={items=expected, pos=0, anchor={-1, 0, 5}}}
+
+ feed('a')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign definea"}},
+ pos = 12,
+ }}}
+ feed('<esc>')
+
+ -- check positioning with multibyte char in pattern
+ command("e långfile1")
+ command("sp långfile2")
+ feed(':b lå<tab>')
+ screen:expect{grid=[[
+ ^ |
+ {3:långfile2 }|
+ |
+ {2:långfile1 }|
+ |
+ ]], popupmenu={
+ anchor = { -1, 0, 2 },
+ items = {{ "långfile1", "", "", "" }, { "långfile2", "", "", "" }},
+ pos = 0
+ }, cmdline={{
+ content = {{ "b långfile1" }},
+ firstc = ":",
+ pos = 12
+ }}}
+ end)
+
+ it('ext_wildmenu takes precedence over ext_popupmenu', function()
+ local expected = {
+ 'define',
+ 'jump',
+ 'list',
+ 'place',
+ 'undefine',
+ 'unplace',
+ }
+
+ command('set wildmode=full')
+ command('set wildmenu')
+ screen:set_option('ext_wildmenu', true)
+ screen:set_option('ext_popupmenu', true)
+ feed(':sign <tab>')
+
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign define"}},
+ pos = 11,
+ }}, wildmenu_items=expected, wildmenu_pos=0}
+ end)
+
end
-- the representation of cmdline and cmdline_block contents changed with ext_linegrid
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index 3e0370db14..4dc86f1e1f 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -216,10 +216,10 @@ describe('ui/cursor', function()
if m.blinkwait then m.blinkwait = 700 end
end
if m.hl_id then
- m.hl_id = 49
+ m.hl_id = 50
m.attr = {background = Screen.colors.DarkGray}
end
- if m.id_lm then m.id_lm = 50 end
+ if m.id_lm then m.id_lm = 51 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index 8e6756e550..8eb2bbf779 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -62,12 +62,12 @@ describe('Diff mode screen', function()
{1: }5 {3:│}{1: }5 |
{1: }6 {3:│}{1: }6 |
{1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -82,12 +82,12 @@ describe('Diff mode screen', function()
{1: }5 {3:│}{1: }5 |
{1: }6 {3:│}{1: }6 |
{1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -108,12 +108,12 @@ describe('Diff mode screen', function()
{1: }5 {3:│}{1: }5 |
{1: }6 {3:│}{1: }6 |
{1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -128,12 +128,12 @@ describe('Diff mode screen', function()
{1: }5 {3:│}{1: }5 |
{1: }6 {3:│}{1: }6 |
{1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -154,12 +154,12 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{2:------------------}{3:│}{1: }{4:11 }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -174,12 +174,12 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{2:------------------}{3:│}{1: }{4:11 }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -200,12 +200,12 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -220,12 +220,12 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -250,8 +250,8 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -270,8 +270,8 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{4:11 }{3:│}{1: }{2:-----------------}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -296,8 +296,8 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{2:------------------}{3:│}{1: }{4:11 }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -316,8 +316,8 @@ describe('Diff mode screen', function()
{1: }9 {3:│}{1: }9 |
{1: }10 {3:│}{1: }10 |
{1: }{2:------------------}{3:│}{1: }{4:11 }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -546,11 +546,11 @@ int main(int argc, char **argv)
{1: }{2:------------------}{3:│}{1: }{4: values.each do }|
{1: } v.finalize {3:│}{1: } v.finalize |
{1: } end {3:│}{1: } end |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler |
]])
@@ -569,11 +569,11 @@ int main(int argc, char **argv)
{1: } values.each do |{3:│}{1: } values.each do |
{1: } v.finalize {3:│}{1: } v.finalize |
{1: } end {3:│}{1: } end |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
|
]])
@@ -593,11 +593,11 @@ int main(int argc, char **argv)
{1: } values.each do |{3:│}{1: } values.each do |
{1: } v.finalize {3:│}{1: } v.finalize |
{1: } end {3:│}{1: } end |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -612,19 +612,19 @@ int main(int argc, char **argv)
feed(':set diffopt=filler<cr>')
screen:expect([[
{1:+ }{5:^+-- 10 lines: 1···}{3:│}{1:+ }{5:+-- 10 lines: 1··}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -632,19 +632,19 @@ int main(int argc, char **argv)
feed(':set diffopt+=internal<cr>')
screen:expect([[
{1:+ }{5:^+-- 10 lines: 1···}{3:│}{1:+ }{5:+-- 10 lines: 1··}|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -658,19 +658,19 @@ int main(int argc, char **argv)
feed(':set diffopt=filler<cr>')
screen:expect([[
{1:- }^ {3:│}{1:- } |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler |
]])
@@ -678,19 +678,19 @@ int main(int argc, char **argv)
feed(':set diffopt+=internal<cr>')
screen:expect([[
{1:- }^ {3:│}{1:- } |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -706,17 +706,17 @@ int main(int argc, char **argv)
{1: }^a {3:│}{1: }A |
{1: }b {3:│}{1: }b |
{1: }{9:cd }{3:│}{1: }{9:cD}{8:e}{9: }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler,icase |
]])
@@ -726,17 +726,17 @@ int main(int argc, char **argv)
{1: }^a {3:│}{1: }A |
{1: }b {3:│}{1: }b |
{1: }{9:cd }{3:│}{1: }{9:cD}{8:e}{9: }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt+=internal |
]])
@@ -763,12 +763,12 @@ int main(int argc, char **argv)
{1: } return 0; {3:│}{1: } return 0; |
{1: }{2:------------------}{3:│}{1: }{4: } }|
{1: }} {3:│}{1: }} |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler,iwhite |
]])
@@ -786,12 +786,12 @@ int main(int argc, char **argv)
{1: } return 0; {3:│}{1: } return 0; |
{1: }{2:------------------}{3:│}{1: }{4: } }|
{1: }} {3:│}{1: }} |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=filler,iwhite,internal |
]])
@@ -815,14 +815,14 @@ int main(int argc, char **argv)
{1: }cd {3:│}{1: }cd |
{1: }ef {3:│}{1: } |
{1: }{8:xxx}{9: }{3:│}{1: }ef |
- {1: }{6:~ }{3:│}{1: }{8:yyy}{9: }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{1: }{8:yyy}{9: }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
:set diffopt=internal,filler,iblank |
]])
@@ -838,15 +838,15 @@ int main(int argc, char **argv)
{1: } {3:│}{1: } |
{1: }cd {3:│}{1: }ef |
{1: }ef {3:│}{1: }{8:yyy}{9: }|
- {1: }{8:xxx}{9: }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{8:xxx}{9: }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -862,15 +862,15 @@ int main(int argc, char **argv)
{1: } {3:│}{1: } |
{1: }cd {3:│}{1: }ef |
{1: }ef {3:│}{1: }{8:yyy}{9: }|
- {1: }{8:xxx}{9: }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{8:xxx}{9: }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -886,15 +886,15 @@ int main(int argc, char **argv)
{1: } {3:│}{1: } |
{1: }cd {3:│}{1: }ef |
{1: }ef {3:│}{1: }{8:yyy}{9: }|
- {1: }{8:xxx}{9: }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{8:xxx}{9: }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -921,12 +921,12 @@ int main(int argc, char **argv)
{1: }foo {3:│}{1: }foo |
{1: }{2:------------------}{3:│}{1: }{4: }|
{1: }bar {3:│}{1: }bar |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
@@ -945,12 +945,12 @@ int main(int argc, char **argv)
{1: }foo {3:│}{1: }foo |
{1: }{2:------------------}{3:│}{1: }{4: }|
{1: }bar {3:│}{1: }bar |
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
- {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
+ {6:~ }{3:│}{6:~ }|
{7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
: |
]])
diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua
index 10dbc68672..9196c8af40 100644
--- a/test/functional/ui/embed_spec.lua
+++ b/test/functional/ui/embed_spec.lua
@@ -8,7 +8,7 @@ local clear = helpers.clear
local function test_embed(ext_linegrid)
local screen
local function startup(...)
- clear{headless=false, args={...}}
+ clear{args_rm={'--headless'}, args={...}}
-- attach immediately after startup, for early UI
screen = Screen.new(60, 8)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 3be2182eb4..a567fbb941 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -5,6 +5,7 @@ local clear, feed = helpers.clear, helpers.feed
local command, feed_command = helpers.command, helpers.feed_command
local eval = helpers.eval
local eq = helpers.eq
+local insert = helpers.insert
local meths = helpers.meths
local curbufmeths = helpers.curbufmeths
local funcs = helpers.funcs
@@ -29,9 +30,27 @@ describe('floating windows', function()
[10] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta},
[11] = {bold = true, foreground = Screen.colors.Magenta},
[12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue1},
- [13] = {background = Screen.colors.WebGray}
+ [13] = {background = Screen.colors.WebGray},
+ [14] = {foreground = Screen.colors.Brown},
+ [15] = {background = Screen.colors.Grey20},
+ [16] = {background = Screen.colors.Grey20, bold = true, foreground = Screen.colors.Blue1},
+ [17] = {background = Screen.colors.Yellow},
}
+ it('behavior', function()
+ -- Create three windows and test that ":wincmd <direction>" changes to the
+ -- first window, if the previous window is invalid.
+ command('split')
+ meths.open_win(0, true, {width=10, height=10, relative='editor', row=0, col=0})
+ eq(1002, funcs.win_getid())
+ eq('editor', meths.win_get_config(1002).relative)
+ command([[
+ call nvim_win_close(1001, v:false)
+ wincmd j
+ ]])
+ eq(1000, funcs.win_getid())
+ end)
+
local function with_ext_multigrid(multigrid)
local screen
before_each(function()
@@ -42,13 +61,11 @@ describe('floating windows', function()
it('can be created and reconfigured', function()
local buf = meths.create_buf(false,false)
- local win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5})
- meths.win_set_option(win , 'winhl', 'Normal:PMenu')
+ local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5})
local expected_pos = {
[3]={{id=1001}, 'NW', 1, 2, 5, true},
}
-
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -83,7 +100,7 @@ describe('floating windows', function()
end
- meths.win_config(win,0,0,{relative='editor', row=0, col=10})
+ meths.win_set_config(win, {relative='editor', row=0, col=10})
expected_pos[3][4] = 0
expected_pos[3][5] = 10
if multigrid then
@@ -151,20 +168,226 @@ describe('floating windows', function()
end
end)
+ it('return their configuration', function()
+ local buf = meths.create_buf(false, false)
+ local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5})
+ local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20}
+ eq(expected, meths.win_get_config(win))
+
+ eq({relative='', external=false, focusable=true}, meths.win_get_config(0))
+
+ if multigrid then
+ meths.win_set_config(win, {external=true, width=10, height=1})
+ eq({external=true,focusable=true,width=10,height=1,relative=''}, meths.win_get_config(win))
+ end
+ end)
+
+ it('defaults to nonumber and NormalFloat highlight', function()
+ command('set number')
+ command('hi NormalFloat guibg=#333333')
+ feed('ix<cr>y<cr><esc>gg')
+ local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ |
+ ## grid 2
+ {14: 1 }^x |
+ {14: 2 }y |
+ {14: 3 } |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {15:x }|
+ {15:y }|
+ {15: }|
+ {16:~ }|
+ ]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}}
+ else
+ screen:expect([[
+ {14: 1 }^x |
+ {14: 2 }y |
+ {14: 3 } {15:x } |
+ {0:~ }{15:y }{0: }|
+ {0:~ }{15: }{0: }|
+ {0:~ }{16:~ }{0: }|
+ |
+ ]])
+ end
+
+ local buf = meths.create_buf(false, true)
+ meths.win_set_buf(win, buf)
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ |
+ ## grid 2
+ {14: 1 }^x |
+ {14: 2 }y |
+ {14: 3 } |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {15: }|
+ {16:~ }|
+ {16:~ }|
+ {16:~ }|
+ ]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}}
+ else
+ screen:expect([[
+ {14: 1 }^x |
+ {14: 2 }y |
+ {14: 3 } {15: } |
+ {0:~ }{16:~ }{0: }|
+ {0:~ }{16:~ }{0: }|
+ {0:~ }{16:~ }{0: }|
+ |
+ ]])
+ end
+ end)
+
+ it('can have minimum size', function()
+ insert("the background text")
+ local buf = meths.create_buf(false, true)
+ meths.buf_set_lines(buf, 0, -1, true, {'x'})
+ local win = meths.open_win(buf, false, {relative='win', width=1, height=1, row=0, col=4, focusable=false})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ |
+ ## grid 2
+ the background tex^t |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 4
+ {1:x}|
+ ]], float_pos={
+ [4] = {{id = 1002}, "NW", 2, 0, 4, false}
+ }}
+ else
+ screen:expect([[
+ the {1:x}ackground tex^t |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end
+
+ meths.win_set_config(win, {relative='win', row=0, col=15})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ |
+ ## grid 2
+ the background tex^t |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 4
+ {1:x}|
+ ]], float_pos={
+ [4] = {{id = 1002}, "NW", 2, 0, 15, false}
+ }}
+ else
+ screen:expect([[
+ the background {1:x}ex^t |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end
+
+ meths.win_close(win,false)
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ |
+ ## grid 2
+ the background tex^t |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ the background tex^t |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end
+ end)
+
it('API has proper error messages', function()
local buf = meths.create_buf(false,false)
- eq({false, "Invalid options key 'bork'"},
- meth_pcall(meths.open_win,buf, false, 20, 2, {bork=true}))
- eq({false, "'win' option is only valid with relative='win'"},
- meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,win=0}))
- eq({false, "Only one of 'relative' and 'external' should be used"},
- meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,external=true}))
- eq({false, "Invalid value of 'relative' option"},
- meth_pcall(meths.open_win,buf, false, 20, 2, {relative='shell',row=0,col=0}))
- eq({false, "Invalid value of 'anchor' option"},
- meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,anchor='bottom'}))
+ eq({false, "Invalid key 'bork'"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=2,bork=true}))
+ eq({false, "'win' key is only valid with relative='win'"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0}))
+ eq({false, "Only one of 'relative' and 'external' must be used"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,external=true}))
+ eq({false, "Invalid value of 'relative' key"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='shell',row=0,col=0}))
+ eq({false, "Invalid value of 'anchor' key"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,anchor='bottom'}))
eq({false, "All of 'relative', 'row', and 'col' has to be specified at once"},
- meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor'}))
+ meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor'}))
+ eq({false, "'width' key must be a positive Integer"},
+ meth_pcall(meths.open_win,buf, false, {width=-1,height=2,relative='editor'}))
+ eq({false, "'height' key must be a positive Integer"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=-1,relative='editor'}))
+ eq({false, "'height' key must be a positive Integer"},
+ meth_pcall(meths.open_win,buf, false, {width=20,height=0,relative='editor'}))
+ eq({false, "Must specify 'width' and 'height'"},
+ meth_pcall(meths.open_win,buf, false, {relative='editor'}))
end)
it('can be placed relative window or cursor', function()
@@ -210,8 +433,7 @@ describe('floating windows', function()
local buf = meths.create_buf(false,false)
-- no 'win' arg, relative default window
- local win = meths.open_win(buf, false, 20, 2, {relative='win', row=0, col=10})
- meths.win_set_option(win, 'winhl', 'Normal:PMenu')
+ local win = meths.open_win(buf, false, {relative='win', width=20, height=2, row=0, col=10})
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -252,7 +474,7 @@ describe('floating windows', function()
]])
end
- meths.win_config(win, -1, -1, {relative='cursor', row=1, col=-2})
+ meths.win_set_config(win, {relative='cursor', row=1, col=-2})
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -293,7 +515,7 @@ describe('floating windows', function()
]])
end
- meths.win_config(win, -1, -1, {relative='cursor', row=0, col=0, anchor='SW'})
+ meths.win_set_config(win, {relative='cursor', row=0, col=0, anchor='SW'})
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -335,7 +557,7 @@ describe('floating windows', function()
end
- meths.win_config(win, -1, -1, {relative='win', win=oldwin, row=1, col=10, anchor='NW'})
+ meths.win_set_config(win, {relative='win', win=oldwin, row=1, col=10, anchor='NW'})
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -376,7 +598,7 @@ describe('floating windows', function()
]])
end
- meths.win_config(win, -1, -1, {relative='win', win=oldwin, row=3, col=39, anchor='SE'})
+ meths.win_set_config(win, {relative='win', win=oldwin, row=3, col=39, anchor='SE'})
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -417,7 +639,7 @@ describe('floating windows', function()
]])
end
- meths.win_config(win, -1, -1, {relative='win', win=0, row=0, col=50, anchor='NE'})
+ meths.win_set_config(win, {relative='win', win=0, row=0, col=50, anchor='NE'})
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -459,6 +681,66 @@ describe('floating windows', function()
end
end)
+ it('validates cursor even when window is not entered', function()
+ screen:try_resize(30,5)
+ command("set nowrap")
+ insert([[some text that is wider than the window]])
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ |
+ ## grid 2
+ that is wider than the windo^w |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ that is wider than the windo^w |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end
+
+ local buf = meths.create_buf(false,true)
+ meths.buf_set_lines(buf, 0, -1, true, {'some floaty text'})
+ meths.open_win(buf, false, {relative='editor', width=20, height=1, row=3, col=1})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ [2:------------------------------]|
+ |
+ ## grid 2
+ that is wider than the windo^w |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 4
+ {1:some floaty text }|
+ ]], float_pos={
+ [4] = {{id = 1002}, "NW", 1, 3, 1, true}
+ }}
+ else
+ screen:expect([[
+ that is wider than the windo^w |
+ {0:~ }|
+ {0:~ }|
+ {0:~}{1:some floaty text }{0: }|
+ |
+ ]])
+ end
+ end)
+
if multigrid then
pending("supports second UI without multigrid", function()
local session2 = helpers.connect(eval('v:servername'))
@@ -467,8 +749,7 @@ describe('floating windows', function()
screen2:attach(nil, session2)
screen2:set_default_attr_ids(attrs)
local buf = meths.create_buf(false,false)
- local win = meths.open_win(buf, true, 20, 2, {relative='editor', row=2, col=5})
- meths.win_set_option(win, 'winhl', 'Normal:PMenu')
+ meths.open_win(buf, true, {relative='editor', width=20, height=2, row=2, col=5})
local expected_pos = {
[2]={{id=1001}, 'NW', 1, 2, 5}
}
@@ -501,8 +782,7 @@ describe('floating windows', function()
it('handles resized screen', function()
local buf = meths.create_buf(false,false)
meths.buf_set_lines(buf, 0, -1, true, {'such', 'very', 'float'})
- local win = meths.open_win(buf, false, 15, 4, {relative='editor', row=2, col=10})
- meths.win_set_option(win , 'winhl', 'Normal:PMenu')
+ local win = meths.open_win(buf, false, {relative='editor', width=15, height=4, row=2, col=10})
local expected_pos = {
[4]={{id=1002}, 'NW', 1, 2, 10, true},
}
@@ -673,15 +953,15 @@ describe('floating windows', function()
screen:expect([[
|
{0:~ }|
+ {0:~ }{1:such }{0: }|
{0:~ }{1:very }{0: }|
{0:~ }{1:^float }{0: }|
- {0:~ }|
- {0:~ }|
+ {0:~ }{2:~ }{0: }|
|
]])
end
- meths.win_config(win, -1, 3, {})
+ meths.win_set_config(win, {height=3})
feed('gg')
if multigrid then
screen:expect{grid=[[
@@ -1010,10 +1290,126 @@ describe('floating windows', function()
screen:expect([[
|
{0:~ }|
- {0:~ }{1:^such }{0: }|
+ {0:~ }{1:^such }{0: }|
+ {0:~ }{1:very }{0: }|
+ {0:~ }{1:float }{0: }|
{0:~ }|
+ |
+ ]])
+ end
+ end)
+
+ it('does not crash with inccommand #9379', function()
+ local expected_pos = {
+ [3]={{id=1001}, 'NW', 1, 2, 0, true},
+ }
+
+ command("set inccommand=split")
+ command("set laststatus=2")
+
+ local buf = meths.create_buf(false,false)
+ meths.open_win(buf, true, {relative='editor', width=30, height=3, row=2, col=0})
+
+ insert([[
+ foo
+ bar
+ ]])
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] }|
+ |
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {1:foo }|
+ {1:bar }|
+ {1:^ }|
+ ]], float_pos=expected_pos}
+ else
+ screen:expect([[
+ |
{0:~ }|
+ {1:foo }{0: }|
+ {1:bar }{0: }|
+ {1:^ }{0: }|
+ {5:[No Name] }|
+ |
+ ]])
+ end
+
+ feed(':%s/.')
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[Preview] }|
+ :%s/.^ |
+ ## grid 2
+ |
+ ## grid 3
+ {17:f}{1:oo }|
+ {17:b}{1:ar }|
+ {1: }|
+ ]], float_pos=expected_pos}
+ else
+ screen:expect([[
+ |
+ {5:[No Name] }|
+ {17:f}{1:oo } |
+ {17:b}{1:ar } |
+ {1: }{0: }|
+ {5:[Preview] }|
+ :%s/.^ |
+ ]])
+ end
+
+ feed('<Esc>')
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] }|
+ |
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {1:foo }|
+ {1:bar }|
+ {1:^ }|
+ ]], float_pos=expected_pos}
+ else
+ screen:expect([[
+ |
{0:~ }|
+ {1:foo }{0: }|
+ {1:bar }{0: }|
+ {1:^ }{0: }|
+ {5:[No Name] }|
|
]])
end
@@ -1021,7 +1417,7 @@ describe('floating windows', function()
it('does not crash when set cmdheight #9680', function()
local buf = meths.create_buf(false,false)
- meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5})
+ meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5})
command("set cmdheight=2")
eq(1, meths.eval('1'))
end)
@@ -1029,7 +1425,7 @@ describe('floating windows', function()
describe('and completion', function()
before_each(function()
local buf = meths.create_buf(false,false)
- local win = meths.open_win(buf, true, 12, 4, {relative='editor', row=2, col=5})
+ local win = meths.open_win(buf, true, {relative='editor', width=12, height=4, row=2, col=5})
meths.win_set_option(win , 'winhl', 'Normal:ErrorMsg')
if multigrid then
screen:expect{grid=[[
@@ -1266,7 +1662,7 @@ describe('floating windows', function()
]], float_pos={
[3] = {{ id = 1001 }, "NW", 1, 2, 5, true},
}, popupmenu={
- anchor = {0, 2, 3}, items = items, pos = 0
+ anchor = {3, 0, 2}, items = items, pos = 0
}}
else
screen:expect{grid=[[
@@ -1278,7 +1674,7 @@ describe('floating windows', function()
{0:~ }{12:~ }{0: }|
{3:-- INSERT --} |
]], popupmenu={
- anchor = {2, 7}, items = items, pos = 0
+ anchor = {1, 2, 7}, items = items, pos = 0
}}
end
@@ -1348,7 +1744,7 @@ describe('floating windows', function()
]], float_pos={
[3] = {{ id = 1001 }, "NW", 1, 2, 5, true},
}, popupmenu={
- anchor = {0, 0, 2}, items = items, pos = 0
+ anchor = {2, 0, 0}, items = items, pos = 0
}}
else
screen:expect{grid=[[
@@ -1360,7 +1756,7 @@ describe('floating windows', function()
{0:~ }{12:~ }{0: }|
{3:-- INSERT --} |
]], popupmenu={
- anchor = {0, 0}, items = items, pos = 0
+ anchor = {1, 0, 0}, items = items, pos = 0
}}
end
@@ -1402,9 +1798,233 @@ describe('floating windows', function()
]])
end
end)
-
end)
+ describe('float shown after pum', function()
+ local win
+ before_each(function()
+ command('hi NormalFloat guibg=#333333')
+ feed('i')
+ funcs.complete(1, {'aa', 'word', 'longtext'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {3:-- INSERT --} |
+ ## grid 2
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {13:aa }|
+ {1:word }|
+ {1:longtext }|
+ ]], float_pos={
+ [3] = {{id = -1}, "NW", 2, 1, 0, false}}
+ }
+ else
+ screen:expect([[
+ aa^ |
+ {13:aa }{0: }|
+ {1:word }{0: }|
+ {1:longtext }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end
+
+ local buf = meths.create_buf(false,true)
+ meths.buf_set_lines(buf,0,-1,true,{"some info", "about item"})
+ win = meths.open_win(buf, false, {relative='cursor', width=12, height=2, row=1, col=10})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {3:-- INSERT --} |
+ ## grid 2
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {13:aa }|
+ {1:word }|
+ {1:longtext }|
+ ## grid 5
+ {15:some info }|
+ {15:about item }|
+ ]], float_pos={
+ [3] = {{id = -1}, "NW", 2, 1, 0, false},
+ [5] = {{id = 1002}, "NW", 2, 1, 12, true},
+ }}
+ else
+ screen:expect([[
+ aa^ |
+ {13:aa }{15:e info }{0: }|
+ {1:word }{15:ut item }{0: }|
+ {1:longtext }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end
+ end)
+
+ it('and close pum first', function()
+ feed('<c-y>')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {3:-- INSERT --} |
+ ## grid 2
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 5
+ {15:some info }|
+ {15:about item }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 2, 1, 12, true},
+ }}
+ else
+ screen:expect([[
+ aa^ |
+ {0:~ }{15:some info }{0: }|
+ {0:~ }{15:about item }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end
+
+ meths.win_close(win, false)
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {3:-- INSERT --} |
+ ## grid 2
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end
+ end)
+
+ it('and close float first', function()
+ meths.win_close(win, false)
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {3:-- INSERT --} |
+ ## grid 2
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {13:aa }|
+ {1:word }|
+ {1:longtext }|
+ ]], float_pos={
+ [3] = {{id = -1}, "NW", 2, 1, 0, false},
+ }}
+ else
+ screen:expect([[
+ aa^ |
+ {13:aa }{0: }|
+ {1:word }{0: }|
+ {1:longtext }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end
+
+ feed('<c-y>')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {3:-- INSERT --} |
+ ## grid 2
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ aa^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:-- INSERT --} |
+ ]])
+ end
+ end)
+ end)
describe("handles :wincmd", function()
local win
@@ -1415,9 +2035,8 @@ describe('floating windows', function()
command("set hidden")
meths.buf_set_lines(0,0,-1,true,{"x"})
local buf = meths.create_buf(false,false)
- win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5})
+ win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5})
meths.buf_set_lines(buf,0,-1,true,{"y"})
- meths.win_set_option(win , 'winhl', 'Normal:PMenu')
expected_pos = {
[3]={{id=1001}, 'NW', 1, 2, 5, true}
}
@@ -1526,7 +2145,7 @@ describe('floating windows', function()
end)
it("w with focusable=false", function()
- meths.win_config(win, -1, -1, {focusable=false})
+ meths.win_set_config(win, {focusable=false})
expected_pos[3][6] = false
feed("<c-w>wi") -- i to provoke redraw
if multigrid then
@@ -1740,7 +2359,7 @@ describe('floating windows', function()
end)
it("focus by mouse (focusable=false)", function()
- meths.win_config(win, -1, -1, {focusable=false})
+ meths.win_set_config(win, {focusable=false})
meths.buf_set_lines(0, -1, -1, true, {"a"})
expected_pos[3][6] = false
if multigrid then
@@ -2074,39 +2693,6 @@ describe('floating windows', function()
{1:y }|
{2:~ }|
## grid 4
- {1:^y }|
- {2:~ }|
- ]], float_pos=expected_pos}
- else
- screen:expect([[
- {1:^y }|
- {2:~ }|
- {4:[No N}{1:y }{4: }|
- x {2:~ } |
- {0:~ }|
- {5:[No Name] [+] }|
- |
- ]])
- end
-
- feed(":set winhighlight=<cr><c-l>")
- if multigrid then
- screen:expect{grid=[[
- ## grid 1
- [4:----------------------------------------]|
- [4:----------------------------------------]|
- {4:[No Name] [+] }|
- [2:----------------------------------------]|
- [2:----------------------------------------]|
- {5:[No Name] [+] }|
- |
- ## grid 2
- x |
- {0:~ }|
- ## grid 3
- {1:y }|
- {2:~ }|
- ## grid 4
^y |
{0:~ }|
]], float_pos=expected_pos}
@@ -2122,7 +2708,6 @@ describe('floating windows', function()
]])
end
-
feed("<c-w>j")
if multigrid then
screen:expect{grid=[[
@@ -2405,6 +2990,119 @@ describe('floating windows', function()
eq(exited, true)
end)
+ it(':quit two floats in a row', function()
+ -- enter first float
+ feed('<c-w><c-w>')
+ -- enter second float
+ meths.open_win(0, true, {relative='editor', width=20, height=2, row=4, col=8})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ |
+ ## grid 2
+ x |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {1:y }|
+ {2:~ }|
+ ## grid 4
+ {1:^y }|
+ {2:~ }|
+ ]], float_pos={
+ [3] = {{id = 1001}, "NW", 1, 2, 5, true},
+ [4] = {{id = 1002}, "NW", 1, 4, 8, true}
+ }}
+ else
+ screen:expect([[
+ x |
+ {0:~ }|
+ {0:~ }{1:y }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }{1:^y }{0: }|
+ {0:~ }{2:~ }{0: }|
+ |
+ ]])
+ end
+
+ feed(':quit<cr>')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ :quit |
+ ## grid 2
+ x |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {1:^y }|
+ {2:~ }|
+ ]], float_pos={
+ [3] = {{id = 1001}, "NW", 1, 2, 5, true},
+ }}
+ else
+ screen:expect([[
+ x |
+ {0:~ }|
+ {0:~ }{1:^y }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }|
+ {5:[No Name] [+] }|
+ :quit |
+ ]])
+ end
+
+ feed(':quit<cr>')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ :quit |
+ ## grid 2
+ ^x |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ ^x |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :quit |
+ ]])
+ end
+
+ eq(2, eval('1+1'))
+ end)
+
it("o (:only) non-float", function()
feed("<c-w>o")
if multigrid then
@@ -2659,23 +3357,23 @@ describe('floating windows', function()
x |
{0:~ }|
## grid 3
- {1:^y }|
- {2:~ }|
+ ^y |
+ {0:~ }|
]]}
else
screen:expect([[
x |
{0:~ }|
{5:[No Name] [+] }|
- {1:^y }|
- {2:~ }|
+ ^y |
+ {0:~ }|
{4:[No Name] [+] }|
|
]])
end
if multigrid then
- meths.win_config(0,-1,-1,{external=true})
+ meths.win_set_config(0, {external=true, width=30, height=2})
expected_pos = {[3]={external=true}}
screen:expect{grid=[[
## grid 1
@@ -2693,12 +3391,12 @@ describe('floating windows', function()
{0:~ }|
{0:~ }|
## grid 3
- {1:^y }|
- {2:~ }|
+ ^y |
+ {0:~ }|
]], float_pos=expected_pos}
else
eq({false, "UI doesn't support external windows"},
- meth_pcall(meths.win_config, 0,-1,-1,{external=true}))
+ meth_pcall(meths.win_set_config, 0, {external=true, width=30, height=2}))
return
end
@@ -2717,11 +3415,10 @@ describe('floating windows', function()
x |
{0:~ }|
## grid 3
- {1:^y }|
- {2:~ }|
+ ^y |
+ {0:~ }|
]])
end
-
end)
it('movements with nested split layout', function()
@@ -2786,8 +3483,8 @@ describe('floating windows', function()
4 |
{0:~ }|
## grid 3
- ^5 |
- {0:~ }|
+ {1:^5 }|
+ {2:~ }|
## grid 4
2 |
{0:~ }|
@@ -2802,8 +3499,8 @@ describe('floating windows', function()
screen:expect([[
1 {5:│}2 |
{0:~ }{5:│}{0:~ }|
- {5:[No N}^5 {5:ame] [+] }|
- 3 {0:~ } |
+ {5:[No N}{1:^5 }{5:ame] [+] }|
+ 3 {2:~ } |
{0:~ }{5:│}{0:~ }|
{5:[No Name] [+] [No Name] [+] }|
:enew |
@@ -2895,13 +3592,14 @@ describe('floating windows', function()
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
- {4:[No Name] [+] }|
+ [2:----------------------------------------]|
:tabnext |
## grid 2
^x |
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
## grid 3
{1:y }|
{2:~ }|
@@ -2919,7 +3617,7 @@ describe('floating windows', function()
{0:~ }{1:y }{0: }|
{0:~ }{2:~ }{0: }|
{0:~ }|
- {4:[No Name] [+] }|
+ {0:~ }|
:tabnext |
]])
end
@@ -2940,6 +3638,7 @@ describe('floating windows', function()
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
## grid 3
{1:y }|
{2:~ }|
@@ -2965,7 +3664,8 @@ describe('floating windows', function()
it(":tabnew and :tabnext (external)", function()
if multigrid then
- meths.win_config(win,-1,-1,{external=true})
+ -- also test external window wider than main screen
+ meths.win_set_config(win, {external=true, width=65, height=4})
expected_pos = {[3]={external=true}}
feed(":tabnew<cr>")
screen:expect{grid=[[
@@ -2985,8 +3685,10 @@ describe('floating windows', function()
{0:~ }|
{0:~ }|
## grid 3
- {1:y }|
- {2:~ }|
+ y |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
## grid 4
^ |
{0:~ }|
@@ -2996,7 +3698,7 @@ describe('floating windows', function()
]], float_pos=expected_pos}
else
eq({false, "UI doesn't support external windows"},
- meth_pcall(meths.win_config, 0,-1,-1,{external=true}))
+ meth_pcall(meths.win_set_config, 0, {external=true, width=65, height=4}))
end
feed(":tabnext<cr>")
@@ -3008,16 +3710,19 @@ describe('floating windows', function()
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
- {4:[No Name] [+] }|
+ [2:----------------------------------------]|
:tabnext |
## grid 2
^x |
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
## grid 3
- {1:y }|
- {2:~ }|
+ y |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
## grid 4
|
{0:~ }|
@@ -3036,21 +3741,25 @@ describe('floating windows', function()
[4:----------------------------------------]|
[4:----------------------------------------]|
[4:----------------------------------------]|
- {4:[No Name] }|
+ [4:----------------------------------------]|
:tabnext |
## grid 2
x |
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
## grid 3
- {1:y }|
- {2:~ }|
+ y |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
## grid 4
^ |
{0:~ }|
{0:~ }|
{0:~ }|
+ {0:~ }|
]], float_pos=expected_pos}
end
end)
@@ -3063,6 +3772,5 @@ describe('floating windows', function()
describe('without ext_multigrid', function()
with_ext_multigrid(false)
end)
-
end)
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 943cbcef56..5fa299bed9 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -39,11 +39,11 @@ describe("folded lines", function()
screen:expect([[
{7:+ }{5: 1 +-- 2 lines: ·························}|
{7:+ }{5: 0 ^+-- 2 lines: ·························}|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
:set foldcolumn=2 |
]])
end)
@@ -93,12 +93,12 @@ describe("folded lines", function()
feed_command("set number foldcolumn=2")
screen:expect([[
{7:+ }{5: 1 ^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة···········}|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
- {7: }{1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
:set number foldcolumn=2 |
]])
@@ -106,12 +106,12 @@ describe("folded lines", function()
feed_command("set rightleft")
screen:expect([[
{5:+-- 2 lines: å ······················^· 1 }{7: +}|
- {1: ~}{7: }|
- {1: ~}{7: }|
- {1: ~}{7: }|
- {1: ~}{7: }|
- {1: ~}{7: }|
- {1: ~}{7: }|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
:set rightleft |
]])
@@ -178,7 +178,7 @@ describe("folded lines", function()
{1::}set foldmethod=manual |
{1::}let x = 1 |
{1::}^ |
- {1::~ }|
+ {1:~ }|
{3:[Command Line] }|
: |
]])
@@ -189,8 +189,8 @@ describe("folded lines", function()
{2:[No Name] }|
{1::}{5:^+-- 2 lines: set foldmethod=manual·········}|
{1::} |
- {1::~ }|
- {1::~ }|
+ {1:~ }|
+ {1:~ }|
{3:[Command Line] }|
: |
]])
@@ -214,7 +214,7 @@ describe("folded lines", function()
{1:/}alpha |
{1:/}{6:omega} |
{1:/}^ |
- {1:/~ }|
+ {1:~ }|
{3:[Command Line] }|
/ |
]])
@@ -224,9 +224,9 @@ describe("folded lines", function()
|
{2:[No Name] }|
{1:/}{5:^+-- 3 lines: alpha·························}|
- {1:/~ }|
- {1:/~ }|
- {1:/~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
{3:[Command Line] }|
/ |
]])
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 3ee3f173d6..85b5aed2f8 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -748,6 +748,66 @@ describe('CursorLine highlight', function()
]])
end)
+ it('always updated. vim-patch:8.1.0849', function()
+ local screen = Screen.new(50,5)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.SlateBlue},
+ [2] = {bold = true, foreground = Screen.colors.Brown},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.Gray90},
+ [5] = {background = Screen.colors.Gray90},
+ [6] = {bold = true, foreground = Screen.colors.Blue1},
+ [7] = {background = Screen.colors.LightRed},
+ [8] = {foreground = Screen.colors.Brown},
+ })
+ screen:attach()
+ command('set cursorline relativenumber')
+ command('call setline(1, ["","1","2","3",""])')
+ feed('Gy3k')
+ screen:expect([[
+ {2: 0 }{5:^1 }|
+ {8: 1 }2 |
+ {8: 2 }3 |
+ {8: 3 } |
+ 4 lines yanked |
+ ]])
+ feed('jj')
+ screen:expect([[
+ {8: 2 }1 |
+ {8: 1 }2 |
+ {2: 0 }{5:^3 }|
+ {8: 1 } |
+ 4 lines yanked |
+ ]])
+ end)
+
+ it('with visual area. vim-patch:8.1.1001', function()
+ local screen = Screen.new(50,5)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.SlateBlue},
+ [2] = {bold = true, foreground = Screen.colors.Brown},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.Gray90},
+ [5] = {background = Screen.colors.Gray90},
+ [6] = {bold = true, foreground = Screen.colors.Blue1},
+ [7] = {background = Screen.colors.LightRed},
+ [8] = {foreground = Screen.colors.Brown},
+ [9] = {background = Screen.colors.LightGrey},
+ [10] = {bold = true},
+ })
+ screen:attach()
+ command('set cursorline')
+ command('call setline(1, repeat(["abc"], 50))')
+ feed('V<C-f>zbkkjk')
+ screen:expect([[
+ {9:abc} |
+ ^a{9:bc} |
+ abc |
+ abc |
+ {10:-- VISUAL LINE --} |
+ ]])
+ end)
+
it('with split-windows in diff-mode', function()
local screen = Screen.new(50,12)
screen:set_default_attr_ids({
@@ -781,9 +841,9 @@ describe('CursorLine highlight', function()
{1: }extra line! {4:│}{1: }extra line! |
{1: }last line ... {4:│}{1: }last line ... |
{1: } {4:│}{1: } |
- {1: }{8:~ }{4:│}{1: }{8:~ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {8:~ }{4:│}{8:~ }|
+ {8:~ }{4:│}{8:~ }|
+ {8:~ }{4:│}{8:~ }|
{4:[No Name] [+] }{9:[No Name] [+] }|
|
]])
@@ -796,9 +856,9 @@ describe('CursorLine highlight', function()
{1: }extra line! {4:│}{1: }extra line! |
{1: }last line ... {4:│}{1: }last line ... |
{1: }{7: }{4:│}{1: }{7:^ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {8:~ }{4:│}{8:~ }|
+ {8:~ }{4:│}{8:~ }|
+ {8:~ }{4:│}{8:~ }|
{4:[No Name] [+] }{9:[No Name] [+] }|
|
]])
@@ -815,9 +875,9 @@ describe('CursorLine highlight', function()
{1: }extra line! {4:│}{1: }extra line! |
{1: }last line ... {4:│}{1: }last line ... |
{1: } {4:│}{1: } |
- {1: }{8:~ }{4:│}{1: }{8:~ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
- {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {8:~ }{4:│}{8:~ }|
+ {8:~ }{4:│}{8:~ }|
+ {8:~ }{4:│}{8:~ }|
{4:[No Name] [+] }{9:[No Name] [+] }|
|
]], {
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index c215ece2f2..4f243e6413 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -2607,3 +2607,30 @@ it(':substitute with inccommand during :terminal activity', function()
end)
end)
+
+it(':substitute with inccommand, timer-induced :redraw #9777', function()
+ local screen = Screen.new(30,12)
+ clear()
+ command('set cmdwinheight=3')
+ command('call timer_start(10, {-> execute("redraw")}, {"repeat":-1})')
+ command('call timer_start(10, {-> execute("redrawstatus")}, {"repeat":-1})')
+ common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz')
+
+ feed('gg')
+ feed(':%s/foo/ZZZ')
+ sleep(20) -- Allow some timer activity.
+ screen:expect([[
+ {12:ZZZ} bar baz |
+ bar baz fox |
+ bar {12:ZZZ} baz |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |1| {12:ZZZ} bar baz |
+ |3| bar {12:ZZZ} baz |
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/foo/ZZZ^ |
+ ]])
+end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 850efed282..121cbe47d6 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -30,6 +30,24 @@ describe('mappings', function()
add_mapping('<c-s-a-d-up>', '<c-s-a-d-up>')
add_mapping('<c-d-a>', '<c-d-a>')
add_mapping('<d-1>', '<d-1>')
+ add_mapping('<khome>','<khome>')
+ add_mapping('<kup>','<kup>')
+ add_mapping('<kpageup>','<kpageup>')
+ add_mapping('<kleft>','<kleft>')
+ add_mapping('<korigin>','<korigin>')
+ add_mapping('<kright>','<kright>')
+ add_mapping('<kend>','<kend>')
+ add_mapping('<kdown>','<kdown>')
+ add_mapping('<kpagedown>','<kpagedown>')
+ add_mapping('<kinsert>','<kinsert>')
+ add_mapping('<kdel>','<kdel>')
+ add_mapping('<kdivide>','<kdivide>')
+ add_mapping('<kmultiply>','<kmultiply>')
+ add_mapping('<kminus>','<kminus>')
+ add_mapping('<kplus>','<kplus>')
+ add_mapping('<kenter>','<kenter>')
+ add_mapping('<kcomma>','<kcomma>')
+ add_mapping('<kequal>','<kequal>')
end)
it('ok', function()
@@ -48,6 +66,42 @@ describe('mappings', function()
check_mapping('<c-d-a>', '<c-d-a>')
check_mapping('<d-c-a>', '<c-d-a>')
check_mapping('<d-1>', '<d-1>')
+ check_mapping('<khome>','<khome>')
+ check_mapping('<KP7>','<khome>')
+ check_mapping('<kup>','<kup>')
+ check_mapping('<KP8>','<kup>')
+ check_mapping('<kpageup>','<kpageup>')
+ check_mapping('<KP9>','<kpageup>')
+ check_mapping('<kleft>','<kleft>')
+ check_mapping('<KP4>','<kleft>')
+ check_mapping('<korigin>','<korigin>')
+ check_mapping('<KP5>','<korigin>')
+ check_mapping('<kright>','<kright>')
+ check_mapping('<KP6>','<kright>')
+ check_mapping('<kend>','<kend>')
+ check_mapping('<KP1>','<kend>')
+ check_mapping('<kdown>','<kdown>')
+ check_mapping('<KP2>','<kdown>')
+ check_mapping('<kpagedown>','<kpagedown>')
+ check_mapping('<KP3>','<kpagedown>')
+ check_mapping('<kinsert>','<kinsert>')
+ check_mapping('<KP0>','<kinsert>')
+ check_mapping('<kdel>','<kdel>')
+ check_mapping('<KPPeriod>','<kdel>')
+ check_mapping('<kdivide>','<kdivide>')
+ check_mapping('<KPDiv>','<kdivide>')
+ check_mapping('<kmultiply>','<kmultiply>')
+ check_mapping('<KPMult>','<kmultiply>')
+ check_mapping('<kminus>','<kminus>')
+ check_mapping('<KPMinus>','<kminus>')
+ check_mapping('<kplus>','<kplus>')
+ check_mapping('<KPPlus>','<kplus>')
+ check_mapping('<kenter>','<kenter>')
+ check_mapping('<KPEnter>','<kenter>')
+ check_mapping('<kcomma>','<kcomma>')
+ check_mapping('<KPComma>','<kcomma>')
+ check_mapping('<kequal>','<kequal>')
+ check_mapping('<KPEquals>','<kequal>')
end)
end)
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 388c6b3e95..d49d2f0316 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -22,8 +22,129 @@ describe('ui/ext_messages', function()
[6] = {bold = true, reverse = true},
})
end)
+ after_each(function()
+ os.remove('Xtest')
+ end)
+
+ it('msg_show kind=confirm,confirm_sub,emsg,wmsg', function()
+ feed('iline 1\nline 2<esc>')
+
+ -- kind=confirm
+ feed(':echo confirm("test")<cr>')
+ screen:expect{grid=[[
+ line 1 |
+ line ^2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={ {
+ content = {{"\ntest\n[O]k: ", 4}},
+ kind = 'confirm',
+ }}}
+ feed('<cr><cr>')
+ screen:expect{grid=[[
+ line 1 |
+ line ^2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={ {
+ content = { { "\ntest\n[O]k: ", 4 } },
+ kind = "confirm"
+ }, {
+ content = { { "1" } },
+ kind = "echo"
+ }, {
+ content = { { "Press ENTER or type command to continue", 4 } },
+ kind = "return_prompt"
+ } }}
+ feed('<cr><cr>')
+
+ -- kind=confirm_sub
+ feed(':%s/i/X/gc<cr>')
+ screen:expect{grid=[[
+ l{7:i}ne 1 |
+ l{8:i}ne ^2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], attr_ids={
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [5] = {foreground = Screen.colors.Blue1},
+ [6] = {bold = true, reverse = true},
+ [7] = {reverse = true},
+ [8] = {background = Screen.colors.Yellow},
+ }, messages={ {
+ content = { { "replace with X (y/n/a/q/l/^E/^Y)?", 4 } },
+ kind = "confirm_sub"
+ } }}
+ feed('nq')
+
+ -- kind=wmsg (editing readonly file)
+ command('write Xtest')
+ command('set readonly nohls')
+ feed('G$x')
+ screen:expect{grid=[[
+ line 1 |
+ {IGNORE}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], attr_ids={
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [7] = {foreground = Screen.colors.Red},
+ }, messages={ {
+ content = { { "W10: Warning: Changing a readonly file", 7 } },
+ kind = "wmsg"
+ }
+ }}
+
+ -- kind=wmsg ('wrapscan' after search reaches EOF)
+ feed('uG$/i<cr>')
+ screen:expect{grid=[[
+ l^ine 1 |
+ line 2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], attr_ids={
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [5] = {foreground = Screen.colors.Blue1},
+ [6] = {bold = true, reverse = true},
+ [7] = {foreground = Screen.colors.Red},
+ }, messages={ {
+ content = { { "search hit BOTTOM, continuing at TOP", 7 } },
+ kind = "wmsg"
+ } }}
+
+ -- kind=emsg after :throw
+ feed(':throw "foo"<cr>')
+ screen:expect{grid=[[
+ l^ine 1 |
+ line 2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={ {
+ content = { { "Error detected while processing :", 2 } },
+ kind = "emsg"
+ }, {
+ content = { { "E605: Exception not caught: foo", 2 } },
+ kind = ""
+ }, {
+ content = { { "Press ENTER or type command to continue", 4 } },
+ kind = "return_prompt"
+ } }
+ }
+ end)
- it('supports :echoerr', function()
+ it(':echoerr', function()
feed(':echoerr "raa"<cr>')
screen:expect{grid=[[
^ |
@@ -142,7 +263,7 @@ describe('ui/ext_messages', function()
}}
end)
- it('supports showmode', function()
+ it('&showmode', function()
command('imap <f2> <cmd>echomsg "stuff"<cr>')
feed('i')
screen:expect{grid=[[
@@ -179,7 +300,7 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
]], popupmenu={
- anchor = { 2, 0 },
+ anchor = { 1, 2, 0 },
items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
pos = 1
}, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 1 of 2", 4 } }}
@@ -194,7 +315,7 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
]], popupmenu={
- anchor = { 2, 0 },
+ anchor = { 1, 2, 0 },
items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
pos = 1
}, messages={ {
@@ -210,7 +331,7 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
]], popupmenu={
- anchor = { 2, 0 },
+ anchor = { 1, 2, 0 },
items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
pos = 0
}, messages={ {
@@ -230,7 +351,7 @@ describe('ui/ext_messages', function()
}}
end)
- it('supports showmode with recording message', function()
+ it('&showmode with macro-recording message', function()
feed('qq')
screen:expect{grid=[[
^ |
@@ -268,7 +389,7 @@ describe('ui/ext_messages', function()
]])
end)
- it('shows recording message with noshowmode', function()
+ it('shows macro-recording message with &noshowmode', function()
command("set noshowmode")
feed('qq')
-- also check mode to avoid immediate success
@@ -308,7 +429,7 @@ describe('ui/ext_messages', function()
]], mode="normal"}
end)
- it('supports showcmd and ruler', function()
+ it('supports &showcmd and &ruler', function()
command('set showcmd ruler')
screen:expect{grid=[[
^ |
@@ -529,7 +650,7 @@ describe('ui/ext_messages', function()
local screen
before_each(function()
- clear{headless=false, args={"--cmd", "set shortmess-=I"}}
+ clear{args_rm={'--headless'}, args={"--cmd", "set shortmess-=I"}}
screen = Screen.new(80, 24)
screen:attach({rgb=true, ext_messages=true, ext_popupmenu=true})
screen:set_default_attr_ids({
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index c54d608ec4..c5a23e4661 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -11,7 +11,7 @@ describe('ext_multigrid', function()
local screen
before_each(function()
- clear{headless=false, args={'--cmd', 'set laststatus=2'}}
+ clear{args_rm={'--headless'}, args={'--cmd', 'set laststatus=2'}}
screen = Screen.new(53,14)
screen:attach({ext_multigrid=true})
screen:set_default_attr_ids({
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index ed630259be..93192934c7 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -1,10 +1,9 @@
-local global_helpers = require('test.helpers')
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
-local shallowcopy = global_helpers.shallowcopy
+local shallowcopy = helpers.shallowcopy
describe('ui receives option updates', function()
local screen
@@ -115,7 +114,8 @@ describe('ui receives option updates', function()
end)
local function startup_test(headless)
- local expected = reset(nil,{headless=headless,args={'--cmd', 'set guifont=Comic\\ Sans\\ 12'}})
+ local expected = reset(nil, {args_rm=(headless and {} or {'--headless'}),
+ args={'--cmd', 'set guifont=Comic\\ Sans\\ 12'}})
expected.guifont = "Comic Sans 12"
screen:expect(function()
eq(expected, screen.options)
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
index 87b489fd71..38c4527a5b 100644
--- a/test/functional/ui/output_spec.lua
+++ b/test/functional/ui/output_spec.lua
@@ -51,7 +51,8 @@ describe("shell command :!", function()
end)
it("throttles shell-command output greater than ~10KB", function()
- if helpers.skip_fragile(pending) then
+ if helpers.skip_fragile(pending,
+ (os.getenv("TRAVIS") and helpers.os_name() == "osx")) then
return
end
child_session.feed_data(
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 1e6ebb87f5..b457ebebab 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -50,7 +50,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=0,
- anchor={1,0},
+ anchor={1,1,0},
}}
feed('<c-p>')
@@ -66,7 +66,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=-1,
- anchor={1,0},
+ anchor={1,1,0},
}}
-- down moves the selection in the menu, but does not insert anything
@@ -83,7 +83,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=1,
- anchor={1,0},
+ anchor={1,1,0},
}}
feed('<cr>')
@@ -113,7 +113,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=0,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(1,false,false,{})
@@ -129,7 +129,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=1,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(2,true,false,{})
@@ -145,7 +145,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=2,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(0,true,true,{})
@@ -174,7 +174,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=0,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(-1,false,false,{})
@@ -190,7 +190,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=-1,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(1,true,false,{})
@@ -206,7 +206,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=1,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(-1,true,false,{})
@@ -222,7 +222,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=-1,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(0,true,false,{})
@@ -238,7 +238,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=0,
- anchor={1,0},
+ anchor={1,1,0},
}}
meths.select_popupmenu_item(-1,true,true,{})
@@ -269,7 +269,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=0,
- anchor={1,0},
+ anchor={1,1,0},
}}
feed('<f1>')
@@ -285,7 +285,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=2,
- anchor={1,0},
+ anchor={1,1,0},
}}
feed('<f2>')
@@ -301,7 +301,7 @@ describe('ui/ext_popupmenu', function()
]], popupmenu={
items=expected,
pos=-1,
- anchor={1,0},
+ anchor={1,1,0},
}}
feed('<f3>')
@@ -366,6 +366,113 @@ describe('ui/ext_popupmenu', function()
{2:-- INSERT --} |
]])
end)
+
+ it('works with wildoptions=pum', function()
+ screen:try_resize(32,10)
+ command('set wildmenu')
+ command('set wildoptions=pum')
+
+ local wild_expected = {
+ {'define', '', '', ''},
+ {'jump', '', '', ''},
+ {'list', '', '', ''},
+ {'place', '', '', ''},
+ {'undefine', '', '', ''},
+ {'unplace', '', '', ''},
+ }
+
+ feed(':sign ')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign ^ |
+ ]])
+
+ feed('<tab>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign define^ |
+ ]], popupmenu={items=wild_expected, pos=0, anchor={1, 9, 6}}}
+
+ feed('<left>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign ^ |
+ ]], popupmenu={items=wild_expected, pos=-1, anchor={1, 9, 6}}}
+
+ feed('<left>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign unplace^ |
+ ]], popupmenu={items=wild_expected, pos=5, anchor={1, 9, 6}}}
+
+ feed('x')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign unplacex^ |
+ ]])
+ feed('<esc>')
+
+ -- check positioning with multibyte char in pattern
+ command("e långfile1")
+ command("sp långfile2")
+ feed(':b lå<tab>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:långfile2 }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {3:långfile1 }|
+ :b långfile1^ |
+ ]], popupmenu={
+ anchor = {1, 9, 3},
+ items = {{"långfile1", "", "", "" }, {"långfile2", "", "", ""}},
+ pos = 0,
+ }}
+ end)
end)
@@ -1209,7 +1316,7 @@ describe('builtin popupmenu', function()
]])
meths.input_mouse('wheel', 'down', '', 0, 6, 15)
- screen:expect([[
+ screen:expect{grid=[[
choice^ |
{1:~ }|
{n:word }{1: }|
@@ -1218,7 +1325,7 @@ describe('builtin popupmenu', function()
{n:thing }{1: }|
{3:[No Name] [+] }|
{2:-- INSERT --} |
- ]])
+ ]], unchanged=true}
end)
it('works with kind, menu and abbr attributes', function()
@@ -1273,6 +1380,131 @@ describe('builtin popupmenu', function()
]])
end)
+ it('works with wildoptions=pum', function()
+ screen:try_resize(32,10)
+ command('set wildmenu')
+ command('set wildoptions=pum')
+
+ feed(':sign ')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign ^ |
+ ]])
+
+ feed('<tab>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{s: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign define^ |
+ ]])
+
+ feed('<left>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign ^ |
+ ]])
+
+ feed('<left>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{n: define }{1: }|
+ {1:~ }{n: jump }{1: }|
+ {1:~ }{n: list }{1: }|
+ {1:~ }{n: place }{1: }|
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{s: unplace }{1: }|
+ :sign unplace^ |
+ ]])
+
+ feed('x')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :sign unplacex^ |
+ ]])
+
+ feed('<esc>')
+
+ -- check positioning with multibyte char in pattern
+ command("e långfile1")
+ command("sp långfile2")
+ feed(':b lå<tab>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:långfile2 }|
+ |
+ {1:~ }|
+ {1:~ }{s: långfile1 }{1: }|
+ {3:lå}{n: långfile2 }{3: }|
+ :b långfile1^ |
+ ]])
+
+ -- check doesn't crash on screen resize
+ screen:try_resize(20,6)
+ screen:expect([[
+ |
+ {1:~ }|
+ {4:långfile2 }|
+ {s: långfile1 } |
+ {3:lå}{n: långfile2 }{3: }|
+ :b långfile1^ |
+ ]])
+
+ screen:try_resize(50,15)
+ screen:expect([[
+ |
+ {1:~ }|
+ {4:långfile2 }|
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }{s: långfile1 }{1: }|
+ {3:lå}{n: långfile2 }{3: }|
+ :b långfile1^ |
+ ]])
+ end)
+
it("'pumblend' RGB-color", function()
screen:try_resize(60,14)
screen:set_default_attr_ids({
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 8b1b77eb81..a81851cbba 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -71,10 +71,10 @@
-- To help write screen tests, see Screen:snapshot_util().
-- To debug screen tests, see Screen:redraw_debug().
-local global_helpers = require('test.helpers')
-local deepcopy = global_helpers.deepcopy
-local shallowcopy = global_helpers.shallowcopy
local helpers = require('test.functional.helpers')(nil)
+local deepcopy = helpers.deepcopy
+local shallowcopy = helpers.shallowcopy
+local concat_tables = helpers.concat_tables
local request, run_session = helpers.request, helpers.run_session
local eq = helpers.eq
local dedent = helpers.dedent
@@ -259,22 +259,19 @@ local ext_keys = {
'messages', 'showmode', 'showcmd', 'ruler', 'float_pos',
}
--- Asserts that the screen state eventually matches an expected state
+-- Asserts that the screen state eventually matches an expected state.
--
--- This function can either be called with the positional forms
---
--- screen:expect(grid, [attr_ids, attr_ignore])
--- screen:expect(condition)
---
--- or to use additional arguments (or grid and condition at the same time)
--- the keyword form has to be used:
---
--- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end}
+-- Can be called with positional args:
+-- screen:expect(grid, [attr_ids, attr_ignore])
+-- screen:expect(condition)
+-- or keyword args (supports more options):
+-- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end}
--
--
-- grid: Expected screen state (string). Each line represents a screen
-- row. Last character of each row (typically "|") is stripped.
-- Common indentation is stripped.
+-- Lines containing only "{IGNORE}|" are skipped.
-- attr_ids: Expected text attributes. Screen rows are transformed according
-- to this table, as follows: each substring S composed of
-- characters having the same attributes will be substituted by
@@ -416,26 +413,23 @@ screen:redraw_debug() to show all intermediate screen states. ]])
end
end
- -- Extension features. The default expectations should cover the case of
+ -- UI extensions. The default expectations should cover the case of
-- the ext_ feature being disabled, or the feature currently not activated
- -- (for instance no external cmdline visible). Some extensions require
+ -- (e.g. no external cmdline visible). Some extensions require
-- preprocessing to represent highlights in a reproducible way.
local extstate = self:_extstate_repr(attr_state)
-
- -- convert assertion errors into invalid screen state descriptions
- local status, res = pcall(function()
- for _, k in ipairs(ext_keys) do
- -- Empty states is considered the default and need not be mentioned
- if not (expected[k] == nil and isempty(extstate[k])) then
- eq(expected[k], extstate[k], k)
+ if expected['mode'] ~= nil then
+ extstate['mode'] = self.mode
+ end
+ -- Convert assertion errors into invalid screen state descriptions.
+ for _, k in ipairs(concat_tables(ext_keys, {'mode'})) do
+ -- Empty states are considered the default and need not be mentioned.
+ if (not (expected[k] == nil and isempty(extstate[k]))) then
+ local status, res = pcall(eq, expected[k], extstate[k], k)
+ if not status then
+ return (tostring(res)..'\nHint: full state of "'..k..'":\n '..inspect(extstate[k]))
end
end
- if expected.mode ~= nil then
- eq(expected.mode, self.mode, "mode")
- end
- end)
- if not status then
- return tostring(res)
end
end, expected)
end
@@ -937,10 +931,7 @@ function Screen:_handle_option_set(name, value)
end
function Screen:_handle_popupmenu_show(items, selected, row, col, grid)
- if (not self._options.ext_multigrid) and grid == 1 then
- grid = nil
- end
- self.popupmenu = {items=items, pos=selected, anchor={row, col, grid}}
+ self.popupmenu = {items=items, pos=selected, anchor={grid, row, col}}
end
function Screen:_handle_popupmenu_select(selected)
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index a46670d8a2..65ae124353 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -163,12 +163,14 @@ describe('search highlighting', function()
]])
feed('/foo')
sleep(50) -- Allow some terminal activity.
- screen:expect([[
- {3:foo} bar baz {3:│}xxx |
- bar baz {2:foo} {3:│}xxx |
- bar {2:foo} baz {3:│}xxx |
- {3:│}xxx |
- {1:~ }{3:│}xxx |
+ -- NB: in earlier versions terminal output was redrawn during cmdline mode.
+ -- For now just assert that the screens remain unchanged.
+ screen:expect([[
+ {3:foo} bar baz {3:│} |
+ bar baz {2:foo} {3:│} |
+ bar {2:foo} baz {3:│} |
+ {3:│} |
+ {1:~ }{3:│} |
{5:[No Name] [+] }{3:term }|
/foo^ |
]], { [1] = {bold = true, foreground = Screen.colors.Blue1},
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index bc0e2e3799..74019046c0 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -43,15 +43,15 @@ describe('Signs', function()
{2: }b |
{1:>>}c |
{2: }^ |
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
@@ -72,14 +72,14 @@ describe('Signs', function()
{1:>>}b |
{2: }c |
{2: } |
- {2: }{0:~ }|
- {2: }{0:~ }|
+ {0:~ }|
+ {0:~ }|
{4:[No Name] [+] }|
{2: }{3:a }|
{1:>>}b |
{2: }c |
{2: } |
- {2: }{0:~ }|
+ {0:~ }|
{5:[No Name] [+] }|
|
]])
@@ -102,31 +102,140 @@ describe('Signs', function()
{2: }{6: 2 }{8:b }|
{2: }{7: 3 }c |
{1:>>}{7: 4 }{8:^ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
+ it('multiple signs #9295', function()
+ feed('ia<cr>b<cr>c<cr><esc>')
+ command('set number')
+ command('set signcolumn=yes:2')
+ command('sign define pietSearch text=>> texthl=Search')
+ command('sign define pietError text=XX texthl=Error')
+ command('sign define pietWarn text=WW texthl=Warning')
+ command('sign place 1 line=1 name=pietSearch buffer=1')
+ command('sign place 2 line=1 name=pietError buffer=1')
+ -- Line 2 helps checking that signs in the same line are ordered by Id.
+ command('sign place 4 line=2 name=pietSearch buffer=1')
+ command('sign place 3 line=2 name=pietError buffer=1')
+ -- Line 3 checks that with a limit over the maximum number
+ -- of signs, the ones with the highest Ids are being picked,
+ -- and presented by their sorted Id order.
+ command('sign place 4 line=3 name=pietSearch buffer=1')
+ command('sign place 5 line=3 name=pietWarn buffer=1')
+ command('sign place 3 line=3 name=pietError buffer=1')
+ screen:expect([[
+ {1:>>}XX{6: 1 }a |
+ XX{1:>>}{6: 2 }b |
+ {1:>>}WW{6: 3 }c |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ -- With the default setting, we get the sign with the top id.
+ command('set signcolumn=yes:1')
+ screen:expect([[
+ XX{6: 1 }a |
+ {1:>>}{6: 2 }b |
+ WW{6: 3 }c |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ -- "auto:3" accommodates all the signs we defined so far.
+ command('set signcolumn=auto:3')
+ screen:expect([[
+ {1:>>}XX{2: }{6: 1 }a |
+ XX{1:>>}{2: }{6: 2 }b |
+ XX{1:>>}WW{6: 3 }c |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ -- Check "yes:9".
+ command('set signcolumn=yes:9')
+ screen:expect([[
+ {1:>>}XX{2: }{6: 1 }a |
+ XX{1:>>}{2: }{6: 2 }b |
+ XX{1:>>}WW{2: }{6: 3 }c |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ -- Check "auto:N" larger than the maximum number of signs defined in
+ -- a single line (same result as "auto:3").
+ command('set signcolumn=auto:4')
+ screen:expect{grid=[[
+ {1:>>}XX{2: }{6: 1 }a |
+ XX{1:>>}{2: }{6: 2 }b |
+ XX{1:>>}WW{6: 3 }c |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
+
it('can have 32bit sign IDs', function()
command('sign define piet text=>> texthl=Search')
command('sign place 100000 line=1 name=piet buffer=1')
feed(':sign place<cr>')
screen:expect([[
{1:>>} |
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{4: }|
:sign place |
{9:--- Signs ---} |
@@ -139,18 +248,18 @@ describe('Signs', function()
feed('<cr>')
screen:expect([[
{1:>>}^ |
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
- {2: }{0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua
index 7cd09fb222..f4b80fd428 100644
--- a/test/functional/ui/wildmode_spec.lua
+++ b/test/functional/ui/wildmode_spec.lua
@@ -1,7 +1,6 @@
-local global_helpers = require('test.helpers')
-local shallowcopy = global_helpers.shallowcopy
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
+local shallowcopy = helpers.shallowcopy
local clear, feed, command = helpers.clear, helpers.feed, helpers.command
local iswin = helpers.iswin
local funcs = helpers.funcs
@@ -29,8 +28,7 @@ describe("'wildmenu'", function()
end
it(':sign <tab> shows wildmenu completions', function()
- command('set wildmode=full')
- command('set wildmenu')
+ command('set wildmenu wildmode=full')
feed(':sign <tab>')
screen:expect([[
|
@@ -96,10 +94,12 @@ describe("'wildmenu'", function()
feed([[<C-\><C-N>gg]])
feed([[:sign <Tab>]]) -- Invoke wildmenu.
+ -- NB: in earlier versions terminal output was redrawn during cmdline mode.
+ -- For now just assert that the screen remains unchanged.
expect_stay_unchanged{grid=[[
- foo |
- foo |
- foo |
+ |
+ |
+ |
define jump list > |
:sign define^ |
]]}
@@ -201,14 +201,28 @@ describe('command line completion', function()
]])
end)
+ it('completes env var names #9681', function()
+ clear()
+ screen:attach()
+ command('let $XTEST_1 = "foo" | let $XTEST_2 = "bar"')
+ command('set wildmenu wildmode=full')
+ feed(':!echo $XTEST_<tab>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {2:XTEST_1}{3: XTEST_2 }|
+ :!echo $XTEST_1^ |
+ ]])
+ end)
+
it('completes (multibyte) env var names #9655', function()
clear({env={
['XTEST_1AaあB']='foo',
['XTEST_2']='bar',
}})
screen:attach()
- command('set wildmode=full')
- command('set wildmenu')
+ command('set wildmenu wildmode=full')
feed(':!echo $XTEST_<tab>')
screen:expect([[
|
diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua
index cd1b312265..9d4cb325d9 100644
--- a/test/functional/viml/completion_spec.lua
+++ b/test/functional/viml/completion_spec.lua
@@ -904,9 +904,9 @@ describe('completion', function()
|
{8:[No Name] }|
{0::}foo faa fee f^ |
- {0::~ }|
- {0::~ }|
- {0::~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{9:[Command Line] }|
{3:-- INSERT --} |
]] )
@@ -915,9 +915,9 @@ describe('completion', function()
|
{8:[No Name] }|
{0::}foo faa fee foo^ |
- {0::~ }{2: foo }{0: }|
- {0::~ }{1: faa }{0: }|
- {0::~ }{1: fee }{0: }|
+ {0:~ }{2: foo }{0: }|
+ {0:~ }{1: faa }{0: }|
+ {0:~ }{1: fee }{0: }|
{9:[Command Line] }|
{3:-- Keyword Local completion (^N^P) }{4:match 1 of 3} |
]])
@@ -926,9 +926,9 @@ describe('completion', function()
|
{8:[No Name] }|
{0::}foo faa fee foo |
- {0::~ }|
- {0::~ }|
- {0::~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{9:[Command Line] }|
:foo faa fee foo^ |
]])
@@ -1072,4 +1072,83 @@ describe('completion', function()
set complete&vim completeopt&vim
]])
end)
+
+ it('CompleteChanged autocommand', function()
+ curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar', ''})
+ source([[
+ set complete=. completeopt=noinsert,noselect,menuone
+ function! OnPumChange()
+ let g:event = copy(v:event)
+ let g:item = get(v:event, 'completed_item', {})
+ let g:word = get(g:item, 'word', v:null)
+ endfunction
+ autocmd! CompleteChanged * :call OnPumChange()
+ call cursor(4, 1)
+ ]])
+
+ feed('Sf<C-N>')
+ screen:expect([[
+ foo |
+ bar |
+ foobar |
+ f^ |
+ {1:foo }{0: }|
+ {1:foobar }{0: }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{5:Back at original} |
+ ]])
+ eq({completed_item = {}, width = 15,
+ height = 2, size = 2,
+ col = 0, row = 4, scrollbar = false},
+ eval('g:event'))
+ feed('<C-N>')
+ screen:expect([[
+ foo |
+ bar |
+ foobar |
+ foo^ |
+ {2:foo }{0: }|
+ {1:foobar }{0: }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{4:match 1 of 2} |
+ ]])
+ eq('foo', eval('g:word'))
+ feed('<C-N>')
+ screen:expect([[
+ foo |
+ bar |
+ foobar |
+ foobar^ |
+ {1:foo }{0: }|
+ {2:foobar }{0: }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{4:match 2 of 2} |
+ ]])
+ eq('foobar', eval('g:word'))
+ feed('<up>')
+ screen:expect([[
+ foo |
+ bar |
+ foobar |
+ foobar^ |
+ {2:foo }{0: }|
+ {1:foobar }{0: }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{4:match 1 of 2} |
+ ]])
+ eq('foo', eval('g:word'))
+ feed('<down>')
+ screen:expect([[
+ foo |
+ bar |
+ foobar |
+ foobar^ |
+ {1:foo }{0: }|
+ {2:foobar }{0: }|
+ {0:~ }|
+ {3:-- Keyword completion (^N^P) }{4:match 2 of 2} |
+ ]])
+ eq('foobar', eval('g:word'))
+ feed('<esc>')
+ end)
end)
diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua
index 6c5a63e6b1..c5390cbd12 100644
--- a/test/functional/viml/errorlist_spec.lua
+++ b/test/functional/viml/errorlist_spec.lua
@@ -26,7 +26,7 @@ describe('setqflist()', function()
it('sets w:quickfix_title', function()
setqflist({''}, 'r', 'foo')
command('copen')
- eq(':foo', get_cur_win_var('quickfix_title'))
+ eq('foo', get_cur_win_var('quickfix_title'))
setqflist({''}, 'r', {['title'] = 'qf_title'})
eq('qf_title', get_cur_win_var('quickfix_title'))
end)
@@ -34,7 +34,7 @@ describe('setqflist()', function()
it('allows string {what} for backwards compatibility', function()
setqflist({}, 'r', '5')
command('copen')
- eq(':5', get_cur_win_var('quickfix_title'))
+ eq('5', get_cur_win_var('quickfix_title'))
end)
it('requires a dict for {what}', function()
@@ -64,8 +64,8 @@ describe('setloclist()', function()
setloclist(1, {}, 'r', 'foo')
setloclist(2, {}, 'r', 'bar')
command('lopen')
- eq(':bar', get_cur_win_var('quickfix_title'))
+ eq('bar', get_cur_win_var('quickfix_title'))
command('lclose | wincmd w | lopen')
- eq(':foo', get_cur_win_var('quickfix_title'))
+ eq('foo', get_cur_win_var('quickfix_title'))
end)
end)
diff --git a/test/helpers.lua b/test/helpers.lua
index 3a766b99f5..2a6285e685 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -1,8 +1,10 @@
require('vim.compat')
+local shared = require('vim.shared')
local assert = require('luassert')
local luv = require('luv')
local lfs = require('lfs')
local relpath = require('pl.path').relpath
+local Paths = require('test.config.paths')
local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote)
local function shell_quote(str)
@@ -235,6 +237,11 @@ local function hasenv(name)
return nil
end
+local function deps_prefix()
+ local env = os.getenv('DEPS_PREFIX')
+ return (env and env ~= '') and env or '.deps/usr'
+end
+
local tests_skipped = 0
local function check_cores(app, force)
@@ -263,7 +270,7 @@ local function check_cores(app, force)
else
initial_path = '.'
re = '/core[^/]*$'
- exc_re = { '^/%.deps$', local_tmpdir, '^/%node_modules$' }
+ exc_re = { '^/%.deps$', '^/%'..deps_prefix()..'$', local_tmpdir, '^/%node_modules$' }
db_cmd = gdb_db_cmd
random_skip = true
end
@@ -329,30 +336,6 @@ local function shallowcopy(orig)
return copy
end
-local deepcopy
-
-local function id(v)
- return v
-end
-
-local deepcopy_funcs = {
- table = function(orig)
- local copy = {}
- for k, v in pairs(orig) do
- copy[deepcopy(k)] = deepcopy(v)
- end
- return copy
- end,
- number = id,
- string = id,
- ['nil'] = id,
- boolean = id,
-}
-
-deepcopy = function(orig)
- return deepcopy_funcs[type(orig)](orig)
-end
-
local REMOVE_THIS = {}
local function mergedicts_copy(d1, d2)
@@ -415,6 +398,7 @@ local function updated(d, d2)
return d
end
+-- Concat list-like tables.
local function concat_tables(...)
local ret = {}
for i = 1, select('#', ...) do
@@ -604,24 +588,6 @@ local function fixtbl_rec(tbl)
return fixtbl(tbl)
end
--- From https://github.com/premake/premake-core/blob/master/src/base/table.lua
-local function table_flatten(arr)
- local result = {}
- local function _table_flatten(_arr)
- local n = #_arr
- for i = 1, n do
- local v = _arr[i]
- if type(v) == "table" then
- _table_flatten(v)
- elseif v then
- table.insert(result, v)
- end
- end
- end
- _table_flatten(arr)
- return result
-end
-
local function hexdump(str)
local len = string.len(str)
local dump = ""
@@ -742,7 +708,6 @@ local module = {
check_logs = check_logs,
concat_tables = concat_tables,
dedent = dedent,
- deepcopy = deepcopy,
dictdiff = dictdiff,
eq = eq,
expect_err = expect_err,
@@ -770,7 +735,6 @@ local module = {
repeated_read_cmd = repeated_read_cmd,
shallowcopy = shallowcopy,
sleep = sleep,
- table_flatten = table_flatten,
tmpname = tmpname,
uname = uname,
updated = updated,
@@ -778,5 +742,6 @@ local module = {
write_file = write_file,
trim = trim,
}
+module = shared.tbl_extend('error', module, Paths, shared)
return module
diff --git a/test/symbolic/klee/nvim/keymap.c b/test/symbolic/klee/nvim/keymap.c
index a341a73689..07eb4fa70a 100644
--- a/test/symbolic/klee/nvim/keymap.c
+++ b/test/symbolic/klee/nvim/keymap.c
@@ -165,6 +165,7 @@ static const struct key_name_entry {
{ K_DEL, "Del" },
{ K_DEL, "Delete" }, // Alternative name
{ K_KDEL, "kDel" },
+ { K_KDEL, "KPPeriod" }, // termkey KPPeriod value
{ K_UP, "Up" },
{ K_DOWN, "Down" },
{ K_LEFT, "Left" },
@@ -173,6 +174,14 @@ static const struct key_name_entry {
{ K_XDOWN, "xDown" },
{ K_XLEFT, "xLeft" },
{ K_XRIGHT, "xRight" },
+ { K_KUP, "kUp" },
+ { K_KUP, "KP8" },
+ { K_KDOWN, "kDown" },
+ { K_KDOWN, "KP2" },
+ { K_KLEFT, "kLeft" },
+ { K_KLEFT, "KP4" },
+ { K_KRIGHT, "kRight" },
+ { K_KRIGHT, "KP6" },
{ K_F1, "F1" },
{ K_F2, "F2" },
@@ -225,8 +234,10 @@ static const struct key_name_entry {
{ K_INS, "Insert" },
{ K_INS, "Ins" }, // Alternative name
{ K_KINS, "kInsert" },
+ { K_KINS, "KP0" },
{ K_HOME, "Home" },
{ K_KHOME, "kHome" },
+ { K_KHOME, "KP7" },
{ K_XHOME, "xHome" },
{ K_ZHOME, "zHome" },
{ K_END, "End" },
@@ -236,13 +247,22 @@ static const struct key_name_entry {
{ K_PAGEUP, "PageUp" },
{ K_PAGEDOWN, "PageDown" },
{ K_KPAGEUP, "kPageUp" },
+ { K_KPAGEUP, "KP9" },
{ K_KPAGEDOWN, "kPageDown" },
+ { K_KPAGEDOWN, "KP3" },
+ { K_KORIGIN, "kOrigin" },
+ { K_KORIGIN, "KP5" },
{ K_KPLUS, "kPlus" },
+ { K_KPLUS, "KPPlus" },
{ K_KMINUS, "kMinus" },
+ { K_KMINUS, "KPMinus" },
{ K_KDIVIDE, "kDivide" },
+ { K_KDIVIDE, "KPDiv" },
{ K_KMULTIPLY, "kMultiply" },
+ { K_KMULTIPLY, "KPMult" },
{ K_KENTER, "kEnter" },
+ { K_KENTER, "KPEnter" },
{ K_KPOINT, "kPoint" },
{ K_K0, "k0" },
diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua
index 919a42fbb9..4535d6a0b2 100644
--- a/test/unit/eval/typval_spec.lua
+++ b/test/unit/eval/typval_spec.lua
@@ -1,7 +1,6 @@
local bit = require('bit')
local helpers = require('test.unit.helpers')(after_each)
local eval_helpers = require('test.unit.eval.helpers')
-local global_helpers = require('test.helpers')
local itp = helpers.gen_itp(it)
@@ -14,6 +13,8 @@ local NULL = helpers.NULL
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
local alloc_log_new = helpers.alloc_log_new
+local concat_tables = helpers.concat_tables
+local map = helpers.map
local a = eval_helpers.alloc_logging_helpers
local int = eval_helpers.int
@@ -40,9 +41,6 @@ local callback2tbl = eval_helpers.callback2tbl
local tbl2callback = eval_helpers.tbl2callback
local dict_watchers = eval_helpers.dict_watchers
-local concat_tables = global_helpers.concat_tables
-local map = global_helpers.map
-
local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h',
'./src/nvim/mbyte.h', './src/nvim/garray.h',
'./src/nvim/eval.h', './src/nvim/vim.h',
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 1345fbce17..b5d3dd9f47 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -15,7 +15,6 @@ local dedent = global_helpers.dedent
local neq = global_helpers.neq
local map = global_helpers.map
local eq = global_helpers.eq
-local ok = global_helpers.ok
local trim = global_helpers.trim
-- C constants.
@@ -839,9 +838,6 @@ local module = {
cimport = cimport,
cppimport = cppimport,
internalize = internalize,
- ok = ok,
- eq = eq,
- neq = neq,
ffi = ffi,
lib = lib,
cstr = cstr,
@@ -866,6 +862,7 @@ local module = {
ptr2key = ptr2key,
debug_log = debug_log,
}
+module = global_helpers.tbl_extend('error', module, global_helpers)
return function()
return module
end
diff --git a/test/unit/undo_spec.lua b/test/unit/undo_spec.lua
index f23110b329..616c6fbe3d 100644
--- a/test/unit/undo_spec.lua
+++ b/test/unit/undo_spec.lua
@@ -2,9 +2,7 @@ local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local lfs = require('lfs')
local child_call_once = helpers.child_call_once
-
-local global_helpers = require('test.helpers')
-local sleep = global_helpers.sleep
+local sleep = helpers.sleep
local ffi = helpers.ffi
local cimport = helpers.cimport
@@ -156,12 +154,12 @@ describe('u_write_undo', function()
local file_contents = "testing permissions"
-- Write a text file where the undofile should go
local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
- global_helpers.write_file(correct_name, file_contents, true, false)
+ helpers.write_file(correct_name, file_contents, true, false)
-- Call with `forceit`.
u_write_undo(correct_name, true, file_buffer, buffer_hash)
- local undo_file_contents = global_helpers.read_file(correct_name)
+ local undo_file_contents = helpers.read_file(correct_name)
neq(file_contents, undo_file_contents)
local success, deletion_err = os.remove(correct_name) -- delete the file now that we're done with it.
diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua
index 1b57a24ad5..358e858d61 100644
--- a/test/unit/viml/expressions/lexer_spec.lua
+++ b/test/unit/viml/expressions/lexer_spec.lua
@@ -1,5 +1,4 @@
local helpers = require('test.unit.helpers')(after_each)
-local global_helpers = require('test.helpers')
local itp = helpers.gen_itp(it)
local viml_helpers = require('test.unit.viml.helpers')
@@ -8,6 +7,8 @@ local conv_enum = helpers.conv_enum
local cimport = helpers.cimport
local ffi = helpers.ffi
local eq = helpers.eq
+local shallowcopy = helpers.shallowcopy
+local intchar2lua = helpers.intchar2lua
local conv_ccs = viml_helpers.conv_ccs
local new_pstate = viml_helpers.new_pstate
@@ -15,9 +16,6 @@ local conv_cmp_type = viml_helpers.conv_cmp_type
local pstate_set_str = viml_helpers.pstate_set_str
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
-local shallowcopy = global_helpers.shallowcopy
-local intchar2lua = global_helpers.intchar2lua
-
local lib = cimport('./src/nvim/viml/parser/expressions.h')
local eltkn_type_tab, eltkn_mul_type_tab, eltkn_opt_scope_tab
diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua
index 73388e5dd2..a8f29529ec 100644
--- a/test/unit/viml/expressions/parser_spec.lua
+++ b/test/unit/viml/expressions/parser_spec.lua
@@ -1,5 +1,4 @@
local helpers = require('test.unit.helpers')(after_each)
-local global_helpers = require('test.helpers')
local itp = helpers.gen_itp(it)
local viml_helpers = require('test.unit.viml.helpers')
@@ -14,6 +13,11 @@ local cimport = helpers.cimport
local ffi = helpers.ffi
local neq = helpers.neq
local eq = helpers.eq
+local mergedicts_copy = helpers.mergedicts_copy
+local format_string = helpers.format_string
+local format_luav = helpers.format_luav
+local intchar2lua = helpers.intchar2lua
+local dictdiff = helpers.dictdiff
local conv_ccs = viml_helpers.conv_ccs
local new_pstate = viml_helpers.new_pstate
@@ -21,12 +25,6 @@ local conv_cmp_type = viml_helpers.conv_cmp_type
local pstate_set_str = viml_helpers.pstate_set_str
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
-local mergedicts_copy = global_helpers.mergedicts_copy
-local format_string = global_helpers.format_string
-local format_luav = global_helpers.format_luav
-local intchar2lua = global_helpers.intchar2lua
-local dictdiff = global_helpers.dictdiff
-
local lib = cimport('./src/nvim/viml/parser/expressions.h',
'./src/nvim/syntax.h')
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index 0b90ea52a4..0a41d502ad 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -97,6 +97,8 @@ else()
set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}")
endif()
+set(DEPS_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
+
if(CMAKE_OSX_SYSROOT)
set(DEPS_C_COMPILER "${DEPS_C_COMPILER} -isysroot${CMAKE_OSX_SYSROOT}")
endif()
@@ -110,11 +112,13 @@ if(CMAKE_CROSSCOMPILING AND NOT UNIX)
set(HOSTDEPS_BIN_DIR "${HOSTDEPS_INSTALL_DIR}/bin")
set(HOSTDEPS_LIB_DIR "${HOSTDEPS_INSTALL_DIR}/lib")
set(HOSTDEPS_C_COMPILER "${HOST_C_COMPILER}")
+ set(HOSTDEPS_CXX_COMPILER "${HOST_CXX_COMPILER}")
else()
set(HOSTDEPS_INSTALL_DIR "${DEPS_INSTALL_DIR}")
set(HOSTDEPS_BIN_DIR "${DEPS_BIN_DIR}")
set(HOSTDEPS_LIB_DIR "${DEPS_LIB_DIR}")
set(HOSTDEPS_C_COMPILER "${DEPS_C_COMPILER}")
+ set(HOSTDEPS_CXX_COMPILER "${DEPS_CXX_COMPILER}")
endif()
include(ExternalProject)
diff --git a/third-party/cmake/BuildGperf.cmake b/third-party/cmake/BuildGperf.cmake
index fa8b35f383..71c3cc1eef 100644
--- a/third-party/cmake/BuildGperf.cmake
+++ b/third-party/cmake/BuildGperf.cmake
@@ -36,11 +36,15 @@ function(BuildGperf)
INSTALL_COMMAND "${_gperf_INSTALL_COMMAND}")
endfunction()
+set(GPERF_BUILDARGS CC=${HOSTDEPS_C_COMPILER} CXX=${HOSTDEPS_CXX_COMPILER}
+ LD=${HOSTDEPS_C_COMPILER})
+
if(UNIX OR (MINGW AND CMAKE_CROSSCOMPILING))
BuildGperf(
CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/gperf/configure
- --prefix=${HOSTDEPS_INSTALL_DIR} MAKE=${MAKE_PRG}
+ --prefix=${HOSTDEPS_INSTALL_DIR}
+ MAKE=${MAKE_PRG} ${GPERF_BUILDARGS}
INSTALL_COMMAND ${MAKE_PRG} install)
elseif(MSVC OR MINGW)
diff --git a/unicode/CaseFolding.txt b/unicode/CaseFolding.txt
index cce350f49c..47949f0f99 100644
--- a/unicode/CaseFolding.txt
+++ b/unicode/CaseFolding.txt
@@ -1,6 +1,6 @@
-# CaseFolding-11.0.0.txt
-# Date: 2018-01-31, 08:20:09 GMT
-# © 2018 Unicode®, Inc.
+# CaseFolding-12.0.0.txt
+# Date: 2019-01-22, 08:18:22 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -1227,6 +1227,13 @@ A7B3; C; AB53; # LATIN CAPITAL LETTER CHI
A7B4; C; A7B5; # LATIN CAPITAL LETTER BETA
A7B6; C; A7B7; # LATIN CAPITAL LETTER OMEGA
A7B8; C; A7B9; # LATIN CAPITAL LETTER U WITH STROKE
+A7BA; C; A7BB; # LATIN CAPITAL LETTER GLOTTAL A
+A7BC; C; A7BD; # LATIN CAPITAL LETTER GLOTTAL I
+A7BE; C; A7BF; # LATIN CAPITAL LETTER GLOTTAL U
+A7C2; C; A7C3; # LATIN CAPITAL LETTER ANGLICANA W
+A7C4; C; A794; # LATIN CAPITAL LETTER C WITH PALATAL HOOK
+A7C5; C; 0282; # LATIN CAPITAL LETTER S WITH HOOK
+A7C6; C; 1D8E; # LATIN CAPITAL LETTER Z WITH PALATAL HOOK
AB70; C; 13A0; # CHEROKEE SMALL LETTER A
AB71; C; 13A1; # CHEROKEE SMALL LETTER E
AB72; C; 13A2; # CHEROKEE SMALL LETTER I
diff --git a/unicode/EastAsianWidth.txt b/unicode/EastAsianWidth.txt
index cb489c3404..424735913b 100644
--- a/unicode/EastAsianWidth.txt
+++ b/unicode/EastAsianWidth.txt
@@ -1,6 +1,6 @@
-# EastAsianWidth-11.0.0.txt
-# Date: 2018-05-14, 09:41:59 GMT [KW, LI]
-# © 2018 Unicode®, Inc.
+# EastAsianWidth-12.0.0.txt
+# Date: 2019-01-21, 14:12:58 GMT [KW, LI]
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -500,6 +500,7 @@
0C60..0C61;N # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
0C62..0C63;N # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
0C66..0C6F;N # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+0C77;N # Po TELUGU SIGN SIDDHAM
0C78..0C7E;N # No [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR
0C7F;N # So TELUGU SIGN TUUMU
0C80;N # Lo KANNADA SIGN SPACING CANDRABINDU
@@ -576,20 +577,13 @@
0E5A..0E5B;N # Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT
0E81..0E82;N # Lo [2] LAO LETTER KO..LAO LETTER KHO SUNG
0E84;N # Lo LAO LETTER KHO TAM
-0E87..0E88;N # Lo [2] LAO LETTER NGO..LAO LETTER CO
-0E8A;N # Lo LAO LETTER SO TAM
-0E8D;N # Lo LAO LETTER NYO
-0E94..0E97;N # Lo [4] LAO LETTER DO..LAO LETTER THO TAM
-0E99..0E9F;N # Lo [7] LAO LETTER NO..LAO LETTER FO SUNG
-0EA1..0EA3;N # Lo [3] LAO LETTER MO..LAO LETTER LO LING
+0E86..0E8A;N # Lo [5] LAO LETTER PALI GHA..LAO LETTER SO TAM
+0E8C..0EA3;N # Lo [24] LAO LETTER PALI JHA..LAO LETTER LO LING
0EA5;N # Lo LAO LETTER LO LOOT
-0EA7;N # Lo LAO LETTER WO
-0EAA..0EAB;N # Lo [2] LAO LETTER SO SUNG..LAO LETTER HO SUNG
-0EAD..0EB0;N # Lo [4] LAO LETTER O..LAO VOWEL SIGN A
+0EA7..0EB0;N # Lo [10] LAO LETTER WO..LAO VOWEL SIGN A
0EB1;N # Mn LAO VOWEL SIGN MAI KAN
0EB2..0EB3;N # Lo [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM
-0EB4..0EB9;N # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
-0EBB..0EBC;N # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EB4..0EBC;N # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
0EBD;N # Lo LAO SEMIVOWEL SIGN NYO
0EC0..0EC4;N # Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
0EC6;N # Lm LAO KO LA
@@ -703,7 +697,8 @@
13F8..13FD;N # Ll [6] CHEROKEE SMALL LETTER YE..CHEROKEE SMALL LETTER MV
1400;N # Pd CANADIAN SYLLABICS HYPHEN
1401..166C;N # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA
-166D..166E;N # Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+166D;N # So CANADIAN SYLLABICS CHI SIGN
+166E;N # Po CANADIAN SYLLABICS FULL STOP
166F..167F;N # Lo [17] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS BLACKFOOT W
1680;N # Zs OGHAM SPACE MARK
1681..169A;N # Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH
@@ -862,12 +857,12 @@
1CE2..1CE8;N # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL
1CE9..1CEC;N # Lo [4] VEDIC SIGN ANUSVARA ANTARGOMUKHA..VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL
1CED;N # Mn VEDIC SIGN TIRYAK
-1CEE..1CF1;N # Lo [4] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ANUSVARA UBHAYATO MUKHA
-1CF2..1CF3;N # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
+1CEE..1CF3;N # Lo [6] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ROTATED ARDHAVISARGA
1CF4;N # Mn VEDIC TONE CANDRA ABOVE
1CF5..1CF6;N # Lo [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA
1CF7;N # Mc VEDIC SIGN ATIKRAMA
1CF8..1CF9;N # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE
+1CFA;N # Lo VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA
1D00..1D2B;N # Ll [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL
1D2C..1D6A;N # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI
1D6B..1D77;N # Ll [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G
@@ -1340,8 +1335,7 @@
2B56..2B59;A # So [4] HEAVY OVAL WITH OVAL INSIDE..HEAVY CIRCLED SALTIRE
2B5A..2B73;N # So [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR
2B76..2B95;N # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
-2B98..2BC8;N # So [49] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED
-2BCA..2BFE;N # So [53] TOP HALF BLACK CIRCLE..REVERSED RIGHT ANGLE
+2B98..2BFF;N # So [104] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..HELLSCHREIBER PAUSE SYMBOL
2C00..2C2E;N # Lu [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
2C30..2C5E;N # Ll [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE
2C60..2C7B;N # L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E
@@ -1409,7 +1403,7 @@
2E40;N # Pd DOUBLE HYPHEN
2E41;N # Po REVERSED COMMA
2E42;N # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK
-2E43..2E4E;N # Po [12] DASH WITH LEFT UPTURN..PUNCTUS ELEVATUS MARK
+2E43..2E4F;N # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
2E80..2E99;W # So [26] CJK RADICAL REPEAT..CJK RADICAL RAP
2E9B..2EF3;W # So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
2F00..2FD5;W # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
@@ -1528,7 +1522,8 @@ A788;N # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
A789..A78A;N # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
A78B..A78E;N # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
A78F;N # Lo LATIN LETTER SINOLOGICAL DOT
-A790..A7B9;N # L& [42] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER U WITH STROKE
+A790..A7BF;N # L& [48] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER GLOTTAL U
+A7C2..A7C6;N # L& [5] LATIN CAPITAL LETTER ANGLICANA W..LATIN CAPITAL LETTER Z WITH PALATAL HOOK
A7F7;N # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I
A7F8..A7F9;N # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
A7FA;N # Ll LATIN LETTER SMALL CAPITAL TURNED M
@@ -1579,8 +1574,8 @@ A9B3;N # Mn JAVANESE SIGN CECAK TELU
A9B4..A9B5;N # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9B6..A9B9;N # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
A9BA..A9BB;N # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BC;N # Mn JAVANESE VOWEL SIGN PEPET
-A9BD..A9C0;N # Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON
+A9BC..A9BD;N # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+A9BE..A9C0;N # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
A9C1..A9CD;N # Po [13] JAVANESE LEFT RERENGGAN..JAVANESE TURNED PADA PISELEH
A9CF;N # Lm JAVANESE PANGRANGKEP
A9D0..A9D9;N # Nd [10] JAVANESE DIGIT ZERO..JAVANESE DIGIT NINE
@@ -1644,7 +1639,7 @@ AB28..AB2E;N # Lo [7] ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO
AB30..AB5A;N # Ll [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG
AB5B;N # Sk MODIFIER BREVE WITH INVERTED BREVE
AB5C..AB5F;N # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
-AB60..AB65;N # Ll [6] LATIN SMALL LETTER SAKHA YAT..GREEK LETTER SMALL CAPITAL OMEGA
+AB60..AB67;N # Ll [8] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK
AB70..ABBF;N # Ll [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
ABC0..ABE2;N # Lo [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
ABE3..ABE4;N # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
@@ -1914,6 +1909,7 @@ FFFD;A # So REPLACEMENT CHARACTER
10F46..10F50;N # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
10F51..10F54;N # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED
10F55..10F59;N # Po [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT
+10FE0..10FF6;N # Lo [23] ELYMAIC LETTER ALEPH..ELYMAIC LIGATURE ZAYIN-YODH
11000;N # Mc BRAHMI SIGN CANDRABINDU
11001;N # Mn BRAHMI SIGN ANUSVARA
11002;N # Mc BRAHMI SIGN VISARGA
@@ -2020,6 +2016,7 @@ FFFD;A # So REPLACEMENT CHARACTER
1145B;N # Po NEWA PLACEHOLDER MARK
1145D;N # Po NEWA INSERTION SIGN
1145E;N # Mn NEWA SANDHI MARK
+1145F;N # Lo NEWA LETTER VEDIC ANUSVARA
11480..114AF;N # Lo [48] TIRHUTA ANJI..TIRHUTA LETTER HA
114B0..114B2;N # Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II
114B3..114B8;N # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL
@@ -2062,6 +2059,7 @@ FFFD;A # So REPLACEMENT CHARACTER
116B0..116B5;N # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU
116B6;N # Mc TAKRI SIGN VIRAMA
116B7;N # Mn TAKRI SIGN NUKTA
+116B8;N # Lo TAKRI LETTER ARCHAIC KHA
116C0..116C9;N # Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE
11700..1171A;N # Lo [27] AHOM LETTER KA..AHOM LETTER ALTERNATE BA
1171D..1171F;N # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA
@@ -2083,6 +2081,17 @@ FFFD;A # So REPLACEMENT CHARACTER
118E0..118E9;N # Nd [10] WARANG CITI DIGIT ZERO..WARANG CITI DIGIT NINE
118EA..118F2;N # No [9] WARANG CITI NUMBER TEN..WARANG CITI NUMBER NINETY
118FF;N # Lo WARANG CITI OM
+119A0..119A7;N # Lo [8] NANDINAGARI LETTER A..NANDINAGARI LETTER VOCALIC RR
+119AA..119D0;N # Lo [39] NANDINAGARI LETTER E..NANDINAGARI LETTER RRA
+119D1..119D3;N # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119D4..119D7;N # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB;N # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119DC..119DF;N # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E0;N # Mn NANDINAGARI SIGN VIRAMA
+119E1;N # Lo NANDINAGARI SIGN AVAGRAHA
+119E2;N # Po NANDINAGARI SIGN SIDDHAM
+119E3;N # Lo NANDINAGARI HEADSTROKE
+119E4;N # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A00;N # Lo ZANABAZAR SQUARE LETTER A
11A01..11A0A;N # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A0B..11A32;N # Lo [40] ZANABAZAR SQUARE LETTER KA..ZANABAZAR SQUARE LETTER KSSA
@@ -2096,8 +2105,7 @@ FFFD;A # So REPLACEMENT CHARACTER
11A51..11A56;N # Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE
11A57..11A58;N # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
11A59..11A5B;N # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
-11A5C..11A83;N # Lo [40] SOYOMBO LETTER KA..SOYOMBO LETTER KSSA
-11A86..11A89;N # Lo [4] SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO CLUSTER-INITIAL LETTER SA
+11A5C..11A89;N # Lo [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA
11A8A..11A96;N # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
11A97;N # Mc SOYOMBO SIGN VISARGA
11A98..11A99;N # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
@@ -2150,11 +2158,17 @@ FFFD;A # So REPLACEMENT CHARACTER
11EF3..11EF4;N # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
11EF5..11EF6;N # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
11EF7..11EF8;N # Po [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION
+11FC0..11FD4;N # No [21] TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL FRACTION DOWNSCALING FACTOR KIIZH
+11FD5..11FDC;N # So [8] TAMIL SIGN NEL..TAMIL SIGN MUKKURUNI
+11FDD..11FE0;N # Sc [4] TAMIL SIGN KAACU..TAMIL SIGN VARAAKAN
+11FE1..11FF1;N # So [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA
+11FFF;N # Po TAMIL PUNCTUATION END OF TEXT
12000..12399;N # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
12400..1246E;N # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
12470..12474;N # Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON
12480..12543;N # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
13000..1342E;N # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032
+13430..13438;N # Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
14400..14646;N # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530
16800..16A38;N # Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ
16A40..16A5E;N # Lo [31] MRO LETTER TA..MRO LETTER TEK
@@ -2177,16 +2191,21 @@ FFFD;A # So REPLACEMENT CHARACTER
16E40..16E7F;N # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
16E80..16E96;N # No [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM
16E97..16E9A;N # Po [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH
-16F00..16F44;N # Lo [69] MIAO LETTER PA..MIAO LETTER HHA
+16F00..16F4A;N # Lo [75] MIAO LETTER PA..MIAO LETTER RTE
+16F4F;N # Mn MIAO SIGN CONSONANT MODIFIER BAR
16F50;N # Lo MIAO LETTER NASALIZATION
-16F51..16F7E;N # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F51..16F87;N # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
16F8F..16F92;N # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
16F93..16F9F;N # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
16FE0..16FE1;W # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
-17000..187F1;W # Lo [6130] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F1
+16FE2;W # Po OLD CHINESE HOOK MARK
+16FE3;W # Lm OLD CHINESE ITERATION MARK
+17000..187F7;W # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
18800..18AF2;W # Lo [755] TANGUT COMPONENT-001..TANGUT COMPONENT-755
1B000..1B0FF;W # Lo [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2
1B100..1B11E;W # Lo [31] HENTAIGANA LETTER RE-3..HENTAIGANA LETTER N-MU-MO-2
+1B150..1B152;W # Lo [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
+1B164..1B167;W # Lo [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
1B170..1B2FB;W # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
1BC00..1BC6A;N # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
1BC70..1BC7C;N # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
@@ -2274,11 +2293,22 @@ FFFD;A # So REPLACEMENT CHARACTER
1E01B..1E021;N # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
1E023..1E024;N # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
1E026..1E02A;N # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+1E100..1E12C;N # Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
+1E130..1E136;N # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E137..1E13D;N # Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
+1E140..1E149;N # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE
+1E14E;N # Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ
+1E14F;N # So NYIAKENG PUACHUE HMONG CIRCLED CA
+1E2C0..1E2EB;N # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH
+1E2EC..1E2EF;N # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
+1E2F0..1E2F9;N # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE
+1E2FF;N # Sc WANCHO NGUN SIGN
1E800..1E8C4;N # Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON
1E8C7..1E8CF;N # No [9] MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE
1E8D0..1E8D6;N # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E900..1E943;N # L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA
1E944..1E94A;N # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
+1E94B;N # Lm ADLAM NASALIZATION MARK
1E950..1E959;N # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE
1E95E..1E95F;N # Po [2] ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK
1EC71..1ECAB;N # No [59] INDIC SIYAQ NUMBER ONE..INDIC SIYAQ NUMBER PREFIXED NINE
@@ -2286,6 +2316,9 @@ FFFD;A # So REPLACEMENT CHARACTER
1ECAD..1ECAF;N # No [3] INDIC SIYAQ FRACTION ONE QUARTER..INDIC SIYAQ FRACTION THREE QUARTERS
1ECB0;N # Sc INDIC SIYAQ RUPEE MARK
1ECB1..1ECB4;N # No [4] INDIC SIYAQ NUMBER ALTERNATE ONE..INDIC SIYAQ ALTERNATE LAKH MARK
+1ED01..1ED2D;N # No [45] OTTOMAN SIYAQ NUMBER ONE..OTTOMAN SIYAQ NUMBER NINETY THOUSAND
+1ED2E;N # So OTTOMAN SIYAQ MARRATAN
+1ED2F..1ED3D;N # No [15] OTTOMAN SIYAQ ALTERNATE NUMBER TWO..OTTOMAN SIYAQ FRACTION ONE SIXTH
1EE00..1EE03;N # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL
1EE05..1EE1F;N # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF
1EE21..1EE22;N # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM
@@ -2334,7 +2367,7 @@ FFFD;A # So REPLACEMENT CHARACTER
1F110..1F12D;A # So [30] PARENTHESIZED LATIN CAPITAL LETTER A..CIRCLED CD
1F12E..1F12F;N # So [2] CIRCLED WZ..COPYLEFT SYMBOL
1F130..1F169;A # So [58] SQUARED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
-1F16A..1F16B;N # So [2] RAISED MC SIGN..RAISED MD SIGN
+1F16A..1F16C;N # So [3] RAISED MC SIGN..RAISED MR SIGN
1F170..1F18D;A # So [30] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED SA
1F18E;W # So NEGATIVE SQUARED AB
1F18F..1F190;A # So [2] NEGATIVE SQUARED WC..SQUARE DJ
@@ -2391,27 +2424,32 @@ FFFD;A # So REPLACEMENT CHARACTER
1F6CD..1F6CF;N # So [3] SHOPPING BAGS..BED
1F6D0..1F6D2;W # So [3] PLACE OF WORSHIP..SHOPPING TROLLEY
1F6D3..1F6D4;N # So [2] STUPA..PAGODA
+1F6D5;W # So HINDU TEMPLE
1F6E0..1F6EA;N # So [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE
1F6EB..1F6EC;W # So [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING
1F6F0..1F6F3;N # So [4] SATELLITE..PASSENGER SHIP
-1F6F4..1F6F9;W # So [6] SCOOTER..SKATEBOARD
+1F6F4..1F6FA;W # So [7] SCOOTER..AUTO RICKSHAW
1F700..1F773;N # So [116] ALCHEMICAL SYMBOL FOR QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE
1F780..1F7D8;N # So [89] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F7E0..1F7EB;W # So [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
1F800..1F80B;N # So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
1F810..1F847;N # So [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW
1F850..1F859;N # So [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW
1F860..1F887;N # So [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW
1F890..1F8AD;N # So [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS
1F900..1F90B;N # So [12] CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT
-1F910..1F93E;W # So [47] ZIPPER-MOUTH FACE..HANDBALL
-1F940..1F970;W # So [49] WILTED FLOWER..SMILING FACE WITH SMILING EYES AND THREE HEARTS
+1F90D..1F971;W # So [101] WHITE HEART..YAWNING FACE
1F973..1F976;W # So [4] FACE WITH PARTY HORN AND PARTY HAT..FREEZING FACE
-1F97A;W # So FACE WITH PLEADING EYES
-1F97C..1F9A2;W # So [39] LAB COAT..SWAN
-1F9B0..1F9B9;W # So [10] EMOJI COMPONENT RED HAIR..SUPERVILLAIN
-1F9C0..1F9C2;W # So [3] CHEESE WEDGE..SALT SHAKER
-1F9D0..1F9FF;W # So [48] FACE WITH MONOCLE..NAZAR AMULET
+1F97A..1F9A2;W # So [41] FACE WITH PLEADING EYES..SWAN
+1F9A5..1F9AA;W # So [6] SLOTH..OYSTER
+1F9AE..1F9CA;W # So [29] GUIDE DOG..ICE CUBE
+1F9CD..1F9FF;W # So [51] STANDING PERSON..NAZAR AMULET
+1FA00..1FA53;N # So [84] NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
1FA60..1FA6D;N # So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+1FA70..1FA73;W # So [4] BALLET SHOES..SHORTS
+1FA78..1FA7A;W # So [3] DROP OF BLOOD..STETHOSCOPE
+1FA80..1FA82;W # So [3] YO-YO..PARACHUTE
+1FA90..1FA95;W # So [6] RINGED PLANET..BANJO
20000..2A6D6;W # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
2A6D7..2A6FF;W # Cn [41] <reserved-2A6D7>..<reserved-2A6FF>
2A700..2B734;W # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734
diff --git a/unicode/UnicodeData.txt b/unicode/UnicodeData.txt
index ec32fafbce..d88a60135f 100644
--- a/unicode/UnicodeData.txt
+++ b/unicode/UnicodeData.txt
@@ -640,7 +640,7 @@
027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6
0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
-0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;A7C5;;A7C5
0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
@@ -2809,6 +2809,7 @@
0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C77;TELUGU SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;;
0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;;
0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;;
@@ -3203,14 +3204,24 @@
0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E86;LAO LETTER PALI GHA;Lo;0;L;;;;;N;;;;;
0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E89;LAO LETTER PALI CHA;Lo;0;L;;;;;N;;;;;
0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8C;LAO LETTER PALI JHA;Lo;0;L;;;;;N;;;;;
0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E8E;LAO LETTER PALI NYA;Lo;0;L;;;;;N;;;;;
+0E8F;LAO LETTER PALI TTA;Lo;0;L;;;;;N;;;;;
+0E90;LAO LETTER PALI TTHA;Lo;0;L;;;;;N;;;;;
+0E91;LAO LETTER PALI DDA;Lo;0;L;;;;;N;;;;;
+0E92;LAO LETTER PALI DDHA;Lo;0;L;;;;;N;;;;;
+0E93;LAO LETTER PALI NNA;Lo;0;L;;;;;N;;;;;
0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E98;LAO LETTER PALI DHA;Lo;0;L;;;;;N;;;;;
0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
@@ -3218,13 +3229,17 @@
0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA0;LAO LETTER PALI BHA;Lo;0;L;;;;;N;;;;;
0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EA8;LAO LETTER SANSKRIT SHA;Lo;0;L;;;;;N;;;;;
+0EA9;LAO LETTER SANSKRIT SSA;Lo;0;L;;;;;N;;;;;
0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAC;LAO LETTER PALI LLA;Lo;0;L;;;;;N;;;;;
0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
@@ -3238,6 +3253,7 @@
0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBA;LAO SIGN PALI VIRAMA;Mn;9;NSM;;;;;N;;;;;
0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
@@ -5079,7 +5095,7 @@
166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
-166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;So;0;L;;;;;N;;;;;
166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
@@ -6488,14 +6504,15 @@
1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;;
-1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
-1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
+1CF2;VEDIC SIGN ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
+1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
1CF7;VEDIC SIGN ATIKRAMA;Mc;0;L;;;;;N;;;;;
1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CFA;VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;;
1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
@@ -6638,7 +6655,7 @@
1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
-1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C6;;A7C6
1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;;
@@ -10165,6 +10182,7 @@
2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC9;NEPTUNE FORM TWO;So;0;ON;;;;;N;;;;;
2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;;
@@ -10218,6 +10236,7 @@
2BFC;DOUBLED SYMBOL;So;0;ON;;;;;N;;;;;
2BFD;PASSED SYMBOL;So;0;ON;;;;;N;;;;;
2BFE;REVERSED RIGHT ANGLE;So;0;ON;;;;;Y;;;;;
+2BFF;HELLSCHREIBER PAUSE SYMBOL;So;0;ON;;;;;N;;;;;
2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30;
2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31;
2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32;
@@ -10756,6 +10775,7 @@
2E4C;MEDIEVAL COMMA;Po;0;ON;;;;;N;;;;;
2E4D;PARAGRAPHUS MARK;Po;0;ON;;;;;N;;;;;
2E4E;PUNCTUS ELEVATUS MARK;Po;0;ON;;;;;N;;;;;
+2E4F;CORNISH VERSE DIVIDER;Po;0;ON;;;;;N;;;;;
2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
@@ -14060,7 +14080,7 @@ A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791;
A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790
A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793;
A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792
-A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C4;;A7C4
A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797;
A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796
@@ -14098,6 +14118,17 @@ A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7;
A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6
A7B8;LATIN CAPITAL LETTER U WITH STROKE;Lu;0;L;;;;;N;;;;A7B9;
A7B9;LATIN SMALL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;A7B8;;A7B8
+A7BA;LATIN CAPITAL LETTER GLOTTAL A;Lu;0;L;;;;;N;;;;A7BB;
+A7BB;LATIN SMALL LETTER GLOTTAL A;Ll;0;L;;;;;N;;;A7BA;;A7BA
+A7BC;LATIN CAPITAL LETTER GLOTTAL I;Lu;0;L;;;;;N;;;;A7BD;
+A7BD;LATIN SMALL LETTER GLOTTAL I;Ll;0;L;;;;;N;;;A7BC;;A7BC
+A7BE;LATIN CAPITAL LETTER GLOTTAL U;Lu;0;L;;;;;N;;;;A7BF;
+A7BF;LATIN SMALL LETTER GLOTTAL U;Ll;0;L;;;;;N;;;A7BE;;A7BE
+A7C2;LATIN CAPITAL LETTER ANGLICANA W;Lu;0;L;;;;;N;;;;A7C3;
+A7C3;LATIN SMALL LETTER ANGLICANA W;Ll;0;L;;;;;N;;;A7C2;;A7C2
+A7C4;LATIN CAPITAL LETTER C WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;A794;
+A7C5;LATIN CAPITAL LETTER S WITH HOOK;Lu;0;L;;;;;N;;;;0282;
+A7C6;LATIN CAPITAL LETTER Z WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;1D8E;
A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;;
A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;;
A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;;
@@ -14506,7 +14537,7 @@ A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;;
A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;;
A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;;
A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;;
-A9BD;JAVANESE CONSONANT SIGN KERET;Mc;0;L;;;;;N;;;;;
+A9BD;JAVANESE CONSONANT SIGN KERET;Mn;0;NSM;;;;;N;;;;;
A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;;
A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;;
A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;;
@@ -14863,6 +14894,8 @@ AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;;
AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;;
AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;;
AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;;
+AB66;LATIN SMALL LETTER DZ DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+AB67;LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0
AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1
AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2
@@ -19105,6 +19138,29 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10F57;SOGDIAN PUNCTUATION CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
10F58;SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS;Po;0;AL;;;;;N;;;;;
10F59;SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10FE0;ELYMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10FE1;ELYMAIC LETTER BETH;Lo;0;R;;;;;N;;;;;
+10FE2;ELYMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10FE3;ELYMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10FE4;ELYMAIC LETTER HE;Lo;0;R;;;;;N;;;;;
+10FE5;ELYMAIC LETTER WAW;Lo;0;R;;;;;N;;;;;
+10FE6;ELYMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10FE7;ELYMAIC LETTER HETH;Lo;0;R;;;;;N;;;;;
+10FE8;ELYMAIC LETTER TETH;Lo;0;R;;;;;N;;;;;
+10FE9;ELYMAIC LETTER YODH;Lo;0;R;;;;;N;;;;;
+10FEA;ELYMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10FEB;ELYMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10FEC;ELYMAIC LETTER MEM;Lo;0;R;;;;;N;;;;;
+10FED;ELYMAIC LETTER NUN;Lo;0;R;;;;;N;;;;;
+10FEE;ELYMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10FEF;ELYMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10FF0;ELYMAIC LETTER PE;Lo;0;R;;;;;N;;;;;
+10FF1;ELYMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10FF2;ELYMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10FF3;ELYMAIC LETTER RESH;Lo;0;R;;;;;N;;;;;
+10FF4;ELYMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10FF5;ELYMAIC LETTER TAW;Lo;0;R;;;;;N;;;;;
+10FF6;ELYMAIC LIGATURE ZAYIN-YODH;Lo;0;R;;;;;N;;;;;
11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
@@ -19887,6 +19943,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;;
1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;;
1145E;NEWA SANDHI MARK;Mn;230;NSM;;;;;N;;;;;
+1145F;NEWA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;;
11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;;
11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;;
@@ -20209,6 +20266,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+116B8;TAKRI LETTER ARCHAIC KHA;Lo;0;L;;;;;N;;;;;
116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -20421,6 +20479,71 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;;
118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;;
+119A0;NANDINAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+119A1;NANDINAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+119A2;NANDINAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+119A3;NANDINAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+119A4;NANDINAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+119A5;NANDINAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+119A6;NANDINAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+119A7;NANDINAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+119AA;NANDINAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+119AB;NANDINAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+119AC;NANDINAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+119AD;NANDINAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+119AE;NANDINAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+119AF;NANDINAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+119B0;NANDINAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+119B1;NANDINAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+119B2;NANDINAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+119B3;NANDINAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+119B4;NANDINAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+119B5;NANDINAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+119B6;NANDINAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+119B7;NANDINAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+119B8;NANDINAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+119B9;NANDINAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+119BA;NANDINAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+119BB;NANDINAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+119BC;NANDINAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+119BD;NANDINAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+119BE;NANDINAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+119BF;NANDINAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+119C0;NANDINAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+119C1;NANDINAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+119C2;NANDINAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+119C3;NANDINAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+119C4;NANDINAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+119C5;NANDINAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+119C6;NANDINAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+119C7;NANDINAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+119C8;NANDINAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+119C9;NANDINAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+119CA;NANDINAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+119CB;NANDINAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+119CC;NANDINAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+119CD;NANDINAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+119CE;NANDINAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+119CF;NANDINAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+119D0;NANDINAGARI LETTER RRA;Lo;0;L;;;;;N;;;;;
+119D1;NANDINAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+119D2;NANDINAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+119D3;NANDINAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+119D4;NANDINAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+119D5;NANDINAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+119D6;NANDINAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+119D7;NANDINAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+119DA;NANDINAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+119DB;NANDINAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+119DC;NANDINAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+119DD;NANDINAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+119DE;NANDINAGARI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+119DF;NANDINAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+119E0;NANDINAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+119E1;NANDINAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+119E2;NANDINAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+119E3;NANDINAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;;
+119E4;NANDINAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;;
11A00;ZANABAZAR SQUARE LETTER A;Lo;0;L;;;;;N;;;;;
11A01;ZANABAZAR SQUARE VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
11A02;ZANABAZAR SQUARE VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
@@ -20545,6 +20668,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11A81;SOYOMBO LETTER SA;Lo;0;L;;;;;N;;;;;
11A82;SOYOMBO LETTER HA;Lo;0;L;;;;;N;;;;;
11A83;SOYOMBO LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11A84;SOYOMBO SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+11A85;SOYOMBO SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
11A86;SOYOMBO CLUSTER-INITIAL LETTER RA;Lo;0;L;;;;;N;;;;;
11A87;SOYOMBO CLUSTER-INITIAL LETTER LA;Lo;0;L;;;;;N;;;;;
11A88;SOYOMBO CLUSTER-INITIAL LETTER SHA;Lo;0;L;;;;;N;;;;;
@@ -20959,6 +21084,57 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
+11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
+11FC2;TAMIL FRACTION ONE EIGHTIETH;No;0;L;;;;1/80;N;;;;;
+11FC3;TAMIL FRACTION ONE SIXTY-FOURTH;No;0;L;;;;1/64;N;;;;;
+11FC4;TAMIL FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;;
+11FC5;TAMIL FRACTION ONE THIRTY-SECOND;No;0;L;;;;1/32;N;;;;;
+11FC6;TAMIL FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;;
+11FC7;TAMIL FRACTION THREE SIXTY-FOURTHS;No;0;L;;;;3/64;N;;;;;
+11FC8;TAMIL FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;;
+11FC9;TAMIL FRACTION ONE SIXTEENTH-1;No;0;L;;;;1/16;N;;;;;
+11FCA;TAMIL FRACTION ONE SIXTEENTH-2;No;0;L;;;;1/16;N;;;;;
+11FCB;TAMIL FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;;
+11FCC;TAMIL FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+11FCD;TAMIL FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;;
+11FCE;TAMIL FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+11FCF;TAMIL FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;;
+11FD0;TAMIL FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+11FD1;TAMIL FRACTION ONE HALF-1;No;0;L;;;;1/2;N;;;;;
+11FD2;TAMIL FRACTION ONE HALF-2;No;0;L;;;;1/2;N;;;;;
+11FD3;TAMIL FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+11FD4;TAMIL FRACTION DOWNSCALING FACTOR KIIZH;No;0;L;;;;1/320;N;;;;;
+11FD5;TAMIL SIGN NEL;So;0;ON;;;;;N;;;;;
+11FD6;TAMIL SIGN CEVITU;So;0;ON;;;;;N;;;;;
+11FD7;TAMIL SIGN AAZHAAKKU;So;0;ON;;;;;N;;;;;
+11FD8;TAMIL SIGN UZHAKKU;So;0;ON;;;;;N;;;;;
+11FD9;TAMIL SIGN MUUVUZHAKKU;So;0;ON;;;;;N;;;;;
+11FDA;TAMIL SIGN KURUNI;So;0;ON;;;;;N;;;;;
+11FDB;TAMIL SIGN PATHAKKU;So;0;ON;;;;;N;;;;;
+11FDC;TAMIL SIGN MUKKURUNI;So;0;ON;;;;;N;;;;;
+11FDD;TAMIL SIGN KAACU;Sc;0;ET;;;;;N;;;;;
+11FDE;TAMIL SIGN PANAM;Sc;0;ET;;;;;N;;;;;
+11FDF;TAMIL SIGN PON;Sc;0;ET;;;;;N;;;;;
+11FE0;TAMIL SIGN VARAAKAN;Sc;0;ET;;;;;N;;;;;
+11FE1;TAMIL SIGN PAARAM;So;0;ON;;;;;N;;;;;
+11FE2;TAMIL SIGN KUZHI;So;0;ON;;;;;N;;;;;
+11FE3;TAMIL SIGN VELI;So;0;ON;;;;;N;;;;;
+11FE4;TAMIL WET CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE5;TAMIL DRY CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE6;TAMIL LAND SIGN;So;0;ON;;;;;N;;;;;
+11FE7;TAMIL SALT PAN SIGN;So;0;ON;;;;;N;;;;;
+11FE8;TAMIL TRADITIONAL CREDIT SIGN;So;0;ON;;;;;N;;;;;
+11FE9;TAMIL TRADITIONAL NUMBER SIGN;So;0;ON;;;;;N;;;;;
+11FEA;TAMIL CURRENT SIGN;So;0;ON;;;;;N;;;;;
+11FEB;TAMIL AND ODD SIGN;So;0;ON;;;;;N;;;;;
+11FEC;TAMIL SPENT SIGN;So;0;ON;;;;;N;;;;;
+11FED;TAMIL TOTAL SIGN;So;0;ON;;;;;N;;;;;
+11FEE;TAMIL IN POSSESSION SIGN;So;0;ON;;;;;N;;;;;
+11FEF;TAMIL STARTING FROM SIGN;So;0;ON;;;;;N;;;;;
+11FF0;TAMIL SIGN MUTHALIYA;So;0;ON;;;;;N;;;;;
+11FF1;TAMIL SIGN VAKAIYARAA;So;0;ON;;;;;N;;;;;
+11FFF;TAMIL PUNCTUATION END OF TEXT;Po;0;L;;;;;N;;;;;
12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;;
12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;;
12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;;
@@ -23264,6 +23440,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
+13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
+13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
+13433;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM START;Cf;0;L;;;;;N;;;;;
+13434;EGYPTIAN HIEROGLYPH INSERT AT TOP END;Cf;0;L;;;;;N;;;;;
+13435;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM END;Cf;0;L;;;;;N;;;;;
+13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
+13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
+13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -24782,6 +24967,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;;
16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;;
16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;;
+16F45;MIAO LETTER BRI;Lo;0;L;;;;;N;;;;;
+16F46;MIAO LETTER SYI;Lo;0;L;;;;;N;;;;;
+16F47;MIAO LETTER DZYI;Lo;0;L;;;;;N;;;;;
+16F48;MIAO LETTER TE;Lo;0;L;;;;;N;;;;;
+16F49;MIAO LETTER TSE;Lo;0;L;;;;;N;;;;;
+16F4A;MIAO LETTER RTE;Lo;0;L;;;;;N;;;;;
+16F4F;MIAO SIGN CONSONANT MODIFIER BAR;Mn;0;NSM;;;;;N;;;;;
16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;;
16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;;
16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;;
@@ -24829,6 +25021,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;;
16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;;
16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;;
+16F7F;MIAO VOWEL SIGN UOG;Mc;0;L;;;;;N;;;;;
+16F80;MIAO VOWEL SIGN YUI;Mc;0;L;;;;;N;;;;;
+16F81;MIAO VOWEL SIGN OG;Mc;0;L;;;;;N;;;;;
+16F82;MIAO VOWEL SIGN OER;Mc;0;L;;;;;N;;;;;
+16F83;MIAO VOWEL SIGN VW;Mc;0;L;;;;;N;;;;;
+16F84;MIAO VOWEL SIGN IG;Mc;0;L;;;;;N;;;;;
+16F85;MIAO VOWEL SIGN EA;Mc;0;L;;;;;N;;;;;
+16F86;MIAO VOWEL SIGN IONG;Mc;0;L;;;;;N;;;;;
+16F87;MIAO VOWEL SIGN UI;Mc;0;L;;;;;N;;;;;
16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;;
16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;;
16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;;
@@ -24848,8 +25049,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;;
16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;;
16FE1;NUSHU ITERATION MARK;Lm;0;L;;;;;N;;;;;
+16FE2;OLD CHINESE HOOK MARK;Po;0;ON;;;;;N;;;;;
+16FE3;OLD CHINESE ITERATION MARK;Lm;0;L;;;;;N;;;;;
17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;;
-187F1;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+187F7;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;;
18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;;
18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;;
@@ -25892,6 +26095,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1B11C;HENTAIGANA LETTER WO-7;Lo;0;L;;;;;N;;;;;
1B11D;HENTAIGANA LETTER N-MU-MO-1;Lo;0;L;;;;;N;;;;;
1B11E;HENTAIGANA LETTER N-MU-MO-2;Lo;0;L;;;;;N;;;;;
+1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B167;KATAKANA LETTER SMALL N;Lo;0;L;;;;;N;;;;;
1B170;NUSHU CHARACTER-1B170;Lo;0;L;;;;;N;;;;;
1B171;NUSHU CHARACTER-1B171;Lo;0;L;;;;;N;;;;;
1B172;NUSHU CHARACTER-1B172;Lo;0;L;;;;;N;;;;;
@@ -28820,6 +29030,136 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
+1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
+1E103;NYIAKENG PUACHUE HMONG LETTER TA;Lo;0;L;;;;;N;;;;;
+1E104;NYIAKENG PUACHUE HMONG LETTER HA;Lo;0;L;;;;;N;;;;;
+1E105;NYIAKENG PUACHUE HMONG LETTER NA;Lo;0;L;;;;;N;;;;;
+1E106;NYIAKENG PUACHUE HMONG LETTER XA;Lo;0;L;;;;;N;;;;;
+1E107;NYIAKENG PUACHUE HMONG LETTER NKA;Lo;0;L;;;;;N;;;;;
+1E108;NYIAKENG PUACHUE HMONG LETTER CA;Lo;0;L;;;;;N;;;;;
+1E109;NYIAKENG PUACHUE HMONG LETTER LA;Lo;0;L;;;;;N;;;;;
+1E10A;NYIAKENG PUACHUE HMONG LETTER SA;Lo;0;L;;;;;N;;;;;
+1E10B;NYIAKENG PUACHUE HMONG LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E10C;NYIAKENG PUACHUE HMONG LETTER NCA;Lo;0;L;;;;;N;;;;;
+1E10D;NYIAKENG PUACHUE HMONG LETTER NTSA;Lo;0;L;;;;;N;;;;;
+1E10E;NYIAKENG PUACHUE HMONG LETTER KA;Lo;0;L;;;;;N;;;;;
+1E10F;NYIAKENG PUACHUE HMONG LETTER DA;Lo;0;L;;;;;N;;;;;
+1E110;NYIAKENG PUACHUE HMONG LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E111;NYIAKENG PUACHUE HMONG LETTER NRA;Lo;0;L;;;;;N;;;;;
+1E112;NYIAKENG PUACHUE HMONG LETTER VA;Lo;0;L;;;;;N;;;;;
+1E113;NYIAKENG PUACHUE HMONG LETTER NTXA;Lo;0;L;;;;;N;;;;;
+1E114;NYIAKENG PUACHUE HMONG LETTER TXA;Lo;0;L;;;;;N;;;;;
+1E115;NYIAKENG PUACHUE HMONG LETTER FA;Lo;0;L;;;;;N;;;;;
+1E116;NYIAKENG PUACHUE HMONG LETTER RA;Lo;0;L;;;;;N;;;;;
+1E117;NYIAKENG PUACHUE HMONG LETTER QA;Lo;0;L;;;;;N;;;;;
+1E118;NYIAKENG PUACHUE HMONG LETTER YA;Lo;0;L;;;;;N;;;;;
+1E119;NYIAKENG PUACHUE HMONG LETTER NQA;Lo;0;L;;;;;N;;;;;
+1E11A;NYIAKENG PUACHUE HMONG LETTER PA;Lo;0;L;;;;;N;;;;;
+1E11B;NYIAKENG PUACHUE HMONG LETTER XYA;Lo;0;L;;;;;N;;;;;
+1E11C;NYIAKENG PUACHUE HMONG LETTER NPA;Lo;0;L;;;;;N;;;;;
+1E11D;NYIAKENG PUACHUE HMONG LETTER DLA;Lo;0;L;;;;;N;;;;;
+1E11E;NYIAKENG PUACHUE HMONG LETTER NPLA;Lo;0;L;;;;;N;;;;;
+1E11F;NYIAKENG PUACHUE HMONG LETTER HAH;Lo;0;L;;;;;N;;;;;
+1E120;NYIAKENG PUACHUE HMONG LETTER MLA;Lo;0;L;;;;;N;;;;;
+1E121;NYIAKENG PUACHUE HMONG LETTER PLA;Lo;0;L;;;;;N;;;;;
+1E122;NYIAKENG PUACHUE HMONG LETTER GA;Lo;0;L;;;;;N;;;;;
+1E123;NYIAKENG PUACHUE HMONG LETTER RRA;Lo;0;L;;;;;N;;;;;
+1E124;NYIAKENG PUACHUE HMONG LETTER A;Lo;0;L;;;;;N;;;;;
+1E125;NYIAKENG PUACHUE HMONG LETTER AA;Lo;0;L;;;;;N;;;;;
+1E126;NYIAKENG PUACHUE HMONG LETTER I;Lo;0;L;;;;;N;;;;;
+1E127;NYIAKENG PUACHUE HMONG LETTER U;Lo;0;L;;;;;N;;;;;
+1E128;NYIAKENG PUACHUE HMONG LETTER O;Lo;0;L;;;;;N;;;;;
+1E129;NYIAKENG PUACHUE HMONG LETTER OO;Lo;0;L;;;;;N;;;;;
+1E12A;NYIAKENG PUACHUE HMONG LETTER E;Lo;0;L;;;;;N;;;;;
+1E12B;NYIAKENG PUACHUE HMONG LETTER EE;Lo;0;L;;;;;N;;;;;
+1E12C;NYIAKENG PUACHUE HMONG LETTER W;Lo;0;L;;;;;N;;;;;
+1E130;NYIAKENG PUACHUE HMONG TONE-B;Mn;230;NSM;;;;;N;;;;;
+1E131;NYIAKENG PUACHUE HMONG TONE-M;Mn;230;NSM;;;;;N;;;;;
+1E132;NYIAKENG PUACHUE HMONG TONE-J;Mn;230;NSM;;;;;N;;;;;
+1E133;NYIAKENG PUACHUE HMONG TONE-V;Mn;230;NSM;;;;;N;;;;;
+1E134;NYIAKENG PUACHUE HMONG TONE-S;Mn;230;NSM;;;;;N;;;;;
+1E135;NYIAKENG PUACHUE HMONG TONE-G;Mn;230;NSM;;;;;N;;;;;
+1E136;NYIAKENG PUACHUE HMONG TONE-D;Mn;230;NSM;;;;;N;;;;;
+1E137;NYIAKENG PUACHUE HMONG SIGN FOR PERSON;Lm;0;L;;;;;N;;;;;
+1E138;NYIAKENG PUACHUE HMONG SIGN FOR THING;Lm;0;L;;;;;N;;;;;
+1E139;NYIAKENG PUACHUE HMONG SIGN FOR LOCATION;Lm;0;L;;;;;N;;;;;
+1E13A;NYIAKENG PUACHUE HMONG SIGN FOR ANIMAL;Lm;0;L;;;;;N;;;;;
+1E13B;NYIAKENG PUACHUE HMONG SIGN FOR INVERTEBRATE;Lm;0;L;;;;;N;;;;;
+1E13C;NYIAKENG PUACHUE HMONG SIGN XW XW;Lm;0;L;;;;;N;;;;;
+1E13D;NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;;
+1E140;NYIAKENG PUACHUE HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E141;NYIAKENG PUACHUE HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E142;NYIAKENG PUACHUE HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E143;NYIAKENG PUACHUE HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E144;NYIAKENG PUACHUE HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E145;NYIAKENG PUACHUE HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E146;NYIAKENG PUACHUE HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E147;NYIAKENG PUACHUE HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E148;NYIAKENG PUACHUE HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E149;NYIAKENG PUACHUE HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E14E;NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ;Lo;0;L;;;;;N;;;;;
+1E14F;NYIAKENG PUACHUE HMONG CIRCLED CA;So;0;L;;;;;N;;;;;
+1E2C0;WANCHO LETTER AA;Lo;0;L;;;;;N;;;;;
+1E2C1;WANCHO LETTER A;Lo;0;L;;;;;N;;;;;
+1E2C2;WANCHO LETTER BA;Lo;0;L;;;;;N;;;;;
+1E2C3;WANCHO LETTER CA;Lo;0;L;;;;;N;;;;;
+1E2C4;WANCHO LETTER DA;Lo;0;L;;;;;N;;;;;
+1E2C5;WANCHO LETTER GA;Lo;0;L;;;;;N;;;;;
+1E2C6;WANCHO LETTER YA;Lo;0;L;;;;;N;;;;;
+1E2C7;WANCHO LETTER PHA;Lo;0;L;;;;;N;;;;;
+1E2C8;WANCHO LETTER LA;Lo;0;L;;;;;N;;;;;
+1E2C9;WANCHO LETTER NA;Lo;0;L;;;;;N;;;;;
+1E2CA;WANCHO LETTER PA;Lo;0;L;;;;;N;;;;;
+1E2CB;WANCHO LETTER TA;Lo;0;L;;;;;N;;;;;
+1E2CC;WANCHO LETTER THA;Lo;0;L;;;;;N;;;;;
+1E2CD;WANCHO LETTER FA;Lo;0;L;;;;;N;;;;;
+1E2CE;WANCHO LETTER SA;Lo;0;L;;;;;N;;;;;
+1E2CF;WANCHO LETTER SHA;Lo;0;L;;;;;N;;;;;
+1E2D0;WANCHO LETTER JA;Lo;0;L;;;;;N;;;;;
+1E2D1;WANCHO LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E2D2;WANCHO LETTER WA;Lo;0;L;;;;;N;;;;;
+1E2D3;WANCHO LETTER VA;Lo;0;L;;;;;N;;;;;
+1E2D4;WANCHO LETTER KA;Lo;0;L;;;;;N;;;;;
+1E2D5;WANCHO LETTER O;Lo;0;L;;;;;N;;;;;
+1E2D6;WANCHO LETTER AU;Lo;0;L;;;;;N;;;;;
+1E2D7;WANCHO LETTER RA;Lo;0;L;;;;;N;;;;;
+1E2D8;WANCHO LETTER MA;Lo;0;L;;;;;N;;;;;
+1E2D9;WANCHO LETTER KHA;Lo;0;L;;;;;N;;;;;
+1E2DA;WANCHO LETTER HA;Lo;0;L;;;;;N;;;;;
+1E2DB;WANCHO LETTER E;Lo;0;L;;;;;N;;;;;
+1E2DC;WANCHO LETTER I;Lo;0;L;;;;;N;;;;;
+1E2DD;WANCHO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1E2DE;WANCHO LETTER U;Lo;0;L;;;;;N;;;;;
+1E2DF;WANCHO LETTER LLHA;Lo;0;L;;;;;N;;;;;
+1E2E0;WANCHO LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E2E1;WANCHO LETTER TRA;Lo;0;L;;;;;N;;;;;
+1E2E2;WANCHO LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E2E3;WANCHO LETTER AANG;Lo;0;L;;;;;N;;;;;
+1E2E4;WANCHO LETTER ANG;Lo;0;L;;;;;N;;;;;
+1E2E5;WANCHO LETTER ING;Lo;0;L;;;;;N;;;;;
+1E2E6;WANCHO LETTER ON;Lo;0;L;;;;;N;;;;;
+1E2E7;WANCHO LETTER EN;Lo;0;L;;;;;N;;;;;
+1E2E8;WANCHO LETTER AAN;Lo;0;L;;;;;N;;;;;
+1E2E9;WANCHO LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E2EA;WANCHO LETTER UEN;Lo;0;L;;;;;N;;;;;
+1E2EB;WANCHO LETTER YIH;Lo;0;L;;;;;N;;;;;
+1E2EC;WANCHO TONE TUP;Mn;230;NSM;;;;;N;;;;;
+1E2ED;WANCHO TONE TUPNI;Mn;230;NSM;;;;;N;;;;;
+1E2EE;WANCHO TONE KOI;Mn;230;NSM;;;;;N;;;;;
+1E2EF;WANCHO TONE KOINI;Mn;230;NSM;;;;;N;;;;;
+1E2F0;WANCHO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E2F1;WANCHO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E2F2;WANCHO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E2F3;WANCHO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E2F4;WANCHO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E2F5;WANCHO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E2F6;WANCHO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E2F7;WANCHO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;;
1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;;
1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;;
@@ -29108,6 +29448,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;;
+1E94B;ADLAM NASALIZATION MARK;Lm;0;R;;;;;N;;;;;
1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;;
1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;;
1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;;
@@ -29188,6 +29529,67 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1ECB2;INDIC SIYAQ NUMBER ALTERNATE TWO;No;0;AL;;;;2;N;;;;;
1ECB3;INDIC SIYAQ NUMBER ALTERNATE TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
1ECB4;INDIC SIYAQ ALTERNATE LAKH MARK;No;0;AL;;;;100000;N;;;;;
+1ED01;OTTOMAN SIYAQ NUMBER ONE;No;0;AL;;;;1;N;;;;;
+1ED02;OTTOMAN SIYAQ NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED03;OTTOMAN SIYAQ NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED04;OTTOMAN SIYAQ NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED05;OTTOMAN SIYAQ NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED06;OTTOMAN SIYAQ NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED07;OTTOMAN SIYAQ NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED08;OTTOMAN SIYAQ NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED09;OTTOMAN SIYAQ NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED0A;OTTOMAN SIYAQ NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED0B;OTTOMAN SIYAQ NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+1ED0C;OTTOMAN SIYAQ NUMBER THIRTY;No;0;AL;;;;30;N;;;;;
+1ED0D;OTTOMAN SIYAQ NUMBER FORTY;No;0;AL;;;;40;N;;;;;
+1ED0E;OTTOMAN SIYAQ NUMBER FIFTY;No;0;AL;;;;50;N;;;;;
+1ED0F;OTTOMAN SIYAQ NUMBER SIXTY;No;0;AL;;;;60;N;;;;;
+1ED10;OTTOMAN SIYAQ NUMBER SEVENTY;No;0;AL;;;;70;N;;;;;
+1ED11;OTTOMAN SIYAQ NUMBER EIGHTY;No;0;AL;;;;80;N;;;;;
+1ED12;OTTOMAN SIYAQ NUMBER NINETY;No;0;AL;;;;90;N;;;;;
+1ED13;OTTOMAN SIYAQ NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+1ED14;OTTOMAN SIYAQ NUMBER TWO HUNDRED;No;0;AL;;;;200;N;;;;;
+1ED15;OTTOMAN SIYAQ NUMBER THREE HUNDRED;No;0;AL;;;;300;N;;;;;
+1ED16;OTTOMAN SIYAQ NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED17;OTTOMAN SIYAQ NUMBER FIVE HUNDRED;No;0;AL;;;;500;N;;;;;
+1ED18;OTTOMAN SIYAQ NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED19;OTTOMAN SIYAQ NUMBER SEVEN HUNDRED;No;0;AL;;;;700;N;;;;;
+1ED1A;OTTOMAN SIYAQ NUMBER EIGHT HUNDRED;No;0;AL;;;;800;N;;;;;
+1ED1B;OTTOMAN SIYAQ NUMBER NINE HUNDRED;No;0;AL;;;;900;N;;;;;
+1ED1C;OTTOMAN SIYAQ NUMBER ONE THOUSAND;No;0;AL;;;;1000;N;;;;;
+1ED1D;OTTOMAN SIYAQ NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED1E;OTTOMAN SIYAQ NUMBER THREE THOUSAND;No;0;AL;;;;3000;N;;;;;
+1ED1F;OTTOMAN SIYAQ NUMBER FOUR THOUSAND;No;0;AL;;;;4000;N;;;;;
+1ED20;OTTOMAN SIYAQ NUMBER FIVE THOUSAND;No;0;AL;;;;5000;N;;;;;
+1ED21;OTTOMAN SIYAQ NUMBER SIX THOUSAND;No;0;AL;;;;6000;N;;;;;
+1ED22;OTTOMAN SIYAQ NUMBER SEVEN THOUSAND;No;0;AL;;;;7000;N;;;;;
+1ED23;OTTOMAN SIYAQ NUMBER EIGHT THOUSAND;No;0;AL;;;;8000;N;;;;;
+1ED24;OTTOMAN SIYAQ NUMBER NINE THOUSAND;No;0;AL;;;;9000;N;;;;;
+1ED25;OTTOMAN SIYAQ NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED26;OTTOMAN SIYAQ NUMBER TWENTY THOUSAND;No;0;AL;;;;20000;N;;;;;
+1ED27;OTTOMAN SIYAQ NUMBER THIRTY THOUSAND;No;0;AL;;;;30000;N;;;;;
+1ED28;OTTOMAN SIYAQ NUMBER FORTY THOUSAND;No;0;AL;;;;40000;N;;;;;
+1ED29;OTTOMAN SIYAQ NUMBER FIFTY THOUSAND;No;0;AL;;;;50000;N;;;;;
+1ED2A;OTTOMAN SIYAQ NUMBER SIXTY THOUSAND;No;0;AL;;;;60000;N;;;;;
+1ED2B;OTTOMAN SIYAQ NUMBER SEVENTY THOUSAND;No;0;AL;;;;70000;N;;;;;
+1ED2C;OTTOMAN SIYAQ NUMBER EIGHTY THOUSAND;No;0;AL;;;;80000;N;;;;;
+1ED2D;OTTOMAN SIYAQ NUMBER NINETY THOUSAND;No;0;AL;;;;90000;N;;;;;
+1ED2E;OTTOMAN SIYAQ MARRATAN;So;0;AL;;;;;N;;;;;
+1ED2F;OTTOMAN SIYAQ ALTERNATE NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED30;OTTOMAN SIYAQ ALTERNATE NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED31;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED32;OTTOMAN SIYAQ ALTERNATE NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED33;OTTOMAN SIYAQ ALTERNATE NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED34;OTTOMAN SIYAQ ALTERNATE NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED35;OTTOMAN SIYAQ ALTERNATE NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED36;OTTOMAN SIYAQ ALTERNATE NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED37;OTTOMAN SIYAQ ALTERNATE NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED38;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED39;OTTOMAN SIYAQ ALTERNATE NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED3A;OTTOMAN SIYAQ ALTERNATE NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED3B;OTTOMAN SIYAQ ALTERNATE NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED3C;OTTOMAN SIYAQ FRACTION ONE HALF;No;0;AL;;;;1/2;N;;;;;
+1ED3D;OTTOMAN SIYAQ FRACTION ONE SIXTH;No;0;AL;;;;1/6;N;;;;;
1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
@@ -29662,6 +30064,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;;
1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;;
+1F16C;RAISED MR SIGN;So;0;ON;<super> 004D 0052;;;;N;;;;;
1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
@@ -30794,6 +31197,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;;
1F6D3;STUPA;So;0;ON;;;;;N;;;;;
1F6D4;PAGODA;So;0;ON;;;;;N;;;;;
+1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;;
1F6E1;SHIELD;So;0;ON;;;;;N;;;;;
1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;;
@@ -30817,6 +31221,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6F7;SLED;So;0;ON;;;;;N;;;;;
1F6F8;FLYING SAUCER;So;0;ON;;;;;N;;;;;
1F6F9;SKATEBOARD;So;0;ON;;;;;N;;;;;
+1F6FA;AUTO RICKSHAW;So;0;ON;;;;;N;;;;;
1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;;
1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;;
1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;;
@@ -31022,6 +31427,18 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E3;LARGE PURPLE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E4;LARGE BROWN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E5;LARGE RED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E6;LARGE BLUE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E7;LARGE ORANGE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E8;LARGE YELLOW SQUARE;So;0;ON;;;;;N;;;;;
+1F7E9;LARGE GREEN SQUARE;So;0;ON;;;;;N;;;;;
+1F7EA;LARGE PURPLE SQUARE;So;0;ON;;;;;N;;;;;
+1F7EB;LARGE BROWN SQUARE;So;0;ON;;;;;N;;;;;
1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
@@ -31182,6 +31599,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F909;DOWNWARD FACING NOTCHED HOOK;So;0;ON;;;;;N;;;;;
1F90A;DOWNWARD FACING HOOK WITH DOT;So;0;ON;;;;;N;;;;;
1F90B;DOWNWARD FACING NOTCHED HOOK WITH DOT;So;0;ON;;;;;N;;;;;
+1F90D;WHITE HEART;So;0;ON;;;;;N;;;;;
+1F90E;BROWN HEART;So;0;ON;;;;;N;;;;;
+1F90F;PINCHING HAND;So;0;ON;;;;;N;;;;;
1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;;
@@ -31229,6 +31649,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F93C;WRESTLERS;So;0;ON;;;;;N;;;;;
1F93D;WATER POLO;So;0;ON;;;;;N;;;;;
1F93E;HANDBALL;So;0;ON;;;;;N;;;;;
+1F93F;DIVING MASK;So;0;ON;;;;;N;;;;;
1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;;
1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;;
1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;;
@@ -31278,11 +31699,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F96E;MOON CAKE;So;0;ON;;;;;N;;;;;
1F96F;BAGEL;So;0;ON;;;;;N;;;;;
1F970;SMILING FACE WITH SMILING EYES AND THREE HEARTS;So;0;ON;;;;;N;;;;;
+1F971;YAWNING FACE;So;0;ON;;;;;N;;;;;
1F973;FACE WITH PARTY HORN AND PARTY HAT;So;0;ON;;;;;N;;;;;
1F974;FACE WITH UNEVEN EYES AND WAVY MOUTH;So;0;ON;;;;;N;;;;;
1F975;OVERHEATED FACE;So;0;ON;;;;;N;;;;;
1F976;FREEZING FACE;So;0;ON;;;;;N;;;;;
1F97A;FACE WITH PLEADING EYES;So;0;ON;;;;;N;;;;;
+1F97B;SARI;So;0;ON;;;;;N;;;;;
1F97C;LAB COAT;So;0;ON;;;;;N;;;;;
1F97D;GOGGLES;So;0;ON;;;;;N;;;;;
1F97E;HIKING BOOT;So;0;ON;;;;;N;;;;;
@@ -31322,6 +31745,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9A0;MICROBE;So;0;ON;;;;;N;;;;;
1F9A1;BADGER;So;0;ON;;;;;N;;;;;
1F9A2;SWAN;So;0;ON;;;;;N;;;;;
+1F9A5;SLOTH;So;0;ON;;;;;N;;;;;
+1F9A6;OTTER;So;0;ON;;;;;N;;;;;
+1F9A7;ORANGUTAN;So;0;ON;;;;;N;;;;;
+1F9A8;SKUNK;So;0;ON;;;;;N;;;;;
+1F9A9;FLAMINGO;So;0;ON;;;;;N;;;;;
+1F9AA;OYSTER;So;0;ON;;;;;N;;;;;
+1F9AE;GUIDE DOG;So;0;ON;;;;;N;;;;;
+1F9AF;PROBING CANE;So;0;ON;;;;;N;;;;;
1F9B0;EMOJI COMPONENT RED HAIR;So;0;ON;;;;;N;;;;;
1F9B1;EMOJI COMPONENT CURLY HAIR;So;0;ON;;;;;N;;;;;
1F9B2;EMOJI COMPONENT BALD;So;0;ON;;;;;N;;;;;
@@ -31332,9 +31763,26 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9B7;TOOTH;So;0;ON;;;;;N;;;;;
1F9B8;SUPERHERO;So;0;ON;;;;;N;;;;;
1F9B9;SUPERVILLAIN;So;0;ON;;;;;N;;;;;
+1F9BA;SAFETY VEST;So;0;ON;;;;;N;;;;;
+1F9BB;EAR WITH HEARING AID;So;0;ON;;;;;N;;;;;
+1F9BC;MOTORIZED WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BD;MANUAL WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BE;MECHANICAL ARM;So;0;ON;;;;;N;;;;;
+1F9BF;MECHANICAL LEG;So;0;ON;;;;;N;;;;;
1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;;
1F9C1;CUPCAKE;So;0;ON;;;;;N;;;;;
1F9C2;SALT SHAKER;So;0;ON;;;;;N;;;;;
+1F9C3;BEVERAGE BOX;So;0;ON;;;;;N;;;;;
+1F9C4;GARLIC;So;0;ON;;;;;N;;;;;
+1F9C5;ONION;So;0;ON;;;;;N;;;;;
+1F9C6;FALAFEL;So;0;ON;;;;;N;;;;;
+1F9C7;WAFFLE;So;0;ON;;;;;N;;;;;
+1F9C8;BUTTER;So;0;ON;;;;;N;;;;;
+1F9C9;MATE DRINK;So;0;ON;;;;;N;;;;;
+1F9CA;ICE CUBE;So;0;ON;;;;;N;;;;;
+1F9CD;STANDING PERSON;So;0;ON;;;;;N;;;;;
+1F9CE;KNEELING PERSON;So;0;ON;;;;;N;;;;;
+1F9CF;DEAF PERSON;So;0;ON;;;;;N;;;;;
1F9D0;FACE WITH MONOCLE;So;0;ON;;;;;N;;;;;
1F9D1;ADULT;So;0;ON;;;;;N;;;;;
1F9D2;CHILD;So;0;ON;;;;;N;;;;;
@@ -31383,6 +31831,90 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9FD;SPONGE;So;0;ON;;;;;N;;;;;
1F9FE;RECEIPT;So;0;ON;;;;;N;;;;;
1F9FF;NAZAR AMULET;So;0;ON;;;;;N;;;;;
+1FA00;NEUTRAL CHESS KING;So;0;ON;;;;;N;;;;;
+1FA01;NEUTRAL CHESS QUEEN;So;0;ON;;;;;N;;;;;
+1FA02;NEUTRAL CHESS ROOK;So;0;ON;;;;;N;;;;;
+1FA03;NEUTRAL CHESS BISHOP;So;0;ON;;;;;N;;;;;
+1FA04;NEUTRAL CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+1FA05;NEUTRAL CHESS PAWN;So;0;ON;;;;;N;;;;;
+1FA06;WHITE CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA07;BLACK CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA08;NEUTRAL CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA09;WHITE CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0A;WHITE CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0B;WHITE CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0C;WHITE CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0D;WHITE CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0E;WHITE CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0F;BLACK CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA10;BLACK CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA11;BLACK CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA12;BLACK CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA13;BLACK CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA14;BLACK CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA15;NEUTRAL CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA16;NEUTRAL CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA17;NEUTRAL CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA18;NEUTRAL CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA19;NEUTRAL CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1A;NEUTRAL CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1B;WHITE CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1C;BLACK CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1D;NEUTRAL CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1E;WHITE CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA1F;WHITE CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA20;WHITE CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA21;WHITE CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA22;WHITE CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA23;WHITE CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA24;BLACK CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA25;BLACK CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA26;BLACK CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA27;BLACK CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA28;BLACK CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA29;BLACK CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA2A;NEUTRAL CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA2B;NEUTRAL CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA2C;NEUTRAL CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA2D;NEUTRAL CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA2E;NEUTRAL CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA2F;NEUTRAL CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA30;WHITE CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA31;BLACK CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA32;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA33;WHITE CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA34;WHITE CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA35;WHITE CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA36;WHITE CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA37;WHITE CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA38;WHITE CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA39;BLACK CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3A;BLACK CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3B;BLACK CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3C;BLACK CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3D;BLACK CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3E;BLACK CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3F;NEUTRAL CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA40;NEUTRAL CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA41;NEUTRAL CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA42;NEUTRAL CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA43;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA44;NEUTRAL CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA45;WHITE CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA46;BLACK CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA47;NEUTRAL CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA48;WHITE CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA49;BLACK CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4A;NEUTRAL CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4B;WHITE CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4C;BLACK CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4D;NEUTRAL CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4E;WHITE CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA4F;WHITE CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA50;WHITE CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
+1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;;
1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;;
1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;;
@@ -31397,6 +31929,22 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA6B;XIANGQI BLACK CHARIOT;So;0;ON;;;;;N;;;;;
1FA6C;XIANGQI BLACK CANNON;So;0;ON;;;;;N;;;;;
1FA6D;XIANGQI BLACK SOLDIER;So;0;ON;;;;;N;;;;;
+1FA70;BALLET SHOES;So;0;ON;;;;;N;;;;;
+1FA71;ONE-PIECE SWIMSUIT;So;0;ON;;;;;N;;;;;
+1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
+1FA73;SHORTS;So;0;ON;;;;;N;;;;;
+1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
+1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
+1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
+1FA80;YO-YO;So;0;ON;;;;;N;;;;;
+1FA81;KITE;So;0;ON;;;;;N;;;;;
+1FA82;PARACHUTE;So;0;ON;;;;;N;;;;;
+1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
+1FA91;CHAIR;So;0;ON;;;;;N;;;;;
+1FA92;RAZOR;So;0;ON;;;;;N;;;;;
+1FA93;AXE;So;0;ON;;;;;N;;;;;
+1FA94;DIYA LAMP;So;0;ON;;;;;N;;;;;
+1FA95;BANJO;So;0;ON;;;;;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
diff --git a/unicode/emoji-data.txt b/unicode/emoji-data.txt
index 6e66455252..2fb5c3ff68 100644
--- a/unicode/emoji-data.txt
+++ b/unicode/emoji-data.txt
@@ -1,11 +1,11 @@
# emoji-data.txt
-# Date: 2018-02-07, 07:55:18 GMT
-# © 2018 Unicode®, Inc.
+# Date: 2019-01-15, 12:10:05 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Emoji Data for UTS #51
-# Version: 11.0
+# Version: 12.0
#
# For documentation and usage, see http://www.unicode.org/reports/tr51
#
@@ -45,7 +45,7 @@
25FB..25FE ; Emoji # 3.2 [4] (◻️..◾) white medium square..black medium-small square
2600..2604 ; Emoji # 1.1 [5] (☀️..☄️) sun..comet
260E ; Emoji # 1.1 [1] (☎️) telephone
-2611 ; Emoji # 1.1 [1] (☑️) ballot box with check
+2611 ; Emoji # 1.1 [1] (☑️) check box with check
2614..2615 ; Emoji # 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
2618 ; Emoji # 4.1 [1] (☘️) shamrock
261D ; Emoji # 1.1 [1] (☝️) index pointing up
@@ -82,14 +82,14 @@
26F7..26FA ; Emoji # 5.2 [4] (⛷️..⛺) skier..tent
26FD ; Emoji # 5.2 [1] (⛽) fuel pump
2702 ; Emoji # 1.1 [1] (✂️) scissors
-2705 ; Emoji # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji # 6.0 [1] (✅) check mark button
2708..2709 ; Emoji # 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Emoji # 6.0 [2] (✊..✋) raised fist..raised hand
270C..270D ; Emoji # 1.1 [2] (✌️..✍️) victory hand..writing hand
270F ; Emoji # 1.1 [1] (✏️) pencil
2712 ; Emoji # 1.1 [1] (✒️) black nib
-2714 ; Emoji # 1.1 [1] (✔️) heavy check mark
-2716 ; Emoji # 1.1 [1] (✖️) heavy multiplication x
+2714 ; Emoji # 1.1 [1] (✔️) check mark
+2716 ; Emoji # 1.1 [1] (✖️) multiplication sign
271D ; Emoji # 1.1 [1] (✝️) latin cross
2721 ; Emoji # 1.1 [1] (✡️) star of David
2728 ; Emoji # 6.0 [1] (✨) sparkles
@@ -100,8 +100,8 @@
274E ; Emoji # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji # 5.2 [1] (❗) exclamation mark
-2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heavy heart exclamation..red heart
-2795..2797 ; Emoji # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heart exclamation..red heart
+2795..2797 ; Emoji # 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Emoji # 1.1 [1] (➡️) right arrow
27B0 ; Emoji # 6.0 [1] (➰) curly loop
27BF ; Emoji # 6.0 [1] (➿) double curly loop
@@ -109,7 +109,7 @@
2B05..2B07 ; Emoji # 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Emoji # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji # 5.1 [1] (⭐) star
-2B55 ; Emoji # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji # 5.2 [1] (⭕) hollow red circle
3030 ; Emoji # 1.1 [1] (〰️) wavy dash
303D ; Emoji # 3.2 [1] (〽️) part alternation mark
3297 ; Emoji # 1.1 [1] (㊗️) Japanese “congratulations” button
@@ -206,7 +206,7 @@
1F62E..1F62F ; Emoji # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -214,6 +214,7 @@
1F6CB..1F6CF ; Emoji # 7.0 [5] (🛋️..🛏️) couch and lamp..bed
1F6D0 ; Emoji # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji # 12.0 [1] (🛕) hindu temple
1F6E0..1F6E5 ; Emoji # 7.0 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E9 ; Emoji # 7.0 [1] (🛩️) small airplane
1F6EB..1F6EC ; Emoji # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
@@ -222,6 +223,9 @@
1F6F4..1F6F6 ; Emoji # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji # 10.0 [1] (🤟) love-you gesture
@@ -231,27 +235,39 @@
1F931..1F932 ; Emoji # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1250
+# Total elements: 1311
# ================================================
@@ -278,19 +294,19 @@
26F5 ; Emoji_Presentation # 5.2 [1] (⛵) sailboat
26FA ; Emoji_Presentation # 5.2 [1] (⛺) tent
26FD ; Emoji_Presentation # 5.2 [1] (⛽) fuel pump
-2705 ; Emoji_Presentation # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji_Presentation # 6.0 [1] (✅) check mark button
270A..270B ; Emoji_Presentation # 6.0 [2] (✊..✋) raised fist..raised hand
2728 ; Emoji_Presentation # 6.0 [1] (✨) sparkles
274C ; Emoji_Presentation # 6.0 [1] (❌) cross mark
274E ; Emoji_Presentation # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji_Presentation # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji_Presentation # 5.2 [1] (❗) exclamation mark
-2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) plus sign..division sign
27B0 ; Emoji_Presentation # 6.0 [1] (➰) curly loop
27BF ; Emoji_Presentation # 6.0 [1] (➿) double curly loop
2B1B..2B1C ; Emoji_Presentation # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji_Presentation # 5.1 [1] (⭐) star
-2B55 ; Emoji_Presentation # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji_Presentation # 5.2 [1] (⭕) hollow red circle
1F004 ; Emoji_Presentation # 5.1 [1] (🀄) mahjong red dragon
1F0CF ; Emoji_Presentation # 6.0 [1] (🃏) joker
1F18E ; Emoji_Presentation # 6.0 [1] (🆎) AB button (blood type)
@@ -349,7 +365,7 @@
1F62E..1F62F ; Emoji_Presentation # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji_Presentation # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji_Presentation # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji_Presentation # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji_Presentation # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji_Presentation # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -357,10 +373,14 @@
1F6CC ; Emoji_Presentation # 7.0 [1] (🛌) person in bed
1F6D0 ; Emoji_Presentation # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji_Presentation # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji_Presentation # 12.0 [1] (🛕) hindu temple
1F6EB..1F6EC ; Emoji_Presentation # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
1F6F4..1F6F6 ; Emoji_Presentation # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji_Presentation # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji_Presentation # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji_Presentation # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji_Presentation # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji_Presentation # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji_Presentation # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji_Presentation # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Presentation # 10.0 [1] (🤟) love-you gesture
@@ -370,27 +390,39 @@
1F931..1F932 ; Emoji_Presentation # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji_Presentation # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji_Presentation # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji_Presentation # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji_Presentation # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji_Presentation # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji_Presentation # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji_Presentation # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji_Presentation # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji_Presentation # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji_Presentation # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji_Presentation # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji_Presentation # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji_Presentation # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji_Presentation # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji_Presentation # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji_Presentation # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji_Presentation # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji_Presentation # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji_Presentation # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji_Presentation # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji_Presentation # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji_Presentation # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji_Presentation # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji_Presentation # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji_Presentation # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji_Presentation # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji_Presentation # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji_Presentation # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji_Presentation # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1032
+# Total elements: 1093
# ================================================
@@ -417,12 +449,12 @@
1F3CB..1F3CC ; Emoji_Modifier_Base # 7.0 [2] (🏋️..🏌️) person lifting weights..person golfing
1F442..1F443 ; Emoji_Modifier_Base # 6.0 [2] (👂..👃) ear..nose
1F446..1F450 ; Emoji_Modifier_Base # 6.0 [11] (👆..👐) backhand index pointing up..open hands
-1F466..1F469 ; Emoji_Modifier_Base # 6.0 [4] (👦..👩) boy..woman
-1F46E ; Emoji_Modifier_Base # 6.0 [1] (👮) police officer
-1F470..1F478 ; Emoji_Modifier_Base # 6.0 [9] (👰..👸) bride with veil..princess
+1F466..1F478 ; Emoji_Modifier_Base # 6.0 [19] (👦..👸) boy..princess
1F47C ; Emoji_Modifier_Base # 6.0 [1] (👼) baby angel
1F481..1F483 ; Emoji_Modifier_Base # 6.0 [3] (💁..💃) person tipping hand..woman dancing
1F485..1F487 ; Emoji_Modifier_Base # 6.0 [3] (💅..💇) nail polish..person getting haircut
+1F48F ; Emoji_Modifier_Base # 6.0 [1] (💏) kiss
+1F491 ; Emoji_Modifier_Base # 6.0 [1] (💑) couple with heart
1F4AA ; Emoji_Modifier_Base # 6.0 [1] (💪) flexed biceps
1F574..1F575 ; Emoji_Modifier_Base # 7.0 [2] (🕴️..🕵️) man in suit levitating..detective
1F57A ; Emoji_Modifier_Base # 9.0 [1] (🕺) man dancing
@@ -434,20 +466,22 @@
1F6B4..1F6B6 ; Emoji_Modifier_Base # 6.0 [3] (🚴..🚶) person biking..person walking
1F6C0 ; Emoji_Modifier_Base # 6.0 [1] (🛀) person taking bath
1F6CC ; Emoji_Modifier_Base # 7.0 [1] (🛌) person in bed
+1F90F ; Emoji_Modifier_Base # 12.0 [1] (🤏) pinching hand
1F918 ; Emoji_Modifier_Base # 8.0 [1] (🤘) sign of the horns
-1F919..1F91C ; Emoji_Modifier_Base # 9.0 [4] (🤙..🤜) call me hand..right-facing fist
-1F91E ; Emoji_Modifier_Base # 9.0 [1] (🤞) crossed fingers
+1F919..1F91E ; Emoji_Modifier_Base # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Modifier_Base # 10.0 [1] (🤟) love-you gesture
1F926 ; Emoji_Modifier_Base # 9.0 [1] (🤦) person facepalming
1F930 ; Emoji_Modifier_Base # 9.0 [1] (🤰) pregnant woman
1F931..1F932 ; Emoji_Modifier_Base # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F939 ; Emoji_Modifier_Base # 9.0 [7] (🤳..🤹) selfie..person juggling
-1F93D..1F93E ; Emoji_Modifier_Base # 9.0 [2] (🤽..🤾) person playing water polo..person playing handball
+1F93C..1F93E ; Emoji_Modifier_Base # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
1F9B5..1F9B6 ; Emoji_Modifier_Base # 11.0 [2] (🦵..🦶) leg..foot
1F9B8..1F9B9 ; Emoji_Modifier_Base # 11.0 [2] (🦸..🦹) superhero..supervillain
-1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) adult..elf
+1F9BB ; Emoji_Modifier_Base # 12.0 [1] (🦻) ear with hearing aid
+1F9CD..1F9CF ; Emoji_Modifier_Base # 12.0 [3] (🧍..🧏) person standing..deaf person
+1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) person..elf
-# Total elements: 106
+# Total elements: 120
# ================================================
@@ -462,7 +496,7 @@
FE0F ; Emoji_Component # 3.2 [1] () VARIATION SELECTOR-16
1F1E6..1F1FF ; Emoji_Component # 6.0 [26] (🇦..🇿) regional indicator symbol letter a..regional indicator symbol letter z
1F3FB..1F3FF ; Emoji_Component # 8.0 [5] (🏻..🏿) light skin tone..dark skin tone
-1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red-haired..white-haired
+1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red hair..white hair
E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..cancel tag
# Total elements: 146
@@ -482,7 +516,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
21A9..21AA ; Extended_Pictographic# 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right
231A..231B ; Extended_Pictographic# 1.1 [2] (⌚..⌛) watch..hourglass done
2328 ; Extended_Pictographic# 1.1 [1] (⌨️) keyboard
-2388 ; Extended_Pictographic# 3.0 [1] (⎈️) HELM SYMBOL
+2388 ; Extended_Pictographic# 3.0 [1] (⎈) HELM SYMBOL
23CF ; Extended_Pictographic# 4.0 [1] (⏏️) eject button
23E9..23F3 ; Extended_Pictographic# 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done
23F8..23FA ; Extended_Pictographic# 7.0 [3] (⏸️..⏺️) pause button..record button
@@ -491,42 +525,42 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
25B6 ; Extended_Pictographic# 1.1 [1] (▶️) play button
25C0 ; Extended_Pictographic# 1.1 [1] (◀️) reverse button
25FB..25FE ; Extended_Pictographic# 3.2 [4] (◻️..◾) white medium square..black medium-small square
-2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★️) sun..BLACK STAR
-2607..2612 ; Extended_Pictographic# 1.1 [12] (☇️..☒️) LIGHTNING..BALLOT BOX WITH X
+2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★) sun..BLACK STAR
+2607..2612 ; Extended_Pictographic# 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X
2614..2615 ; Extended_Pictographic# 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
-2616..2617 ; Extended_Pictographic# 3.2 [2] (☖️..☗️) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+2616..2617 ; Extended_Pictographic# 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
2618 ; Extended_Pictographic# 4.1 [1] (☘️) shamrock
-2619 ; Extended_Pictographic# 3.0 [1] (☙️) REVERSED ROTATED FLORAL HEART BULLET
-261A..266F ; Extended_Pictographic# 1.1 [86] (☚️..♯️) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
-2670..2671 ; Extended_Pictographic# 3.0 [2] (♰️..♱️) WEST SYRIAC CROSS..EAST SYRIAC CROSS
-2672..267D ; Extended_Pictographic# 3.2 [12] (♲️..♽️) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+2619 ; Extended_Pictographic# 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET
+261A..266F ; Extended_Pictographic# 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
+2670..2671 ; Extended_Pictographic# 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS
+2672..267D ; Extended_Pictographic# 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
267E..267F ; Extended_Pictographic# 4.1 [2] (♾️..♿) infinity..wheelchair symbol
-2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀️..⚅️) DIE FACE-1..DIE FACE-6
-2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐️..⚑️) WHITE FLAG..BLACK FLAG
+2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
+2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
2692..269C ; Extended_Pictographic# 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis
-269D ; Extended_Pictographic# 5.1 [1] (⚝️) OUTLINED WHITE STAR
-269E..269F ; Extended_Pictographic# 5.2 [2] (⚞️..⚟️) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+269D ; Extended_Pictographic# 5.1 [1] (⚝) OUTLINED WHITE STAR
+269E..269F ; Extended_Pictographic# 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
26A0..26A1 ; Extended_Pictographic# 4.0 [2] (⚠️..⚡) warning..high voltage
-26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢️..⚱️) DOUBLED FEMALE SIGN..funeral urn
-26B2 ; Extended_Pictographic# 5.0 [1] (⚲️) NEUTER
-26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳️..⚼️) CERES..SESQUIQUADRATE
-26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿️) soccer ball..SQUARED KEY
-26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀️..⛃️) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
-26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍️) snowman without snow..DISABLED CAR
+26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn
+26B2 ; Extended_Pictographic# 5.0 [1] (⚲) NEUTER
+26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE
+26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY
+26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR
26CE ; Extended_Pictographic# 6.0 [1] (⛎) Ophiuchus
-26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡️) pick..RESTRICTED LEFT ENTRY-2
-26E2 ; Extended_Pictographic# 6.0 [1] (⛢️) ASTRONOMICAL SYMBOL FOR URANUS
-26E3 ; Extended_Pictographic# 5.2 [1] (⛣️) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
-26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤️..⛧️) PENTAGRAM..INVERTED PENTAGRAM
-26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨️..⛿️) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
-2700 ; Extended_Pictographic# 7.0 [1] (✀️) BLACK SAFETY SCISSORS
-2701..2704 ; Extended_Pictographic# 1.1 [4] (✁️..✄️) UPPER BLADE SCISSORS..WHITE SCISSORS
-2705 ; Extended_Pictographic# 6.0 [1] (✅) white heavy check mark
+26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2
+26E2 ; Extended_Pictographic# 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS
+26E3 ; Extended_Pictographic# 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM
+26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+2700 ; Extended_Pictographic# 7.0 [1] (✀) BLACK SAFETY SCISSORS
+2701..2704 ; Extended_Pictographic# 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS
+2705 ; Extended_Pictographic# 6.0 [1] (✅) check mark button
2708..2709 ; Extended_Pictographic# 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Extended_Pictographic# 6.0 [2] (✊..✋) raised fist..raised hand
270C..2712 ; Extended_Pictographic# 1.1 [7] (✌️..✒️) victory hand..black nib
-2714 ; Extended_Pictographic# 1.1 [1] (✔️) heavy check mark
-2716 ; Extended_Pictographic# 1.1 [1] (✖️) heavy multiplication x
+2714 ; Extended_Pictographic# 1.1 [1] (✔️) check mark
+2716 ; Extended_Pictographic# 1.1 [1] (✖️) multiplication sign
271D ; Extended_Pictographic# 1.1 [1] (✝️) latin cross
2721 ; Extended_Pictographic# 1.1 [1] (✡️) star of David
2728 ; Extended_Pictographic# 6.0 [1] (✨) sparkles
@@ -537,8 +571,8 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
274E ; Extended_Pictographic# 6.0 [1] (❎) cross mark button
2753..2755 ; Extended_Pictographic# 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Extended_Pictographic# 5.2 [1] (❗) exclamation mark
-2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧️) heavy heart exclamation..ROTATED FLORAL HEART BULLET
-2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET
+2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Extended_Pictographic# 1.1 [1] (➡️) right arrow
27B0 ; Extended_Pictographic# 6.0 [1] (➰) curly loop
27BF ; Extended_Pictographic# 6.0 [1] (➿) double curly loop
@@ -546,45 +580,46 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
2B05..2B07 ; Extended_Pictographic# 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Extended_Pictographic# 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Extended_Pictographic# 5.1 [1] (⭐) star
-2B55 ; Extended_Pictographic# 5.2 [1] (⭕) heavy large circle
+2B55 ; Extended_Pictographic# 5.2 [1] (⭕) hollow red circle
3030 ; Extended_Pictographic# 1.1 [1] (〰️) wavy dash
303D ; Extended_Pictographic# 3.2 [1] (〽️) part alternation mark
3297 ; Extended_Pictographic# 1.1 [1] (㊗️) Japanese “congratulations” button
3299 ; Extended_Pictographic# 1.1 [1] (㊙️) Japanese “secret” button
-1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀️..🀫️) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
-1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬️..🀯️) <reserved-1F02C>..<reserved-1F02F>
-1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰️..🂓️) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
-1F094..1F09F ; Extended_Pictographic# NA [12] (🂔️..🂟️) <reserved-1F094>..<reserved-1F09F>
-1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠️..🂮️) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
-1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯️..🂰️) <reserved-1F0AF>..<reserved-1F0B0>
-1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱️..🂾️) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
-1F0BF ; Extended_Pictographic# 7.0 [1] (🂿️) PLAYING CARD RED JOKER
-1F0C0 ; Extended_Pictographic# NA [1] (🃀️) <reserved-1F0C0>
-1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁️..🃏) PLAYING CARD ACE OF DIAMONDS..joker
-1F0D0 ; Extended_Pictographic# NA [1] (🃐️) <reserved-1F0D0>
-1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑️..🃟️) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
-1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠️..🃵️) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
-1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶️..🃿️) <reserved-1F0F6>..<reserved-1F0FF>
-1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍️..🄏️) <reserved-1F10D>..<reserved-1F10F>
-1F12F ; Extended_Pictographic# 11.0 [1] (🄯️) COPYLEFT SYMBOL
-1F16C..1F16F ; Extended_Pictographic# NA [4] (🅬️..🅯️) <reserved-1F16C>..<reserved-1F16F>
+1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬..🀯) <reserved-1F02C>..<reserved-1F02F>
+1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+1F094..1F09F ; Extended_Pictographic# NA [12] (🂔..🂟) <reserved-1F094>..<reserved-1F09F>
+1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯..🂰) <reserved-1F0AF>..<reserved-1F0B0>
+1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
+1F0BF ; Extended_Pictographic# 7.0 [1] (🂿) PLAYING CARD RED JOKER
+1F0C0 ; Extended_Pictographic# NA [1] (🃀) <reserved-1F0C0>
+1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker
+1F0D0 ; Extended_Pictographic# NA [1] (🃐) <reserved-1F0D0>
+1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
+1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
+1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶..🃿) <reserved-1F0F6>..<reserved-1F0FF>
+1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍..🄏) <reserved-1F10D>..<reserved-1F10F>
+1F12F ; Extended_Pictographic# 11.0 [1] (🄯) COPYLEFT SYMBOL
+1F16C ; Extended_Pictographic# 12.0 [1] (🅬) RAISED MR SIGN
+1F16D..1F16F ; Extended_Pictographic# NA [3] (🅭..🅯) <reserved-1F16D>..<reserved-1F16F>
1F170..1F171 ; Extended_Pictographic# 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
1F17E ; Extended_Pictographic# 6.0 [1] (🅾️) O button (blood type)
1F17F ; Extended_Pictographic# 5.2 [1] (🅿️) P button
1F18E ; Extended_Pictographic# 6.0 [1] (🆎) AB button (blood type)
1F191..1F19A ; Extended_Pictographic# 6.0 [10] (🆑..🆚) CL button..VS button
-1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭️..🇥️) <reserved-1F1AD>..<reserved-1F1E5>
+1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭..🇥) <reserved-1F1AD>..<reserved-1F1E5>
1F201..1F202 ; Extended_Pictographic# 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
-1F203..1F20F ; Extended_Pictographic# NA [13] (🈃️..🈏️) <reserved-1F203>..<reserved-1F20F>
+1F203..1F20F ; Extended_Pictographic# NA [13] (🈃..🈏) <reserved-1F203>..<reserved-1F20F>
1F21A ; Extended_Pictographic# 5.2 [1] (🈚) Japanese “free of charge” button
1F22F ; Extended_Pictographic# 5.2 [1] (🈯) Japanese “reserved” button
1F232..1F23A ; Extended_Pictographic# 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button
-1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼️..🈿️) <reserved-1F23C>..<reserved-1F23F>
-1F249..1F24F ; Extended_Pictographic# NA [7] (🉉️..🉏️) <reserved-1F249>..<reserved-1F24F>
+1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼..🈿) <reserved-1F23C>..<reserved-1F23F>
+1F249..1F24F ; Extended_Pictographic# NA [7] (🉉..🉏) <reserved-1F249>..<reserved-1F24F>
1F250..1F251 ; Extended_Pictographic# 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
-1F252..1F25F ; Extended_Pictographic# NA [14] (🉒️..🉟️) <reserved-1F252>..<reserved-1F25F>
-1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠️..🉥️) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
-1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦️..🋿️) <reserved-1F266>..<reserved-1F2FF>
+1F252..1F25F ; Extended_Pictographic# NA [14] (🉒..🉟) <reserved-1F252>..<reserved-1F25F>
+1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦..🋿) <reserved-1F266>..<reserved-1F2FF>
1F300..1F320 ; Extended_Pictographic# 6.0 [33] (🌀..🌠) cyclone..shooting star
1F321..1F32C ; Extended_Pictographic# 7.0 [12] (🌡️..🌬️) thermometer..wind face
1F32D..1F32F ; Extended_Pictographic# 8.0 [3] (🌭..🌯) hot dog..burrito
@@ -594,7 +629,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F37D ; Extended_Pictographic# 7.0 [1] (🍽️) fork and knife with plate
1F37E..1F37F ; Extended_Pictographic# 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn
1F380..1F393 ; Extended_Pictographic# 6.0 [20] (🎀..🎓) ribbon..graduation cap
-1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔️..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
+1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
1F3A0..1F3C4 ; Extended_Pictographic# 6.0 [37] (🎠..🏄) carousel horse..person surfing
1F3C5 ; Extended_Pictographic# 7.0 [1] (🏅) sports medal
1F3C6..1F3CA ; Extended_Pictographic# 6.0 [5] (🏆..🏊) trophy..person swimming
@@ -602,7 +637,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F3CF..1F3D3 ; Extended_Pictographic# 8.0 [5] (🏏..🏓) cricket game..ping pong
1F3D4..1F3DF ; Extended_Pictographic# 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium
1F3E0..1F3F0 ; Extended_Pictographic# 6.0 [17] (🏠..🏰) house..castle
-1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱️..🏷️) WHITE PENNANT..label
+1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱..🏷️) WHITE PENNANT..label
1F3F8..1F3FA ; Extended_Pictographic# 8.0 [3] (🏸..🏺) badminton..amphora
1F400..1F43E ; Extended_Pictographic# 6.0 [63] (🐀..🐾) rat..paw prints
1F43F ; Extended_Pictographic# 7.0 [1] (🐿️) chipmunk
@@ -611,15 +646,15 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F442..1F4F7 ; Extended_Pictographic# 6.0[182] (👂..📷) ear..camera
1F4F8 ; Extended_Pictographic# 7.0 [1] (📸) camera with flash
1F4F9..1F4FC ; Extended_Pictographic# 6.0 [4] (📹..📼) video camera..videocassette
-1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾️) film projector..PORTABLE STEREO
+1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO
1F4FF ; Extended_Pictographic# 8.0 [1] (📿) prayer beads
1F500..1F53D ; Extended_Pictographic# 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button
-1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆️..🕊️) WHITE LATIN CROSS..dove
-1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏️) kaaba..BOWL OF HYGIEIA
+1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove
+1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA
1F550..1F567 ; Extended_Pictographic# 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty
-1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨️..🕹️) RIGHT SPEAKER..joystick
+1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick
1F57A ; Extended_Pictographic# 9.0 [1] (🕺) man dancing
-1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻️..🖣️) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
+1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
1F5A4 ; Extended_Pictographic# 9.0 [1] (🖤) black heart
1F5A5..1F5FA ; Extended_Pictographic# 7.0 [86] (🖥️..🗺️) desktop computer..world map
1F5FB..1F5FF ; Extended_Pictographic# 6.0 [5] (🗻..🗿) mount fuji..moai
@@ -644,32 +679,37 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F62E..1F62F ; Extended_Pictographic# 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Extended_Pictographic# 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Extended_Pictographic# 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Extended_Pictographic# 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Extended_Pictographic# 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Extended_Pictographic# 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
1F680..1F6C5 ; Extended_Pictographic# 6.0 [70] (🚀..🛅) rocket..left luggage
-1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆️..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
+1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
1F6D0 ; Extended_Pictographic# 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Extended_Pictographic# 9.0 [2] (🛑..🛒) stop sign..shopping cart
-1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓️..🛔️) STUPA..PAGODA
-1F6D5..1F6DF ; Extended_Pictographic# NA [11] (🛕️..🛟️) <reserved-1F6D5>..<reserved-1F6DF>
+1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓..🛔) STUPA..PAGODA
+1F6D5 ; Extended_Pictographic# 12.0 [1] (🛕) hindu temple
+1F6D6..1F6DF ; Extended_Pictographic# NA [10] (🛖..🛟) <reserved-1F6D6>..<reserved-1F6DF>
1F6E0..1F6EC ; Extended_Pictographic# 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival
-1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭️..🛯️) <reserved-1F6ED>..<reserved-1F6EF>
+1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭..🛯) <reserved-1F6ED>..<reserved-1F6EF>
1F6F0..1F6F3 ; Extended_Pictographic# 7.0 [4] (🛰️..🛳️) satellite..passenger ship
1F6F4..1F6F6 ; Extended_Pictographic# 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Extended_Pictographic# 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Extended_Pictographic# 11.0 [1] (🛹) skateboard
-1F6FA..1F6FF ; Extended_Pictographic# NA [6] (🛺️..🛿️) <reserved-1F6FA>..<reserved-1F6FF>
-1F774..1F77F ; Extended_Pictographic# NA [12] (🝴️..🝿️) <reserved-1F774>..<reserved-1F77F>
-1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕️..🟘️) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
-1F7D9..1F7FF ; Extended_Pictographic# NA [39] (🟙️..🟿️) <reserved-1F7D9>..<reserved-1F7FF>
-1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌️..🠏️) <reserved-1F80C>..<reserved-1F80F>
-1F848..1F84F ; Extended_Pictographic# NA [8] (🡈️..🡏️) <reserved-1F848>..<reserved-1F84F>
-1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚️..🡟️) <reserved-1F85A>..<reserved-1F85F>
-1F888..1F88F ; Extended_Pictographic# NA [8] (🢈️..🢏️) <reserved-1F888>..<reserved-1F88F>
-1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮️..🣿️) <reserved-1F8AE>..<reserved-1F8FF>
-1F90C..1F90F ; Extended_Pictographic# NA [4] (🤌️..🤏️) <reserved-1F90C>..<reserved-1F90F>
+1F6FA ; Extended_Pictographic# 12.0 [1] (🛺) auto rickshaw
+1F6FB..1F6FF ; Extended_Pictographic# NA [5] (🛻..🛿) <reserved-1F6FB>..<reserved-1F6FF>
+1F774..1F77F ; Extended_Pictographic# NA [12] (🝴..🝿) <reserved-1F774>..<reserved-1F77F>
+1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F7D9..1F7DF ; Extended_Pictographic# NA [7] (🟙..🟟) <reserved-1F7D9>..<reserved-1F7DF>
+1F7E0..1F7EB ; Extended_Pictographic# 12.0 [12] (🟠..🟫) orange circle..brown square
+1F7EC..1F7FF ; Extended_Pictographic# NA [20] (🟬..🟿) <reserved-1F7EC>..<reserved-1F7FF>
+1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌..🠏) <reserved-1F80C>..<reserved-1F80F>
+1F848..1F84F ; Extended_Pictographic# NA [8] (🡈..🡏) <reserved-1F848>..<reserved-1F84F>
+1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚..🡟) <reserved-1F85A>..<reserved-1F85F>
+1F888..1F88F ; Extended_Pictographic# NA [8] (🢈..🢏) <reserved-1F888>..<reserved-1F88F>
+1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮..🣿) <reserved-1F8AE>..<reserved-1F8FF>
+1F90C ; Extended_Pictographic# NA [1] (🤌) <reserved-1F90C>
+1F90D..1F90F ; Extended_Pictographic# 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Extended_Pictographic# 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Extended_Pictographic# 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Extended_Pictographic# 10.0 [1] (🤟) love-you gesture
@@ -679,35 +719,50 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F931..1F932 ; Extended_Pictographic# 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Extended_Pictographic# 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Extended_Pictographic# 9.0 [3] (🤼..🤾) people wrestling..person playing handball
-1F93F ; Extended_Pictographic# NA [1] (🤿️) <reserved-1F93F>
+1F93F ; Extended_Pictographic# 12.0 [1] (🤿) diving mask
1F940..1F945 ; Extended_Pictographic# 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Extended_Pictographic# 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Extended_Pictographic# 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Extended_Pictographic# 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Extended_Pictographic# 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Extended_Pictographic# 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
-1F971..1F972 ; Extended_Pictographic# NA [2] (🥱️..🥲️) <reserved-1F971>..<reserved-1F972>
+1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Extended_Pictographic# 12.0 [1] (🥱) yawning face
+1F972 ; Extended_Pictographic# NA [1] (🥲) <reserved-1F972>
1F973..1F976 ; Extended_Pictographic# 11.0 [4] (🥳..🥶) partying face..cold face
-1F977..1F979 ; Extended_Pictographic# NA [3] (🥷️..🥹️) <reserved-1F977>..<reserved-1F979>
+1F977..1F979 ; Extended_Pictographic# NA [3] (🥷..🥹) <reserved-1F977>..<reserved-1F979>
1F97A ; Extended_Pictographic# 11.0 [1] (🥺) pleading face
-1F97B ; Extended_Pictographic# NA [1] (🥻️) <reserved-1F97B>
-1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Extended_Pictographic# 12.0 [1] (🥻) sari
+1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Extended_Pictographic# 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Extended_Pictographic# 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Extended_Pictographic# 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9A3..1F9AF ; Extended_Pictographic# NA [13] (🦣️..🦯️) <reserved-1F9A3>..<reserved-1F9AF>
-1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red-haired..supervillain
-1F9BA..1F9BF ; Extended_Pictographic# NA [6] (🦺️..🦿️) <reserved-1F9BA>..<reserved-1F9BF>
+1F9A3..1F9A4 ; Extended_Pictographic# NA [2] (🦣..🦤) <reserved-1F9A3>..<reserved-1F9A4>
+1F9A5..1F9AA ; Extended_Pictographic# 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AB..1F9AD ; Extended_Pictographic# NA [3] (🦫..🦭) <reserved-1F9AB>..<reserved-1F9AD>
+1F9AE..1F9AF ; Extended_Pictographic# 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Extended_Pictographic# 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Extended_Pictographic# 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Extended_Pictographic# 11.0 [2] (🧁..🧂) cupcake..salt
-1F9C3..1F9CF ; Extended_Pictographic# NA [13] (🧃️..🧏️) <reserved-1F9C3>..<reserved-1F9CF>
+1F9C3..1F9CA ; Extended_Pictographic# 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CB..1F9CC ; Extended_Pictographic# NA [2] (🧋..🧌) <reserved-1F9CB>..<reserved-1F9CC>
+1F9CD..1F9CF ; Extended_Pictographic# 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Extended_Pictographic# 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Extended_Pictographic# 11.0 [25] (🧧..🧿) red envelope..nazar amulet
-1FA00..1FA5F ; Extended_Pictographic# NA [96] (🨀️..🩟️) <reserved-1FA00>..<reserved-1FA5F>
-1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠️..🩭️) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
-1FA6E..1FFFD ; Extended_Pictographic# NA[1424] (🩮️..🿽️) <reserved-1FA6E>..<reserved-1FFFD>
+1FA00..1FA53 ; Extended_Pictographic# 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+1FA54..1FA5F ; Extended_Pictographic# NA [12] (🩔..🩟) <reserved-1FA54>..<reserved-1FA5F>
+1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+1FA6E..1FA6F ; Extended_Pictographic# NA [2] (🩮..🩯) <reserved-1FA6E>..<reserved-1FA6F>
+1FA70..1FA73 ; Extended_Pictographic# 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA74..1FA77 ; Extended_Pictographic# NA [4] (🩴..🩷) <reserved-1FA74>..<reserved-1FA77>
+1FA78..1FA7A ; Extended_Pictographic# 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA7B..1FA7F ; Extended_Pictographic# NA [5] (🩻..🩿) <reserved-1FA7B>..<reserved-1FA7F>
+1FA80..1FA82 ; Extended_Pictographic# 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA83..1FA8F ; Extended_Pictographic# NA [13] (🪃..🪏) <reserved-1FA83>..<reserved-1FA8F>
+1FA90..1FA95 ; Extended_Pictographic# 12.0 [6] (🪐..🪕) ringed planet..banjo
+1FA96..1FFFD ; Extended_Pictographic# NA[1384] (🪖..🿽) <reserved-1FA96>..<reserved-1FFFD>
# Total elements: 3793