aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig3
-rw-r--r--.github/workflows/ci.yml5
-rw-r--r--.github/workflows/squash-typos.yml33
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--runtime/autoload/dist/ft.vim2
-rw-r--r--runtime/autoload/health/provider.vim2
-rw-r--r--runtime/autoload/msgpack.vim6
-rw-r--r--runtime/doc/api.txt90
-rw-r--r--runtime/doc/diagnostic.txt18
-rw-r--r--runtime/doc/editing.txt10
-rw-r--r--runtime/doc/eval.txt64
-rw-r--r--runtime/doc/lsp.txt33
-rw-r--r--runtime/doc/lua.txt47
-rw-r--r--runtime/doc/map.txt7
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt6
-rw-r--r--runtime/doc/options.txt8
-rw-r--r--runtime/doc/quickref.txt1
-rw-r--r--runtime/doc/sign.txt5
-rw-r--r--runtime/doc/syntax.txt2
-rw-r--r--runtime/doc/treesitter.txt14
-rw-r--r--runtime/doc/usr_41.txt1
-rw-r--r--runtime/doc/windows.txt10
-rw-r--r--runtime/filetype.vim13
-rw-r--r--runtime/ftplugin/zsh.vim8
-rw-r--r--runtime/indent/sh.vim2
-rw-r--r--runtime/indent/xml.vim8
-rw-r--r--runtime/lua/vim/diagnostic.lua8
-rw-r--r--runtime/lua/vim/lsp.lua66
-rw-r--r--runtime/lua/vim/lsp/handlers.lua2
-rw-r--r--runtime/lua/vim/lsp/log.lua4
-rw-r--r--runtime/lua/vim/lsp/rpc.lua5
-rw-r--r--runtime/lua/vim/lsp/util.lua29
-rw-r--r--runtime/lua/vim/uri.lua4
-rw-r--r--runtime/optwin.vim4
-rw-r--r--runtime/pack/dist/opt/matchit/autoload/matchit.vim251
-rw-r--r--runtime/pack/dist/opt/matchit/doc/matchit.txt10
-rw-r--r--runtime/pack/dist/opt/matchit/plugin/matchit.vim42
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim15
-rw-r--r--runtime/syntax/debcontrol.vim20
-rw-r--r--runtime/syntax/dep3patch.vim57
-rw-r--r--runtime/syntax/dtd.vim6
-rw-r--r--runtime/syntax/xml.vim12
-rw-r--r--runtime/syntax/zsh.vim223
-rw-r--r--scripts/squash_typos.py263
-rw-r--r--src/nvim/api/buffer.c63
-rw-r--r--src/nvim/api/keysets.lua12
-rw-r--r--src/nvim/api/private/helpers.c185
-rw-r--r--src/nvim/api/vim.c73
-rw-r--r--src/nvim/api/window.c2
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/diff.c8
-rw-r--r--src/nvim/edit.c13
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c96
-rw-r--r--src/nvim/event/loop.c2
-rw-r--r--src/nvim/ex_cmds.c5
-rw-r--r--src/nvim/ex_cmds2.c2
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c234
-rw-r--r--src/nvim/ex_docmd.h21
-rw-r--r--src/nvim/ex_getln.c32
-rw-r--r--src/nvim/file_search.c19
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua4
-rw-r--r--src/nvim/generators/gen_keysets.lua15
-rw-r--r--src/nvim/getchar.c8
-rw-r--r--src/nvim/indent_c.c19
-rw-r--r--src/nvim/lua/executor.c64
-rw-r--r--src/nvim/lua/executor.h1
-rw-r--r--src/nvim/lua/spell.c99
-rw-r--r--src/nvim/lua/spell.h12
-rw-r--r--src/nvim/lua/stdlib.c5
-rw-r--r--src/nvim/lua/vim.lua2
-rw-r--r--src/nvim/menu.c2
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c39
-rw-r--r--src/nvim/option.h1
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/plines.c2
-rw-r--r--src/nvim/popupmnu.c43
-rw-r--r--src/nvim/quickfix.c2
-rw-r--r--src/nvim/regexp_nfa.c10
-rw-r--r--src/nvim/screen.c39
-rw-r--r--src/nvim/sign.c3
-rw-r--r--src/nvim/spell.c8
-rw-r--r--src/nvim/spellfile.c4
-rw-r--r--src/nvim/terminal.c43
-rw-r--r--src/nvim/testdir/runtest.vim6
-rw-r--r--src/nvim/testdir/setup.vim2
-rw-r--r--src/nvim/testdir/test_autocmd.vim90
-rw-r--r--src/nvim/testdir/test_blob.vim8
-rw-r--r--src/nvim/testdir/test_cmdline.vim37
-rw-r--r--src/nvim/testdir/test_display.vim25
-rw-r--r--src/nvim/testdir/test_filechanged.vim149
-rw-r--r--src/nvim/testdir/test_fileformat.vim246
-rw-r--r--src/nvim/testdir/test_filetype.vim5
-rw-r--r--src/nvim/testdir/test_listchars.vim16
-rw-r--r--src/nvim/testdir/test_messages.vim8
-rw-r--r--src/nvim/testdir/test_options.vim21
-rw-r--r--src/nvim/testdir/test_put.vim22
-rw-r--r--src/nvim/testdir/test_signs.vim25
-rw-r--r--src/nvim/testdir/test_visual.vim2
-rw-r--r--src/nvim/tui/tui.c2
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c4
-rw-r--r--test/README.md4
-rw-r--r--test/functional/api/command_spec.lua121
-rw-r--r--test/functional/api/vim_spec.lua74
-rw-r--r--test/functional/editor/mode_insert_spec.lua11
-rw-r--r--test/functional/editor/mode_visual_spec.lua27
-rw-r--r--test/functional/legacy/options_spec.lua5
-rw-r--r--test/functional/lua/api_spec.lua11
-rw-r--r--test/functional/lua/spell_spec.lua53
-rw-r--r--test/functional/lua/uri_spec.lua6
-rw-r--r--test/functional/plugin/lsp_spec.lua37
-rw-r--r--test/functional/terminal/mouse_spec.lua92
-rw-r--r--test/functional/ui/popupmenu_spec.lua130
120 files changed, 2620 insertions, 1256 deletions
diff --git a/.editorconfig b/.editorconfig
index bb6a1423ef..22fee54b22 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -8,6 +8,9 @@ end_of_line = lf
insert_final_newline = true
charset = utf-8
+[*.{c,h,in,lua}]
+max_line_length = 100
+
[{Makefile,**/Makefile,runtime/doc/*.txt}]
indent_style = tab
indent_size = 8
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6a81ee4238..d2eef13098 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,6 +10,11 @@ on:
- 'master'
- 'release-[0-9]+.[0-9]+'
+# Cancel any in-progress CI runs for a PR if it is updated
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
+ cancel-in-progress: true
+
jobs:
unixish:
name: ${{ matrix.runner }} ${{ matrix.flavor }} (cc=${{ matrix.cc }})
diff --git a/.github/workflows/squash-typos.yml b/.github/workflows/squash-typos.yml
deleted file mode 100644
index 6779589dc6..0000000000
--- a/.github/workflows/squash-typos.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: Squash Typo Pull Requests
-
-on:
- pull_request_target:
- types: labeled
-concurrency:
- group: ${{ github.workflow }}
-jobs:
- build:
- if: github.event.label.name == 'typo'
- runs-on: ubuntu-latest
-
- permissions:
- contents: write
- pull-requests: write
-
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
- - uses: actions/setup-python@v2
-
- - name: Setup git config
- run: |
- git config --global user.name 'marvim'
- git config --global user.email 'marvim@users.noreply.github.com'
-
- - run: python scripts/squash_typos.py
- env:
- PR_NUMBER: ${{ github.event.number }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index faf9181a2c..b02aeb1ed1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@ If you want to help but don't know where to start, here are some
low-risk/isolated tasks:
- [Merge a Vim patch].
-- Try a [good first issue](../../labels/good-first-issue) or [complexity:low] issue.
+- Try a [complexity:low] issue.
- Fix bugs found by [Clang](#clang-scan-build), [PVS](#pvs-studio) or
[Coverity](#coverity).
- [Improve documentation][wiki-contribute-help]
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 366d7b009d..9933ace8c2 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: 2021 Nov 27
+" Last Change: 2021 Dec 17
" These functions are moved here from runtime/filetype.vim to make startup
" faster.
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index 7b4dce3441..e6523aa215 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -523,7 +523,7 @@ function! s:check_virtualenv() abort
let hint = '$PATH ambiguities in subshells typically are '
\.'caused by your shell config overriding the $PATH previously set by the '
\.'virtualenv. Either prevent them from doing so, or use this workaround: '
- \.'https://vi.stackexchange.com/a/7654'
+ \.'https://vi.stackexchange.com/a/34996'
let hints[hint] = v:true
endif
endfor
diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim
index 7dd225e3d9..7f98a5b230 100644
--- a/runtime/autoload/msgpack.vim
+++ b/runtime/autoload/msgpack.vim
@@ -56,6 +56,7 @@ function s:msgpack_init_python() abort
\. " time = datetime.datetime.fromtimestamp(timestamp)\n"
\. " return time.strftime(fmt)\n"
\. "def shada_dict_strptime():\n"
+ \. " import calendar\n"
\. " import datetime\n"
\. " import vim\n"
\. " fmt = vim.eval('a:format')\n"
@@ -64,7 +65,10 @@ function s:msgpack_init_python() abort
\. " try:\n"
\. " timestamp = int(timestamp.timestamp())\n"
\. " except:\n"
- \. " timestamp = int(timestamp.strftime('%s'))\n"
+ \. " try:\n"
+ \. " timestamp = int(timestamp.strftime('%s'))\n"
+ \. " except:\n"
+ \. " timestamp = calendar.timegm(timestamp.utctimetuple())\n"
\. " if timestamp > 2 ** 31:\n"
\. " tsabs = abs(timestamp)\n"
\. " return ('{\"_TYPE\": v:msgpack_types.integer,'\n"
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 8fb6290e50..efffca72ad 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -361,7 +361,7 @@ UTF-32 and UTF-16 sizes of the deleted region is also passed as additional
arguments {old_utf32_size} and {old_utf16_size}.
"on_changedtick" is invoked when |b:changedtick| was incremented but no text
-was changed. The parameters recieved are ("changedtick", {buf}, {changedtick}).
+was changed. The parameters received are ("changedtick", {buf}, {changedtick}).
*api-lua-detach*
In-process Lua callbacks can detach by returning `true`. This will detach all
@@ -626,6 +626,56 @@ nvim__stats() *nvim__stats()*
Return: ~
Map of various internal stats.
+ *nvim_add_user_command()*
+nvim_add_user_command({name}, {command}, {*opts})
+ Create a new user command |user-commands|
+
+ {name} is the name of the new command. The name must begin
+ with an uppercase letter.
+
+ {command} is the replacement text or Lua function to execute.
+
+ Example: >
+ :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
+ :SayHello
+ Hello world!
+<
+
+ Parameters: ~
+ {name} Name of the new user command. Must begin with
+ an uppercase letter.
+ {command} Replacement command to execute when this user
+ command is executed. When called from Lua, the
+ command can also be a Lua function. The
+ function is called with a single table argument
+ that contains the following keys:
+ • args: (string) The args passed to the
+ command, if any |<args>|
+ • bang: (boolean) "true" if the command was
+ executed with a ! modifier |<bang>|
+ • line1: (number) The starting line of the
+ command range |<line1>|
+ • line2: (number) The final line of the command
+ range |<line2>|
+ • range: (number) The number of items in the
+ command range: 0, 1, or 2 |<range>|
+ • count: (number) Any count supplied |<count>|
+ • reg: (string) The optional register, if
+ specified |<reg>|
+ • mods: (string) Command modifiers, if any
+ |<mods>|
+ {opts} Optional command attributes. See
+ |command-attributes| for more details. To use
+ boolean attributes (such as |:command-bang| or
+ |:command-bar|) set the value to "true". When
+ using a Lua function for {command} you can also
+ provide a "desc" key that will be displayed
+ when listing commands. In addition to the
+ string options listed in |:command-complete|,
+ the "complete" key also accepts a Lua function
+ which works like the "customlist" completion
+ mode |:command-complete-customlist|.
+
nvim_call_atomic({calls}) *nvim_call_atomic()*
Calls many API methods atomically.
@@ -634,7 +684,7 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
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).
+ have such side effects, e.g. |:sleep| may wake timers).
2. To minimize RPC overhead (roundtrips) of a sequence of many
requests.
@@ -698,7 +748,7 @@ nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()*
|nvim_set_keymap()|
nvim_del_mark({name}) *nvim_del_mark()*
- Deletes a uppercase/file named mark. See |mark-motions|.
+ Deletes an uppercase/file named mark. See |mark-motions|.
Note:
fails with error if a lowercase or buffer local named mark
@@ -714,6 +764,12 @@ nvim_del_mark({name}) *nvim_del_mark()*
|nvim_buf_del_mark()|
|nvim_get_mark()|
+nvim_del_user_command({name}) *nvim_del_user_command()*
+ Delete a user-defined command.
+
+ Parameters: ~
+ {name} Name of the command to delete.
+
nvim_del_var({name}) *nvim_del_var()*
Removes a global (g:) variable.
@@ -1061,7 +1117,7 @@ nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()*
Parameters: ~
{name} Option name
{opts} Optional parameters
- • scope: One of 'global' or 'local'. Analagous to
+ • scope: One of 'global' or 'local'. Analogous to
|:setglobal| and |:setlocal|, respectively.
Return: ~
@@ -1545,7 +1601,7 @@ nvim_set_option_value({name}, {value}, {*opts})
{name} Option name
{value} New option value
{opts} Optional parameters
- • scope: One of 'global' or 'local'. Analagous to
+ • scope: One of 'global' or 'local'. Analogous to
|:setglobal| and |:setlocal|, respectively.
nvim_set_var({name}, {value}) *nvim_set_var()*
@@ -1790,6 +1846,16 @@ nvim__buf_redraw_range({buffer}, {first}, {last})
nvim__buf_stats({buffer}) *nvim__buf_stats()*
TODO: Documentation
+ *nvim_buf_add_user_command()*
+nvim_buf_add_user_command({buffer}, {name}, {command}, {*opts})
+ Create a new user command |user-commands| in the given buffer.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer.
+
+ See also: ~
+ nvim_add_user_command
+
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activates buffer-update events on a channel, or as Lua
callbacks.
@@ -1925,6 +1991,18 @@ nvim_buf_del_mark({buffer}, {name}) *nvim_buf_del_mark()*
|nvim_buf_set_mark()|
|nvim_del_mark()|
+ *nvim_buf_del_user_command()*
+nvim_buf_del_user_command({buffer}, {name})
+ Delete a buffer-local user-defined command.
+
+ Only commands created with |:command-buffer| or
+ |nvim_buf_add_user_command()| can be deleted with this
+ function.
+
+ Parameters: ~
+ {buffer} Buffer handle, or 0 for current buffer.
+ {name} Name of the command to delete.
+
nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
Removes a buffer-scoped (b:) variable
@@ -2687,7 +2765,7 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()*
true if the window is valid, false otherwise
nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()*
- Sets the current buffer in a window, without side-effects
+ Sets the current buffer in a window, without side effects
Attributes: ~
not allowed when |textlock| is active
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index a825435179..bb36fa46f6 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -132,7 +132,7 @@ with |vim.notify()|: >
In this example, there is nothing to do when diagnostics are hidden, so we
omit the "hide" function.
-Existing handlers can be overriden. For example, use the following to only
+Existing handlers can be overridden. For example, use the following to only
show a sign for the highest severity diagnostic on a given line: >
-- Create a custom namespace. This will aggregate signs from all other
@@ -177,8 +177,9 @@ All highlights defined for diagnostics begin with `Diagnostic` followed by
the type of highlight (e.g., `Sign`, `Underline`, etc.) and the severity (e.g.
`Error`, `Warn`, etc.)
-Sign, underline and virtual text highlights (by default) are linked to their
-corresponding default highlight.
+By default, highlights for signs, floating windows, and virtual text are linked to the
+corresponding default highlight. Underline highlights are not linked and use their
+own default highlight groups.
For example, the default highlighting for |hl-DiagnosticSignError| is linked
to |hl-DiagnosticError|. To change the default (and therefore the linked
@@ -298,7 +299,6 @@ Example: >
autocmd DiagnosticChanged * lua vim.diagnostic.setqflist({open = false })
<
==============================================================================
-==============================================================================
Lua module: vim.diagnostic *diagnostic-api*
config({opts}, {namespace}) *vim.diagnostic.config()*
@@ -343,7 +343,10 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
|diagnostic-severity|
• virtual_text: (default true) Use virtual
- text for diagnostics. Options:
+ text for diagnostics. If multiple
+ diagnostics are set for a namespace, one
+ prefix per diagnostic + the last diagnostic
+ message are shown. Options:
• severity: Only show virtual text for
diagnostics matching the given severity
|diagnostic-severity|
@@ -353,6 +356,11 @@ config({opts}, {namespace}) *vim.diagnostic.config()*
is more than one diagnostic source in the
buffer. Otherwise, any truthy value means
to always show the diagnostic source.
+ • spacing: (number) Amount of empty spaces
+ inserted at the beginning of the virtual
+ text.
+ • prefix: (string) Prepend diagnostic
+ message with prefix.
• format: (function) A function that takes
a diagnostic as input and returns a
string. The return value is the text used
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 4e3173cfa9..14df41e6c8 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1253,10 +1253,12 @@ working directory. If a local working directory (tab or window) does not
exist, the next-higher scope in the hierarchy applies.
*:cd* *E747* *E472*
-:cd[!] On non-Unix systems: Print the current directory
- name. On Unix systems: Change the current directory
- to the home directory. Use |:pwd| to print the
- current directory on all systems.
+:cd[!] On non-Unix systems when 'cdhome' is off: Print the
+ current directory name.
+ Otherwise: Change the current directory to the home
+ directory. Clear any window-local directory.
+ Use |:pwd| to print the current directory on all
+ systems.
:cd[!] {path} Change the current directory to {path}.
If {path} is relative, it is searched for in the
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index aef303195d..c2158f3a1e 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2278,7 +2278,7 @@ USAGE RESULT DESCRIPTION ~
abs({expr}) Float or Number absolute value of {expr}
acos({expr}) Float arc cosine of {expr}
add({object}, {item}) List/Blob append {item} to {object}
-and({expr}, {expr}) Number bitwise AND
+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}
@@ -2310,7 +2310,7 @@ assert_notmatch({pat}, {text} [, {msg}])
assert_report({msg}) Number report a test failure
assert_true({actual} [, {msg}]) Number assert {actual} is true
atan({expr}) Float arc tangent of {expr}
-atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2}
+atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2}
browse({save}, {title}, {initdir}, {default})
String put up a file requester
browsedir({title}, {initdir}) String put up a directory requester
@@ -2336,7 +2336,7 @@ char2nr({expr}[, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
charidx({string}, {idx} [, {countcc}])
Number char index of byte {idx} in {string}
chdir({dir}) String change current working directory
-cindent({lnum}) Number C indent for line {lnum}
+cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
@@ -2349,7 +2349,7 @@ copy({expr}) any make a shallow copy of {expr}
cos({expr}) Float cosine of {expr}
cosh({expr}) Float hyperbolic cosine of {expr}
count({list}, {expr} [, {ic} [, {start}]])
- Number count how many {expr} are in {list}
+ Number count how many {expr} are in {list}
cscope_connection([{num}, {dbpath} [, {prepend}]])
Number checks existence of cscope connection
ctxget([{index}]) Dict return the |context| dict at {index}
@@ -2362,7 +2362,7 @@ ctxsize() Number return |context-stack| size
cursor({lnum}, {col} [, {off}])
Number move cursor to {lnum}, {col}, {off}
cursor({list}) Number move cursor to position in {list}
-debugbreak({pid}) Number interrupt process being debugged
+debugbreak({pid}) Number interrupt process being debugged
deepcopy({expr} [, {noref}]) any make a full copy of {expr}
delete({fname} [, {flags}]) Number delete the file or directory {fname}
deletebufline({buf}, {first}[, {last}])
@@ -2381,7 +2381,7 @@ eval({string}) any evaluate {string} into its value
eventhandler() Number |TRUE| if inside an event handler
executable({expr}) Number 1 if executable {expr} exists
execute({command}) String execute and capture output of {command}
-exepath({expr}) String full path of the command {expr}
+exepath({expr}) String full path of the command {expr}
exists({expr}) Number |TRUE| if {expr} exists
extend({expr1}, {expr2} [, {expr3}])
List/Dict insert items of {expr2} into {expr1}
@@ -2410,10 +2410,11 @@ foldlevel({lnum}) Number fold level at {lnum}
foldtext() String line displayed for closed fold
foldtextresult({lnum}) String text for closed fold at {lnum}
foreground() Number bring the Vim window to the foreground
+fullcommand({name}) String get full command from {name}
funcref({name} [, {arglist}] [, {dict}])
- Funcref reference to function {name}
+ Funcref reference to function {name}
function({name} [, {arglist}] [, {dict}])
- Funcref named reference to function {name}
+ Funcref named reference to function {name}
garbagecollect([{atexit}]) none free memory, breaking cyclic references
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
@@ -2501,11 +2502,11 @@ inputlist({textlist}) Number let the user pick from a choice list
inputrestore() Number restore typeahead
inputsave() Number save and clear typeahead
inputsecret({prompt} [, {text}])
- String like input() but hiding the text
+ String like input() but hiding the text
insert({object}, {item} [, {idx}])
List insert {item} in {object} [before {idx}]
interrupt() none interrupt script execution
-invert({expr}) Number bitwise invert
+invert({expr}) Number bitwise invert
isdirectory({directory}) Number |TRUE| if {directory} is a directory
isinf({expr}) Number determine if {expr} is infinity value
(positive or negative)
@@ -2525,7 +2526,7 @@ json_encode({expr}) String Convert {expr} to JSON
keys({dict}) List keys in {dict}
len({expr}) Number the length of {expr}
libcall({lib}, {func}, {arg}) String call {func} in library {lib} with {arg}
-libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
+libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
line({expr} [, {winid}]) Number line nr of cursor, last line or mark
line2byte({lnum}) Number byte count of line {lnum}
lispindent({lnum}) Number Lisp indent for line {lnum}
@@ -2567,7 +2568,7 @@ msgpackparse({data}) List parse msgpack to a list of objects
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
nr2char({expr}[, {utf8}]) String single char with ASCII/UTF-8 value {expr}
nvim_...({args}...) any call nvim |api| functions
-or({expr}, {expr}) Number bitwise OR
+or({expr}, {expr}) Number bitwise OR
pathshorten({expr}) String shorten directory names in a path
perleval({expr}) any evaluate |perl| expression
pow({x}, {y}) Float {x} to the power of {y}
@@ -2627,7 +2628,7 @@ screenrow() Number current cursor row
screenstring({row}, {col}) String characters at screen position
search({pattern} [, {flags} [, {stopline} [, {timeout}]]])
Number search for {pattern}
-searchcount([{options}]) Dict Get or update the last search count
+searchcount([{options}]) Dict Get or update the last search count
searchdecl({name} [, {global} [, {thisblock}]])
Number search for variable declaration
searchpair({start}, {middle}, {end} [, {flags} [, {skip} [...]]])
@@ -2699,7 +2700,7 @@ split({expr} [, {pat} [, {keepempty}]])
List make |List| from {pat} separated {expr}
sqrt({expr}) Float square root of {expr}
stdioopen({dict}) Number open stdio in a headless instance.
-stdpath({what}) String/List returns the standard path(s) for {what}
+stdpath({what}) String/List returns the standard path(s) for {what}
str2float({expr} [, {quoted}]) Float convert String to Float
str2list({expr} [, {utf8}]) List convert each character of {expr} to
ASCII/UTF-8 value
@@ -2735,7 +2736,7 @@ synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col}
synIDattr({synID}, {what} [, {mode}])
String attribute {what} of syntax ID {synID}
synIDtrans({synID}) Number translated syntax ID of {synID}
-synconcealed({lnum}, {col}) List info about concealing
+synconcealed({lnum}, {col}) List info about concealing
synstack({lnum}, {col}) List stack of syntax IDs at {lnum} and {col}
system({cmd} [, {input}]) String output of shell command/filter {cmd}
systemlist({cmd} [, {input}]) List output of shell command/filter {cmd}
@@ -2771,7 +2772,7 @@ values({dict}) List values in {dict}
virtcol({expr}) Number screen column of cursor or mark
visualmode([expr]) String last visual mode used
wait({timeout}, {condition}[, {interval}])
- Number Wait until {condition} is satisfied
+ Number Wait until {condition} is satisfied
wildmenumode() Number whether 'wildmenu' mode is active
win_execute({id}, {command} [, {silent}])
String execute {command} in window {id}
@@ -4162,11 +4163,12 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
Can also be used as a |method|: >
Getpattern()->expand()
-expandcmd({expr}) *expandcmd()*
- Expand special items in {expr} like what is done for an Ex
- command such as `:edit`. This expands special keywords, like
- with |expand()|, and environment variables, anywhere in
- {expr}. "~user" and "~/path" are only expanded at the start.
+expandcmd({string}) *expandcmd()*
+ Expand special items in String {string} like what is done for
+ an Ex command such as `:edit`. This expands special keywords,
+ like with |expand()|, and environment variables, anywhere in
+ {string}. "~user" and "~/path" are only expanded at the
+ start.
Returns the expanded string. Example: >
:echo expandcmd('make %<.o')
@@ -4550,6 +4552,21 @@ foreground() Move the Vim window to the foreground. Useful when sent from
|remote_foreground()| instead.
{only in the Win32 GUI and console version}
+fullcommand({name}) *fullcommand()*
+ Get the full command name from a short abbreviated command
+ name; see |20.2| for details on command abbreviations.
+
+ The string argument {name} may start with a `:` and can
+ include a [range], these are skipped and not returned.
+ Returns an empty string if a command doesn't exist or if it's
+ ambiguous (for user-defined commands).
+
+ For example `fullcommand('s')`, `fullcommand('sub')`,
+ `fullcommand(':%substitute')` all return "substitute".
+
+ Can also be used as a |method|: >
+ GetName()->fullcommand()
+<
*funcref()*
funcref({name} [, {arglist}] [, {dict}])
Just like |function()|, but the returned Funcref will lookup
@@ -5420,8 +5437,9 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()*
The result is a String, which is the contents of register
{regname}. Example: >
:let cliptext = getreg('*')
-< When {regname} was not set the result is an empty string.
- The {regname} argument is a string.
+< When register {regname} was not set the result is an empty
+ string.
+ The {regname} argument must be a string.
getreg('=') returns the last evaluated value of the expression
register. (For use in maps.)
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 3793a21f36..614dd82443 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -214,7 +214,7 @@ For |lsp-request|, each |lsp-handler| has this signature: >
request, a table with information about the error
is sent. Otherwise, it is `nil`. See |lsp-response|.
{result} (Result | Params | nil)
- When the language server is able to succesfully
+ When the language server is able to successfully
complete a request, this contains the `result` key
of the response. See |lsp-response|.
{ctx} (table)
@@ -236,7 +236,7 @@ For |lsp-request|, each |lsp-handler| has this signature: >
{config} (table)
Configuration for the handler.
- Each handler can define it's own configuration
+ Each handler can define its own configuration
table that allows users to customize the behavior
of a particular handler.
@@ -274,7 +274,7 @@ For |lsp-notification|, each |lsp-handler| has this signature: >
{config} (table)
Configuration for the handler.
- Each handler can define it's own configuration
+ Each handler can define its own configuration
table that allows users to customize the behavior
of a particular handler.
@@ -369,7 +369,7 @@ Handlers can be set by:
For example: >
vim.lsp.start_client {
- ..., -- Other configuration ommitted.
+ ..., -- Other configuration omitted.
handlers = {
["textDocument/definition"] = my_custom_server_definition
},
@@ -394,6 +394,9 @@ in the following order:
2. Handler defined in |vim.lsp.start_client()|, if any.
3. Handler defined in |vim.lsp.handlers|, if any.
+ *vim.lsp.log_levels*
+Log levels are defined in |vim.log.levels|
+
VIM.LSP.PROTOCOL *vim.lsp.protocol*
@@ -444,7 +447,7 @@ LspCodeLens
|nvim_buf_set_extmark()|.
LspCodeLensSeparator *hl-LspCodeLensSeparator*
- Used to color the seperator between two or more code lens.
+ Used to color the separator between two or more code lens.
*lsp-highlight-signature*
@@ -485,6 +488,16 @@ buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
{bufnr} (number) Buffer handle, or 0 for current
{client_id} (number) Client id
+buf_detach_client({bufnr}, {client_id}) *vim.lsp.buf_detach_client()*
+ Detaches client from the specified buffer. Note: While the
+ server is notified that the text document (buffer) was closed,
+ it is still able to send notifications should it ignore this
+ notification.
+
+ Parameters: ~
+ {bufnr} number Buffer handle, or 0 for current
+ {client_id} number Client id
+
buf_get_clients({bufnr}) *vim.lsp.buf_get_clients()*
Gets a map of client_id:client pairs for the given buffer,
where each value is a |vim.lsp.client| object.
@@ -825,10 +838,10 @@ start_client({config}) *vim.lsp.start_client()*
throws an error. `code` is a number
describing the error. Other arguments
may be passed depending on the error
- kind. See |vim.lsp.client_errors| for
- possible errors. Use
- `vim.lsp.client_errors[code]` to get
- human-friendly name.
+ kind. See |vim.lsp.rpc.client_errors|
+ for possible errors. Use
+ `vim.lsp.rpc.client_errors[code]` to
+ get human-friendly name.
{before_init} Callback with parameters
(initialize_params, config) invoked
before the LSP "initialize" phase,
@@ -1291,7 +1304,7 @@ hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
{config} table Configuration table.
• border: (default=nil)
• Add borders to the floating window
- • See |vim.api.nvim_open_win()|
+ • See |nvim_open_win()|
*vim.lsp.handlers.signature_help()*
signature_help({_}, {result}, {ctx}, {config})
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 630df16e79..036454702b 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -708,6 +708,38 @@ vim.mpack.decode({str}) *vim.mpack.decode*
Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object.
------------------------------------------------------------------------------
+VIM.SPELL *lua-spell*
+
+vim.spell.check({str}) *vim.spell.check()*
+ Check {str} for spelling errors. Similar to the Vimscript function
+ |spellbadword()|.
+
+ Note: The behaviour of this function is dependent on: 'spelllang',
+ 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local
+ to the buffer. Consider calling this with |nvim_buf_call()|.
+
+ Example: >
+ vim.spell.check("the quik brown fox")
+ -->
+ {
+ {'quik', 'bad', 4}
+ }
+<
+
+ Parameters: ~
+ {str} String to spell check.
+
+ Return: ~
+ List of tuples with three items:
+ - The badly spelled word.
+ - The type of the spelling error:
+ "bad" spelling mistake
+ "rare" rare word
+ "local" word only valid in another region
+ "caps" word should start with Capital
+ - The position in {str} where the word begins.
+
+------------------------------------------------------------------------------
VIM *lua-builtin*
vim.api.{func}({...}) *vim.api*
@@ -759,9 +791,9 @@ vim.stricmp({a}, {b}) *vim.stricmp()*
respectively.
vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
- Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not
- supplied, the length of the string is used. All indicies are zero-based.
- Returns two values: the UTF-32 and UTF-16 indicies respectively.
+ Convert byte index to UTF-32 and UTF-16 indices. If {index} is not
+ supplied, the length of the string is used. All indices are zero-based.
+ Returns two values: the UTF-32 and UTF-16 indices respectively.
Embedded NUL bytes are treated as terminating the string. Invalid
UTF-8 bytes, and embedded surrogates are counted as one code
@@ -881,6 +913,15 @@ vim.types *vim.types*
`vim.types.dictionary` will not change or that `vim.types` table will
only contain values for these three types.
+ *log_levels* *vim.log.levels*
+Log levels are one of the values defined in `vim.log.levels`:
+
+ vim.log.levels.DEBUG
+ vim.log.levels.ERROR
+ vim.log.levels.INFO
+ vim.log.levels.TRACE
+ vim.log.levels.WARN
+
------------------------------------------------------------------------------
LUA-VIMSCRIPT BRIDGE *lua-vimscript*
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index 6ad5622893..f6d9e45d64 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -837,8 +837,7 @@ g@{motion} Call the function set by the 'operatorfunc' option.
"line" {motion} was |linewise|
"char" {motion} was |charwise|
"block" {motion} was |blockwise-visual|
- Although "block" would rarely appear, since it can
- only result from Visual mode where "g@" is not useful.
+ The type can be forced, see |forced-motion|.
Here is an example that counts the number of spaces with <F4>: >
@@ -1248,8 +1247,8 @@ See |:verbose-cmd| for more information.
Command attributes ~
-
-User-defined commands are treated by Vim just like any other Ex commands. They
+ *command-attributes*
+User-defined commands are treated by Nvim just like any other Ex commands. They
can have arguments, or have a range specified. Arguments are subject to
completion as filenames, buffers, etc. Exactly how this works depends upon the
command's attributes, which are specified when the command is defined.
diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt
index e83b17f9a0..bfacbe19f5 100644
--- a/runtime/doc/nvim_terminal_emulator.txt
+++ b/runtime/doc/nvim_terminal_emulator.txt
@@ -90,7 +90,7 @@ Mouse input has the following behavior:
- If another window is clicked, terminal focus will be lost and nvim will jump
to the clicked window
- If the mouse wheel is used while the mouse is positioned in another window,
- the terminal wont lose focus and the hovered window will be scrolled.
+ the terminal won't lose focus and the hovered window will be scrolled.
==============================================================================
Configuration *terminal-config*
@@ -391,6 +391,8 @@ GDB command *termdebug-customizing*
To change the name of the gdb command, set the "termdebugger" variable before
invoking `:Termdebug`: >
let termdebugger = "mygdb"
+If the command needs an argument use a List: >
+ let g:termdebugger = ['rr', 'replay', '--']
To not use neovim floating windows for previewing variable evaluation, set the
`g:termdebug_useFloatingHover` variable like this: >
@@ -426,7 +428,7 @@ When 'background' is "dark":
hi debugBreakpoint term=reverse ctermbg=red guibg=red
-Shorcuts *termdebug_shortcuts*
+Shortcuts *termdebug_shortcuts*
You can define your own shortcuts (mappings) to control gdb, that can work in
any window, using the TermDebugSendCommand() function. Example: >
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index b75dd78b57..c929178f5a 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1159,6 +1159,14 @@ A jump table for the options with a short description can be found at |Q_op|.
case mapping, the current locale is not effective.
This probably only matters for Turkish.
+ *'cdhome'* *'cdh'*
+'cdhome' 'cdh' boolean (default: off)
+ global
+ When on, |:cd|, |:tcd| and |:lcd| without an argument changes the
+ current working directory to the |$HOME| directory like in Unix.
+ When off, those commands just print the current directory name.
+ On Unix this option has no effect.
+
*'cdpath'* *'cd'* *E344* *E346*
'cdpath' 'cd' string (default: equivalent to $CDPATH or ",,")
global
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index a229c8742f..af8301f1a0 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -629,6 +629,7 @@ Short explanation of each option: *option-list*
'buflisted' 'bl' whether the buffer shows up in the buffer list
'buftype' 'bt' special type of buffer
'casemap' 'cmp' specifies how case of letters is changed
+'cdhome' 'cdh' change directory to the home directory by ":cd"
'cdpath' 'cd' list of directories searched with ":cd"
'cedit' key used to open the command-line window
'charconvert' 'ccv' expression for character encoding conversion
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index 68165f3d3d..5cfa06c33c 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -122,8 +122,9 @@ See |sign_define()| for the equivalent Vim script function.
in. Most useful is defining a background color.
numhl={group}
- Highlighting group used for 'number' column at the associated
- line. Overrides |hl-LineNr|, |hl-CursorLineNr|.
+ Highlighting group used for the line number on the line where
+ the sign is placed. Overrides |hl-LineNr|, |hl-LineNrAbove|,
+ |hl-LineNrBelow|, and |hl-CursorLineNr|.
text={text} *E239*
Define the text that is displayed when there is no icon or the
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index d49809599d..0fd481cd83 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -4455,7 +4455,7 @@ it marks the "\(\I\i*\)" sub-expression as external; in the end pattern, it
changes the \z1 back-reference into an external reference referring to the
first external sub-expression in the start pattern. External references can
also be used in skip patterns: >
- :syn region foo start="start \(\I\i*\)" skip="not end \z1" end="end \z1"
+ :syn region foo start="start \z(\I\i*\)" skip="not end \z1" end="end \z1"
Note that normal and external sub-expressions are completely orthogonal and
indexed separately; for instance, if the pattern "\z(..\)\(..\)" is applied
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 8f7241dd46..5f238fb2ff 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -100,7 +100,7 @@ tsnode:prev_named_sibling() *tsnode:prev_named_sibling()*
tsnode:iter_children() *tsnode:iter_children()*
Iterates over all the direct children of {tsnode}, regardless of
- wether they are named or not.
+ whether they are named or not.
Returns the child node plus the eventual field name corresponding to
this child node.
@@ -155,9 +155,9 @@ tsnode:sexpr() *tsnode:sexpr()*
Get an S-expression representing the node as a string.
tsnode:id() *tsnode:id()*
- Get an unique identier for the node inside its own tree.
+ Get an unique identifier for the node inside its own tree.
- No guarantees are made about this identifer's internal representation,
+ No guarantees are made about this identifier's internal representation,
except for being a primitive lua type with value equality (so not a table).
Presently it is a (non-printable) string.
@@ -195,7 +195,7 @@ to a match.
Treesitter Query Predicates *lua-treesitter-predicates*
When writing queries for treesitter, one might use `predicates`, that is,
-special scheme nodes that are evaluted to verify things on a captured node for
+special scheme nodes that are evaluated to verify things on a captured node for
example, the |eq?| predicate : >
((identifier) @foo (#eq? @foo "foo"))
@@ -203,7 +203,7 @@ This will only match identifier corresponding to the `"foo"` text.
Here is a list of built-in predicates :
`eq?` *ts-predicate-eq?*
- This predicate will check text correspondance between nodes or
+ This predicate will check text correspondence between nodes or
strings : >
((identifier) @foo (#eq? @foo "foo"))
((node1) @left (node2) @right (#eq? @left @right))
@@ -212,7 +212,7 @@ Here is a list of built-in predicates :
`vim-match?` *ts-predicate-vim-match?*
This will match if the provived vim regex matches the text
corresponding to a node : >
- ((idenfitier) @constant (#match? @constant "^[A-Z_]+$"))
+ ((identifier) @constant (#match? @constant "^[A-Z_]+$"))
< Note: the `^` and `$` anchors will respectively match the
start and end of the node's text.
@@ -267,7 +267,7 @@ Here is a list of built-in directives:
`offset!` *ts-predicate-offset!*
Takes the range of the captured node and applies the offsets
to it's range : >
- ((idenfitier) @constant (#offset! @constant 0 1 0 -1))
+ ((identifier) @constant (#offset! @constant 0 1 0 -1))
< This will generate a range object for the captured node with the
offsets applied. The arguments are
`({capture_id}, {start_row}, {start_col}, {end_row}, {end_col}, {key?})`
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 6a9284dac9..7e611a47f3 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -852,6 +852,7 @@ Command line: *command-line-functions*
getcmdtype() return the current command-line type
getcmdwintype() return the current command-line window type
getcompletion() list of command-line completion matches
+ fullcommand() get full command name
Quickfix and location lists: *quickfix-functions*
getqflist() list of quickfix errors
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index e0c33fa2c9..bb31895c96 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -116,10 +116,12 @@ other windows. If 'mouse' is enabled, a status line can be dragged to resize
windows.
*filler-lines*
-The lines after the last buffer line in a window are called filler lines.
-These lines start with a tilde (~) character. By default, these are
-highlighted as NonText (|hl-NonText|). The EndOfBuffer highlight group
-(|hl-EndOfBuffer|) can be used to change the highlighting of filler lines.
+The lines after the last buffer line in a window are called filler lines. By
+default, these lines start with a tilde (~) character. The 'eob' item in the
+'fillchars' option can be used to change this character. By default, these
+characters are highlighted as NonText (|hl-NonText|). The EndOfBuffer
+highlight group (|hl-EndOfBuffer|) can be used to change the highlighting of
+the filler characters.
==============================================================================
3. Opening and closing a window *opening-window* *E36*
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 57aaeab766..104836862f 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1,7 +1,7 @@
" Vim support file to detect file types
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2021 Dec 14
+" Last Change: 2021 Dec 27
" Listen very carefully, I will say this only once
if exists("did_load_filetypes")
@@ -392,7 +392,8 @@ au BufNewFile,BufRead configure.in,configure.ac setf config
" CUDA Compute Unified Device Architecture
au BufNewFile,BufRead *.cu,*.cuh setf cuda
-" Dockerfilb; Podman uses the same syntax with name Containerfile
+" Dockerfile; Podman uses the same syntax with name Containerfile
+" Also see Dockerfile.* below.
au BufNewFile,BufRead Containerfile,Dockerfile,*.Dockerfile setf dockerfile
" WildPackets EtherPeek Decoder
@@ -1087,7 +1088,9 @@ au BufNewFile,BufRead *.mmp setf mmp
" Modsim III (or LambdaProlog)
au BufNewFile,BufRead *.mod
- \ if getline(1) =~ '\<module\>' |
+ \ if expand("<afile>") =~ '\<go.mod$' |
+ \ setf gomod |
+ \ elseif getline(1) =~ '\<module\>' |
\ setf lprolog |
\ else |
\ setf modsim3 |
@@ -1744,6 +1747,7 @@ au BufNewFile,BufRead *.ice setf slice
" Microsoft Visual Studio Solution
au BufNewFile,BufRead *.sln setf solution
+au BufNewFile,BufRead *.slnf setf json
" Spice
au BufNewFile,BufRead *.sp,*.spice setf spice
@@ -2234,6 +2238,9 @@ au BufNewFile,BufRead crontab,crontab.*,*/etc/cron.d/* call s:StarSetf('crontab
" dnsmasq(8) configuration
au BufNewFile,BufRead */etc/dnsmasq.d/* call s:StarSetf('dnsmasq')
+" Dockerfile
+au BufNewFile,BufRead Dockerfile.*,Containerfile.* call s:StarSetf('dockerfile')
+
" Dracula
au BufNewFile,BufRead drac.* call s:StarSetf('dracula')
diff --git a/runtime/ftplugin/zsh.vim b/runtime/ftplugin/zsh.vim
index 53ce1417dd..34410f1c62 100644
--- a/runtime/ftplugin/zsh.vim
+++ b/runtime/ftplugin/zsh.vim
@@ -18,13 +18,13 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
let b:undo_ftplugin = "setl com< cms< fo< "
-if executable('zsh')
+if executable('zsh') && &shell !~# '/\%(nologin\|false\)$'
if !has('gui_running') && executable('less')
- command! -buffer -nargs=1 RunHelp silent exe '!MANPAGER= zsh -ic "autoload -Uz run-help; run-help <args> 2>/dev/null | LESS= less"' | redraw!
+ command! -buffer -nargs=1 RunHelp silent exe '!MANPAGER= zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null | LESS= less"' | redraw!
elseif has('terminal')
- command! -buffer -nargs=1 RunHelp silent exe ':term zsh -ic "autoload -Uz run-help; run-help <args>"'
+ command! -buffer -nargs=1 RunHelp silent exe ':term zsh -c "autoload -Uz run-help; run-help <args>"'
else
- command! -buffer -nargs=1 RunHelp echo system('zsh -ic "autoload -Uz run-help; run-help <args> 2>/dev/null"')
+ command! -buffer -nargs=1 RunHelp echo system('zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null"')
endif
if !exists('current_compiler')
compiler zsh
diff --git a/runtime/indent/sh.vim b/runtime/indent/sh.vim
index d2fb1ba452..aa47c6d1bd 100644
--- a/runtime/indent/sh.vim
+++ b/runtime/indent/sh.vim
@@ -109,7 +109,7 @@ function! GetShIndent()
let ind += s:indent_value('continuation-line')
endif
elseif s:end_block(line) && !s:start_block(line)
- let ind -= s:indent_value('default')
+ let ind = indent(lnum)
elseif pnum != 0 &&
\ s:is_continuation_line(pline) &&
\ !s:end_block(curline) &&
diff --git a/runtime/indent/xml.vim b/runtime/indent/xml.vim
index da65417939..5bf53ad1f8 100644
--- a/runtime/indent/xml.vim
+++ b/runtime/indent/xml.vim
@@ -39,6 +39,8 @@ setlocal indentkeys=o,O,*<Return>,<>>,<<>,/,{,},!^F
" autoindent: used when the indentexpr returns -1
setlocal autoindent
+let b:undo_indent = "setl ai< inde< indk<"
+
if !exists('b:xml_indent_open')
let b:xml_indent_open = '.\{-}<[:A-Z_a-z]'
" pre tag, e.g. <address>
@@ -51,6 +53,10 @@ if !exists('b:xml_indent_close')
" let b:xml_indent_close = '.\{-}</\(address\)\@!'
endif
+if !exists('b:xml_indent_continuation_filetype')
+ let b:xml_indent_continuation_filetype = 'xml'
+endif
+
let &cpo = s:keepcpo
unlet s:keepcpo
@@ -162,7 +168,7 @@ endfun
func! <SID>IsXMLContinuation(line)
" Checks, whether or not the line matches a start-of-tag
- return a:line !~ '^\s*<' && &ft is# 'xml'
+ return a:line !~ '^\s*<' && &ft =~# b:xml_indent_continuation_filetype
endfunc
func! <SID>HasNoTagEnd(line)
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 742ebf69b2..ddcead6ec5 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -556,13 +556,19 @@ end
--- - underline: (default true) Use underline for diagnostics. Options:
--- * severity: Only underline diagnostics matching the given severity
--- |diagnostic-severity|
---- - virtual_text: (default true) Use virtual text for diagnostics. Options:
+--- - virtual_text: (default true) Use virtual text for diagnostics. If multiple diagnostics
+--- are set for a namespace, one prefix per diagnostic + the last diagnostic
+--- message are shown.
+--- Options:
--- * severity: Only show virtual text for diagnostics matching the given
--- severity |diagnostic-severity|
--- * source: (boolean or string) Include the diagnostic source in virtual
--- text. Use "if_many" to only show sources if there is more than
--- one diagnostic source in the buffer. Otherwise, any truthy value
--- means to always show the diagnostic source.
+--- * spacing: (number) Amount of empty spaces inserted at the beginning
+--- of the virtual text.
+--- * prefix: (string) Prepend diagnostic message with prefix.
--- * format: (function) A function that takes a diagnostic as input and
--- returns a string. The return value is the text used to display
--- the diagnostic. Example:
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 25cde843a0..8bb9960a1b 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -290,7 +290,7 @@ end
--- Memoizes a function. On first run, the function return value is saved and
--- immediately returned on subsequent runs. If the function returns a multival,
--- only the first returned value will be memoized and returned. The function will only be run once,
---- even if it has side-effects.
+--- even if it has side effects.
---
---@param fn (function) Function to run
---@returns (function) Memoized function
@@ -645,8 +645,8 @@ end
---@param on_error Callback with parameters (code, ...), invoked
--- when the client operation throws an error. `code` is a number describing
--- the error. Other arguments may be passed depending on the error kind. See
---- |vim.lsp.client_errors| for possible errors.
---- Use `vim.lsp.client_errors[code]` to get human-friendly name.
+--- |vim.lsp.rpc.client_errors| for possible errors.
+--- Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
---
---@param before_init Callback with parameters (initialize_params, config)
--- invoked before the LSP "initialize" phase, where `params` contains the
@@ -757,8 +757,8 @@ function lsp.start_client(config)
---
---@param code (number) Error code
---@param err (...) Other arguments may be passed depending on the error kind
- ---@see |vim.lsp.client_errors| for possible errors. Use
- ---`vim.lsp.client_errors[code]` to get a human-friendly name.
+ ---@see |vim.lsp.rpc.client_errors| for possible errors. Use
+ ---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name.
function dispatch.on_error(code, err)
local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err })
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
@@ -1112,9 +1112,9 @@ local text_document_did_change_handler
do
text_document_did_change_handler = function(_, bufnr, changedtick, firstline, lastline, new_lastline)
- -- Don't do anything if there are no clients attached.
+ -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached
if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
- return
+ return true
end
util.buf_versions[bufnr] = changedtick
local compute_change_and_notify = changetracking.prepare(bufnr, firstline, lastline, new_lastline)
@@ -1220,6 +1220,50 @@ function lsp.buf_attach_client(bufnr, client_id)
return true
end
+--- Detaches client from the specified buffer.
+--- Note: While the server is notified that the text document (buffer)
+--- was closed, it is still able to send notifications should it ignore this notification.
+---
+---@param bufnr number Buffer handle, or 0 for current
+---@param client_id number Client id
+function lsp.buf_detach_client(bufnr, client_id)
+ validate {
+ bufnr = {bufnr, 'n', true};
+ client_id = {client_id, 'n'};
+ }
+ bufnr = resolve_bufnr(bufnr)
+
+ local client = lsp.get_client_by_id(client_id)
+ if not client or not client.attached_buffers[bufnr] then
+ vim.notify(
+ string.format('Buffer (id: %d) is not attached to client (id: %d). Cannot detach.', client_id, bufnr)
+ )
+ return
+ end
+
+ changetracking.reset_buf(client, bufnr)
+
+ if client.resolved_capabilities.text_document_open_close then
+ local uri = vim.uri_from_bufnr(bufnr)
+ local params = { textDocument = { uri = uri; } }
+ client.notify('textDocument/didClose', params)
+ end
+
+ client.attached_buffers[bufnr] = nil
+ util.buf_versions[bufnr] = nil
+
+ all_buffer_active_clients[bufnr][client_id] = nil
+ if #vim.tbl_keys(all_buffer_active_clients[bufnr]) == 0 then
+ all_buffer_active_clients[bufnr] = nil
+ end
+
+ local namespace = vim.lsp.diagnostic.get_namespace(client_id)
+ vim.diagnostic.reset(namespace, bufnr)
+
+ vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id))
+
+end
+
--- Checks if a buffer is attached for a particular client.
---
---@param bufnr (number) Buffer handle, or 0 for current
@@ -1664,14 +1708,14 @@ end
--
-- Can be used to lookup the number from the name or the
-- name from the number.
--- Levels by name: "trace", "debug", "info", "warn", "error"
--- Level numbers begin with "trace" at 0
+-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+-- Level numbers begin with "TRACE" at 0
lsp.log_levels = log.levels
--- Sets the global log level for LSP logging.
---
---- Levels by name: "trace", "debug", "info", "warn", "error"
---- Level numbers begin with "trace" at 0
+--- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+--- Level numbers begin with "TRACE" at 0
---
--- Use `lsp.log_levels` for reverse lookup.
---
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index c974d1a6b4..a1d3b2aa94 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -246,7 +246,7 @@ end
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
---- - See |vim.api.nvim_open_win()|
+--- - See |nvim_open_win()|
function M.hover(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua
index dbc473b52c..e0b5653587 100644
--- a/runtime/lua/vim/lsp/log.lua
+++ b/runtime/lua/vim/lsp/log.lua
@@ -8,8 +8,8 @@ local log = {}
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the name from the number.
--- Levels by name: 'trace', 'debug', 'info', 'warn', 'error'
--- Level numbers begin with 'trace' at 0
+-- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR"
+-- Level numbers begin with "TRACE" at 0
log.levels = vim.deepcopy(vim.log.levels)
-- Default log level is warn.
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 1fb75ddeb7..1ecac50df4 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -133,7 +133,8 @@ local function request_parser_loop()
end
end
-local client_errors = vim.tbl_add_reverse_lookup {
+--- Mapping of error codes used by the client
+local client_errors = {
INVALID_SERVER_MESSAGE = 1;
INVALID_SERVER_JSON = 2;
NO_RESULT_CALLBACK_FOUND = 3;
@@ -143,6 +144,8 @@ local client_errors = vim.tbl_add_reverse_lookup {
SERVER_RESULT_CALLBACK_ERROR = 7;
}
+client_errors = vim.tbl_add_reverse_lookup(client_errors)
+
--- Constructs an error message from an LSP error object.
---
---@param err (table) The error object
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 68a030d50b..5921eb5bf0 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -97,15 +97,17 @@ end
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number `encoding` index of `index` in `line`
function M._str_utfindex_enc(line, index, encoding)
- if encoding ~= 'utf-8' then
- local col32, col16 = vim.str_utfindex(line, index)
- if encoding == 'utf-32' then
- return col32
- else
- return col16
- end
+ if not encoding then encoding = 'utf-16' end
+ if encoding == 'utf-8' then
+ if index then return index else return #line end
+ elseif encoding == 'utf-16' then
+ local _, col16 = vim.str_utfindex(line, index)
+ return col16
+ elseif encoding == 'utf-32' then
+ local col32, _ = vim.str_utfindex(line, index)
+ return col32
else
- return index
+ error("Invalid encoding: " .. vim.inspect(encoding))
end
end
@@ -117,10 +119,15 @@ end
---@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16
---@return number byte (utf-8) index of `encoding` index `index` in `line`
function M._str_byteindex_enc(line, index, encoding)
- if encoding ~= 'utf-8' then
- return vim.str_byteindex(line, index, not encoding or encoding ~= 'utf-32')
+ if not encoding then encoding = 'utf-16' end
+ if encoding == 'utf-8' then
+ if index then return index else return #line end
+ elseif encoding == 'utf-16' then
+ return vim.str_byteindex(line, index, true)
+ elseif encoding == 'utf-32' then
+ return vim.str_byteindex(line, index)
else
- return index
+ error("Invalid encoding: " .. vim.inspect(encoding))
end
end
diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua
index d08d2a3ee3..11b661cd1a 100644
--- a/runtime/lua/vim/uri.lua
+++ b/runtime/lua/vim/uri.lua
@@ -74,8 +74,8 @@ local function uri_from_fname(path)
return table.concat(uri_parts)
end
-local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):.*'
-local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):[a-zA-Z]:.*'
+local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):.*'
+local WINDOWS_URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9.+-]*):[a-zA-Z]:.*'
--- Get a URI from a bufnr
---@param bufnr number
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index d4c10f7afa..c873252909 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2020 Oct 27
+" Last Change: 2021 Dec 12
" If there already is an option window, jump to that one.
let buf = bufnr('option-window')
@@ -261,6 +261,8 @@ call <SID>OptionG("sect", &sect)
call append("$", "path\tlist of directory names used for file searching")
call append("$", "\t(global or local to buffer)")
call <SID>OptionG("pa", &pa)
+call <SID>AddOption("cdhome", ":cd without argument goes to the home directory")
+call <SID>BinOptionG("cdh", &cdh)
call append("$", "cdpath\tlist of directory names used for :cd")
call <SID>OptionG("cd", &cd)
if exists("+autochdir")
diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim
index 4f3dd8ff9e..e8689980ae 100644
--- a/runtime/pack/dist/opt/matchit/autoload/matchit.vim
+++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim
@@ -1,6 +1,11 @@
" matchit.vim: (global plugin) Extended "%" matching
" autload script of matchit plugin, see ../plugin/matchit.vim
-" Last Change: Mar 01, 2020
+" Last Change: Jun 10, 2021
+
+" Neovim does not support scriptversion
+if has("vimscript-4")
+ scriptversion 4
+endif
let s:last_mps = ""
let s:last_words = ":"
@@ -30,11 +35,11 @@ function s:RestoreOptions()
" In s:CleanUp(), :execute "set" restore_options .
let restore_options = ""
if get(b:, 'match_ignorecase', &ic) != &ic
- let restore_options .= (&ic ? " " : " no") . "ignorecase"
+ let restore_options ..= (&ic ? " " : " no") .. "ignorecase"
let &ignorecase = b:match_ignorecase
endif
if &ve != ''
- let restore_options = " ve=" . &ve . restore_options
+ let restore_options = " ve=" .. &ve .. restore_options
set ve=
endif
return restore_options
@@ -42,22 +47,23 @@ endfunction
function matchit#Match_wrapper(word, forward, mode) range
let restore_options = s:RestoreOptions()
- " If this function was called from Visual mode, make sure that the cursor
- " is at the correct end of the Visual range:
- if a:mode == "v"
- execute "normal! gv\<Esc>"
- elseif a:mode == "o" && mode(1) !~# '[vV]'
- exe "norm! v"
- elseif a:mode == "n" && mode(1) =~# 'ni'
- exe "norm! v"
- endif
" In s:CleanUp(), we may need to check whether the cursor moved forward.
let startpos = [line("."), col(".")]
- " Use default behavior if called with a count.
+ " if a count has been applied, use the default [count]% mode (see :h N%)
if v:count
- exe "normal! " . v:count . "%"
+ exe "normal! " .. v:count .. "%"
return s:CleanUp(restore_options, a:mode, startpos)
end
+ if a:mode =~# "v" && mode(1) =~# 'ni'
+ exe "norm! gv"
+ elseif a:mode == "o" && mode(1) !~# '[vV]'
+ exe "norm! v"
+ " If this function was called from Visual mode, make sure that the cursor
+ " is at the correct end of the Visual range:
+ elseif a:mode == "v"
+ execute "normal! gv\<Esc>"
+ let startpos = [line("."), col(".")]
+ endif
" First step: if not already done, set the script variables
" s:do_BR flag for whether there are backrefs
@@ -78,30 +84,30 @@ function matchit#Match_wrapper(word, forward, mode) range
" quote the special chars in 'matchpairs', replace [,:] with \| and then
" append the builtin pairs (/*, */, #if, #ifdef, #ifndef, #else, #elif,
" #endif)
- let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") .
+ let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") ..
\ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>'
" s:all = pattern with all the keywords
- let match_words = match_words . (strlen(match_words) ? "," : "") . default
+ let match_words = match_words .. (strlen(match_words) ? "," : "") .. default
let s:last_words = match_words
- if match_words !~ s:notslash . '\\\d'
+ if match_words !~ s:notslash .. '\\\d'
let s:do_BR = 0
let s:pat = match_words
else
let s:do_BR = 1
let s:pat = s:ParseWords(match_words)
endif
- let s:all = substitute(s:pat, s:notslash . '\zs[,:]\+', '\\|', 'g')
+ let s:all = substitute(s:pat, s:notslash .. '\zs[,:]\+', '\\|', 'g')
" Just in case there are too many '\(...)' groups inside the pattern, make
" sure to use \%(...) groups, so that error E872 can be avoided
let s:all = substitute(s:all, '\\(', '\\%(', 'g')
- let s:all = '\%(' . s:all . '\)'
+ let s:all = '\%(' .. s:all .. '\)'
if exists("b:match_debug")
let b:match_pat = s:pat
endif
" Reconstruct the version with unresolved backrefs.
- let s:patBR = substitute(match_words.',',
- \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
- let s:patBR = substitute(s:patBR, s:notslash.'\zs:\{2,}', ':', 'g')
+ let s:patBR = substitute(match_words .. ',',
+ \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g')
+ let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g')
endif
" Second step: set the following local variables:
@@ -128,12 +134,15 @@ function matchit#Match_wrapper(word, forward, mode) range
let curcol = match(matchline, regexp)
" If there is no match, give up.
if curcol == -1
+ " Make sure macros abort properly
+ "exe "norm! \<esc>"
+ call feedkeys("\e", 'tni')
return s:CleanUp(restore_options, a:mode, startpos)
endif
let endcol = matchend(matchline, regexp)
let suf = strlen(matchline) - endcol
- let prefix = (curcol ? '^.*\%' . (curcol + 1) . 'c\%(' : '^\%(')
- let suffix = (suf ? '\)\%' . (endcol + 1) . 'c.*$' : '\)$')
+ let prefix = (curcol ? '^.*\%' .. (curcol + 1) .. 'c\%(' : '^\%(')
+ let suffix = (suf ? '\)\%' .. (endcol + 1) .. 'c.*$' : '\)$')
endif
if exists("b:match_debug")
let b:match_match = matchstr(matchline, regexp)
@@ -150,7 +159,7 @@ function matchit#Match_wrapper(word, forward, mode) range
" 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns
" group . "," . groupBR, and we pick it apart.
let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, s:patBR)
- let i = matchend(group, s:notslash . ",")
+ let i = matchend(group, s:notslash .. ",")
let groupBR = strpart(group, i)
let group = strpart(group, 0, i-1)
" Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix
@@ -159,32 +168,32 @@ function matchit#Match_wrapper(word, forward, mode) range
endif
if exists("b:match_debug")
let b:match_wholeBR = groupBR
- let i = matchend(groupBR, s:notslash . ":")
+ let i = matchend(groupBR, s:notslash .. ":")
let b:match_iniBR = strpart(groupBR, 0, i-1)
endif
" Fourth step: Set the arguments for searchpair().
- let i = matchend(group, s:notslash . ":")
- let j = matchend(group, '.*' . s:notslash . ":")
+ let i = matchend(group, s:notslash .. ":")
+ let j = matchend(group, '.*' .. s:notslash .. ":")
let ini = strpart(group, 0, i-1)
- let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g')
+ let mid = substitute(strpart(group, i,j-i-1), s:notslash .. '\zs:', '\\|', 'g')
let fin = strpart(group, j)
"Un-escape the remaining , and : characters.
- let ini = substitute(ini, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
- let mid = substitute(mid, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
- let fin = substitute(fin, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
+ let ini = substitute(ini, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g')
+ let mid = substitute(mid, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g')
+ let fin = substitute(fin, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g')
" searchpair() requires that these patterns avoid \(\) groups.
- let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g')
- let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g')
- let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g')
+ let ini = substitute(ini, s:notslash .. '\zs\\(', '\\%(', 'g')
+ let mid = substitute(mid, s:notslash .. '\zs\\(', '\\%(', 'g')
+ let fin = substitute(fin, s:notslash .. '\zs\\(', '\\%(', 'g')
" Set mid. This is optimized for readability, not micro-efficiency!
- if a:forward && matchline =~ prefix . fin . suffix
- \ || !a:forward && matchline =~ prefix . ini . suffix
+ if a:forward && matchline =~ prefix .. fin .. suffix
+ \ || !a:forward && matchline =~ prefix .. ini .. suffix
let mid = ""
endif
" Set flag. This is optimized for readability, not micro-efficiency!
- if a:forward && matchline =~ prefix . fin . suffix
- \ || !a:forward && matchline !~ prefix . ini . suffix
+ if a:forward && matchline =~ prefix .. fin .. suffix
+ \ || !a:forward && matchline !~ prefix .. ini .. suffix
let flag = "bW"
else
let flag = "W"
@@ -193,14 +202,14 @@ function matchit#Match_wrapper(word, forward, mode) range
if exists("b:match_skip")
let skip = b:match_skip
elseif exists("b:match_comment") " backwards compatibility and testing!
- let skip = "r:" . b:match_comment
+ let skip = "r:" .. b:match_comment
else
let skip = 's:comment\|string'
endif
let skip = s:ParseSkip(skip)
if exists("b:match_debug")
let b:match_ini = ini
- let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin
+ let b:match_tail = (strlen(mid) ? mid .. '\|' : '') .. fin
endif
" Fifth step: actually start moving the cursor and call searchpair().
@@ -210,25 +219,29 @@ function matchit#Match_wrapper(word, forward, mode) range
if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
let skip = "0"
else
- execute "if " . skip . "| let skip = '0' | endif"
+ execute "if " .. skip .. "| let skip = '0' | endif"
endif
let sp_return = searchpair(ini, mid, fin, flag, skip)
if &selection isnot# 'inclusive' && a:mode == 'v'
" move cursor one pos to the right, because selection is not inclusive
- " add virtualedit=onemore, to make it work even when the match ends the " line
+ " add virtualedit=onemore, to make it work even when the match ends the
+ " line
if !(col('.') < col('$')-1)
- set ve=onemore
+ let eolmark=1 " flag to set a mark on eol (since we cannot move there)
endif
norm! l
endif
- let final_position = "call cursor(" . line(".") . "," . col(".") . ")"
+ let final_position = "call cursor(" .. line(".") .. "," .. col(".") .. ")"
" Restore cursor position and original screen.
call winrestview(view)
normal! m'
if sp_return > 0
execute final_position
endif
- return s:CleanUp(restore_options, a:mode, startpos, mid.'\|'.fin)
+ if exists('eolmark') && eolmark
+ call setpos("''", [0, line('.'), col('$'), 0]) " set mark on the eol
+ endif
+ return s:CleanUp(restore_options, a:mode, startpos, mid .. '\|' .. fin)
endfun
" Restore options and do some special handling for Operator-pending mode.
@@ -270,16 +283,16 @@ endfun
" a:matchline = "123<tag>12" or "123</tag>12"
" then extract "tag" from a:matchline and return "<tag>:</tag>" .
fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline)
- if a:matchline !~ a:prefix .
- \ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix
+ if a:matchline !~ a:prefix ..
+ \ substitute(a:group, s:notslash .. '\zs:', '\\|', 'g') .. a:suffix
return a:group
endif
- let i = matchend(a:groupBR, s:notslash . ':')
+ let i = matchend(a:groupBR, s:notslash .. ':')
let ini = strpart(a:groupBR, 0, i-1)
let tailBR = strpart(a:groupBR, i)
let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix,
\ a:groupBR)
- let i = matchend(word, s:notslash . ":")
+ let i = matchend(word, s:notslash .. ":")
let wordBR = strpart(word, i)
let word = strpart(word, 0, i-1)
" Now, a:matchline =~ a:prefix . word . a:suffix
@@ -289,10 +302,10 @@ fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline)
let table = ""
let d = 0
while d < 10
- if tailBR =~ s:notslash . '\\' . d
- let table = table . d
+ if tailBR =~ s:notslash .. '\\' .. d
+ let table = table .. d
else
- let table = table . "-"
+ let table = table .. "-"
endif
let d = d + 1
endwhile
@@ -300,13 +313,13 @@ fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline)
let d = 9
while d
if table[d] != "-"
- let backref = substitute(a:matchline, a:prefix.word.a:suffix,
- \ '\'.table[d], "")
+ let backref = substitute(a:matchline, a:prefix .. word .. a:suffix,
+ \ '\' .. table[d], "")
" Are there any other characters that should be escaped?
let backref = escape(backref, '*,:')
execute s:Ref(ini, d, "start", "len")
- let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len)
- let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d,
+ let ini = strpart(ini, 0, start) .. backref .. strpart(ini, start+len)
+ let tailBR = substitute(tailBR, s:notslash .. '\zs\\' .. d,
\ escape(backref, '\\&'), 'g')
endif
let d = d-1
@@ -320,7 +333,7 @@ fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline)
let b:match_word = ""
endif
endif
- return ini . ":" . tailBR
+ return ini .. ":" .. tailBR
endfun
" Input a comma-separated list of groups with backrefs, such as
@@ -328,25 +341,25 @@ endfun
" and return a comma-separated list of groups with backrefs replaced:
" return '\(foo\):end\(foo\),\(bar\):end\(bar\)'
fun! s:ParseWords(groups)
- let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
- let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g')
+ let groups = substitute(a:groups .. ",", s:notslash .. '\zs[,:]*,[,:]*', ',', 'g')
+ let groups = substitute(groups, s:notslash .. '\zs:\{2,}', ':', 'g')
let parsed = ""
while groups =~ '[^,:]'
- let i = matchend(groups, s:notslash . ':')
- let j = matchend(groups, s:notslash . ',')
+ let i = matchend(groups, s:notslash .. ':')
+ let j = matchend(groups, s:notslash .. ',')
let ini = strpart(groups, 0, i-1)
- let tail = strpart(groups, i, j-i-1) . ":"
+ let tail = strpart(groups, i, j-i-1) .. ":"
let groups = strpart(groups, j)
- let parsed = parsed . ini
- let i = matchend(tail, s:notslash . ':')
+ let parsed = parsed .. ini
+ let i = matchend(tail, s:notslash .. ':')
while i != -1
" In 'if:else:endif', ini='if' and word='else' and then word='endif'.
let word = strpart(tail, 0, i-1)
let tail = strpart(tail, i)
- let i = matchend(tail, s:notslash . ':')
- let parsed = parsed . ":" . s:Resolve(ini, word, "word")
+ let i = matchend(tail, s:notslash .. ':')
+ let parsed = parsed .. ":" .. s:Resolve(ini, word, "word")
endwhile " Now, tail has been used up.
- let parsed = parsed . ","
+ let parsed = parsed .. ","
endwhile " groups =~ '[^,:]'
let parsed = substitute(parsed, ',$', '', '')
return parsed
@@ -364,14 +377,14 @@ endfun
" let j = matchend(getline("."), regexp)
" let match = matchstr(getline("."), regexp)
fun! s:Wholematch(string, pat, start)
- let group = '\%(' . a:pat . '\)'
- let prefix = (a:start ? '\(^.*\%<' . (a:start + 2) . 'c\)\zs' : '^')
+ let group = '\%(' .. a:pat .. '\)'
+ let prefix = (a:start ? '\(^.*\%<' .. (a:start + 2) .. 'c\)\zs' : '^')
let len = strlen(a:string)
- let suffix = (a:start+1 < len ? '\(\%>'.(a:start+1).'c.*$\)\@=' : '$')
- if a:string !~ prefix . group . suffix
+ let suffix = (a:start+1 < len ? '\(\%>' .. (a:start+1) .. 'c.*$\)\@=' : '$')
+ if a:string !~ prefix .. group .. suffix
let prefix = ''
endif
- return prefix . group . suffix
+ return prefix .. group .. suffix
endfun
" No extra arguments: s:Ref(string, d) will
@@ -392,7 +405,7 @@ fun! s:Ref(string, d, ...)
let match = a:string
while cnt
let cnt = cnt - 1
- let index = matchend(match, s:notslash . '\\(')
+ let index = matchend(match, s:notslash .. '\\(')
if index == -1
return ""
endif
@@ -404,7 +417,7 @@ fun! s:Ref(string, d, ...)
endif
let cnt = 1
while cnt
- let index = matchend(match, s:notslash . '\\(\|\\)') - 1
+ let index = matchend(match, s:notslash .. '\\(\|\\)') - 1
if index == -2
return ""
endif
@@ -418,7 +431,7 @@ fun! s:Ref(string, d, ...)
if a:0 == 1
return len
elseif a:0 == 2
- return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len
+ return "let " .. a:1 .. "=" .. start .. "| let " .. a:2 .. "=" .. len
else
return strpart(a:string, start, len)
endif
@@ -431,9 +444,9 @@ endfun
fun! s:Count(string, pattern, ...)
let pat = escape(a:pattern, '\\')
if a:0 > 1
- let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g")
+ let foo = substitute(a:string, '[^' .. a:pattern .. ']', "a:1", "g")
let foo = substitute(a:string, pat, a:2, "g")
- let foo = substitute(foo, '[^' . a:2 . ']', "", "g")
+ let foo = substitute(foo, '[^' .. a:2 .. ']', "", "g")
return strlen(foo)
endif
let result = 0
@@ -456,7 +469,7 @@ endfun
" unless it is preceded by "\".
fun! s:Resolve(source, target, output)
let word = a:target
- let i = matchend(word, s:notslash . '\\\d') - 1
+ let i = matchend(word, s:notslash .. '\\\d') - 1
let table = "----------"
while i != -2 " There are back references to be replaced.
let d = word[i]
@@ -477,28 +490,28 @@ fun! s:Resolve(source, target, output)
if table[s] == "-"
if w + b < 10
" let table[s] = w + b
- let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1)
+ let table = strpart(table, 0, s) .. (w+b) .. strpart(table, s+1)
endif
let b = b + 1
let s = s + 1
else
execute s:Ref(backref, b, "start", "len")
let ref = strpart(backref, start, len)
- let backref = strpart(backref, 0, start) . ":". table[s]
- \ . strpart(backref, start+len)
+ let backref = strpart(backref, 0, start) .. ":" .. table[s]
+ \ .. strpart(backref, start+len)
let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1')
endif
endwhile
- let word = strpart(word, 0, i-1) . backref . strpart(word, i+1)
- let i = matchend(word, s:notslash . '\\\d') - 1
+ let word = strpart(word, 0, i-1) .. backref .. strpart(word, i+1)
+ let i = matchend(word, s:notslash .. '\\\d') - 1
endwhile
- let word = substitute(word, s:notslash . '\zs:', '\\', 'g')
+ let word = substitute(word, s:notslash .. '\zs:', '\\', 'g')
if a:output == "table"
return table
elseif a:output == "word"
return word
else
- return table . word
+ return table .. word
endif
endfun
@@ -508,21 +521,21 @@ endfun
" If <patn> is the first pattern that matches a:string then return <patn>
" if no optional arguments are given; return <patn>,<altn> if a:1 is given.
fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...)
- let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma)
- let i = matchend(tail, s:notslash . a:comma)
+ let tail = (a:patterns =~ a:comma .. "$" ? a:patterns : a:patterns .. a:comma)
+ let i = matchend(tail, s:notslash .. a:comma)
if a:0
- let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma)
- let j = matchend(alttail, s:notslash . a:comma)
+ let alttail = (a:1 =~ a:comma .. "$" ? a:1 : a:1 .. a:comma)
+ let j = matchend(alttail, s:notslash .. a:comma)
endif
let current = strpart(tail, 0, i-1)
if a:branch == ""
let currpat = current
else
- let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g')
+ let currpat = substitute(current, s:notslash .. a:branch, '\\|', 'g')
endif
- while a:string !~ a:prefix . currpat . a:suffix
+ while a:string !~ a:prefix .. currpat .. a:suffix
let tail = strpart(tail, i)
- let i = matchend(tail, s:notslash . a:comma)
+ let i = matchend(tail, s:notslash .. a:comma)
if i == -1
return -1
endif
@@ -530,15 +543,15 @@ fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...)
if a:branch == ""
let currpat = current
else
- let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g')
+ let currpat = substitute(current, s:notslash .. a:branch, '\\|', 'g')
endif
if a:0
let alttail = strpart(alttail, j)
- let j = matchend(alttail, s:notslash . a:comma)
+ let j = matchend(alttail, s:notslash .. a:comma)
endif
endwhile
if a:0
- let current = current . a:comma . strpart(alttail, 0, j-1)
+ let current = current .. a:comma .. strpart(alttail, 0, j-1)
endif
return current
endfun
@@ -562,7 +575,7 @@ fun! matchit#Match_debug()
" fin = 'endif' piece, with all backrefs resolved from match
amenu &Matchit.&word :echo b:match_word<CR>
" '\'.d in ini refers to the same thing as '\'.table[d] in word.
- amenu &Matchit.t&able :echo '0:' . b:match_table . ':9'<CR>
+ amenu &Matchit.t&able :echo '0:' .. b:match_table .. ':9'<CR>
endfun
" Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW"
@@ -598,26 +611,26 @@ fun! matchit#MultiMatch(spflag, mode)
endif
if (match_words != s:last_words) || (&mps != s:last_mps) ||
\ exists("b:match_debug")
- let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") .
+ let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") ..
\ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>'
let s:last_mps = &mps
- let match_words = match_words . (strlen(match_words) ? "," : "") . default
+ let match_words = match_words .. (strlen(match_words) ? "," : "") .. default
let s:last_words = match_words
- if match_words !~ s:notslash . '\\\d'
+ if match_words !~ s:notslash .. '\\\d'
let s:do_BR = 0
let s:pat = match_words
else
let s:do_BR = 1
let s:pat = s:ParseWords(match_words)
endif
- let s:all = '\%(' . substitute(s:pat, '[,:]\+', '\\|', 'g') . '\)'
+ let s:all = '\%(' .. substitute(s:pat, '[,:]\+', '\\|', 'g') .. '\)'
if exists("b:match_debug")
let b:match_pat = s:pat
endif
" Reconstruct the version with unresolved backrefs.
- let s:patBR = substitute(match_words.',',
- \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
- let s:patBR = substitute(s:patBR, s:notslash.'\zs:\{2,}', ':', 'g')
+ let s:patBR = substitute(match_words .. ',',
+ \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g')
+ let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g')
endif
" Second step: figure out the patterns for searchpair()
@@ -625,23 +638,23 @@ fun! matchit#MultiMatch(spflag, mode)
" - TODO: A lot of this is copied from matchit#Match_wrapper().
" - maybe even more functionality should be split off
" - into separate functions!
- let openlist = split(s:pat . ',', s:notslash . '\zs:.\{-}' . s:notslash . ',')
- let midclolist = split(',' . s:pat, s:notslash . '\zs,.\{-}' . s:notslash . ':')
- call map(midclolist, {-> split(v:val, s:notslash . ':')})
+ let openlist = split(s:pat .. ',', s:notslash .. '\zs:.\{-}' .. s:notslash .. ',')
+ let midclolist = split(',' .. s:pat, s:notslash .. '\zs,.\{-}' .. s:notslash .. ':')
+ call map(midclolist, {-> split(v:val, s:notslash .. ':')})
let closelist = []
let middlelist = []
call map(midclolist, {i,v -> [extend(closelist, v[-1 : -1]),
\ extend(middlelist, v[0 : -2])]})
- call map(openlist, {i,v -> v =~# s:notslash . '\\|' ? '\%(' . v . '\)' : v})
- call map(middlelist, {i,v -> v =~# s:notslash . '\\|' ? '\%(' . v . '\)' : v})
- call map(closelist, {i,v -> v =~# s:notslash . '\\|' ? '\%(' . v . '\)' : v})
+ call map(openlist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v})
+ call map(middlelist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v})
+ call map(closelist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v})
let open = join(openlist, ',')
let middle = join(middlelist, ',')
let close = join(closelist, ',')
if exists("b:match_skip")
let skip = b:match_skip
elseif exists("b:match_comment") " backwards compatibility and testing!
- let skip = "r:" . b:match_comment
+ let skip = "r:" .. b:match_comment
else
let skip = 's:comment\|string'
endif
@@ -650,18 +663,18 @@ fun! matchit#MultiMatch(spflag, mode)
" Third step: call searchpair().
" Replace '\('--but not '\\('--with '\%(' and ',' with '\|'.
- let openpat = substitute(open, '\%(' . s:notslash . '\)\@<=\\(', '\\%(', 'g')
+ let openpat = substitute(open, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g')
let openpat = substitute(openpat, ',', '\\|', 'g')
- let closepat = substitute(close, '\%(' . s:notslash . '\)\@<=\\(', '\\%(', 'g')
+ let closepat = substitute(close, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g')
let closepat = substitute(closepat, ',', '\\|', 'g')
- let middlepat = substitute(middle, '\%(' . s:notslash . '\)\@<=\\(', '\\%(', 'g')
+ let middlepat = substitute(middle, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g')
let middlepat = substitute(middlepat, ',', '\\|', 'g')
if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
let skip = '0'
else
try
- execute "if " . skip . "| let skip = '0' | endif"
+ execute "if " .. skip .. "| let skip = '0' | endif"
catch /^Vim\%((\a\+)\)\=:E363/
" We won't find anything, so skip searching, should keep Vim responsive.
return {}
@@ -744,11 +757,11 @@ fun! s:ParseSkip(str)
let skip = a:str
if skip[1] == ":"
if skip[0] == "s"
- let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" .
- \ strpart(skip,2) . "'"
+ let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" ..
+ \ strpart(skip,2) .. "'"
elseif skip[0] == "S"
- let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" .
- \ strpart(skip,2) . "'"
+ let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" ..
+ \ strpart(skip,2) .. "'"
elseif skip[0] == "r"
let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'"
elseif skip[0] == "R"
diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt
index b719fae730..553359ffaf 100644
--- a/runtime/pack/dist/opt/matchit/doc/matchit.txt
+++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt
@@ -4,7 +4,7 @@ For instructions on installing this file, type
`:help matchit-install`
inside Vim.
-For Vim version 8.1. Last change: 2021 Nov 13
+For Vim version 8.2. Last change: 2021 Dec 24
VIM REFERENCE MANUAL by Benji Fisher et al
@@ -150,6 +150,10 @@ To use the matchit plugin add this line to your |vimrc|: >
The script should start working the next time you start Vim.
+To use the matchit plugin after startup, you can use this command (note the
+omitted '!'): >
+ packadd matchit
+
(Earlier versions of the script did nothing unless a |buffer-variable| named
|b:match_words| was defined. Even earlier versions contained autocommands
that set this variable for various file types. Now, |b:match_words| is
@@ -378,8 +382,8 @@ The back reference '\'.d refers to the same thing as '\'.b:match_table[d] in
5. Known Bugs and Limitations *matchit-bugs*
Repository: https://github.com/chrisbra/matchit/
-Bugs can be reported at the repository (alternatively you can send me a mail).
-The latest development snapshot can also be downloaded there.
+Bugs can be reported at the repository and the latest development snapshot can
+also be downloaded there.
Just because I know about a bug does not mean that it is on my todo list. I
try to respond to reports of bugs that cause real problems. If it does not
diff --git a/runtime/pack/dist/opt/matchit/plugin/matchit.vim b/runtime/pack/dist/opt/matchit/plugin/matchit.vim
index b62cc3913a..51ba3a7f51 100644
--- a/runtime/pack/dist/opt/matchit/plugin/matchit.vim
+++ b/runtime/pack/dist/opt/matchit/plugin/matchit.vim
@@ -1,7 +1,7 @@
" matchit.vim: (global plugin) Extended "%" matching
" Maintainer: Christian Brabandt
-" Version: 1.17
-" Last Change: 2019 Oct 24
+" Version: 1.18
+" Last Change: 2020 Dec 23
" Repository: https://github.com/chrisbra/matchit
" Previous URL:http://www.vim.org/script.php?script_id=39
" Previous Maintainer: Benji Fisher PhD <benji@member.AMS.org>
@@ -48,18 +48,12 @@ set cpo&vim
nnoremap <silent> <Plug>(MatchitNormalForward) :<C-U>call matchit#Match_wrapper('',1,'n')<CR>
nnoremap <silent> <Plug>(MatchitNormalBackward) :<C-U>call matchit#Match_wrapper('',0,'n')<CR>
-xnoremap <silent> <Plug>(MatchitVisualForward) :<C-U>call matchit#Match_wrapper('',1,'v')<CR>m'gv``
+xnoremap <silent> <Plug>(MatchitVisualForward) :<C-U>call matchit#Match_wrapper('',1,'v')<CR>
+ \:if col("''") != col("$") \| exe ":normal! m'" \| endif<cr>gv``
xnoremap <silent> <Plug>(MatchitVisualBackward) :<C-U>call matchit#Match_wrapper('',0,'v')<CR>m'gv``
onoremap <silent> <Plug>(MatchitOperationForward) :<C-U>call matchit#Match_wrapper('',1,'o')<CR>
onoremap <silent> <Plug>(MatchitOperationBackward) :<C-U>call matchit#Match_wrapper('',0,'o')<CR>
-nmap <silent> % <Plug>(MatchitNormalForward)
-nmap <silent> g% <Plug>(MatchitNormalBackward)
-xmap <silent> % <Plug>(MatchitVisualForward)
-xmap <silent> g% <Plug>(MatchitVisualBackward)
-omap <silent> % <Plug>(MatchitOperationForward)
-omap <silent> g% <Plug>(MatchitOperationBackward)
-
" Analogues of [{ and ]} using matching patterns:
nnoremap <silent> <Plug>(MatchitNormalMultiBackward) :<C-U>call matchit#MultiMatch("bW", "n")<CR>
nnoremap <silent> <Plug>(MatchitNormalMultiForward) :<C-U>call matchit#MultiMatch("W", "n")<CR>
@@ -68,16 +62,28 @@ xnoremap <silent> <Plug>(MatchitVisualMultiForward) :<C-U>call matchit#Multi
onoremap <silent> <Plug>(MatchitOperationMultiBackward) :<C-U>call matchit#MultiMatch("bW", "o")<CR>
onoremap <silent> <Plug>(MatchitOperationMultiForward) :<C-U>call matchit#MultiMatch("W", "o")<CR>
-nmap <silent> [% <Plug>(MatchitNormalMultiBackward)
-nmap <silent> ]% <Plug>(MatchitNormalMultiForward)
-xmap <silent> [% <Plug>(MatchitVisualMultiBackward)
-xmap <silent> ]% <Plug>(MatchitVisualMultiForward)
-omap <silent> [% <Plug>(MatchitOperationMultiBackward)
-omap <silent> ]% <Plug>(MatchitOperationMultiForward)
-
" text object:
xmap <silent> <Plug>(MatchitVisualTextObject) <Plug>(MatchitVisualMultiBackward)o<Plug>(MatchitVisualMultiForward)
-xmap a% <Plug>(MatchitVisualTextObject)
+
+if !exists("g:no_plugin_maps")
+ nmap <silent> % <Plug>(MatchitNormalForward)
+ nmap <silent> g% <Plug>(MatchitNormalBackward)
+ xmap <silent> % <Plug>(MatchitVisualForward)
+ xmap <silent> g% <Plug>(MatchitVisualBackward)
+ omap <silent> % <Plug>(MatchitOperationForward)
+ omap <silent> g% <Plug>(MatchitOperationBackward)
+
+ " Analogues of [{ and ]} using matching patterns:
+ nmap <silent> [% <Plug>(MatchitNormalMultiBackward)
+ nmap <silent> ]% <Plug>(MatchitNormalMultiForward)
+ xmap <silent> [% <Plug>(MatchitVisualMultiBackward)
+ xmap <silent> ]% <Plug>(MatchitVisualMultiForward)
+ omap <silent> [% <Plug>(MatchitOperationMultiBackward)
+ omap <silent> ]% <Plug>(MatchitOperationMultiForward)
+
+ " Text object
+ xmap a% <Plug>(MatchitVisualTextObject)
+endif
" Call this function to turn on debugging information. Every time the main
" script is run, buffer variables will be saved. These can be used directly
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 8fa556aab6..f9978a6b00 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -104,6 +104,10 @@ call s:Highlight(1, '', &background)
hi default debugBreakpoint term=reverse ctermbg=red guibg=red
hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray
+func s:GetCommand()
+ return type(g:termdebugger) == v:t_list ? copy(g:termdebugger) : [g:termdebugger]
+endfunc
+
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})
@@ -119,8 +123,9 @@ func s:StartDebug_internal(dict)
echoerr 'Terminal debugger already running, cannot run two'
return
endif
- if !executable(g:termdebugger)
- echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"'
+ let gdbcmd = s:GetCommand()
+ if !executable(gdbcmd[0])
+ echoerr 'Cannot execute debugger program "' .. gdbcmd[0] .. '"'
return
endif
@@ -192,7 +197,7 @@ endfunc
func s:CheckGdbRunning()
if nvim_get_chan_info(s:gdb_job_id) == {}
- echoerr string(g:termdebugger) . ' exited unexpectedly'
+ echoerr string(s:GetCommand()[0]) . ' exited unexpectedly'
call s:CloseBuffers()
return ''
endif
@@ -245,7 +250,7 @@ func s:StartDebug_term(dict)
let gdb_args = get(a:dict, 'gdb_args', [])
let proc_args = get(a:dict, 'proc_args', [])
- let gdb_cmd = [g:termdebugger]
+ let gdb_cmd = s:GetCommand()
" Add -quiet to avoid the intro message causing a hit-enter prompt.
let gdb_cmd += ['-quiet']
" Disable pagination, it causes everything to stop at the gdb
@@ -379,7 +384,7 @@ func s:StartDebug_prompt(dict)
let gdb_args = get(a:dict, 'gdb_args', [])
let proc_args = get(a:dict, 'proc_args', [])
- let gdb_cmd = [g:termdebugger]
+ let gdb_cmd = s:GetCommand()
" Add -quiet to avoid the intro message causing a hit-enter prompt.
let gdb_cmd += ['-quiet']
" Disable pagination, it causes everything to stop at the gdb, needs to be run early
diff --git a/runtime/syntax/debcontrol.vim b/runtime/syntax/debcontrol.vim
index 25fc252de6..8b65ece4ca 100644
--- a/runtime/syntax/debcontrol.vim
+++ b/runtime/syntax/debcontrol.vim
@@ -3,7 +3,7 @@
" Maintainer: Debian Vim Maintainers
" Former Maintainers: Gerfried Fuchs <alfie@ist.org>
" Wichert Akkerman <wakkerma@debian.org>
-" Last Change: 2020 Oct 26
+" Last Change: 2021 Nov 26
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debcontrol.vim
" Standard syntax initialization
@@ -91,6 +91,11 @@ syn case ignore
" Handle all fields from deb-src-control(5)
+" Catch-all for the legal fields
+syn region debcontrolField matchgroup=debcontrolKey start="^\%(\%(XSBC-Original-\)\=Maintainer\|Standards-Version\|Bugs\|Origin\|X[SB]-Python-Version\|\%(XS-\)\=Vcs-Mtn\|\%(XS-\)\=Testsuite\%(-Triggers\)\=\|Build-Profiles\|Tag\|Subarchitecture\|Kernel-Version\|Installer-Menu-Item\): " end="$" contains=debcontrolVariable,debcontrolEmail oneline
+syn region debcontrolMultiField matchgroup=debcontrolKey start="^\%(Build-\%(Conflicts\|Depends\)\%(-Arch\|-Indep\)\=\|\%(Pre-\)\=Depends\|Recommends\|Suggests\|Breaks\|Enhances\|Replaces\|Conflicts\|Provides\|Built-Using\|Uploaders\|X[SBC]\{0,3\}\%(Private-\)\=-[-a-zA-Z0-9]\+\): *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment
+syn region debcontrolMultiFieldSpell matchgroup=debcontrolKey start="^Description: *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment,@Spell
+
" Fields for which we do strict syntax checking
syn region debcontrolStrictField matchgroup=debcontrolKey start="^Architecture: *" end="$" contains=debcontrolArchitecture,debcontrolSpace oneline
syn region debcontrolStrictField matchgroup=debcontrolKey start="^Multi-Arch: *" end="$" contains=debcontrolMultiArch oneline
@@ -99,20 +104,15 @@ syn region debcontrolStrictField matchgroup=debcontrolKey start="^Priority: *" e
syn region debcontrolStrictField matchgroup=debcontrolKey start="^Section: *" end="$" contains=debcontrolSection oneline
syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XC-\)\=Package-Type: *" end="$" contains=debcontrolPackageType oneline
syn region debcontrolStrictField matchgroup=debcontrolKey start="^Homepage: *" end="$" contains=debcontrolHTTPUrl oneline keepend
-syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-\)\=Vcs-\%(Browser\|Arch\|Bzr\|Darcs\|Hg\): *" end="$" contains=debcontrolHTTPUrl oneline keepend
-syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-\)\=Vcs-Svn: *" end="$" contains=debcontrolVcsSvn,debcontrolHTTPUrl oneline keepend
-syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-\)\=Vcs-Cvs: *" end="$" contains=debcontrolVcsCvs oneline keepend
-syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-\)\=Vcs-Git: *" end="$" contains=debcontrolVcsGit oneline keepend
+syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-[-a-zA-Z0-9]\+-\)\=Vcs-\%(Browser\|Arch\|Bzr\|Darcs\|Hg\): *" end="$" contains=debcontrolHTTPUrl oneline keepend
+syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-[-a-zA-Z0-9]\+-\)\=Vcs-Svn: *" end="$" contains=debcontrolVcsSvn,debcontrolHTTPUrl oneline keepend
+syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-[-a-zA-Z0-9]\+-\)\=Vcs-Cvs: *" end="$" contains=debcontrolVcsCvs oneline keepend
+syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(XS-[-a-zA-Z0-9]\+-\)\=Vcs-Git: *" end="$" contains=debcontrolVcsGit oneline keepend
syn region debcontrolStrictField matchgroup=debcontrolKey start="^Rules-Requires-Root: *" end="$" contains=debcontrolR3 oneline
syn region debcontrolStrictField matchgroup=debcontrolKey start="^\%(Build-\)\=Essential: *" end="$" contains=debcontrolYesNo oneline
syn region debcontrolStrictField matchgroup=debcontrolDeprecatedKey start="^\%(XS-\)\=DM-Upload-Allowed: *" end="$" contains=debcontrolDmUpload oneline
-" Catch-all for the other legal fields
-syn region debcontrolField matchgroup=debcontrolKey start="^\%(\%(XSBC-Original-\)\=Maintainer\|Standards-Version\|Bugs\|Origin\|X[SB]-Python-Version\|\%(XS-\)\=Vcs-Mtn\|\%(XS-\)\=Testsuite\%(-Triggers\)\=\|Build-Profiles\|Tag\|Subarchitecture\|Kernel-Version\|Installer-Menu-Item\): " end="$" contains=debcontrolVariable,debcontrolEmail oneline
-syn region debcontrolMultiField matchgroup=debcontrolKey start="^\%(Build-\%(Conflicts\|Depends\)\%(-Arch\|-Indep\)\=\|\%(Pre-\)\=Depends\|Recommends\|Suggests\|Breaks\|Enhances\|Replaces\|Conflicts\|Provides\|Built-Using\|Uploaders\|X[SBC]\{0,3\}\%(Private-\)\=-[-a-zA-Z0-9]\+\): *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment
-syn region debcontrolMultiFieldSpell matchgroup=debcontrolKey start="^Description: *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment,@Spell
-
" Associate our matches and regions with pretty colours
hi def link debcontrolKey Keyword
hi def link debcontrolField Normal
diff --git a/runtime/syntax/dep3patch.vim b/runtime/syntax/dep3patch.vim
new file mode 100644
index 0000000000..8b2cee629c
--- /dev/null
+++ b/runtime/syntax/dep3patch.vim
@@ -0,0 +1,57 @@
+" Vim syntax file
+" Language: Debian DEP3 Patch headers
+" Maintainer: Gabriel Filion <gabster@lelutin.ca>
+" Last Change: 2021-01-09
+" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/dep3patch.vim
+"
+" Specification of the DEP3 patch header format is available at:
+" https://dep-team.pages.debian.net/deps/dep3/
+
+" Standard syntax initialization
+if exists('b:current_syntax')
+ finish
+endif
+
+runtime! syntax/diff.vim
+unlet! b:current_syntax
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+syn region dep3patchHeaders start="\%^" end="^\%(---\)\@=" contains=dep3patchKey,dep3patchMultiField
+
+syn case ignore
+
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^\%(Description\|Subject\)\ze: *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contained contains=@Spell
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^Origin\ze: *" end="$" contained contains=dep3patchHTTPUrl,dep3patchCommitID,dep3patchOriginCategory oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^Bug\%(-[[:graph:]]\+\)\?\ze: *" end="$" contained contains=dep3patchHTTPUrl oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^Forwarded\ze: *" end="$" contained contains=dep3patchHTTPUrl,dep3patchForwardedShort oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^\%(Author\|From\)\ze: *" end="$" contained contains=dep3patchEmail oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^\%(Reviewed-by\|Acked-by\)\ze: *" end="$" contained contains=dep3patchEmail oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^Last-Updated\ze: *" end="$" contained contains=dep3patchISODate oneline keepend
+syn region dep3patchMultiField matchgroup=dep3patchKey start="^Applied-Upstream\ze: *" end="$" contained contains=dep3patchHTTPUrl,dep3patchCommitID oneline keepend
+
+syn match dep3patchHTTPUrl contained "\vhttps?://[[:alnum:]][-[:alnum:]]*[[:alnum:]]?(\.[[:alnum:]][-[:alnum:]]*[[:alnum:]]?)*\.[[:alpha:]][-[:alnum:]]*[[:alpha:]]?(:\d+)?(/[^[:space:]]*)?$"
+syn match dep3patchCommitID contained "commit:[[:alnum:]]\+"
+syn match dep3patchOriginCategory contained "\%(upstream\|backport\|vendor\|other\), "
+syn match dep3patchForwardedShort contained "\%(yes\|no\|not-needed\), "
+syn match dep3patchEmail "[_=[:alnum:]\.+-]\+@[[:alnum:]\./\-]\+"
+syn match dep3patchEmail "<.\{-}>"
+syn match dep3patchISODate "[[:digit:]]\{4}-[[:digit:]]\{2}-[[:digit:]]\{2}"
+
+" Associate our matches and regions with pretty colours
+hi def link dep3patchKey Keyword
+hi def link dep3patchOriginCategory Keyword
+hi def link dep3patchForwardedShort Keyword
+hi def link dep3patchMultiField Normal
+hi def link dep3patchHTTPUrl Identifier
+hi def link dep3patchCommitID Identifier
+hi def link dep3patchEmail Identifier
+hi def link dep3patchISODate Identifier
+
+let b:current_syntax = 'dep3patch'
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: ts=8 sw=2
diff --git a/runtime/syntax/dtd.vim b/runtime/syntax/dtd.vim
index ef0592e1d1..58f07c98dd 100644
--- a/runtime/syntax/dtd.vim
+++ b/runtime/syntax/dtd.vim
@@ -45,7 +45,7 @@ if !exists("dtd_no_tag_errors")
syn region dtdError contained start=+<!+lc=2 end=+>+
endif
-" if this is a html like comment hightlight also
+" if this is a html like comment highlight also
" the opening <! and the closing > as Comment.
syn region dtdComment start=+<![ \t]*--+ end=+-->+ contains=dtdTodo,@Spell
@@ -99,8 +99,8 @@ syn match dtdEntity "&[^; \t]*;" contains=dtdEntityPunct
syn match dtdEntityPunct contained "[&.;]"
" Strings are between quotes
-syn region dtdString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=dtdAttrDef,dtdAttrType,dtdEnum,dtdParamEntityInst,dtdEntity,dtdCard
-syn region dtdString start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=dtdAttrDef,dtdAttrType,dtdEnum,dtdParamEntityInst,dtdEntity,dtdCard
+syn region dtdString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=dtdAttrDef,dtdAttrType,dtdParamEntityInst,dtdEntity,dtdCard
+syn region dtdString start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=dtdAttrDef,dtdAttrType,dtdParamEntityInst,dtdEntity,dtdCard
" Enumeration of elements or data between parenthesis
"
diff --git a/runtime/syntax/xml.vim b/runtime/syntax/xml.vim
index 7c9791a7cc..d99f8b467a 100644
--- a/runtime/syntax/xml.vim
+++ b/runtime/syntax/xml.vim
@@ -10,6 +10,7 @@
" 20190923 - Fix xmlEndTag to match xmlTag (vim/vim#884)
" 20190924 - Fix xmlAttribute property (amadeus/vim-xml@d8ce1c946)
" 20191103 - Enable spell checking globally
+" 20210428 - Improve syntax synchronizing
" CONFIGURATION:
" syntax folding can be turned on by
@@ -302,9 +303,12 @@ unlet b:current_syntax
" synchronizing
-" TODO !!! to be improved !!!
-syn sync match xmlSyncDT grouphere xmlDocType +\_.\(<!DOCTYPE\)\@=+
+syn sync match xmlSyncComment grouphere xmlComment +<!--+
+syn sync match xmlSyncComment groupthere NONE +-->+
+
+" The following is slow on large documents (and the doctype is optional
+" syn sync match xmlSyncDT grouphere xmlDocType +\_.\(<!DOCTYPE\)\@=+
" syn sync match xmlSyncDT groupthere NONE +]>+
if exists('g:xml_syntax_folding')
@@ -313,7 +317,7 @@ if exists('g:xml_syntax_folding')
syn sync match xmlSync groupthere xmlRegion +</[^ /!?<>"']\+>+
endif
-syn sync minlines=100
+syn sync minlines=100 maxlines=200
" The default highlighting.
@@ -354,4 +358,4 @@ let b:current_syntax = "xml"
let &cpo = s:xml_cpo_save
unlet s:xml_cpo_save
-" vim: ts=8
+" vim: ts=4
diff --git a/runtime/syntax/zsh.vim b/runtime/syntax/zsh.vim
index 819c419228..bab89b916e 100644
--- a/runtime/syntax/zsh.vim
+++ b/runtime/syntax/zsh.vim
@@ -41,11 +41,12 @@ if get(g:, 'zsh_fold_enable', 0)
setlocal foldmethod=syntax
endif
+syn match zshQuoted '\\.'
syn match zshPOSIXQuoted '\\[xX][0-9a-fA-F]\{1,2}'
syn match zshPOSIXQuoted '\\[0-7]\{1,3}'
syn match zshPOSIXQuoted '\\u[0-9a-fA-F]\{1,4}'
syn match zshPOSIXQuoted '\\U[1-9a-fA-F]\{1,8}'
-syn match zshQuoted '\\.'
+
syn region zshString matchgroup=zshStringDelimiter start=+"+ end=+"+
\ contains=zshQuoted,@zshDerefs,@zshSubst fold
syn region zshString matchgroup=zshStringDelimiter start=+'+ end=+'+ fold
@@ -133,217 +134,15 @@ syn keyword zshCommands alias autoload bg bindkey break bye cap cd
\ zmodload zparseopts zprof zpty zrecompile
\ zregexparse zsocket zstyle ztcp
-" Options, generated by: echo ${(j:\n:)options[(I)*]} | sort
-" Create a list of option names from zsh source dir:
-" #!/bin/zsh
-" topdir=/path/to/zsh-xxx
-" grep '^pindex([A-Za-z_]*)$' $topdir/Doc/Zsh/options.yo |
-" while read opt
-" do
-" echo ${${(L)opt#pindex\(}%\)}
-" done
-
+" Options, generated by from the zsh source with the make-options.zsh script.
syn case ignore
-
-syn match zshOptStart /^\s*\%(\%(\%(un\)\?setopt\)\|set\s+[-+]o\)/ nextgroup=zshOption skipwhite
-syn match zshOption /
- \ \%(\%(\<no_\?\)\?aliases\>\)\|
- \ \%(\%(\<no_\?\)\?aliasfuncdef\>\)\|\%(\%(no_\?\)\?alias_func_def\>\)\|
- \ \%(\%(\<no_\?\)\?allexport\>\)\|\%(\%(no_\?\)\?all_export\>\)\|
- \ \%(\%(\<no_\?\)\?alwayslastprompt\>\)\|\%(\%(no_\?\)\?always_last_prompt\>\)\|\%(\%(no_\?\)\?always_lastprompt\>\)\|
- \ \%(\%(\<no_\?\)\?alwaystoend\>\)\|\%(\%(no_\?\)\?always_to_end\>\)\|
- \ \%(\%(\<no_\?\)\?appendcreate\>\)\|\%(\%(no_\?\)\?append_create\>\)\|
- \ \%(\%(\<no_\?\)\?appendhistory\>\)\|\%(\%(no_\?\)\?append_history\>\)\|
- \ \%(\%(\<no_\?\)\?autocd\>\)\|\%(\%(no_\?\)\?auto_cd\>\)\|
- \ \%(\%(\<no_\?\)\?autocontinue\>\)\|\%(\%(no_\?\)\?auto_continue\>\)\|
- \ \%(\%(\<no_\?\)\?autolist\>\)\|\%(\%(no_\?\)\?auto_list\>\)\|
- \ \%(\%(\<no_\?\)\?automenu\>\)\|\%(\%(no_\?\)\?auto_menu\>\)\|
- \ \%(\%(\<no_\?\)\?autonamedirs\>\)\|\%(\%(no_\?\)\?auto_name_dirs\>\)\|
- \ \%(\%(\<no_\?\)\?autoparamkeys\>\)\|\%(\%(no_\?\)\?auto_param_keys\>\)\|
- \ \%(\%(\<no_\?\)\?autoparamslash\>\)\|\%(\%(no_\?\)\?auto_param_slash\>\)\|
- \ \%(\%(\<no_\?\)\?autopushd\>\)\|\%(\%(no_\?\)\?auto_pushd\>\)\|
- \ \%(\%(\<no_\?\)\?autoremoveslash\>\)\|\%(\%(no_\?\)\?auto_remove_slash\>\)\|
- \ \%(\%(\<no_\?\)\?autoresume\>\)\|\%(\%(no_\?\)\?auto_resume\>\)\|
- \ \%(\%(\<no_\?\)\?badpattern\>\)\|\%(\%(no_\?\)\?bad_pattern\>\)\|
- \ \%(\%(\<no_\?\)\?banghist\>\)\|\%(\%(no_\?\)\?bang_hist\>\)\|
- \ \%(\%(\<no_\?\)\?bareglobqual\>\)\|\%(\%(no_\?\)\?bare_glob_qual\>\)\|
- \ \%(\%(\<no_\?\)\?bashautolist\>\)\|\%(\%(no_\?\)\?bash_auto_list\>\)\|
- \ \%(\%(\<no_\?\)\?bashrematch\>\)\|\%(\%(no_\?\)\?bash_rematch\>\)\|
- \ \%(\%(\<no_\?\)\?beep\>\)\|
- \ \%(\%(\<no_\?\)\?bgnice\>\)\|\%(\%(no_\?\)\?bg_nice\>\)\|
- \ \%(\%(\<no_\?\)\?braceccl\>\)\|\%(\%(no_\?\)\?brace_ccl\>\)\|
- \ \%(\%(\<no_\?\)\?braceexpand\>\)\|\%(\%(no_\?\)\?brace_expand\>\)\|
- \ \%(\%(\<no_\?\)\?bsdecho\>\)\|\%(\%(no_\?\)\?bsd_echo\>\)\|
- \ \%(\%(\<no_\?\)\?caseglob\>\)\|\%(\%(no_\?\)\?case_glob\>\)\|
- \ \%(\%(\<no_\?\)\?casematch\>\)\|\%(\%(no_\?\)\?case_match\>\)\|
- \ \%(\%(\<no_\?\)\?cbases\>\)\|\%(\%(no_\?\)\?c_bases\>\)\|
- \ \%(\%(\<no_\?\)\?cdablevars\>\)\|\%(\%(no_\?\)\?cdable_vars\>\)\|\%(\%(no_\?\)\?cd_able_vars\>\)\|
- \ \%(\%(\<no_\?\)\?cdsilent\>\)\|\%(\%(no_\?\)\?cd_silent\>\)\|\%(\%(no_\?\)\?cd_silent\>\)\|
- \ \%(\%(\<no_\?\)\?chasedots\>\)\|\%(\%(no_\?\)\?chase_dots\>\)\|
- \ \%(\%(\<no_\?\)\?chaselinks\>\)\|\%(\%(no_\?\)\?chase_links\>\)\|
- \ \%(\%(\<no_\?\)\?checkjobs\>\)\|\%(\%(no_\?\)\?check_jobs\>\)\|
- \ \%(\%(\<no_\?\)\?checkrunningjobs\>\)\|\%(\%(no_\?\)\?check_running_jobs\>\)\|
- \ \%(\%(\<no_\?\)\?clobber\>\)\|
- \ \%(\%(\<no_\?\)\?clobberempty\>\)\|\%(\%(no_\?\)\?clobber_empty\>\)\|
- \ \%(\%(\<no_\?\)\?combiningchars\>\)\|\%(\%(no_\?\)\?combining_chars\>\)\|
- \ \%(\%(\<no_\?\)\?completealiases\>\)\|\%(\%(no_\?\)\?complete_aliases\>\)\|
- \ \%(\%(\<no_\?\)\?completeinword\>\)\|\%(\%(no_\?\)\?complete_in_word\>\)\|
- \ \%(\%(\<no_\?\)\?continueonerror\>\)\|\%(\%(no_\?\)\?continue_on_error\>\)\|
- \ \%(\%(\<no_\?\)\?correct\>\)\|
- \ \%(\%(\<no_\?\)\?correctall\>\)\|\%(\%(no_\?\)\?correct_all\>\)\|
- \ \%(\%(\<no_\?\)\?cprecedences\>\)\|\%(\%(no_\?\)\?c_precedences\>\)\|
- \ \%(\%(\<no_\?\)\?cshjunkiehistory\>\)\|\%(\%(no_\?\)\?csh_junkie_history\>\)\|
- \ \%(\%(\<no_\?\)\?cshjunkieloops\>\)\|\%(\%(no_\?\)\?csh_junkie_loops\>\)\|
- \ \%(\%(\<no_\?\)\?cshjunkiequotes\>\)\|\%(\%(no_\?\)\?csh_junkie_quotes\>\)\|
- \ \%(\%(\<no_\?\)\?csh_nullcmd\>\)\|\%(\%(no_\?\)\?csh_null_cmd\>\)\|\%(\%(no_\?\)\?cshnullcmd\>\)\|\%(\%(no_\?\)\?csh_null_cmd\>\)\|
- \ \%(\%(\<no_\?\)\?cshnullglob\>\)\|\%(\%(no_\?\)\?csh_null_glob\>\)\|
- \ \%(\%(\<no_\?\)\?debugbeforecmd\>\)\|\%(\%(no_\?\)\?debug_before_cmd\>\)\|
- \ \%(\%(\<no_\?\)\?dotglob\>\)\|\%(\%(no_\?\)\?dot_glob\>\)\|
- \ \%(\%(\<no_\?\)\?dvorak\>\)\|
- \ \%(\%(\<no_\?\)\?emacs\>\)\|
- \ \%(\%(\<no_\?\)\?equals\>\)\|
- \ \%(\%(\<no_\?\)\?errexit\>\)\|\%(\%(no_\?\)\?err_exit\>\)\|
- \ \%(\%(\<no_\?\)\?errreturn\>\)\|\%(\%(no_\?\)\?err_return\>\)\|
- \ \%(\%(\<no_\?\)\?evallineno\>\)\|\%(\%(no_\?\)\?eval_lineno\>\)\|
- \ \%(\%(\<no_\?\)\?exec\>\)\|
- \ \%(\%(\<no_\?\)\?extendedglob\>\)\|\%(\%(no_\?\)\?extended_glob\>\)\|
- \ \%(\%(\<no_\?\)\?extendedhistory\>\)\|\%(\%(no_\?\)\?extended_history\>\)\|
- \ \%(\%(\<no_\?\)\?flowcontrol\>\)\|\%(\%(no_\?\)\?flow_control\>\)\|
- \ \%(\%(\<no_\?\)\?forcefloat\>\)\|\%(\%(no_\?\)\?force_float\>\)\|
- \ \%(\%(\<no_\?\)\?functionargzero\>\)\|\%(\%(no_\?\)\?function_argzero\>\)\|\%(\%(no_\?\)\?function_arg_zero\>\)\|
- \ \%(\%(\<no_\?\)\?glob\>\)\|
- \ \%(\%(\<no_\?\)\?globalexport\>\)\|\%(\%(no_\?\)\?global_export\>\)\|
- \ \%(\%(\<no_\?\)\?globalrcs\>\)\|\%(\%(no_\?\)\?global_rcs\>\)\|
- \ \%(\%(\<no_\?\)\?globassign\>\)\|\%(\%(no_\?\)\?glob_assign\>\)\|
- \ \%(\%(\<no_\?\)\?globcomplete\>\)\|\%(\%(no_\?\)\?glob_complete\>\)\|
- \ \%(\%(\<no_\?\)\?globdots\>\)\|\%(\%(no_\?\)\?glob_dots\>\)\|
- \ \%(\%(\<no_\?\)\?glob_subst\>\)\|\%(\%(no_\?\)\?globsubst\>\)\|
- \ \%(\%(\<no_\?\)\?globstarshort\>\)\|\%(\%(no_\?\)\?glob_star_short\>\)\|
- \ \%(\%(\<no_\?\)\?hashall\>\)\|\%(\%(no_\?\)\?hash_all\>\)\|
- \ \%(\%(\<no_\?\)\?hashcmds\>\)\|\%(\%(no_\?\)\?hash_cmds\>\)\|
- \ \%(\%(\<no_\?\)\?hashdirs\>\)\|\%(\%(no_\?\)\?hash_dirs\>\)\|
- \ \%(\%(\<no_\?\)\?hashexecutablesonly\>\)\|\%(\%(no_\?\)\?hash_executables_only\>\)\|
- \ \%(\%(\<no_\?\)\?hashlistall\>\)\|\%(\%(no_\?\)\?hash_list_all\>\)\|
- \ \%(\%(\<no_\?\)\?histallowclobber\>\)\|\%(\%(no_\?\)\?hist_allow_clobber\>\)\|
- \ \%(\%(\<no_\?\)\?histappend\>\)\|\%(\%(no_\?\)\?hist_append\>\)\|
- \ \%(\%(\<no_\?\)\?histbeep\>\)\|\%(\%(no_\?\)\?hist_beep\>\)\|
- \ \%(\%(\<no_\?\)\?hist_expand\>\)\|\%(\%(no_\?\)\?histexpand\>\)\|
- \ \%(\%(\<no_\?\)\?hist_expire_dups_first\>\)\|\%(\%(no_\?\)\?histexpiredupsfirst\>\)\|
- \ \%(\%(\<no_\?\)\?histfcntllock\>\)\|\%(\%(no_\?\)\?hist_fcntl_lock\>\)\|
- \ \%(\%(\<no_\?\)\?histfindnodups\>\)\|\%(\%(no_\?\)\?hist_find_no_dups\>\)\|
- \ \%(\%(\<no_\?\)\?histignorealldups\>\)\|\%(\%(no_\?\)\?hist_ignore_all_dups\>\)\|
- \ \%(\%(\<no_\?\)\?histignoredups\>\)\|\%(\%(no_\?\)\?hist_ignore_dups\>\)\|
- \ \%(\%(\<no_\?\)\?histignorespace\>\)\|\%(\%(no_\?\)\?hist_ignore_space\>\)\|
- \ \%(\%(\<no_\?\)\?histlexwords\>\)\|\%(\%(no_\?\)\?hist_lex_words\>\)\|
- \ \%(\%(\<no_\?\)\?histnofunctions\>\)\|\%(\%(no_\?\)\?hist_no_functions\>\)\|
- \ \%(\%(\<no_\?\)\?histnostore\>\)\|\%(\%(no_\?\)\?hist_no_store\>\)\|
- \ \%(\%(\<no_\?\)\?histreduceblanks\>\)\|\%(\%(no_\?\)\?hist_reduce_blanks\>\)\|
- \ \%(\%(\<no_\?\)\?histsavebycopy\>\)\|\%(\%(no_\?\)\?hist_save_by_copy\>\)\|
- \ \%(\%(\<no_\?\)\?histsavenodups\>\)\|\%(\%(no_\?\)\?hist_save_no_dups\>\)\|
- \ \%(\%(\<no_\?\)\?histsubstpattern\>\)\|\%(\%(no_\?\)\?hist_subst_pattern\>\)\|
- \ \%(\%(\<no_\?\)\?histverify\>\)\|\%(\%(no_\?\)\?hist_verify\>\)\|
- \ \%(\%(\<no_\?\)\?hup\>\)\|
- \ \%(\%(\<no_\?\)\?ignorebraces\>\)\|\%(\%(no_\?\)\?ignore_braces\>\)\|
- \ \%(\%(\<no_\?\)\?ignoreclosebraces\>\)\|\%(\%(no_\?\)\?ignore_close_braces\>\)\|
- \ \%(\%(\<no_\?\)\?ignoreeof\>\)\|\%(\%(no_\?\)\?ignore_eof\>\)\|
- \ \%(\%(\<no_\?\)\?incappendhistory\>\)\|\%(\%(no_\?\)\?inc_append_history\>\)\|
- \ \%(\%(\<no_\?\)\?incappendhistorytime\>\)\|\%(\%(no_\?\)\?inc_append_history_time\>\)\|
- \ \%(\%(\<no_\?\)\?interactive\>\)\|
- \ \%(\%(\<no_\?\)\?interactivecomments\>\)\|\%(\%(no_\?\)\?interactive_comments\>\)\|
- \ \%(\%(\<no_\?\)\?ksharrays\>\)\|\%(\%(no_\?\)\?ksh_arrays\>\)\|
- \ \%(\%(\<no_\?\)\?kshautoload\>\)\|\%(\%(no_\?\)\?ksh_autoload\>\)\|
- \ \%(\%(\<no_\?\)\?kshglob\>\)\|\%(\%(no_\?\)\?ksh_glob\>\)\|
- \ \%(\%(\<no_\?\)\?kshoptionprint\>\)\|\%(\%(no_\?\)\?ksh_option_print\>\)\|
- \ \%(\%(\<no_\?\)\?kshtypeset\>\)\|\%(\%(no_\?\)\?ksh_typeset\>\)\|
- \ \%(\%(\<no_\?\)\?kshzerosubscript\>\)\|\%(\%(no_\?\)\?ksh_zero_subscript\>\)\|
- \ \%(\%(\<no_\?\)\?listambiguous\>\)\|\%(\%(no_\?\)\?list_ambiguous\>\)\|
- \ \%(\%(\<no_\?\)\?listbeep\>\)\|\%(\%(no_\?\)\?list_beep\>\)\|
- \ \%(\%(\<no_\?\)\?listpacked\>\)\|\%(\%(no_\?\)\?list_packed\>\)\|
- \ \%(\%(\<no_\?\)\?listrowsfirst\>\)\|\%(\%(no_\?\)\?list_rows_first\>\)\|
- \ \%(\%(\<no_\?\)\?listtypes\>\)\|\%(\%(no_\?\)\?list_types\>\)\|
- \ \%(\%(\<no_\?\)\?localloops\>\)\|\%(\%(no_\?\)\?local_loops\>\)\|
- \ \%(\%(\<no_\?\)\?localoptions\>\)\|\%(\%(no_\?\)\?local_options\>\)\|
- \ \%(\%(\<no_\?\)\?localpatterns\>\)\|\%(\%(no_\?\)\?local_patterns\>\)\|
- \ \%(\%(\<no_\?\)\?localtraps\>\)\|\%(\%(no_\?\)\?local_traps\>\)\|
- \ \%(\%(\<no_\?\)\?log\>\)\|
- \ \%(\%(\<no_\?\)\?login\>\)\|
- \ \%(\%(\<no_\?\)\?longlistjobs\>\)\|\%(\%(no_\?\)\?long_list_jobs\>\)\|
- \ \%(\%(\<no_\?\)\?magicequalsubst\>\)\|\%(\%(no_\?\)\?magic_equal_subst\>\)\|
- \ \%(\%(\<no_\?\)\?mark_dirs\>\)\|
- \ \%(\%(\<no_\?\)\?mailwarn\>\)\|\%(\%(no_\?\)\?mail_warn\>\)\|
- \ \%(\%(\<no_\?\)\?mailwarning\>\)\|\%(\%(no_\?\)\?mail_warning\>\)\|
- \ \%(\%(\<no_\?\)\?markdirs\>\)\|
- \ \%(\%(\<no_\?\)\?menucomplete\>\)\|\%(\%(no_\?\)\?menu_complete\>\)\|
- \ \%(\%(\<no_\?\)\?monitor\>\)\|
- \ \%(\%(\<no_\?\)\?multibyte\>\)\|\%(\%(no_\?\)\?multi_byte\>\)\|
- \ \%(\%(\<no_\?\)\?multifuncdef\>\)\|\%(\%(no_\?\)\?multi_func_def\>\)\|
- \ \%(\%(\<no_\?\)\?multios\>\)\|\%(\%(no_\?\)\?multi_os\>\)\|
- \ \%(\%(\<no_\?\)\?nomatch\>\)\|\%(\%(no_\?\)\?no_match\>\)\|
- \ \%(\%(\<no_\?\)\?notify\>\)\|
- \ \%(\%(\<no_\?\)\?nullglob\>\)\|\%(\%(no_\?\)\?null_glob\>\)\|
- \ \%(\%(\<no_\?\)\?numericglobsort\>\)\|\%(\%(no_\?\)\?numeric_glob_sort\>\)\|
- \ \%(\%(\<no_\?\)\?octalzeroes\>\)\|\%(\%(no_\?\)\?octal_zeroes\>\)\|
- \ \%(\%(\<no_\?\)\?onecmd\>\)\|\%(\%(no_\?\)\?one_cmd\>\)\|
- \ \%(\%(\<no_\?\)\?overstrike\>\)\|\%(\%(no_\?\)\?over_strike\>\)\|
- \ \%(\%(\<no_\?\)\?pathdirs\>\)\|\%(\%(no_\?\)\?path_dirs\>\)\|
- \ \%(\%(\<no_\?\)\?pathscript\>\)\|\%(\%(no_\?\)\?path_script\>\)\|
- \ \%(\%(\<no_\?\)\?physical\>\)\|
- \ \%(\%(\<no_\?\)\?pipefail\>\)\|\%(\%(no_\?\)\?pipe_fail\>\)\|
- \ \%(\%(\<no_\?\)\?posixaliases\>\)\|\%(\%(no_\?\)\?posix_aliases\>\)\|
- \ \%(\%(\<no_\?\)\?posixargzero\>\)\|\%(\%(no_\?\)\?posix_arg_zero\>\)\|\%(\%(no_\?\)\?posix_argzero\>\)\|
- \ \%(\%(\<no_\?\)\?posixbuiltins\>\)\|\%(\%(no_\?\)\?posix_builtins\>\)\|
- \ \%(\%(\<no_\?\)\?posixcd\>\)\|\%(\%(no_\?\)\?posix_cd\>\)\|
- \ \%(\%(\<no_\?\)\?posixidentifiers\>\)\|\%(\%(no_\?\)\?posix_identifiers\>\)\|
- \ \%(\%(\<no_\?\)\?posixjobs\>\)\|\%(\%(no_\?\)\?posix_jobs\>\)\|
- \ \%(\%(\<no_\?\)\?posixstrings\>\)\|\%(\%(no_\?\)\?posix_strings\>\)\|
- \ \%(\%(\<no_\?\)\?posixtraps\>\)\|\%(\%(no_\?\)\?posix_traps\>\)\|
- \ \%(\%(\<no_\?\)\?printeightbit\>\)\|\%(\%(no_\?\)\?print_eight_bit\>\)\|
- \ \%(\%(\<no_\?\)\?printexitvalue\>\)\|\%(\%(no_\?\)\?print_exit_value\>\)\|
- \ \%(\%(\<no_\?\)\?privileged\>\)\|
- \ \%(\%(\<no_\?\)\?promptbang\>\)\|\%(\%(no_\?\)\?prompt_bang\>\)\|
- \ \%(\%(\<no_\?\)\?promptcr\>\)\|\%(\%(no_\?\)\?prompt_cr\>\)\|
- \ \%(\%(\<no_\?\)\?promptpercent\>\)\|\%(\%(no_\?\)\?prompt_percent\>\)\|
- \ \%(\%(\<no_\?\)\?promptsp\>\)\|\%(\%(no_\?\)\?prompt_sp\>\)\|
- \ \%(\%(\<no_\?\)\?promptsubst\>\)\|\%(\%(no_\?\)\?prompt_subst\>\)\|
- \ \%(\%(\<no_\?\)\?promptvars\>\)\|\%(\%(no_\?\)\?prompt_vars\>\)\|
- \ \%(\%(\<no_\?\)\?pushdignoredups\>\)\|\%(\%(no_\?\)\?pushd_ignore_dups\>\)\|
- \ \%(\%(\<no_\?\)\?pushdminus\>\)\|\%(\%(no_\?\)\?pushd_minus\>\)\|
- \ \%(\%(\<no_\?\)\?pushdsilent\>\)\|\%(\%(no_\?\)\?pushd_silent\>\)\|
- \ \%(\%(\<no_\?\)\?pushdtohome\>\)\|\%(\%(no_\?\)\?pushd_to_home\>\)\|
- \ \%(\%(\<no_\?\)\?rcexpandparam\>\)\|\%(\%(no_\?\)\?rc_expandparam\>\)\|\%(\%(no_\?\)\?rc_expand_param\>\)\|
- \ \%(\%(\<no_\?\)\?rcquotes\>\)\|\%(\%(no_\?\)\?rc_quotes\>\)\|
- \ \%(\%(\<no_\?\)\?rcs\>\)\|
- \ \%(\%(\<no_\?\)\?recexact\>\)\|\%(\%(no_\?\)\?rec_exact\>\)\|
- \ \%(\%(\<no_\?\)\?rematchpcre\>\)\|\%(\%(no_\?\)\?re_match_pcre\>\)\|\%(\%(no_\?\)\?rematch_pcre\>\)\|
- \ \%(\%(\<no_\?\)\?restricted\>\)\|
- \ \%(\%(\<no_\?\)\?rmstarsilent\>\)\|\%(\%(no_\?\)\?rm_star_silent\>\)\|
- \ \%(\%(\<no_\?\)\?rmstarwait\>\)\|\%(\%(no_\?\)\?rm_star_wait\>\)\|
- \ \%(\%(\<no_\?\)\?sharehistory\>\)\|\%(\%(no_\?\)\?share_history\>\)\|
- \ \%(\%(\<no_\?\)\?shfileexpansion\>\)\|\%(\%(no_\?\)\?sh_file_expansion\>\)\|
- \ \%(\%(\<no_\?\)\?shglob\>\)\|\%(\%(no_\?\)\?sh_glob\>\)\|
- \ \%(\%(\<no_\?\)\?shinstdin\>\)\|\%(\%(no_\?\)\?shin_stdin\>\)\|
- \ \%(\%(\<no_\?\)\?shnullcmd\>\)\|\%(\%(no_\?\)\?sh_nullcmd\>\)\|
- \ \%(\%(\<no_\?\)\?shoptionletters\>\)\|\%(\%(no_\?\)\?sh_option_letters\>\)\|
- \ \%(\%(\<no_\?\)\?shortloops\>\)\|\%(\%(no_\?\)\?short_loops\>\)\|
- \ \%(\%(\<no_\?\)\?shortrepeat\>\)\|\%(\%(no_\?\)\?short_repeat\>\)\|
- \ \%(\%(\<no_\?\)\?shwordsplit\>\)\|\%(\%(no_\?\)\?sh_word_split\>\)\|
- \ \%(\%(\<no_\?\)\?singlecommand\>\)\|\%(\%(no_\?\)\?single_command\>\)\|
- \ \%(\%(\<no_\?\)\?singlelinezle\>\)\|\%(\%(no_\?\)\?single_line_zle\>\)\|
- \ \%(\%(\<no_\?\)\?sourcetrace\>\)\|\%(\%(no_\?\)\?source_trace\>\)\|
- \ \%(\%(\<no_\?\)\?stdin\>\)\|
- \ \%(\%(\<no_\?\)\?sunkeyboardhack\>\)\|\%(\%(no_\?\)\?sun_keyboard_hack\>\)\|
- \ \%(\%(\<no_\?\)\?trackall\>\)\|\%(\%(no_\?\)\?track_all\>\)\|
- \ \%(\%(\<no_\?\)\?transientrprompt\>\)\|\%(\%(no_\?\)\?transient_rprompt\>\)\|
- \ \%(\%(\<no_\?\)\?trapsasync\>\)\|\%(\%(no_\?\)\?traps_async\>\)\|
- \ \%(\%(\<no_\?\)\?typesetsilent\>\)\|\%(\%(no_\?\)\?type_set_silent\>\)\|\%(\%(no_\?\)\?typeset_silent\>\)\|
- \ \%(\%(\<no_\?\)\?unset\>\)\|
- \ \%(\%(\<no_\?\)\?verbose\>\)\|
- \ \%(\%(\<no_\?\)\?vi\>\)\|
- \ \%(\%(\<no_\?\)\?warnnestedvar\>\)\|\%(\%(no_\?\)\?warn_nested_var\>\)\|
- \ \%(\%(\<no_\?\)\?warncreateglobal\>\)\|\%(\%(no_\?\)\?warn_create_global\>\)\|
- \ \%(\%(\<no_\?\)\?xtrace\>\)\|
- \ \%(\%(\<no_\?\)\?zle\>\)/ nextgroup=zshOption,zshComment skipwhite contained
-
+syn match zshOptStart
+ \ /\v^\s*%(%(un)?setopt|set\s+[-+]o)/
+ \ nextgroup=zshOption skipwhite
+syn match zshOption nextgroup=zshOption,zshComment skipwhite contained /\v
+ \ <%(no_?)?%(
+ \ auto_?cd|auto_?pushd|cdable_?vars|cd_?silent|chase_?dots|chase_?links|posix_?cd|pushd_?ignore_?dups|pushd_?minus|pushd_?silent|pushd_?to_?home|always_?last_?prompt|always_?to_?end|auto_?list|auto_?menu|auto_?name_?dirs|auto_?param_?keys|auto_?param_?slash|auto_?remove_?slash|bash_?auto_?list|complete_?aliases|complete_?in_?word|glob_?complete|hash_?list_?all|list_?ambiguous|list_?beep|list_?packed|list_?rows_?first|list_?types|menu_?complete|rec_?exact|bad_?pattern|bare_?glob_?qual|brace_?ccl|case_?glob|case_?match|case_?paths|csh_?null_?glob|equals|extended_?glob|force_?float|glob|glob_?assign|glob_?dots|glob_?star_?short|glob_?subst|hist_?subst_?pattern|ignore_?braces|ignore_?close_?braces|ksh_?glob|magic_?equal_?subst|mark_?dirs|multibyte|nomatch|null_?glob|numeric_?glob_?sort|rc_?expand_?param|rematch_?pcre|sh_?glob|unset|warn_?create_?global|warn_?nested_?var|warnnestedvar|append_?history|bang_?hist|extended_?history|hist_?allow_?clobber|hist_?beep|hist_?expire_?dups_?first|hist_?fcntl_?lock|hist_?find_?no_?dups|hist_?ignore_?all_?dups|hist_?ignore_?dups|hist_?ignore_?space|hist_?lex_?words|hist_?no_?functions|hist_?no_?store|hist_?reduce_?blanks|hist_?save_?by_?copy|hist_?save_?no_?dups|hist_?verify|inc_?append_?history|inc_?append_?history_?time|share_?history|all_?export|global_?export|global_?rcs|rcs|aliases|clobber|clobber_?empty|correct|correct_?all|dvorak|flow_?control|ignore_?eof|interactive_?comments|hash_?cmds|hash_?dirs|hash_?executables_?only|mail_?warning|path_?dirs|path_?script|print_?eight_?bit|print_?exit_?value|rc_?quotes|rm_?star_?silent|rm_?star_?wait|short_?loops|short_?repeat|sun_?keyboard_?hack|auto_?continue|auto_?resume|bg_?nice|check_?jobs|check_?running_?jobs|hup|long_?list_?jobs|monitor|notify|posix_?jobs|prompt_?bang|prompt_?cr|prompt_?sp|prompt_?percent|prompt_?subst|transient_?rprompt|alias_?func_?def|c_?bases|c_?precedences|debug_?before_?cmd|err_?exit|err_?return|eval_?lineno|exec|function_?argzero|local_?loops|local_?options|local_?patterns|local_?traps|multi_?func_?def|multios|octal_?zeroes|pipe_?fail|source_?trace|typeset_?silent|typeset_?to_?unset|verbose|xtrace|append_?create|bash_?rematch|bsd_?echo|continue_?on_?error|csh_?junkie_?history|csh_?junkie_?loops|csh_?junkie_?quotes|csh_?nullcmd|ksh_?arrays|ksh_?autoload|ksh_?option_?print|ksh_?typeset|ksh_?zero_?subscript|posix_?aliases|posix_?argzero|posix_?builtins|posix_?identifiers|posix_?strings|posix_?traps|sh_?file_?expansion|sh_?nullcmd|sh_?option_?letters|sh_?word_?split|traps_?async|interactive|login|privileged|restricted|shin_?stdin|single_?command|beep|combining_?chars|emacs|overstrike|single_?line_?zle|vi|zle|brace_?expand|dot_?glob|hash_?all|hist_?append|hist_?expand|log|mail_?warn|one_?cmd|physical|prompt_?vars|stdin|track_?all|no_?match
+ \)>/
syn case match
syn keyword zshTypes float integer local typeset declare private readonly
@@ -365,7 +164,7 @@ syn region zshGlob start='(#' end=')'
syn region zshMathSubst matchgroup=zshSubstDelim transparent
\ start='\%(\$\?\)[<=>]\@<!((' skip='\\)' end='))'
\ contains=zshParentheses,@zshSubst,zshNumber,
- \ @zshDerefs,zshString keepend fold
+ \ @zshDerefs,zshString fold
" The ms=s+1 prevents matching zshBrackets several times on opening brackets
" (see https://github.com/chrisbra/vim-zsh/issues/21#issuecomment-576330348)
syn region zshBrackets contained transparent start='{'ms=s+1 skip='\\}'
diff --git a/scripts/squash_typos.py b/scripts/squash_typos.py
deleted file mode 100644
index b403a9b7c8..0000000000
--- a/scripts/squash_typos.py
+++ /dev/null
@@ -1,263 +0,0 @@
-#!/usr/bin/env python
-"""
-
-This script squashes a PR tagged with the "typo" label into a single, dedicated
-"squash PR".
-
-"""
-
-import subprocess
-import sys
-import os
-
-
-def get_authors_and_emails_from_pr():
- """
-
- Return all contributing authors and their emails for the PR on current branch.
- This includes co-authors, meaning that if two authors are credited for a
- single commit, which is possible with GitHub, then both will get credited.
-
- """
-
- # Get a list of all authors involved in the pull request (including co-authors).
- authors = subprocess.check_output(
- [
- "gh",
- "pr",
- "view",
- os.environ["PR_NUMBER"],
- "--json",
- "commits",
- "--jq",
- ".[][].authors.[].name",
- ],
- text=True,
- ).splitlines()
-
- # Get a list of emails of the aforementioned authors.
- emails = subprocess.check_output(
- [
- "gh",
- "pr",
- "view",
- os.environ["PR_NUMBER"],
- "--json",
- "commits",
- "--jq",
- ".[][].authors.[].email",
- ],
- text=True,
- ).splitlines()
-
- authors_and_emails = [(author, mail) for author, mail in zip(authors, emails)]
-
- return authors_and_emails
-
-
-def rebase_onto_pr():
- """
-
- Rebase current branch onto the PR.
-
- """
-
- # Check out the pull request.
- subprocess.call(["gh", "pr", "checkout", os.environ["PR_NUMBER"]])
-
- rebase_onto_master()
-
- # Change back to the original branch.
- subprocess.call(["git", "switch", "-"])
-
- # Rebase onto the pull request, aka include the commits in the pull request
- # in the current branch. Abort with error message if rebase fails.
-
- try:
- subprocess.check_call(["git", "rebase", "-"])
- except subprocess.CalledProcessError:
- subprocess.call(["git", "rebase", "--abort"])
- squash_url = subprocess.check_output(
- ["gh", "pr", "view", "--json", "url", "--jq", ".url"], text=True
- ).strip()
-
- subprocess.call(
- [
- "gh",
- "pr",
- "comment",
- os.environ["PR_NUMBER"],
- "--body",
- f"Your edit conflicts with an already scheduled fix \
- ({squash_url}). Please check that batch PR whether your fix is \
- already included; if not, then please wait until the batch PR \
- is merged and then rebase your PR on top of master.",
- ]
- )
-
- sys.exit(
- f"\n\nERROR: Your edit conflicts with an already scheduled fix \
-{squash_url} \n\n"
- )
-
-
-def rebase_onto_master():
- """
-
- Rebase current branch onto the master i.e. make sure current branch is up
- to date. Abort on error.
-
- """
-
- default_branch = f"{os.environ['GITHUB_BASE_REF']}"
- subprocess.check_call(["git", "rebase", default_branch])
-
-
-def squash_all_commits(message_body_before):
- """
-
- Squash all commits on the PR into a single commit. Credit all authors by
- name and email.
-
- """
-
- default_branch = f"{os.environ['GITHUB_BASE_REF']}"
- subprocess.call(["git", "reset", "--soft", default_branch])
-
- authors_and_emails = get_authors_and_emails_from_pr()
- commit_message_coauthors = (
- "\n"
- + "\n".join([f"Co-authored-by: {i[0]} <{i[1]}>" for i in authors_and_emails])
- + "\n"
- + message_body_before
- )
- subprocess.call(
- ["git", "commit", "-m", "chore: typo fixes", "-m", commit_message_coauthors]
- )
-
-
-def force_push(branch):
- """
-
- Like the name implies, force push <branch>.
-
- """
-
- gh_actor = os.environ["GITHUB_ACTOR"]
- gh_token = os.environ["GITHUB_TOKEN"]
- gh_repo = os.environ["GITHUB_REPOSITORY"]
- subprocess.call(
- [
- "git",
- "push",
- "--force",
- f"https://{gh_actor}:{gh_token}@github.com/{gh_repo}",
- branch,
- ]
- )
-
-
-def checkout_branch(branch):
- """
-
- Create and checkout <branch>. Check if branch exists on remote, if so then
- sync local branch to remote.
-
- Return True if remote branch exists, else False.
-
- """
-
- # FIXME I'm not sure why the local branch isn't tracking the remote branch
- # automatically. This works but I'm pretty sure it can be done in a more
- # "elegant" fashion
-
- show_ref_output = subprocess.check_output(["git", "show-ref"], text=True).strip()
-
- if branch in show_ref_output:
- subprocess.call(["git", "checkout", "-b", branch, f"origin/{branch}"])
- return True
-
- subprocess.call(["git", "checkout", "-b", branch])
- return False
-
-
-def get_all_pr_urls(pr_branch_exists):
- """
-
- Return a list of URLs for the pull requests with the typo fixes. If a
- squash branch exists then extract the URLs from the body text.
-
- """
-
- all_pr_urls = ""
- if pr_branch_exists:
- all_pr_urls += subprocess.check_output(
- ["gh", "pr", "view", "--json", "body", "--jq", ".body"], text=True
- )
-
- all_pr_urls += subprocess.check_output(
- ["gh", "pr", "view", os.environ["PR_NUMBER"], "--json", "url", "--jq", ".url"],
- text=True,
- ).strip()
-
- return all_pr_urls
-
-
-def main():
- pr_branch = "marvim/squash-typos"
-
- pr_branch_exists = checkout_branch(pr_branch)
-
- rebase_onto_master()
- force_push(pr_branch)
-
- message_body_before = "\n".join(
- subprocess.check_output(
- ["git", "log", "--format=%B", "-n1", pr_branch], text=True
- ).splitlines()[2:]
- )
-
- rebase_onto_pr()
- force_push(pr_branch)
-
- subprocess.call(
- [
- "gh",
- "pr",
- "create",
- "--fill",
- "--head",
- pr_branch,
- "--title",
- "chore: typo fixes (automated)",
- ],
- text=True,
- )
-
- squash_all_commits(message_body_before)
- force_push(pr_branch)
-
- all_pr_urls = get_all_pr_urls(pr_branch_exists)
- subprocess.call(["gh", "pr", "edit", "--add-label", "typo", "--body", all_pr_urls])
-
- subprocess.call(["gh", "pr", "close", os.environ["PR_NUMBER"]])
-
- squash_url = subprocess.check_output(
- ["gh", "pr", "view", "--json", "url", "--jq", ".url"], text=True
- ).strip()
- subprocess.call(
- [
- "gh",
- "pr",
- "comment",
- os.environ["PR_NUMBER"],
- "--body",
- f"Thank you for your contribution! We collect all typo fixes \
- into a single pull request and merge it once it gets big enough: \
- {squash_url}",
- ]
- )
-
-
-if __name__ == "__main__":
- main()
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 7988bff25a..bacb991c4b 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -12,20 +12,16 @@
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
-#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
-#include "nvim/fileio.h"
-#include "nvim/getchar.h"
#include "nvim/lua/executor.h"
-#include "nvim/map.h"
-#include "nvim/map_defs.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -1277,6 +1273,63 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
return res;
}
+/// Create a new user command |user-commands| in the given buffer.
+///
+/// @param buffer Buffer handle, or 0 for current buffer.
+/// @param[out] err Error details, if any.
+/// @see nvim_add_user_command
+void nvim_buf_add_user_command(Buffer buffer, String name, Object command,
+ Dict(user_command) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ buf_T *target_buf = find_buffer_by_handle(buffer, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+
+ buf_T *save_curbuf = curbuf;
+ curbuf = target_buf;
+ add_user_command(name, command, opts, UC_BUFFER, err);
+ curbuf = save_curbuf;
+}
+
+/// Delete a buffer-local user-defined command.
+///
+/// Only commands created with |:command-buffer| or
+/// |nvim_buf_add_user_command()| can be deleted with this function.
+///
+/// @param buffer Buffer handle, or 0 for current buffer.
+/// @param name Name of the command to delete.
+/// @param[out] err Error details, if any.
+void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
+ FUNC_API_SINCE(9)
+{
+ garray_T *gap;
+ if (buffer == -1) {
+ gap = &ucmds;
+ } else {
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ gap = &buf->b_ucmds;
+ }
+
+ for (int i = 0; i < gap->ga_len; i++) {
+ ucmd_T *cmd = USER_CMD_GA(gap, i);
+ if (!STRCMP(name.data, cmd->uc_name)) {
+ free_ucmd(cmd);
+
+ gap->ga_len -= 1;
+
+ if (i < gap->ga_len) {
+ memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
+ }
+
+ return;
+ }
+ }
+
+ api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
+}
+
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
{
Dictionary rv = ARRAY_DICT_INIT;
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index f3e7f2f1dc..e47dec9eb7 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -33,6 +33,18 @@ return {
get_commands = {
"builtin";
};
+ user_command = {
+ "addr";
+ "bang";
+ "bar";
+ "complete";
+ "count";
+ "desc";
+ "force";
+ "nargs";
+ "range";
+ "register";
+ };
float_config = {
"row";
"col";
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 9b407eab8b..962fce6952 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -961,6 +961,10 @@ Object copy_object(Object obj)
case kObjectTypeDictionary:
return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
+
+ case kObjectTypeLuaRef:
+ return LUAREF_OBJ(api_new_luaref(obj.data.luaref));
+
default:
abort();
}
@@ -1342,3 +1346,184 @@ const char *get_default_stl_hl(win_T *wp)
return "StatusLineNC";
}
}
+
+void add_user_command(String name, Object command, Dict(user_command) *opts, int flags, Error *err)
+{
+ uint32_t argt = 0;
+ long def = -1;
+ cmd_addr_T addr_type_arg = ADDR_NONE;
+ int compl = EXPAND_NOTHING;
+ char *compl_arg = NULL;
+ char *rep = NULL;
+ LuaRef luaref = LUA_NOREF;
+ LuaRef compl_luaref = LUA_NOREF;
+
+ if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) {
+ api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive");
+ goto err;
+ }
+
+ if (opts->nargs.type == kObjectTypeInteger) {
+ switch (opts->nargs.data.integer) {
+ case 0:
+ // Default value, nothing to do
+ break;
+ case 1:
+ argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+ } else if (opts->nargs.type == kObjectTypeString) {
+ if (opts->nargs.data.string.size > 1) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+
+ switch (opts->nargs.data.string.data[0]) {
+ case '*':
+ argt |= EX_EXTRA;
+ break;
+ case '?':
+ argt |= EX_EXTRA | EX_NOSPC;
+ break;
+ case '+':
+ argt |= EX_EXTRA | EX_NEEDARG;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+ } else if (HAS_KEY(opts->nargs)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
+ goto err;
+ }
+
+ if (HAS_KEY(opts->complete) && !argt) {
+ api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
+ goto err;
+ }
+
+ if (opts->range.type == kObjectTypeBoolean) {
+ if (opts->range.data.boolean) {
+ argt |= EX_RANGE;
+ addr_type_arg = ADDR_LINES;
+ }
+ } else if (opts->range.type == kObjectTypeString) {
+ if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
+ argt |= EX_RANGE | EX_DFLALL;
+ addr_type_arg = ADDR_LINES;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
+ goto err;
+ }
+ } else if (opts->range.type == kObjectTypeInteger) {
+ argt |= EX_RANGE | EX_ZEROR;
+ def = opts->range.data.integer;
+ addr_type_arg = ADDR_LINES;
+ } else if (HAS_KEY(opts->range)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
+ goto err;
+ }
+
+ if (opts->count.type == kObjectTypeBoolean) {
+ if (opts->count.data.boolean) {
+ argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
+ addr_type_arg = ADDR_OTHER;
+ def = 0;
+ }
+ } else if (opts->count.type == kObjectTypeInteger) {
+ argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
+ addr_type_arg = ADDR_OTHER;
+ def = opts->count.data.integer;
+ } else if (HAS_KEY(opts->count)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
+ goto err;
+ }
+
+ if (opts->addr.type == kObjectTypeString) {
+ if (parse_addr_type_arg((char_u *)opts->addr.data.string.data, (int)opts->addr.data.string.size,
+ &addr_type_arg) != OK) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
+ goto err;
+ }
+
+ if (addr_type_arg != ADDR_LINES) {
+ argt |= EX_ZEROR;
+ }
+ } else if (HAS_KEY(opts->addr)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
+ goto err;
+ }
+
+ if (api_object_to_bool(opts->bang, "bang", false, err)) {
+ argt |= EX_BANG;
+ } else if (ERROR_SET(err)) {
+ goto err;
+ }
+
+ if (api_object_to_bool(opts->bar, "bar", false, err)) {
+ argt |= EX_TRLBAR;
+ } else if (ERROR_SET(err)) {
+ goto err;
+ }
+
+
+ if (api_object_to_bool(opts->register_, "register", false, err)) {
+ argt |= EX_REGSTR;
+ } else if (ERROR_SET(err)) {
+ goto err;
+ }
+
+ bool force = api_object_to_bool(opts->force, "force", false, err);
+ if (ERROR_SET(err)) {
+ goto err;
+ }
+
+ if (opts->complete.type == kObjectTypeLuaRef) {
+ compl = EXPAND_USER_LUA;
+ compl_luaref = api_new_luaref(opts->complete.data.luaref);
+ } else if (opts->complete.type == kObjectTypeString) {
+ if (parse_compl_arg((char_u *)opts->complete.data.string.data,
+ (int)opts->complete.data.string.size, &compl, &argt,
+ (char_u **)&compl_arg) != OK) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
+ goto err;
+ }
+ } else if (HAS_KEY(opts->complete)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
+ goto err;
+ }
+
+ switch (command.type) {
+ case kObjectTypeLuaRef:
+ luaref = api_new_luaref(command.data.luaref);
+ if (opts->desc.type == kObjectTypeString) {
+ rep = opts->desc.data.string.data;
+ } else {
+ snprintf((char *)IObuff, IOSIZE, "<Lua function %d>", luaref);
+ rep = (char *)IObuff;
+ }
+ break;
+ case kObjectTypeString:
+ rep = command.data.string.data;
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
+ goto err;
+ }
+
+ if (uc_add_command((char_u *)name.data, name.size, (char_u *)rep, argt, def, flags,
+ compl, (char_u *)compl_arg, compl_luaref, addr_type_arg, luaref,
+ force) != OK) {
+ api_set_error(err, kErrorTypeException, "Failed to create user command");
+ goto err;
+ }
+
+ return;
+
+err:
+ NLUA_CLEAR_REF(luaref);
+ NLUA_CLEAR_REF(compl_luaref);
+}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 4f7c320129..dfc606f927 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -169,7 +169,7 @@ void nvim__set_hl_ns(Integer ns_id, Error *err)
// event path for redraws caused by "fast" events. This could tie in with
// better throttling of async events causing redraws, such as non-batched
// nvim_buf_set_extmark calls from async contexts.
- if (!provider_active && !ns_hl_changed) {
+ if (!provider_active && !ns_hl_changed && must_redraw < NOT_VALID) {
multiqueue_put(main_loop.events, on_redraw_event, 0);
}
ns_hl_changed = true;
@@ -662,7 +662,7 @@ Object nvim_get_option(String name, Error *err)
///
/// @param name Option name
/// @param opts Optional parameters
-/// - scope: One of 'global' or 'local'. Analagous to
+/// - scope: One of 'global' or 'local'. Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// @param[out] err Error details, if any
/// @return Option value
@@ -696,7 +696,17 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
rv = INTEGER_OBJ(numval);
break;
case 2:
- rv = BOOLEAN_OBJ(!!numval);
+ switch (numval) {
+ case 0:
+ case 1:
+ rv = BOOLEAN_OBJ(numval);
+ break;
+ default:
+ // Boolean options that return something other than 0 or 1 should return nil. Currently this
+ // only applies to 'autoread' which uses -1 as a local value to indicate "unset"
+ rv = NIL;
+ break;
+ }
break;
default:
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
@@ -714,7 +724,7 @@ end:
/// @param name Option name
/// @param value New option value
/// @param opts Optional parameters
-/// - scope: One of 'global' or 'local'. Analagous to
+/// - scope: One of 'global' or 'local'. Analogous to
/// |:setglobal| and |:setlocal|, respectively.
/// @param[out] err Error details, if any
void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err)
@@ -749,7 +759,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
stringval = value.data.string.data;
break;
case kObjectTypeNil:
- // Do nothing
+ scope |= OPT_CLEAR;
break;
default:
api_set_error(err, kErrorTypeValidation, "invalid value for option");
@@ -1731,7 +1741,7 @@ Array nvim_list_chans(void)
/// 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).
+/// 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
@@ -2091,7 +2101,7 @@ void nvim__screenshot(String path)
}
-/// Deletes a uppercase/file named mark. See |mark-motions|.
+/// Deletes an uppercase/file named mark. See |mark-motions|.
///
/// @note fails with error if a lowercase or buffer local named mark is used.
/// @param name Mark name
@@ -2353,3 +2363,52 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
return result;
}
+
+/// Create a new user command |user-commands|
+///
+/// {name} is the name of the new command. The name must begin with an uppercase letter.
+///
+/// {command} is the replacement text or Lua function to execute.
+///
+/// Example:
+/// <pre>
+/// :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
+/// :SayHello
+/// Hello world!
+/// </pre>
+///
+/// @param name Name of the new user command. Must begin with an uppercase letter.
+/// @param command Replacement command to execute when this user command is executed. When called
+/// from Lua, the command can also be a Lua function. The function is called with a
+/// single table argument that contains the following keys:
+/// - args: (string) The args passed to the command, if any |<args>|
+/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>|
+/// - line1: (number) The starting line of the command range |<line1>|
+/// - line2: (number) The final line of the command range |<line2>|
+/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
+/// - count: (number) Any count supplied |<count>|
+/// - reg: (string) The optional register, if specified |<reg>|
+/// - mods: (string) Command modifiers, if any |<mods>|
+/// @param opts Optional command attributes. See |command-attributes| for more details. To use
+/// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to
+/// "true". When using a Lua function for {command} you can also provide a "desc"
+/// key that will be displayed when listing commands. In addition to the string
+/// options listed in |:command-complete|, the "complete" key also accepts a Lua
+/// function which works like the "customlist" completion mode
+/// |:command-complete-customlist|.
+/// @param[out] err Error details, if any.
+void nvim_add_user_command(String name, Object command, Dict(user_command) *opts, Error *err)
+ FUNC_API_SINCE(9)
+{
+ add_user_command(name, command, opts, 0, err);
+}
+
+/// Delete a user-defined command.
+///
+/// @param name Name of the command to delete.
+/// @param[out] err Error details, if any.
+void nvim_del_user_command(String name, Error *err)
+ FUNC_API_SINCE(9)
+{
+ nvim_buf_del_user_command(-1, name, err);
+}
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 6e68c057dc..907306da7b 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -39,7 +39,7 @@ Buffer nvim_win_get_buf(Window window, Error *err)
return win->w_buffer->handle;
}
-/// Sets the current buffer in a window, without side-effects
+/// Sets the current buffer in a window, without side effects
///
/// @param window Window handle, or 0 for current window
/// @param buffer Buffer handle
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 89baea83f8..abd22fba26 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -4576,7 +4576,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use
cur_tab_rec->def.func = NULL;
}
- // When inside update_screen we do not want redrawing a stausline, ruler,
+ // When inside update_screen we do not want redrawing a statusline, ruler,
// title, etc. to trigger another redraw, it may cause an endless loop.
if (updating_screen) {
must_redraw = save_must_redraw;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 0233b3a5ab..4f4da7c2a9 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -37,8 +37,8 @@
#include "nvim/path.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
-#include "nvim/undo.h"
#include "nvim/ui.h"
+#include "nvim/undo.h"
#include "nvim/vim.h"
#include "nvim/window.h"
#include "xdiff/xdiff.h"
@@ -674,11 +674,11 @@ void diff_redraw(bool dofold)
}
if (wp_other != NULL && curwin->w_p_scb) {
if (used_max_fill_curwin) {
- // The current window was set to used the maximum number of filler
+ // The current window was set to use the maximum number of filler
// lines, may need to reduce them.
diff_set_topline(wp_other, curwin);
} else if (used_max_fill_other) {
- // The other window was set to used the maximum number of filler
+ // The other window was set to use the maximum number of filler
// lines, may need to reduce them.
diff_set_topline(curwin, wp_other);
}
@@ -1508,7 +1508,7 @@ void ex_diffoff(exarg_T *eap)
diff_clear(curtab);
}
- // Remove "hor" from from 'scrollopt' if there are no diff windows left.
+ // Remove "hor" from 'scrollopt' if there are no diff windows left.
if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) {
do_cmdline_cmd("set sbo-=hor");
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 2135d0bcd2..3517b3244e 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -643,7 +643,10 @@ static int insert_check(VimState *state)
update_curswant();
s->old_topline = curwin->w_topline;
s->old_topfill = curwin->w_topfill;
- s->lastc = s->c; // remember previous char for CTRL-D
+
+ if (s->c != K_EVENT) {
+ s->lastc = s->c; // remember previous char for CTRL-D
+ }
// After using CTRL-G U the next cursor key will not break undo.
if (dont_sync_undo == kNone) {
@@ -1597,8 +1600,8 @@ static void ins_ctrl_v(void)
*/
static int pc_status;
#define PC_STATUS_UNSET 0 // pc_bytes was not set
-#define PC_STATUS_RIGHT 1 // right halve of double-wide char
-#define PC_STATUS_LEFT 2 // left halve of double-wide char
+#define PC_STATUS_RIGHT 1 // right half of double-wide char
+#define PC_STATUS_LEFT 2 // left half of double-wide char
#define PC_STATUS_SET 3 // pc_bytes was filled
static char_u pc_bytes[MB_MAXBYTES + 1]; // saved bytes
static int pc_attr;
@@ -1685,7 +1688,7 @@ static void init_prompt(int cmdchar_todo)
// Insert always starts after the prompt, allow editing text after it.
if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) {
Insstart.lnum = curwin->w_cursor.lnum;
- Insstart.col = STRLEN(prompt);
+ Insstart.col = (colnr_T)STRLEN(prompt);
Insstart_orig = Insstart;
Insstart_textlen = Insstart.col;
Insstart_blank_vcol = MAXCOL;
@@ -1696,7 +1699,7 @@ static void init_prompt(int cmdchar_todo)
coladvance(MAXCOL);
}
if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) {
- curwin->w_cursor.col = STRLEN(prompt);
+ curwin->w_cursor.col = (colnr_T)STRLEN(prompt);
}
// Make sure the cursor is in a valid position.
check_cursor();
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 9507a12a02..e445a08227 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -133,6 +133,7 @@ return {
foldtext={},
foldtextresult={args=1, base=1},
foreground={},
+ fullcommand={args=1, base=1},
funcref={args={1, 3}, base=1},
['function']={args={1, 3}, base=1},
garbagecollect={args={0, 1}},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index d43eeb4a15..32026282cf 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1082,15 +1082,13 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Return the current directory
cwd = xmalloc(MAXPATHL);
- if (cwd != NULL) {
- if (os_dirname(cwd, MAXPATHL) != FAIL) {
+ if (os_dirname(cwd, MAXPATHL) != FAIL) {
#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(cwd);
+ slash_adjust(cwd);
#endif
- rettv->vval.v_string = vim_strsave(cwd);
- }
- xfree(cwd);
+ rettv->vval.v_string = vim_strsave(cwd);
}
+ xfree(cwd);
if (curwin->w_localdir != NULL) {
scope = kCdScopeWindow;
@@ -3920,34 +3918,46 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_qf_loc_list(true, NULL, &argvars[0], rettv);
}
-/// "getreg()" function
-static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+/// Common between getreg(), getreginfo() and getregtype(): get the register
+/// name from the first argument.
+/// Returns zero on error.
+static int getreg_get_regname(typval_T *argvars)
{
- const char *strregname;
- int arg2 = false;
- bool return_list = false;
- bool error = false;
+ const char_u *strregname;
if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- error = strregname == NULL;
- if (argvars[1].v_type != VAR_UNKNOWN) {
- arg2 = tv_get_number_chk(&argvars[1], &error);
- if (!error && argvars[2].v_type != VAR_UNKNOWN) {
- return_list = tv_get_number_chk(&argvars[2], &error);
- }
+ strregname = (const char_u *)tv_get_string_chk(&argvars[0]);
+ if (strregname == NULL) { // type error; errmsg already given
+ return 0;
}
} else {
- strregname = _(get_vim_var_str(VV_REG));
+ // Default to v:register
+ strregname = get_vim_var_str(VV_REG);
}
- if (error) {
+ return *strregname == 0 ? '"' : *strregname;
+}
+
+/// "getreg()" function
+static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ int arg2 = false;
+ bool return_list = false;
+
+ int regname = getreg_get_regname(argvars);
+ if (regname == 0) {
return;
}
- int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
- if (regname == 0) {
- regname = '"';
+ if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) {
+ bool error = false;
+ arg2 = (int)tv_get_number_chk(&argvars[1], &error);
+ if (!error && argvars[2].v_type != VAR_UNKNOWN) {
+ return_list = (bool)tv_get_number_chk(&argvars[2], &error);
+ }
+ if (error) {
+ return;
+ }
}
if (return_list) {
@@ -3964,28 +3974,16 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/*
- * "getregtype()" function
- */
+/// "getregtype()" function
static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *strregname;
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- if (strregname == NULL) { // Type error; errmsg already given.
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- return;
- }
- } else {
- // Default to v:register.
- strregname = _(get_vim_var_str(VV_REG));
- }
+ // on error return an empty string
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
- int regname = (uint8_t)(strregname == NULL ? '"' : *strregname);
+ int regname = getreg_get_regname(argvars);
if (regname == 0) {
- regname = '"';
+ return;
}
colnr_T reglen = 0;
@@ -7333,18 +7331,12 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "getreginfo()" function
static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- const char *strregname;
- if (argvars[0].v_type != VAR_UNKNOWN) {
- strregname = tv_get_string_chk(&argvars[0]);
- if (strregname == NULL) {
- return;
- }
- } else {
- strregname = (const char *)get_vim_var_str(VV_REG);
+ int regname = getreg_get_regname(argvars);
+ if (regname == 0) {
+ return;
}
- int regname = (strregname == NULL ? '"' : *strregname);
- if (regname == 0 || regname == '@') {
+ if (regname == '@') {
regname = '"';
}
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 892c46dd04..89fced59c5 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -75,7 +75,7 @@ bool loop_poll_events(Loop *loop, int ms)
/// @note Event is queued into `fast_events`, which is processed outside of the
/// primary `events` queue by loop_poll_events(). For `main_loop`, that
/// means `fast_events` is NOT processed in an "editor mode"
-/// (VimState.execute), so redraw and other side-effects are likely to be
+/// (VimState.execute), so redraw and other side effects are likely to be
/// skipped.
/// @see loop_schedule_deferred
void loop_schedule_fast(Loop *loop, Event event)
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index c0cb17fa61..cc5ab1b554 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3025,7 +3025,7 @@ void ex_append(exarg_T *eap)
// "start" is set to eap->line2+1 unless that position is invalid (when
// eap->line2 pointed to the end of the buffer and nothing was appended)
// "end" is set to lnum when something has been appended, otherwise
- // it is the same than "start" -- Acevedo
+ // it is the same as "start" -- Acevedo
curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ?
eap->line2 + 1 : curbuf->b_ml.ml_line_count;
if (eap->cmdidx != CMD_append) {
@@ -4584,6 +4584,9 @@ void ex_global(exarg_T *eap)
// a match on this line?
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL, NULL);
+ if (regmatch.regprog == NULL) {
+ break; // re-compiling regprog failed
+ }
if ((type == 'g' && match) || (type == 'v' && !match)) {
ml_setmarked(lnum);
ndone++;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 33f9477608..2e8d39ec30 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2162,7 +2162,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
funccal_entry_T funccalp_entry;
save_funccal(&funccalp_entry);
- // Check if this script was sourced before to finds its SID.
+ // Check if this script was sourced before to find its SID.
// If it's new, generate a new SID.
// Always use a new sequence number.
const sctx_T save_current_sctx = current_sctx;
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index ea899b660b..e5eab61f9e 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -192,6 +192,7 @@ struct expand {
int xp_context; // type of expansion
size_t xp_pattern_len; // bytes in xp_pattern before cursor
char_u *xp_arg; // completion function
+ LuaRef xp_luaref; // Ref to Lua completion function
sctx_T xp_script_ctx; // SCTX for completion function
int xp_backslash; // one of the XP_BS_ values
#ifndef BACKSLASH_IN_FILENAME
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 9f0f8d93a3..0d82406a0a 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -67,8 +67,8 @@
#include "nvim/sign.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
-#include "nvim/strings.h"
#include "nvim/state.h"
+#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/terminal.h"
@@ -81,23 +81,7 @@
static int quitmore = 0;
static bool ex_pressedreturn = false;
-typedef struct ucmd {
- char_u *uc_name; // The command name
- uint32_t uc_argt; // The argument type
- char_u *uc_rep; // The command's replacement string
- long uc_def; // The default value for a range/count
- int uc_compl; // completion type
- cmd_addr_T uc_addr_type; // The command's address type
- sctx_T uc_script_ctx; // SCTX where the command was defined
- char_u *uc_compl_arg; // completion argument if any
-} ucmd_T;
-
-#define UC_BUFFER 1 // -buffer: local to current buffer
-
-static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
-
-#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
-#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
+garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
// Whether a command index indicates a user command.
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
@@ -2761,6 +2745,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *
*complp = uc->uc_compl;
}
if (xp != NULL) {
+ xp->xp_luaref = uc->uc_compl_luaref;
xp->xp_arg = uc->uc_compl_arg;
xp->xp_script_ctx = uc->uc_script_ctx;
xp->xp_script_ctx.sc_lnum += sourcing_lnum;
@@ -2900,6 +2885,31 @@ int cmd_exists(const char *const name)
return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1);
}
+// "fullcommand" function
+void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ exarg_T ea;
+ char_u *name = argvars[0].vval.v_string;
+
+ while (name[0] != NUL && name[0] == ':') {
+ name++;
+ }
+ name = skip_range(name, NULL);
+
+ rettv->v_type = VAR_STRING;
+
+ ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
+ ea.cmdidx = (cmdidx_T)0;
+ char_u *p = find_command(&ea, NULL);
+ if (p == NULL || ea.cmdidx == CMD_SIZE) {
+ return;
+ }
+
+ rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
+ ? get_user_commands(NULL, ea.useridx)
+ : cmdnames[ea.cmdidx].cmd_name);
+}
+
/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
/// we don't need/want deleted. Maybe this could be done better if we didn't
/// repeat all this stuff. The only problem is that they may not stay
@@ -5146,8 +5156,9 @@ char_u *get_command_name(expand_T *xp, int idx)
return cmdnames[idx].cmd_name;
}
-static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def,
- int flags, int compl, char_u *compl_arg, cmd_addr_T addr_type, bool force)
+int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags,
+ int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type,
+ LuaRef luaref, bool force)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
ucmd_T *cmd = NULL;
@@ -5201,6 +5212,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
XFREE_CLEAR(cmd->uc_rep);
XFREE_CLEAR(cmd->uc_compl_arg);
+ NLUA_CLEAR_REF(cmd->uc_luaref);
+ NLUA_CLEAR_REF(cmd->uc_compl_luaref);
break;
}
@@ -5231,13 +5244,17 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
cmd->uc_script_ctx = current_sctx;
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
cmd->uc_compl_arg = compl_arg;
+ cmd->uc_compl_luaref = compl_luaref;
cmd->uc_addr_type = addr_type;
+ cmd->uc_luaref = luaref;
return OK;
fail:
xfree(rep_buf);
xfree(compl_arg);
+ NLUA_CLEAR_REF(luaref);
+ NLUA_CLEAR_REF(compl_luaref);
return FAIL;
}
@@ -5276,6 +5293,7 @@ static const char *command_complete[] =
[EXPAND_CSCOPE] = "cscope",
[EXPAND_USER_DEFINED] = "custom",
[EXPAND_USER_LIST] = "customlist",
+ [EXPAND_USER_LUA] = "<Lua function>",
[EXPAND_DIFF_BUFFERS] = "diff_buffer",
[EXPAND_DIRECTORIES] = "dir",
[EXPAND_ENV_VARS] = "environment",
@@ -5677,8 +5695,8 @@ static void ex_command(exarg_T *eap)
} else if (compl > 0 && (argt & EX_EXTRA) == 0) {
emsg(_(e_complete_used_without_nargs));
} else {
- uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
- addr_type_arg, eap->forceit);
+ uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF,
+ addr_type_arg, LUA_NOREF, eap->forceit);
}
}
@@ -5692,11 +5710,13 @@ void ex_comclear(exarg_T *eap)
uc_clear(&curbuf->b_ucmds);
}
-static void free_ucmd(ucmd_T *cmd)
+void free_ucmd(ucmd_T *cmd)
{
xfree(cmd->uc_name);
xfree(cmd->uc_rep);
xfree(cmd->uc_compl_arg);
+ NLUA_CLEAR_REF(cmd->uc_compl_luaref);
+ NLUA_CLEAR_REF(cmd->uc_luaref);
}
/*
@@ -5734,9 +5754,7 @@ static void ex_delcommand(exarg_T *eap)
return;
}
- xfree(cmd->uc_name);
- xfree(cmd->uc_rep);
- xfree(cmd->uc_compl_arg);
+ free_ucmd(cmd);
--gap->ga_len;
@@ -5818,7 +5836,7 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp)
return buf;
}
-static size_t add_cmd_modifier(char_u *buf, char *mod_str, bool *multi_mods)
+static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
{
size_t result = STRLEN(mod_str);
if (*multi_mods) {
@@ -6019,70 +6037,8 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
*buf = '\0';
}
- bool multi_mods = false;
-
- // :aboveleft and :leftabove
- if (cmdmod.split & WSP_ABOVE) {
- result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
- }
- // :belowright and :rightbelow
- if (cmdmod.split & WSP_BELOW) {
- result += add_cmd_modifier(buf, "belowright", &multi_mods);
- }
- // :botright
- if (cmdmod.split & WSP_BOT) {
- result += add_cmd_modifier(buf, "botright", &multi_mods);
- }
-
- typedef struct {
- bool *set;
- char *name;
- } mod_entry_T;
- static mod_entry_T mod_entries[] = {
- { &cmdmod.browse, "browse" },
- { &cmdmod.confirm, "confirm" },
- { &cmdmod.hide, "hide" },
- { &cmdmod.keepalt, "keepalt" },
- { &cmdmod.keepjumps, "keepjumps" },
- { &cmdmod.keepmarks, "keepmarks" },
- { &cmdmod.keeppatterns, "keeppatterns" },
- { &cmdmod.lockmarks, "lockmarks" },
- { &cmdmod.noswapfile, "noswapfile" }
- };
- // the modifiers that are simple flags
- for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
- if (*mod_entries[i].set) {
- result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
- }
- }
-
- // TODO(vim): How to support :noautocmd?
- // TODO(vim): How to support :sandbox?
-
- // :silent
- if (msg_silent > 0) {
- result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent",
- &multi_mods);
- }
- // :tab
- if (cmdmod.tab > 0) {
- result += add_cmd_modifier(buf, "tab", &multi_mods);
- }
- // :topleft
- if (cmdmod.split & WSP_TOP) {
- result += add_cmd_modifier(buf, "topleft", &multi_mods);
- }
+ result += uc_mods((char *)buf);
- // TODO(vim): How to support :unsilent?
-
- // :verbose
- if (p_verbose > 0) {
- result += add_cmd_modifier(buf, "verbose", &multi_mods);
- }
- // :vertical
- if (cmdmod.split & WSP_VERT) {
- result += add_cmd_modifier(buf, "vertical", &multi_mods);
- }
if (quote && buf != NULL) {
buf += result - 2;
*buf = '"';
@@ -6127,6 +6083,76 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
return result;
}
+size_t uc_mods(char *buf)
+{
+ size_t result = 0;
+ bool multi_mods = false;
+
+ // :aboveleft and :leftabove
+ if (cmdmod.split & WSP_ABOVE) {
+ result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
+ }
+ // :belowright and :rightbelow
+ if (cmdmod.split & WSP_BELOW) {
+ result += add_cmd_modifier(buf, "belowright", &multi_mods);
+ }
+ // :botright
+ if (cmdmod.split & WSP_BOT) {
+ result += add_cmd_modifier(buf, "botright", &multi_mods);
+ }
+
+ typedef struct {
+ bool *set;
+ char *name;
+ } mod_entry_T;
+ static mod_entry_T mod_entries[] = {
+ { &cmdmod.browse, "browse" },
+ { &cmdmod.confirm, "confirm" },
+ { &cmdmod.hide, "hide" },
+ { &cmdmod.keepalt, "keepalt" },
+ { &cmdmod.keepjumps, "keepjumps" },
+ { &cmdmod.keepmarks, "keepmarks" },
+ { &cmdmod.keeppatterns, "keeppatterns" },
+ { &cmdmod.lockmarks, "lockmarks" },
+ { &cmdmod.noswapfile, "noswapfile" }
+ };
+ // the modifiers that are simple flags
+ for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
+ if (*mod_entries[i].set) {
+ result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
+ }
+ }
+
+ // TODO(vim): How to support :noautocmd?
+ // TODO(vim): How to support :sandbox?
+
+ // :silent
+ if (msg_silent > 0) {
+ result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
+ }
+ // :tab
+ if (cmdmod.tab > 0) {
+ result += add_cmd_modifier(buf, "tab", &multi_mods);
+ }
+ // :topleft
+ if (cmdmod.split & WSP_TOP) {
+ result += add_cmd_modifier(buf, "topleft", &multi_mods);
+ }
+
+ // TODO(vim): How to support :unsilent?
+
+ // :verbose
+ if (p_verbose > 0) {
+ result += add_cmd_modifier(buf, "verbose", &multi_mods);
+ }
+ // :vertical
+ if (cmdmod.split & WSP_VERT) {
+ result += add_cmd_modifier(buf, "vertical", &multi_mods);
+ }
+
+ return result;
+}
+
static void do_ucmd(exarg_T *eap)
{
char_u *buf;
@@ -6149,6 +6175,11 @@ static void do_ucmd(exarg_T *eap)
cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
}
+ if (cmd->uc_luaref > 0) {
+ nlua_do_ucmd(cmd, eap);
+ return;
+ }
+
/*
* Replace <> in the command by the arguments.
* First round: "buf" is NULL, compute length, allocate "buf".
@@ -7464,9 +7495,8 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
if ((eap->cmdidx == CMD_new
|| eap->cmdidx == CMD_tabnew
|| eap->cmdidx == CMD_tabedit
- || eap->cmdidx == CMD_vnew
- ) && *eap->arg == NUL) {
- // ":new" or ":tabnew" without argument: edit an new empty buffer
+ || eap->cmdidx == CMD_vnew) && *eap->arg == NUL) {
+ // ":new" or ":tabnew" without argument: edit a new empty buffer
setpcmark();
(void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE,
ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0),
@@ -7807,17 +7837,21 @@ bool changedir_func(char_u *new_dir, CdScope scope)
prev_dir = pdir;
}
+ // For UNIX ":cd" means: go to home directory.
+ // On other systems too if 'cdhome' is set.
#if defined(UNIX)
- // On Unix ":cd" means: go to home directory.
if (*new_dir == NUL) {
+#else
+ if (*new_dir == NUL && p_cdh) {
+#endif
// Use NameBuff for home directory name.
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
new_dir = NameBuff;
}
-#endif
- if (vim_chdir(new_dir) == 0) {
- bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ bool dir_differs = new_dir == NULL || pdir == NULL
+ || pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
+ if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) {
post_chdir(scope, dir_differs);
retval = true;
} else {
@@ -7833,9 +7867,9 @@ void ex_cd(exarg_T *eap)
{
char_u *new_dir;
new_dir = eap->arg;
-#if !defined(UNIX) && !defined(VMS)
- // for non-UNIX ":cd" means: print current directory
- if (*new_dir == NUL) {
+#if !defined(UNIX)
+ // for non-UNIX ":cd" means: print current directory unless 'cdhome' is set
+ if (*new_dir == NUL && !p_cdh) {
ex_pwd(NULL);
} else
#endif
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index 7ec4fad277..abf6ec347b 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -2,6 +2,7 @@
#define NVIM_EX_DOCMD_H
#include "nvim/ex_cmds_defs.h"
+#include "nvim/eval/funcs.h"
#include "nvim/globals.h"
// flags for do_cmdline()
@@ -31,6 +32,26 @@ typedef struct {
tasave_T tabuf;
} save_state_T;
+typedef struct ucmd {
+ char_u *uc_name; // The command name
+ uint32_t uc_argt; // The argument type
+ char_u *uc_rep; // The command's replacement string
+ long uc_def; // The default value for a range/count
+ int uc_compl; // completion type
+ cmd_addr_T uc_addr_type; // The command's address type
+ sctx_T uc_script_ctx; // SCTX where the command was defined
+ char_u *uc_compl_arg; // completion argument if any
+ LuaRef uc_compl_luaref; // Reference to Lua completion function
+ LuaRef uc_luaref; // Reference to Lua function
+} ucmd_T;
+
+#define UC_BUFFER 1 // -buffer: local to current buffer
+
+extern garray_T ucmds;
+
+#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
+#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_docmd.h.generated.h"
#endif
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ba2238ace2..67fd5a4efc 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4983,6 +4983,9 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
if (xp->xp_context == EXPAND_USER_LIST) {
return ExpandUserList(xp, num_file, file);
}
+ if (xp->xp_context == EXPAND_USER_LUA) {
+ return ExpandUserLua(xp, num_file, file);
+ }
if (xp->xp_context == EXPAND_PACKADD) {
return ExpandPackAddDir(pat, num_file, file);
}
@@ -5411,6 +5414,35 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
return OK;
}
+static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file)
+{
+ typval_T rettv;
+ nlua_call_user_expand_func(xp, &rettv);
+ if (rettv.v_type != VAR_LIST) {
+ tv_clear(&rettv);
+ return FAIL;
+ }
+
+ list_T *const retlist = rettv.vval.v_list;
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char *), 3);
+ // Loop over the items in the list.
+ TV_LIST_ITER_CONST(retlist, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
+ || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
+ continue; // Skip non-string items and empty strings.
+ }
+
+ GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
+ });
+ tv_list_unref(retlist);
+
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
/// Expand color scheme, compiler or filetype names.
/// Search from 'runtimepath':
/// 'runtimepath'/{dirnames}/{pat}.vim
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index d31021b3ef..b2cd5c510b 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -607,7 +607,7 @@ char_u *vim_findfile(void *search_ctx_arg)
for (;;) {
// downward search loop
for (;;) {
- // check if user user wants to stop the search
+ // check if user wants to stop the search
os_breakcheck();
if (got_int) {
break;
@@ -1139,7 +1139,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *
bool url = false;
FileID file_id;
- // For an URL we only compare the name, otherwise we compare the
+ // For a URL we only compare the name, otherwise we compare the
// device/inode.
if (path_with_url((char *)fname)) {
STRLCPY(ff_expand_buffer, fname, MAXPATHL);
@@ -1667,14 +1667,19 @@ int vim_chdirfile(char_u *fname, CdCause cause)
NameBuff[0] = NUL;
}
- if (os_chdir(dir) == 0) {
- if (cause != kCdCauseOther && pathcmp(dir, (char *)NameBuff, -1) != 0) {
- do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
- }
- } else {
+ if (pathcmp(dir, (char *)NameBuff, -1) == 0) {
+ // nothing to do
+ return OK;
+ }
+
+ if (os_chdir(dir) != 0) {
return FAIL;
}
+ if (cause != kCdCauseOther) {
+ do_autocmd_dirchanged(dir, kCdScopeWindow, cause);
+ }
+
return OK;
}
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index b1d4321d4c..546345eeac 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -2332,7 +2332,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level,
* firstlnum.
*/
while (!got_int) {
- // set concat to 1 if it's allowed to concatenated this fold
+ // set concat to 1 if it's allowed to concatenate this fold
// with a previous one that touches it.
if (flp->start != 0 || flp->had_end <= MAX_LEVEL) {
concat = 0;
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 21f8c3855e..c6dd25154b 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -441,8 +441,8 @@ local function process_function(fn)
local cparam = string.format('arg%u', j)
local param_type = real_type(param[1])
local lc_param_type = real_type(param[1]):lower()
- local extra = ((param_type == "Object" or param_type == "Dictionary") and "false, ") or ""
- if param[1] == "DictionaryOf(LuaRef)" then
+ local extra = param_type == "Dictionary" and "false, " or ""
+ if param[1] == "Object" or param[1] == "DictionaryOf(LuaRef)" then
extra = "true, "
end
local errshift = 0
diff --git a/src/nvim/generators/gen_keysets.lua b/src/nvim/generators/gen_keysets.lua
index 63ef202fe1..01d8c1d357 100644
--- a/src/nvim/generators/gen_keysets.lua
+++ b/src/nvim/generators/gen_keysets.lua
@@ -26,6 +26,17 @@ local defspipe = io.open(defs_file, 'wb')
local keysets = require'api.keysets'
+local keywords = {
+ register = true,
+}
+
+local function sanitize(key)
+ if keywords[key] then
+ return key .. "_"
+ end
+ return key
+end
+
for name, keys in pairs(keysets) do
local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
return name.."_table["..idx.."].str"
@@ -33,7 +44,7 @@ for name, keys in pairs(keysets) do
defspipe:write("typedef struct {\n")
for _, key in ipairs(neworder) do
- defspipe:write(" Object "..key..";\n")
+ defspipe:write(" Object "..sanitize(key)..";\n")
end
defspipe:write("} KeyDict_"..name..";\n\n")
@@ -41,7 +52,7 @@ for name, keys in pairs(keysets) do
funcspipe:write("KeySetLink "..name.."_table[] = {\n")
for _, key in ipairs(neworder) do
- funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..key..")},\n")
+ funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..sanitize(key)..")},\n")
end
funcspipe:write(' {NULL, 0},\n')
funcspipe:write("};\n\n")
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 6b1150cefa..424bf758e2 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -146,7 +146,7 @@ static int KeyNoremap = 0; // remapping flags
// typebuf.tb_buf has three parts: room in front (for result of mappings), the
// middle for typeahead and room for new characters (which needs to be 3 *
-// MAXMAPLEN) for the Amiga).
+// MAXMAPLEN for the Amiga).
#define TYPELEN_INIT (5 * (MAXMAPLEN + 3))
static char_u typebuf_init[TYPELEN_INIT]; // initial typebuf.tb_buf
static char_u noremapbuf_init[TYPELEN_INIT]; // initial typebuf.tb_noremap
@@ -861,7 +861,7 @@ void init_default_mappings(void)
//
// If noremap is REMAP_YES, new string can be mapped again.
// If noremap is REMAP_NONE, new string cannot be mapped again.
-// If noremap is REMAP_SKIP, fist char of new string cannot be mapped again,
+// If noremap is REMAP_SKIP, first char of new string cannot be mapped again,
// but abbreviations are allowed.
// If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for
// script-local mappings.
@@ -1693,7 +1693,7 @@ typedef enum {
map_result_fail, // failed, break loop
map_result_get, // get a character from typeahead
map_result_retry, // try to map again
- map_result_nomatch // no matching mapping, get char
+ map_result_nomatch, // no matching mapping, get char
} map_result_T;
/// Handle mappings in the typeahead buffer.
@@ -2470,7 +2470,7 @@ static int vgetorpeek(bool advance)
/// Return the number of obtained characters.
/// Return -1 when end of input script reached.
///
-/// @param wait_time milli seconds
+/// @param wait_time milliseconds
int inchar(char_u *buf, int maxlen, long wait_time)
{
int len = 0; // Init for GCC.
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index faa9b38cf7..0f0cab33ea 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -923,11 +923,10 @@ static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum)
while (*s && *s != ';' && *s != '\'' && *s != '"') {
if (*s == ')' && cin_nocode(s + 1)) {
- /* ')' at the end: may have found a match
- * Check for he previous line not to end in a backslash:
- * #if defined(x) && \
- * defined(y)
- */
+ // ')' at the end: may have found a match
+ // Check for the previous line not to end in a backslash:
+ // #if defined(x) && {backslash}
+ // defined(y)
lnum = first_lnum - 1;
s = ml_get(lnum);
if (*s == NUL || s[STRLEN(s) - 1] != '\\')
@@ -1634,8 +1633,8 @@ void parse_cino(buf_T *buf)
* itself is also unclosed. */
buf->b_ind_unclosed2 = sw;
- /* Suppress ignoring spaces from the indent of a line starting with an
- * unclosed parentheses. */
+ // Suppress ignoring spaces from the indent of a line starting with an
+ // unclosed parenthesis.
buf->b_ind_unclosed_noignore = 0;
/* If the opening paren is the last nonwhite character on the line, and
@@ -1647,11 +1646,11 @@ void parse_cino(buf_T *buf)
* an unclosed parentheses. */
buf->b_ind_unclosed_whiteok = 0;
- /* Indent a closing parentheses under the line start of the matching
- * opening parentheses. */
+ // Indent a closing parenthesis under the line start of the matching
+ // opening parenthesis.
buf->b_ind_matching_paren = 0;
- // Indent a closing parentheses under the previous line.
+ // Indent a closing parenthesis under the previous line.
buf->b_ind_paren_prev = 0;
// Extra indent for comments.
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 107ff22913..7d43d21d53 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -18,6 +18,7 @@
#include "nvim/event/loop.h"
#include "nvim/event/time.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/func_attr.h"
@@ -914,6 +915,24 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg
}
}
+void nlua_call_user_expand_func(expand_T *xp, typval_T *ret_tv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ lua_State *const lstate = global_lstate;
+
+ nlua_pushref(lstate, xp->xp_luaref);
+ lua_pushstring(lstate, (char *)xp->xp_pattern);
+ lua_pushstring(lstate, (char *)xp->xp_line);
+ lua_pushinteger(lstate, xp->xp_col);
+
+ if (nlua_pcall(lstate, 3, 1)) {
+ nlua_error(lstate, _("E5108: Error executing Lua function: %.*s"));
+ return;
+ }
+
+ nlua_pop_typval(lstate, ret_tv);
+}
+
static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name,
typval_T *const args, int argcount, bool special, typval_T *ret_tv)
{
@@ -1432,3 +1451,48 @@ void nlua_execute_on_key(int c)
#endif
}
+void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
+{
+ lua_State *const lstate = global_lstate;
+
+ nlua_pushref(lstate, cmd->uc_luaref);
+
+ lua_newtable(lstate);
+ lua_pushboolean(lstate, eap->forceit == 1);
+ lua_setfield(lstate, -2, "bang");
+
+ lua_pushinteger(lstate, eap->line1);
+ lua_setfield(lstate, -2, "line1");
+
+ lua_pushinteger(lstate, eap->line2);
+ lua_setfield(lstate, -2, "line2");
+
+ lua_pushstring(lstate, (const char *)eap->arg);
+ lua_setfield(lstate, -2, "args");
+
+ lua_pushstring(lstate, (const char *)&eap->regname);
+ lua_setfield(lstate, -2, "reg");
+
+ lua_pushinteger(lstate, eap->addr_count);
+ lua_setfield(lstate, -2, "range");
+
+ if (eap->addr_count > 0) {
+ lua_pushinteger(lstate, eap->line2);
+ } else {
+ lua_pushinteger(lstate, cmd->uc_def);
+ }
+ lua_setfield(lstate, -2, "count");
+
+ // The size of this buffer is chosen empirically to be large enough to hold
+ // every possible modifier (with room to spare). If the list of possible
+ // modifiers grows this may need to be updated.
+ char buf[200] = { 0 };
+ (void)uc_mods(buf);
+ lua_pushstring(lstate, buf);
+ lua_setfield(lstate, -2, "mods");
+
+ if (nlua_pcall(lstate, 1, 0)) {
+ nlua_error(lstate, _("Error executing Lua callback: %.*s"));
+ }
+}
+
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index a1f66bd02b..bf78f7ec5e 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -7,6 +7,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/func_attr.h"
#include "nvim/lua/converter.h"
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
new file mode 100644
index 0000000000..b84124bc19
--- /dev/null
+++ b/src/nvim/lua/spell.c
@@ -0,0 +1,99 @@
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "nvim/spell.h"
+#include "nvim/vim.h"
+#include "nvim/lua/spell.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/spell.c.generated.h"
+#endif
+
+int nlua_spell_check(lua_State *lstate)
+{
+ if (lua_gettop(lstate) < 1) {
+ return luaL_error(lstate, "Expected 1 argument");
+ }
+
+ if (lua_type(lstate, 1) != LUA_TSTRING) {
+ luaL_argerror(lstate, 1, "expected string");
+ }
+
+ const char *str = lua_tolstring(lstate, 1, NULL);
+
+ // spell.c requires that 'spell' is enabled, so we need to temporarily enable
+ // it before we can call spell functions.
+ const int wo_spell_save = curwin->w_p_spell;
+
+ if (!curwin->w_p_spell) {
+ did_set_spelllang(curwin);
+ curwin->w_p_spell = true;
+ }
+
+ // Check 'spelllang'
+ if (*curwin->w_s->b_p_spl == NUL) {
+ emsg(_(e_no_spell));
+ curwin->w_p_spell = wo_spell_save;
+ return 0;
+ }
+
+ hlf_T attr = HLF_COUNT;
+ size_t len = 0;
+ size_t pos = 0;
+ int capcol = -1;
+ int no_res = 0;
+ const char * result;
+
+ lua_createtable(lstate, 0, 0);
+
+ while (*str != NUL) {
+ attr = HLF_COUNT;
+ len = spell_check(curwin, (char_u *)str, &attr, &capcol, false);
+ assert(len <= INT_MAX);
+
+ if (attr != HLF_COUNT) {
+ lua_createtable(lstate, 3, 0);
+
+ lua_pushlstring(lstate, str, len);
+ lua_rawseti(lstate, -2, 1);
+
+ result = attr == HLF_SPB ? "bad" :
+ attr == HLF_SPR ? "rare" :
+ attr == HLF_SPL ? "local" :
+ attr == HLF_SPC ? "caps" :
+ NULL;
+
+ assert(result != NULL);
+
+ lua_pushstring(lstate, result);
+ lua_rawseti(lstate, -2, 2);
+
+ // +1 for 1-indexing
+ lua_pushinteger(lstate, (long)pos + 1);
+ lua_rawseti(lstate, -2, 3);
+
+ lua_rawseti(lstate, -2, ++no_res);
+ }
+
+ str += len;
+ pos += len;
+ capcol -= (int)len;
+ }
+
+ // Restore 'spell'
+ curwin->w_p_spell = wo_spell_save;
+ return 1;
+}
+
+static const luaL_Reg spell_functions[] = {
+ { "check", nlua_spell_check },
+ { NULL , NULL }
+};
+
+int luaopen_spell(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_register(L, NULL, spell_functions);
+ return 1;
+}
diff --git a/src/nvim/lua/spell.h b/src/nvim/lua/spell.h
new file mode 100644
index 0000000000..8f798a5191
--- /dev/null
+++ b/src/nvim/lua/spell.h
@@ -0,0 +1,12 @@
+#ifndef NVIM_LUA_SPELL_H
+#define NVIM_LUA_SPELL_H
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <lualib.h>
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "lua/spell.h.generated.h"
+#endif
+
+#endif // NVIM_LUA_SPELL_H
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 0d6789317c..18a579ed0f 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -30,6 +30,7 @@
#include "nvim/lua/stdlib.h"
#include "nvim/lua/treesitter.h"
#include "nvim/lua/xdiff.h"
+#include "nvim/lua/spell.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memline.h"
@@ -518,6 +519,10 @@ void nlua_state_add_stdlib(lua_State *const lstate)
lua_pushcfunction(lstate, &nlua_xdl_diff);
lua_setfield(lstate, -2, "diff");
+ // vim.spell
+ luaopen_spell(lstate);
+ lua_setfield(lstate, -2, "spell");
+
lua_cjson_new(lstate);
lua_setfield(lstate, -2, "json");
}
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index c1a1e7f162..f9b15d242a 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -424,7 +424,7 @@ end
--- Without a runtime, writes to :Messages
---@see :help nvim_notify
---@param msg string Content of the notification to show to the user
----@param log_level number|nil enum from vim.log.levels
+---@param log_level number|nil enum from |vim.log.levels|
---@param opts table|nil additional options (timeout, etc)
function vim.notify(msg, log_level, opts) -- luacheck: no unused
if log_level == vim.log.levels.ERROR then
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index ac4d52c392..0db9d69a7e 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -361,7 +361,7 @@ static int add_menu_path(const char_u *const menu_path, vimmenu_T *menuarg,
goto erret;
}
- // Not already there, so lets add it
+ // Not already there, so let's add it
menu = xcalloc(1, sizeof(vimmenu_T));
menu->modes = modes;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index befca8c76b..39b023132e 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1269,7 +1269,7 @@ static void hit_return_msg(void)
{
int save_p_more = p_more;
- p_more = FALSE; // don't want see this message when scrolling back
+ p_more = false; // don't want to see this message when scrolling back
if (msg_didout) { // start on a new line
msg_putchar('\n');
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 15ba6645f5..67ec19903f 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -909,7 +909,7 @@ void curs_columns(win_T *wp, int may_scroll)
}
wp->w_skipcol = n * width;
} else if (extra == 1) {
- // less then 'scrolloff' lines above, decrease skipcol
+ // less than 'scrolloff' lines above, decrease skipcol
assert(so <= INT_MAX);
extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol
+ width - 1) / width;
@@ -920,7 +920,7 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_skipcol -= extra * width;
}
} else if (extra == 2) {
- // less then 'scrolloff' lines below, increase skipcol
+ // less than 'scrolloff' lines below, increase skipcol
endcol = (n - wp->w_height_inner + 1) * width;
while (endcol > wp->w_virtcol) {
endcol -= width;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index c6f9c5f04f..6c2db1e9ac 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -4123,7 +4123,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in
* If first leader has 'f' flag, the lines can be joined only if the
* second line does not have a leader.
* If first leader has 'e' flag, the lines can never be joined.
- * If fist leader has 's' flag, the lines can only be joined if there is
+ * If first leader has 's' flag, the lines can only be joined if there is
* some text after it and the second line has the 'm' flag.
*/
if (leader1_flags != NULL) {
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 2ceb1bd992..659965b64c 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1345,7 +1345,7 @@ int do_set(char_u *arg, int opt_flags)
if (nextchar == '&') { // set to default val
newval = options[opt_idx].def_val;
- // expand environment variables and ~ (since the
+ // expand environment variables and ~ since the
// default value was already expanded, only
// required when an environment variable was set
// later
@@ -2991,7 +2991,7 @@ ambw_end:
}
} else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
// 'foldcolumn'
- if (check_opt_strings(*varp, p_fdc_values, false) != OK) {
+ if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_pt) {
@@ -3370,6 +3370,9 @@ static int int_cmp(const void *a, const void *b)
/// @return OK when the value is valid, FAIL otherwise
int check_signcolumn(char_u *val)
{
+ if (*val == NUL) {
+ return FAIL;
+ }
// check for basic match
if (check_opt_strings(val, p_scl_values, false) == OK) {
return OK;
@@ -3610,7 +3613,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
c2 = c3 = 0;
s = p + len + 1;
c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || utf_char2cells(c1) > 1) {
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
@@ -3618,12 +3621,12 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
return e_invarg;
}
c2 = get_encoded_char_adv(&s);
- if (c2 == 0 || utf_char2cells(c2) > 1) {
+ if (c2 == 0 || char2cells(c2) > 1) {
return e_invarg;
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
- if (c3 == 0 || utf_char2cells(c3) > 1) {
+ if (c3 == 0 || char2cells(c3) > 1) {
return e_invarg;
}
}
@@ -3657,7 +3660,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set)
multispace_len = 0;
while (*s != NUL && *s != ',') {
c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || utf_char2cells(c1) > 1) {
+ if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
}
multispace_len++;
@@ -4129,7 +4132,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
}
- // Arabic requires a utf-8 encoding, inform the user if its not
+ // Arabic requires a utf-8 encoding, inform the user if it's not
// set.
if (STRCMP(p_enc, "utf-8") != 0) {
static char *w_arabic = N_("W17: Arabic requires UTF-8, do ':set encoding=utf-8'");
@@ -5059,6 +5062,9 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
/// @param[in] number New value for the number or boolean option.
/// @param[in] string New value for string option.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
+/// If OPT_CLEAR is set, the value of the option
+/// is cleared (the exact semantics of this depend
+/// on the option).
///
/// @return NULL on success, error message on error.
char *set_option_value(const char *const name, const long number, const char *const string,
@@ -5084,7 +5090,7 @@ char *set_option_value(const char *const name, const long number, const char *co
}
if (flags & P_STRING) {
const char *s = string;
- if (s == NULL) {
+ if (s == NULL || opt_flags & OPT_CLEAR) {
s = "";
}
return set_string_option(opt_idx, s, opt_flags);
@@ -5106,10 +5112,23 @@ char *set_option_value(const char *const name, const long number, const char *co
return NULL; // do nothing as we hit an error
}
}
+ long numval = number;
+ if (opt_flags & OPT_CLEAR) {
+ if ((int *)varp == &curbuf->b_p_ar) {
+ numval = -1;
+ } else if ((long *)varp == &curbuf->b_p_ul) {
+ numval = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
+ numval = -1;
+ } else {
+ char *s = NULL;
+ (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
+ }
+ }
if (flags & P_NUM) {
- return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags);
+ return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
} else {
- return set_bool_option(opt_idx, varp, (int)number, opt_flags);
+ return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
}
}
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index 452494172f..f7dbaafeec 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -22,6 +22,7 @@ typedef enum {
OPT_ONECOLUMN = 64, ///< list options one per line
OPT_NO_REDRAW = 128, ///< ignore redraw flags on option
OPT_SKIPRTP = 256, ///< "skiprtp" in 'sessionoptions'
+ OPT_CLEAR = 512, ///< Clear local value of an option.
} OptionFlags;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 19cb33a354..09c3bf3800 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -743,6 +743,7 @@ EXTERN int p_write; // 'write'
EXTERN int p_wa; // 'writeany'
EXTERN int p_wb; // 'writebackup'
EXTERN long p_wd; // 'writedelay'
+EXTERN int p_cdh; // 'cdhome'
EXTERN int p_force_on; ///< options that cannot be turned off.
EXTERN int p_force_off; ///< options that cannot be turned on.
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 71208dfc68..28b4eb9fe2 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -275,6 +275,14 @@ return {
defaults={if_true="internal,keepascii"}
},
{
+ full_name='cdhome', abbreviation='cdh',
+ short_desc=N_(":cd without argument goes to the home directory"),
+ type='bool', scope={'global'},
+ secure=true,
+ varname='p_cdh',
+ defaults={if_true=false}
+ },
+ {
full_name='cdpath', abbreviation='cd',
short_desc=N_("list of directories searched with \":cd\""),
type='string', list='comma', scope={'global'},
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index a061f76f34..a572f747df 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -83,7 +83,7 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight)
return 1;
}
- // A folded lines is handled just like an empty line.
+ // Folded lines are handled just like an empty line.
if (lineFolded(wp, lnum)) {
return 1;
}
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 606c03f838..da2ada791f 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -386,7 +386,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
void pum_redraw(void)
{
int row = 0;
- int col;
+ int grid_col;
int attr_norm = win_hl_attr(curwin, HLF_PNI);
int attr_select = win_hl_attr(curwin, HLF_PSI);
int attr_scroll = win_hl_attr(curwin, HLF_PSB);
@@ -479,7 +479,7 @@ void pum_redraw(void)
// Display each entry, use two spaces for a Tab.
// Do this 3 times: For the main text, kind and extra info
- col = col_off;
+ grid_col = col_off;
totwidth = 0;
for (round = 1; round <= 3; ++round) {
@@ -537,24 +537,15 @@ void pum_redraw(void)
}
}
grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
- col - size + 1, attr);
+ grid_col - size + 1, attr);
xfree(rt_start);
xfree(st);
- col -= width;
+ grid_col -= width;
} else {
- int size = (int)STRLEN(st);
- int cells = (int)mb_string2cells(st);
-
- // only draw the text that fits
- while (size > 0 && col + cells > pum_width + pum_col) {
- size--;
- size -= utf_head_off(st, st + size);
- cells -= utf_ptr2cells(st + size);
- }
-
- grid_puts_len(&pum_grid, st, size, row, col, attr);
+ // use grid_puts_len() to truncate the text
+ grid_puts(&pum_grid, st, row, grid_col, attr);
xfree(st);
- col += width;
+ grid_col += width;
}
if (*p != TAB) {
@@ -563,12 +554,12 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (pum_rl) {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col - 1,
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1,
attr);
- col -= 2;
+ grid_col -= 2;
} else {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col, attr);
- col += 2;
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
+ grid_col += 2;
}
totwidth += 2;
// start text at next char
@@ -599,21 +590,21 @@ void pum_redraw(void)
if (pum_rl) {
grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
- col + 1, ' ', ' ', attr);
- col = col_off - pum_base_width - n + 1;
+ grid_col + 1, ' ', ' ', attr);
+ grid_col = col_off - pum_base_width - n + 1;
} else {
- grid_fill(&pum_grid, row, row + 1, col,
+ grid_fill(&pum_grid, row, row + 1, grid_col,
col_off + pum_base_width + n, ' ', ' ', attr);
- col = col_off + pum_base_width + n;
+ grid_col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (pum_rl) {
- grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, col + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, grid_col + 1,
' ', ' ', attr);
} else {
- grid_fill(&pum_grid, row, row + 1, col, col_off + pum_width, ' ', ' ',
+ grid_fill(&pum_grid, row, row + 1, grid_col, col_off + pum_width, ' ', ' ',
attr);
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 32d0ebe8eb..0196e05455 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3947,7 +3947,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
int len;
buf_T *errbuf;
- // If the 'quickfixtextfunc' function returned an non-empty custom string
+ // If the 'quickfixtextfunc' function returned a non-empty custom string
// for this entry, then use it.
if (qftf_str != NULL && *qftf_str != NUL) {
STRLCPY(IObuff, qftf_str, IOSIZE);
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index eac1b4596e..cafffc0319 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -2013,7 +2013,7 @@ static int nfa_regpiece(void)
// will emit NFA_STAR.
// Bail out if we can use the other engine, but only, when the
// pattern does not need the NFA engine like (e.g. [[:upper:]]\{2,\}
- // does not work with with characters > 8 bit with the BT engine)
+ // does not work with characters > 8 bit with the BT engine)
if ((nfa_re_flags & RE_AUTO)
&& (maxval > 500 || maxval > minval + 200)
&& (maxval != MAX_LIMIT && minval < 200)
@@ -2565,20 +2565,20 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
ga_concat(indent, (char_u *)"| ");
else
ga_concat(indent, (char_u *)" ");
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
nfa_print_state2(debugf, state->out, indent);
/* replace last part of indent for state->out1 */
indent->ga_len -= 3;
ga_concat(indent, (char_u *)" ");
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
nfa_print_state2(debugf, state->out1, indent);
/* shrink indent */
indent->ga_len -= 3;
- ga_append(indent, '\0');
+ ga_append(indent, NUL);
}
/*
@@ -4367,7 +4367,7 @@ static regsubs_T *addstate_here(
// First add the state(s) at the end, so that we know how many there are.
// Pass the listidx as offset (avoids adding another argument to
- // addstate().
+ // addstate()).
regsubs_T *r = addstate(l, state, subs, pim, -listidx - ADDSTATE_HERE_OFFSET);
if (r == NULL) {
return NULL;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index a938a3b062..b1ca8c5805 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3771,7 +3771,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// Make sure, the highlighting for the tab char will be
// correctly set further below (effectively reverts the
- // FIX_FOR_BOGSUCOLS macro.
+ // FIX_FOR_BOGSUCOLS macro).
if (n_extra == tab_len + vc_saved && wp->w_p_list
&& wp->w_p_lcs_chars.tab1) {
tab_len += vc_saved;
@@ -4294,7 +4294,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
// Store the character.
//
if (wp->w_p_rl && utf_char2cells(mb_c) > 1) {
- // A double-wide character is: put first halve in left cell.
+ // A double-wide character is: put first half in left cell.
off--;
col--;
}
@@ -4637,8 +4637,8 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
{
return wp->w_p_cul
- && lnum == wp->w_cursor.lnum
- && (wp->w_p_culopt_flags & CULOPT_NBR);
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR);
}
// Get information needed to display the sign in line 'lnum' in window 'wp'.
@@ -4834,9 +4834,9 @@ static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol,
end_dirty = col + char_cells;
// When writing a single-width character over a double-width
// character and at the end of the redrawn text, need to clear out
- // the right halve of the old character.
- // Also required when writing the right halve of a double-width
- // char over the left halve of an existing one
+ // the right half of the old character.
+ // Also required when writing the right half of a double-width
+ // char over the left half of an existing one
if (col + char_cells == endcol
&& ((char_cells == 1
&& grid_off2cells(grid, off_to, max_off_to) > 1)
@@ -5887,8 +5887,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
}
off = grid->line_offset[row] + col;
- /* When drawing over the right halve of a double-wide char clear out the
- * left halve. Only needed in a terminal. */
+ // When drawing over the right half of a double-wide char clear out the
+ // left half. Only needed in a terminal.
if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
// redraw the previous cell, make it empty
put_dirty_first = -1;
@@ -5933,6 +5933,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
// Only 1 cell left, but character requires 2 cells:
// display a '>' in the last column to avoid wrapping. */
c = '>';
+ u8c = '>';
+ u8cc[0] = 0;
mbyte_cells = 1;
}
@@ -5948,9 +5950,9 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
if (need_redraw) {
// When at the end of the text and overwriting a two-cell
// character with a one-cell character, need to clear the next
- // cell. Also when overwriting the left halve of a two-cell char
- // with the right halve of a two-cell char. Do this only once
- // (utf8_off2cells() may return 2 on the right halve).
+ // cell. Also when overwriting the left half of a two-cell char
+ // with the right half of a two-cell char. Do this only once
+ // (utf8_off2cells() may return 2 on the right half).
if (clear_next_cell) {
clear_next_cell = false;
} else if ((len < 0 ? ptr[mbyte_blen] == NUL
@@ -5963,6 +5965,13 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
clear_next_cell = true;
}
+ // When at the start of the text and overwriting the right half of a
+ // two-cell character in the same grid, truncate that into a '>'.
+ if (ptr == text && col > 0 && grid->chars[off][0] == 0) {
+ grid->chars[off - 1][0] = '>';
+ grid->chars[off - 1][1] = 0;
+ }
+
schar_copy(grid->chars[off], buf);
grid->attrs[off] = attr;
if (mbyte_cells == 2) {
@@ -6342,9 +6351,9 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
}
for (int row = start_row; row < end_row; row++) {
- // When drawing over the right halve of a double-wide char clear
- // out the left halve. When drawing over the left halve of a
- // double wide-char clear out the right halve. Only needed in a
+ // When drawing over the right half of a double-wide char clear
+ // out the left half. When drawing over the left half of a
+ // double wide-char clear out the right half. Only needed in a
// terminal.
if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0);
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 32be714184..a308df07d1 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -1740,7 +1740,7 @@ char_u *get_sign_name(expand_T *xp, int idx)
case EXP_SUBCMD:
return (char_u *)cmds[idx];
case EXP_DEFINE: {
- char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=",
+ char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=",
NULL };
return (char_u *)define_arg[idx];
}
@@ -1849,6 +1849,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
case SIGNCMD_DEFINE:
if (STRNCMP(last, "texthl", 6) == 0
|| STRNCMP(last, "linehl", 6) == 0
+ || STRNCMP(last, "culhl", 5) == 0
|| STRNCMP(last, "numhl", 5) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
} else if (STRNCMP(last, "icon", 4) == 0) {
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index bd31e98faa..9429a06e92 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -219,7 +219,7 @@ typedef struct {
#define SCORE_THRES3 100 // word count threshold for COMMON3
// When trying changed soundfold words it becomes slow when trying more than
-// two changes. With less then two changes it's slightly faster but we miss a
+// two changes. With less than two changes it's slightly faster but we miss a
// few good suggestions. In rare cases we need to try three of four changes.
#define SCORE_SFMAX1 200 // maximum score for first try
#define SCORE_SFMAX2 300 // maximum score for second try
@@ -1628,7 +1628,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
}
// For spell checking: concatenate the start of the following line "line" into
-// "buf", blanking-out special characters. Copy less then "maxlen" bytes.
+// "buf", blanking-out special characters. Copy less than "maxlen" bytes.
// Keep the blanks at the start of the next line, this is used in win_line()
// to skip those bytes if the word was OK.
void spell_cat_line(char_u *buf, char_u *line, int maxlen)
@@ -6106,7 +6106,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff)
&& ws[0] != NUL; ++n) {
// Quickly skip entries that don't match the word. Most
- // entries are less then three chars, optimize for that.
+ // entries are less than three chars, optimize for that.
if (c != ws[0]) {
continue;
}
@@ -7057,7 +7057,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
arridx[depth] = idxs[n];
curi[depth] = 1;
- // Check if this characters matches with the pattern.
+ // Check if this character matches with the pattern.
// If not skip the whole tree below it.
// Always ignore case here, dump_word() will check
// proper case later. This isn't exactly right when
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 42bb3c61a5..f6b95f37b1 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -4395,7 +4395,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
//
// The table with character flags and the table for case folding.
// This makes sure the same characters are recognized as word characters
- // when generating an when using a spell file.
+ // when generating and when using a spell file.
// Skip this for ASCII, the table may conflict with the one used for
// 'encoding'.
// Also skip this for an .add.spl file, the main spell file must contain
@@ -5122,7 +5122,7 @@ static int sug_filltable(spellinfo_T *spin, wordnode_T *node, int startwordnr, g
wordnr++;
// Remove extra NUL entries, we no longer need them. We don't
- // bother freeing the nodes, the won't be reused anyway.
+ // bother freeing the nodes, they won't be reused anyway.
while (p->wn_sibling != NULL && p->wn_sibling->wn_byte == NUL) {
p->wn_sibling = p->wn_sibling->wn_sibling;
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index d97c24dcf7..a7b52d8238 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -135,7 +135,6 @@ struct terminal {
int row, col;
bool visible;
} cursor;
- int pressed_button; // which mouse button is pressed
bool pending_resize; // pending width/height
bool color_set[16];
@@ -1209,21 +1208,12 @@ static VTermKey convert_key(int key, VTermModifier *statep)
}
}
-static void mouse_action(Terminal *term, int button, int row, int col, bool drag, VTermModifier mod)
+static void mouse_action(Terminal *term, int button, int row, int col, bool pressed,
+ VTermModifier mod)
{
- if (term->pressed_button && (term->pressed_button != button || !drag)) {
- // release the previous button
- vterm_mouse_button(term->vt, term->pressed_button, 0, mod);
- term->pressed_button = 0;
- }
-
- // move the mouse
vterm_mouse_move(term->vt, row, col, mod);
-
- if (!term->pressed_button) {
- // press the button if not already pressed
- vterm_mouse_button(term->vt, button, 1, mod);
- term->pressed_button = button;
+ if (button) {
+ vterm_mouse_button(term->vt, button, pressed, mod);
}
}
@@ -1242,32 +1232,35 @@ static bool send_mouse_event(Terminal *term, int c)
// event in the terminal window and mouse events was enabled by the
// program. translate and forward the event
int button;
- bool drag = false;
+ bool pressed = false;
switch (c) {
case K_LEFTDRAG:
- drag = true; FALLTHROUGH;
case K_LEFTMOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_LEFTRELEASE:
button = 1; break;
case K_MOUSEMOVE:
- drag = true; button = 0; break;
+ button = 0; break;
case K_MIDDLEDRAG:
- drag = true; FALLTHROUGH;
case K_MIDDLEMOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_MIDDLERELEASE:
button = 2; break;
case K_RIGHTDRAG:
- drag = true; FALLTHROUGH;
case K_RIGHTMOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_RIGHTRELEASE:
button = 3; break;
case K_MOUSEDOWN:
- button = 4; break;
+ pressed = true; button = 4; break;
case K_MOUSEUP:
- button = 5; break;
+ pressed = true; button = 5; break;
default:
return false;
}
- mouse_action(term, button, row, col - offset, drag, 0);
+ mouse_action(term, button, row, col - offset, pressed, 0);
size_t len = vterm_output_read(term->vt, term->textbuf,
sizeof(term->textbuf));
terminal_send(term, term->textbuf, len);
@@ -1295,6 +1288,12 @@ static bool send_mouse_event(Terminal *term, int c)
return mouse_win == curwin;
}
+ // ignore left release action if it was not proccessed above
+ // to prevent leaving Terminal mode after entering to it using a mouse
+ if (c == K_LEFTRELEASE && mouse_win->w_buffer->terminal == term) {
+ return false;
+ }
+
end:
ins_char_typebuf(c);
return true;
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index ab047fd2a8..b0d872e392 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -64,6 +64,9 @@ if has('reltime')
let s:start_time = reltime()
endif
+" Always use forward slashes.
+set shellslash
+
" Common with all tests on all systems.
source setup.vim
@@ -104,9 +107,6 @@ lang mess C
" Nvim: append runtime from build dir, which contains the generated doc/tags.
let &runtimepath .= ','.expand($BUILD_DIR).'/runtime/'
-" Always use forward slashes.
-set shellslash
-
let s:t_bold = &t_md
let s:t_normal = &t_me
if has('win32')
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index b3df8c63e6..fdae0697c3 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -13,7 +13,7 @@ set fillchars=vert:\|,fold:-
set laststatus=1
set listchars=eol:$
set joinspaces
-set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd
+set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd
set nrformats+=octal
set shortmess-=F
set sidescroll=0
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 4e1a24af61..45285b69a1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2380,95 +2380,7 @@ func Test_autocmd_was_using_freed_memory()
pclose
endfunc
-func Test_FileChangedShell_reload()
- if !has('unix')
- return
- endif
- augroup testreload
- au FileChangedShell Xchanged let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
- augroup END
- new Xchanged
- call setline(1, 'reload this')
- write
- " Need to wait until the timestamp would change by at least a second.
- sleep 2
- silent !echo 'extra line' >>Xchanged
- checktime
- call assert_equal('changed', g:reason)
- call assert_equal(2, line('$'))
- call assert_equal('extra line', getline(2))
-
- " Only triggers once
- let g:reason = ''
- checktime
- call assert_equal('', g:reason)
-
- " When deleted buffer is not reloaded
- silent !rm Xchanged
- let g:reason = ''
- checktime
- call assert_equal('deleted', g:reason)
- call assert_equal(2, line('$'))
- call assert_equal('extra line', getline(2))
-
- " When recreated buffer is reloaded
- call setline(1, 'buffer is changed')
- silent !echo 'new line' >>Xchanged
- let g:reason = ''
- checktime
- call assert_equal('conflict', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- " Only mode changed
- silent !chmod +x Xchanged
- let g:reason = ''
- checktime
- call assert_equal('mode', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- " Only time changed
- sleep 2
- silent !touch Xchanged
- let g:reason = ''
- checktime
- call assert_equal('time', g:reason)
- call assert_equal(1, line('$'))
- call assert_equal('new line', getline(1))
-
- if has('persistent_undo')
- " With an undo file the reload can be undone and a change before the
- " reload.
- set undofile
- call setline(2, 'before write')
- write
- call setline(2, 'after write')
- sleep 2
- silent !echo 'different line' >>Xchanged
- let g:reason = ''
- checktime
- call assert_equal('conflict', g:reason)
- call assert_equal(3, line('$'))
- call assert_equal('before write', getline(2))
- call assert_equal('different line', getline(3))
- " undo the reload
- undo
- call assert_equal(2, line('$'))
- call assert_equal('after write', getline(2))
- " undo the change before reload
- undo
- call assert_equal(2, line('$'))
- call assert_equal('before write', getline(2))
-
- set noundofile
- endif
-
-
- au! testreload
- bwipe!
- call delete('Xchanged')
-endfunc
+" FileChangedShell tested in test_filechanged.vim
func LogACmd()
call add(g:logged, line('$'))
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index 20758b0c0a..af42b3857d 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -346,4 +346,12 @@ func Test_blob_sort()
endif
endfunc
+" The following used to cause an out-of-bounds memory access
+func Test_blob2string()
+ let v = '0z' .. repeat('01010101.', 444)
+ let v ..= '01'
+ exe 'let b = ' .. v
+ call assert_equal(v, string(b))
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 49a5386337..1672b0e840 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -467,6 +467,43 @@ func Test_getcompletion()
call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc
+func Test_fullcommand()
+ let tests = {
+ \ '': '',
+ \ ':': '',
+ \ ':::': '',
+ \ ':::5': '',
+ \ 'not_a_cmd': '',
+ \ 'Check': '',
+ \ 'syntax': 'syntax',
+ \ ':syntax': 'syntax',
+ \ '::::syntax': 'syntax',
+ \ 'sy': 'syntax',
+ \ 'syn': 'syntax',
+ \ 'synt': 'syntax',
+ \ ':sy': 'syntax',
+ \ '::::sy': 'syntax',
+ \ 'match': 'match',
+ \ '2match': 'match',
+ \ '3match': 'match',
+ \ 'aboveleft': 'aboveleft',
+ \ 'abo': 'aboveleft',
+ \ 's': 'substitute',
+ \ '5s': 'substitute',
+ \ ':5s': 'substitute',
+ \ "'<,'>s": 'substitute',
+ \ ":'<,'>s": 'substitute',
+ \ 'CheckUni': 'CheckUnix',
+ \ 'CheckUnix': 'CheckUnix',
+ \ }
+
+ for [in, want] in items(tests)
+ call assert_equal(want, fullcommand(in))
+ endfor
+
+ call assert_equal('syntax', 'syn'->fullcommand())
+endfunc
+
func Test_shellcmd_completion()
let save_path = $PATH
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 12327f34d6..c2a9683f7c 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -263,6 +263,31 @@ func Test_display_scroll_at_topline()
call StopVimInTerminal(buf)
endfunc
+" Test for 'eob' (EndOfBuffer) item in 'fillchars'
+func Test_eob_fillchars()
+ " default value (skipped)
+ " call assert_match('eob:\~', &fillchars)
+ " invalid values
+ call assert_fails(':set fillchars=eob:', 'E474:')
+ call assert_fails(':set fillchars=eob:xy', 'E474:')
+ call assert_fails(':set fillchars=eob:\255', 'E474:')
+ call assert_fails(':set fillchars=eob:<ff>', 'E474:')
+ call assert_fails(":set fillchars=eob:\x01", 'E474:')
+ call assert_fails(':set fillchars=eob:\\x01', 'E474:')
+ " default is ~
+ new
+ redraw
+ call assert_equal('~', Screenline(2))
+ set fillchars=eob:+
+ redraw
+ call assert_equal('+', Screenline(2))
+ set fillchars=eob:\
+ redraw
+ call assert_equal(' ', nr2char(screenchar(2, 1)))
+ set fillchars&
+ close
+endfunc
+
func Test_display_linebreak_breakat()
new
vert resize 25
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
new file mode 100644
index 0000000000..b95cd5faf8
--- /dev/null
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -0,0 +1,149 @@
+" Tests for when a file was changed outside of Vim.
+
+func Test_FileChangedShell_reload()
+ if !has('unix')
+ return
+ endif
+ augroup testreload
+ au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload'
+ augroup END
+ new Xchanged_r
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_r
+ checktime
+ call assert_equal('changed', g:reason)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+
+ " Only triggers once
+ let g:reason = ''
+ checktime
+ call assert_equal('', g:reason)
+
+ " When deleted buffer is not reloaded
+ silent !rm Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('deleted', g:reason)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+
+ " When recreated buffer is reloaded
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >>Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('conflict', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed
+ silent !chmod +x Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('mode', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed
+ sleep 2
+ silent !touch Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('time', g:reason)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ if has('persistent_undo')
+ " With an undo file the reload can be undone and a change before the
+ " reload.
+ set undofile
+ call setline(2, 'before write')
+ write
+ call setline(2, 'after write')
+ sleep 2
+ silent !echo 'different line' >>Xchanged_r
+ let g:reason = ''
+ checktime
+ call assert_equal('conflict', g:reason)
+ call assert_equal(3, line('$'))
+ call assert_equal('before write', getline(2))
+ call assert_equal('different line', getline(3))
+ " undo the reload
+ undo
+ call assert_equal(2, line('$'))
+ call assert_equal('after write', getline(2))
+ " undo the change before reload
+ undo
+ call assert_equal(2, line('$'))
+ call assert_equal('before write', getline(2))
+
+ set noundofile
+ endif
+
+ au! testreload
+ bwipe!
+ call delete(undofile('Xchanged_r'))
+ call delete('Xchanged_r')
+endfunc
+
+func Test_file_changed_dialog()
+ throw 'skipped: TODO: '
+ if !has('unix') || has('gui_running')
+ return
+ endif
+ au! FileChangedShell
+
+ new Xchanged_d
+ call setline(1, 'reload this')
+ write
+ " Need to wait until the timestamp would change by at least a second.
+ sleep 2
+ silent !echo 'extra line' >>Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W11:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('reload this', getline(1))
+ call assert_equal('extra line', getline(2))
+
+ " delete buffer, only shows an error, no prompt
+ silent !rm Xchanged_d
+ checktime
+ call assert_match('E211:', v:warningmsg)
+ call assert_equal(2, line('$'))
+ call assert_equal('extra line', getline(2))
+ let v:warningmsg = 'empty'
+
+ " change buffer, recreate the file and reload
+ call setline(1, 'buffer is changed')
+ silent !echo 'new line' >Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W12:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only mode changed, reload
+ silent !chmod +x Xchanged_d
+ call feedkeys('L', 'L')
+ checktime
+ call assert_match('W16:', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ " Only time changed, no prompt
+ sleep 2
+ silent !touch Xchanged_d
+ let v:warningmsg = ''
+ checktime
+ call assert_equal('', v:warningmsg)
+ call assert_equal(1, line('$'))
+ call assert_equal('new line', getline(1))
+
+ bwipe!
+ call delete('Xchanged_d')
+endfunc
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
index 465613f1cf..81127ea59a 100644
--- a/src/nvim/testdir/test_fileformat.vim
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -1,5 +1,4 @@
" Test behavior of fileformat after bwipeout of last buffer
-
func Test_fileformat_after_bw()
bwipeout
set fileformat&
@@ -32,6 +31,251 @@ func Test_fileformat_autocommand()
bw!
endfunc
+" Convert the contents of a file into a literal string
+func s:file2str(fname)
+ let b = readfile(a:fname, 'B')
+ let s = ''
+ for c in b
+ let s .= nr2char(c)
+ endfor
+ return s
+endfunc
+
+" Concatenate the contents of files 'f1' and 'f2' and create 'destfile'
+func s:concat_files(f1, f2, destfile)
+ let b1 = readfile(a:f1, 'B')
+ let b2 = readfile(a:f2, 'B')
+ let b3 = b1 + b2
+ call writefile(b3, a:destfile)
+endfun
+
+" Test for a lot of variations of the 'fileformats' option
+func Test_fileformats()
+ " create three test files, one in each format
+ call writefile(['unix', 'unix'], 'XXUnix')
+ call writefile(["dos\r", "dos\r"], 'XXDos')
+ call writefile(["mac\rmac\r"], 'XXMac', 'b')
+ " create a file with no End Of Line
+ call writefile(["noeol"], 'XXEol', 'b')
+ " create mixed format files
+ call s:concat_files('XXUnix', 'XXDos', 'XXUxDs')
+ call s:concat_files('XXUnix', 'XXMac', 'XXUxMac')
+ call s:concat_files('XXDos', 'XXMac', 'XXDosMac')
+ call s:concat_files('XXMac', 'XXEol', 'XXMacEol')
+ call s:concat_files('XXUxDs', 'XXMac', 'XXUxDsMc')
+
+ new
+
+ " Test 1: try reading and writing with 'fileformats' empty
+ set fileformats=
+
+ " try with 'fileformat' set to 'unix'
+ set fileformat=unix
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r\n", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " try with 'fileformat' set to 'dos'
+ set fileformat=dos
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\n", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r\r\n", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " try with 'fileformat' set to 'mac'
+ set fileformat=mac
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n\r", s:file2str('Xtest'))
+ e! XXDos
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\n\r", s:file2str('Xtest'))
+ e! XXMac
+ w! Xtest
+ call assert_equal("mac\rmac\r", s:file2str('Xtest'))
+ bwipe XXUnix XXDos XXMac
+
+ " Test 2: try reading and writing with 'fileformats' set to one format
+
+ " try with 'fileformats' set to 'unix'
+ set fileformats=unix
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " try with 'fileformats' set to 'dos'
+ set fileformats=dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " try with 'fileformats' set to 'mac'
+ set fileformats=mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ " Test 3: try reading and writing with 'fileformats' set to two formats
+
+ " try with 'fileformats' set to 'unix,dos'
+ set fileformats=unix,dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXUxMac
+ w! Xtest
+ call assert_equal("unix\nunix\nmac\rmac\r\n", s:file2str('Xtest'))
+ bwipe XXUxMac
+
+ e! XXDosMac
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\nmac\rmac\r\r\n", s:file2str('Xtest'))
+ bwipe XXDosMac
+
+ " try with 'fileformats' set to 'unix,mac'
+ set fileformats=unix,mac
+ e! XXUxDs
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\n", s:file2str('Xtest'))
+ bwipe XXUxDs
+
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXDosMac
+ w! Xtest
+ call assert_equal("dos\r\ndos\r\nmac\rmac\r", s:file2str('Xtest'))
+ bwipe XXDosMac
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("unix,mac:unix\nnoeol\n", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ " try with 'fileformats' set to 'dos,mac'
+ set fileformats=dos,mac
+ e! XXUxDs
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\n", s:file2str('Xtest'))
+ bwipe XXUxDs
+
+ e! XXUxMac
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("dos,mac:dos\r\nunix\r\nunix\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe! XXUxMac
+
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\r\nunix\r\ndos\r\ndos\r\nmac\rmac\r\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXMacEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("dos,mac:mac\rmac\rmac\rnoeol\r", s:file2str('Xtest'))
+ bwipe! XXMacEol
+
+ " Test 4: try reading and writing with 'fileformats' set to three formats
+ set fileformats=unix,dos,mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("unix,dos,mac:unix\nnoeol\n", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ set fileformats=mac,dos,unix
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r\n",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXEol
+ exe "normal ggO\<C-R>=&ffs\<CR>:\<C-R>=&ff\<CR>"
+ w! Xtest
+ call assert_equal("mac,dos,unix:mac\rnoeol\r", s:file2str('Xtest'))
+ bwipe! XXEol
+
+ " Test 5: try with 'binary' set
+ set fileformats=mac,unix,dos
+ set binary
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ set fileformats=mac
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ set fileformats=dos
+ e! XXUxDsMc
+ w! Xtest
+ call assert_equal("unix\nunix\ndos\r\ndos\r\nmac\rmac\r",
+ \ s:file2str('Xtest'))
+ bwipe XXUxDsMc
+
+ e! XXUnix
+ w! Xtest
+ call assert_equal("unix\nunix\n", s:file2str('Xtest'))
+ bwipe! XXUnix
+
+ set nobinary ff& ffs&
+
+ " cleanup
+ only
+ %bwipe!
+ call delete('XXUnix')
+ call delete('XXDos')
+ call delete('XXMac')
+ call delete('XXEol')
+ call delete('XXUxDs')
+ call delete('XXUxMac')
+ call delete('XXDosMac')
+ call delete('XXMacEol')
+ call delete('XXUxDsMc')
+ call delete('Xtest')
+endfunc
+
" Test for changing the fileformat using ++read
func Test_fileformat_plusplus_read()
new
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index dbe0cd8388..1ffa1f86dc 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -146,7 +146,7 @@ let s:filename_checks = {
\ 'diff': ['file.diff', 'file.rej'],
\ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS', 'any/etc/DIR_COLORS'],
\ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'],
- \ 'dockerfile': ['Containerfile', 'Dockerfile', 'file.Dockerfile'],
+ \ 'dockerfile': ['Containerfile', 'Dockerfile', 'file.Dockerfile', 'Dockerfile.debian', 'Containerfile.something'],
\ 'dosbatch': ['file.bat', 'file.sys'],
\ 'dosini': ['.editorconfig', '/etc/pacman.conf', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/pacman.conf', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'],
\ 'dot': ['file.dot', 'file.gv'],
@@ -204,6 +204,7 @@ let s:filename_checks = {
\ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
\ 'gnuplot': ['file.gpi'],
\ 'go': ['file.go'],
+ \ 'gomod': ['go.mod'],
\ 'gp': ['file.gp', '.gprc'],
\ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'],
\ 'grads': ['file.gs'],
@@ -262,7 +263,7 @@ let s:filename_checks = {
\ 'jgraph': ['file.jgr'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'],
- \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'],
\ 'jsonc': ['file.jsonc'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index f4ee539803..0bcbd9c4a5 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -331,7 +331,7 @@ func Test_listchars_invalid()
call assert_fails('set listchars=space:xx', 'E474:')
call assert_fails('set listchars=tab:xxxx', 'E474:')
- " Has non-single width character
+ " Has double-width character
call assert_fails('set listchars=space:·', 'E474:')
call assert_fails('set listchars=tab:·x', 'E474:')
call assert_fails('set listchars=tab:x·', 'E474:')
@@ -339,6 +339,20 @@ func Test_listchars_invalid()
call assert_fails('set listchars=multispace:·', 'E474:')
call assert_fails('set listchars=multispace:xxx·', 'E474:')
+ " Has control character
+ call assert_fails("set listchars=space:\x01", 'E474:')
+ call assert_fails("set listchars=tab:\x01x", 'E474:')
+ call assert_fails("set listchars=tab:x\x01", 'E474:')
+ call assert_fails("set listchars=tab:xx\x01", 'E474:')
+ call assert_fails("set listchars=multispace:\x01", 'E474:')
+ call assert_fails("set listchars=multispace:xxx\x01", 'E474:')
+ call assert_fails('set listchars=space:\\x01', 'E474:')
+ call assert_fails('set listchars=tab:\\x01x', 'E474:')
+ call assert_fails('set listchars=tab:x\\x01', 'E474:')
+ call assert_fails('set listchars=tab:xx\\x01', 'E474:')
+ call assert_fails('set listchars=multispace:\\x01', 'E474:')
+ call assert_fails('set listchars=multispace:xxx\\x01', 'E474:')
+
enew!
set ambiwidth& listchars& ff&
endfunction
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 2140fe21ea..e0286548d9 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -108,3 +108,11 @@ func Test_echospace()
set ruler& showcmd&
endfunc
+
+" this was missing a terminating NUL
+func Test_echo_string_partial()
+ function CountSpaces()
+ endfunction
+ call assert_equal("function('CountSpaces', [{'ccccccccccc': ['ab', 'cd'], 'aaaaaaaaaaa': v:false, 'bbbbbbbbbbbb': ''}])", string(function('CountSpaces', [#{aaaaaaaaaaa: v:false, bbbbbbbbbbbb: '', ccccccccccc: ['ab', 'cd']}])))
+endfunc
+
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 7d9cada074..5946732937 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -732,4 +732,25 @@ func Test_opt_reset_scroll()
call delete('Xscroll')
endfunc
+" Test for the 'cdhome' option
+func Test_opt_cdhome()
+ if has('unix') || has('vms')
+ throw 'Skipped: only works on non-Unix'
+ endif
+
+ set cdhome&
+ call assert_equal(0, &cdhome)
+ set cdhome
+
+ " This paragraph is copied from Test_cd_no_arg().
+ let path = getcwd()
+ cd
+ call assert_equal($HOME, getcwd())
+ call assert_notequal(path, getcwd())
+ exe 'cd ' .. fnameescape(path)
+ call assert_equal(path, getcwd())
+
+ set cdhome&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim
index f42b177c50..440717eaa8 100644
--- a/src/nvim/testdir/test_put.vim
+++ b/src/nvim/testdir/test_put.vim
@@ -113,14 +113,14 @@ func Test_put_p_indent_visual()
endfunc
func Test_multibyte_op_end_mark()
- new
- call setline(1, 'тест')
- normal viwdp
- call assert_equal([0, 1, 7, 0], getpos("'>"))
- call assert_equal([0, 1, 7, 0], getpos("']"))
-
- normal Vyp
- call assert_equal([0, 1, 2147483647, 0], getpos("'>"))
- call assert_equal([0, 2, 7, 0], getpos("']"))
- bwipe!
- endfunc
+ new
+ call setline(1, 'тест')
+ normal viwdp
+ call assert_equal([0, 1, 7, 0], getpos("'>"))
+ call assert_equal([0, 1, 7, 0], getpos("']"))
+
+ normal Vyp
+ call assert_equal([0, 1, 2147483647, 0], getpos("'>"))
+ call assert_equal([0, 2, 7, 0], getpos("']"))
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index 799e6cb57b..ff9ba3d8ed 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -218,15 +218,13 @@ func Test_sign_completion()
call assert_equal('"sign define jump list place undefine unplace', @:)
call feedkeys(":sign define Sign \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign icon= linehl= numhl= text= texthl=', @:)
+ call assert_equal('"sign define Sign culhl= icon= linehl= numhl= text= texthl=', @:)
- call feedkeys(":sign define Sign linehl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' .
- \ 'SpellLocal SpellRare', @:)
-
- call feedkeys(":sign define Sign texthl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign texthl=SpellBad SpellCap ' .
- \ 'SpellLocal SpellRare', @:)
+ for hl in ['culhl', 'linehl', 'numhl', 'texthl']
+ call feedkeys(":sign define Sign "..hl.."=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"sign define Sign '..hl..'=SpellBad SpellCap ' .
+ \ 'SpellLocal SpellRare', @:)
+ endfor
call writefile(repeat(["Sun is shining"], 30), "XsignOne")
call writefile(repeat(["Sky is blue"], 30), "XsignTwo")
@@ -417,20 +415,21 @@ func Test_sign_funcs()
" Tests for sign_define()
let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error',
- \ 'culhl': 'Visual'}
+ \ 'culhl': 'Visual', 'numhl': 'Number'}
call assert_equal(0, "sign1"->sign_define(attr))
- call assert_equal([{'name' : 'sign1', 'texthl' : 'Error',
- \ 'linehl' : 'Search', 'culhl': 'Visual', 'text' : '=>'}],
+ call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl': 'Search',
+ \ 'culhl': 'Visual', 'numhl': 'Number', 'text' : '=>'}],
\ sign_getdefined())
" Define a new sign without attributes and then update it
call sign_define("sign2")
let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange',
- \ 'culhl': 'DiffDelete', 'icon' : 'sign2.ico'}
+ \ 'culhl': 'DiffDelete', 'numhl': 'Number', 'icon' : 'sign2.ico'}
call Sign_define_ignore_error("sign2", attr)
call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange',
\ 'linehl' : 'DiffAdd', 'culhl' : 'DiffDelete', 'text' : '!!',
- \ 'icon' : 'sign2.ico'}], "sign2"->sign_getdefined())
+ \ 'numhl': 'Number', 'icon' : 'sign2.ico'}],
+ \ "sign2"->sign_getdefined())
" Test for a sign name with digits
call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'}))
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index dbabdcf427..8344598486 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -144,7 +144,6 @@ endfun
" Test Virtual replace mode.
func Test_virtual_replace()
- throw 'skipped: TODO: '
if exists('&t_kD')
let save_t_kD = &t_kD
endif
@@ -166,7 +165,6 @@ func Test_virtual_replace()
\ ], getline(1, 6))
normal G
mark a
- inoremap <C-D> <Del>
exe "normal o0\<C-D>\nabcdefghi\njk\tlmn\n opq\trst\n\<C-D>uvwxyz\n"
exe "normal 'ajgR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" . repeat("\<BS>", 29)
call assert_equal([' 1',
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index e7a60aca49..58061f020d 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1877,7 +1877,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col
"\x1b[?c");
} else if (konsolev > 0 && konsolev < 180770) {
// Konsole before version 18.07.70: set up a nonce profile. This has
- // side-effects on temporary font resizing. #6798
+ // side effects on temporary font resizing. #6798
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
TMUX_WRAP(tmux,
"\x1b]50;CursorShape=%?"
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 2f8ddd1e88..6e0e9922a6 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -143,6 +143,7 @@ enum {
EXPAND_COMPILER,
EXPAND_USER_DEFINED,
EXPAND_USER_LIST,
+ EXPAND_USER_LUA,
EXPAND_SHELLCMD,
EXPAND_CSCOPE,
EXPAND_SIGN,
diff --git a/src/nvim/window.c b/src/nvim/window.c
index c711f462d1..1e747a67e9 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1591,7 +1591,7 @@ int make_windows(int count, bool vertical)
int todo;
if (vertical) {
- // Each windows needs at least 'winminwidth' lines and a separator
+ // Each window needs at least 'winminwidth' lines and a separator
// column.
maxcount = (curwin->w_width + curwin->w_vsep_width
- (p_wiw - p_wmw)) / (p_wmw + 1);
@@ -2119,7 +2119,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
m = frame_minheight(topfr, next_curwin);
room = height - m;
if (room < 0) {
- // The room is less then 'winheight', use all space for the
+ // The room is less than 'winheight', use all space for the
// current window.
next_curwin_size = p_wh + room;
room = 0;
diff --git a/test/README.md b/test/README.md
index c6e173ead2..cc630cb8bf 100644
--- a/test/README.md
+++ b/test/README.md
@@ -38,7 +38,7 @@ Layout
- `/test/functional` : functional tests
- `/test/unit` : unit tests
- `/test/config` : contains `*.in` files which are transformed into `*.lua`
- files using `configure_file` CMake command: this is for acessing CMake
+ files using `configure_file` CMake command: this is for accessing CMake
variables in lua tests.
- `/test/includes` : include-files for use by luajit `ffi.cdef` C definitions
parser: normally used to make macros not accessible via this mechanism
@@ -197,7 +197,7 @@ Guidelines
(success + fail + error + pending) is the same in all environments.
- *Note:* `pending()` is ignored if it is missing an argument, unless it is
[contained in an `it()` block](https://github.com/neovim/neovim/blob/d21690a66e7eb5ebef18046c7a79ef898966d786/test/functional/ex_cmds/grep_spec.lua#L11).
- Provide empty function argument if the `pending()` call is outside of `it()`
+ Provide empty function argument if the `pending()` call is outside `it()`
([example](https://github.com/neovim/neovim/commit/5c1dc0fbe7388528875aff9d7b5055ad718014de#diff-bf80b24c724b0004e8418102f68b0679R18)).
- Really long `source([=[...]=])` blocks may break Vim's Lua syntax
highlighting. Try `:syntax sync fromstart` to fix it.
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index 6f929ad1ca..6c2c136edc 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -6,8 +6,14 @@ local command = helpers.command
local curbufmeths = helpers.curbufmeths
local eq = helpers.eq
local meths = helpers.meths
+local bufmeths = helpers.bufmeths
+local matches = helpers.matches
local source = helpers.source
local pcall_err = helpers.pcall_err
+local exec_lua = helpers.exec_lua
+local assert_alive = helpers.assert_alive
+local feed = helpers.feed
+local funcs = helpers.funcs
describe('nvim_get_commands', function()
local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
@@ -78,3 +84,118 @@ describe('nvim_get_commands', function()
eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
end)
end)
+
+describe('nvim_add_user_command', function()
+ before_each(clear)
+
+ it('works with strings', function()
+ meths.add_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
+ meths.command('SomeCommand 42')
+ eq(42, meths.eval('g:command_fired'))
+ end)
+
+ it('works with Lua functions', function()
+ exec_lua [[
+ result = {}
+ vim.api.nvim_add_user_command('CommandWithLuaCallback', function(opts)
+ result = opts
+ end, {
+ nargs = "*",
+ bang = true,
+ count = 2,
+ })
+ ]]
+
+ eq({
+ args = "hello",
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithLuaCallback hello')
+ return result
+ ]])
+
+ eq({
+ args = "",
+ bang = true,
+ line1 = 10,
+ line2 = 10,
+ mods = "botright",
+ range = 1,
+ count = 10,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('botright 10CommandWithLuaCallback!')
+ return result
+ ]])
+
+ eq({
+ args = "",
+ bang = false,
+ line1 = 1,
+ line2 = 42,
+ mods = "",
+ range = 1,
+ count = 42,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithLuaCallback 42')
+ return result
+ ]])
+ end)
+
+ it('can define buffer-local commands', function()
+ local bufnr = meths.create_buf(false, false)
+ bufmeths.add_user_command(bufnr, "Hello", "", {})
+ matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
+ meths.set_current_buf(bufnr)
+ meths.command("Hello")
+ assert_alive()
+ end)
+
+ it('can use a Lua complete function', function()
+ exec_lua [[
+ vim.api.nvim_add_user_command('Test', '', {
+ nargs = "*",
+ complete = function(arg, cmdline, pos)
+ local options = {"aaa", "bbb", "ccc"}
+ local t = {}
+ for _, v in ipairs(options) do
+ if string.find(v, "^" .. arg) then
+ table.insert(t, v)
+ end
+ end
+ return t
+ end,
+ })
+ ]]
+
+ feed(':Test a<Tab>')
+ eq('Test aaa', funcs.getcmdline())
+ feed('<C-U>Test b<Tab>')
+ eq('Test bbb', funcs.getcmdline())
+ end)
+end)
+
+describe('nvim_del_user_command', function()
+ before_each(clear)
+
+ it('can delete global commands', function()
+ meths.add_user_command('Hello', 'echo "Hi"', {})
+ meths.command('Hello')
+ meths.del_user_command('Hello')
+ matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
+ end)
+
+ it('can delete buffer-local commands', function()
+ bufmeths.add_user_command(0, 'Hello', 'echo "Hi"', {})
+ meths.command('Hello')
+ bufmeths.del_user_command(0, 'Hello')
+ matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
+ end)
+end)
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index d53208a915..22201e21a2 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -974,6 +974,40 @@ describe('API', function()
eq('hello', nvim('get_option_value', 'makeprg', {}))
eq('', nvim('get_option_value', 'makeprg', {scope = 'local'}))
end)
+
+ it('clears the local value of an option with nil', function()
+ -- Set global value
+ nvim('set_option_value', 'shiftwidth', 42, {})
+ eq(42, nvim('get_option_value', 'shiftwidth', {}))
+
+ -- Set local value
+ nvim('set_option_value', 'shiftwidth', 8, {scope = 'local'})
+ eq(8, nvim('get_option_value', 'shiftwidth', {}))
+ eq(8, nvim('get_option_value', 'shiftwidth', {scope = 'local'}))
+ eq(42, nvim('get_option_value', 'shiftwidth', {scope = 'global'}))
+
+ -- Clear value without scope
+ nvim('set_option_value', 'shiftwidth', NIL, {})
+ eq(42, nvim('get_option_value', 'shiftwidth', {}))
+ eq(42, nvim('get_option_value', 'shiftwidth', {scope = 'local'}))
+
+ -- Clear value with explicit scope
+ nvim('set_option_value', 'shiftwidth', 8, {scope = 'local'})
+ nvim('set_option_value', 'shiftwidth', NIL, {scope = 'local'})
+ eq(42, nvim('get_option_value', 'shiftwidth', {}))
+ eq(42, nvim('get_option_value', 'shiftwidth', {scope = 'local'}))
+
+ -- Now try with options with a special "local is unset" value (e.g. 'undolevels')
+ nvim('set_option_value', 'undolevels', 1000, {})
+ eq(1000, nvim('get_option_value', 'undolevels', {scope = 'local'}))
+ nvim('set_option_value', 'undolevels', NIL, {scope = 'local'})
+ eq(-123456, nvim('get_option_value', 'undolevels', {scope = 'local'}))
+
+ nvim('set_option_value', 'autoread', true, {})
+ eq(true, nvim('get_option_value', 'autoread', {scope = 'local'}))
+ nvim('set_option_value', 'autoread', NIL, {scope = 'local'})
+ eq(NIL, nvim('get_option_value', 'autoread', {scope = 'local'}))
+ end)
end)
describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
@@ -1120,8 +1154,8 @@ describe('API', function()
end)
end)
- describe('RPC (K_EVENT) #6166', function()
- it('does not complete ("interrupt") normal-mode operator-pending', function()
+ describe('RPC (K_EVENT)', function()
+ it('does not complete ("interrupt") normal-mode operator-pending #6166', function()
helpers.insert([[
FIRST LINE
SECOND LINE]])
@@ -1157,7 +1191,7 @@ describe('API', function()
]])
end)
- it('does not complete ("interrupt") normal-mode map-pending', function()
+ it('does not complete ("interrupt") normal-mode map-pending #6166', function()
command("nnoremap dd :let g:foo='it worked...'<CR>")
helpers.insert([[
FIRST LINE
@@ -1173,7 +1207,8 @@ describe('API', function()
SECOND LINE]])
eq('it worked...', helpers.eval('g:foo'))
end)
- it('does not complete ("interrupt") insert-mode map-pending', function()
+
+ it('does not complete ("interrupt") insert-mode map-pending #6166', function()
command('inoremap xx foo')
command('set timeoutlen=9999')
helpers.insert([[
@@ -1188,6 +1223,37 @@ describe('API', function()
FIRST LINE
SECOND LINfooE]])
end)
+
+ it('does not interrupt Insert mode i_CTRL-O #10035', function()
+ feed('iHello World<c-o>')
+ eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event
+ eq(2, eval('1+1')) -- causes K_EVENT key
+ eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
+ feed('dd')
+ eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode
+ expect('') -- executed the command
+ end)
+
+ it('does not interrupt Select mode v_CTRL-O #15688', function()
+ feed('iHello World<esc>gh<c-o>')
+ eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event
+ eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288
+ eq(2, eval('1+1')) -- causes K_EVENT key
+ eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
+ feed('^')
+ eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode
+ feed('h')
+ eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode
+ expect('h') -- selection is the whole line and is replaced
+ end)
+
+ it('does not interrupt Insert mode i_0_CTRL-D #13997', function()
+ command('set timeoutlen=9999')
+ feed('i<Tab><Tab>a0')
+ eq(2, eval('1+1')) -- causes K_EVENT key
+ feed('<C-D>')
+ expect('a') -- recognized i_0_CTRL-D
+ end)
end)
describe('nvim_get_context', function()
diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua
index 46ab483036..f03508035d 100644
--- a/test/functional/editor/mode_insert_spec.lua
+++ b/test/functional/editor/mode_insert_spec.lua
@@ -6,7 +6,6 @@ local expect = helpers.expect
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
-local meths = helpers.meths
describe('insert-mode', function()
before_each(function()
@@ -75,15 +74,5 @@ describe('insert-mode', function()
feed('ooo')
expect('hello oooworld')
end)
-
- it("doesn't cancel Ctrl-O mode when processing event", function()
- feed('iHello World<c-o>')
- eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event
- eq(2, eval('1+1')) -- causes K_EVENT key
- eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
- feed('dd')
- eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode
- expect('') -- executed the command
- end)
end)
end)
diff --git a/test/functional/editor/mode_visual_spec.lua b/test/functional/editor/mode_visual_spec.lua
deleted file mode 100644
index 468ae00e01..0000000000
--- a/test/functional/editor/mode_visual_spec.lua
+++ /dev/null
@@ -1,27 +0,0 @@
--- Visual-mode tests.
-
-local helpers = require('test.functional.helpers')(after_each)
-local clear = helpers.clear
-local eq = helpers.eq
-local eval = helpers.eval
-local expect = helpers.expect
-local feed = helpers.feed
-local meths = helpers.meths
-
-describe('visual-mode', function()
- before_each(clear)
-
- it("select-mode Ctrl-O doesn't cancel Ctrl-O mode when processing event #15688", function()
- feed('iHello World<esc>gh<c-o>')
- eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event
- eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288
- eq(2, eval('1+1')) -- causes K_EVENT key
- eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
- feed('^')
- eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode
- feed('h')
- eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode
- expect('h') -- selection is the whole line and is replaced
- end)
-end)
-
diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua
index 023cdd4ae1..bd14f3bc53 100644
--- a/test/functional/legacy/options_spec.lua
+++ b/test/functional/legacy/options_spec.lua
@@ -83,4 +83,9 @@ describe('set', function()
Press ENTER or type command to continue^ |
]])
end)
+
+ it('foldcolumn and signcolumn to empty string is disallowed', function()
+ matches('E474: Invalid argument: fdc=', exc_exec('set fdc='))
+ matches('E474: Invalid argument: scl=', exc_exec('set scl='))
+ end)
end)
diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua
index 81e00bba6d..f6038e23fe 100644
--- a/test/functional/lua/api_spec.lua
+++ b/test/functional/lua/api_spec.lua
@@ -8,6 +8,7 @@ local clear = helpers.clear
local eval = helpers.eval
local NIL = helpers.NIL
local eq = helpers.eq
+local exec_lua = helpers.exec_lua
before_each(clear)
@@ -111,6 +112,12 @@ describe('luaeval(vim.api.…)', function()
eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]]))
eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})'))
+
+ eq(true, funcs.luaeval('vim.api.nvim__id(vim.api.nvim__id)(true)'))
+ eq(42, exec_lua [[
+ local f = vim.api.nvim__id({42, vim.api.nvim__id})
+ return f[2](f[1])
+ ]])
end)
it('correctly converts container objects with type_idx to API objects', function()
@@ -159,12 +166,8 @@ describe('luaeval(vim.api.…)', function()
it('errors out correctly when working with API', function()
-- Conversion errors
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
- remove_trace(exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
- eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
- remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]])))
-- Errors in number of arguments
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id()")]])))
diff --git a/test/functional/lua/spell_spec.lua b/test/functional/lua/spell_spec.lua
new file mode 100644
index 0000000000..7e831f16a7
--- /dev/null
+++ b/test/functional/lua/spell_spec.lua
@@ -0,0 +1,53 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+local pcall_err = helpers.pcall_err
+
+describe('vim.spell', function()
+ before_each(function()
+ clear()
+ end)
+
+ describe('.check', function()
+ local check = function(x, exp)
+ return eq(exp, exec_lua("return vim.spell.check(...)", x))
+ end
+
+ it('can handle nil', function()
+ eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'check' (expected string)]],
+ pcall_err(exec_lua, [[vim.spell.check(nil)]]))
+ end)
+
+ it('can check spellings', function()
+ check('hello', {})
+
+ check(
+ 'helloi',
+ {{"helloi", "bad", 1}}
+ )
+
+ check(
+ 'hello therei',
+ {{"therei", "bad", 7}}
+ )
+
+ check(
+ 'hello. there',
+ {{"there", "caps", 8}}
+ )
+
+ check(
+ 'neovim cna chkc spellins. okay?',
+ {
+ {"neovim" , "bad" , 1},
+ {"cna" , "bad" , 8},
+ {"chkc" , "bad" , 12},
+ {"spellins", "bad" , 17},
+ {"okay" , "caps", 27}
+ }
+ )
+ end)
+
+ end)
+end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index dbfbe2dbfe..fa11fdf794 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -155,6 +155,12 @@ describe('URI methods', function()
return pcall(vim.uri_to_fname, 'not_an_uri.txt')
]])
end)
+
+ it('uri_to_fname should not treat comma as a scheme character', function()
+ eq(false, exec_lua [[
+ return pcall(vim.uri_to_fname, 'foo,://bar')
+ ]])
+ end)
end)
end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index b12d4227d5..1af31c38f8 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -301,6 +301,43 @@ describe('LSP', function()
}
end)
+ it('should detach buffer in response to nvim_buf_detach', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_finish";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ ]]
+ eq(true, exec_lua("return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)"))
+ eq(true, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
+ exec_lua [[
+ vim.api.nvim_command(BUFFER.."bwipeout")
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ client.notify('finish')
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end;
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
+ exec_lua("return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)")
+ eq(false, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
+ client.stop()
+ end
+ end;
+ }
+ end)
+
it('client should return settings via workspace/configuration handler', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index 3d8441b93c..780a0b9b5a 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -45,6 +45,12 @@ describe(':terminal mouse', function()
eq('nt', eval('mode(1)'))
end)
+ it('does not leave terminal mode on left-release', function()
+ if helpers.pending_win32(pending) then return end
+ feed('<LeftRelease>')
+ eq('t', eval('mode(1)'))
+ end)
+
describe('with mouse events enabled by the program', function()
before_each(function()
thelpers.enable_mouse()
@@ -60,7 +66,7 @@ describe(':terminal mouse', function()
]])
end)
- it('will forward mouse clicks to the program', function()
+ it('will forward mouse press, drag and release to the program', function()
if helpers.pending_win32(pending) then return end
feed('<LeftMouse><1,2>')
screen:expect([[
@@ -72,6 +78,36 @@ describe(':terminal mouse', function()
"#{1: } |
{3:-- TERMINAL --} |
]])
+ feed('<LeftDrag><2,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ @##{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<LeftDrag><3,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ @$#{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<LeftRelease><3,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ #$#{1: } |
+ {3:-- TERMINAL --} |
+ ]])
end)
it('will forward mouse scroll to the program', function()
@@ -88,6 +124,60 @@ describe(':terminal mouse', function()
]])
end)
+ it('dragging and scrolling do not interfere with each other', function()
+ if helpers.pending_win32(pending) then return end
+ feed('<LeftMouse><1,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ "#{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<ScrollWheelUp><1,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ `"#{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<LeftDrag><2,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ @##{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<ScrollWheelUp><2,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ `##{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<LeftRelease><2,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ ###{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
it('will forward mouse clicks to the program with the correct even if set nu', function()
if helpers.pending_win32(pending) then return end
nvim('command', 'set number')
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index aeba049557..4fc5c389e5 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -2213,4 +2213,134 @@ describe('builtin popupmenu', function()
feed('<c-y>')
assert_alive()
end)
+
+ it('truncates double-width character correctly when there is no scrollbar', function()
+ screen:try_resize(32,8)
+ command('set completeopt+=menuone,noselect')
+ feed('i' .. string.rep(' ', 13))
+ funcs.complete(14, {'哦哦哦哦哦哦哦哦哦哦'})
+ screen:expect([[
+ ^ |
+ {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+
+ it('truncates double-width character correctly when there is scrollbar', function()
+ screen:try_resize(32,8)
+ command('set completeopt+=noselect')
+ command('set pumheight=4')
+ feed('i' .. string.rep(' ', 12))
+ local items = {}
+ for _ = 1, 8 do
+ table.insert(items, {word = '哦哦哦哦哦哦哦哦哦哦', equal = 1, dup = 1})
+ end
+ funcs.complete(13, items)
+ screen:expect([[
+ ^ |
+ {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{c: }|
+ {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{c: }|
+ {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{s: }|
+ {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{s: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+end)
+
+describe('builtin popupmenu with ui/ext_multigrid', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(32, 20)
+ screen:attach({ext_multigrid=true})
+ screen:set_default_attr_ids({
+ -- popup selected item / scrollbar track
+ ['s'] = {background = Screen.colors.WebGray},
+ -- popup non-selected item
+ ['n'] = {background = Screen.colors.LightMagenta},
+ -- popup scrollbar knob
+ ['c'] = {background = Screen.colors.Grey0},
+ [1] = {bold = true, foreground = Screen.colors.Blue},
+ [2] = {bold = true},
+ [3] = {reverse = true},
+ [4] = {bold = true, reverse = true},
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ })
+ end)
+
+ it('truncates double-width character correctly when there is no scrollbar', function()
+ screen:try_resize(32,8)
+ command('set completeopt+=menuone,noselect')
+ feed('i' .. string.rep(' ', 13))
+ funcs.complete(14, {'哦哦哦哦哦哦哦哦哦哦'})
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ {2:-- INSERT --} |
+ ## grid 4
+ {n: 哦哦哦哦哦哦哦哦哦>}|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 12, false, 100}}})
+ end)
+
+ it('truncates double-width character correctly when there is scrollbar', function()
+ screen:try_resize(32,8)
+ command('set completeopt+=noselect')
+ command('set pumheight=4')
+ feed('i' .. string.rep(' ', 12))
+ local items = {}
+ for _ = 1, 8 do
+ table.insert(items, {word = '哦哦哦哦哦哦哦哦哦哦', equal = 1, dup = 1})
+ end
+ funcs.complete(13, items)
+ screen:expect({grid=[[
+ ## grid 1
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [2:--------------------------------]|
+ [3:--------------------------------]|
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ {2:-- INSERT --} |
+ ## grid 4
+ {n: 哦哦哦哦哦哦哦哦哦>}{c: }|
+ {n: 哦哦哦哦哦哦哦哦哦>}{c: }|
+ {n: 哦哦哦哦哦哦哦哦哦>}{s: }|
+ {n: 哦哦哦哦哦哦哦哦哦>}{s: }|
+ ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 11, false, 100}}})
+ end)
end)