From 9915b5497bcd6a055842011c23fe0a8086f91f03 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 15 Sep 2019 16:19:49 -0700 Subject: version bump --- runtime/nvim.appdata.xml | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 32d3c523c6..10946c356e 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ + -- cgit From 1f76c4af13c71e4737c3e311da93ab491b7384f7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 15 Sep 2019 17:24:41 -0700 Subject: nvim.appdata.xml [ci skip] --- runtime/nvim.appdata.xml | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 10946c356e..ea69d5a872 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ + -- cgit From 60e0000c5dd4465fb2982ba83d40e642fe10060b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 15 Sep 2019 19:44:11 -0700 Subject: nvim.appdata.xml [ci skip] --- runtime/nvim.appdata.xml | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index ea69d5a872..ceffc7ba98 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ + -- cgit From 792c2903435ceda05e68007d7bee344f65ee3a4f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 16 Sep 2019 21:02:49 -0400 Subject: vim-patch:8.0.1523: cannot write and read terminal screendumps Problem: Cannot write and read terminal screendumps. Solution: Add term_dumpwrite(), term_dumpread() and term_dumpdiff(). Also add assert_equalfile(). https://github.com/vim/vim/commit/d96ff165113ce5fe62107add590997660e3d4802 --- runtime/doc/eval.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 512cfc4e58..fb39617c17 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2007,6 +2007,8 @@ argv([-1, {winid}]) List the argument list assert_beeps({cmd}) none assert {cmd} causes a beep assert_equal({exp}, {act} [, {msg}]) none assert {exp} is equal to {act} +assert_equalfile({fname-one}, {fname-two}) + none assert file contents is equal assert_exception({error} [, {msg}]) none assert {error} is in v:exception assert_fails({cmd} [, {error}]) none assert {cmd} fails @@ -2597,6 +2599,13 @@ assert_equal({expected}, {actual}, [, {msg}]) < Will result in a string to be added to |v:errors|: test.vim line 12: Expected 'foo' but got 'bar' ~ + *assert_equalfile()* +assert_equalfile({fname-one}, {fname-two}) + When the files {fname-one} and {fname-two} do not contain + exactly the same text an error message is added to |v:errors|. + When {fname-one} or {fname-two} does not exist the error will + mention that. + assert_exception({error} [, {msg}]) *assert_exception()* When v:exception does not contain the string {error} an error message is added to |v:errors|. @@ -4479,8 +4488,7 @@ getftype({fname}) *getftype()* systems that support it. On some systems only "dir" and "file" are returned. - *getjumplist()* -getjumplist([{winnr} [, {tabnr}]]) +getjumplist([{winnr} [, {tabnr}]]) *getjumplist()* Returns the |jumplist| for the specified window. Without arguments use the current window. -- cgit From 8db9e82e3e1aa094ca9224b01384da1b07fda410 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 16 Sep 2019 22:26:41 -0400 Subject: vim-patch:8.0.1770: assert functions don't return anything Problem: Assert functions don't return anything. Solution: Return non-zero when the assertion fails. https://github.com/vim/vim/commit/65a5464985f980d2bbbf4e14d39d416dce065ec7 --- runtime/doc/eval.txt | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index fb39617c17..bac7709ef5 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1547,10 +1547,12 @@ v:errmsg Last given error message. :if v:errmsg != "" : ... handle error < - *v:errors* *errors-variable* + *v:errors* *errors-variable* *assert-return* v:errors Errors found by assert functions, such as |assert_true()|. This is a list of strings. The assert functions append an item when an assert fails. + The return value indicates this: a one is returned if an item + was added to v:errors, otherwise zero is returned. To remove old results make it empty: > :let v:errors = [] < If v:errors is set to anything but a list it is made an empty @@ -2004,26 +2006,26 @@ argidx() Number current index in the argument list arglistid([{winnr} [, {tabnr}]]) Number argument list id argv({nr} [, {winid}]) String {nr} entry of the argument list argv([-1, {winid}]) List the argument list -assert_beeps({cmd}) none assert {cmd} causes a beep +assert_beeps({cmd}) Number assert {cmd} causes a beep assert_equal({exp}, {act} [, {msg}]) - none assert {exp} is equal to {act} + Number assert {exp} is equal to {act} assert_equalfile({fname-one}, {fname-two}) - none assert file contents is equal + Number assert file contents is equal assert_exception({error} [, {msg}]) - none assert {error} is in v:exception -assert_fails({cmd} [, {error}]) none assert {cmd} fails + Number assert {error} is in v:exception +assert_fails({cmd} [, {error}]) Number assert {cmd} fails assert_false({actual} [, {msg}]) - none assert {actual} is false + Number assert {actual} is false assert_inrange({lower}, {upper}, {actual} [, {msg}]) - none assert {actual} is inside the range + Number assert {actual} is inside the range assert_match({pat}, {text} [, {msg}]) - none assert {pat} matches {text} + Number assert {pat} matches {text} assert_notequal({exp}, {act} [, {msg}]) - none assert {exp} is not equal {act} + Number assert {exp} is not equal {act} assert_notmatch({pat}, {text} [, {msg}]) - none assert {pat} not matches {text} -assert_report({msg}) none report a test failure -assert_true({actual} [, {msg}]) none assert {actual} is true + Number assert {pat} not matches {text} +assert_report({msg}) Number report a test failure +assert_true({actual} [, {msg}]) Number assert {actual} is true asin({expr}) Float arc sine of {expr} atan({expr}) Float arc tangent of {expr} atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2} @@ -2582,12 +2584,13 @@ argv([{nr} [, {winid}]) assert_beeps({cmd}) *assert_beeps()* Run {cmd} and add an error message to |v:errors| if it does NOT produce a beep or visual bell. - Also see |assert_fails()|. + Also see |assert_fails()| and |assert-return|. *assert_equal()* assert_equal({expected}, {actual}, [, {msg}]) When {expected} and {actual} are not equal an error message is - added to |v:errors|. + added to |v:errors| and 1 is returned. Otherwise zero is + returned |assert-return|. There is no automatic conversion, the String "4" is different from the Number 4. And the number 4 is different from the Float 4.0. The value of 'ignorecase' is not used here, case @@ -2603,12 +2606,13 @@ assert_equal({expected}, {actual}, [, {msg}]) assert_equalfile({fname-one}, {fname-two}) When the files {fname-one} and {fname-two} do not contain exactly the same text an error message is added to |v:errors|. + Also see |assert-return|. When {fname-one} or {fname-two} does not exist the error will mention that. assert_exception({error} [, {msg}]) *assert_exception()* When v:exception does not contain the string {error} an error - message is added to |v:errors|. + message is added to |v:errors|. Also see |assert-return|. This can be used to assert that a command throws an exception. Using the error number, followed by a colon, avoids problems with translations: > @@ -2621,7 +2625,7 @@ assert_exception({error} [, {msg}]) *assert_exception()* assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()* Run {cmd} and add an error message to |v:errors| if it does - NOT produce an error. + NOT produce an error. Also see |assert-return|. When {error} is given it must match in |v:errmsg|. Note that beeping is not considered an error, and some failing commands only beep. Use |assert_beeps()| for those. @@ -2629,6 +2633,7 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()* assert_false({actual} [, {msg}]) *assert_false()* When {actual} is not false an error message is added to |v:errors|, like with |assert_equal()|. + Also see |assert-return|. A value is false when it is zero or |v:false|. When "{actual}" is not a number or |v:false| the assert fails. When {msg} is omitted an error in the form @@ -2645,7 +2650,7 @@ assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()* *assert_match()* assert_match({pattern}, {actual} [, {msg}]) When {pattern} does not match {actual} an error message is - added to |v:errors|. + added to |v:errors|. Also see |assert-return|. {pattern} is used as with |=~|: The matching is always done like 'magic' was set and 'cpoptions' is empty, no matter what @@ -2666,18 +2671,22 @@ assert_match({pattern}, {actual} [, {msg}]) assert_notequal({expected}, {actual} [, {msg}]) The opposite of `assert_equal()`: add an error message to |v:errors| when {expected} and {actual} are equal. + Also see |assert-return|. *assert_notmatch()* assert_notmatch({pattern}, {actual} [, {msg}]) The opposite of `assert_match()`: add an error message to |v:errors| when {pattern} matches {actual}. + Also see |assert-return|. assert_report({msg}) *assert_report()* Report a test failure directly, using {msg}. + Always returns one. assert_true({actual} [, {msg}]) *assert_true()* When {actual} is not true an error message is added to |v:errors|, like with |assert_equal()|. + Also see |assert-return|. A value is |TRUE| when it is a non-zero number or |v:true|. When {actual} is not a number or |v:true| the assert fails. When {msg} is omitted an error in the form "Expected True but -- cgit From d4785421106c9ac81adc9ddd5778446d80dbc4ba Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 18:21:44 +0200 Subject: health#provider: fix duplicated output/stderr (#11048) Ref: https://github.com/neovim/neovim/pull/11047#issuecomment-532268826 --- runtime/autoload/health/provider.vim | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 87d82150b6..f1238edbde 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -38,9 +38,10 @@ endfunction " Handler for s:system() function. function! s:system_handler(jobid, data, event) dict abort if a:event ==# 'stderr' - let self.stderr .= join(a:data, '') - if !self.ignore_stderr + if self.add_stderr_to_output let self.output .= join(a:data, '') + else + let self.stderr .= join(a:data, '') endif elseif a:event ==# 'stdout' let self.output .= join(a:data, '') @@ -64,7 +65,7 @@ function! s:system(cmd, ...) abort let stdin = a:0 ? a:1 : '' let ignore_error = a:0 > 2 ? a:3 : 0 let opts = { - \ 'ignore_stderr': a:0 > 1 ? a:2 : 0, + \ 'add_stderr_to_output': a:0 > 1 ? a:2 : 0, \ 'output': '', \ 'stderr': '', \ 'on_stdout': function('s:system_handler'), @@ -89,8 +90,15 @@ function! s:system(cmd, ...) abort call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd))) call jobstop(jobid) elseif s:shell_error != 0 && !ignore_error - call health#report_error(printf("Command error (job=%d, exit code %d): `%s` (in %s)\nOutput: %s\nStderr: %s", - \ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd()), opts.output, opts.stderr)) + let emsg = printf("Command error (job=%d, exit code %d): `%s` (in %s)", + \ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd())) + if !empty(opts.output) + let emsg .= "\noutput: " . opts.output + end + if !empty(opts.stderr) + let emsg .= "\nstderr: " . opts.stderr + end + call health#report_error(emsg) endif return opts.output -- cgit From 7f59f5b1042a39d2b29bb79603bdf4b235971ba1 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:36:38 +0200 Subject: vim-patch:8.1.0496: no tests for indent files Problem: No tests for indent files. Solution: Add a mechanism for running indent file tests. Add a first test for Vim indenting. https://github.com/vim/vim/commit/c0fe4978f2311be9a0221d4c2369251c719b399a --- runtime/indent/Makefile | 13 ++++ runtime/indent/README.txt | 2 + runtime/indent/testdir/README.txt | 92 +++++++++++++++++++++++++++ runtime/indent/testdir/cleantest.vim | 6 ++ runtime/indent/testdir/runtest.vim | 117 +++++++++++++++++++++++++++++++++++ runtime/indent/testdir/vim.in | 46 ++++++++++++++ runtime/indent/testdir/vim.ok | 46 ++++++++++++++ 7 files changed, 322 insertions(+) create mode 100644 runtime/indent/Makefile create mode 100644 runtime/indent/testdir/README.txt create mode 100644 runtime/indent/testdir/cleantest.vim create mode 100644 runtime/indent/testdir/runtest.vim create mode 100644 runtime/indent/testdir/vim.in create mode 100644 runtime/indent/testdir/vim.ok (limited to 'runtime') diff --git a/runtime/indent/Makefile b/runtime/indent/Makefile new file mode 100644 index 0000000000..fa81db0470 --- /dev/null +++ b/runtime/indent/Makefile @@ -0,0 +1,13 @@ +# Portable Makefile for running indent tests. + +VIM = vim + +# Run the tests that didn't run yet or failed previously. +# If a test succeeds a testdir/*.out file will be written. +# If a test fails a testdir/*.fail file will be written. +test: + $(VIM) --not-a-term -u testdir/runtest.vim + + +clean: + $(VIM) --not-a-term -u testdir/cleantest.vim diff --git a/runtime/indent/README.txt b/runtime/indent/README.txt index a424b4f8c0..8b114365c6 100644 --- a/runtime/indent/README.txt +++ b/runtime/indent/README.txt @@ -43,3 +43,5 @@ running. Add a test if the function exists and use ":finish", like this: The user may have several options set unlike you, try to write the file such that it works with any option settings. Also be aware of certain features not being compiled in. + +To test the indent file, see testdir/README.txt. diff --git a/runtime/indent/testdir/README.txt b/runtime/indent/testdir/README.txt new file mode 100644 index 0000000000..8efd34ed4c --- /dev/null +++ b/runtime/indent/testdir/README.txt @@ -0,0 +1,92 @@ +TESTING INDENT SCRIPTS + +We'll use FILETYPE for the filetype name here. + + +FORMAT OF THE FILETYPE.IN FILE + +First of all, create a FILETYPE.in file. It should contain: + +- A modeline setting the 'filetype' and any other option values. + This must work like a comment for FILETYPE. E.g. for vim: + " vim: set ft=vim sw=4 : + +- At least one block of lines to indent, prefixed with START_INDENT and + followed by END_INDENT. These lines must also look like a comment for your + FILETYPE. You would normally leave out all indent, so that the effect of + the indent command results in adding indent. Example: + + " START_INDENT + func Some() + let x = 1 + endfunc + " END_INDENT + +- Optionally, a line with INDENT_EXE, followed by a Vim command. This will be + executed before indenting the lines. Example: + + " START_INDENT + " INDENT_EXE let g:vim_indent_cont = 6 + let cmd = + \ 'some ' + \ 'string' + " END_INDENT + + Note that the command is not undone, you may need to reverse the effect for + the next block of lines. + +- Alternatively to indenting all the lines between START_INDENT and + END_INDENT, use a INDENT_AT line, which specifies a pattern to find the line + to indent. Example: + + " START_INDENT + " INDENT_AT this-line + func Some() + let f = x " this-line + endfunc + " END_INDENT + + Alternatively you can use INDENT_NEXT to indent the line below the matching + pattern: + + " START_INDENT + " INDENT_NEXT next-line + func Some() + " next-line + let f = x + endfunc + " END_INDENT + + Or use INDENT_PREV to indent the line above the matching pattern: + + " START_INDENT + " INDENT_PREV prev-line + func Some() + let f = x + " prev-line + endfunc + " END_INDENT + +It's best to keep the whole file valid for FILETYPE, so that syntax +highlighting works normally, and any indenting that depends on the syntax +highlighting also works. + + +RUNNING THE TEST + +Before running the test, create a FILETYPE.ok file. You can leave it empty at +first. + +Now run "make test". After Vim has done the indenting you will see a +FILETYPE.fail file. This contains the actual result of indenting, and it's +different from the FILETYPE.ok file. + +Check the contents of the FILETYPE.fail file. If it is perfectly OK, then +rename it to overwrite the FILETYPE.ok file. If you now run "make test" again, +the test will pass and create a FILETYPE.out file, which is identical to the +FILETYPE.ok file. + +If you try to run "make test" again you will notice that nothing happens, +because the FILETYPE.out file already exists. Delete it, or do "make clean", +so that the text runs again. If you edit the FILETYPE.in file, so that it's +newer than the FILETYPE.out file, the test will also run. diff --git a/runtime/indent/testdir/cleantest.vim b/runtime/indent/testdir/cleantest.vim new file mode 100644 index 0000000000..909cf812c4 --- /dev/null +++ b/runtime/indent/testdir/cleantest.vim @@ -0,0 +1,6 @@ +" Deletes all the test output files: *.fail and *.out +for fname in glob('testdir/*.out', 1, 1) + glob('testdir/*.fail', 1, 1) + call delete(fname) +endfor + +quit diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim new file mode 100644 index 0000000000..5d36512f94 --- /dev/null +++ b/runtime/indent/testdir/runtest.vim @@ -0,0 +1,117 @@ +" Runs all the indent tests for which there is no .out file + +set nocp +filetype indent on +set nowrapscan + +au! SwapExists * call HandleSwapExists() +func HandleSwapExists() + " Ignore finding a swap file for the test input and output, the user might be + " editing them and that's OK. + if expand('') =~ '.*\.\(in\|out\|fail\|ok\)' + let v:swapchoice = 'e' + endif +endfunc + +for fname in glob('testdir/*.in', 1, 1) + let root = substitute(fname, '\.in', '', '') + + " Execute the test if the .out file does not exist of when the .in file is + " newer. + let in_time = getftime(fname) + let out_time = getftime(root . '.out') + if out_time < 0 || in_time > out_time + call delete(root . '.fail') + call delete(root . '.out') + + set sw& ts& filetype= + exe 'split ' . fname + + let did_some = 0 + let failed = 0 + let end = 1 + while 1 + " Indent all the lines between "START_INDENT" and "END_INDENT" + exe end + let start = search('\') + let end = search('\') + if start <= 0 || end <= 0 || end <= start + if did_some == 0 + call append(0, 'ERROR: START_INDENT and/or END_INDENT not found') + let failed = 1 + endif + break + else + let did_some = 1 + + " Execute all commands marked with INDENT_EXE and find any pattern. + let lnum = start + let pattern = '' + let at = '' + while 1 + exe lnum + 1 + let lnum_exe = search('\') + exe lnum + 1 + let indent_at = search('\') + if lnum_exe > 0 && lnum_exe < end && (indent_at <= 0 || lnum_exe < indent_at) + exe substitute(getline(lnum_exe), '.*INDENT_EXE', '', '') + let lnum = lnum_exe + let start = lnum + elseif indent_at > 0 && indent_at < end + if pattern != '' + call append(indent_at, 'ERROR: duplicate pattern') + let failed = 1 + break + endif + let text = getline(indent_at) + let pattern = substitute(text, '.*INDENT_\S*\s*', '', '') + let at = substitute(text, '.*INDENT_\(\S*\).*', '\1', '') + let lnum = indent_at + let start = lnum + else + break + endif + endwhile + + exe start + 1 + if pattern == '' + exe 'normal =' . (end - 1) . 'G' + else + let lnum = search(pattern) + if lnum <= 0 + call append(indent_at, 'ERROR: pattern not found: ' . pattern) + let failed = 1 + break + endif + if at == 'AT' + exe lnum + elseif at == 'NEXT' + exe lnum + 1 + else + exe lnum - 1 + endif + normal == + endif + endif + endwhile + + if !failed + " Check the resulting text equals the .ok file. + if getline(1, '$') != readfile(root . '.ok') + let failed = 1 + endif + endif + + if failed + exe 'write ' . root . '.fail' + echoerr 'Test ' . fname . ' FAILED!' + sleep 2 + else + exe 'write ' . root . '.out' + endif + + quit! " close the indented file + endif +endfor + +qall! diff --git a/runtime/indent/testdir/vim.in b/runtime/indent/testdir/vim.in new file mode 100644 index 0000000000..ca105f6eda --- /dev/null +++ b/runtime/indent/testdir/vim.in @@ -0,0 +1,46 @@ +" vim: set ft=vim sw=4 : + +" START_INDENT + +func Some() +let x = 1 +endfunc + +let cmd = +\ 'some ' +\ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 6 + +let cmd = +\ 'some ' +\ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet g:vim_indent_cont +" INDENT_AT this-line +func Some() +let f = x " this-line +endfunc +" END_INDENT + +" START_INDENT +" INDENT_NEXT next-line +func Some() + " next-line +let f = x +endfunc +" END_INDENT + +" START_INDENT +" INDENT_PREV prev-line +func Some() +let f = x +" prev-line +endfunc +" END_INDENT diff --git a/runtime/indent/testdir/vim.ok b/runtime/indent/testdir/vim.ok new file mode 100644 index 0000000000..542861ec9c --- /dev/null +++ b/runtime/indent/testdir/vim.ok @@ -0,0 +1,46 @@ +" vim: set ft=vim sw=4 : + +" START_INDENT + +func Some() + let x = 1 +endfunc + +let cmd = + \ 'some ' + \ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 6 + +let cmd = + \ 'some ' + \ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet g:vim_indent_cont +" INDENT_AT this-line +func Some() + let f = x " this-line +endfunc +" END_INDENT + +" START_INDENT +" INDENT_NEXT next-line +func Some() + " next-line + let f = x +endfunc +" END_INDENT + +" START_INDENT +" INDENT_PREV prev-line +func Some() + let f = x +" prev-line +endfunc +" END_INDENT -- cgit From b466f0e1141fd09847c9763aaa2d02de770e299d Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:37:41 +0200 Subject: vim-patch:8.1.0545: when executing indent tests user preferences interfere Problem: When executing indent tests user preferences interfere. Solution: Add "--clean". https://github.com/vim/vim/commit/dc2f73a6980be13c97a83047d0de50824bc0f20f --- runtime/indent/Makefile | 4 ++-- runtime/indent/testdir/runtest.vim | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/indent/Makefile b/runtime/indent/Makefile index fa81db0470..3791f3348d 100644 --- a/runtime/indent/Makefile +++ b/runtime/indent/Makefile @@ -6,8 +6,8 @@ VIM = vim # If a test succeeds a testdir/*.out file will be written. # If a test fails a testdir/*.fail file will be written. test: - $(VIM) --not-a-term -u testdir/runtest.vim + $(VIM) --clean --not-a-term -u testdir/runtest.vim clean: - $(VIM) --not-a-term -u testdir/cleantest.vim + $(VIM) --clean --not-a-term -u testdir/cleantest.vim diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim index 5d36512f94..1cc0d3f716 100644 --- a/runtime/indent/testdir/runtest.vim +++ b/runtime/indent/testdir/runtest.vim @@ -1,4 +1,6 @@ -" Runs all the indent tests for which there is no .out file +" Runs all the indent tests for which there is no .out file. +" +" Current directory must be runtime/indent. set nocp filetype indent on -- cgit From 865aaa031a06fca61d717621a26906cef42732d1 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:38:07 +0200 Subject: vim-patch:8.1.0576: indent script tests pick up installed scripts Problem: Indent script tests pick up installed scripts. Solution: Use current runtime indent scripts. https://github.com/vim/vim/commit/30700cd5ffa258f1d684ab6b34bd03e970450dba --- runtime/indent/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/indent/Makefile b/runtime/indent/Makefile index 3791f3348d..1917e46c6b 100644 --- a/runtime/indent/Makefile +++ b/runtime/indent/Makefile @@ -1,13 +1,14 @@ # Portable Makefile for running indent tests. VIM = vim +VIMRUNTIME = .. # Run the tests that didn't run yet or failed previously. # If a test succeeds a testdir/*.out file will be written. # If a test fails a testdir/*.fail file will be written. test: - $(VIM) --clean --not-a-term -u testdir/runtest.vim + VIMRUNTIME=$(VIMRUNTIME) $(VIM) --clean --not-a-term -u testdir/runtest.vim clean: - $(VIM) --clean --not-a-term -u testdir/cleantest.vim + VIMRUNTIME=$(VIMRUNTIME) $(VIM) --clean --not-a-term -u testdir/cleantest.vim -- cgit From 10c050caf99df7324f84b4595e8fd1db7f7a2c10 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:38:27 +0200 Subject: vim-patch:8.1.0599: without the +eval feature the indent tests don't work Problem: Without the +eval feature the indent tests don't work. Solution: Skip the body of the tests. https://github.com/vim/vim/commit/eeed665b0ecd917e88e3475c9615d52546aa124d --- runtime/indent/testdir/cleantest.vim | 13 +++++++++---- runtime/indent/testdir/runtest.vim | 6 ++++++ 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/indent/testdir/cleantest.vim b/runtime/indent/testdir/cleantest.vim index 909cf812c4..69c0a1d4ba 100644 --- a/runtime/indent/testdir/cleantest.vim +++ b/runtime/indent/testdir/cleantest.vim @@ -1,6 +1,11 @@ -" Deletes all the test output files: *.fail and *.out -for fname in glob('testdir/*.out', 1, 1) + glob('testdir/*.fail', 1, 1) - call delete(fname) -endfor +" Only do this with the +eval feature +if 1 + + " Deletes all the test output files: *.fail and *.out + for fname in glob('testdir/*.out', 1, 1) + glob('testdir/*.fail', 1, 1) + call delete(fname) + endfor + +endif quit diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim index 1cc0d3f716..96c31453af 100644 --- a/runtime/indent/testdir/runtest.vim +++ b/runtime/indent/testdir/runtest.vim @@ -2,6 +2,9 @@ " " Current directory must be runtime/indent. +" Only do this with the +eval feature +if 1 + set nocp filetype indent on set nowrapscan @@ -116,4 +119,7 @@ for fname in glob('testdir/*.in', 1, 1) endif endfor +" Matching "if 1" at the start. +endif + qall! -- cgit From 48b2d21d5ea328089a2e8fb3798aa93d1a15a388 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:40:06 +0200 Subject: vim-patch:8.1.1213: "make clean" in top dir does not cleanup indent test output Problem: "make clean" in top dir does not cleanup indent test output. Solution: Clean the indent test output. Do not rely on the vim executable for that. (closes vim/vim#4307) https://github.com/vim/vim/commit/e13a3901cae0afb4d2af30d497696af08029fd81 --- runtime/indent/Makefile | 2 +- runtime/indent/testdir/cleantest.vim | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 runtime/indent/testdir/cleantest.vim (limited to 'runtime') diff --git a/runtime/indent/Makefile b/runtime/indent/Makefile index 1917e46c6b..d192605527 100644 --- a/runtime/indent/Makefile +++ b/runtime/indent/Makefile @@ -11,4 +11,4 @@ test: clean: - VIMRUNTIME=$(VIMRUNTIME) $(VIM) --clean --not-a-term -u testdir/cleantest.vim + rm -f testdir/*.fail testdir/*.out diff --git a/runtime/indent/testdir/cleantest.vim b/runtime/indent/testdir/cleantest.vim deleted file mode 100644 index 69c0a1d4ba..0000000000 --- a/runtime/indent/testdir/cleantest.vim +++ /dev/null @@ -1,11 +0,0 @@ -" Only do this with the +eval feature -if 1 - - " Deletes all the test output files: *.fail and *.out - for fname in glob('testdir/*.out', 1, 1) + glob('testdir/*.fail', 1, 1) - call delete(fname) - endfor - -endif - -quit -- cgit From 0e75a9eead33b99d8bbf30ec2b67f09257bf71ee Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:51:50 +0200 Subject: Update runtime/indent/testdir to latest Vim runtime Several runtime updates ignored the non-existing files. --- runtime/indent/testdir/README.txt | 23 ++++++++++++++--------- runtime/indent/testdir/runtest.vim | 3 ++- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/indent/testdir/README.txt b/runtime/indent/testdir/README.txt index 8efd34ed4c..65975605c2 100644 --- a/runtime/indent/testdir/README.txt +++ b/runtime/indent/testdir/README.txt @@ -22,8 +22,12 @@ First of all, create a FILETYPE.in file. It should contain: endfunc " END_INDENT -- Optionally, a line with INDENT_EXE, followed by a Vim command. This will be - executed before indenting the lines. Example: + If you just want to test normal indenting with default options, you can make + this a large number of lines. Just add all kinds of language constructs, + nested statements, etc. with valid syntax. + +- Optionally, add lines with INDENT_EXE after START_INDENT, followed by a Vim + command. This will be executed before indenting the lines. Example: " START_INDENT " INDENT_EXE let g:vim_indent_cont = 6 @@ -36,8 +40,8 @@ First of all, create a FILETYPE.in file. It should contain: the next block of lines. - Alternatively to indenting all the lines between START_INDENT and - END_INDENT, use a INDENT_AT line, which specifies a pattern to find the line - to indent. Example: + END_INDENT, use an INDENT_AT line, which specifies a pattern to find the + line to indent. Example: " START_INDENT " INDENT_AT this-line @@ -47,7 +51,8 @@ First of all, create a FILETYPE.in file. It should contain: " END_INDENT Alternatively you can use INDENT_NEXT to indent the line below the matching - pattern: + pattern. Keep in mind that quite often it will indent relative to the + matching line: " START_INDENT " INDENT_NEXT next-line @@ -77,14 +82,14 @@ RUNNING THE TEST Before running the test, create a FILETYPE.ok file. You can leave it empty at first. -Now run "make test". After Vim has done the indenting you will see a -FILETYPE.fail file. This contains the actual result of indenting, and it's -different from the FILETYPE.ok file. +Now run "make test" from the parent directory. After Vim has done the +indenting you will see a FILETYPE.fail file. This contains the actual result +of indenting, and it's different from the FILETYPE.ok file. Check the contents of the FILETYPE.fail file. If it is perfectly OK, then rename it to overwrite the FILETYPE.ok file. If you now run "make test" again, the test will pass and create a FILETYPE.out file, which is identical to the -FILETYPE.ok file. +FILETYPE.ok file. The FILETYPE.fail file will be deleted. If you try to run "make test" again you will notice that nothing happens, because the FILETYPE.out file already exists. Delete it, or do "make clean", diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim index 96c31453af..0f0051415d 100644 --- a/runtime/indent/testdir/runtest.vim +++ b/runtime/indent/testdir/runtest.vim @@ -7,7 +7,9 @@ if 1 set nocp filetype indent on +syn on set nowrapscan +set report=9999 au! SwapExists * call HandleSwapExists() func HandleSwapExists() @@ -110,7 +112,6 @@ for fname in glob('testdir/*.in', 1, 1) if failed exe 'write ' . root . '.fail' echoerr 'Test ' . fname . ' FAILED!' - sleep 2 else exe 'write ' . root . '.out' endif -- cgit From 660b452440d60725f0cd68f264ba10b6add068c8 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 18 Sep 2019 22:52:22 +0200 Subject: vim-patch:8.1.2056: "make test" for indent files doesn't cause make to fail Problem: "make test" for indent files doesn't cause make to fail. Solution: Exit the script with ":cquit". (Daniel Hahler, closes vim/vim#4949) https://github.com/vim/vim/commit/cd67059c0c3abf1e28aa66458abdf6f338252eb2 --- runtime/indent/testdir/runtest.vim | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'runtime') diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim index 0f0051415d..9502c42f3e 100644 --- a/runtime/indent/testdir/runtest.vim +++ b/runtime/indent/testdir/runtest.vim @@ -20,6 +20,7 @@ func HandleSwapExists() endif endfunc +let failed_count = 0 for fname in glob('testdir/*.in', 1, 1) let root = substitute(fname, '\.in', '', '') @@ -110,6 +111,7 @@ for fname in glob('testdir/*.in', 1, 1) endif if failed + let failed_count += 1 exe 'write ' . root . '.fail' echoerr 'Test ' . fname . ' FAILED!' else @@ -123,4 +125,8 @@ endfor " Matching "if 1" at the start. endif +if failed_count > 0 + " have make report an error + cquit +endif qall! -- cgit From ca116625153d806bd192f0f533346cc9536904a9 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 20 Sep 2019 02:18:38 -0400 Subject: vim-patch:8.0.1539: no test for the popup menu positioning Problem: No test for the popup menu positioning. Solution: Add a screendump test for the popup menu. https://github.com/vim/vim/commit/6bb2cdfe604e51eec216cbe23bb6e8fb47810347 --- runtime/doc/eval.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index bac7709ef5..5e6bfd0dbc 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -6657,7 +6657,7 @@ remote_expr({server}, {string} [, {idvar} [, {timeout}]]) between (not at the end), like with join(expr, "\n"). If {idvar} is present and not empty, it is taken as the name of a variable and a {serverid} for later use with - remote_read() is stored there. + |remote_read()| is stored there. If {timeout} is given the read times out after this many seconds. Otherwise a timeout of 600 seconds is used. See also |clientserver| |RemoteReply|. -- cgit From 45447e3b647259d78434798ddd9c2ae245dcdbcc Mon Sep 17 00:00:00 2001 From: Yoshio S Date: Sun, 22 Sep 2019 08:17:22 +0900 Subject: checkhealth: skip python checks if intentionally disabled #11044 close #11040 --- runtime/autoload/health/provider.vim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index f1238edbde..f52c2c2cbf 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -276,7 +276,11 @@ function! s:check_python(version) abort let python_multiple = [] if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') - call health#report_info('Disabled ('.loaded_var.'='.eval(loaded_var).'). This might be due to some previous error.') + let v = eval(loaded_var) + call health#report_info('Disabled ('.loaded_var.'='.v.').'.(0 is v ? '' : ' This might be due to some previous error.')) + if 0 is v + return + endif endif let [pyenv, pyenv_root] = s:check_for_pyenv() -- cgit From 18e5869f56aab8a52d84185e5bd043799c36ae2d Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Sun, 15 Sep 2019 14:16:44 -0500 Subject: Fix "precedes" listchar behavior in wrap mode Previously, the "precedes" character would be rendered on every row when w_skipcol > 0 (i.e., when viewing a single line longer than the entire screen), instead of just on the first row. Make sure to only render it on the first row in this case. Add a test for this behavior. Fix documentation for the "precedes" character, which erroneously stated that it was only active when wrap mode was off. --- runtime/doc/options.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 188f7fc2e2..ec0f119033 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3698,9 +3698,8 @@ A jump table for the options with a short description can be found at |Q_op|. off and the line continues beyond the right of the screen. *lcs-precedes* - precedes:c Character to show in the first column, when 'wrap' - is off and there is text preceding the character - visible in the first column. + precedes:c Character to show in the first column, when there is + text preceding the character visible in the first column. *lcs-conceal* conceal:c Character to show in place of concealed text, when 'conceallevel' is set to 1. A space when omitted. -- cgit From 16549324988be0717b59f7e5fec818ee9ad70f52 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 21 Sep 2019 20:29:15 -0700 Subject: vim-patch:8.1.2060: "precedes" in 'listchars' not used properly (Credit: Zach Wegner, https://github.com/neovim/neovim/pull/11034) Problem: "precedes" in 'listchars' not used properly. Solution: Correctly handle the "precedes" char in list mode for long lines. https://github.com/vim/vim/commit/bffba7f7042f6082e75b42484b15f66087b01941 --- runtime/doc/options.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index ec0f119033..d87898bb89 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3698,8 +3698,9 @@ A jump table for the options with a short description can be found at |Q_op|. off and the line continues beyond the right of the screen. *lcs-precedes* - precedes:c Character to show in the first column, when there is - text preceding the character visible in the first column. + precedes:c Character to show in the first visible column of the + physical line, when there is text preceding the + character visible in the first column. *lcs-conceal* conceal:c Character to show in place of concealed text, when 'conceallevel' is set to 1. A space when omitted. -- cgit From cb252071718a58c2d9747177ebeb81ff1210ddd1 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 17 Jun 2019 22:35:07 +0200 Subject: vim-patch:8.0.0914: highlight attributes are always combined (#10256) Problem: Highlight attributes are always combined. Solution: Add the 'nocombine' value to replace attributes instead of combining them. (scauligi, closes vim/vim#1963) https://github.com/vim/vim/commit/0cd2a94a4030f6bd12eaec44db92db108e33c913 Closes https://github.com/neovim/neovim/pull/10256. --- runtime/doc/syntax.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 0a4257e2b2..5424ad00ec 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4720,18 +4720,19 @@ the same syntax file on all UIs. *bold* *underline* *undercurl* *inverse* *italic* *standout* - *strikethrough* + *nocombine* *strikethrough* cterm={attr-list} *attr-list* *highlight-cterm* *E418* attr-list is a comma separated list (without spaces) of the following items (in any order): bold underline undercurl curly underline + strikethrough reverse inverse same as reverse italic standout - strikethrough + nocombine override attributes instead of combining them NONE no attributes used (used to reset it) Note that "bold" can be used here and by using a bold font. They -- cgit From cd100963866b2c33a286cbf6aac8e42cd16fd248 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 29 Oct 2018 19:11:41 +0100 Subject: tree-sitter: initial tree-sitter support --- runtime/lua/treesitter_rt.lua | 119 ++++++++++++++++++++++++++++++++++++++++++ runtime/plugin/ts_test.vim | 32 ++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 runtime/lua/treesitter_rt.lua create mode 100644 runtime/plugin/ts_test.vim (limited to 'runtime') diff --git a/runtime/lua/treesitter_rt.lua b/runtime/lua/treesitter_rt.lua new file mode 100644 index 0000000000..ba7849abd6 --- /dev/null +++ b/runtime/lua/treesitter_rt.lua @@ -0,0 +1,119 @@ +local a = vim.api + +if __treesitter_rt_ns == nil then + __treesitter_rt_ns = a.nvim_buf_add_highlight(0, 0, "", 0, 0, 0) + __treesitter_rt_syn_ns = a.nvim_buf_add_highlight(0, 0, "", 0, 0, 0) +end +local my_ns = __treesitter_rt_ns +local my_syn_ns = __treesitter_rt_syn_ns + +local path = '.deps/build/src/treesitter-javascript/src/highlights.json' +a.nvim_set_var("_ts_path", path) +obj = a.nvim_eval("json_decode(readfile(g:_ts_path,'b'))") + + +--luadev = require'luadev' +--i = require'inspect' + + +function parse_tree(tsstate, force) + if tsstate.valid and not force then + return tsstate.tree + end + tsstate.tree = tsstate.parser:parse_buf(tsstate.bufnr) + tsstate.valid = true + return tsstate.tree +end + +function the_cb(tsstate, ev, bufnr, tick, start_row, oldstopline, stop_row) + local start_byte = a.nvim_buf_get_offset(bufnr,start_row) + -- a bit messy, should we expose edited but not reparsed tree? + -- are multiple edits safe in general? + local root = tsstate.parser:tree():root() + -- TODO: add proper lookup function! + local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) + local edit + if inode == nil then + local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) + tsstate.parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) + else + local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() + local fake_rows = fakeoldstoprow-oldstopline + local fakestop = stop_row+fake_rows + local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol + tsstate.parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) + end + tsstate.valid = false + --luadev.append_buf({i{edit.start_byte,edit.old_end_byte,edit.new_end_byte}, + -- i{edit.start_point, edit.old_end_point, edit.new_end_point}}) +end + +function attach_buf(tsstate) + local function cb(ev, ...) + return the_cb(tsstate, ev, ...) + end + a.nvim_buf_attach(tsstate.bufnr, false, {on_lines=cb}) +end + +function create_parser(bufnr) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + local ft = a.nvim_buf_get_option(bufnr, "filetype") + local tsstate = {} + tsstate.bufnr = bufnr + tsstate.parser = vim.ts_parser(ft) + parse_tree(tsstate) + attach_buf(tsstate) + return tsstate +end + +function ts_inspect_pos(row,col) + local tree = parse_tree(theparser) + local root = tree:root() + local node = root:descendant_for_point_range(row,col,row,col) + show_node(node) +end + +function show_node(node) + if node == nil then + return + end + a.nvim_buf_clear_highlight(0, my_ns, 0, -1) + shown_node = node + print(node:type()) + local start_row, start_col, end_row, end_col = node:range() + + a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1) + + if end_col >= 1 then + end_col = end_col - 1 + end + a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1) +end + +function ts_expand_node() + if shown_node == nil then + return + end + parent = shown_node:parent() + show_node(parent) +end + +function ts_cursor() + local row, col = unpack(a.nvim_win_get_cursor(0)) + ts_inspect_pos(row-1, col) +end + +if false then + ctree = theparser.tree + root = ctree:root() + cursor = root:to_cursor() + node = cursor:forward(5000) if true then return node end + print(#root) + c = root:child(50) + print(require'inspect'{c:extent()}) + type(ctree.__tostring) + root:__tostring() + print(_tslua_debug()) +end diff --git a/runtime/plugin/ts_test.vim b/runtime/plugin/ts_test.vim new file mode 100644 index 0000000000..e40e2792e1 --- /dev/null +++ b/runtime/plugin/ts_test.vim @@ -0,0 +1,32 @@ +let g:ts_test_path = expand(":p:h:h") +let g:has_ts = v:false + +func! TSTest() + if g:has_ts + return + end + " TODO: module! + lua require'treesitter_rt' + lua theparser = create_parser(vim.api.nvim_get_current_buf()) + let g:has_ts = v:true +endfunc + +func! TSCursor() + " disable matchparen + NoMatchParen + call TSTest() + au CursorMoved lua ts_cursor() + au CursorMovedI lua ts_cursor() + map (ts-expand) lua ts_expand_node() +endfunc + +func! TSSyntax() + " disable matchparen + set syntax= + call TSTest() + lua ts_syntax() +endfunc + +command! TSTest call TSTest() +command! TSCursor call TSCursor() +command! TSSyntax call TSSyntax() -- cgit From 0e0beef85e4d3932e0d49528d8474794f7b69b01 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 6 Jun 2019 12:20:07 +0200 Subject: tree-sitter: load parsers as .so files --- runtime/lua/treesitter_rt.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/treesitter_rt.lua b/runtime/lua/treesitter_rt.lua index ba7849abd6..1dea22000a 100644 --- a/runtime/lua/treesitter_rt.lua +++ b/runtime/lua/treesitter_rt.lua @@ -7,10 +7,6 @@ end local my_ns = __treesitter_rt_ns local my_syn_ns = __treesitter_rt_syn_ns -local path = '.deps/build/src/treesitter-javascript/src/highlights.json' -a.nvim_set_var("_ts_path", path) -obj = a.nvim_eval("json_decode(readfile(g:_ts_path,'b'))") - --luadev = require'luadev' --i = require'inspect' @@ -62,7 +58,7 @@ function create_parser(bufnr) local ft = a.nvim_buf_get_option(bufnr, "filetype") local tsstate = {} tsstate.bufnr = bufnr - tsstate.parser = vim.ts_parser(ft) + tsstate.parser = vim.ts_parser(ft.."_parser.so", ft) parse_tree(tsstate) attach_buf(tsstate) return tsstate -- cgit From 005b6d638caa200711bf5960e0c0d70ba5721c94 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 7 Jun 2019 14:21:00 +0200 Subject: tree-sitter: split tree-sitter lua interface from demo code --- runtime/lua/tree_sitter_demo.lua | 57 +++++++++++++++++++ runtime/lua/treesitter_rt.lua | 115 --------------------------------------- runtime/lua/vim/tree_sitter.lua | 47 ++++++++++++++++ runtime/plugin/ts_test.vim | 3 +- 4 files changed, 106 insertions(+), 116 deletions(-) create mode 100644 runtime/lua/tree_sitter_demo.lua delete mode 100644 runtime/lua/treesitter_rt.lua create mode 100644 runtime/lua/vim/tree_sitter.lua (limited to 'runtime') diff --git a/runtime/lua/tree_sitter_demo.lua b/runtime/lua/tree_sitter_demo.lua new file mode 100644 index 0000000000..24a0f3d622 --- /dev/null +++ b/runtime/lua/tree_sitter_demo.lua @@ -0,0 +1,57 @@ +local a = vim.api +_G.a = vim.api + +if __treesitter_rt_ns == nil then + __treesitter_rt_ns = a.nvim_create_namespace("treesitter_demp") +end +local my_ns = __treesitter_rt_ns + +function ts_inspect_pos(row,col) + local tree = parse_tree(theparser) + local root = tree:root() + local node = root:descendant_for_point_range(row,col,row,col) + show_node(node) +end + +function show_node(node) + if node == nil then + return + end + a.nvim_buf_clear_highlight(0, my_ns, 0, -1) + shown_node = node + print(node:type()) + local start_row, start_col, end_row, end_col = node:range() + + a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1) + + if end_col >= 1 then + end_col = end_col - 1 + end + a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1) +end + +function ts_expand_node() + if shown_node == nil then + return + end + parent = shown_node:parent() + show_node(parent) +end + +function ts_cursor() + local row, col = unpack(a.nvim_win_get_cursor(0)) + ts_inspect_pos(row-1, col) +end + +if false then + ctree = theparser.tree + root = ctree:root() + cursor = root:to_cursor() + node = cursor:forward(5000) if true then return node end + print(#root) + c = root:child(50) + print(require'inspect'{c:extent()}) + type(ctree.__tostring) + root:__tostring() + print(_tslua_debug()) +end diff --git a/runtime/lua/treesitter_rt.lua b/runtime/lua/treesitter_rt.lua deleted file mode 100644 index 1dea22000a..0000000000 --- a/runtime/lua/treesitter_rt.lua +++ /dev/null @@ -1,115 +0,0 @@ -local a = vim.api - -if __treesitter_rt_ns == nil then - __treesitter_rt_ns = a.nvim_buf_add_highlight(0, 0, "", 0, 0, 0) - __treesitter_rt_syn_ns = a.nvim_buf_add_highlight(0, 0, "", 0, 0, 0) -end -local my_ns = __treesitter_rt_ns -local my_syn_ns = __treesitter_rt_syn_ns - - ---luadev = require'luadev' ---i = require'inspect' - - -function parse_tree(tsstate, force) - if tsstate.valid and not force then - return tsstate.tree - end - tsstate.tree = tsstate.parser:parse_buf(tsstate.bufnr) - tsstate.valid = true - return tsstate.tree -end - -function the_cb(tsstate, ev, bufnr, tick, start_row, oldstopline, stop_row) - local start_byte = a.nvim_buf_get_offset(bufnr,start_row) - -- a bit messy, should we expose edited but not reparsed tree? - -- are multiple edits safe in general? - local root = tsstate.parser:tree():root() - -- TODO: add proper lookup function! - local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) - local edit - if inode == nil then - local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) - tsstate.parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) - else - local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() - local fake_rows = fakeoldstoprow-oldstopline - local fakestop = stop_row+fake_rows - local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol - tsstate.parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) - end - tsstate.valid = false - --luadev.append_buf({i{edit.start_byte,edit.old_end_byte,edit.new_end_byte}, - -- i{edit.start_point, edit.old_end_point, edit.new_end_point}}) -end - -function attach_buf(tsstate) - local function cb(ev, ...) - return the_cb(tsstate, ev, ...) - end - a.nvim_buf_attach(tsstate.bufnr, false, {on_lines=cb}) -end - -function create_parser(bufnr) - if bufnr == 0 then - bufnr = a.nvim_get_current_buf() - end - local ft = a.nvim_buf_get_option(bufnr, "filetype") - local tsstate = {} - tsstate.bufnr = bufnr - tsstate.parser = vim.ts_parser(ft.."_parser.so", ft) - parse_tree(tsstate) - attach_buf(tsstate) - return tsstate -end - -function ts_inspect_pos(row,col) - local tree = parse_tree(theparser) - local root = tree:root() - local node = root:descendant_for_point_range(row,col,row,col) - show_node(node) -end - -function show_node(node) - if node == nil then - return - end - a.nvim_buf_clear_highlight(0, my_ns, 0, -1) - shown_node = node - print(node:type()) - local start_row, start_col, end_row, end_col = node:range() - - a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1) - - if end_col >= 1 then - end_col = end_col - 1 - end - a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1) -end - -function ts_expand_node() - if shown_node == nil then - return - end - parent = shown_node:parent() - show_node(parent) -end - -function ts_cursor() - local row, col = unpack(a.nvim_win_get_cursor(0)) - ts_inspect_pos(row-1, col) -end - -if false then - ctree = theparser.tree - root = ctree:root() - cursor = root:to_cursor() - node = cursor:forward(5000) if true then return node end - print(#root) - c = root:child(50) - print(require'inspect'{c:extent()}) - type(ctree.__tostring) - root:__tostring() - print(_tslua_debug()) -end diff --git a/runtime/lua/vim/tree_sitter.lua b/runtime/lua/vim/tree_sitter.lua new file mode 100644 index 0000000000..a4cb3f3db6 --- /dev/null +++ b/runtime/lua/vim/tree_sitter.lua @@ -0,0 +1,47 @@ +local a = vim.api + +function parse_tree(tsstate, force) + if tsstate.valid and not force then + return tsstate.tree + end + tsstate.tree = tsstate.parser:parse_buf(tsstate.bufnr) + tsstate.valid = true + return tsstate.tree +end + +local function change_cb(tsstate, ev, bufnr, tick, start_row, oldstopline, stop_row) + local start_byte = a.nvim_buf_get_offset(bufnr,start_row) + -- a bit messy, should we expose edited but not reparsed tree? + -- are multiple edits safe in general? + local root = tsstate.parser:tree():root() + -- TODO: add proper lookup function! + local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) + if inode == nil then + local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) + tsstate.parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) + else + local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() + local fake_rows = fakeoldstoprow-oldstopline + local fakestop = stop_row+fake_rows + local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol + tsstate.parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) + end + tsstate.valid = false +end + +function create_parser(bufnr) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + local ft = a.nvim_buf_get_option(bufnr, "filetype") + local tsstate = {} + tsstate.bufnr = bufnr + tsstate.parser = vim.ts_parser(ft.."_parser.so", ft) + parse_tree(tsstate) + local function cb(ev, ...) + return change_cb(tsstate, ev, ...) + end + a.nvim_buf_attach(tsstate.bufnr, false, {on_lines=cb}) + return tsstate +end + diff --git a/runtime/plugin/ts_test.vim b/runtime/plugin/ts_test.vim index e40e2792e1..d048dbe7c8 100644 --- a/runtime/plugin/ts_test.vim +++ b/runtime/plugin/ts_test.vim @@ -6,7 +6,8 @@ func! TSTest() return end " TODO: module! - lua require'treesitter_rt' + lua require'vim.tree_sitter' + lua require'tree_sitter_demo' lua theparser = create_parser(vim.api.nvim_get_current_buf()) let g:has_ts = v:true endfunc -- cgit From 1e9e2451bef21ff705e677802d1b0980356f1f86 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 7 Jun 2019 18:19:59 +0200 Subject: tree-sitter: objectify API --- runtime/lua/tree_sitter_demo.lua | 2 +- runtime/lua/vim/tree_sitter.lua | 46 +++++++++++++++++++++++----------------- runtime/plugin/ts_test.vim | 3 +-- 3 files changed, 29 insertions(+), 22 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/tree_sitter_demo.lua b/runtime/lua/tree_sitter_demo.lua index 24a0f3d622..bbfd69109d 100644 --- a/runtime/lua/tree_sitter_demo.lua +++ b/runtime/lua/tree_sitter_demo.lua @@ -7,7 +7,7 @@ end local my_ns = __treesitter_rt_ns function ts_inspect_pos(row,col) - local tree = parse_tree(theparser) + local tree = theparser:parse_tree() local root = tree:root() local node = root:descendant_for_point_range(row,col,row,col) show_node(node) diff --git a/runtime/lua/vim/tree_sitter.lua b/runtime/lua/vim/tree_sitter.lua index a4cb3f3db6..bbc4db5f29 100644 --- a/runtime/lua/vim/tree_sitter.lua +++ b/runtime/lua/vim/tree_sitter.lua @@ -1,47 +1,55 @@ local a = vim.api -function parse_tree(tsstate, force) - if tsstate.valid and not force then - return tsstate.tree +local Parser = {} +Parser.__index = Parser + +function Parser:parse_tree(force) + if self.valid and not force then + return self.tree end - tsstate.tree = tsstate.parser:parse_buf(tsstate.bufnr) - tsstate.valid = true - return tsstate.tree + self.tree = self._parser:parse_buf(self.bufnr) + self.valid = true + return self.tree end -local function change_cb(tsstate, ev, bufnr, tick, start_row, oldstopline, stop_row) +local function change_cb(self, ev, bufnr, tick, start_row, oldstopline, stop_row) local start_byte = a.nvim_buf_get_offset(bufnr,start_row) -- a bit messy, should we expose edited but not reparsed tree? -- are multiple edits safe in general? - local root = tsstate.parser:tree():root() + local root = self._parser:tree():root() -- TODO: add proper lookup function! local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) if inode == nil then local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) - tsstate.parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) + self._parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) else local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() local fake_rows = fakeoldstoprow-oldstopline local fakestop = stop_row+fake_rows local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol - tsstate.parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) + self._parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) end - tsstate.valid = false + self.valid = false end -function create_parser(bufnr) +local function create_parser(bufnr) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end local ft = a.nvim_buf_get_option(bufnr, "filetype") - local tsstate = {} - tsstate.bufnr = bufnr - tsstate.parser = vim.ts_parser(ft.."_parser.so", ft) - parse_tree(tsstate) + local self = setmetatable({bufnr=bufnr, valid=false}, Parser) + self._parser = vim._create_ts_parser(ft.."_parser.so", ft) + self:parse_tree() local function cb(ev, ...) - return change_cb(tsstate, ev, ...) + -- TODO: use weakref to self, so that the parser is free'd is no plugin is + -- using it. + return change_cb(self, ev, ...) end - a.nvim_buf_attach(tsstate.bufnr, false, {on_lines=cb}) - return tsstate + a.nvim_buf_attach(self.bufnr, false, {on_lines=cb}) + return self end +-- TODO: weak table with reusable parser per buffer. + +return {create_parser=create_parser} + diff --git a/runtime/plugin/ts_test.vim b/runtime/plugin/ts_test.vim index d048dbe7c8..76318163f6 100644 --- a/runtime/plugin/ts_test.vim +++ b/runtime/plugin/ts_test.vim @@ -6,9 +6,8 @@ func! TSTest() return end " TODO: module! - lua require'vim.tree_sitter' + lua theparser = require'vim.tree_sitter'.create_parser(0) lua require'tree_sitter_demo' - lua theparser = create_parser(vim.api.nvim_get_current_buf()) let g:has_ts = v:true endfunc -- cgit From afba23099fccc929fd0319a9a965a7b727407c7a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 8 Jun 2019 15:51:38 +0200 Subject: tree-sitter: support pre-registration of languages --- runtime/lua/vim/tree_sitter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/tree_sitter.lua b/runtime/lua/vim/tree_sitter.lua index bbc4db5f29..a7830bc312 100644 --- a/runtime/lua/vim/tree_sitter.lua +++ b/runtime/lua/vim/tree_sitter.lua @@ -38,7 +38,7 @@ local function create_parser(bufnr) end local ft = a.nvim_buf_get_option(bufnr, "filetype") local self = setmetatable({bufnr=bufnr, valid=false}, Parser) - self._parser = vim._create_ts_parser(ft.."_parser.so", ft) + self._parser = vim._create_ts_parser(ft) self:parse_tree() local function cb(ev, ...) -- TODO: use weakref to self, so that the parser is free'd is no plugin is -- cgit From 4ea5e63aa8c866b4fcc9d10f1a26078d2517f96a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 9 Jun 2019 13:26:48 +0200 Subject: tree-sitter: add basic testing on ci build tree-sitter c parser on ci for testing purposes --- runtime/lua/vim/tree_sitter.lua | 8 +++++--- runtime/plugin/ts_test.vim | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/tree_sitter.lua b/runtime/lua/vim/tree_sitter.lua index a7830bc312..1b5f416b67 100644 --- a/runtime/lua/vim/tree_sitter.lua +++ b/runtime/lua/vim/tree_sitter.lua @@ -32,11 +32,13 @@ local function change_cb(self, ev, bufnr, tick, start_row, oldstopline, stop_row self.valid = false end -local function create_parser(bufnr) +local function create_parser(bufnr, ft) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end - local ft = a.nvim_buf_get_option(bufnr, "filetype") + if ft == nil then + ft = a.nvim_buf_get_option(bufnr, "filetype") + end local self = setmetatable({bufnr=bufnr, valid=false}, Parser) self._parser = vim._create_ts_parser(ft) self:parse_tree() @@ -51,5 +53,5 @@ end -- TODO: weak table with reusable parser per buffer. -return {create_parser=create_parser} +return {create_parser=create_parser, add_language=vim._ts_add_language} diff --git a/runtime/plugin/ts_test.vim b/runtime/plugin/ts_test.vim index 76318163f6..9420c2c9d3 100644 --- a/runtime/plugin/ts_test.vim +++ b/runtime/plugin/ts_test.vim @@ -6,7 +6,7 @@ func! TSTest() return end " TODO: module! - lua theparser = require'vim.tree_sitter'.create_parser(0) + lua theparser = vim.tree_sitter.create_parser(0) lua require'tree_sitter_demo' let g:has_ts = v:true endfunc -- cgit From c8f861b739b4703b1198dc1f88b09edbeb0d9f2e Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 12:10:12 +0200 Subject: tree-sitter: rename tree_sitter => treesitter for consistency --- runtime/lua/tree_sitter_demo.lua | 57 --------------------------------------- runtime/lua/treesitter_demo.lua | 58 ++++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/tree_sitter.lua | 57 --------------------------------------- runtime/lua/vim/treesitter.lua | 57 +++++++++++++++++++++++++++++++++++++++ runtime/plugin/ts_test.vim | 4 +-- 5 files changed, 117 insertions(+), 116 deletions(-) delete mode 100644 runtime/lua/tree_sitter_demo.lua create mode 100644 runtime/lua/treesitter_demo.lua delete mode 100644 runtime/lua/vim/tree_sitter.lua create mode 100644 runtime/lua/vim/treesitter.lua (limited to 'runtime') diff --git a/runtime/lua/tree_sitter_demo.lua b/runtime/lua/tree_sitter_demo.lua deleted file mode 100644 index bbfd69109d..0000000000 --- a/runtime/lua/tree_sitter_demo.lua +++ /dev/null @@ -1,57 +0,0 @@ -local a = vim.api -_G.a = vim.api - -if __treesitter_rt_ns == nil then - __treesitter_rt_ns = a.nvim_create_namespace("treesitter_demp") -end -local my_ns = __treesitter_rt_ns - -function ts_inspect_pos(row,col) - local tree = theparser:parse_tree() - local root = tree:root() - local node = root:descendant_for_point_range(row,col,row,col) - show_node(node) -end - -function show_node(node) - if node == nil then - return - end - a.nvim_buf_clear_highlight(0, my_ns, 0, -1) - shown_node = node - print(node:type()) - local start_row, start_col, end_row, end_col = node:range() - - a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1) - - if end_col >= 1 then - end_col = end_col - 1 - end - a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1) -end - -function ts_expand_node() - if shown_node == nil then - return - end - parent = shown_node:parent() - show_node(parent) -end - -function ts_cursor() - local row, col = unpack(a.nvim_win_get_cursor(0)) - ts_inspect_pos(row-1, col) -end - -if false then - ctree = theparser.tree - root = ctree:root() - cursor = root:to_cursor() - node = cursor:forward(5000) if true then return node end - print(#root) - c = root:child(50) - print(require'inspect'{c:extent()}) - type(ctree.__tostring) - root:__tostring() - print(_tslua_debug()) -end diff --git a/runtime/lua/treesitter_demo.lua b/runtime/lua/treesitter_demo.lua new file mode 100644 index 0000000000..82c36f94c0 --- /dev/null +++ b/runtime/lua/treesitter_demo.lua @@ -0,0 +1,58 @@ +-- TODO: externalize this +local a = vim.api +_G.a = vim.api + +if __treesitter_rt_ns == nil then + __treesitter_rt_ns = a.nvim_create_namespace("treesitter_demp") +end +local my_ns = __treesitter_rt_ns + +function ts_inspect_pos(row,col) + local tree = theparser:parse_tree() + local root = tree:root() + local node = root:descendant_for_point_range(row,col,row,col) + show_node(node) +end + +function show_node(node) + if node == nil then + return + end + a.nvim_buf_clear_highlight(0, my_ns, 0, -1) + shown_node = node + print(node:type()) + local start_row, start_col, end_row, end_col = node:range() + + a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1) + + if end_col >= 1 then + end_col = end_col - 1 + end + a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1) +end + +function ts_expand_node() + if shown_node == nil then + return + end + parent = shown_node:parent() + show_node(parent) +end + +function ts_cursor() + local row, col = unpack(a.nvim_win_get_cursor(0)) + ts_inspect_pos(row-1, col) +end + +if false then + ctree = theparser.tree + root = ctree:root() + cursor = root:to_cursor() + node = cursor:forward(5000) if true then return node end + print(#root) + c = root:child(50) + print(require'inspect'{c:extent()}) + type(ctree.__tostring) + root:__tostring() + print(_tslua_debug()) +end diff --git a/runtime/lua/vim/tree_sitter.lua b/runtime/lua/vim/tree_sitter.lua deleted file mode 100644 index 1b5f416b67..0000000000 --- a/runtime/lua/vim/tree_sitter.lua +++ /dev/null @@ -1,57 +0,0 @@ -local a = vim.api - -local Parser = {} -Parser.__index = Parser - -function Parser:parse_tree(force) - if self.valid and not force then - return self.tree - end - self.tree = self._parser:parse_buf(self.bufnr) - self.valid = true - return self.tree -end - -local function change_cb(self, ev, bufnr, tick, start_row, oldstopline, stop_row) - local start_byte = a.nvim_buf_get_offset(bufnr,start_row) - -- a bit messy, should we expose edited but not reparsed tree? - -- are multiple edits safe in general? - local root = self._parser:tree():root() - -- TODO: add proper lookup function! - local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) - if inode == nil then - local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) - self._parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) - else - local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() - local fake_rows = fakeoldstoprow-oldstopline - local fakestop = stop_row+fake_rows - local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol - self._parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) - end - self.valid = false -end - -local function create_parser(bufnr, ft) - if bufnr == 0 then - bufnr = a.nvim_get_current_buf() - end - if ft == nil then - ft = a.nvim_buf_get_option(bufnr, "filetype") - end - local self = setmetatable({bufnr=bufnr, valid=false}, Parser) - self._parser = vim._create_ts_parser(ft) - self:parse_tree() - local function cb(ev, ...) - -- TODO: use weakref to self, so that the parser is free'd is no plugin is - -- using it. - return change_cb(self, ev, ...) - end - a.nvim_buf_attach(self.bufnr, false, {on_lines=cb}) - return self -end - --- TODO: weak table with reusable parser per buffer. - -return {create_parser=create_parser, add_language=vim._ts_add_language} - diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua new file mode 100644 index 0000000000..1b5f416b67 --- /dev/null +++ b/runtime/lua/vim/treesitter.lua @@ -0,0 +1,57 @@ +local a = vim.api + +local Parser = {} +Parser.__index = Parser + +function Parser:parse_tree(force) + if self.valid and not force then + return self.tree + end + self.tree = self._parser:parse_buf(self.bufnr) + self.valid = true + return self.tree +end + +local function change_cb(self, ev, bufnr, tick, start_row, oldstopline, stop_row) + local start_byte = a.nvim_buf_get_offset(bufnr,start_row) + -- a bit messy, should we expose edited but not reparsed tree? + -- are multiple edits safe in general? + local root = self._parser:tree():root() + -- TODO: add proper lookup function! + local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) + if inode == nil then + local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) + self._parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) + else + local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() + local fake_rows = fakeoldstoprow-oldstopline + local fakestop = stop_row+fake_rows + local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol + self._parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) + end + self.valid = false +end + +local function create_parser(bufnr, ft) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + if ft == nil then + ft = a.nvim_buf_get_option(bufnr, "filetype") + end + local self = setmetatable({bufnr=bufnr, valid=false}, Parser) + self._parser = vim._create_ts_parser(ft) + self:parse_tree() + local function cb(ev, ...) + -- TODO: use weakref to self, so that the parser is free'd is no plugin is + -- using it. + return change_cb(self, ev, ...) + end + a.nvim_buf_attach(self.bufnr, false, {on_lines=cb}) + return self +end + +-- TODO: weak table with reusable parser per buffer. + +return {create_parser=create_parser, add_language=vim._ts_add_language} + diff --git a/runtime/plugin/ts_test.vim b/runtime/plugin/ts_test.vim index 9420c2c9d3..15192d8dda 100644 --- a/runtime/plugin/ts_test.vim +++ b/runtime/plugin/ts_test.vim @@ -6,8 +6,8 @@ func! TSTest() return end " TODO: module! - lua theparser = vim.tree_sitter.create_parser(0) - lua require'tree_sitter_demo' + lua theparser = vim.treesitter.create_parser(0) + lua require'treesitter_demo' let g:has_ts = v:true endfunc -- cgit From d24dec596c25690aba0aca658546ffdfcc6a952c Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 15 Jun 2019 14:05:35 +0200 Subject: tree-sitter: inspect language --- runtime/lua/vim/treesitter.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 1b5f416b67..3a1b1fc4b3 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -53,5 +53,9 @@ end -- TODO: weak table with reusable parser per buffer. -return {create_parser=create_parser, add_language=vim._ts_add_language} +return { + create_parser=create_parser, + add_language=vim._ts_add_language, + inspect_language=vim._ts_inspect_language, +} -- cgit From 167a1cfdef0c4b3526830ad0356f06bf480df6af Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 17 Jun 2019 21:46:31 +0200 Subject: tree-sitter: improve parser API (shared parser between plugins) --- runtime/lua/vim/treesitter.lua | 57 +++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 15 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 3a1b1fc4b3..f26d63d6ce 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -3,8 +3,13 @@ local a = vim.api local Parser = {} Parser.__index = Parser -function Parser:parse_tree(force) - if self.valid and not force then +-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. +-- Consider use weak references to release parser if all plugins are done with +-- it. +local parsers = {} + +function Parser:parse() + if self.valid then return self.tree end self.tree = self._parser:parse_buf(self.bufnr) @@ -12,7 +17,7 @@ function Parser:parse_tree(force) return self.tree end -local function change_cb(self, ev, bufnr, tick, start_row, oldstopline, stop_row) +local function on_lines(self, bufnr, _, start_row, oldstopline, stop_row) local start_byte = a.nvim_buf_get_offset(bufnr,start_row) -- a bit messy, should we expose edited but not reparsed tree? -- are multiple edits safe in general? @@ -21,41 +26,63 @@ local function change_cb(self, ev, bufnr, tick, start_row, oldstopline, stop_row local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) if inode == nil then local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) - self._parser:edit(start_byte,stop_byte,stop_byte,start_row,0,stop_row,0,stop_row,0) + self._parser:edit(start_byte,stop_byte,stop_byte, + start_row,0,stop_row,0,stop_row,0) else local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() local fake_rows = fakeoldstoprow-oldstopline local fakestop = stop_row+fake_rows local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol - self._parser:edit(start_byte,fakebyteoldstop,fakebytestop,start_row,0,fakeoldstoprow,fakeoldstopcol,fakestop,fakeoldstopcol) + self._parser:edit(start_byte, fakebyteoldstop, fakebytestop, + start_row, 0, + fakeoldstoprow, fakeoldstopcol, + fakestop, fakeoldstopcol) end self.valid = false end -local function create_parser(bufnr, ft) +local function create_parser(bufnr, ft, id) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end - if ft == nil then - ft = a.nvim_buf_get_option(bufnr, "filetype") - end local self = setmetatable({bufnr=bufnr, valid=false}, Parser) self._parser = vim._create_ts_parser(ft) - self:parse_tree() - local function cb(ev, ...) + self:parse() -- TODO: use weakref to self, so that the parser is free'd is no plugin is -- using it. - return change_cb(self, ev, ...) + local function lines_cb(ev, ...) + return on_lines(self, ...) end - a.nvim_buf_attach(self.bufnr, false, {on_lines=cb}) + local detach_cb = nil + if id ~= nil then + detach_cb = function() + if parsers[id] == self then + parsers[id] = nil + end + end + end + a.nvim_buf_attach(self.bufnr, false, {on_lines=lines_cb, on_detach=detach_cb}) return self end --- TODO: weak table with reusable parser per buffer. +local function get_parser(bufnr, ft) + if bufnr == nil or bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + if ft == nil then + ft = a.nvim_buf_get_option(bufnr, "filetype") + end + local id = tostring(bufnr)..'_'..ft + + if parsers[id] == nil then + parsers[id] = create_parser(bufnr, ft, id) + end + return parsers[id] +end return { + get_parser=get_parser, create_parser=create_parser, add_language=vim._ts_add_language, inspect_language=vim._ts_inspect_language, } - -- cgit From 06ee45b9b1c14c7ce6cb23403cdbe2852d495cad Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 21 Jun 2019 14:14:51 +0200 Subject: tree-sitter: fix lint, delete "demo" plugin (replaced by functional tests) --- runtime/lua/treesitter_demo.lua | 58 ----------------------------------------- runtime/lua/vim/treesitter.lua | 2 +- runtime/plugin/ts_test.vim | 32 ----------------------- 3 files changed, 1 insertion(+), 91 deletions(-) delete mode 100644 runtime/lua/treesitter_demo.lua delete mode 100644 runtime/plugin/ts_test.vim (limited to 'runtime') diff --git a/runtime/lua/treesitter_demo.lua b/runtime/lua/treesitter_demo.lua deleted file mode 100644 index 82c36f94c0..0000000000 --- a/runtime/lua/treesitter_demo.lua +++ /dev/null @@ -1,58 +0,0 @@ --- TODO: externalize this -local a = vim.api -_G.a = vim.api - -if __treesitter_rt_ns == nil then - __treesitter_rt_ns = a.nvim_create_namespace("treesitter_demp") -end -local my_ns = __treesitter_rt_ns - -function ts_inspect_pos(row,col) - local tree = theparser:parse_tree() - local root = tree:root() - local node = root:descendant_for_point_range(row,col,row,col) - show_node(node) -end - -function show_node(node) - if node == nil then - return - end - a.nvim_buf_clear_highlight(0, my_ns, 0, -1) - shown_node = node - print(node:type()) - local start_row, start_col, end_row, end_col = node:range() - - a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", start_row, start_col, start_col+1) - - if end_col >= 1 then - end_col = end_col - 1 - end - a.nvim_buf_add_highlight(0, my_ns, "ErrorMsg", end_row, end_col, end_col+1) -end - -function ts_expand_node() - if shown_node == nil then - return - end - parent = shown_node:parent() - show_node(parent) -end - -function ts_cursor() - local row, col = unpack(a.nvim_win_get_cursor(0)) - ts_inspect_pos(row-1, col) -end - -if false then - ctree = theparser.tree - root = ctree:root() - cursor = root:to_cursor() - node = cursor:forward(5000) if true then return node end - print(#root) - c = root:child(50) - print(require'inspect'{c:extent()}) - type(ctree.__tostring) - root:__tostring() - print(_tslua_debug()) -end diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index f26d63d6ce..8aa170061b 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -50,7 +50,7 @@ local function create_parser(bufnr, ft, id) self:parse() -- TODO: use weakref to self, so that the parser is free'd is no plugin is -- using it. - local function lines_cb(ev, ...) + local function lines_cb(_, ...) return on_lines(self, ...) end local detach_cb = nil diff --git a/runtime/plugin/ts_test.vim b/runtime/plugin/ts_test.vim deleted file mode 100644 index 15192d8dda..0000000000 --- a/runtime/plugin/ts_test.vim +++ /dev/null @@ -1,32 +0,0 @@ -let g:ts_test_path = expand(":p:h:h") -let g:has_ts = v:false - -func! TSTest() - if g:has_ts - return - end - " TODO: module! - lua theparser = vim.treesitter.create_parser(0) - lua require'treesitter_demo' - let g:has_ts = v:true -endfunc - -func! TSCursor() - " disable matchparen - NoMatchParen - call TSTest() - au CursorMoved lua ts_cursor() - au CursorMovedI lua ts_cursor() - map (ts-expand) lua ts_expand_node() -endfunc - -func! TSSyntax() - " disable matchparen - set syntax= - call TSTest() - lua ts_syntax() -endfunc - -command! TSTest call TSTest() -command! TSCursor call TSCursor() -command! TSSyntax call TSSyntax() -- cgit From f86a2c33a2e54193598be1d8c856363e02b91e37 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 5 Aug 2019 19:37:17 +0200 Subject: tree-sitter: simplify editing using the new old_byte_size parameter --- runtime/lua/vim/treesitter.lua | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 8aa170061b..69b1ac8716 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -17,27 +17,12 @@ function Parser:parse() return self.tree end -local function on_lines(self, bufnr, _, start_row, oldstopline, stop_row) +local function on_lines(self, bufnr, _, start_row, old_stop_row, stop_row, old_byte_size) local start_byte = a.nvim_buf_get_offset(bufnr,start_row) - -- a bit messy, should we expose edited but not reparsed tree? - -- are multiple edits safe in general? - local root = self._parser:tree():root() - -- TODO: add proper lookup function! - local inode = root:descendant_for_point_range(oldstopline+9000,0, oldstopline,0) - if inode == nil then - local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) - self._parser:edit(start_byte,stop_byte,stop_byte, - start_row,0,stop_row,0,stop_row,0) - else - local fakeoldstoprow, fakeoldstopcol, fakebyteoldstop = inode:start() - local fake_rows = fakeoldstoprow-oldstopline - local fakestop = stop_row+fake_rows - local fakebytestop = a.nvim_buf_get_offset(bufnr,fakestop)+fakeoldstopcol - self._parser:edit(start_byte, fakebyteoldstop, fakebytestop, - start_row, 0, - fakeoldstoprow, fakeoldstopcol, - fakestop, fakeoldstopcol) - end + local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) + local old_stop_byte = start_byte + old_byte_size + self._parser:edit(start_byte,old_stop_byte,stop_byte, + start_row,0,old_stop_row,0,stop_row,0) self.valid = false end -- cgit From 3ffcb477ef1e4ae0a7183a934382ffd2c449d818 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 21 Sep 2019 10:35:49 +0200 Subject: tree-sitter: start docs --- runtime/doc/if_lua.txt | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index aa2d0a03c6..0ba35aeae6 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -445,6 +445,112 @@ Example: TCP echo-server *tcp-server* end) print('TCP echo-server listening on port: '..server:getsockname().port) +------------------------------------------------------------------------------ +VIM.TREESITTER *lua-treesitter* + +Nvim integrates the tree-sitter library for incremental parsing of buffers. + +Currently Nvim does not provide the tree-sitter parsers, instead these must +be built separately, for instance using the tree-sitter utility. +The parser is loaded into nvim using > + + vim.treesitter.add_language("/path/to/c_parser.so", "c") + + + + parser = vim.treesitter.get_parser(bufnr, lang) + +<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this +doesn't work yet for some filetypes like "cpp") Currently, the parser will be +retained for the lifetime of a buffer but this is subject to change. A plugin +should keep a reference to the parser object as long as it wants incremental +updates. + +Whenever you need to access the current syntax tree, parse the buffer: > + + tstree = parser:parse() + + Date: Sat, 28 Sep 2019 14:04:05 +0200 Subject: tree-sitter: use "module" pattern in lua source --- runtime/lua/vim/treesitter.lua | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 69b1ac8716..e0202927bb 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -1,13 +1,13 @@ local a = vim.api -local Parser = {} -Parser.__index = Parser - -- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. -- Consider use weak references to release parser if all plugins are done with -- it. local parsers = {} +local Parser = {} +Parser.__index = Parser + function Parser:parse() if self.valid then return self.tree @@ -17,7 +17,7 @@ function Parser:parse() return self.tree end -local function on_lines(self, bufnr, _, start_row, old_stop_row, stop_row, old_byte_size) +function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_size) local start_byte = a.nvim_buf_get_offset(bufnr,start_row) local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row) local old_stop_byte = start_byte + old_byte_size @@ -26,17 +26,22 @@ local function on_lines(self, bufnr, _, start_row, old_stop_row, stop_row, old_b self.valid = false end -local function create_parser(bufnr, ft, id) +local module = { + add_language=vim._ts_add_language, + inspect_language=vim._ts_inspect_language, +} + +function module.create_parser(bufnr, ft, id) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end local self = setmetatable({bufnr=bufnr, valid=false}, Parser) self._parser = vim._create_ts_parser(ft) self:parse() - -- TODO: use weakref to self, so that the parser is free'd is no plugin is + -- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is -- using it. local function lines_cb(_, ...) - return on_lines(self, ...) + return self:_on_lines(...) end local detach_cb = nil if id ~= nil then @@ -50,7 +55,7 @@ local function create_parser(bufnr, ft, id) return self end -local function get_parser(bufnr, ft) +function module.get_parser(bufnr, ft) if bufnr == nil or bufnr == 0 then bufnr = a.nvim_get_current_buf() end @@ -60,14 +65,9 @@ local function get_parser(bufnr, ft) local id = tostring(bufnr)..'_'..ft if parsers[id] == nil then - parsers[id] = create_parser(bufnr, ft, id) + parsers[id] = module.create_parser(bufnr, ft, id) end return parsers[id] end -return { - get_parser=get_parser, - create_parser=create_parser, - add_language=vim._ts_add_language, - inspect_language=vim._ts_inspect_language, -} +return module -- cgit From 179c46a016388c2acead17e56d5860e667748561 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 30 Sep 2019 12:52:04 +0200 Subject: provider#pythonx: resolve/expand exe from host var (#11047) This reverts part of ade88fe4c [1]. This is required for `let g:python3_host_prog = 'python'` etc, where it should get picked up from PATH. Without this it would show: ``` - INFO: pyenv: Path: /home/user/.pyenv/libexec/pyenv - INFO: pyenv: Root: /home/user/.pyenv - INFO: Using: g:python3_host_prog = "python" - ERROR: "python" was not found. - INFO: Executable: Not found - ERROR: Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": python - ADVICE: - Use that Python version to reinstall "pynvim" and optionally "neovim". pip3 uninstall pynvim neovim pip3 install pynvim pip3 install neovim # only if needed by third-party software ``` Note that it additionally causes a weird error ("Detected pip upgrade failure"), due to `s:check_bin` emptying `python_exe` (because the non-absolute file not being readable), and `provider#pythonx#DetectByModule('pynvim', a:version)` from 75593e6fce then just getting the value from the host var again (without actual checks). This is implicitly fixed via this patch now (because it is skipped), but could need some improvement in this regard probably. With this patch it resolves it (for a virtualenv where pynvim is not made available intentionally): ``` - INFO: pyenv: Path: /home/daniel/.pyenv/libexec/pyenv - INFO: pyenv: Root: /home/daniel/.pyenv - INFO: Using: g:python3_host_prog = "python" - WARNING: $VIRTUAL_ENV exists but appears to be inactive. This could lead to unexpected results. - ADVICE: - If you are using Zsh, see: http://vi.stackexchange.com/a/7654 - INFO: Executable: /home/daniel/.pyenv/shims/tmp-system-deoplete.nvim-f205aF/python - ERROR: Command error (job=11, exit code 1): `'/home/daniel/.pyenv/shims/tmp-system-deoplete.nvim-f205aF/python' -c 'import sys; sys.path.remove(""); import neovim; print(neovim.__file__)'` (in '/home/daniel/.dotfiles/vim/plugged/deoplete.nvim') Output: Traceback (most recent call last): File "", line 1, in ModuleNotFoundError: No module named 'neovim' Stderr: Traceback (most recent call last): File "", line 1, in ModuleNotFoundError: No module named 'neovim' - INFO: Python version: 3.7.4 - INFO: pynvim version: unable to load neovim Python module - ERROR: pynvim is not installed. Error: unable to load neovim Python module - ADVICE: - Run in shell: pip3 install pynvim ``` Note: this appears to display the error twice via "Output:" and "Stderr:". 1: https://github.com/neovim/neovim/pull/8784 --- runtime/autoload/health/provider.vim | 2 +- runtime/autoload/provider/pythonx.vim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index f52c2c2cbf..61858193c3 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -298,7 +298,7 @@ function! s:check_python(version) abort let python_exe = pyname endif - " No Python executable could `import neovim`. + " No Python executable could `import neovim`, or host_prog_var was used. if !empty(pythonx_errors) call health#report_error('Python provider error:', pythonx_errors) diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 59b1c27b72..6ce7165467 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -43,7 +43,7 @@ function! provider#pythonx#DetectByModule(module, major_version) abort let python_exe = s:get_python_executable_from_host_var(a:major_version) if !empty(python_exe) - return [python_exe, ''] + return [exepath(expand(python_exe)), ''] endif let candidates = s:get_python_candidates(a:major_version) -- cgit From dd26bd59745c9fd358624312feb315ec0f106de8 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 1 Sep 2019 11:13:13 +0200 Subject: screen: don't crash on invalid grid cells being recomposed --- runtime/doc/options.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d87898bb89..6b9f0380f6 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4583,6 +4583,14 @@ A jump table for the options with a short description can be found at |Q_op|. RedrawDebugRecompose guibg=Red redraw generated by the compositor itself, due to a grid being moved or deleted. + nothrottle Turn off throttling of the message grid. This is an + optimization that joins many small scrolls to one + larger scroll when drawing the message area (with + 'display' msgsep flag active). + invalid Enable stricter checking (abort) of inconsistencies + of the internal screen state. This is mosly + useful when running nvim inside a debugger (and + the test suite). *'redrawtime'* *'rdt'* 'redrawtime' 'rdt' number (default 2000) -- cgit From 17e96d96bac4ea9074a337b263fe85d4755106b6 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 24 Sep 2019 22:53:20 -0400 Subject: vim-patch:8.1.0514: CTRL-W ^ does not work when alternate buffer has no name Problem: CTRL-W ^ does not work when alternate buffer has no name. Solution: Use another method to split and edit the alternate buffer. (Jason Franklin) https://github.com/vim/vim/commit/1bbb61948342b5cf6e363629f145c65eb455c388 --- runtime/doc/windows.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 76bb096ee3..977e0daef7 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -201,9 +201,11 @@ CTRL-W CTRL_N *CTRL-W_CTRL-N* |:find|. Doesn't split if {file} is not found. CTRL-W CTRL-^ *CTRL-W_CTRL-^* *CTRL-W_^* -CTRL-W ^ Does ":split #", split window in two and edit alternate file. - When a count is given, it becomes ":split #N", split window - and edit buffer N. +CTRL-W ^ Split the current window in two and edit the alternate file. + When a count N is given, split the current window and edit + buffer N. Similar to ":sp #" and ":sp #N", but it allows the + other buffer to be unnamed. This command matches the behavior + of |CTRL-^|, except that it splits a window first. CTRL-W ge *CTRL-W_ge* Detach the current window as an external window. -- cgit From efef797126506e84fadfc7214ffc55aa7c084e80 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 30 Sep 2019 20:17:11 -0400 Subject: vim-patch:8.1.2103: wrong error message if "termdebugger" is not executable Problem: wrong error message if "termdebugger" is not executable. Solution: Check if "termdebugger" is executable and give a clear error message. (Ozaki Kiichi, closes vim/vim#5000) Fix indents. https://github.com/vim/vim/commit/18223a592efa4399e3951c86deeb712a13b05ca5 --- runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index a97461ad69..52b4829f5f 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -67,8 +67,8 @@ command -nargs=* -complete=file -bang Termdebug call s:StartDebug(0, 0, ) " Name of the gdb command, defaults to "gdb". -if !exists('termdebugger') - let termdebugger = 'gdb' +if !exists('g:termdebugger') + let g:termdebugger = 'gdb' endif let s:pc_id = 12 @@ -106,9 +106,14 @@ endfunc func s:StartDebug_internal(dict) if exists('s:gdbwin') - echoerr 'Terminal debugger already running' + echoerr 'Terminal debugger already running, cannot run two' return endif + if !executable(g:termdebugger) + echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' + return + endif + let s:ptywin = 0 let s:pid = 0 -- cgit From 382391bb2de4698feb05e4c8e6c8286ccc4eaa8c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 4 Oct 2019 08:16:30 +0200 Subject: health: provider: skip checks with `g:loaded_X_provider = 0` (#11147) The Python provider was special (via [1]), and would continue to do checks with `0` being set explicitly even. This was fixed in #11044 (45447e3b6), ref: #11040. This extends it to use the same method with all providers. 1: https://github.com/neovim/neovim/pull/8047 --- runtime/autoload/health/provider.vim | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 61858193c3..c750a954fa 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -265,6 +265,22 @@ function! s:check_bin(bin) abort return 1 endfunction +" Check "loaded" var for given a:provider. +" Returns 1 if the caller should return (skip checks). +function! s:disabled_via_loaded_var(provider) abort + let loaded_var = 'g:loaded_'.a:provider.'_provider' + if exists(loaded_var) && !exists('*provider#'.a:provider.'#Call') + let v = eval(loaded_var) + if 0 is v + call health#report_info('Disabled ('.loaded_var.'='.v.').') + return 1 + else + call health#report_info('Disabled ('.loaded_var.'='.v.'). This might be due to some previous error.') + endif + endif + return 0 +endfunction + function! s:check_python(version) abort call health#report_start('Python ' . a:version . ' provider (optional)') @@ -272,15 +288,10 @@ function! s:check_python(version) abort let python_exe = '' let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' let host_prog_var = pyname.'_host_prog' - let loaded_var = 'g:loaded_'.pyname.'_provider' let python_multiple = [] - if exists(loaded_var) && !exists('*provider#'.pyname.'#Call') - let v = eval(loaded_var) - call health#report_info('Disabled ('.loaded_var.'='.v.').'.(0 is v ? '' : ' This might be due to some previous error.')) - if 0 is v - return - endif + if s:disabled_via_loaded_var(pyname) + return endif let [pyenv, pyenv_root] = s:check_for_pyenv() @@ -488,9 +499,7 @@ endfunction function! s:check_ruby() abort call health#report_start('Ruby provider (optional)') - let loaded_var = 'g:loaded_ruby_provider' - if exists(loaded_var) && !exists('*provider#ruby#Call') - call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + if s:disabled_via_loaded_var('ruby') return endif @@ -544,9 +553,7 @@ endfunction function! s:check_node() abort call health#report_start('Node.js provider (optional)') - let loaded_var = 'g:loaded_node_provider' - if exists(loaded_var) && !exists('*provider#node#Call') - call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var)) + if s:disabled_via_loaded_var('node') return endif -- cgit From a341eb608706e5e8ac691a7e8f4a9d314bafee20 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 26 Sep 2019 09:15:21 +0200 Subject: win_line: update `w_last_cursorline` always Vim patch 8.1.0856 (54d9ea6) caused a performance regression in Neovim, when `set conceallevel=1 nocursorline` was used, since then due to refactoring in 23c71d5 `w_last_cursorline` would never get updated anymore. Adds/uses `redrawdebug+=nodelta` for testing this. Fixes https://github.com/neovim/neovim/issues/11100. Closes https://github.com/neovim/neovim/pull/11101. --- runtime/doc/options.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 6b9f0380f6..8518d989d6 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4588,9 +4588,11 @@ A jump table for the options with a short description can be found at |Q_op|. larger scroll when drawing the message area (with 'display' msgsep flag active). invalid Enable stricter checking (abort) of inconsistencies - of the internal screen state. This is mosly + of the internal screen state. This is mostly useful when running nvim inside a debugger (and the test suite). + nodelta Send all internally redrawn cells to the UI, even if + they are unchanged from the already displayed state. *'redrawtime'* *'rdt'* 'redrawtime' 'rdt' number (default 2000) @@ -6916,7 +6918,6 @@ A jump table for the options with a short description can be found at |Q_op|. global The number of milliseconds to wait for each character sent to the screen. When positive, characters are sent to the UI one by one. - When negative, all redrawn characters cause a delay, even if the - character already was displayed by the UI. For debugging purposes. + See 'redrawdebug' for more options. For debugging purposes. vim:tw=78:ts=8:noet:ft=help:norl: -- cgit From d1abd6513e95a41e41ad570038310087e97f3bb1 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 6 Oct 2019 08:03:47 -0400 Subject: vim-patch:8.1.0586: :digraph output is not easy to read Problem: :digraph output is not easy to read. Solution: Add highlighting for :digraphs. (Marcin Szamotulski, closes vim/vim#3572) Also add section headers for :digraphs!. https://github.com/vim/vim/commit/eae8ae1b2b4e532b125077d9838b70d966891be3 --- runtime/doc/digraph.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt index b106e625f2..7f807b5eee 100644 --- a/runtime/doc/digraph.txt +++ b/runtime/doc/digraph.txt @@ -20,7 +20,9 @@ An alternative is using the 'keymap' option. 1. Defining digraphs *digraphs-define* *:dig* *:digraphs* -:dig[raphs] show currently defined digraphs. +:dig[raphs][!] Show currently defined digraphs. + With [!] headers are used to make it a bit easier to + find a specific character. *E104* *E39* :dig[raphs] {char1}{char2} {number} ... Add digraph {char1}{char2} to the list. {number} is -- cgit From b1ada8ec2159fbc69b58cc40eb62a4e76edd8d45 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sun, 22 Sep 2019 21:47:24 +0200 Subject: vim-patch:8.1.1354: getting a list of text lines is clumsy Problem: Getting a list of text lines is clumsy. Solution: Add the =<< assignment. (Yegappan Lakshmanan, closes vim/vim#4386) https://github.com/vim/vim/commit/f5842c5a533346c4ff41ff666e465c85f1de35d5 --- runtime/doc/eval.txt | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 5e6bfd0dbc..607e88b7c8 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -9779,6 +9779,44 @@ This does NOT work: > Like above, but append/add/subtract the value for each |List| item. + *:let=<<* *:let-heredoc* *E990* *E991* +:let {var-name} =<< [trim] {marker} +text... +text... +{marker} + Set internal variable {var-name} to a List containing + the lines of text bounded by the string {marker}. + {marker} must not contain white space. + The last line should end only with the {marker} string + without any other character. Watch out for white + space after {marker}! + If {marker} is not supplied, then "." is used as the + default marker. + + Any white space characters in the lines of text are + preserved. If "trim" is specified before {marker}, + then all the leading indentation exactly matching the + leading indentation before `let` is stripped from the + input lines and the line containing {marker}. Note + that the difference between space and tab matters + here. + + If {var-name} didn't exist yet, it is created. + Cannot be followed by another command, but can be + followed by a comment. + + Examples: > + let var1 =<< END + Sample text 1 + Sample text 2 + Sample text 3 + END + + let data =<< trim DATA + 1 2 3 4 + 5 6 7 8 + DATA +< *E121* :let {var-name} .. List the value of variable {var-name}. Multiple variable names may be given. Special names recognized -- cgit From 51f2826f617532aaf5d682dfc3229f3723427ce6 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 10 Oct 2019 04:16:02 -0400 Subject: doc: update shellquote for powershell #11122 shellquote is not treated like shellxquote for non-quote values. --- runtime/doc/options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 8518d989d6..971c4ffbd5 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5169,7 +5169,7 @@ A jump table for the options with a short description can be found at |Q_op|. unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* To use powershell (on Windows): > - set shell=powershell shellquote=( shellpipe=\| shellxquote= + set shell=powershell shellquote= shellpipe=\| shellxquote= set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command set shellredir=\|\ Out-File\ -Encoding\ UTF8 -- cgit From 6c012b0624935b93e92a0b12d86d49ef695210ba Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 12 Oct 2019 09:48:48 +0200 Subject: vim-patch:8.1.1585: :let-heredoc does not trim enough Problem: :let-heredoc does not trim enough. Solution: Trim indent from the contents based on the indent of the first line. Use let-heredoc in more tests. https://github.com/vim/vim/commit/e7eb92708ec2092a2fc11e78703b5dcf83844412 --- runtime/doc/eval.txt | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 607e88b7c8..8cdaef007c 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -9793,13 +9793,24 @@ text... If {marker} is not supplied, then "." is used as the default marker. - Any white space characters in the lines of text are - preserved. If "trim" is specified before {marker}, - then all the leading indentation exactly matching the - leading indentation before `let` is stripped from the - input lines and the line containing {marker}. Note - that the difference between space and tab matters - here. + Without "trim" any white space characters in the lines + of text are preserved. If "trim" is specified before + {marker}, then indentation is stripped so you can do: > + let text =<< trim END + if ok + echo 'done' + endif + END +< Results in: ["if ok", " echo 'done'", "endif"] + The marker must line up with "let" and the indentation + of the first line is removed from all the text lines. + Specifically: all the leading indentation exactly + matching the leading indentation of the first + non-empty text line is stripped from the input lines. + All leading indentation exactly matching the leading + indentation before `let` is stripped from the line + containing {marker}. Note that the difference between + space and tab matters here. If {var-name} didn't exist yet, it is created. Cannot be followed by another command, but can be -- cgit From 3b894b1cb18a9d4e399ab5b55004767f63a384c3 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Sat, 12 Oct 2019 23:47:00 +0200 Subject: vim-patch:8.1.1723: heredoc assignment has no room for new features Problem: Heredoc assignment has no room for new features. (FUJIWARA Takuya) Solution: Require the marker does not start with a lower case character. (closes vim/vim#4705) https://github.com/vim/vim/commit/24582007294b0db3be9669d3b583ea45fc4f19b8 --- runtime/doc/eval.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 8cdaef007c..77b6ee24a4 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -9779,19 +9779,18 @@ This does NOT work: > Like above, but append/add/subtract the value for each |List| item. - *:let=<<* *:let-heredoc* *E990* *E991* + *:let=<<* *:let-heredoc* + *E990* *E991* *E172* *E221* :let {var-name} =<< [trim] {marker} text... text... {marker} Set internal variable {var-name} to a List containing the lines of text bounded by the string {marker}. - {marker} must not contain white space. + {marker} cannot start with a lower case character. The last line should end only with the {marker} string without any other character. Watch out for white space after {marker}! - If {marker} is not supplied, then "." is used as the - default marker. Without "trim" any white space characters in the lines of text are preserved. If "trim" is specified before -- cgit From 1e4a9f9993a26a1495d1a3bdfd80fe079127ba83 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 18 Oct 2019 23:20:15 -0400 Subject: vim-patch:8.1.2175: meson files are not recognized Problem: Meson files are not recognized. Solution: Add the meson filetype. (Liam Beguin , Nirbheek Chauhan, closes vim/vim#5056) Also recognize hollywood. https://github.com/vim/vim/commit/c3bf7b56f2703e2d6f36dfb05fd32b5b43ce3c3f --- runtime/filetype.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 6abf9da55f..bbf9a91e2d 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -975,6 +975,9 @@ au BufNewFile,BufRead hg-editor-*.txt setf hgcommit " Mercurial config (looks like generic config file) au BufNewFile,BufRead *.hgrc,*hgrc setf cfg +" Meson Build system config +au BufNewFile,BufRead meson.build,meson_options.txt setf meson + " Messages (logs mostly) au BufNewFile,BufRead */log/{auth,cron,daemon,debug,kern,lpr,mail,messages,news/news,syslog,user}{,.log,.err,.info,.warn,.crit,.notice}{,.[0-9]*,-[0-9]*} setf messages -- cgit From 437fe261ab93e5b366fdcd095ccac7be1235b0eb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 19 Oct 2019 11:55:23 -0400 Subject: vim-patch:8.1.2177: Dart files are not recognized Problem: Dart files are not recognized. Solution: Add a filetype rule. (Eugene Ciurana, closes vim/vim#5087) https://github.com/vim/vim/commit/afbdb905c37675851e79d21239f502cd8e4ced9e --- runtime/filetype.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index bbf9a91e2d..8ce45b6a50 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -421,6 +421,9 @@ au BufNewFile,BufRead *.csp,*.fdr setf csp au BufNewFile,BufRead *.pld setf cupl au BufNewFile,BufRead *.si setf cuplsim +" Dart +au BufRead,BufNewfile *.dart,*.drt setf dart + " Debian Control au BufNewFile,BufRead */debian/control setf debcontrol au BufNewFile,BufRead control -- cgit From 3de4dc539ae938c5fdeddbdf25722fd1f6d9c77c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sat, 19 Oct 2019 23:11:31 +0200 Subject: vim-patch:8.1.2180: Error E303 is not useful when 'directory' is empty (#11257) Problem: Error E303 is not useful when 'directory' is empty. Solution: Skip the error message. (Daniel Hahler, vim/vim#5067) https://github.com/vim/vim/commit/00e192becd50a38cb21a1bc3f86fcc7a21f8ee88 --- runtime/doc/message.txt | 3 ++- runtime/doc/options.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index e8c76adad4..965b062728 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -556,7 +556,8 @@ allowed for the command that was used. Vim was not able to create a swap file. You can still edit the file, but if Vim unexpectedly exits the changes will be lost. And Vim may consume a lot of memory when editing a big file. You may want to change the 'directory' option -to avoid this error. See |swap-file|. +to avoid this error. This error is not given when 'directory' is empty. See +|swap-file|. *E140* > Use ! to write partial buffer diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 971c4ffbd5..ee4625a9e0 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1978,7 +1978,7 @@ A jump table for the options with a short description can be found at |Q_op|. possible. If it is not possible in any directory, but last directory listed in the option does not exist, it is created. - Empty means that no swap file will be used (recovery is - impossible!). + impossible!) and no |E303| error will be given. - A directory "." means to put the swap file in the same directory as the edited file. On Unix, a dot is prepended to the file name, so it doesn't show in a directory listing. On MS-Windows the "hidden" -- cgit From 019c8d13dd7056725c0715dc15e451118b767b7d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 19 Oct 2019 18:04:08 -0700 Subject: build/doc/CI: remove/update quickbuild references #11258 --- runtime/doc/if_lua.txt | 4 ++-- runtime/doc/intro.txt | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 0ba35aeae6..8528085f47 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -361,7 +361,7 @@ Note that underscore-prefixed functions (e.g. "_os_proc_children") are internal/private and must not be used by plugins. ------------------------------------------------------------------------------ -VIM.API *lua-api* +VIM.API *lua-api* *vim.api* `vim.api` exposes the full Nvim |API| as a table of Lua functions. @@ -371,7 +371,7 @@ Example: to use the "nvim_get_current_line()" API function, call print(tostring(vim.api.nvim_get_current_line())) ------------------------------------------------------------------------------ -VIM.LOOP *lua-loop* +VIM.LOOP *lua-loop* *vim.loop* `vim.loop` exposes all features of the Nvim event-loop. This is a low-level API that provides functionality for networking, filesystem, and process diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 887ef764bd..3292489eda 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -378,11 +378,11 @@ notation meaning equivalent decimal value(s) ~ keypad = *keypad-equal* keypad Enter *keypad-enter* - keypad 0 to 9 *keypad-0* *keypad-9* - shift-key *shift* * control-key *control* *ctrl* * alt-key or meta-key *META* *ALT* * same as * command-key or "super" key * shift-key *shift* * control-key *control* *ctrl* * alt-key or meta-key *META* *ALT* * same as * command-key or "super" key * Date: Thu, 10 Oct 2019 22:06:45 +0100 Subject: vim-patch:8.1.1228: not possible to process tags with a function Problem: Not possible to process tags with a function. Solution: Add tagfunc() (Christian Brabandt, Andy Massimino, closes vim/vim#4010) https://github.com/vim/vim/commit/45e18cbdc40afd8144d20dcc07ad2d981636f4c9 --- runtime/doc/options.txt | 8 ++++++ runtime/doc/tagsrch.txt | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ runtime/optwin.vim | 5 ++++ 3 files changed, 79 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 971c4ffbd5..65d2a7652c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6159,6 +6159,14 @@ A jump table for the options with a short description can be found at |Q_op|. match Match case smart Ignore case unless an upper case letter is used + *'tagfunc'* *'tfu'* + 'tagfunc' 'tfu' string (default: empty) + local to buffer + This option specifies a function to be used to perform tag searches. + The function gets the tag pattern and should return a List of matching + tags. See |tag-function| for an explanation of how to write the + function and an example. + *'taglength'* *'tl'* 'taglength' 'tl' number (default 0) global diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index bb3134feb6..b011db3dd3 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -838,4 +838,70 @@ Common arguments for the commands above: < For a ":djump", ":dsplit", ":dlist" and ":dsearch" command the pattern is used as a literal string, not as a search pattern. +============================================================================== +7. Using 'tagfunc' *tag-function* + +It is possible to provide Vim with a function which will generate a list of +tags used for commands like |:tag|, |:tselect| and Normal mode tag commands +like |CTRL-]|. + +The function used for generating the taglist is specified by setting the +'tagfunc' option. The function will be called with three arguments: + a:pattern The tag identifier used during the tag search. + a:flags List of flags to control the function behavior. + a:info Dict containing the following entries: + buf_ffname Full filename which can be used for priority. + user_data Custom data String, if stored in the tag + stack previously by tagfunc. + +Currently two flags may be passed to the tag function: + 'c' The function was invoked by a normal command being processed + (mnemonic: the tag function may use the context around the + cursor to perform a better job of generating the tag list.) + 'i' In Insert mode, the user was completing a tag (with + |i_CTRL-X_CTRL-]|). + +Note that when 'tagfunc' is set, the priority of the tags described in +|tag-priority| does not apply. Instead, the priority is exactly as the +ordering of the elements in the list returned by the function. + *E987* +The function should return a List of Dict entries. Each Dict must at least +include the following entries and each value must be a string: + name Name of the tag. + filename Name of the file where the tag is defined. It is + either relative to the current directory or a full path. + cmd Ex command used to locate the tag in the file. This + can be either an Ex search pattern or a line number. +Note that the format is similar to that of |taglist()|, which makes it possible +to use its output to generate the result. +The following fields are optional: + kind Type of the tag. + user_data String of custom data stored in the tag stack which + can be used to disambiguate tags between operations. + +If the function returns |v:null| instead of a List, a standard tag lookup will +be performed instead. + +It is not allowed to change the tagstack from inside 'tagfunc'. *E986* + +The following is a hypothetical example of a function used for 'tagfunc'. It +uses the output of |taglist()| to generate the result: a list of tags in the +inverse order of file names. +> + function! TagFunc(pattern, flags, info) + function! CompareFilenames(item1, item2) + let f1 = a:item1['filename'] + let f2 = a:item2['filename'] + return f1 >=# f2 ? + \ -1 : f1 <=# f2 ? 1 : 0 + endfunction + + let result = taglist(a:pattern) + call sort(result, "CompareFilenames") + + return result + endfunc + set tagfunc=TagFunc +< + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 51b2bbb583..6b3328a5d4 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -300,6 +300,11 @@ call append("$", "tagstack\ta :tag command will use the tagstack") call BinOptionG("tgst", &tgst) call append("$", "showfulltag\twhen completing tags in Insert mode show more info") call BinOptionG("sft", &sft) +if has("eval") + call append("$", "tagfunc\ta function to be used to perform tag searches") + call append("$", "\t(local to buffer)") + call OptionL("tfu") +endif if has("cscope") call append("$", "cscopeprg\tcommand for executing cscope") call OptionG("csprg", &csprg) -- cgit From c2fc4255f92157b65a2f0d5b299027195541d6b8 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Wed, 23 Oct 2019 10:50:42 +0900 Subject: runtime: Use module pattern with vim/shared.lua It's a bit cumbersome for us to add an export target every time we define a new function. It's also cumbersome to care about the order of definition when creating a new function by referring to other functions in the module. --- runtime/lua/vim/shared.lua | 75 +++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 41 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 220b6c6c7c..7727fdbab0 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -4,34 +4,37 @@ -- test-suite. If, in the future, Nvim itself is used to run the test-suite -- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua +local vim = {} --- Returns a deep copy of the given object. Non-table objects are copied as --- in a typical Lua assignment, whereas table objects are copied recursively. --- --@param orig Table to copy --@returns New table of copied keys and (nested) values. -local function deepcopy(orig) - error(orig) -end -local function _id(v) - return v -end -local deepcopy_funcs = { - table = function(orig) - local copy = {} - for k, v in pairs(orig) do - copy[deepcopy(k)] = deepcopy(v) - end - return copy - end, - number = _id, - string = _id, - ['nil'] = _id, - boolean = _id, -} -deepcopy = function(orig) - return deepcopy_funcs[type(orig)](orig) -end +function vim.deepcopy(orig) end -- luacheck: no unused +vim.deepcopy = (function() + local function _id(v) + return v + end + + local deepcopy_funcs = { + table = function(orig) + local copy = {} + for k, v in pairs(orig) do + copy[vim.deepcopy(k)] = vim.deepcopy(v) + end + return copy + end, + number = _id, + string = _id, + ['nil'] = _id, + boolean = _id, + } + + return function(orig) + return deepcopy_funcs[type(orig)](orig) + end +end)() --- Splits a string at each instance of a separator. --- @@ -43,7 +46,7 @@ end --@param sep Separator string or pattern --@param plain If `true` use `sep` literally (passed to String.find) --@returns Iterator over the split components -local function gsplit(s, sep, plain) +function vim.gsplit(s, sep, plain) assert(type(s) == "string", string.format("Expected string, got %s", type(s))) assert(type(sep) == "string", string.format("Expected string, got %s", type(sep))) assert(type(plain) == "boolean" or type(plain) == "nil", string.format("Expected boolean or nil, got %s", type(plain))) @@ -92,8 +95,8 @@ end --@param sep Separator string or pattern --@param plain If `true` use `sep` literally (passed to String.find) --@returns List-like table of the split components. -local function split(s,sep,plain) - local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end +function vim.split(s,sep,plain) + local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end return t end @@ -102,7 +105,7 @@ end --@param t Table to check --@param value Value to compare --@returns true if `t` contains `value` -local function tbl_contains(t, value) +function vim.tbl_contains(t, value) assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) for _,v in ipairs(t) do @@ -122,7 +125,7 @@ end --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map --@param ... Two or more map-like tables. -local function tbl_extend(behavior, ...) +function vim.tbl_extend(behavior, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) end @@ -149,7 +152,7 @@ end --- --@param t List-like table --@returns Flattened copy of the given list-like table. -local function tbl_flatten(t) +function vim.tbl_flatten(t) -- From https://github.com/premake/premake-core/blob/master/src/base/table.lua local result = {} local function _tbl_flatten(_t) @@ -172,7 +175,7 @@ end --@see https://www.lua.org/pil/20.2.html --@param s String to trim --@returns String with whitespace removed from its beginning and end -local function trim(s) +function vim.trim(s) assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) return s:match('^%s*(.*%S)') or '' end @@ -182,19 +185,9 @@ end --@see https://github.com/rxi/lume --@param s String to escape --@returns %-escaped pattern string -local function pesc(s) +function vim.pesc(s) assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end -local module = { - deepcopy = deepcopy, - gsplit = gsplit, - pesc = pesc, - split = split, - tbl_contains = tbl_contains, - tbl_extend = tbl_extend, - tbl_flatten = tbl_flatten, - trim = trim, -} -return module +return vim -- cgit From f3d6d8750b70d56f92d6ece87031ed205abdfcca Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 13 Oct 2019 19:49:17 -0400 Subject: vim-patch:8.1.0345: cannot get the window id associated with the location list Problem: Cannot get the window id associated with the location list. Solution: Add the "filewinid" argument to getloclist(). (Yegappan Lakshmanan, closes vim/vim#3202) https://github.com/vim/vim/commit/c9cc9c78f21caba7ecb5c90403df5e19a57aa96a --- runtime/doc/eval.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 77b6ee24a4..d21e441888 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4553,6 +4553,10 @@ getloclist({nr},[, {what}]) *getloclist()* If the optional {what} dictionary argument is supplied, then returns the items listed in {what} as a dictionary. Refer to |getqflist()| for the supported items in {what}. + If {what} contains 'filewinid', then returns the id of the + window used to display files from the location list. This + field is applicable only when called from a location list + window. getmatches() *getmatches()* Returns a |List| with all matches previously defined for the -- cgit From ed72d9597d61f4f32162b7810dc93469bcee1ce8 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 24 Oct 2019 20:25:58 +0100 Subject: man.vim: pull out s:get_paths() --- runtime/autoload/man.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 153f1afed8..ec48d96dd6 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -360,14 +360,18 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort return s:complete(sect, sect, name) endfunction -function! s:complete(sect, psect, name) abort +function! s:get_paths(sect, name) abort try let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',') catch call s:error(v:exception) return endtry - let pages = globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) + return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) +endfunction + +function! s:complete(sect, psect, name) abort + let pages = s:get_paths(a:sect, a:name) " We remove duplicates in case the same manpage in different languages was found. return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i')) endfunction -- cgit From 63f0ca326322376271c68f51cf8908daad524339 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 22 Oct 2019 23:40:16 +0100 Subject: man.vim: use 'tagfunc' instead of remapping man#pop_tag() is also no longer used --- runtime/autoload/man.vim | 23 +++++++++++++++-------- runtime/ftplugin/man.vim | 4 ++-- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index ec48d96dd6..8825719ec7 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -264,14 +264,6 @@ function! s:push_tag() abort \ }] endfunction -function! man#pop_tag() abort - if !empty(s:tag_stack) - let tag = remove(s:tag_stack, -1) - execute 'silent' tag['buf'].'buffer' - call cursor(tag['lnum'], tag['col']) - endif -endfunction - " extracts the name and sect out of 'path/name.sect' function! s:extract_sect_and_name_path(path) abort let tail = fnamemodify(a:path, ':t') @@ -410,4 +402,19 @@ function! man#init_pager() abort endif endfunction +function! man#goto_tag(pattern, flags, info) abort + " currently no support for section completion + let sect = "" + + let candidates = s:get_paths(sect, a:pattern) + + return map(candidates, { + \ _, path -> { + \ 'name': s:extract_sect_and_name_path(path)[1], + \ 'filename': 'man://' . path, + \ 'cmd': '1' + \ } + \ }) +endfunction + call s:init() diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 6c7f095f62..b3b23833ba 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -20,13 +20,13 @@ setlocal wrap breakindent linebreak setlocal nonumber norelativenumber setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable +setlocal tagfunc=man#goto_tag + if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') nnoremap j gj nnoremap k gk nnoremap gO :call man#show_toc() - nnoremap :Man nnoremap K :Man - nnoremap :call man#pop_tag() if 1 == bufnr('%') || s:pager nnoremap q :lclose:q else -- cgit From 2f0412e61d3c5113f9c121283a7e94a294706387 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 24 Oct 2019 20:48:18 +0100 Subject: man.vim: `:Man` preserves the tag stack --- runtime/autoload/man.vim | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 8825719ec7..9280474516 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -64,8 +64,9 @@ function! man#open_page(count, count1, mods, ...) abort return endtry - call s:push_tag() - let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')') + let fullname = name.(empty(sect)?'':'('.sect.')') + call s:push_tag(fullname) + let bufname = 'man://'.fullname try set eventignore+=BufReadCmd @@ -254,14 +255,30 @@ function! s:verify_exists(sect, name) abort return s:extract_sect_and_name_path(path) + [path] endfunction -let s:tag_stack = [] +function! s:push_tag(name) abort + " emulate vim's tag pushing for cases where we don't use 'tagfunc' + if !&tagstack + return + endif + + let winnr = winnr() + let stack = gettagstack(winnr) + + let curidx = stack.curidx + let items = stack.items + + let newstack = items[0 : curidx - 1] + let newstack += [{ + \ 'bufnr': bufnr('%'), + \ 'from': getpos('.'), + \ 'matchnr': 0, + \ 'tagname': a:name, + \ }] -function! s:push_tag() abort - let s:tag_stack += [{ - \ 'buf': bufnr('%'), - \ 'lnum': line('.'), - \ 'col': col('.'), - \ }] + call settagstack(winnr, { + \ 'length': len(newstack), + \ 'items': newstack, + \ }) endfunction " extracts the name and sect out of 'path/name.sect' -- cgit From 0173bdf98be6f867d1b316d4d2ac87f7a93d95e4 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 24 Oct 2019 21:15:08 +0100 Subject: man.vim: parse the section from the tag --- runtime/autoload/man.vim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 9280474516..08c6fc1eca 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -420,10 +420,9 @@ function! man#init_pager() abort endfunction function! man#goto_tag(pattern, flags, info) abort - " currently no support for section completion - let sect = "" + let [sect, name] = man#extract_sect_and_name_ref(a:pattern) - let candidates = s:get_paths(sect, a:pattern) + let candidates = s:get_paths(sect, name) return map(candidates, { \ _, path -> { -- cgit From 99aa166cb105cb33df7bb153e93f15b509fcbc8c Mon Sep 17 00:00:00 2001 From: Joshua Rubin Date: Fri, 25 Oct 2019 10:41:22 -0600 Subject: man.vim: never switch to non-man window #11286 In order to find if there was already an open man page, the :Man command would cycle through each window to see if &ft=='man'. This triggers autocmds, e.g. BufEnter, unnecessarily and can have unexpected side-effects. Change the logic to check each window's ft without switching to it unless it is actually a man window. Signed-off-by: Joshua Rubin --- runtime/autoload/man.vim | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 153f1afed8..a9256638e1 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -284,20 +284,16 @@ function! s:extract_sect_and_name_path(path) abort endfunction function! s:find_man() abort - if &filetype ==# 'man' - return 1 - elseif winnr('$') ==# 1 - return 0 - endif - let thiswin = winnr() - while 1 - wincmd w - if &filetype ==# 'man' + let l:win = 1 + while l:win <= winnr('$') + let l:buf = winbufnr(l:win) + if getbufvar(l:buf, '&filetype', '') ==# 'man' + execute l:win.'wincmd w' return 1 - elseif thiswin ==# winnr() - return 0 endif + let l:win += 1 endwhile + return 0 endfunction function! s:error(msg) abort -- cgit From 996a057fb9b4b7d791adad19f07b2f9c53a88ab5 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 21 Oct 2019 23:46:28 +0900 Subject: lua/stdlib: adjust some validation messages #11271 close #11271 --- runtime/lua/vim/shared.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index cd6f8a04d8..220b6c6c7c 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -44,9 +44,9 @@ end --@param plain If `true` use `sep` literally (passed to String.find) --@returns Iterator over the split components local function gsplit(s, sep, plain) - assert(type(s) == "string") - assert(type(sep) == "string") - assert(type(plain) == "boolean" or type(plain) == "nil") + assert(type(s) == "string", string.format("Expected string, got %s", type(s))) + assert(type(sep) == "string", string.format("Expected string, got %s", type(sep))) + assert(type(plain) == "boolean" or type(plain) == "nil", string.format("Expected boolean or nil, got %s", type(plain))) local start = 1 local done = false @@ -103,9 +103,8 @@ end --@param value Value to compare --@returns true if `t` contains `value` local function tbl_contains(t, value) - if type(t) ~= 'table' then - error('t must be a table') - end + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + for _,v in ipairs(t) do if v == value then return true @@ -174,7 +173,7 @@ end --@param s String to trim --@returns String with whitespace removed from its beginning and end local function trim(s) - assert(type(s) == 'string', 'Only strings can be trimmed') + assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) return s:match('^%s*(.*%S)') or '' end @@ -184,7 +183,7 @@ end --@param s String to escape --@returns %-escaped pattern string local function pesc(s) - assert(type(s) == 'string') + assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end -- cgit From 8ee7c94a92598d46b488b7fe3b1a5cff6b1bf94a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 25 Aug 2019 22:01:35 +0200 Subject: lua: add vim.fn.{func} for direct access to vimL function compared to vim.api.|nvim_call_function|, this fixes some typing issues due to the indirect conversion via the API. float values are preserved as such (fixes #9389) as well as empty dicts/arrays. Ref https://github.com/norcalli/nvim.lua for the call syntax --- runtime/doc/if_lua.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 8528085f47..d527a91a93 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -588,6 +588,22 @@ vim.schedule({callback}) *vim.schedule()* Schedules {callback} to be invoked soon by the main event-loop. Useful to avoid |textlock| or other temporary restrictions. +vim.fn.{func}({...}) + Call vimL function {func} with arguments. {func} can be both builtin + functions and user functions. To call autoload functions, use the + syntax `vim.fn['some#function']({...})` + + Note: unlike vim.api.|nvim_call_function| this converts values directly + between vimL values and lua values. If the vimL function returns a + float, it will be representeted directly as a lua number. Both empty + lists and dictonaries will be represented by an empty table. + + Note: vim.fn keys are generated on demand. So `pairs(vim.fn)` + does NOT work to enumerate all functions. + +vim.call({func}, {...}) + Call vim script function {func}. Equivalent to `vim.fn[func]({...})` + vim.type_idx *vim.type_idx* Type index for use in |lua-special-tbl|. Specifying one of the values from |vim.types| allows typing the empty table (it is -- cgit From 31536ae003c0bd0ee311fc97b26ded0db8b3fa34 Mon Sep 17 00:00:00 2001 From: supermomonga Date: Mon, 28 Oct 2019 06:27:22 +0900 Subject: provider/pythonx: don't assume CWD (empty string) is in path #11304 sys.path.remove("") raises ValueError if the item is missing. https://docs.python.org/3/library/functions.html#filter: > filter(function, iterable) is equivalent to the generator expression (item > for item in iterable if function(item)) fixes #11293 --- runtime/autoload/health/provider.vim | 3 ++- runtime/autoload/provider/pythonx.vim | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index c750a954fa..ad7a614ff5 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -202,7 +202,8 @@ function! s:version_info(python) abort let nvim_path = s:trim(s:system([ \ a:python, '-c', - \ 'import sys; sys.path.remove(""); ' . + \ 'import sys; ' . + \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' . \ 'import neovim; print(neovim.__file__)'])) if s:shell_error || empty(nvim_path) return [python_version, 'unable to load neovim Python module', pypi_version, diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 6ce7165467..aec18c0508 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -10,7 +10,8 @@ function! provider#pythonx#Require(host) abort " Python host arguments let prog = (ver == '2' ? provider#python#Prog() : provider#python3#Prog()) - let args = [prog, '-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()'] + let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()'] + " Collect registered Python plugins into args let python_plugins = remote#host#PluginsForHost(a:host.name) @@ -66,7 +67,7 @@ endfunction function! s:import_module(prog, module) abort let prog_version = system([a:prog, '-c' , printf( \ 'import sys; ' . - \ 'sys.path.remove(""); ' . + \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' . \ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' . \ 'import pkgutil; ' . \ 'exit(2*int(pkgutil.get_loader("%s") is None))', -- cgit From a4d48d37c15604a96be13eb4deaedbb132be6e10 Mon Sep 17 00:00:00 2001 From: Shane Smith Date: Fri, 13 Sep 2019 22:16:12 -0400 Subject: vim-patch:8.1.1256: cannot navigate through errors relative to the cursor Problem: Cannot navigate through errors relative to the cursor. Solution: Add :cabove, :cbelow, :labove and :lbelow. (Yegappan Lakshmanan, closes vim/vim#4316) https://github.com/vim/vim/commit/3ff33114d70fc0f7e9c3187c5fec9028f6499cf3 --- runtime/doc/index.txt | 4 ++++ runtime/doc/quickfix.txt | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index be9e25113a..9e7051b437 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1163,11 +1163,13 @@ tag command action ~ |:cNfile| :cNf[ile] go to last error in previous file |:cabbrev| :ca[bbrev] like ":abbreviate" but for Command-line mode |:cabclear| :cabc[lear] clear all abbreviations for Command-line mode +|:cabove| :cabo[ve] go to error above current line |:caddbuffer| :cad[dbuffer] add errors from buffer |:caddexpr| :cadde[xpr] add errors from expr |:caddfile| :caddf[ile] add error message to current quickfix list |:call| :cal[l] call a function |:catch| :cat[ch] part of a :try command +|:cbelow| :cbe[low] got to error below current line |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbuffer| :cb[uffer] parse error messages and jump to first error |:cc| :cc go to specific error @@ -1324,12 +1326,14 @@ tag command action ~ |:lNext| :lN[ext] go to previous entry in location list |:lNfile| :lNf[ile] go to last entry in previous file |:list| :l[ist] print lines +|:labove| :lab[ove] go to location above current line |:laddexpr| :lad[dexpr] add locations from expr |:laddbuffer| :laddb[uffer] add locations from buffer |:laddfile| :laddf[ile] add locations to current location list |:last| :la[st] go to the last file in the argument list |:language| :lan[guage] set the language (locale) |:later| :lat[er] go to newer change, redo +|:lbelow| :lbe[low] go to location below current line |:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbuffer| :lb[uffer] parse locations and jump to first location |:lcd| :lc[d] change directory locally diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 3ae6d9461f..61e090cc78 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -109,6 +109,36 @@ processing a quickfix or location list command, it will be aborted. list for the current window is used instead of the quickfix list. + *:cabo* *:cabove* +:[count]cabo[ve] Go to the [count] error above the current line in the + current buffer. If [count] is omitted, then 1 is + used. If there are no errors, then an error message + is displayed. Assumes that the entries in a quickfix + list are sorted by their buffer number and line + number. If there are multiple errors on the same line, + then only the first entry is used. If [count] exceeds + the number of entries above the current line, then the + first error in the file is selected. + + *:lab* *:labove* +:[count]lab[ove] Same as ":cabove", except the location list for the + current window is used instead of the quickfix list. + + *:cbe* *:cbelow* +:[count]cbe[low] Go to the [count] error below the current line in the + current buffer. If [count] is omitted, then 1 is + used. If there are no errors, then an error message + is displayed. Assumes that the entries in a quickfix + list are sorted by their buffer number and line + number. If there are multiple errors on the same + line, then only the first entry is used. If [count] + exceeds the number of entries below the current line, + then the last error in the file is selected. + + *:lbe* *:lbelow* +:[count]lbe[low] Same as ":cbelow", except the location list for the + current window is used instead of the quickfix list. + *:cnf* *:cnfile* :[count]cnf[ile][!] Display the first error in the [count] next file in the list that includes a file name. If there are no -- cgit From 83b6d9f197a44e3b8140619e6b7e9950f217aafb Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Tue, 29 Oct 2019 05:05:49 +0100 Subject: termdebug.vim: reset evalFromBalloonExprResult #11309 The value is used again in case of a pointer and it will cause errors then. --- runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 52b4829f5f..b9fc77dc37 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -583,6 +583,7 @@ func s:HandleEvaluate(msg) endif let s:evalFromBalloonExprResult = split(s:evalFromBalloonExprResult, '\\n') call s:OpenHoverPreview(s:evalFromBalloonExprResult, v:null) + let s:evalFromBalloonExprResult = '' else echomsg '"' . s:evalexpr . '": ' . value endif -- cgit From c6afad78d39aa77a4d372759336018ef6e101dab Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 29 Oct 2019 18:41:30 +0000 Subject: man.vim: remove push_tag and simplify man#open_page --- runtime/autoload/man.vim | 50 ++++++------------------------------------------ 1 file changed, 6 insertions(+), 44 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 08c6fc1eca..ecbe4bb374 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -64,34 +64,22 @@ function! man#open_page(count, count1, mods, ...) abort return endtry - let fullname = name.(empty(sect)?'':'('.sect.')') - call s:push_tag(fullname) - let bufname = 'man://'.fullname - + let [l:buf, l:save_tfu] = [bufnr(), &tagfunc] try set eventignore+=BufReadCmd + set tagfunc=man#goto_tag + let l:target = l:name . '(' . l:sect . ')' if a:mods !~# 'tab' && s:find_man() - execute 'silent keepalt edit' fnameescape(bufname) + execute 'silent keepalt tag' l:target else - execute 'silent keepalt' a:mods 'split' fnameescape(bufname) + execute 'silent keepalt' a:mods 'stag' l:target endif finally + call setbufvar(l:buf, '&tagfunc', l:save_tfu) set eventignore-=BufReadCmd endtry - try - let page = s:get_page(path) - catch - if a:mods =~# 'tab' || !s:find_man() - " a new window was opened - close - endif - call s:error(v:exception) - return - endtry - let b:man_sect = sect - call s:put_page(page) endfunction function! man#read_page(ref) abort @@ -255,32 +243,6 @@ function! s:verify_exists(sect, name) abort return s:extract_sect_and_name_path(path) + [path] endfunction -function! s:push_tag(name) abort - " emulate vim's tag pushing for cases where we don't use 'tagfunc' - if !&tagstack - return - endif - - let winnr = winnr() - let stack = gettagstack(winnr) - - let curidx = stack.curidx - let items = stack.items - - let newstack = items[0 : curidx - 1] - let newstack += [{ - \ 'bufnr': bufnr('%'), - \ 'from': getpos('.'), - \ 'matchnr': 0, - \ 'tagname': a:name, - \ }] - - call settagstack(winnr, { - \ 'length': len(newstack), - \ 'items': newstack, - \ }) -endfunction - " extracts the name and sect out of 'path/name.sect' function! s:extract_sect_and_name_path(path) abort let tail = fnamemodify(a:path, ':t') -- cgit From 471427d045f74fa9d2e47b2975ef178e719c2ae8 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 1 Nov 2019 00:43:20 -0400 Subject: vim-patch:8.1.2231: introduce gM command #11321 Problem: Not easy to move to the middle of a text line. Solution: Add the gM command. (Yasuhiro Matsumoto, closes vim/vim#2070) https://github.com/vim/vim/commit/8b530c1ff91f07cf6b0289a536992b7dfbc86598 --- runtime/doc/index.txt | 1 + runtime/doc/motion.txt | 6 ++++++ runtime/doc/quickref.txt | 1 + runtime/doc/usr_25.txt | 9 +++++---- 4 files changed, 13 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index be9e25113a..9fd68144bc 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -767,6 +767,7 @@ tag char note action in Normal mode ~ |gn| gn 1,2 find the next match with the last used search pattern and Visually select it |gm| gm 1 go to character at middle of the screenline +|gM| gM 1 go to character at middle of the text line |go| go 1 cursor to byte N in the buffer |gp| ["x]gp 2 put the text [from register x] after the cursor N times, leave the cursor after it diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 97c7d1cc43..9e24ee0320 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -219,6 +219,12 @@ g^ When lines wrap ('wrap' on): To the first non-blank gm Like "g0", but half a screenwidth to the right (or as much as possible). + *gM* +gM Like "g0", but to halfway the text of the line. + With a count: to this percentage of text in the line. + Thus "10gM" is near the start of the text and "90gM" + is near the end of the text. + *g$* *g* g$ or g When lines wrap ('wrap' on): To the last character of the screen line and [count - 1] screen lines downward diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 87cb9b54f5..dfa7218bdf 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -47,6 +47,7 @@ N is used to indicate an optional count that can be given before the command. |g$| N g$ to last character in screen line (differs from "$" when lines wrap) |gm| gm to middle of the screen line +|gM| gM to middle of the line |bar| N | to column N (default: 1) |f| N f{char} to the Nth occurrence of {char} to the right |F| N F{char} to the Nth occurrence of {char} to the left diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt index 3a58af6412..2efb67e55f 100644 --- a/runtime/doc/usr_25.txt +++ b/runtime/doc/usr_25.txt @@ -346,12 +346,13 @@ scroll: g0 to first visible character in this line g^ to first non-blank visible character in this line - gm to middle of this line + gm to middle of screen line + gM to middle of the text in this line g$ to last visible character in this line - |<-- window -->| - some long text, part of which is visible ~ - g0 g^ gm g$ + |<-- window -->| + some long text, part of which is visible in one line ~ + g0 g^ gm gM g$ BREAKING AT WORDS *edit-no-break* -- cgit From c66297452c523ce599aad79aeea2f510094c93d4 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 22 Oct 2019 23:59:31 -0700 Subject: doc: file-change-detect [ci skip] --- runtime/doc/if_lua.txt | 30 ++++++++++++++++++++++++++++-- runtime/doc/options.txt | 16 ++++++++-------- 2 files changed, 36 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index d527a91a93..fd2630d550 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -416,20 +416,46 @@ Example: repeating timer print('sleeping'); +Example: File-change detection *file-change-detect* + 1. Save this code to a file. + 2. Execute it with ":luafile %". + 3. Use ":Watch %" to watch any file. + 4. Try editing the file from another text editor. + 5. Observe that the file reloads in Nvim (because on_change() calls + |:checktime|). > + + local w = vim.loop.new_fs_event() + local function on_change(err, fname, status) + -- Do work... + vim.api.nvim_command('checktime') + -- Debounce: stop/start. + w:stop() + watch_file(fname) + end + function watch_file(fname) + local fullpath = vim.api.nvim_call_function( + 'fnamemodify', {fname, ':p'}) + w:start(fullpath, {}, vim.schedule_wrap(function(...) + on_change(...) end)) + end + vim.api.nvim_command( + "command! -nargs=1 Watch call luaeval('watch_file(_A)', expand(''))") + + Example: TCP echo-server *tcp-server* 1. Save this code to a file. 2. Execute it with ":luafile %". 3. Note the port number. 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): > - local function create_server(host, port, on_connection) + local function create_server(host, port, on_connect) local server = vim.loop.new_tcp() server:bind(host, port) server:listen(128, function(err) assert(not err, err) -- Check for errors. local sock = vim.loop.new_tcp() server:accept(sock) -- Accept client connection. - on_connection(sock) -- Start reading messages. + on_connect(sock) -- Start reading messages. end) return server end diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e12a7d4986..52d8624935 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6159,14 +6159,14 @@ A jump table for the options with a short description can be found at |Q_op|. match Match case smart Ignore case unless an upper case letter is used - *'tagfunc'* *'tfu'* - 'tagfunc' 'tfu' string (default: empty) - local to buffer - This option specifies a function to be used to perform tag searches. - The function gets the tag pattern and should return a List of matching - tags. See |tag-function| for an explanation of how to write the - function and an example. - + *'tagfunc'* *'tfu'* +'tagfunc' 'tfu' string (default: empty) + local to buffer + This option specifies a function to be used to perform tag searches. + The function gets the tag pattern and should return a List of matching + tags. See |tag-function| for an explanation of how to write the + function and an example. + *'taglength'* *'tl'* 'taglength' 'tl' number (default 0) global -- cgit From 9ef16a1628722958b6e14fe9274006e50ed6682d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 27 Oct 2019 15:05:59 -0700 Subject: doc: vim.fn, vim.call(), vim.api [ci skip] --- runtime/doc/api.txt | 75 ++++++++++++++++++++++++++++----------------- runtime/doc/develop.txt | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ runtime/doc/eval.txt | 13 +++++--- runtime/doc/if_lua.txt | 81 ++++++++++++++++++++++++++++--------------------- runtime/doc/index.txt | 2 +- runtime/doc/motion.txt | 16 +++++----- 6 files changed, 192 insertions(+), 76 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 98dd330b48..d6e420c427 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -850,10 +850,10 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* {enter} Enter the window (make it the current window) {config} Map defining the window configuration. Keys: • `relative` : Sets the window layout to "floating", placed - at (row,col) coordinates relative to one of: + at (row,col) coordinates relative to: • "editor" The global editor grid • "win" Window given by the `win` field, or - current window by default. + current window. • "cursor" Cursor position in current window. • `win` : |window-ID| for relative="win". @@ -1476,45 +1476,66 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()* Line count, or 0 for unloaded buffer. |api-buffer| nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* - Activates buffer-update events on a channel, or as lua + Activates buffer-update events on a channel, or as Lua callbacks. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {send_buffer} Set to true if the initial notification - should contain the whole buffer. If so, the - first notification will be a - `nvim_buf_lines_event` . Otherwise, the - first notification will be a - `nvim_buf_changedtick_event` . Not used for - lua callbacks. + {send_buffer} True if the initial notification should + contain the whole buffer: first + notification will be `nvim_buf_lines_event` + . Else the first notification will be + `nvim_buf_changedtick_event` . Not for Lua + callbacks. {opts} Optional parameters. - • `on_lines` : lua callback received on - change. - • `on_changedtick` : lua callback received - on changedtick increment without text - change. - • `utf_sizes` : include UTF-32 and UTF-16 - size of the replaced region. See - |api-buffer-updates-lua| for more - information + • on_lines: Lua callback invoked on change. + Return `true` to detach. Args: + • buffer handle + • b:changedtick + • first line that changed (zero-indexed) + • last line that was changed + • last line in the updated range + • byte count of previous contents + • deleted_codepoints (if `utf_sizes` is + true) + • deleted_codeunits (if `utf_sizes` is + true) + + • on_changedtick: Lua callback invoked on + changedtick increment without text + change. Args: + • buffer handle + • b:changedtick + + • on_detach: Lua callback invoked on + detach. Args: + • buffer handle + + • utf_sizes: include UTF-32 and UTF-16 size + of the replaced region, as args to + `on_lines` . + + Return: ~ + False if attach failed (invalid parameter, or buffer isn't + loaded); otherwise True. TODO: LUA_API_NO_EVAL - Return: ~ - False when updates couldn't be enabled because the buffer - isn't loaded or `opts` contained an invalid key; otherwise - True. TODO: LUA_API_NO_EVAL + See also: ~ + |nvim_buf_detach()| + |api-buffer-updates-lua| nvim_buf_detach({buffer}) *nvim_buf_detach()* Deactivates buffer-update events on the channel. - For Lua callbacks see |api-lua-detach|. - Parameters: ~ {buffer} Buffer handle, or 0 for current buffer Return: ~ - False when updates couldn't be disabled because the buffer - isn't loaded; otherwise True. + False if detach failed (because the buffer isn't loaded); + otherwise True. + + See also: ~ + |nvim_buf_attach()| + |api-lua-detach| for detaching Lua callbacks *nvim_buf_get_lines()* nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 90c2e30771..ba887a83c8 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -143,6 +143,87 @@ DOCUMENTATION *dev-doc* /// @param dirname The path fragment before `pend` < +C docstrings ~ + +Nvim API documentation lives in the source code, as docstrings (Doxygen +comments) on the function definitions. The |api| :help is generated +from the docstrings defined in src/nvim/api/*.c. + +Docstring format: +- Lines start with `///` +- Special tokens start with `@` followed by the token name: + `@note`, `@param`, `@returns` +- Limited markdown is supported. + - List-items start with `-` (useful to nest or "indent") +- Use `
`  for code samples.
+
+Example: the help for |nvim_open_win()| is generated from a docstring defined
+in src/nvim/api/vim.c like this: >
+
+    /// Opens a new window.
+    /// ...
+    ///
+    /// Example (Lua): window-relative float
+    /// 
+    ///     vim.api.nvim_open_win(0, false,
+    ///       {relative='win', row=3, col=3, width=12, height=3})
+    /// 
+ /// + /// @param buffer Buffer to display + /// @param enter Enter the window + /// @param config Map defining the window configuration. Keys: + /// - relative: Sets the window layout, relative to: + /// - "editor" The global editor grid. + /// - "win" Window given by the `win` field. + /// - "cursor" Cursor position in current window. + /// ... + /// @param[out] err Error details, if any + /// + /// @return Window handle, or 0 on error + + +Lua docstrings ~ + *dev-lua-doc* +Lua documentation lives in the source code, as docstrings on the function +definitions. The |lua-vim| :help is generated from the docstrings. + +Docstring format: +- Lines in the main description start with `---` +- Special tokens start with `--@` followed by the token name: + `--@see`, `--@param`, `--@returns` +- Limited markdown is supported. + - List-items start with `-` (useful to nest or "indent") +- Use `
`  for code samples.
+
+Example: the help for |vim.paste()| is generated from a docstring decorating
+vim.paste in src/nvim/lua/vim.lua like this: >
+
+    --- Paste handler, invoked by |nvim_paste()| when a conforming UI
+    --- (such as the |TUI|) pastes text into the editor.
+    ---
+    --- Example: To remove ANSI color codes when pasting:
+    --- 
+    --- vim.paste = (function()
+    ---   local overridden = vim.paste
+    ---   ...
+    --- end)()
+    --- 
+ --- + --@see |paste| + --- + --@param lines ... + --@param phase ... + --@returns false if client should cancel the paste. + + +LUA *dev-lua* + +- Keep the core Lua modules |lua-stdlib| simple. Avoid elaborate OOP or + pseudo-OOP designs. Plugin authors just want functions to call, they don't + want to learn a big, fancy inheritance hierarchy. So we should avoid complex + objects: tables are usually better. + + API *dev-api* Use this template to name new API functions: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d21e441888..1eb873a5b4 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1217,7 +1217,7 @@ lambda expression *expr-lambda* *lambda* {args -> expr1} lambda expression A lambda expression creates a new unnamed function which returns the result of -evaluating |expr1|. Lambda expressions differ from |user-functions| in +evaluating |expr1|. Lambda expressions differ from |user-function|s in the following ways: 1. The body of the lambda expression is an |expr1| and not a sequence of |Ex| @@ -1986,9 +1986,12 @@ v:windowid Application-specific window "handle" which may be set by any |window-ID|. ============================================================================== -4. Builtin Functions *functions* +4. Builtin Functions *vim-function* *functions* -See |function-list| for a list grouped by what the function is used for. +The Vimscript subsystem (referred to as "eval" internally) provides the +following builtin functions. Scripts can also define |user-function|s. + +See |function-list| to browse functions by topic. (Use CTRL-] on the function name to jump to the full explanation.) @@ -3543,7 +3546,7 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is string) *funcname built-in function (see |functions|) or user defined function (see - |user-functions|). Also works for a + |user-function|). Also works for a variable that is a Funcref. varname internal variable (see |internal-variables|). Also works @@ -9243,7 +9246,7 @@ Don't forget that "^" will only match at the first character of the String and "\n". ============================================================================== -5. Defining functions *user-functions* +5. Defining functions *user-function* New functions can be defined. These can be called just like builtin functions. The function executes a sequence of Ex commands. Normal mode diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index fd2630d550..97d851a20f 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -285,7 +285,7 @@ Example: > Lua tables are used as both dictionaries and lists, so it is impossible to determine whether empty table is meant to be empty list or empty dictionary. -Additionally lua does not have integer numbers. To distinguish between these +Additionally Lua does not have integer numbers. To distinguish between these cases there is the following agreement: 0. Empty table is empty list. @@ -300,7 +300,7 @@ cases there is the following agreement: 4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point value: - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to - a floating-point 1.0. Note that by default integral lua numbers are + a floating-point 1.0. Note that by default integral Lua numbers are converted to |Number|s, non-integral are converted to |Float|s. This variant allows integral |Float|s. - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty @@ -321,10 +321,10 @@ Examples: > : endfunction :echo Rand(1,10) -Note that currently second argument to `luaeval` undergoes VimL to lua -conversion, so changing containers in lua do not affect values in VimL. Return -value is also always converted. When converting, |msgpack-special-dict|s are -treated specially. +Note: second argument to `luaeval` undergoes VimL to Lua conversion +("marshalled"), so changes to Lua containers do not affect values in VimL. +Return value is also always converted. When converting, +|msgpack-special-dict|s are treated specially. ============================================================================== Lua standard modules *lua-stdlib* @@ -355,21 +355,11 @@ Result is something like this: > To find documentation on e.g. the "deepcopy" function: > - :help vim.deepcopy + :help vim.deepcopy() Note that underscore-prefixed functions (e.g. "_os_proc_children") are internal/private and must not be used by plugins. ------------------------------------------------------------------------------- -VIM.API *lua-api* *vim.api* - -`vim.api` exposes the full Nvim |API| as a table of Lua functions. - -Example: to use the "nvim_get_current_line()" API function, call -"vim.api.nvim_get_current_line()": > - - print(tostring(vim.api.nvim_get_current_line())) - ------------------------------------------------------------------------------ VIM.LOOP *lua-loop* *vim.loop* @@ -503,7 +493,7 @@ it should call `parse()` again. If the buffer wasn't edited, the same tree will be returned again without extra work. If the buffer was parsed before, incremental parsing will be done of the changed parts. -NB: to use the parser directly inside a |nvim_buf_attach| lua callback, you must +NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must call `get_parser()` before you register your callback. But preferably parsing shouldn't be done directly in the change callback anyway as they will be very frequent. Rather a plugin that does any kind of analysis on a tree should use @@ -578,7 +568,17 @@ tsnode:named_descendant_for_range(start_row, start_col, end_row, end_col) range of (row, column) positions ------------------------------------------------------------------------------ -VIM *lua-util* +VIM *lua-builtin* + +vim.api.{func}({...}) *vim.api* + Invokes Nvim |API| function {func} with arguments {...}. + Example: call the "nvim_get_current_line()" API function: > + print(tostring(vim.api.nvim_get_current_line())) + +vim.call({func}, {...}) *vim.call()* + Invokes |vim-function| or |user-function| {func} with arguments {...}. + See also |vim.fn|. Equivalent to: > + vim.fn[func]({...}) vim.in_fast_event() *vim.in_fast_event()* Returns true if the code is executing as part of a "fast" event @@ -614,26 +614,23 @@ vim.schedule({callback}) *vim.schedule()* Schedules {callback} to be invoked soon by the main event-loop. Useful to avoid |textlock| or other temporary restrictions. -vim.fn.{func}({...}) - Call vimL function {func} with arguments. {func} can be both builtin - functions and user functions. To call autoload functions, use the - syntax `vim.fn['some#function']({...})` - - Note: unlike vim.api.|nvim_call_function| this converts values directly - between vimL values and lua values. If the vimL function returns a - float, it will be representeted directly as a lua number. Both empty - lists and dictonaries will be represented by an empty table. - - Note: vim.fn keys are generated on demand. So `pairs(vim.fn)` - does NOT work to enumerate all functions. +vim.fn.{func}({...}) *vim.fn* + Invokes |vim-function| or |user-function| {func} with arguments {...}. + To call autoload functions, use the syntax: > + vim.fn['some#function']({...}) +< + Unlike vim.api.|nvim_call_function| this converts directly between Vim + objects and Lua objects. If the Vim function returns a float, it will + be represented directly as a Lua number. Empty lists and dictionaries + both are represented by an empty table. -vim.call({func}, {...}) - Call vim script function {func}. Equivalent to `vim.fn[func]({...})` + Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only + enumerates functions that were called at least once. vim.type_idx *vim.type_idx* Type index for use in |lua-special-tbl|. Specifying one of the values from |vim.types| allows typing the empty table (it is - unclear whether empty lua table represents empty list or empty array) + unclear whether empty Lua table represents empty list or empty array) and forcing integral numbers to be |Float|. See |lua-special-tbl| for more details. @@ -657,7 +654,7 @@ vim.types *vim.types* `vim.types.array` and `vim.types.dictionary` fall under only two following assumptions: 1. Value may serve both as a key and as a value in a table. Given the - properties of lua tables this basically means “value is not `nil`”. + properties of Lua tables this basically means “value is not `nil`”. 2. For each value in `vim.types` table `vim.types[vim.types[value]]` is the same as `value`. No other restrictions are put on types, and it is not guaranteed that @@ -679,6 +676,20 @@ paste({lines}, {phase}) *vim.paste()* Paste handler, invoked by |nvim_paste()| when a conforming UI (such as the |TUI|) pastes text into the editor. + Example: To remove ANSI color codes when pasting: > + + vim.paste = (function() + local overridden = vim.paste + return function(lines, phase) + for i,line in ipairs(lines) do + -- Scrub ANSI color codes from paste input. + lines[i] = line:gsub('\27%[[0-9;mK]+', '') + end + overridden(lines, phase) + end + end)() +< + Parameters: ~ {lines} |readfile()|-style list of lines to paste. |channel-lines| diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 7221888074..211b7be2cc 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1170,7 +1170,7 @@ tag command action ~ |:caddfile| :caddf[ile] add error message to current quickfix list |:call| :cal[l] call a function |:catch| :cat[ch] part of a :try command -|:cbelow| :cbe[low] got to error below current line +|:cbelow| :cbe[low] go to error below current line |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbuffer| :cb[uffer] parse error messages and jump to first error |:cc| :cc go to specific error diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 9e24ee0320..e93c833c76 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -418,35 +418,35 @@ between Vi and Vim. 5. Text object motions *object-motions* *(* -( [count] sentences backward. |exclusive| motion. +( [count] |sentence|s backward. |exclusive| motion. *)* -) [count] sentences forward. |exclusive| motion. +) [count] |sentence|s forward. |exclusive| motion. *{* -{ [count] paragraphs backward. |exclusive| motion. +{ [count] |paragraph|s backward. |exclusive| motion. *}* -} [count] paragraphs forward. |exclusive| motion. +} [count] |paragraph|s forward. |exclusive| motion. *]]* -]] [count] sections forward or to the next '{' in the +]] [count] |section|s forward or to the next '{' in the first column. When used after an operator, then also stops below a '}' in the first column. |exclusive| Note that |exclusive-linewise| often applies. *][* -][ [count] sections forward or to the next '}' in the +][ [count] |section|s forward or to the next '}' in the first column. |exclusive| Note that |exclusive-linewise| often applies. *[[* -[[ [count] sections backward or to the previous '{' in +[[ [count] |section|s backward or to the previous '{' in the first column. |exclusive| Note that |exclusive-linewise| often applies. *[]* -[] [count] sections backward or to the previous '}' in +[] [count] |section|s backward or to the previous '}' in the first column. |exclusive| Note that |exclusive-linewise| often applies. -- cgit From 474d0bcbf724c7eed740f60391a0ed35d651e1d3 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 27 Oct 2019 20:49:03 +0100 Subject: lua: vim.rpcrequest, vim.rpcnotify, vim.NIL --- runtime/doc/if_lua.txt | 23 +++++++++++++++++++++++ runtime/lua/vim/inspect.lua | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 97d851a20f..bba43ea32c 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -587,6 +587,26 @@ vim.in_fast_event() *vim.in_fast_event()* for input. When this is `false` most API functions are callable (but may be subject to other restrictions such as |textlock|). +vim.NIL *vim.NIL* + Special value used to represent NIL in msgpack-rpc and |v:null| in + vimL interaction, and similar cases. Lua `nil` cannot be used as + part of a lua table representing a Dictionary or Array, as it + is equivalent to a missing value: `{"foo", nil}` is the same as + `{"foo"}` + +vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* + Sends {event} to {channel} via |RPC| and returns immediately. + If {channel} is 0, the event is broadcast to all channels. + + This function also works in a fast callback |lua-loop-callbacks|. + +vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()* + Sends a request to {channel} to invoke {method} via + |RPC| and blocks until a response is received. + + Note: NIL values as part of the return value is represented as + |vim.NIL| special value + vim.stricmp({a}, {b}) *vim.stricmp()* Compares strings case-insensitively. Returns 0, 1 or -1 if strings are equal, {a} is greater than {b} or {a} is lesser than {b}, @@ -624,6 +644,9 @@ vim.fn.{func}({...}) *vim.fn* be represented directly as a Lua number. Empty lists and dictionaries both are represented by an empty table. + Note: |v:null| values as part of the return value is represented as + |vim.NIL| special value + Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only enumerates functions that were called at least once. diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index 7cb40ca64d..0f3b908dc1 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -289,7 +289,7 @@ function Inspector:putValue(v) if tv == 'string' then self:puts(smartQuote(escape(v))) elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or - tv == 'cdata' or tv == 'ctype' then + tv == 'cdata' or tv == 'ctype' or (vim and v == vim.NIL) then self:puts(tostring(v)) elseif tv == 'table' then self:putTable(v) -- cgit From 1cb4674547828a315b7aef5b6c635726b3bc12e5 Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Sun, 10 Nov 2019 16:38:04 +0100 Subject: api: add nvim_buf_get_virtual_text() (#11354) This adds the missing partner function of nvim_buf_set_virtual_text(). --- runtime/doc/api.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index d6e420c427..93440f307c 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1842,6 +1842,27 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts}) Return: ~ The ns_id that was used +nvim_buf_get_virtual_text({buffer}, {lnum}) *nvim_buf_get_virtual_text()* + Get the virtual text (annotation) for a buffer line. + + The virtual text is returned as list of lists, whereas the + inner lists have either one or two elements. The first element + is the actual text, the optional second element is the + highlight group. + + The format is exactly the same as given to + nvim_buf_set_virtual_text(). + + If there is no virtual text associated with the given line, an + empty list is returned. + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {line} Line to get the virtual text from (zero-indexed) + + Return: ~ + List of virtual text chunks + nvim__buf_stats({buffer}) *nvim__buf_stats()* TODO: Documentation -- cgit From b9c9283f729c60d98634587190eb14c6bbf428e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 10 Nov 2019 12:35:45 -0800 Subject: spellfile.vim: improve error message for missing spellfile Problem: If spellfile is missing, then "set spell" in modeline/sandbox fails with a non-obvious error. Solution: Check for sandbox early and give a meaningful error. Fixes #11359 Test case: # test.latex has `% vim: set spelllang=hu:` # no spell file for `hu` yet! nvim -u NORC --cmd 'autocmd FileType tex setlocal spell' --cmd 'set modeline' test.latex Before: No spell file for "hu" in utf-8 Download it? Downloading hu.utf-8.spl... Error detected while processing /usr/local/share/nvim/runtime/autoload/netrw.vim: line 583: E12: Command not allowed from exrc/vimrc in current dir or tag search: au WinEnter *^Iif &ft == "netrw"|call s:NetrwInsureWinVars()|endif Error detected while processing function spellfile#LoadFile[60]..spellfile#Nread[13]..netrw#NetRead[4]..67_NetrwOptionsSave: line 66: E171: Missing :endif Error detected while processing function spellfile#LoadFile[60]..spellfile#Nread: line 13: E171: Missing :endif Error detected while processing function spellfile#LoadFile: line 60: E171: Missing :endif Error detected while processing modelines: line 1: E12: Command not allowed from exrc/vimrc in current dir or tag search After: Error detected while processing function spellfile#LoadFile: line 5: E605: Exception not caught: Cannot download spellfile in sandbox/modeline. Try ":set spell" from the cmdline. Error detected while processing modelines: line 1: E12: Command not allowed from exrc/vimrc in current dir or tag search --- runtime/autoload/spellfile.vim | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/spellfile.vim b/runtime/autoload/spellfile.vim index c0ef51cdfe..d098902305 100644 --- a/runtime/autoload/spellfile.vim +++ b/runtime/autoload/spellfile.vim @@ -13,6 +13,13 @@ let s:spellfile_URL = '' " Start with nothing so that s:donedict is reset. " This function is used for the spellfile plugin. function! spellfile#LoadFile(lang) + " Check for sandbox/modeline. #11359 + try + :! + catch /\/ + throw 'Cannot download spellfile in sandbox/modeline. Try ":set spell" from the cmdline.' + endtry + " If the netrw plugin isn't loaded we silently skip everything. if !exists(":Nread") if &verbose -- cgit From 678a51b1da0c0535299341e7a598c080adcf8553 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 28 Oct 2019 20:52:18 +0900 Subject: Lua: vim.validate() We often want to do type checking of public function arguments. - test: Rename utility_function_spec.lua to vim_spec.lua - .luacov: Map lua module names --- runtime/lua/vim/shared.lua | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 7727fdbab0..9f656774a1 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -190,4 +190,78 @@ function vim.pesc(s) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end +--- Type checking validation function +--- +--- Examples: +---
+---  validate({ arg={ { 'foo' }, 'table' }})                                     --> Nop
+---  validate({ arg={ 1, 'table' } })                                            --> error("arg: expected table, got number")
+---  validate({ arg1={ { 'foo' }, 'table' }, arg2={ 1, 'string' } })             --> error("arg2: expected string, got number")
+---  validate({ arg={ 3, function(a) return (a % 2) == 0  end, 'even number' }}) --> error("arg: expected even number, got 3")
+--- 
+--- +---@param ... Table or list of table. That table is "argument_name = { validation_target, type_name (, whether nil is allowed) }" +--- or "argument_name = { validation_target, validation function, expected_description }". +--- The following can be used as type_names: +--- - table or t +--- - string or s +--- - number or n +--- - boolean or b +--- - function or f +--- - nil +--- - thread +--- - userdata +function vim.validate(opt) + local function _type_name(t) + if t == 't' or t == 'table' then return 'table' end + if t == 's' or t == 'string' then return 'string' end + if t == 'n' or t == 'number' then return 'number' end + if t == 'b' or t == 'boolean' then return 'boolean' end + if t == 'f' or t == 'function' then return 'function' end + if t == 'c' then return 'callable' end + if t == 'nil' then return 'nil' end + if t == 'thread' or t == 'thread' then return 'thread' end + if t == 'userdata' then return 'userdata' end + if vim.is_callable(t) then return end + + error(string.format("Invalid type name '%s'. See \":help validate\" for more info.", t)) + end + local function _check_type(target, expected_type) + if expected_type == 'callable' then + return vim.is_callable(target) + else + return type(target) == expected_type + end + end + + for arg, v in pairs(opt) do + assert(type(arg) == 'string',string.format('Expected string, got %s', type(arg))) + assert(type(v) == 'table', string.format('Expected table, got %s', type(v))) + + local actual_arg_type = type(v[1]) + local expected_type = _type_name(v[2]) + + if expected_type then + if v[3] == true then + assert(_check_type(v[1], expected_type) or actual_arg_type == 'nil', string.format("%s: expected %s, got %s", arg, expected_type, actual_arg_type)) + else + assert(_check_type(v[1], expected_type), string.format("%s: expected %s, got %s", arg, expected_type, actual_arg_type)) + end + else + assert(v[2](v[1]), string.format("%s: expected %s, got %s", arg, v[3], v[1])) + end + end +end + +--- Return whether an object can call be used as a function. +--- +--@param f Any type of variable +--@return Boolean +function vim.is_callable(f) + if type(f) == 'function' then return true end + local m = getmetatable(f) + if m == nil then return false end + return type(m.__call) == 'function' +end + return vim -- cgit From 7aa4042d3bddf2f39d048b9ba1dd7adf2193d4eb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 10 Nov 2019 19:58:14 -0800 Subject: Lua: vim.validate() --- runtime/lua/vim/shared.lua | 111 +++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 49 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 9f656774a1..7d2523403a 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -190,68 +190,81 @@ function vim.pesc(s) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end ---- Type checking validation function +--- Validates a parameter specification (types and values). --- --- Examples: ---
----  validate({ arg={ { 'foo' }, 'table' }})                                     --> Nop
----  validate({ arg={ 1, 'table' } })                                            --> error("arg: expected table, got number")
----  validate({ arg1={ { 'foo' }, 'table' }, arg2={ 1, 'string' } })             --> error("arg2: expected string, got number")
----  validate({ arg={ 3, function(a) return (a % 2) == 0  end, 'even number' }}) --> error("arg: expected even number, got 3")
+---  function user.new(name, age, hobbies)
+---    vim.validate{
+---      name={name, 'string'},
+---      age={age, 'number'},
+---      hobbies={hobbies, 'table'},
+---    }
+---    ...
+---  end
+--
+---  vim.validate{ arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
+---     => NOP (success)
+---  vim.validate{arg1={1, 'table'}}
+---     => error("arg1: expected table, got number")
+---  vim.validate{arg1={{'foo'}, 'table'}, arg2={1, 'string'}}
+---     => error("arg2: expected string, got number")
+---  vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
+---     => error("arg1: expected even number, got 3")
 --- 
--- ----@param ... Table or list of table. That table is "argument_name = { validation_target, type_name (, whether nil is allowed) }" ---- or "argument_name = { validation_target, validation function, expected_description }". ---- The following can be used as type_names: ---- - table or t ---- - string or s ---- - number or n ---- - boolean or b ---- - function or f ---- - nil ---- - thread ---- - userdata -function vim.validate(opt) - local function _type_name(t) - if t == 't' or t == 'table' then return 'table' end - if t == 's' or t == 'string' then return 'string' end - if t == 'n' or t == 'number' then return 'number' end - if t == 'b' or t == 'boolean' then return 'boolean' end - if t == 'f' or t == 'function' then return 'function' end - if t == 'c' then return 'callable' end - if t == 'nil' then return 'nil' end - if t == 'thread' or t == 'thread' then return 'thread' end - if t == 'userdata' then return 'userdata' end - if vim.is_callable(t) then return end - - error(string.format("Invalid type name '%s'. See \":help validate\" for more info.", t)) - end - local function _check_type(target, expected_type) - if expected_type == 'callable' then - return vim.is_callable(target) - else - return type(target) == expected_type +--@param opt Map of parameter names to validations. Each key is a parameter +--- name; each value is a tuple in one of these forms: +--- 1. {arg_value, type_name, optional} +--- - arg_value: argument value +--- - type_name: string type name, one of: ("table", "t", "string", +--- "s", "number", "n", "boolean", "b", "function", "f", "nil", +--- "thread", "userdata") +--- - optional: (optional) boolean, if true, `nil` is valid +--- 2. {arg_value, fn, msg} +--- - arg_value: argument value +--- - fn: any function accepting one argument, returns true if and +--- only if the argument is valid +--- - msg: (optional) error string if validation fails +function vim.validate(opt) end -- luacheck: no unused +vim.validate = (function() + local type_names = { + t='table', s='string', n='number', b='boolean', f='function', c='callable', + ['table']='table', ['string']='string', ['number']='number', + ['boolean']='boolean', ['function']='function', ['callable']='callable', + ['nil']='nil', ['thread']='thread', ['userdata']='userdata', + } + local function type_name(t) + local tname = type_names[t] + if tname == nil then + error(string.format('invalid type name: %s', tostring(t))) end + return tname + end + local function is_type(val, t) + return t == 'callable' and vim.is_callable(val) or type(val) == t end - for arg, v in pairs(opt) do - assert(type(arg) == 'string',string.format('Expected string, got %s', type(arg))) - assert(type(v) == 'table', string.format('Expected table, got %s', type(v))) + return function(opt) + assert(type(opt) == 'table', string.format('opt: expected table, got %s', type(opt))) + for param_name, spec in pairs(opt) do + assert(type(spec) == 'table', string.format('%s: expected table, got %s', param_name, type(spec))) - local actual_arg_type = type(v[1]) - local expected_type = _type_name(v[2]) + local val = spec[1] -- Argument value. + local t = spec[2] -- Type name, or callable. + local optional = (true == spec[3]) - if expected_type then - if v[3] == true then - assert(_check_type(v[1], expected_type) or actual_arg_type == 'nil', string.format("%s: expected %s, got %s", arg, expected_type, actual_arg_type)) - else - assert(_check_type(v[1], expected_type), string.format("%s: expected %s, got %s", arg, expected_type, actual_arg_type)) + if not vim.is_callable(t) then -- Check type name. + if not (optional or type(val) == 'nil') and not is_type(val, type_name(t)) then + error(string.format("%s: expected %s, got %s", param_name, type_name(t), type(val))) + end + elseif not t(val) then -- Check user-provided validation function. + error(string.format("%s: expected %s, got %s", param_name, spec[3], val)) end - else - assert(v[2](v[1]), string.format("%s: expected %s, got %s", arg, v[3], v[1])) end + return true end -end +end)() --- Return whether an object can call be used as a function. --- -- cgit From a0d992785feeefaea2810dcf08564fd8bea8cab9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 10 Nov 2019 22:18:59 -0800 Subject: Lua: Use vim.validate() instead of assert() --- runtime/doc/if_lua.txt | 59 ++++++++++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/shared.lua | 47 ++++++++++++++++++------------------ 2 files changed, 83 insertions(+), 23 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index bba43ea32c..7eef5d3903 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -847,4 +847,63 @@ pesc({s}) *vim.pesc()* See also: ~ https://github.com/rxi/lume +validate({opt}) *vim.validate()* + Validates a parameter specification (types and values). + + Usage example: > + + function user.new(name, age, hobbies) + vim.validate{ + name={name, 'string'}, + age={age, 'number'}, + hobbies={hobbies, 'table'}, + } + ... + end +< + + Examples with explicit argument values (can be run directly): > + + vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} + => NOP (success) +< +> + vim.validate{arg1={1, 'table'}} + => error('arg1: expected table, got number') +< +> + vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} + => error('arg1: expected even number, got 3') +< + + Parameters: ~ + {opt} Map of parameter names to validations. Each key is + a parameter name; each value is a tuple in one of + these forms: + 1. (arg_value, type_name, optional) + • arg_value: argument value + • type_name: string type name, one of: ("table", + "t", "string", "s", "number", "n", "boolean", + "b", "function", "f", "nil", "thread", + "userdata") + • optional: (optional) boolean, if true, `nil` + is valid + + 2. (arg_value, fn, msg) + • arg_value: argument value + • fn: any function accepting one argument, + returns true if and only if the argument is + valid + • msg: (optional) error string if validation + fails + +is_callable({f}) *vim.is_callable()* + Returns true if object `f` can be called as a function. + + Parameters: ~ + {f} Any object + + Return: ~ + true if `f` is callable, else false + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 7d2523403a..e987e07a2a 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -47,9 +47,7 @@ end)() --@param plain If `true` use `sep` literally (passed to String.find) --@returns Iterator over the split components function vim.gsplit(s, sep, plain) - assert(type(s) == "string", string.format("Expected string, got %s", type(s))) - assert(type(sep) == "string", string.format("Expected string, got %s", type(sep))) - assert(type(plain) == "boolean" or type(plain) == "nil", string.format("Expected boolean or nil, got %s", type(plain))) + vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}} local start = 1 local done = false @@ -106,7 +104,7 @@ end --@param value Value to compare --@returns true if `t` contains `value` function vim.tbl_contains(t, value) - assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + vim.validate{t={t,'t'}} for _,v in ipairs(t) do if v == value then @@ -176,7 +174,7 @@ end --@param s String to trim --@returns String with whitespace removed from its beginning and end function vim.trim(s) - assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) + vim.validate{s={s,'s'}} return s:match('^%s*(.*%S)') or '' end @@ -186,13 +184,13 @@ end --@param s String to escape --@returns %-escaped pattern string function vim.pesc(s) - assert(type(s) == 'string', string.format("Expected string, got %s", type(s))) + vim.validate{s={s,'s'}} return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end --- Validates a parameter specification (types and values). --- ---- Examples: +--- Usage example: ---
 ---  function user.new(name, age, hobbies)
 ---    vim.validate{
@@ -202,26 +200,29 @@ end
 ---    }
 ---    ...
 ---  end
---
----  vim.validate{ arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
+--- 
+--- +--- Examples with explicit argument values (can be run directly): +---
+---  vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
 ---     => NOP (success)
+---
 ---  vim.validate{arg1={1, 'table'}}
----     => error("arg1: expected table, got number")
----  vim.validate{arg1={{'foo'}, 'table'}, arg2={1, 'string'}}
----     => error("arg2: expected string, got number")
+---     => error('arg1: expected table, got number')
+---
 ---  vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
----     => error("arg1: expected even number, got 3")
+---     => error('arg1: expected even number, got 3')
 --- 
--- --@param opt Map of parameter names to validations. Each key is a parameter --- name; each value is a tuple in one of these forms: ---- 1. {arg_value, type_name, optional} +--- 1. (arg_value, type_name, optional) --- - arg_value: argument value --- - type_name: string type name, one of: ("table", "t", "string", --- "s", "number", "n", "boolean", "b", "function", "f", "nil", --- "thread", "userdata") --- - optional: (optional) boolean, if true, `nil` is valid ---- 2. {arg_value, fn, msg} +--- 2. (arg_value, fn, msg) --- - arg_value: argument value --- - fn: any function accepting one argument, returns true if and --- only if the argument is valid @@ -234,14 +235,14 @@ vim.validate = (function() ['boolean']='boolean', ['function']='function', ['callable']='callable', ['nil']='nil', ['thread']='thread', ['userdata']='userdata', } - local function type_name(t) + local function _type_name(t) local tname = type_names[t] if tname == nil then error(string.format('invalid type name: %s', tostring(t))) end return tname end - local function is_type(val, t) + local function _is_type(val, t) return t == 'callable' and vim.is_callable(val) or type(val) == t end @@ -255,21 +256,21 @@ vim.validate = (function() local optional = (true == spec[3]) if not vim.is_callable(t) then -- Check type name. - if not (optional or type(val) == 'nil') and not is_type(val, type_name(t)) then - error(string.format("%s: expected %s, got %s", param_name, type_name(t), type(val))) + if (not optional or val ~= nil) and not _is_type(val, _type_name(t)) then + error(string.format("%s: expected %s, got %s", param_name, _type_name(t), type(val))) end elseif not t(val) then -- Check user-provided validation function. - error(string.format("%s: expected %s, got %s", param_name, spec[3], val)) + error(string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val)) end end return true end end)() ---- Return whether an object can call be used as a function. +--- Returns true if object `f` can be called as a function. --- ---@param f Any type of variable ---@return Boolean +--@param f Any object +--@return true if `f` is callable, else false function vim.is_callable(f) if type(f) == 'function' then return true end local m = getmetatable(f) -- cgit From a9065a50518ef59351f9d0d32041a991a751653f Mon Sep 17 00:00:00 2001 From: timeyyy Date: Wed, 18 Jan 2017 13:20:07 +0100 Subject: nsmarks: initial commit --- runtime/doc/api.txt | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 93440f307c..d6b7938633 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -438,6 +438,36 @@ Example: create a float with scratch buffer: > call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight') > +============================================================================== +Buffer extended marks *api-extended-marks* + +An extended mark represents a buffer annotation that remains logically +stationary even as the buffer changes. They could be used to represent cursors, +folds, misspelled words, and anything else that needs to track a logical +location in the buffer over time. + +Example: + +We will set an extmark at row 1, column three. + +`let g:mark_ns = nvim_create_namespace('myplugin')` +`let g:mark_id = nvim_buf_set_mark(0, :g:mark_ns, 0, 1, 3)` + +Note: the mark id was randomly generated because we used an inital value of 0 + +`echo nvim_buf_lookup_mark(0, g:mark_ns, g:mark_id)` +=> [1, 1, 3] +`echo nvim_buf_get_marks(0, g:mark_ns, [1, 1], [1, 3], -1, 0)` +=> [[1, 1, 3]] + +Deleting the text all around an extended mark does not remove it. If you want +to remove an extended mark, use the |nvim_buf_unset_mark()| function. + +The namepsace ensures you only ever work with the extended marks you mean to. + +Calling set and unset of marks will not automatically prop a new undo. + + ============================================================================== Global Functions *api-global* @@ -1449,6 +1479,22 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* TODO: Documentation + *nvim_init_mark_ns()* +nvim_init_mark_ns({namespace}) + Create a new namepsace for holding extended marks. The id + of the namespace is returned, this namespace id is required + for using the rest of the extended marks api. + + Parameters:~ + {namespace} String name to be assigned to the namespace + + + *nvim_mark_get_ns_ids()* +nvim_mark_get_ns_ids() + Returns a list of extended mark namespaces. + Pairs of ids and string names are returned. + An empty list will be returned if no namespaces are set. + ============================================================================== Buffer Functions *api-buffer* -- cgit From 18a8b702c0ce7a8bacd84f6c95e440ae23a3299e Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 9 Nov 2019 12:41:50 +0100 Subject: extmark: review changes --- runtime/doc/api.txt | 130 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 30 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index d6b7938633..5d30c10486 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -439,34 +439,40 @@ Example: create a float with scratch buffer: > > ============================================================================== -Buffer extended marks *api-extended-marks* +Extended marks *api-extended-marks* -An extended mark represents a buffer annotation that remains logically -stationary even as the buffer changes. They could be used to represent cursors, +An extended mark (extmark) represents a buffer annotation that follows +movements as the buffer changes. They could be used to represent cursors, folds, misspelled words, and anything else that needs to track a logical location in the buffer over time. Example: -We will set an extmark at row 1, column three. +We will set an extmark at the first row and third column. As the API is zero- +indexed, use row and column counts 0 and 2: `let g:mark_ns = nvim_create_namespace('myplugin')` -`let g:mark_id = nvim_buf_set_mark(0, :g:mark_ns, 0, 1, 3)` +`let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2)` -Note: the mark id was randomly generated because we used an inital value of 0 +Passing in id=0 creates a new mark and returns the id. we can look-up a mark +by its id: -`echo nvim_buf_lookup_mark(0, g:mark_ns, g:mark_id)` -=> [1, 1, 3] -`echo nvim_buf_get_marks(0, g:mark_ns, [1, 1], [1, 3], -1, 0)` -=> [[1, 1, 3]] +`echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id)` +=> [0, 2] -Deleting the text all around an extended mark does not remove it. If you want -to remove an extended mark, use the |nvim_buf_unset_mark()| function. +Or we can look-up all marks in a buffer for our namespace (or by a range): +`echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, -1)` +=> [[1, 0, 2]] -The namepsace ensures you only ever work with the extended marks you mean to. +Deleting the text all around an extended mark does not remove it. If you want +to remove an extended mark, use the |nvim_buf_del_extmark()| function. -Calling set and unset of marks will not automatically prop a new undo. +The namespace ensures your plugin doesn't have to deal with extmarks created +by another plugin. +Mark positions changed by an edit will be restored on undo/redo. Creating and +deleting marks doesn't count as a buffer change on itself, i e new undo +states will not be created only for marks. ============================================================================== Global Functions *api-global* @@ -1479,22 +1485,6 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* TODO: Documentation - *nvim_init_mark_ns()* -nvim_init_mark_ns({namespace}) - Create a new namepsace for holding extended marks. The id - of the namespace is returned, this namespace id is required - for using the rest of the extended marks api. - - Parameters:~ - {namespace} String name to be assigned to the namespace - - - *nvim_mark_get_ns_ids()* -nvim_mark_get_ns_ids() - Returns a list of extended mark namespaces. - Pairs of ids and string names are returned. - An empty list will be returned if no namespaces are set. - ============================================================================== Buffer Functions *api-buffer* @@ -1793,6 +1783,86 @@ nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()* Return: ~ (row, col) tuple + *nvim_buf_get_extmark_by_id()* +nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}) + Returns position for a given extmark id + + Parameters: ~ + {buffer} The buffer handle + {namespace} a identifier returned previously with + nvim_create_namespace + {id} the extmark id + + Return: ~ + (row, col) tuple or empty list () if extmark id was absent + + *nvim_buf_get_extmarks()* +nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) + List extmarks in a range (inclusive) + + range ends can be specified as (row, col) tuples, as well as + extmark ids in the same namespace. In addition, 0 and -1 works + as shorthands for (0,0) and (-1,-1) respectively, so that all + marks in the buffer can be quieried as: + + all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1) + + If end is a lower position than start, then the range will be + traversed backwards. This is mostly used with limited amount, + to be able to get the first marks prior to a given position. + + Parameters: ~ + {buffer} The buffer handle + {ns_id} An id returned previously from + nvim_create_namespace + {lower} One of: extmark id, (row, col) or 0, -1 for + buffer ends + {upper} One of: extmark id, (row, col) or 0, -1 for + buffer ends + {opts} additional options. Supports the keys: + • amount: Maximum number of marks to return • + + Return: ~ + [[nsmark_id, row, col], ...] + + *nvim_buf_set_extmark()* +nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) + Create or update an extmark at a position + + If an invalid namespace is given, an error will be raised. + + To create a new extmark, pass in id=0. The new extmark id will + be returned. To move an existing mark, pass in its id. + + It is also allowed to create a new mark by passing in a + previously unused id, but the caller must then keep track of + existing and unused ids itself. This is mainly useful over + RPC, to avoid needing to wait for the return value. + + Parameters: ~ + {buffer} The buffer handle + {ns_id} a identifier returned previously with + nvim_create_namespace + {id} The extmark's id or 0 to create a new mark. + {row} The row to set the extmark to. + {col} The column to set the extmark to. + {opts} Optional parameters. Currently not used. + + Return: ~ + the id of the extmark. + +nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* + Remove an extmark + + Parameters: ~ + {buffer} The buffer handle + {ns_id} a identifier returned previously with + nvim_create_namespace + {id} The extmarks's id + + Return: ~ + true on success, false if the extmark was not found. + *nvim_buf_add_highlight()* nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, {col_end}) -- cgit From 54473e9a677b001b47b1fe528b6056e9feed0f60 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 10 Nov 2019 16:52:14 -0800 Subject: doc [ci skip] --- runtime/doc/api.txt | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 5d30c10486..4ed0a6aba0 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -441,38 +441,39 @@ Example: create a float with scratch buffer: > ============================================================================== Extended marks *api-extended-marks* -An extended mark (extmark) represents a buffer annotation that follows -movements as the buffer changes. They could be used to represent cursors, -folds, misspelled words, and anything else that needs to track a logical -location in the buffer over time. +Extended marks (extmarks) represent buffer annotations that track text changes +in the buffer. They could be used to represent cursors, folds, misspelled +words, and anything else that needs to track a logical location in the buffer +over time. Example: -We will set an extmark at the first row and third column. As the API is zero- -indexed, use row and column counts 0 and 2: +We will set an extmark at the first row and third column. |api-indexing| is +zero-indexed, so we use row=0 and column=2. Passing id=0 creates a new mark +and returns the id: > -`let g:mark_ns = nvim_create_namespace('myplugin')` -`let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2)` + let g:mark_ns = nvim_create_namespace('myplugin') + let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2, {}) -Passing in id=0 creates a new mark and returns the id. we can look-up a mark -by its id: +We can get a mark by its id: > -`echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id)` -=> [0, 2] + echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id) + => [0, 2] -Or we can look-up all marks in a buffer for our namespace (or by a range): -`echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, -1)` -=> [[1, 0, 2]] +We can get all marks in a buffer for our namespace (or by a range): > -Deleting the text all around an extended mark does not remove it. If you want -to remove an extended mark, use the |nvim_buf_del_extmark()| function. + echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, -1) + => [[1, 0, 2]] -The namespace ensures your plugin doesn't have to deal with extmarks created -by another plugin. +Deleting all text surrounding an extmark does not remove the extmark. To +remove an extmark use |nvim_buf_del_extmark()|. -Mark positions changed by an edit will be restored on undo/redo. Creating and -deleting marks doesn't count as a buffer change on itself, i e new undo -states will not be created only for marks. +Namespaces allow your plugin to manage only its own extmarks, ignoring those +created by another plugin. + +Extmark positions changed by an edit will be restored on undo/redo. Creating +and deleting extmarks is not a buffer change, thus new undo states are not +created for extmark changes. ============================================================================== Global Functions *api-global* @@ -1820,10 +1821,10 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) {upper} One of: extmark id, (row, col) or 0, -1 for buffer ends {opts} additional options. Supports the keys: - • amount: Maximum number of marks to return • + • amount: Maximum number of marks to return Return: ~ - [[nsmark_id, row, col], ...] + [[extmark_id, row, col], ...] *nvim_buf_set_extmark()* nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) -- cgit From 00dc12c5d8454a2d3c6806710f63bbb446076e96 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 13 Nov 2019 12:55:26 -0800 Subject: lua LSP client: initial implementation (#11336) Mainly configuration and RPC infrastructure can be considered "done". Specific requests and their callbacks will be improved later (and also served by plugins). There are also some TODO:s for the client itself, like incremental updates. Co-authored by at-tjdevries and at-h-michael, with many review/suggestion contributions. --- runtime/autoload/lsp.vim | 45 ++ runtime/doc/lsp.txt | 662 ++++++++++++++++++ runtime/lua/vim/lsp.lua | 1055 +++++++++++++++++++++++++++++ runtime/lua/vim/lsp/builtin_callbacks.lua | 296 ++++++++ runtime/lua/vim/lsp/log.lua | 95 +++ runtime/lua/vim/lsp/protocol.lua | 936 +++++++++++++++++++++++++ runtime/lua/vim/lsp/rpc.lua | 451 ++++++++++++ runtime/lua/vim/lsp/util.lua | 557 +++++++++++++++ runtime/lua/vim/shared.lua | 127 +++- runtime/lua/vim/uri.lua | 89 +++ 10 files changed, 4312 insertions(+), 1 deletion(-) create mode 100644 runtime/autoload/lsp.vim create mode 100644 runtime/doc/lsp.txt create mode 100644 runtime/lua/vim/lsp.lua create mode 100644 runtime/lua/vim/lsp/builtin_callbacks.lua create mode 100644 runtime/lua/vim/lsp/log.lua create mode 100644 runtime/lua/vim/lsp/protocol.lua create mode 100644 runtime/lua/vim/lsp/rpc.lua create mode 100644 runtime/lua/vim/lsp/util.lua create mode 100644 runtime/lua/vim/uri.lua (limited to 'runtime') diff --git a/runtime/autoload/lsp.vim b/runtime/autoload/lsp.vim new file mode 100644 index 0000000000..4c8f8b396a --- /dev/null +++ b/runtime/autoload/lsp.vim @@ -0,0 +1,45 @@ +function! lsp#add_filetype_config(config) abort + call luaeval('vim.lsp.add_filetype_config(_A)', a:config) +endfunction + +function! lsp#set_log_level(level) abort + call luaeval('vim.lsp.set_log_level(_A)', a:level) +endfunction + +function! lsp#get_log_path() abort + return luaeval('vim.lsp.get_log_path()') +endfunction + +function! lsp#omnifunc(findstart, base) abort + return luaeval("vim.lsp.omnifunc(_A[1], _A[2])", [a:findstart, a:base]) +endfunction + +function! lsp#text_document_hover() abort + lua vim.lsp.buf_request(nil, 'textDocument/hover', vim.lsp.protocol.make_text_document_position_params()) + return '' +endfunction + +function! lsp#text_document_declaration() abort + lua vim.lsp.buf_request(nil, 'textDocument/declaration', vim.lsp.protocol.make_text_document_position_params()) + return '' +endfunction + +function! lsp#text_document_definition() abort + lua vim.lsp.buf_request(nil, 'textDocument/definition', vim.lsp.protocol.make_text_document_position_params()) + return '' +endfunction + +function! lsp#text_document_signature_help() abort + lua vim.lsp.buf_request(nil, 'textDocument/signatureHelp', vim.lsp.protocol.make_text_document_position_params()) + return '' +endfunction + +function! lsp#text_document_type_definition() abort + lua vim.lsp.buf_request(nil, 'textDocument/typeDefinition', vim.lsp.protocol.make_text_document_position_params()) + return '' +endfunction + +function! lsp#text_document_implementation() abort + lua vim.lsp.buf_request(nil, 'textDocument/implementation', vim.lsp.protocol.make_text_document_position_params()) + return '' +endfunction diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt new file mode 100644 index 0000000000..26850b3683 --- /dev/null +++ b/runtime/doc/lsp.txt @@ -0,0 +1,662 @@ +*lsp.txt* The Language Server Protocol + + NVIM REFERENCE MANUAL + + +Neovim Language Server Protocol (LSP) API + +Neovim exposes a powerful API that conforms to Microsoft's published Language +Server Protocol specification. The documentation can be found here: + + https://microsoft.github.io/language-server-protocol/ + + +================================================================================ + *lsp-api* + +Neovim exposes a API for the language server protocol. To get the real benefits +of this API, a language server must be installed. +Many examples can be found here: + + https://microsoft.github.io/language-server-protocol/implementors/servers/ + +After installing a language server to your machine, you must let Neovim know +how to start and interact with that language server. + +To do so, you can either: +- Use the |vim.lsp.add_filetype_config()|, which solves the common use-case of + a single server for one or more filetypes. This can also be used from vim + via |lsp#add_filetype_config()|. +- Or |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. These are the + backbone of the LSP API. These are easy to use enough for basic or more + complex configurations such as in |lsp-advanced-js-example|. + +================================================================================ + *lsp-filetype-config* + +These are utilities specific to filetype based configurations. + + *lsp#add_filetype_config()* + *vim.lsp.add_filetype_config()* +lsp#add_filetype_config({config}) for Vim. +vim.lsp.add_filetype_config({config}) for Lua + + These are functions which can be used to create a simple configuration which + will start a language server for a list of filetypes based on the |FileType| + event. + It will lazily start start the server, meaning that it will only start once + a matching filetype is encountered. + + The {config} options are the same as |vim.lsp.start_client()|, but + with a few additions and distinctions: + + Additional parameters:~ + `filetype` + {string} or {list} of filetypes to attach to. + `name` + A unique identifying string among all other servers configured with + |vim.lsp.add_filetype_config|. + + Differences:~ + `root_dir` + Will default to |getcwd()| instead of being required. + + NOTE: the function options in {config} like {config.on_init} are for Lua + callbacks, not Vim callbacks. +> + " Go example + call lsp#add_filetype_config({ + \ 'filetype': 'go', + \ 'name': 'gopls', + \ 'cmd': 'gopls' + \ }) + " Python example + call lsp#add_filetype_config({ + \ 'filetype': 'python', + \ 'name': 'pyls', + \ 'cmd': 'pyls' + \ }) + " Rust example + call lsp#add_filetype_config({ + \ 'filetype': 'rust', + \ 'name': 'rls', + \ 'cmd': 'rls', + \ 'capabilities': { + \ 'clippy_preference': 'on', + \ 'all_targets': v:false, + \ 'build_on_save': v:true, + \ 'wait_to_build': 0 + \ }}) +< +> + -- From Lua + vim.lsp.add_filetype_config { + name = "clangd"; + filetype = {"c", "cpp"}; + cmd = "clangd -background-index"; + capabilities = { + offsetEncoding = {"utf-8", "utf-16"}; + }; + on_init = vim.schedule_wrap(function(client, result) + if result.offsetEncoding then + client.offset_encoding = result.offsetEncoding + end + end) + } +< + *vim.lsp.copy_filetype_config()* +vim.lsp.copy_filetype_config({existing_name}, [{override_config}]) + + You can use this to copy an existing filetype configuration and change it by + specifying {override_config} which will override any properties in the + existing configuration. If you don't specify a new unique name with + {override_config.name} then it will try to create one and return it. + + Returns:~ + `name` the new configuration name. + + *vim.lsp.get_filetype_client_by_name()* +vim.lsp.get_filetype_client_by_name({name}) + + Use this to look up a client by its name created from + |vim.lsp.add_filetype_config()|. + + Returns nil if the client is not active or the name is not valid. + +================================================================================ + *lsp-core-api* +These are the core api functions for working with clients. You will mainly be +using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations +and |vim.lsp.get_client_by_id()| to retrieve a client by its id after it has +initialized (or {config.on_init}. see below) + + *vim.lsp.start_client()* + +vim.lsp.start_client({config}) + + The main function used for starting clients. + Start a client and initialize it. + + Its arguments are passed via a configuration object {config}. + + Mandatory parameters:~ + + `root_dir` + {string} specifying the directory where the LSP server will base + as its rootUri on initialization. + + `cmd` + {string} or {list} which is the base command to execute for the LSP. A + string will be run using |'shell'| and a list will be interpreted as a + bare command with arguments passed. This is the same as |jobstart()|. + + Optional parameters:~ + + `cmd_cwd` + {string} specifying the directory to launch the `cmd` process. This is not + related to `root_dir`. + By default, |getcwd()| is used. + + `cmd_env` + {table} specifying the environment flags to pass to the LSP on spawn. + This can be specified using keys like a map or as a list with `k=v` pairs + or both. Non-string values are coerced to a string. + For example: + `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }` + + `capabilities` + A {table} which will be used instead of + `vim.lsp.protocol.make_client_capabilities()` which contains neovim's + default capabilities and passed to the language server on initialization. + You'll probably want to use make_client_capabilities() and modify the + result. + NOTE: + To send an empty dictionary, you should use + `{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as + an array. + + `callbacks` + A {table} of whose keys are language server method names and the values + are `function(err, method, params, client_id)` See |lsp-callbacks| for + more. This will be combined with |lsp-builtin-callbacks| to provide + defaults. + + `init_options` + A {table} of values to pass in the initialization request as + `initializationOptions`. See the `initialize` in the LSP spec. + + `name` + A {string} used in log messages. Defaults to {client_id} + + `offset_encoding` + One of "utf-8", "utf-16", or "utf-32" which is the encoding that the LSP + server expects. + The default encoding for Language Server Protocol is UTF-16, but there are + language servers which may use other encodings. + By default, it is "utf-16" as specified in the LSP specification. The + client does not verify this is correct. + + `on_error(code, ...)` + A function for handling errors thrown by client operation. {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. + `vim.lsp.client_errors[code]` can be used to retrieve a human + understandable string. + + `on_init(client, initialize_result)` + A function which is called after the request `initialize` is completed. + `initialize_result` contains `capabilities` and anything else the server + may send. For example, `clangd` sends `initialize_result.offsetEncoding` + if `capabilities.offsetEncoding` was sent to it. You can *only* modify the + `client.offset_encoding` here before any notifications are sent. + + `on_attach(client, bufnr)` + A function which is called after the client is attached to a buffer. + + `on_exit(code, signal, client_id)` + A function which is called after the client has exited. code is the exit + code of the process, and signal is a number describing the signal used to + terminate (if any). + + `trace` + "off" | "messages" | "verbose" | nil passed directly to the language + server in the initialize request. + Invalid/empty values will default to "off" + + Returns:~ + {client_id} + You can use |vim.lsp.get_client_by_id()| to get the actual client object. + See |lsp-client| for what the client structure will be. + + NOTE: The client is only available *after* it has been initialized, which + may happen after a small delay (or never if there is an error). For this + reason, you may want to use `on_init` to do any actions once the client has + been initialized. + + *lsp-client* + +The client object has some methods and members related to using the client. + + Methods:~ + + `request(method, params, [callback])` + Send a request to the server. If callback is not specified, it will use + {client.callbacks} to try to find a callback. If one is not found there, + then an error will occur. + This is a thin wrapper around {client.rpc.request} with some additional + checking. + Returns a boolean to indicate if the notification was successful. If it + is false, then it will always be false (the client has shutdown). + If it was successful, then it will return the request id as the second + result. You can use this with `notify("$/cancel", { id = request_id })` + to cancel the request. This helper is made automatically with + |vim.lsp.buf_request()| + Returns: status, [client_id] + + `notify(method, params)` + This is just {client.rpc.notify}() + Returns a boolean to indicate if the notification was successful. If it + is false, then it will always be false (the client has shutdown). + Returns: status + + `cancel_request(id)` + This is just {client.rpc.notify}("$/cancelRequest", { id = id }) + Returns the same as `notify()`. + + `stop([force])` + Stop a client, optionally with force. + By default, it will just ask the server to shutdown without force. + If you request to stop a client which has previously been requested to + shutdown, it will automatically escalate and force shutdown. + + `is_stopped()` + Returns true if the client is fully stopped. + + Members: ~ + `id` (number) + The id allocated to the client. + + `name` (string) + If a name is specified on creation, that will be used. Otherwise it is + just the client id. This is used for logs and messages. + + `offset_encoding` (string) + The encoding used for communicating with the server. You can modify this + in the `on_init` method before text is sent to the server. + + `callbacks` (table) + The callbacks used by the client as described in |lsp-callbacks|. + + `config` (table) + A copy of the table that was passed by the user to + |vim.lsp.start_client()|. + + `server_capabilities` (table) + The response from the server sent on `initialize` describing the + server's capabilities. + + `resolved_capabilities` (table) + A normalized table of capabilities that we have detected based on the + initialize response from the server in `server_capabilities`. + + + *vim.lsp.buf_attach_client()* +vim.lsp.buf_attach_client({bufnr}, {client_id}) + + Implements the `textDocument/did*` notifications required to track a buffer + for any language server. + + Without calling this, the server won't be notified of changes to a buffer. + + *vim.lsp.get_client_by_id()* +vim.lsp.get_client_by_id({client_id}) + + Look up an active client by its id, returns nil if it is not yet initialized + or is not a valid id. Returns |lsp-client| + + *vim.lsp.stop_client()* +vim.lsp.stop_client({client_id}, [{force}]) + + Stop a client, optionally with force. + By default, it will just ask the server to shutdown without force. + If you request to stop a client which has previously been requested to + shutdown, it will automatically escalate and force shutdown. + + You can also use `client.stop()` if you have access to the client. + + *vim.lsp.stop_all_clients()* +vim.lsp.stop_all_clients([{force}]) + + |vim.lsp.stop_client()|, but for all active clients. + + *vim.lsp.get_active_clients()* +vim.lsp.get_active_clients() + + Return a list of all of the active clients. See |lsp-client| for a + description of what a client looks like. + + *vim.lsp.rpc_response_error()* +vim.lsp.rpc_response_error({code}, [{message}], [{data}]) + + Helper function to create an RPC response object/table. This is an alias for + |vim.lsp.rpc.rpc_response_error|. Code must be an RPC error code as + described in `vim.lsp.protocol.ErrorCodes`. + + You can describe an optional {message} string or arbitrary {data} to send to + the server. + +================================================================================ + *vim.lsp.builtin_callbacks* + +The |vim.lsp.builtin_callbacks| table contains the default |lsp-callbacks| +that are used when creating a new client. The keys are the LSP method names. + +The following requests and notifications have built-in callbacks defined to +handle the response in an idiomatic way. + + textDocument/completion + textDocument/declaration + textDocument/definition + textDocument/hover + textDocument/implementation + textDocument/rename + textDocument/signatureHelp + textDocument/typeDefinition + window/logMessage + window/showMessage + +You can check these via `vim.tbl_keys(vim.lsp.builtin_callbacks)`. + +These will be automatically used and can be overridden by users (either by +modifying the |vim.lsp.builtin_callbacks| object or on a per-client basis +by passing in a table via the {callbacks} parameter on |vim.lsp.start_client| +or |vim.lsp.add_filetype_config|. + +More information about callbacks can be found in |lsp-callbacks|. + +================================================================================ + *lsp-callbacks* + +Callbacks are functions which are called in a variety of situations by the +client. Their signature is `function(err, method, params, client_id)` They can +be set by the {callbacks} parameter for |vim.lsp.start_client| and +|vim.lsp.add_filetype_config| or via the |vim.lsp.builtin_callbacks|. + +This will be called for: +- notifications from the server, where `err` will always be `nil` +- requests initiated by the server. The parameter `err` will be `nil` here as + well. + For these, you can respond by returning two values: `result, err` The + err must be in the format of an RPC error, which is + `{ code, message, data? }` + You can use |vim.lsp.rpc_response_error()| to help with creating this object. +- as a callback for requests initiated by the client if the request doesn't + explicitly specify a callback (such as in |vim.lsp.buf_request|). + +================================================================================ + *vim.lsp.protocol* +vim.lsp.protocol + + Contains constants as described in the Language Server Protocol + specification and helper functions for creating protocol related objects. + + https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md + + Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow + reverse lookup by either the number or string name. + + e.g. vim.lsp.protocol.TextDocumentSyncKind.Full == 1 + vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" + + Utility functions used internally are: + `vim.lsp.make_client_capabilities()` + Make a ClientCapabilities object. These are the builtin + capabilities. + `vim.lsp.make_text_document_position_params()` + Make a TextDocumentPositionParams object. + `vim.lsp.resolve_capabilities(server_capabilites)` + Creates a normalized object describing capabilities from the server + capabilities. + +================================================================================ + *vim.lsp.util* + +TODO: Describe the utils here for handling/applying things from LSP. + +================================================================================ + *lsp-buf-methods* +There are methods which operate on the buffer level for all of the active +clients attached to the buffer. + + *vim.lsp.buf_request()* +vim.lsp.buf_request({bufnr}, {method}, {params}, [{callback}]) + Send a async request for all the clients active and attached to the buffer. + + Parameters: ~ + {bufnr}: The buffer handle or 0 for the current buffer. + + {method}: The LSP method name. + + {params}: The parameters to send. + + {callback}: An optional `function(err, method, params, client_id)` which + will be called for this request. If you do not specify it, then it will + use the client's callback in {client.callbacks}. See |lsp-callbacks| for + more information. + + Returns:~ + + A table from client id to the request id for all of the successful + requests. + + The second result is a function which can be used to cancel all the + requests. You can do this individually with `client.cancel_request()` + + *vim.lsp.buf_request_sync()* +vim.lsp.buf_request_sync({bufnr}, {method}, {params}, [{timeout_ms}]) + Calls |vim.lsp.buf_request()|, but it will wait for the result and block Vim + in the process. + The parameters are the same as |vim.lsp.buf_request()|, but the return + result is different. + It will wait maximum of {timeout_ms} which defaults to 100ms. + + Returns:~ + + If the timeout is exceeded or a cancel is sent or an error, it will cancel + the request and return `nil, err` where `err` is a string that describes + the reason why it failed. + + If it is successful, it will return a table from client id to result id. + + *vim.lsp.buf_notify()* +vim.lsp.buf_notify({bufnr}, {method}, {params}) + Send a notification to all servers on the buffer. + + Parameters: ~ + {bufnr}: The buffer handle or 0 for the current buffer. + + {method}: The LSP method name. + + {params}: The parameters to send. + +================================================================================ + *lsp-logging* + + *lsp#set_log_level()* +lsp#set_log_level({level}) + You can set the log level for language server client logging. + Possible values: "trace", "debug", "info", "warn", "error" + + Default: "warn" + + Example: `call lsp#set_log_level("debug")` + + *lsp#get_log_path()* + *vim.lsp.get_log_path()* +lsp#get_log_path() +vim.lsp.get_log_path() + Returns the path that LSP logs are written. + + *vim.lsp.log_levels* +vim.lsp.log_levels + 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 + +================================================================================ + *lsp-omnifunc* + *vim.lsp.omnifunc()* + *lsp#omnifunc* +lsp#omnifunc({findstart}, {base}) +vim.lsp.omnifunc({findstart}, {base}) + +To configure omnifunc, add the following in your init.vim: +> + set omnifunc=lsp#omnifunc + + " This is optional, but you may find it useful + autocmd CompleteDone * pclose +< +================================================================================ + *lsp-vim-functions* + +These methods can be used in mappings and are the equivalent of using the +request from lua as follows: + +> + lua vim.lsp.buf_request(0, "textDocument/hover", vim.lsp.protocol.make_text_document_position_params()) +< + + lsp#text_document_declaration() + lsp#text_document_definition() + lsp#text_document_hover() + lsp#text_document_implementation() + lsp#text_document_signature_help() + lsp#text_document_type_definition() + +> + " Example config + autocmd Filetype rust,python,go,c,cpp setl omnifunc=lsp#omnifunc + nnoremap ;dc :call lsp#text_document_declaration() + nnoremap ;df :call lsp#text_document_definition() + nnoremap ;h :call lsp#text_document_hover() + nnoremap ;i :call lsp#text_document_implementation() + nnoremap ;s :call lsp#text_document_signature_help() + nnoremap ;td :call lsp#text_document_type_definition() +< +================================================================================ + *lsp-advanced-js-example* + +For more advanced configurations where just filtering by filetype isn't +sufficient, you can use the `vim.lsp.start_client()` and +`vim.lsp.buf_attach_client()` commands to easily customize the configuration +however you please. For example, if you want to do your own filtering, or +start a new LSP client based on the root directory for if you plan to work +with multiple projects in a single session. Below is a fully working Lua +example which can do exactly that. + +The example will: +1. Check for each new buffer whether or not we want to start an LSP client. +2. Try to find a root directory by ascending from the buffer's path. +3. Create a new LSP for that root directory if one doesn't exist. +4. Attach the buffer to the client for that root directory. + +> + -- Some path manipulation utilities + local function is_dir(filename) + local stat = vim.loop.fs_stat(filename) + return stat and stat.type == 'directory' or false + end + + local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" + -- Asumes filepath is a file. + local function dirname(filepath) + local is_changed = false + local result = filepath:gsub(path_sep.."([^"..path_sep.."]+)$", function() + is_changed = true + return "" + end) + return result, is_changed + end + + local function path_join(...) + return table.concat(vim.tbl_flatten {...}, path_sep) + end + + -- Ascend the buffer's path until we find the rootdir. + -- is_root_path is a function which returns bool + local function buffer_find_root_dir(bufnr, is_root_path) + local bufname = vim.api.nvim_buf_get_name(bufnr) + if vim.fn.filereadable(bufname) == 0 then + return nil + end + local dir = bufname + -- Just in case our algo is buggy, don't infinite loop. + for _ = 1, 100 do + local did_change + dir, did_change = dirname(dir) + if is_root_path(dir, bufname) then + return dir, bufname + end + -- If we can't ascend further, then stop looking. + if not did_change then + return nil + end + end + end + + -- A table to store our root_dir to client_id lookup. We want one LSP per + -- root directory, and this is how we assert that. + local javascript_lsps = {} + -- Which filetypes we want to consider. + local javascript_filetypes = { + ["javascript.jsx"] = true; + ["javascript"] = true; + ["typescript"] = true; + ["typescript.jsx"] = true; + } + + -- Create a template configuration for a server to start, minus the root_dir + -- which we will specify later. + local javascript_lsp_config = { + name = "javascript"; + cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") }; + } + + -- This needs to be global so that we can call it from the autocmd. + function check_start_javascript_lsp() + local bufnr = vim.api.nvim_get_current_buf() + -- Filter which files we are considering. + if not javascript_filetypes[vim.api.nvim_buf_get_option(bufnr, 'filetype')] then + return + end + -- Try to find our root directory. We will define this as a directory which contains + -- node_modules. Another choice would be to check for `package.json`, or for `.git`. + local root_dir = buffer_find_root_dir(bufnr, function(dir) + return is_dir(path_join(dir, 'node_modules')) + -- return vim.fn.filereadable(path_join(dir, 'package.json')) == 1 + -- return is_dir(path_join(dir, '.git')) + end) + -- We couldn't find a root directory, so ignore this file. + if not root_dir then return end + + -- Check if we have a client alredy or start and store it. + local client_id = javascript_lsps[root_dir] + if not client_id then + local new_config = vim.tbl_extend("error", javascript_lsp_config, { + root_dir = root_dir; + }) + client_id = vim.lsp.start_client(new_config) + javascript_lsps[root_dir] = client_id + end + -- Finally, attach to the buffer to track changes. This will do nothing if we + -- are already attached. + vim.lsp.buf_attach_client(bufnr, client_id) + end + + vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]] +< + +vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua new file mode 100644 index 0000000000..9dbe03dace --- /dev/null +++ b/runtime/lua/vim/lsp.lua @@ -0,0 +1,1055 @@ +local builtin_callbacks = require 'vim.lsp.builtin_callbacks' +local log = require 'vim.lsp.log' +local lsp_rpc = require 'vim.lsp.rpc' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' + +local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option + = vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option +local uv = vim.loop +local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend +local validate = vim.validate + +local lsp = { + protocol = protocol; + builtin_callbacks = builtin_callbacks; + util = util; + -- Allow raw RPC access. + rpc = lsp_rpc; + -- Export these directly from rpc. + rpc_response_error = lsp_rpc.rpc_response_error; + -- You probably won't need this directly, since __tostring is set for errors + -- by the RPC. + -- format_rpc_error = lsp_rpc.format_rpc_error; +} + +-- TODO consider whether 'eol' or 'fixeol' should change the nvim_buf_get_lines that send. +-- TODO improve handling of scratch buffers with LSP attached. + +local function resolve_bufnr(bufnr) + validate { bufnr = { bufnr, 'n', true } } + if bufnr == nil or bufnr == 0 then + return vim.api.nvim_get_current_buf() + end + return bufnr +end + +local function is_dir(filename) + validate{filename={filename,'s'}} + local stat = uv.fs_stat(filename) + return stat and stat.type == 'directory' or false +end + +-- TODO Use vim.wait when that is available, but provide an alternative for now. +local wait = vim.wait or function(timeout_ms, condition, interval) + validate { + timeout_ms = { timeout_ms, 'n' }; + condition = { condition, 'f' }; + interval = { interval, 'n', true }; + } + assert(timeout_ms > 0, "timeout_ms must be > 0") + local _ = log.debug() and log.debug("wait.fallback", timeout_ms) + interval = interval or 200 + local interval_cmd = "sleep "..interval.."m" + local timeout = timeout_ms + uv.now() + -- TODO is there a better way to sync this? + while true do + uv.update_time() + if condition() then + return 0 + end + if uv.now() >= timeout then + return -1 + end + nvim_command(interval_cmd) + -- vim.loop.sleep(10) + end +end +local wait_result_reason = { [-1] = "timeout"; [-2] = "interrupted"; [-3] = "error" } + +local valid_encodings = { + ["utf-8"] = 'utf-8'; ["utf-16"] = 'utf-16'; ["utf-32"] = 'utf-32'; + ["utf8"] = 'utf-8'; ["utf16"] = 'utf-16'; ["utf32"] = 'utf-32'; + UTF8 = 'utf-8'; UTF16 = 'utf-16'; UTF32 = 'utf-32'; +} + +local client_index = 0 +local function next_client_id() + client_index = client_index + 1 + return client_index +end +-- Tracks all clients created via lsp.start_client +local active_clients = {} +local all_buffer_active_clients = {} +local uninitialized_clients = {} + +local function for_each_buffer_client(bufnr, callback) + validate { + callback = { callback, 'f' }; + } + bufnr = resolve_bufnr(bufnr) + local client_ids = all_buffer_active_clients[bufnr] + if not client_ids or tbl_isempty(client_ids) then + return + end + for client_id in pairs(client_ids) do + -- This is unlikely to happen. Could only potentially happen in a race + -- condition between literally a single statement. + -- We could skip this error, but let's error for now. + local client = active_clients[client_id] + -- or error(string.format("Client %d has already shut down.", client_id)) + if client then + callback(client, client_id) + end + end +end + +-- Error codes to be used with `on_error` from |vim.lsp.start_client|. +-- Can be used to look up the string from a the number or the number +-- from the string. +lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_reverse_lookup { + ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1; +}) + +local function validate_encoding(encoding) + validate { + encoding = { encoding, 's' }; + } + return valid_encodings[encoding:lower()] + or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) +end + +local function validate_command(input) + local cmd, cmd_args + if type(input) == 'string' then + -- Use a shell to execute the command if it is a string. + cmd = vim.api.nvim_get_option('shell') + cmd_args = {vim.api.nvim_get_option('shellcmdflag'), input} + elseif vim.tbl_islist(input) then + cmd = input[1] + cmd_args = {} + -- Don't mutate our input. + for i, v in ipairs(input) do + assert(type(v) == 'string', "input arguments must be strings") + if i > 1 then + table.insert(cmd_args, v) + end + end + else + error("cmd type must be string or list.") + end + return cmd, cmd_args +end + +local function optional_validator(fn) + return function(v) + return v == nil or fn(v) + end +end + +local function validate_client_config(config) + validate { + config = { config, 't' }; + } + validate { + root_dir = { config.root_dir, is_dir, "directory" }; + callbacks = { config.callbacks, "t", true }; + capabilities = { config.capabilities, "t", true }; + -- cmd = { config.cmd, "s", false }; + cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; + cmd_env = { config.cmd_env, "f", true }; + name = { config.name, 's', true }; + on_error = { config.on_error, "f", true }; + on_exit = { config.on_exit, "f", true }; + on_init = { config.on_init, "f", true }; + offset_encoding = { config.offset_encoding, "s", true }; + } + local cmd, cmd_args = validate_command(config.cmd) + local offset_encoding = valid_encodings.UTF16 + if config.offset_encoding then + offset_encoding = validate_encoding(config.offset_encoding) + end + return { + cmd = cmd; cmd_args = cmd_args; + offset_encoding = offset_encoding; + } +end + +local function text_document_did_open_handler(bufnr, client) + if not client.resolved_capabilities.text_document_open_close then + return + end + if not vim.api.nvim_buf_is_loaded(bufnr) then + return + end + local params = { + textDocument = { + version = 0; + uri = vim.uri_from_bufnr(bufnr); + -- TODO make sure our filetypes are compatible with languageId names. + languageId = nvim_buf_get_option(bufnr, 'filetype'); + text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n'); + } + } + client.notify('textDocument/didOpen', params) +end + + +--- Start a client and initialize it. +-- Its arguments are passed via a configuration object. +-- +-- Mandatory parameters: +-- +-- root_dir: {string} specifying the directory where the LSP server will base +-- as its rootUri on initialization. +-- +-- cmd: {string} or {list} which is the base command to execute for the LSP. A +-- string will be run using |'shell'| and a list will be interpreted as a bare +-- command with arguments passed. This is the same as |jobstart()|. +-- +-- Optional parameters: + +-- cmd_cwd: {string} specifying the directory to launch the `cmd` process. This +-- is not related to `root_dir`. By default, |getcwd()| is used. +-- +-- cmd_env: {table} specifying the environment flags to pass to the LSP on +-- spawn. This can be specified using keys like a map or as a list with `k=v` +-- pairs or both. Non-string values are coerced to a string. +-- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`. +-- +-- capabilities: A {table} which will be used instead of +-- `vim.lsp.protocol.make_client_capabilities()` which contains neovim's +-- default capabilities and passed to the language server on initialization. +-- You'll probably want to use make_client_capabilities() and modify the +-- result. +-- NOTE: +-- To send an empty dictionary, you should use +-- `{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as +-- an array. +-- +-- callbacks: A {table} of whose keys are language server method names and the +-- values are `function(err, method, params, client_id)`. +-- This will be called for: +-- - notifications from the server, where `err` will always be `nil` +-- - requests initiated by the server. For these, you can respond by returning +-- two values: `result, err`. The err must be in the format of an RPC error, +-- which is `{ code, message, data? }`. You can use |vim.lsp.rpc_response_error()| +-- to help with this. +-- - as a callback for requests initiated by the client if the request doesn't +-- explicitly specify a callback. +-- +-- init_options: A {table} of values to pass in the initialization request +-- as `initializationOptions`. See the `initialize` in the LSP spec. +-- +-- name: A {string} used in log messages. Defaults to {client_id} +-- +-- offset_encoding: One of 'utf-8', 'utf-16', or 'utf-32' which is the +-- encoding that the LSP server expects. By default, it is 'utf-16' as +-- specified in the LSP specification. The client does not verify this +-- is correct. +-- +-- on_error(code, ...): A function for handling errors thrown by client +-- operation. {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. `vim.lsp.client_errors[code]` can be used to retrieve a +-- human understandable string. +-- +-- on_init(client, initialize_result): A function which is called after the +-- request `initialize` is completed. `initialize_result` contains +-- `capabilities` and anything else the server may send. For example, `clangd` +-- sends `result.offsetEncoding` if `capabilities.offsetEncoding` was sent to +-- it. +-- +-- on_exit(code, signal, client_id): A function which is called after the +-- client has exited. code is the exit code of the process, and signal is a +-- number describing the signal used to terminate (if any). +-- +-- on_attach(client, bufnr): A function which is called after the client is +-- attached to a buffer. +-- +-- trace: 'off' | 'messages' | 'verbose' | nil passed directly to the language +-- server in the initialize request. Invalid/empty values will default to 'off' +-- +-- @returns client_id You can use |vim.lsp.get_client_by_id()| to get the +-- actual client. +-- +-- NOTE: The client is only available *after* it has been initialized, which +-- may happen after a small delay (or never if there is an error). +-- For this reason, you may want to use `on_init` to do any actions once the +-- client has been initialized. +function lsp.start_client(config) + local cleaned_config = validate_client_config(config) + local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding + + local client_id = next_client_id() + + local callbacks = tbl_extend("keep", config.callbacks or {}, builtin_callbacks) + -- Copy metatable if it has one. + if config.callbacks and config.callbacks.__metatable then + setmetatable(callbacks, getmetatable(config.callbacks)) + end + local name = config.name or tostring(client_id) + local log_prefix = string.format("LSP[%s]", name) + + local handlers = {} + + function handlers.notification(method, params) + local _ = log.debug() and log.debug('notification', method, params) + local callback = callbacks[method] + if callback then + -- Method name is provided here for convenience. + callback(nil, method, params, client_id) + end + end + + function handlers.server_request(method, params) + local _ = log.debug() and log.debug('server_request', method, params) + local callback = callbacks[method] + if callback then + local _ = log.debug() and log.debug("server_request: found callback for", method) + return callback(nil, method, params, client_id) + end + local _ = log.debug() and log.debug("server_request: no callback found for", method) + return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) + end + + function handlers.on_error(code, err) + local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) + nvim_err_writeln(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) + if config.on_error then + local status, usererr = pcall(config.on_error, code, err) + if not status then + local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr }) + nvim_err_writeln(log_prefix.." user on_error failed: "..tostring(usererr)) + end + end + end + + function handlers.on_exit(code, signal) + active_clients[client_id] = nil + uninitialized_clients[client_id] = nil + for _, client_ids in pairs(all_buffer_active_clients) do + client_ids[client_id] = nil + end + if config.on_exit then + pcall(config.on_exit, code, signal, client_id) + end + end + + -- Start the RPC client. + local rpc = lsp_rpc.start(cmd, cmd_args, handlers, { + cwd = config.cmd_cwd; + env = config.cmd_env; + }) + + local client = { + id = client_id; + name = name; + rpc = rpc; + offset_encoding = offset_encoding; + callbacks = callbacks; + config = config; + } + + -- Store the uninitialized_clients for cleanup in case we exit before + -- initialize finishes. + uninitialized_clients[client_id] = client; + + local function initialize() + local valid_traces = { + off = 'off'; messages = 'messages'; verbose = 'verbose'; + } + local initialize_params = { + -- The process Id of the parent process that started the server. Is null if + -- the process has not been started by another process. If the parent + -- process is not alive then the server should exit (see exit notification) + -- its process. + processId = uv.getpid(); + -- The rootPath of the workspace. Is null if no folder is open. + -- + -- @deprecated in favour of rootUri. + rootPath = nil; + -- The rootUri of the workspace. Is null if no folder is open. If both + -- `rootPath` and `rootUri` are set `rootUri` wins. + rootUri = vim.uri_from_fname(config.root_dir); +-- rootUri = vim.uri_from_fname(vim.fn.expand("%:p:h")); + -- User provided initialization options. + initializationOptions = config.init_options; + -- The capabilities provided by the client (editor or tool) + capabilities = config.capabilities or protocol.make_client_capabilities(); + -- The initial trace setting. If omitted trace is disabled ('off'). + -- trace = 'off' | 'messages' | 'verbose'; + trace = valid_traces[config.trace] or 'off'; + -- The workspace folders configured in the client when the server starts. + -- This property is only available if the client supports workspace folders. + -- It can be `null` if the client supports workspace folders but none are + -- configured. + -- + -- Since 3.6.0 + -- workspaceFolders?: WorkspaceFolder[] | null; + -- export interface WorkspaceFolder { + -- -- The associated URI for this workspace folder. + -- uri + -- -- The name of the workspace folder. Used to refer to this + -- -- workspace folder in the user interface. + -- name + -- } + workspaceFolders = nil; + } + local _ = log.debug() and log.debug(log_prefix, "initialize_params", initialize_params) + rpc.request('initialize', initialize_params, function(init_err, result) + assert(not init_err, tostring(init_err)) + assert(result, "server sent empty result") + rpc.notify('initialized', {}) + client.initialized = true + uninitialized_clients[client_id] = nil + client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities") + -- These are the cleaned up capabilities we use for dynamically deciding + -- when to send certain events to clients. + client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities) + if config.on_init then + local status, err = pcall(config.on_init, client, result) + if not status then + pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err) + end + end + local _ = log.debug() and log.debug(log_prefix, "server_capabilities", client.server_capabilities) + local _ = log.info() and log.info(log_prefix, "initialized", { resolved_capabilities = client.resolved_capabilities }) + + -- Only assign after initialized. + active_clients[client_id] = client + -- If we had been registered before we start, then send didOpen This can + -- happen if we attach to buffers before initialize finishes or if + -- someone restarts a client. + for bufnr, client_ids in pairs(all_buffer_active_clients) do + if client_ids[client_id] then + client._on_attach(bufnr) + end + end + end) + end + + local function unsupported_method(method) + local msg = "server doesn't support "..method + local _ = log.warn() and log.warn(msg) + nvim_err_writeln(msg) + return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) + end + + --- Checks capabilities before rpc.request-ing. + function client.request(method, params, callback) + if not callback then + callback = client.callbacks[method] + or error(string.format("request callback is empty and no default was found for client %s", client.name)) + end + local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback) + -- TODO keep these checks or just let it go anyway? + if (not client.resolved_capabilities.hover and method == 'textDocument/hover') + or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp') + or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') + or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') + then + callback(unsupported_method(method), method, nil, client_id) + return + end + return rpc.request(method, params, function(err, result) + callback(err, method, result, client_id) + end) + end + + function client.notify(...) + return rpc.notify(...) + end + + function client.cancel_request(id) + validate{id = {id, 'n'}} + return rpc.notify("$/cancelRequest", { id = id }) + end + + -- Track this so that we can escalate automatically if we've alredy tried a + -- graceful shutdown + local tried_graceful_shutdown = false + function client.stop(force) + local handle = rpc.handle + if handle:is_closing() then + return + end + if force or (not client.initialized) or tried_graceful_shutdown then + handle:kill(15) + return + end + tried_graceful_shutdown = true + -- Sending a signal after a process has exited is acceptable. + rpc.request('shutdown', nil, function(err, _) + if err == nil then + rpc.notify('exit') + else + -- If there was an error in the shutdown request, then term to be safe. + handle:kill(15) + end + end) + end + + function client.is_stopped() + return rpc.handle:is_closing() + end + + function client._on_attach(bufnr) + text_document_did_open_handler(bufnr, client) + if config.on_attach then + -- TODO(ashkan) handle errors. + pcall(config.on_attach, client, bufnr) + end + end + + initialize() + + return client_id +end + +local function once(fn) + local value + return function(...) + if not value then value = fn(...) end + return value + end +end + +local text_document_did_change_handler +do + local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; } + text_document_did_change_handler = function(_, bufnr, changedtick, + firstline, lastline, new_lastline, old_byte_size, old_utf32_size, + old_utf16_size) + local _ = log.debug() and log.debug("on_lines", bufnr, changedtick, firstline, + lastline, new_lastline, old_byte_size, old_utf32_size, old_utf16_size, nvim_buf_get_lines(bufnr, firstline, new_lastline, true)) + if old_byte_size == 0 then + return + end + -- Don't do anything if there are no clients attached. + if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then + return + end + -- Lazy initialize these because clients may not even need them. + local incremental_changes = once(function(client) + local size_index = encoding_index[client.offset_encoding] + local length = select(size_index, old_byte_size, old_utf16_size, old_utf32_size) + local lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true) + -- This is necessary because we are specifying the full line including the + -- newline in range. Therefore, we must replace the newline as well. + if #lines > 0 then + table.insert(lines, '') + end + return { + range = { + start = { line = firstline, character = 0 }; + ["end"] = { line = lastline, character = 0 }; + }; + rangeLength = length; + text = table.concat(lines, '\n'); + }; + end) + local full_changes = once(function() + return { + text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), "\n"); + }; + end) + local uri = vim.uri_from_bufnr(bufnr) + for_each_buffer_client(bufnr, function(client, _client_id) + local text_document_did_change = client.resolved_capabilities.text_document_did_change + local changes + if text_document_did_change == protocol.TextDocumentSyncKind.None then + return + --[=[ TODO(ashkan) there seem to be problems with the byte_sizes sent by + -- neovim right now so only send the full content for now. In general, we + -- can assume that servers *will* support both versions anyway, as there + -- is no way to specify the sync capability by the client. + -- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both. + --]=] + elseif true or text_document_did_change == protocol.TextDocumentSyncKind.Full then + changes = full_changes(client) + elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then + changes = incremental_changes(client) + end + client.notify("textDocument/didChange", { + textDocument = { + uri = uri; + version = changedtick; + }; + contentChanges = { changes; } + }) + end) + end +end + +-- Buffer lifecycle handler for textDocument/didSave +function lsp._text_document_did_save_handler(bufnr) + bufnr = resolve_bufnr(bufnr) + local uri = vim.uri_from_bufnr(bufnr) + local text = once(function() + return table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n') + end) + for_each_buffer_client(bufnr, function(client, _client_id) + if client.resolved_capabilities.text_document_save then + local included_text + if client.resolved_capabilities.text_document_save_include_text then + included_text = text() + end + client.notify('textDocument/didSave', { + textDocument = { + uri = uri; + text = included_text; + } + }) + end + end) +end + +-- Implements the textDocument/did* notifications required to track a buffer +-- for any language server. +-- @param bufnr [number] buffer handle or 0 for current +-- @param client_id [number] the client id +function lsp.buf_attach_client(bufnr, client_id) + validate { + bufnr = {bufnr, 'n', true}; + client_id = {client_id, 'n'}; + } + bufnr = resolve_bufnr(bufnr) + local buffer_client_ids = all_buffer_active_clients[bufnr] + -- This is our first time attaching to this buffer. + if not buffer_client_ids then + buffer_client_ids = {} + all_buffer_active_clients[bufnr] = buffer_client_ids + + local uri = vim.uri_from_bufnr(bufnr) + nvim_command(string.format("autocmd BufWritePost lua vim.lsp._text_document_did_save_handler(0)", bufnr)) + -- First time, so attach and set up stuff. + vim.api.nvim_buf_attach(bufnr, false, { + on_lines = text_document_did_change_handler; + on_detach = function() + local params = { textDocument = { uri = uri; } } + for_each_buffer_client(bufnr, function(client, _client_id) + if client.resolved_capabilities.text_document_open_close then + client.notify('textDocument/didClose', params) + end + end) + all_buffer_active_clients[bufnr] = nil + end; + -- TODO if we know all of the potential clients ahead of time, then we + -- could conditionally set this. + -- utf_sizes = size_index > 1; + utf_sizes = true; + }) + end + if buffer_client_ids[client_id] then return end + -- This is our first time attaching this client to this buffer. + buffer_client_ids[client_id] = true + + local client = active_clients[client_id] + -- Send didOpen for the client if it is initialized. If it isn't initialized + -- then it will send didOpen on initialize. + if client then + client._on_attach(bufnr) + end + return true +end + +-- Check if a buffer is attached for a particular client. +-- @param bufnr [number] buffer handle or 0 for current +-- @param client_id [number] the client id +function lsp.buf_is_attached(bufnr, client_id) + return (all_buffer_active_clients[bufnr] or {})[client_id] == true +end + +-- Look up an active client by its id, returns nil if it is not yet initialized +-- or is not a valid id. +-- @param client_id number the client id. +function lsp.get_client_by_id(client_id) + return active_clients[client_id] +end + +-- Stop a client by its id, optionally with force. +-- You can also use the `stop()` function on a client if you already have +-- access to it. +-- By default, it will just ask the server to shutdown without force. +-- If you request to stop a client which has previously been requested to shutdown, +-- it will automatically force shutdown. +-- @param client_id number the client id. +-- @param force boolean (optional) whether to use force or request shutdown +function lsp.stop_client(client_id, force) + local client + client = active_clients[client_id] + if client then + client.stop(force) + return + end + client = uninitialized_clients[client_id] + if client then + client.stop(true) + end +end + +-- Returns a list of all the active clients. +function lsp.get_active_clients() + return vim.tbl_values(active_clients) +end + +-- Stop all the clients, optionally with force. +-- You can also use the `stop()` function on a client if you already have +-- access to it. +-- By default, it will just ask the server to shutdown without force. +-- If you request to stop a client which has previously been requested to shutdown, +-- it will automatically force shutdown. +-- @param force boolean (optional) whether to use force or request shutdown +function lsp.stop_all_clients(force) + for _, client in pairs(uninitialized_clients) do + client.stop(true) + end + for _, client in pairs(active_clients) do + client.stop(force) + end +end + +function lsp._vim_exit_handler() + log.info("exit_handler", active_clients) + for _, client in pairs(uninitialized_clients) do + client.stop(true) + end + -- TODO handle v:dying differently? + if tbl_isempty(active_clients) then + return + end + for _, client in pairs(active_clients) do + client.stop() + end + local wait_result = wait(500, function() return tbl_isempty(active_clients) end, 50) + if wait_result ~= 0 then + for _, client in pairs(active_clients) do + client.stop(true) + end + end +end + +nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") + +--- +--- Buffer level client functions. +--- + +--- Send a request to a server and return the response +-- @param bufnr [number] Buffer handle or 0 for current. +-- @param method [string] Request method name +-- @param params [table|nil] Parameters to send to the server +-- @param callback [function|nil] Request callback (or uses the client's callbacks) +-- +-- @returns: client_request_ids, cancel_all_requests +function lsp.buf_request(bufnr, method, params, callback) + validate { + bufnr = { bufnr, 'n', true }; + method = { method, 's' }; + callback = { callback, 'f', true }; + } + local client_request_ids = {} + for_each_buffer_client(bufnr, function(client, client_id) + local request_success, request_id = client.request(method, params, callback) + + -- This could only fail if the client shut down in the time since we looked + -- it up and we did the request, which should be rare. + if request_success then + client_request_ids[client_id] = request_id + end + end) + + local function cancel_all_requests() + for client_id, request_id in pairs(client_request_ids) do + local client = active_clients[client_id] + client.cancel_request(request_id) + end + end + + return client_request_ids, cancel_all_requests +end + +--- Send a request to a server and wait for the response. +-- @param bufnr [number] Buffer handle or 0 for current. +-- @param method [string] Request method name +-- @param params [string] Parameters to send to the server +-- @param timeout_ms [number|100] Maximum ms to wait for a result +-- +-- @returns: The table of {[client_id] = request_result} +function lsp.buf_request_sync(bufnr, method, params, timeout_ms) + local request_results = {} + local result_count = 0 + local function callback(err, _method, result, client_id) + request_results[client_id] = { error = err, result = result } + result_count = result_count + 1 + end + local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, callback) + local expected_result_count = 0 + for _ in pairs(client_request_ids) do + expected_result_count = expected_result_count + 1 + end + local wait_result = wait(timeout_ms or 100, function() + return result_count >= expected_result_count + end, 10) + if wait_result ~= 0 then + cancel() + return nil, wait_result_reason[wait_result] + end + return request_results +end + +--- Send a notification to a server +-- @param bufnr [number] (optional): The number of the buffer +-- @param method [string]: Name of the request method +-- @param params [string]: Arguments to send to the server +-- +-- @returns nil +function lsp.buf_notify(bufnr, method, params) + validate { + bufnr = { bufnr, 'n', true }; + method = { method, 's' }; + } + for_each_buffer_client(bufnr, function(client, _client_id) + client.rpc.notify(method, params) + end) +end + +--- Function which can be called to generate omnifunc compatible completion. +function lsp.omnifunc(findstart, base) + local _ = log.debug() and log.debug("omnifunc.findstart", { findstart = findstart, base = base }) + + local bufnr = resolve_bufnr() + local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {}) + if not has_buffer_clients then + if findstart == 1 then + return -1 + else + return {} + end + end + + if findstart == 1 then + return vim.fn.col('.') + else + local pos = vim.api.nvim_win_get_cursor(0) + local line = assert(nvim_buf_get_lines(bufnr, pos[1]-1, pos[1], false)[1]) + local _ = log.trace() and log.trace("omnifunc.line", pos, line) + local line_to_cursor = line:sub(1, pos[2]+1) + local _ = log.trace() and log.trace("omnifunc.line_to_cursor", line_to_cursor) + local params = { + textDocument = { + uri = vim.uri_from_bufnr(bufnr); + }; + position = { + -- 0-indexed for both line and character + line = pos[1] - 1, + character = pos[2], + }; + -- The completion context. This is only available if the client specifies + -- to send this using `ClientCapabilities.textDocument.completion.contextSupport === true` + -- context = nil or { + -- triggerKind = protocol.CompletionTriggerKind.Invoked; + -- triggerCharacter = nil or ""; + -- }; + } + -- TODO handle timeout error differently? Like via an error? + local client_responses = lsp.buf_request_sync(bufnr, 'textDocument/completion', params) or {} + local matches = {} + for _, response in pairs(client_responses) do + -- TODO how to handle errors? + if not response.error then + local data = response.result + local completion_items = util.text_document_completion_list_to_complete_items(data or {}, line_to_cursor) + local _ = log.trace() and log.trace("omnifunc.completion_items", completion_items) + vim.list_extend(matches, completion_items) + end + end + return matches + end +end + +--- +--- FileType based configuration utility +--- + +local all_filetype_configs = {} + +-- Lookup a filetype config client by its name. +function lsp.get_filetype_client_by_name(name) + local config = all_filetype_configs[name] + if config.client_id then + return active_clients[config.client_id] + end +end + +local function start_filetype_config(config) + config.client_id = lsp.start_client(config) + nvim_command(string.format( + "autocmd FileType %s silent lua vim.lsp.buf_attach_client(0, %d)", + table.concat(config.filetypes, ','), + config.client_id)) + return config.client_id +end + +-- Easy configuration option for common LSP use-cases. +-- This will lazy initialize the client when the filetypes specified are +-- encountered and attach to those buffers. +-- +-- The configuration options are the same as |vim.lsp.start_client()|, but +-- with a few additions and distinctions: +-- +-- Additional parameters: +-- - filetype: {string} or {list} of filetypes to attach to. +-- - name: A unique string among all other servers configured with +-- |vim.lsp.add_filetype_config|. +-- +-- Differences: +-- - root_dir: will default to |getcwd()| +-- +function lsp.add_filetype_config(config) + -- Additional defaults. + -- Keep a copy of the user's input for debugging reasons. + local user_config = config + config = tbl_extend("force", {}, user_config) + config.root_dir = config.root_dir or uv.cwd() + -- Validate config. + validate_client_config(config) + validate { + name = { config.name, 's' }; + } + assert(config.filetype, "config must have 'filetype' key") + + local filetypes + if type(config.filetype) == 'string' then + filetypes = { config.filetype } + elseif type(config.filetype) == 'table' then + filetypes = config.filetype + assert(not tbl_isempty(filetypes), "config.filetype must not be an empty table") + else + error("config.filetype must be a string or a list of strings") + end + + if all_filetype_configs[config.name] then + -- If the client exists, then it is likely that they are doing some kind of + -- reload flow, so let's not throw an error here. + if all_filetype_configs[config.name].client_id then + -- TODO log here? It might be unnecessarily annoying. + return + end + error(string.format('A configuration with the name %q already exists. They must be unique', config.name)) + end + + all_filetype_configs[config.name] = tbl_extend("keep", config, { + client_id = nil; + filetypes = filetypes; + user_config = user_config; + }) + + nvim_command(string.format( + "autocmd FileType %s ++once silent lua vim.lsp._start_filetype_config_client(%q)", + table.concat(filetypes, ','), + config.name)) +end + +-- Create a copy of an existing configuration, and override config with values +-- from new_config. +-- This is useful if you wish you create multiple LSPs with different root_dirs +-- or other use cases. +-- +-- You can specify a new unique name, but if you do not, a unique name will be +-- created like `name-dup_count`. +-- +-- existing_name: the name of the existing config to copy. +-- new_config: the new configuration options. @see |vim.lsp.start_client()|. +-- @returns string the new name. +function lsp.copy_filetype_config(existing_name, new_config) + local config = all_filetype_configs[existing_name] + or error(string.format("Configuration with name %q doesn't exist", existing_name)) + config = tbl_extend("force", config, new_config or {}) + config.client_id = nil + config.original_config_name = existing_name + + -- If the user didn't rename it, we will. + if config.name == existing_name then + -- Create a new, unique name. + local duplicate_count = 0 + for _, conf in pairs(all_filetype_configs) do + if conf.original_config_name == existing_name then + duplicate_count = duplicate_count + 1 + end + end + config.name = string.format("%s-%d", existing_name, duplicate_count + 1) + end + print("New config name:", config.name) + lsp.add_filetype_config(config) + return config.name +end + +-- Autocmd handler to actually start the client when an applicable filetype is +-- encountered. +function lsp._start_filetype_config_client(name) + local config = all_filetype_configs[name] + -- If it exists and is running, don't make it again. + if config.client_id and active_clients[config.client_id] then + -- TODO log here? + return + end + lsp.buf_attach_client(0, start_filetype_config(config)) + return config.client_id +end + +--- +--- Miscellaneous utilities. +--- + +-- Retrieve a map from client_id to client of all active buffer clients. +-- @param bufnr [number] (optional): buffer handle or 0 for current +function lsp.buf_get_clients(bufnr) + bufnr = resolve_bufnr(bufnr) + local result = {} + for_each_buffer_client(bufnr, function(client, client_id) + result[client_id] = client + end) + return result +end + +-- Print some debug information about the current buffer clients. +-- The output of this function should not be relied upon and may change. +function lsp.buf_print_debug_info(bufnr) + print(vim.inspect(lsp.buf_get_clients(bufnr))) +end + +-- Print some debug information about all LSP related things. +-- The output of this function should not be relied upon and may change. +function lsp.print_debug_info() + print(vim.inspect({ clients = active_clients, filetype_configs = all_filetype_configs })) +end + +-- 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 +lsp.log_levels = log.levels + +-- Set the log level for lsp logging. +-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error' +-- Level numbers begin with 'trace' at 0 +-- @param level [number|string] the case insensitive level name or number @see |vim.lsp.log_levels| +function lsp.set_log_level(level) + if type(level) == 'string' or type(level) == 'number' then + log.set_level(level) + else + error(string.format("Invalid log level: %q", level)) + end +end + +-- Return the path of the logfile used by the LSP client. +function lsp.get_log_path() + return log.get_filename() +end + +return lsp +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/builtin_callbacks.lua b/runtime/lua/vim/lsp/builtin_callbacks.lua new file mode 100644 index 0000000000..cc739ce3ad --- /dev/null +++ b/runtime/lua/vim/lsp/builtin_callbacks.lua @@ -0,0 +1,296 @@ +--- Implements the following default callbacks: +-- +-- vim.api.nvim_buf_set_lines(0, 0, 0, false, vim.tbl_keys(vim.lsp.builtin_callbacks)) +-- + +-- textDocument/completion +-- textDocument/declaration +-- textDocument/definition +-- textDocument/hover +-- textDocument/implementation +-- textDocument/publishDiagnostics +-- textDocument/rename +-- textDocument/signatureHelp +-- textDocument/typeDefinition +-- TODO codeLens/resolve +-- TODO completionItem/resolve +-- TODO documentLink/resolve +-- TODO textDocument/codeAction +-- TODO textDocument/codeLens +-- TODO textDocument/documentHighlight +-- TODO textDocument/documentLink +-- TODO textDocument/documentSymbol +-- TODO textDocument/formatting +-- TODO textDocument/onTypeFormatting +-- TODO textDocument/rangeFormatting +-- TODO textDocument/references +-- window/logMessage +-- window/showMessage + +local log = require 'vim.lsp.log' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' +local api = vim.api + +local function split_lines(value) + return vim.split(value, '\n', true) +end + +local builtin_callbacks = {} + +-- textDocument/completion +-- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion +builtin_callbacks['textDocument/completion'] = function(_, _, result) + if not result or vim.tbl_isempty(result) then + return + end + local pos = api.nvim_win_get_cursor(0) + local row, col = pos[1], pos[2] + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) + + local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) + local match_result = vim.fn.matchstrpos(line_to_cursor, '\\k\\+$') + local match_start, match_finish = match_result[2], match_result[3] + + vim.fn.complete(col + 1 - (match_finish - match_start), matches) +end + +-- textDocument/rename +builtin_callbacks['textDocument/rename'] = function(_, _, result) + if not result then return end + util.workspace_apply_workspace_edit(result) +end + +local function uri_to_bufnr(uri) + return vim.fn.bufadd((vim.uri_to_fname(uri))) +end + +builtin_callbacks['textDocument/publishDiagnostics'] = function(_, _, result) + if not result then return end + local uri = result.uri + local bufnr = uri_to_bufnr(uri) + if not bufnr then + api.nvim_err_writeln(string.format("LSP.publishDiagnostics: Couldn't find buffer for %s", uri)) + return + end + util.buf_clear_diagnostics(bufnr) + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + util.buf_diagnostics_underline(bufnr, result.diagnostics) + util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + -- util.buf_loclist(bufnr, result.diagnostics) +end + +-- textDocument/hover +-- https://microsoft.github.io/language-server-protocol/specification#textDocument_hover +-- @params MarkedString | MarkedString[] | MarkupContent +builtin_callbacks['textDocument/hover'] = function(_, _, result) + if result == nil or vim.tbl_isempty(result) then + return + end + + if result.contents ~= nil then + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + if vim.tbl_isempty(markdown_lines) then + markdown_lines = { 'No information available' } + end + util.open_floating_preview(markdown_lines, 'markdown') + end +end + +builtin_callbacks['textDocument/peekDefinition'] = function(_, _, result) + if result == nil or vim.tbl_isempty(result) then return end + -- TODO(ashkan) what to do with multiple locations? + result = result[1] + local bufnr = uri_to_bufnr(result.uri) + assert(bufnr) + local start = result.range.start + local finish = result.range["end"] + util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) + util.open_floating_preview({"*Peek:*", string.rep(" ", finish.character - start.character + 1) }, 'markdown', { offset_y = -(finish.line - start.line) }) +end + +--- Convert SignatureHelp response to preview contents. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +local function signature_help_to_preview_contents(input) + if not input.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = input.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #input.signatures then + active_signature = 0 + end + local signature = input.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, split_lines(signature.label)) + if signature.documentation then + util.convert_input_to_markdown_lines(signature.documentation, contents) + end + if input.parameters then + local active_parameter = input.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #input.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + util.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + +-- textDocument/signatureHelp +-- https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp +builtin_callbacks['textDocument/signatureHelp'] = function(_, _, result) + if result == nil or vim.tbl_isempty(result) then + return + end + + -- TODO show empty popup when signatures is empty? + if #result.signatures > 0 then + local markdown_lines = signature_help_to_preview_contents(result) + if vim.tbl_isempty(markdown_lines) then + markdown_lines = { 'No signature available' } + end + util.open_floating_preview(markdown_lines, 'markdown') + end +end + +local function update_tagstack() + local bufnr = api.nvim_get_current_buf() + local line = vim.fn.line('.') + local col = vim.fn.col('.') + local tagname = vim.fn.expand('') + local item = { bufnr = bufnr, from = { bufnr, line, col, 0 }, tagname = tagname } + local winid = vim.fn.win_getid() + local tagstack = vim.fn.gettagstack(winid) + + local action + + if tagstack.length == tagstack.curidx then + action = 'r' + tagstack.items[tagstack.curidx] = item + elseif tagstack.length > tagstack.curidx then + action = 'r' + if tagstack.curidx > 1 then + tagstack.items = table.insert(tagstack.items[tagstack.curidx - 1], item) + else + tagstack.items = { item } + end + else + action = 'a' + tagstack.items = { item } + end + + tagstack.curidx = tagstack.curidx + 1 + vim.fn.settagstack(winid, tagstack, action) +end + +local function handle_location(result) + -- We can sometimes get a list of locations, so set the first value as the + -- only value we want to handle + -- TODO(ashkan) was this correct^? We could use location lists. + if result[1] ~= nil then + result = result[1] + end + if result.uri == nil then + api.nvim_err_writeln('[LSP] Could not find a valid location') + return + end + local result_file = vim.uri_to_fname(result.uri) + local bufnr = vim.fn.bufadd(result_file) + update_tagstack() + api.nvim_set_current_buf(bufnr) + local start = result.range.start + api.nvim_win_set_cursor(0, {start.line + 1, start.character}) +end + +local function location_callback(_, method, result) + if result == nil or vim.tbl_isempty(result) then + local _ = log.info() and log.info(method, 'No location found') + return nil + end + handle_location(result) + return true +end + +local location_callbacks = { + -- https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration + 'textDocument/declaration'; + -- https://microsoft.github.io/language-server-protocol/specification#textDocument_definition + 'textDocument/definition'; + -- https://microsoft.github.io/language-server-protocol/specification#textDocument_implementation + 'textDocument/implementation'; + -- https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition + 'textDocument/typeDefinition'; +} + +for _, location_method in ipairs(location_callbacks) do + builtin_callbacks[location_method] = location_callback +end + +local function log_message(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + api.nvim_err_writeln(string.format("LSP[%s] client has shut down after sending the message", client_name)) + end + if message_type == protocol.MessageType.Error then + -- Might want to not use err_writeln, + -- but displaying a message with red highlights or something + api.nvim_err_writeln(string.format("LSP[%s] %s", client_name, message)) + else + local message_type_name = protocol.MessageType[message_type] + api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) + end + return result +end + +builtin_callbacks['window/showMessage'] = log_message +builtin_callbacks['window/logMessage'] = log_message + +-- Add boilerplate error validation and logging for all of these. +for k, fn in pairs(builtin_callbacks) do + builtin_callbacks[k] = function(err, method, params, client_id) + local _ = log.debug() and log.debug('builtin_callback', method, { params = params, client_id = client_id, err = err }) + if err then + error(tostring(err)) + end + return fn(err, method, params, client_id) + end +end + +return builtin_callbacks +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua new file mode 100644 index 0000000000..974eaae38c --- /dev/null +++ b/runtime/lua/vim/lsp/log.lua @@ -0,0 +1,95 @@ +-- Logger for language client plugin. + +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 +log.levels = { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARN = 3; + ERROR = 4; + -- FATAL = 4; +} + +-- Default log level is warn. +local current_log_level = log.levels.WARN +local log_date_format = "%FT%H:%M:%SZ%z" + +do + local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" + local function path_join(...) + return table.concat(vim.tbl_flatten{...}, path_sep) + end + local logfilename = path_join(vim.fn.stdpath('data'), 'vim-lsp.log') + + --- Return the log filename. + function log.get_filename() + return logfilename + end + + vim.fn.mkdir(vim.fn.stdpath('data'), "p") + local logfile = assert(io.open(logfilename, "a+")) + for level, levelnr in pairs(log.levels) do + -- Also export the log level on the root object. + log[level] = levelnr + -- Set the lowercase name as the main use function. + -- If called without arguments, it will check whether the log level is + -- greater than or equal to this one. When called with arguments, it will + -- log at that level (if applicable, it is checked either way). + -- + -- Recommended usage: + -- ``` + -- local _ = log.warn() and log.warn("123") + -- ``` + -- + -- This way you can avoid string allocations if the log level isn't high enough. + log[level:lower()] = function(...) + local argc = select("#", ...) + if levelnr < current_log_level then return false end + if argc == 0 then return true end + local info = debug.getinfo(2, "Sl") + local fileinfo = string.format("%s:%s", info.short_src, info.currentline) + local parts = { table.concat({"[", level, "]", os.date(log_date_format), "]", fileinfo, "]"}, " ") } + for i = 1, argc do + local arg = select(i, ...) + if arg == nil then + table.insert(parts, "nil") + else + table.insert(parts, vim.inspect(arg, {newline=''})) + end + end + logfile:write(table.concat(parts, '\t'), "\n") + logfile:flush() + end + end + -- Add some space to make it easier to distinguish different neovim runs. + logfile:write("\n") +end + +-- This is put here on purpose after the loop above so that it doesn't +-- interfere with iterating the levels +vim.tbl_add_reverse_lookup(log.levels) + +function log.set_level(level) + if type(level) == 'string' then + current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) + else + assert(type(level) == 'number', "level must be a number or string") + assert(log.levels[level], string.format("Invalid log level: %d", level)) + current_log_level = level + end +end + +-- Return whether the level is sufficient for logging. +-- @param level number log level +function log.should_log(level) + return level >= current_log_level +end + +return log +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua new file mode 100644 index 0000000000..1413a88ce2 --- /dev/null +++ b/runtime/lua/vim/lsp/protocol.lua @@ -0,0 +1,936 @@ +-- Protocol for the Microsoft Language Server Protocol (mslsp) + +local protocol = {} + +local function ifnil(a, b) + if a == nil then return b end + return a +end + + +--[=[ +-- Useful for interfacing with: +-- https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md +-- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md +function transform_schema_comments() + nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] + nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]] +end +function transform_schema_to_table() + transform_schema_comments() + nvim.command [[silent! '<,'>s/: \S\+//]] + nvim.command [[silent! '<,'>s/export const //]] + nvim.command [[silent! '<,'>s/export namespace \(\S*\)\s*{/protocol.\1 = {/]] + nvim.command [[silent! '<,'>s/namespace \(\S*\)\s*{/protocol.\1 = {/]] +end +--]=] + +local constants = { + DiagnosticSeverity = { + -- Reports an error. + Error = 1; + -- Reports a warning. + Warning = 2; + -- Reports an information. + Information = 3; + -- Reports a hint. + Hint = 4; + }; + + MessageType = { + -- An error message. + Error = 1; + -- A warning message. + Warning = 2; + -- An information message. + Info = 3; + -- A log message. + Log = 4; + }; + + -- The file event type. + FileChangeType = { + -- The file got created. + Created = 1; + -- The file got changed. + Changed = 2; + -- The file got deleted. + Deleted = 3; + }; + + -- The kind of a completion entry. + CompletionItemKind = { + Text = 1; + Method = 2; + Function = 3; + Constructor = 4; + Field = 5; + Variable = 6; + Class = 7; + Interface = 8; + Module = 9; + Property = 10; + Unit = 11; + Value = 12; + Enum = 13; + Keyword = 14; + Snippet = 15; + Color = 16; + File = 17; + Reference = 18; + Folder = 19; + EnumMember = 20; + Constant = 21; + Struct = 22; + Event = 23; + Operator = 24; + TypeParameter = 25; + }; + + -- How a completion was triggered + CompletionTriggerKind = { + -- Completion was triggered by typing an identifier (24x7 code + -- complete), manual invocation (e.g Ctrl+Space) or via API. + Invoked = 1; + -- Completion was triggered by a trigger character specified by + -- the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + TriggerCharacter = 2; + -- Completion was re-triggered as the current completion list is incomplete. + TriggerForIncompleteCompletions = 3; + }; + + -- A document highlight kind. + DocumentHighlightKind = { + -- A textual occurrence. + Text = 1; + -- Read-access of a symbol, like reading a variable. + Read = 2; + -- Write-access of a symbol, like writing to a variable. + Write = 3; + }; + + -- A symbol kind. + SymbolKind = { + File = 1; + Module = 2; + Namespace = 3; + Package = 4; + Class = 5; + Method = 6; + Property = 7; + Field = 8; + Constructor = 9; + Enum = 10; + Interface = 11; + Function = 12; + Variable = 13; + Constant = 14; + String = 15; + Number = 16; + Boolean = 17; + Array = 18; + Object = 19; + Key = 20; + Null = 21; + EnumMember = 22; + Struct = 23; + Event = 24; + Operator = 25; + TypeParameter = 26; + }; + + -- Represents reasons why a text document is saved. + TextDocumentSaveReason = { + -- Manually triggered, e.g. by the user pressing save, by starting debugging, + -- or by an API call. + Manual = 1; + -- Automatic after a delay. + AfterDelay = 2; + -- When the editor lost focus. + FocusOut = 3; + }; + + ErrorCodes = { + -- Defined by JSON RPC + ParseError = -32700; + InvalidRequest = -32600; + MethodNotFound = -32601; + InvalidParams = -32602; + InternalError = -32603; + serverErrorStart = -32099; + serverErrorEnd = -32000; + ServerNotInitialized = -32002; + UnknownErrorCode = -32001; + -- Defined by the protocol. + RequestCancelled = -32800; + ContentModified = -32801; + }; + + -- Describes the content type that a client supports in various + -- result literals like `Hover`, `ParameterInfo` or `CompletionItem`. + -- + -- Please note that `MarkupKinds` must not start with a `$`. This kinds + -- are reserved for internal usage. + MarkupKind = { + -- Plain text is supported as a content format + PlainText = 'plaintext'; + -- Markdown is supported as a content format + Markdown = 'markdown'; + }; + + ResourceOperationKind = { + -- Supports creating new files and folders. + Create = 'create'; + -- Supports renaming existing files and folders. + Rename = 'rename'; + -- Supports deleting existing files and folders. + Delete = 'delete'; + }; + + FailureHandlingKind = { + -- Applying the workspace change is simply aborted if one of the changes provided + -- fails. All operations executed before the failing operation stay executed. + Abort = 'abort'; + -- All operations are executed transactionally. That means they either all + -- succeed or no changes at all are applied to the workspace. + Transactional = 'transactional'; + -- If the workspace edit contains only textual file changes they are executed transactionally. + -- If resource changes (create, rename or delete file) are part of the change the failure + -- handling strategy is abort. + TextOnlyTransactional = 'textOnlyTransactional'; + -- The client tries to undo the operations already executed. But there is no + -- guarantee that this succeeds. + Undo = 'undo'; + }; + + -- Known error codes for an `InitializeError`; + InitializeError = { + -- If the protocol version provided by the client can't be handled by the server. + -- @deprecated This initialize error got replaced by client capabilities. There is + -- no version handshake in version 3.0x + unknownProtocolVersion = 1; + }; + + -- Defines how the host (editor) should sync document changes to the language server. + TextDocumentSyncKind = { + -- Documents should not be synced at all. + None = 0; + -- Documents are synced by always sending the full content + -- of the document. + Full = 1; + -- Documents are synced by sending the full content on open. + -- After that only incremental updates to the document are + -- send. + Incremental = 2; + }; + + WatchKind = { + -- Interested in create events. + Create = 1; + -- Interested in change events + Change = 2; + -- Interested in delete events + Delete = 4; + }; + + -- Defines whether the insert text in a completion item should be interpreted as + -- plain text or a snippet. + InsertTextFormat = { + -- The primary text to be inserted is treated as a plain string. + PlainText = 1; + -- The primary text to be inserted is treated as a snippet. + -- + -- A snippet can define tab stops and placeholders with `$1`, `$2` + -- and `${3:foo};`. `$0` defines the final tab stop, it defaults to + -- the end of the snippet. Placeholders with equal identifiers are linked, + -- that is typing in one will update others too. + Snippet = 2; + }; + + -- A set of predefined code action kinds + CodeActionKind = { + -- Empty kind. + Empty = ''; + -- Base kind for quickfix actions + QuickFix = 'quickfix'; + -- Base kind for refactoring actions + Refactor = 'refactor'; + -- Base kind for refactoring extraction actions + -- + -- Example extract actions: + -- + -- - Extract method + -- - Extract function + -- - Extract variable + -- - Extract interface from class + -- - ... + RefactorExtract = 'refactor.extract'; + -- Base kind for refactoring inline actions + -- + -- Example inline actions: + -- + -- - Inline function + -- - Inline variable + -- - Inline constant + -- - ... + RefactorInline = 'refactor.inline'; + -- Base kind for refactoring rewrite actions + -- + -- Example rewrite actions: + -- + -- - Convert JavaScript function to class + -- - Add or remove parameter + -- - Encapsulate field + -- - Make method static + -- - Move method to base class + -- - ... + RefactorRewrite = 'refactor.rewrite'; + -- Base kind for source actions + -- + -- Source code actions apply to the entire file. + Source = 'source'; + -- Base kind for an organize imports source action + SourceOrganizeImports = 'source.organizeImports'; + }; +} + +for k, v in pairs(constants) do + vim.tbl_add_reverse_lookup(v) + protocol[k] = v +end + +--[=[ +--Text document specific client capabilities. +export interface TextDocumentClientCapabilities { + synchronization?: { + --Whether text document synchronization supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports sending will save notifications. + willSave?: boolean; + --The client supports sending a will save request and + --waits for a response providing text edits which will + --be applied to the document before it is saved. + willSaveWaitUntil?: boolean; + --The client supports did save notifications. + didSave?: boolean; + } + --Capabilities specific to the `textDocument/completion` + completion?: { + --Whether completion supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports the following `CompletionItem` specific + --capabilities. + completionItem?: { + --The client supports snippets as insert text. + -- + --A snippet can define tab stops and placeholders with `$1`, `$2` + --and `${3:foo}`. `$0` defines the final tab stop, it defaults to + --the end of the snippet. Placeholders with equal identifiers are linked, + --that is typing in one will update others too. + snippetSupport?: boolean; + --The client supports commit characters on a completion item. + commitCharactersSupport?: boolean + --The client supports the following content formats for the documentation + --property. The order describes the preferred format of the client. + documentationFormat?: MarkupKind[]; + --The client supports the deprecated property on a completion item. + deprecatedSupport?: boolean; + --The client supports the preselect property on a completion item. + preselectSupport?: boolean; + } + completionItemKind?: { + --The completion item kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + -- + --If this property is not present the client only supports + --the completion items kinds from `Text` to `Reference` as defined in + --the initial version of the protocol. + valueSet?: CompletionItemKind[]; + }, + --The client supports to send additional context information for a + --`textDocument/completion` request. + contextSupport?: boolean; + }; + --Capabilities specific to the `textDocument/hover` + hover?: { + --Whether hover supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports the follow content formats for the content + --property. The order describes the preferred format of the client. + contentFormat?: MarkupKind[]; + }; + --Capabilities specific to the `textDocument/signatureHelp` + signatureHelp?: { + --Whether signature help supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports the following `SignatureInformation` + --specific properties. + signatureInformation?: { + --The client supports the follow content formats for the documentation + --property. The order describes the preferred format of the client. + documentationFormat?: MarkupKind[]; + --Client capabilities specific to parameter information. + parameterInformation?: { + --The client supports processing label offsets instead of a + --simple label string. + -- + --Since 3.14.0 + labelOffsetSupport?: boolean; + } + }; + }; + --Capabilities specific to the `textDocument/references` + references?: { + --Whether references supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentHighlight` + documentHighlight?: { + --Whether document highlight supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentSymbol` + documentSymbol?: { + --Whether document symbol supports dynamic registration. + dynamicRegistration?: boolean; + --Specific capabilities for the `SymbolKind`. + symbolKind?: { + --The symbol kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + -- + --If this property is not present the client only supports + --the symbol kinds from `File` to `Array` as defined in + --the initial version of the protocol. + valueSet?: SymbolKind[]; + } + --The client supports hierarchical document symbols. + hierarchicalDocumentSymbolSupport?: boolean; + }; + --Capabilities specific to the `textDocument/formatting` + formatting?: { + --Whether formatting supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/rangeFormatting` + rangeFormatting?: { + --Whether range formatting supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/onTypeFormatting` + onTypeFormatting?: { + --Whether on type formatting supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/declaration` + declaration?: { + --Whether declaration supports dynamic registration. If this is set to `true` + --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of declaration links. + -- + --Since 3.14.0 + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/definition`. + -- + --Since 3.14.0 + definition?: { + --Whether definition supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of definition links. + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/typeDefinition` + -- + --Since 3.6.0 + typeDefinition?: { + --Whether typeDefinition supports dynamic registration. If this is set to `true` + --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of definition links. + -- + --Since 3.14.0 + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/implementation`. + -- + --Since 3.6.0 + implementation?: { + --Whether implementation supports dynamic registration. If this is set to `true` + --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The client supports additional metadata in the form of definition links. + -- + --Since 3.14.0 + linkSupport?: boolean; + }; + --Capabilities specific to the `textDocument/codeAction` + codeAction?: { + --Whether code action supports dynamic registration. + dynamicRegistration?: boolean; + --The client support code action literals as a valid + --response of the `textDocument/codeAction` request. + -- + --Since 3.8.0 + codeActionLiteralSupport?: { + --The code action kind is support with the following value + --set. + codeActionKind: { + --The code action kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + valueSet: CodeActionKind[]; + }; + }; + }; + --Capabilities specific to the `textDocument/codeLens` + codeLens?: { + --Whether code lens supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentLink` + documentLink?: { + --Whether document link supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `textDocument/documentColor` and the + --`textDocument/colorPresentation` request. + -- + --Since 3.6.0 + colorProvider?: { + --Whether colorProvider supports dynamic registration. If this is set to `true` + --the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + } + --Capabilities specific to the `textDocument/rename` + rename?: { + --Whether rename supports dynamic registration. + dynamicRegistration?: boolean; + --The client supports testing for validity of rename operations + --before execution. + prepareSupport?: boolean; + }; + --Capabilities specific to `textDocument/publishDiagnostics`. + publishDiagnostics?: { + --Whether the clients accepts diagnostics with related information. + relatedInformation?: boolean; + }; + --Capabilities specific to `textDocument/foldingRange` requests. + -- + --Since 3.10.0 + foldingRange?: { + --Whether implementation supports dynamic registration for folding range providers. If this is set to `true` + --the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` + --return value for the corresponding server capability as well. + dynamicRegistration?: boolean; + --The maximum number of folding ranges that the client prefers to receive per document. The value serves as a + --hint, servers are free to follow the limit. + rangeLimit?: number; + --If set, the client signals that it only supports folding complete lines. If set, client will + --ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. + lineFoldingOnly?: boolean; + }; +} +--]=] + +--[=[ +--Workspace specific client capabilities. +export interface WorkspaceClientCapabilities { + --The client supports applying batch edits to the workspace by supporting + --the request 'workspace/applyEdit' + applyEdit?: boolean; + --Capabilities specific to `WorkspaceEdit`s + workspaceEdit?: { + --The client supports versioned document changes in `WorkspaceEdit`s + documentChanges?: boolean; + --The resource operations the client supports. Clients should at least + --support 'create', 'rename' and 'delete' files and folders. + resourceOperations?: ResourceOperationKind[]; + --The failure handling strategy of a client if applying the workspace edit + --fails. + failureHandling?: FailureHandlingKind; + }; + --Capabilities specific to the `workspace/didChangeConfiguration` notification. + didChangeConfiguration?: { + --Did change configuration notification supports dynamic registration. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + didChangeWatchedFiles?: { + --Did change watched files notification supports dynamic registration. Please note + --that the current protocol doesn't support static configuration for file changes + --from the server side. + dynamicRegistration?: boolean; + }; + --Capabilities specific to the `workspace/symbol` request. + symbol?: { + --Symbol request supports dynamic registration. + dynamicRegistration?: boolean; + --Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. + symbolKind?: { + --The symbol kind values the client supports. When this + --property exists the client also guarantees that it will + --handle values outside its set gracefully and falls back + --to a default value when unknown. + -- + --If this property is not present the client only supports + --the symbol kinds from `File` to `Array` as defined in + --the initial version of the protocol. + valueSet?: SymbolKind[]; + } + }; + --Capabilities specific to the `workspace/executeCommand` request. + executeCommand?: { + --Execute command supports dynamic registration. + dynamicRegistration?: boolean; + }; + --The client has support for workspace folders. + -- + --Since 3.6.0 + workspaceFolders?: boolean; + --The client supports `workspace/configuration` requests. + -- + --Since 3.6.0 + configuration?: boolean; +} +--]=] + +function protocol.make_client_capabilities() + return { + textDocument = { + synchronization = { + dynamicRegistration = false; + + -- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre) + willSave = false; + + -- TODO(ashkan) Implement textDocument/willSaveWaitUntil + willSaveWaitUntil = false; + + -- Send textDocument/didSave after saving (BufWritePost) + didSave = true; + }; + completion = { + dynamicRegistration = false; + completionItem = { + + -- TODO(tjdevries): Is it possible to implement this in plain lua? + snippetSupport = false; + commitCharactersSupport = false; + preselectSupport = false; + deprecatedSupport = false; + documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + }; + completionItemKind = { + valueSet = (function() + local res = {} + for k in pairs(protocol.CompletionItemKind) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + + -- TODO(tjdevries): Implement this + contextSupport = false; + }; + hover = { + dynamicRegistration = false; + contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + }; + signatureHelp = { + dynamicRegistration = false; + signatureInformation = { + documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; + -- parameterInformation = { + -- labelOffsetSupport = false; + -- }; + }; + }; + references = { + dynamicRegistration = false; + }; + documentHighlight = { + dynamicRegistration = false + }; + -- documentSymbol = { + -- dynamicRegistration = false; + -- symbolKind = { + -- valueSet = (function() + -- local res = {} + -- for k in pairs(protocol.SymbolKind) do + -- if type(k) == 'string' then table.insert(res, k) end + -- end + -- return res + -- end)(); + -- }; + -- hierarchicalDocumentSymbolSupport = false; + -- }; + }; + workspace = nil; + experimental = nil; + } +end + +function protocol.make_text_document_position_params() + local position = vim.api.nvim_win_get_cursor(0) + return { + textDocument = { + uri = vim.uri_from_bufnr() + }; + position = { + line = position[1] - 1; + character = position[2]; + } + } +end + +--[=[ +export interface DocumentFilter { + --A language id, like `typescript`. + language?: string; + --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + scheme?: string; + --A glob pattern, like `*.{ts,js}`. + -- + --Glob patterns can have the following syntax: + --- `*` to match one or more characters in a path segment + --- `?` to match on one character in a path segment + --- `**` to match any number of path segments, including none + --- `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + pattern?: string; +} +--]=] + +--[[ +--Static registration options to be returned in the initialize request. +interface StaticRegistrationOptions { + --The id used to register the request. The id can be used to deregister + --the request again. See also Registration#id. + id?: string; +} + +export interface DocumentFilter { + --A language id, like `typescript`. + language?: string; + --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. + scheme?: string; + --A glob pattern, like `*.{ts,js}`. + -- + --Glob patterns can have the following syntax: + --- `*` to match one or more characters in a path segment + --- `?` to match on one character in a path segment + --- `**` to match any number of path segments, including none + --- `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + pattern?: string; +} +export type DocumentSelector = DocumentFilter[]; +export interface TextDocumentRegistrationOptions { + --A document selector to identify the scope of the registration. If set to null + --the document selector provided on the client side will be used. + documentSelector: DocumentSelector | null; +} + +--Code Action options. +export interface CodeActionOptions { + --CodeActionKinds that this server may return. + -- + --The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server + --may list out every specific kind they provide. + codeActionKinds?: CodeActionKind[]; +} + +interface ServerCapabilities { + --Defines how text documents are synced. Is either a detailed structure defining each notification or + --for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`. + textDocumentSync?: TextDocumentSyncOptions | number; + --The server provides hover support. + hoverProvider?: boolean; + --The server provides completion support. + completionProvider?: CompletionOptions; + --The server provides signature help support. + signatureHelpProvider?: SignatureHelpOptions; + --The server provides goto definition support. + definitionProvider?: boolean; + --The server provides Goto Type Definition support. + -- + --Since 3.6.0 + typeDefinitionProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides Goto Implementation support. + -- + --Since 3.6.0 + implementationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides find references support. + referencesProvider?: boolean; + --The server provides document highlight support. + documentHighlightProvider?: boolean; + --The server provides document symbol support. + documentSymbolProvider?: boolean; + --The server provides workspace symbol support. + workspaceSymbolProvider?: boolean; + --The server provides code actions. The `CodeActionOptions` return type is only + --valid if the client signals code action literal support via the property + --`textDocument.codeAction.codeActionLiteralSupport`. + codeActionProvider?: boolean | CodeActionOptions; + --The server provides code lens. + codeLensProvider?: CodeLensOptions; + --The server provides document formatting. + documentFormattingProvider?: boolean; + --The server provides document range formatting. + documentRangeFormattingProvider?: boolean; + --The server provides document formatting on typing. + documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions; + --The server provides rename support. RenameOptions may only be + --specified if the client states that it supports + --`prepareSupport` in its initial `initialize` request. + renameProvider?: boolean | RenameOptions; + --The server provides document link support. + documentLinkProvider?: DocumentLinkOptions; + --The server provides color provider support. + -- + --Since 3.6.0 + colorProvider?: boolean | ColorProviderOptions | (ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides folding provider support. + -- + --Since 3.10.0 + foldingRangeProvider?: boolean | FoldingRangeProviderOptions | (FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides go to declaration support. + -- + --Since 3.14.0 + declarationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + --The server provides execute command support. + executeCommandProvider?: ExecuteCommandOptions; + --Workspace specific server capabilities + workspace?: { + --The server supports workspace folder. + -- + --Since 3.6.0 + workspaceFolders?: { + * The server has support for workspace folders + supported?: boolean; + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a strings is provided the string is treated as a ID + * under which the notification is registered on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + changeNotifications?: string | boolean; + } + } + --Experimental server capabilities. + experimental?: any; +} +--]] +function protocol.resolve_capabilities(server_capabilities) + local general_properties = {} + local text_document_sync_properties + do + local TextDocumentSyncKind = protocol.TextDocumentSyncKind + local textDocumentSync = server_capabilities.textDocumentSync + if textDocumentSync == nil then + -- Defaults if omitted. + text_document_sync_properties = { + text_document_open_close = false; + text_document_did_change = TextDocumentSyncKind.None; +-- text_document_did_change = false; + text_document_will_save = false; + text_document_will_save_wait_until = false; + text_document_save = false; + text_document_save_include_text = false; + } + elseif type(textDocumentSync) == 'number' then + -- Backwards compatibility + if not TextDocumentSyncKind[textDocumentSync] then + return nil, "Invalid server TextDocumentSyncKind for textDocumentSync" + end + text_document_sync_properties = { + text_document_open_close = true; + text_document_did_change = textDocumentSync; + text_document_will_save = false; + text_document_will_save_wait_until = false; + text_document_save = false; + text_document_save_include_text = false; + } + elseif type(textDocumentSync) == 'table' then + text_document_sync_properties = { + text_document_open_close = ifnil(textDocumentSync.openClose, false); + text_document_did_change = ifnil(textDocumentSync.change, TextDocumentSyncKind.None); + text_document_will_save = ifnil(textDocumentSync.willSave, false); + text_document_will_save_wait_until = ifnil(textDocumentSync.willSaveWaitUntil, false); + text_document_save = ifnil(textDocumentSync.save, false); + text_document_save_include_text = ifnil(textDocumentSync.save and textDocumentSync.save.includeText, false); + } + else + return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync)) + end + end + general_properties.hover = server_capabilities.hoverProvider or false + general_properties.goto_definition = server_capabilities.definitionProvider or false + general_properties.find_references = server_capabilities.referencesProvider or false + general_properties.document_highlight = server_capabilities.documentHighlightProvider or false + general_properties.document_symbol = server_capabilities.documentSymbolProvider or false + general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false + general_properties.document_formatting = server_capabilities.documentFormattingProvider or false + general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false + + if server_capabilities.codeActionProvider == nil then + general_properties.code_action = false + elseif type(server_capabilities.codeActionProvider) == 'boolean' then + general_properties.code_action = server_capabilities.codeActionProvider + elseif type(server_capabilities.codeActionProvider) == 'table' then + -- TODO(ashkan) support CodeActionKind + general_properties.code_action = false + else + error("The server sent invalid codeActionProvider") + end + + if server_capabilities.implementationProvider == nil then + general_properties.implementation = false + elseif type(server_capabilities.implementationProvider) == 'boolean' then + general_properties.implementation = server_capabilities.implementationProvider + elseif type(server_capabilities.implementationProvider) == 'table' then + -- TODO(ashkan) support more detailed implementation options. + general_properties.implementation = false + else + error("The server sent invalid implementationProvider") + end + + local signature_help_properties + if server_capabilities.signatureHelpProvider == nil then + signature_help_properties = { + signature_help = false; + signature_help_trigger_characters = {}; + } + elseif type(server_capabilities.signatureHelpProvider) == 'table' then + signature_help_properties = { + signature_help = true; + -- The characters that trigger signature help automatically. + signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {}; + } + else + error("The server sent invalid signatureHelpProvider") + end + + return vim.tbl_extend("error" + , text_document_sync_properties + , signature_help_properties + , general_properties + ) +end + +return protocol +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua new file mode 100644 index 0000000000..e0ec8863d6 --- /dev/null +++ b/runtime/lua/vim/lsp/rpc.lua @@ -0,0 +1,451 @@ +local uv = vim.loop +local log = require('vim.lsp.log') +local protocol = require('vim.lsp.protocol') +local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap + +-- TODO replace with a better implementation. +local function json_encode(data) + local status, result = pcall(vim.fn.json_encode, data) + if status then + return result + else + return nil, result + end +end +local function json_decode(data) + local status, result = pcall(vim.fn.json_decode, data) + if status then + return result + else + return nil, result + end +end + +local function is_dir(filename) + local stat = vim.loop.fs_stat(filename) + return stat and stat.type == 'directory' or false +end + +local NIL = vim.NIL +local function convert_NIL(v) + if v == NIL then return nil end + return v +end + +-- If a dictionary is passed in, turn it into a list of string of "k=v" +-- Accepts a table which can be composed of k=v strings or map-like +-- specification, such as: +-- +-- ``` +-- { +-- "PRODUCTION=false"; +-- "PATH=/usr/bin/"; +-- PORT = 123; +-- HOST = "0.0.0.0"; +-- } +-- ``` +-- +-- Non-string values will be cast with `tostring` +local function force_env_list(final_env) + if final_env then + local env = final_env + final_env = {} + for k,v in pairs(env) do + -- If it's passed in as a dict, then convert to list of "k=v" + if type(k) == "string" then + table.insert(final_env, k..'='..tostring(v)) + elseif type(v) == 'string' then + table.insert(final_env, v) + else + -- TODO is this right or should I exception here? + -- Try to coerce other values to string. + table.insert(final_env, tostring(v)) + end + end + return final_env + end +end + +local function format_message_with_content_length(encoded_message) + return table.concat { + 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; + encoded_message; + } +end + +--- Parse an LSP Message's header +-- @param header: The header to parse. +local function parse_headers(header) + if type(header) ~= 'string' then + return nil + end + local headers = {} + for line in vim.gsplit(header, '\r\n', true) do + if line == '' then + break + end + local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$") + if key then + key = key:lower():gsub('%-', '_') + headers[key] = value + else + local _ = log.error() and log.error("invalid header line %q", line) + error(string.format("invalid header line %q", line)) + end + end + headers.content_length = tonumber(headers.content_length) + or error(string.format("Content-Length not found in headers. %q", header)) + return headers +end + +-- This is the start of any possible header patterns. The gsub converts it to a +-- case insensitive pattern. +local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) + +local function request_parser_loop() + local buffer = '' + while true do + -- A message can only be complete if it has a double CRLF and also the full + -- payload, so first let's check for the CRLFs + local start, finish = buffer:find('\r\n\r\n', 1, true) + -- Start parsing the headers + if start then + -- This is a workaround for servers sending initial garbage before + -- sending headers, such as if a bash script sends stdout. It assumes + -- that we know all of the headers ahead of time. At this moment, the + -- only valid headers start with "Content-*", so that's the thing we will + -- be searching for. + -- TODO(ashkan) I'd like to remove this, but it seems permanent :( + local buffer_start = buffer:find(header_start_pattern) + local headers = parse_headers(buffer:sub(buffer_start, start-1)) + buffer = buffer:sub(finish+1) + local content_length = headers.content_length + -- Keep waiting for data until we have enough. + while #buffer < content_length do + buffer = buffer..(coroutine.yield() + or error("Expected more data for the body. The server may have died.")) -- TODO hmm. + end + local body = buffer:sub(1, content_length) + buffer = buffer:sub(content_length + 1) + -- Yield our data. + buffer = buffer..(coroutine.yield(headers, body) + or error("Expected more data for the body. The server may have died.")) -- TODO hmm. + else + -- Get more data since we don't have enough. + buffer = buffer..(coroutine.yield() + or error("Expected more data for the header. The server may have died.")) -- TODO hmm. + end + end +end + +local client_errors = vim.tbl_add_reverse_lookup { + INVALID_SERVER_MESSAGE = 1; + INVALID_SERVER_JSON = 2; + NO_RESULT_CALLBACK_FOUND = 3; + READ_ERROR = 4; + NOTIFICATION_HANDLER_ERROR = 5; + SERVER_REQUEST_HANDLER_ERROR = 6; + SERVER_RESULT_CALLBACK_ERROR = 7; +} + +local function format_rpc_error(err) + validate { + err = { err, 't' }; + } + local code_name = assert(protocol.ErrorCodes[err.code], "err.code is invalid") + local message_parts = {"RPC", code_name} + if err.message then + table.insert(message_parts, "message = ") + table.insert(message_parts, string.format("%q", err.message)) + end + if err.data then + table.insert(message_parts, "data = ") + table.insert(message_parts, vim.inspect(err.data)) + end + return table.concat(message_parts, ' ') +end + +local function rpc_response_error(code, message, data) + -- TODO should this error or just pick a sane error (like InternalError)? + local code_name = assert(protocol.ErrorCodes[code], 'Invalid rpc error code') + return setmetatable({ + code = code; + message = message or code_name; + data = data; + }, { + __tostring = format_rpc_error; + }) +end + +local default_handlers = {} +function default_handlers.notification(method, params) + local _ = log.debug() and log.debug('notification', method, params) +end +function default_handlers.server_request(method, params) + local _ = log.debug() and log.debug('server_request', method, params) + return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound) +end +function default_handlers.on_exit(code, signal) + local _ = log.info() and log.info("client exit", { code = code, signal = signal }) +end +function default_handlers.on_error(code, err) + local _ = log.error() and log.error('client_error:', client_errors[code], err) +end + +--- Create and start an RPC client. +-- @param cmd [ +local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_params) + local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params}) + validate { + cmd = { cmd, 's' }; + cmd_args = { cmd_args, 't' }; + handlers = { handlers, 't', true }; + } + + if not (vim.fn.executable(cmd) == 1) then + error(string.format("The given command %q is not executable.", cmd)) + end + if handlers then + local user_handlers = handlers + handlers = {} + for handle_name, default_handler in pairs(default_handlers) do + local user_handler = user_handlers[handle_name] + if user_handler then + if type(user_handler) ~= 'function' then + error(string.format("handler.%s must be a function", handle_name)) + end + -- server_request is wrapped elsewhere. + if not (handle_name == 'server_request' + or handle_name == 'on_exit') -- TODO this blocks the loop exiting for some reason. + then + user_handler = schedule_wrap(user_handler) + end + handlers[handle_name] = user_handler + else + handlers[handle_name] = default_handler + end + end + else + handlers = default_handlers + end + + local stdin = uv.new_pipe(false) + local stdout = uv.new_pipe(false) + local stderr = uv.new_pipe(false) + + local message_index = 0 + local message_callbacks = {} + + local handle, pid + do + local function onexit(code, signal) + stdin:close() + stdout:close() + stderr:close() + handle:close() + -- Make sure that message_callbacks can be gc'd. + message_callbacks = nil + handlers.on_exit(code, signal) + end + local spawn_params = { + args = cmd_args; + stdio = {stdin, stdout, stderr}; + } + if extra_spawn_params then + spawn_params.cwd = extra_spawn_params.cwd + if spawn_params.cwd then + assert(is_dir(spawn_params.cwd), "cwd must be a directory") + end + spawn_params.env = force_env_list(extra_spawn_params.env) + end + handle, pid = uv.spawn(cmd, spawn_params, onexit) + end + + local function encode_and_send(payload) + local _ = log.debug() and log.debug("rpc.send.payload", payload) + if handle:is_closing() then return false end + -- TODO(ashkan) remove this once we have a Lua json_encode + schedule(function() + local encoded = assert(json_encode(payload)) + stdin:write(format_message_with_content_length(encoded)) + end) + return true + end + + local function send_notification(method, params) + local _ = log.debug() and log.debug("rpc.notify", method, params) + return encode_and_send { + jsonrpc = "2.0"; + method = method; + params = params; + } + end + + local function send_response(request_id, err, result) + return encode_and_send { + id = request_id; + jsonrpc = "2.0"; + error = err; + result = result; + } + end + + local function send_request(method, params, callback) + validate { + callback = { callback, 'f' }; + } + message_index = message_index + 1 + local message_id = message_index + local result = encode_and_send { + id = message_id; + jsonrpc = "2.0"; + method = method; + params = params; + } + if result then + message_callbacks[message_id] = schedule_wrap(callback) + return result, message_id + else + return false + end + end + + stderr:read_start(function(_err, chunk) + if chunk then + local _ = log.error() and log.error("rpc", cmd, "stderr", chunk) + end + end) + + local function on_error(errkind, ...) + assert(client_errors[errkind]) + -- TODO what to do if this fails? + pcall(handlers.on_error, errkind, ...) + end + local function pcall_handler(errkind, status, head, ...) + if not status then + on_error(errkind, head, ...) + return status, head + end + return status, head, ... + end + local function try_call(errkind, fn, ...) + return pcall_handler(errkind, pcall(fn, ...)) + end + + -- TODO periodically check message_callbacks for old requests past a certain + -- time and log them. This would require storing the timestamp. I could call + -- them with an error then, perhaps. + + local function handle_body(body) + local decoded, err = json_decode(body) + if not decoded then + on_error(client_errors.INVALID_SERVER_JSON, err) + end + local _ = log.debug() and log.debug("decoded", decoded) + + if type(decoded.method) == 'string' and decoded.id then + -- Server Request + decoded.params = convert_NIL(decoded.params) + -- Schedule here so that the users functions don't trigger an error and + -- we can still use the result. + schedule(function() + local status, result + status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR, + handlers.server_request, decoded.method, decoded.params) + local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err }) + if status then + if not (result or err) then + -- TODO this can be a problem if `null` is sent for result. needs vim.NIL + error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method)) + end + if err then + assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.") + local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.") + err.message = err.message or code_name + end + else + -- On an exception, result will contain the error message. + err = rpc_response_error(protocol.ErrorCodes.InternalError, result) + result = nil + end + send_response(decoded.id, err, result) + end) + -- This works because we are expecting vim.NIL here + elseif decoded.id and (decoded.result or decoded.error) then + -- Server Result + decoded.error = convert_NIL(decoded.error) + decoded.result = convert_NIL(decoded.result) + + -- We sent a number, so we expect a number. + local result_id = tonumber(decoded.id) + local callback = message_callbacks[result_id] + if callback then + message_callbacks[result_id] = nil + validate { + callback = { callback, 'f' }; + } + if decoded.error then + decoded.error = setmetatable(decoded.error, { + __tostring = format_rpc_error; + }) + end + try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR, + callback, decoded.error, decoded.result) + else + on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded) + local _ = log.error() and log.error("No callback found for server response id "..result_id) + end + elseif type(decoded.method) == 'string' then + -- Notification + decoded.params = convert_NIL(decoded.params) + try_call(client_errors.NOTIFICATION_HANDLER_ERROR, + handlers.notification, decoded.method, decoded.params) + else + -- Invalid server message + on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) + end + end + -- TODO(ashkan) remove this once we have a Lua json_decode + handle_body = schedule_wrap(handle_body) + + local request_parser = coroutine.wrap(request_parser_loop) + request_parser() + stdout:read_start(function(err, chunk) + if err then + -- TODO better handling. Can these be intermittent errors? + on_error(client_errors.READ_ERROR, err) + return + end + -- This should signal that we are done reading from the client. + if not chunk then return end + -- Flush anything in the parser by looping until we don't get a result + -- anymore. + while true do + local headers, body = request_parser(chunk) + -- If we successfully parsed, then handle the response. + if headers then + handle_body(body) + -- Set chunk to empty so that we can call request_parser to get + -- anything existing in the parser to flush. + chunk = '' + else + break + end + end + end) + + return { + pid = pid; + handle = handle; + request = send_request; + notify = send_notification; + } +end + +return { + start = create_and_start_client; + rpc_response_error = rpc_response_error; + format_rpc_error = format_rpc_error; + client_errors = client_errors; +} +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua new file mode 100644 index 0000000000..f96e0f01a8 --- /dev/null +++ b/runtime/lua/vim/lsp/util.lua @@ -0,0 +1,557 @@ +local protocol = require 'vim.lsp.protocol' +local validate = vim.validate +local api = vim.api + +local M = {} + +local split = vim.split +local function split_lines(value) + return split(value, '\n', true) +end + +local list_extend = vim.list_extend + +--- Find the longest shared prefix between prefix and word. +-- e.g. remove_prefix("123tes", "testing") == "ting" +local function remove_prefix(prefix, word) + local max_prefix_length = math.min(#prefix, #word) + local prefix_length = 0 + for i = 1, max_prefix_length do + local current_line_suffix = prefix:sub(-i) + local word_prefix = word:sub(1, i) + if current_line_suffix == word_prefix then + prefix_length = i + end + end + return word:sub(prefix_length + 1) +end + +local function resolve_bufnr(bufnr) + if bufnr == nil or bufnr == 0 then + return api.nvim_get_current_buf() + end + return bufnr +end + +-- local valid_windows_path_characters = "[^<>:\"/\\|?*]" +-- local valid_unix_path_characters = "[^/]" +-- https://github.com/davidm/lua-glob-pattern +-- https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names +-- function M.glob_to_regex(glob) +-- end + +--- Apply the TextEdit response. +-- @params TextEdit [table] see https://microsoft.github.io/language-server-protocol/specification +function M.text_document_apply_text_edit(text_edit, bufnr) + bufnr = resolve_bufnr(bufnr) + local range = text_edit.range + local start = range.start + local finish = range['end'] + local new_lines = split_lines(text_edit.newText) + if start.character == 0 and finish.character == 0 then + api.nvim_buf_set_lines(bufnr, start.line, finish.line, false, new_lines) + return + end + api.nvim_err_writeln('apply_text_edit currently only supports character ranges starting at 0') + error('apply_text_edit currently only supports character ranges starting at 0') + return + -- TODO test and finish this support for character ranges. +-- local lines = api.nvim_buf_get_lines(0, start.line, finish.line + 1, false) +-- local suffix = lines[#lines]:sub(finish.character+2) +-- local prefix = lines[1]:sub(start.character+2) +-- new_lines[#new_lines] = new_lines[#new_lines]..suffix +-- new_lines[1] = prefix..new_lines[1] +-- api.nvim_buf_set_lines(0, start.line, finish.line, false, new_lines) +end + +-- textDocument/completion response returns one of CompletionItem[], CompletionList or null. +-- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion +function M.extract_completion_items(result) + if type(result) == 'table' and result.items then + return result.items + elseif result ~= nil then + return result + else + return {} + end +end + +--- Apply the TextDocumentEdit response. +-- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification +function M.text_document_apply_text_document_edit(text_document_edit, bufnr) + -- local text_document = text_document_edit.textDocument + -- TODO use text_document_version? + -- local text_document_version = text_document.version + + -- TODO technically, you could do this without doing multiple buf_get/set + -- by getting the full region (smallest line and largest line) and doing + -- the edits on the buffer, and then applying the buffer at the end. + -- I'm not sure if that's better. + for _, text_edit in ipairs(text_document_edit.edits) do + M.text_document_apply_text_edit(text_edit, bufnr) + end +end + +function M.get_current_line_to_cursor() + local pos = api.nvim_win_get_cursor(0) + local line = assert(api.nvim_buf_get_lines(0, pos[1]-1, pos[1], false)[1]) + return line:sub(pos[2]+1) +end + +--- Getting vim complete-items with incomplete flag. +-- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) +-- @return { matches = complete-items table, incomplete = boolean } +function M.text_document_completion_list_to_complete_items(result, line_prefix) + local items = M.extract_completion_items(result) + if vim.tbl_isempty(items) then + return {} + end + -- Only initialize if we have some items. + if not line_prefix then + line_prefix = M.get_current_line_to_cursor() + end + + local matches = {} + + for _, completion_item in ipairs(items) do + local info = ' ' + local documentation = completion_item.documentation + if documentation then + if type(documentation) == 'string' and documentation ~= '' then + info = documentation + elseif type(documentation) == 'table' and type(documentation.value) == 'string' then + info = documentation.value + -- else + -- TODO(ashkan) Validation handling here? + end + end + + local word = completion_item.insertText or completion_item.label + + -- Ref: `:h complete-items` + table.insert(matches, { + word = remove_prefix(line_prefix, word), + abbr = completion_item.label, + kind = protocol.CompletionItemKind[completion_item.kind] or '', + menu = completion_item.detail or '', + info = info, + icase = 1, + dup = 0, + empty = 1, + }) + end + + return matches +end + +-- @params WorkspaceEdit [table] see https://microsoft.github.io/language-server-protocol/specification +function M.workspace_apply_workspace_edit(workspace_edit) + if workspace_edit.documentChanges then + for _, change in ipairs(workspace_edit.documentChanges) do + if change.kind then + -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile + error(string.format("Unsupported change: %q", vim.inspect(change))) + else + M.text_document_apply_text_document_edit(change) + end + end + return + end + + if workspace_edit.changes == nil or #workspace_edit.changes == 0 then + return + end + + for uri, changes in pairs(workspace_edit.changes) do + local fname = vim.uri_to_fname(uri) + -- TODO improve this approach. Try to edit open buffers without switching. + -- Not sure how to handle files which aren't open. This is deprecated + -- anyway, so I guess it could be left as is. + api.nvim_command('edit '..fname) + for _, change in ipairs(changes) do + M.text_document_apply_text_edit(change) + end + end +end + +--- Convert any of MarkedString | MarkedString[] | MarkupContent into markdown text lines +-- see https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_hover +-- Useful for textDocument/hover, textDocument/signatureHelp, and potentially others. +function M.convert_input_to_markdown_lines(input, contents) + contents = contents or {} + -- MarkedString variation 1 + if type(input) == 'string' then + list_extend(contents, split_lines(input)) + else + assert(type(input) == 'table', "Expected a table for Hover.contents") + -- MarkupContent + if input.kind then + -- The kind can be either plaintext or markdown. However, either way we + -- will just be rendering markdown, so we handle them both the same way. + -- TODO these can have escaped/sanitized html codes in markdown. We + -- should make sure we handle this correctly. + + -- Some servers send input.value as empty, so let's ignore this :( + -- assert(type(input.value) == 'string') + list_extend(contents, split_lines(input.value or '')) + -- MarkupString variation 2 + elseif input.language then + -- Some servers send input.value as empty, so let's ignore this :( + -- assert(type(input.value) == 'string') + table.insert(contents, "```"..input.language) + list_extend(contents, split_lines(input.value or '')) + table.insert(contents, "```") + -- By deduction, this must be MarkedString[] + else + -- Use our existing logic to handle MarkedString + for _, marked_string in ipairs(input) do + M.convert_input_to_markdown_lines(marked_string, contents) + end + end + end + if contents[1] == '' or contents[1] == nil then + return {} + end + return contents +end + +function M.make_floating_popup_options(width, height, opts) + validate { + opts = { opts, 't', true }; + } + opts = opts or {} + validate { + ["opts.offset_x"] = { opts.offset_x, 'n', true }; + ["opts.offset_y"] = { opts.offset_y, 'n', true }; + } + + local anchor = '' + local row, col + + if vim.fn.winline() <= height then + anchor = anchor..'N' + row = 1 + else + anchor = anchor..'S' + row = 0 + end + + if vim.fn.wincol() + width <= api.nvim_get_option('columns') then + anchor = anchor..'W' + col = 0 + else + anchor = anchor..'E' + col = 1 + end + + return { + anchor = anchor, + col = col + (opts.offset_x or 0), + height = height, + relative = 'cursor', + row = row + (opts.offset_y or 0), + style = 'minimal', + width = width, + } +end + +function M.open_floating_preview(contents, filetype, opts) + validate { + contents = { contents, 't' }; + filetype = { filetype, 's', true }; + opts = { opts, 't', true }; + } + + -- Trim empty lines from the end. + for i = #contents, 1, -1 do + if #contents[i] == 0 then + table.remove(contents) + else + break + end + end + + local width = 0 + local height = #contents + for i, line in ipairs(contents) do + -- Clean up the input and add left pad. + line = " "..line:gsub("\r", "") + -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. + local line_width = vim.fn.strdisplaywidth(line) + width = math.max(line_width, width) + contents[i] = line + end + -- Add right padding of 1 each. + width = width + 1 + + local floating_bufnr = api.nvim_create_buf(false, true) + if filetype then + api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) + end + local float_option = M.make_floating_popup_options(width, height, opts) + local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option) + if filetype == 'markdown' then + api.nvim_win_set_option(floating_winnr, 'conceallevel', 2) + end + api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) + api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) + api.nvim_command("autocmd CursorMoved ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") + return floating_bufnr, floating_winnr +end + +local function validate_lsp_position(pos) + validate { pos = {pos, 't'} } + validate { + line = {pos.line, 'n'}; + character = {pos.character, 'n'}; + } + return true +end + +function M.open_floating_peek_preview(bufnr, start, finish, opts) + validate { + bufnr = {bufnr, 'n'}; + start = {start, validate_lsp_position, 'valid start Position'}; + finish = {finish, validate_lsp_position, 'valid finish Position'}; + opts = { opts, 't', true }; + } + local width = math.max(finish.character - start.character + 1, 1) + local height = math.max(finish.line - start.line + 1, 1) + local floating_winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) + api.nvim_win_set_cursor(floating_winnr, {start.line+1, start.character}) + api.nvim_command("autocmd CursorMoved * ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") + return floating_winnr +end + + +local function highlight_range(bufnr, ns, hiname, start, finish) + if start[1] == finish[1] then + -- TODO care about encoding here since this is in byte index? + api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], finish[2]) + else + api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], -1) + for line = start[1] + 1, finish[1] - 1 do + api.nvim_buf_add_highlight(bufnr, ns, hiname, line, 0, -1) + end + api.nvim_buf_add_highlight(bufnr, ns, hiname, finish[1], 0, finish[2]) + end +end + +do + local all_buffer_diagnostics = {} + + local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") + + local default_severity_highlight = { + [protocol.DiagnosticSeverity.Error] = { guifg = "Red" }; + [protocol.DiagnosticSeverity.Warning] = { guifg = "Orange" }; + [protocol.DiagnosticSeverity.Information] = { guifg = "LightBlue" }; + [protocol.DiagnosticSeverity.Hint] = { guifg = "LightGrey" }; + } + + local underline_highlight_name = "LspDiagnosticsUnderline" + api.nvim_command(string.format("highlight %s gui=underline cterm=underline", underline_highlight_name)) + + local function find_color_rgb(color) + local rgb_hex = api.nvim_get_color_by_name(color) + validate { color = {color, function() return rgb_hex ~= -1 end, "valid color name"} } + return rgb_hex + end + + --- Determine whether to use black or white text + -- Ref: https://stackoverflow.com/a/1855903/837964 + -- https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color + local function color_is_bright(r, g, b) + -- Counting the perceptive luminance - human eye favors green color + local luminance = (0.299*r + 0.587*g + 0.114*b)/255 + if luminance > 0.5 then + return true -- Bright colors, black font + else + return false -- Dark colors, white font + end + end + + local severity_highlights = {} + + function M.set_severity_highlights(highlights) + validate {highlights = {highlights, 't'}} + for severity, default_color in pairs(default_severity_highlight) do + local severity_name = protocol.DiagnosticSeverity[severity] + local highlight_name = "LspDiagnostics"..severity_name + local hi_info = highlights[severity] or default_color + -- Try to fill in the foreground color with a sane default. + if not hi_info.guifg and hi_info.guibg then + -- TODO(ashkan) move this out when bitop is guaranteed to be included. + local bit = require 'bit' + local band, rshift = bit.band, bit.rshift + local rgb = find_color_rgb(hi_info.guibg) + local is_bright = color_is_bright(rshift(rgb, 16), band(rshift(rgb, 8), 0xFF), band(rgb, 0xFF)) + hi_info.guifg = is_bright and "Black" or "White" + end + if not hi_info.ctermfg and hi_info.ctermbg then + -- TODO(ashkan) move this out when bitop is guaranteed to be included. + local bit = require 'bit' + local band, rshift = bit.band, bit.rshift + local rgb = find_color_rgb(hi_info.ctermbg) + local is_bright = color_is_bright(rshift(rgb, 16), band(rshift(rgb, 8), 0xFF), band(rgb, 0xFF)) + hi_info.ctermfg = is_bright and "Black" or "White" + end + local cmd_parts = {"highlight", highlight_name} + for k, v in pairs(hi_info) do + table.insert(cmd_parts, k.."="..v) + end + api.nvim_command(table.concat(cmd_parts, ' ')) + severity_highlights[severity] = highlight_name + end + end + + function M.buf_clear_diagnostics(bufnr) + validate { bufnr = {bufnr, 'n', true} } + bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) + end + + -- Initialize with the defaults. + M.set_severity_highlights(default_severity_highlight) + + function M.get_severity_highlight_name(severity) + return severity_highlights[severity] + end + + function M.show_line_diagnostics() + local bufnr = api.nvim_get_current_buf() + local line = api.nvim_win_get_cursor(0)[1] - 1 + -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {}) + -- if #marks == 0 then + -- return + -- end + -- local buffer_diagnostics = all_buffer_diagnostics[bufnr] + local lines = {"Diagnostics:"} + local highlights = {{0, "Bold"}} + + local buffer_diagnostics = all_buffer_diagnostics[bufnr] + if not buffer_diagnostics then return end + local line_diagnostics = buffer_diagnostics[line] + if not line_diagnostics then return end + + for i, diagnostic in ipairs(line_diagnostics) do + -- for i, mark in ipairs(marks) do + -- local mark_id = mark[1] + -- local diagnostic = buffer_diagnostics[mark_id] + + -- TODO(ashkan) make format configurable? + local prefix = string.format("%d. ", i) + local hiname = severity_highlights[diagnostic.severity] + local message_lines = split_lines(diagnostic.message) + table.insert(lines, prefix..message_lines[1]) + table.insert(highlights, {#prefix + 1, hiname}) + for j = 2, #message_lines do + table.insert(lines, message_lines[j]) + table.insert(highlights, {0, hiname}) + end + end + local popup_bufnr, winnr = M.open_floating_preview(lines, 'plaintext') + for i, hi in ipairs(highlights) do + local prefixlen, hiname = unpack(hi) + -- Start highlight after the prefix + api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1) + end + return popup_bufnr, winnr + end + + function M.buf_diagnostics_save_positions(bufnr, diagnostics) + validate { + bufnr = {bufnr, 'n', true}; + diagnostics = {diagnostics, 't', true}; + } + if not diagnostics then return end + bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + + if not all_buffer_diagnostics[bufnr] then + -- Clean up our data when the buffer unloads. + api.nvim_buf_attach(bufnr, false, { + on_detach = function(b) + all_buffer_diagnostics[b] = nil + end + }) + end + all_buffer_diagnostics[bufnr] = {} + local buffer_diagnostics = all_buffer_diagnostics[bufnr] + + for _, diagnostic in ipairs(diagnostics) do + local start = diagnostic.range.start + -- local mark_id = api.nvim_buf_set_extmark(bufnr, diagnostic_ns, 0, start.line, 0, {}) + -- buffer_diagnostics[mark_id] = diagnostic + local line_diagnostics = buffer_diagnostics[start.line] + if not line_diagnostics then + line_diagnostics = {} + buffer_diagnostics[start.line] = line_diagnostics + end + table.insert(line_diagnostics, diagnostic) + end + end + + + function M.buf_diagnostics_underline(bufnr, diagnostics) + for _, diagnostic in ipairs(diagnostics) do + local start = diagnostic.range.start + local finish = diagnostic.range["end"] + + -- TODO care about encoding here since this is in byte index? + highlight_range(bufnr, diagnostic_ns, underline_highlight_name, + {start.line, start.character}, + {finish.line, finish.character} + ) + end + end + + function M.buf_diagnostics_virtual_text(bufnr, diagnostics) + local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] + if not buffer_line_diagnostics then + M.buf_diagnostics_save_positions(bufnr, diagnostics) + end + buffer_line_diagnostics = all_buffer_diagnostics[bufnr] + if not buffer_line_diagnostics then + return + end + for line, line_diags in pairs(buffer_line_diagnostics) do + local virt_texts = {} + for i = 1, #line_diags - 1 do + table.insert(virt_texts, {"■", severity_highlights[line_diags[i].severity]}) + end + local last = line_diags[#line_diags] + -- TODO(ashkan) use first line instead of subbing 2 spaces? + table.insert(virt_texts, {"■ "..last.message:gsub("\r", ""):gsub("\n", " "), severity_highlights[last.severity]}) + api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {}) + end + end +end + +function M.buf_loclist(bufnr, locations) + local targetwin + for _, winnr in ipairs(api.nvim_list_wins()) do + local winbuf = api.nvim_win_get_buf(winnr) + if winbuf == bufnr then + targetwin = winnr + break + end + end + if not targetwin then return end + + local items = {} + local path = api.nvim_buf_get_name(bufnr) + for _, d in ipairs(locations) do + -- TODO: URL parsing here? + local start = d.range.start + table.insert(items, { + filename = path, + lnum = start.line + 1, + col = start.character + 1, + text = d.message, + }) + end + vim.fn.setloclist(targetwin, items, ' ', 'Language Server') +end + +return M +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index e987e07a2a..ff89acc524 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -98,6 +98,38 @@ function vim.split(s,sep,plain) return t end +--- Return a list of all keys used in a table. +--- However, the order of the return table of keys is not guaranteed. +--- +--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +--- +--@param t Table +--@returns list of keys +function vim.tbl_keys(t) + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + + local keys = {} + for k, _ in pairs(t) do + table.insert(keys, k) + end + return keys +end + +--- Return a list of all values used in a table. +--- However, the order of the return table of values is not guaranteed. +--- +--@param t Table +--@returns list of values +function vim.tbl_values(t) + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + + local values = {} + for _, v in pairs(t) do + table.insert(values, v) + end + return values +end + --- Checks if a list-like (vector) table contains `value`. --- --@param t Table to check @@ -114,6 +146,16 @@ function vim.tbl_contains(t, value) return false end +-- Returns true if the table is empty, and contains no indexed or keyed values. +-- +--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +-- +--@param t Table to check +function vim.tbl_isempty(t) + assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + return next(t) == nil +end + --- Merges two or more map-like tables. --- --@see |extend()| @@ -145,13 +187,69 @@ function vim.tbl_extend(behavior, ...) return ret end +--- Deep compare values for equality +function vim.deep_equal(a, b) + if a == b then return true end + if type(a) ~= type(b) then return false end + if type(a) == 'table' then + -- TODO improve this algorithm's performance. + for k, v in pairs(a) do + if not vim.deep_equal(v, b[k]) then + return false + end + end + for k, v in pairs(b) do + if not vim.deep_equal(v, a[k]) then + return false + end + end + return true + end + return false +end + +--- Add the reverse lookup values to an existing table. +--- For example: +--- `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }` +-- +--Do note that it *modifies* the input. +--@param o table The table to add the reverse to. +function vim.tbl_add_reverse_lookup(o) + local keys = vim.tbl_keys(o) + for _, k in ipairs(keys) do + local v = o[k] + if o[v] then + error(string.format("The reverse lookup found an existing value for %q while processing key %q", tostring(v), tostring(k))) + end + o[v] = k + end + return o +end + +--- Extends a list-like table with the values of another list-like table. +--- +--NOTE: This *mutates* dst! +--@see |extend()| +--- +--@param dst The list which will be modified and appended to. +--@param src The list from which values will be inserted. +function vim.list_extend(dst, src) + assert(type(dst) == 'table', "dst must be a table") + assert(type(src) == 'table', "src must be a table") + for _, v in ipairs(src) do + table.insert(dst, v) + end + return dst +end + --- Creates a copy of a list-like table such that any nested tables are --- "unrolled" and appended to the result. --- +--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +--- --@param t List-like table --@returns Flattened copy of the given list-like table. function vim.tbl_flatten(t) - -- From https://github.com/premake/premake-core/blob/master/src/base/table.lua local result = {} local function _tbl_flatten(_t) local n = #_t @@ -168,6 +266,32 @@ function vim.tbl_flatten(t) return result end +-- Determine whether a Lua table can be treated as an array. +--- +--@params Table +--@returns true: A non-empty array, false: A non-empty table, nil: An empty table +function vim.tbl_islist(t) + if type(t) ~= 'table' then + return false + end + + local count = 0 + + for k, _ in pairs(t) do + if type(k) == "number" then + count = count + 1 + else + return false + end + end + + if count > 0 then + return true + else + return nil + end +end + --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- --@see https://www.lua.org/pil/20.2.html @@ -279,3 +403,4 @@ function vim.is_callable(f) end return vim +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua new file mode 100644 index 0000000000..0a6e0fcb97 --- /dev/null +++ b/runtime/lua/vim/uri.lua @@ -0,0 +1,89 @@ +--- TODO: This is implemented only for files now. +-- https://tools.ietf.org/html/rfc3986 +-- https://tools.ietf.org/html/rfc2732 +-- https://tools.ietf.org/html/rfc2396 + + +local uri_decode +do + local schar = string.char + local function hex_to_char(hex) + return schar(tonumber(hex, 16)) + end + uri_decode = function(str) + return str:gsub("%%([a-fA-F0-9][a-fA-F0-9])", hex_to_char) + end +end + +local uri_encode +do + local PATTERNS = { + --- RFC 2396 + -- https://tools.ietf.org/html/rfc2396#section-2.2 + rfc2396 = "^A-Za-z0-9%-_.!~*'()"; + --- RFC 2732 + -- https://tools.ietf.org/html/rfc2732 + rfc2732 = "^A-Za-z0-9%-_.!~*'()[]"; + --- RFC 3986 + -- https://tools.ietf.org/html/rfc3986#section-2.2 + rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/"; + } + local sbyte, tohex = string.byte + if jit then + tohex = require'bit'.tohex + else + tohex = function(b) return string.format("%02x", b) end + end + local function percent_encode_char(char) + return "%"..tohex(sbyte(char), 2) + end + uri_encode = function(text, rfc) + if not text then return end + local pattern = PATTERNS[rfc] or PATTERNS.rfc3986 + return text:gsub("(["..pattern.."])", percent_encode_char) + end +end + + +local function is_windows_file_uri(uri) + return uri:match('^file:///[a-zA-Z]:') ~= nil +end + +local function uri_from_fname(path) + local volume_path, fname = path:match("^([a-zA-Z]:)(.*)") + local is_windows = volume_path ~= nil + if is_windows then + path = volume_path..uri_encode(fname:gsub("\\", "/")) + else + path = uri_encode(path) + end + local uri_parts = {"file://"} + if is_windows then + table.insert(uri_parts, "/") + end + table.insert(uri_parts, path) + return table.concat(uri_parts) +end + +local function uri_from_bufnr(bufnr) + return uri_from_fname(vim.api.nvim_buf_get_name(bufnr)) +end + +local function uri_to_fname(uri) + -- TODO improve this. + if is_windows_file_uri(uri) then + uri = uri:gsub('^file:///', '') + uri = uri:gsub('/', '\\') + else + uri = uri:gsub('^file://', '') + end + + return uri_decode(uri) +end + +return { + uri_from_fname = uri_from_fname, + uri_from_bufnr = uri_from_bufnr, + uri_to_fname = uri_to_fname, +} +-- vim:sw=2 ts=2 et -- cgit From aeee41192b4b1e8733f1fa2ebdea32a5dd21dca5 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Wed, 13 Nov 2019 21:56:20 +0000 Subject: Remove eventignore - :Man now uses :tag to populate the page --- runtime/autoload/man.vim | 2 -- 1 file changed, 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index ecbe4bb374..f2d34ab82d 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -66,7 +66,6 @@ function! man#open_page(count, count1, mods, ...) abort let [l:buf, l:save_tfu] = [bufnr(), &tagfunc] try - set eventignore+=BufReadCmd set tagfunc=man#goto_tag let l:target = l:name . '(' . l:sect . ')' if a:mods !~# 'tab' && s:find_man() @@ -76,7 +75,6 @@ function! man#open_page(count, count1, mods, ...) abort endif finally call setbufvar(l:buf, '&tagfunc', l:save_tfu) - set eventignore-=BufReadCmd endtry let b:man_sect = sect -- cgit From 18c5f6ab9fa58a69d02a7993dd3ff6f0b9882f26 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Wed, 13 Nov 2019 21:57:17 +0000 Subject: Don't attempt swapfiles for man pages This is because we now use :tag to open a man page, which attempts to open a swap file for a path under man://... --- runtime/autoload/man.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index f2d34ab82d..078ccbb429 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -150,6 +150,7 @@ endfunction function! s:put_page(page) abort setlocal modifiable setlocal noreadonly + setlocal noswapfile silent keepjumps %delete _ silent put =a:page while getline(1) =~# '^\s*$' -- cgit From 807e4039cb209539dc71aacbd1718666c18d4552 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Wed, 13 Nov 2019 22:00:11 +0000 Subject: Sort man pages by relevance during goto_tag() --- runtime/autoload/man.vim | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 078ccbb429..6c74617aca 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -381,14 +381,23 @@ function! man#init_pager() abort endfunction function! man#goto_tag(pattern, flags, info) abort - let [sect, name] = man#extract_sect_and_name_ref(a:pattern) + let [l:sect, l:name] = man#extract_sect_and_name_ref(a:pattern) - let candidates = s:get_paths(sect, name) + let l:paths = s:get_paths(l:sect, l:name) + let l:structured = [] - return map(candidates, { - \ _, path -> { - \ 'name': s:extract_sect_and_name_path(path)[1], - \ 'filename': 'man://' . path, + for l:path in l:paths + let l:n = s:extract_sect_and_name_path(l:path)[1] + let l:structured += [{ 'name': l:n, 'path': l:path }] + endfor + + " sort by relevance - exact matches first, then the previous order + call sort(l:structured, { a, b -> a.name ==? l:name ? -1 : b.name ==? l:name ? 1 : 0 }) + + return map(l:structured, { + \ _, entry -> { + \ 'name': entry.name, + \ 'filename': 'man://' . entry.path, \ 'cmd': '1' \ } \ }) -- cgit From 6989ac05f45f2c3c9539522272fd2c73960fa134 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 12 Nov 2019 22:42:30 -0500 Subject: vim-patch:8.1.0927: USE_CR is never defined Problem: USE_CR is never defined. Solution: Remove usage of USE_CR. (Ken Takata, closes vim/vim#3958) https://github.com/vim/vim/commit/00590740081489db69f43d9f1c0e3f70e29ce6da --- runtime/doc/options.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 52d8624935..d29d9c2676 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2242,8 +2242,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fileformat'* *'ff'* 'fileformat' 'ff' string (Windows default: "dos", - Unix default: "unix", - Macintosh default: "mac") + Unix default: "unix") local to buffer This gives the of the current buffer, which is used for reading/writing the buffer from/to a file: @@ -2265,7 +2264,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'fileformats' 'ffs' string (default: Vim+Vi Win32: "dos,unix", Vim Unix: "unix,dos", - Vim Mac: "mac,unix,dos", Vi others: "") global This gives the end-of-line () formats that will be tried when -- cgit From dab40f43b18d35b283804ecb033310198cbf7548 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 30 Oct 2019 20:53:09 +0100 Subject: Add v:lua.func() vimL syntax for calling lua Also simplify error messages when calling lua from vimL. --- runtime/doc/eval.txt | 4 ++++ runtime/doc/if_lua.txt | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1eb873a5b4..79bf81dc0e 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1737,6 +1737,10 @@ v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and expressions is being evaluated. Read-only when in the |sandbox|. + *v:lua* *lua-variable* +v:lua Prefix for calling lua functions from expressions. + See |v:lua-call| for more information. + *v:mouse_win* *mouse_win-variable* v:mouse_win Window number for a mouse click obtained with |getchar()|. First window has number 1, like with |winnr()|. The value is diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 7eef5d3903..911197acd4 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -326,6 +326,38 @@ Note: second argument to `luaeval` undergoes VimL to Lua conversion Return value is also always converted. When converting, |msgpack-special-dict|s are treated specially. +============================================================================== +v:lua function calls *v:lua-call* + +The special prefix `v:lua` can be used in vimL expressions to call lua +functions which are global or nested inside global tables. The expression +`v:lua.func(arg1, arg2)` is equivalent to executing the lua code +`return func(...)` where the args have been converted to lua values. In addition +`v:lua.somemod.func(args)` will work like `return somemod.func(...)` . + +`v:lua` can also be used in function options like 'omnifunc'. As an +example, consider the following lua implementation of an omnifunc: > + + function mymod.omnifunc(findstart, base) + if findstart == 1 then + return 0 + else + return {'stuff', 'steam', 'strange things'} + end + end + vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc') + +A limitation is that the plugin module ("mymod" in this case) must +be made available as a global. + +Note: `v:lua` without a call is not allowed in a vimL expression. Funcrefs +to lua functions cannot be created. The following are errors: > + + let g:Myvar = v:lua.myfunc + call SomeFunc(v:lua.mycallback) + let g:foo = v:lua + let g:foo = v:['lua'] + ============================================================================== Lua standard modules *lua-stdlib* -- cgit From b83027858af71e5ca976c3b43e0b798c624f5529 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 16 Nov 2019 16:13:51 -0500 Subject: vim-patch:8.1.2289: after :diffsplit closing the window does not disable diff Problem: After :diffsplit closing the window does not disable diff. Solution: Add "closeoff" to 'diffopt' and add it to the default. https://github.com/vim/vim/commit/c8234779790dd873acb88331c50988adf94cc383 --- runtime/doc/options.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d29d9c2676..e64ff1b12c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1875,7 +1875,7 @@ A jump table for the options with a short description can be found at |Q_op|. security reasons. *'dip'* *'diffopt'* -'diffopt' 'dip' string (default "internal,filler") +'diffopt' 'dip' string (default "internal,filler,closeoff") global Option settings for diff mode. It can consist of the following items. All are optional. Items must be separated by a comma. @@ -1932,6 +1932,12 @@ A jump table for the options with a short description can be found at |Q_op|. vertical Start diff mode with vertical splits (unless explicitly specified otherwise). + closeoff When a window is closed where 'diff' is set + and there is only one window remaining in the + same tab page with 'diff' set, execute + `:diffoff` in that window. This undoes a + `:diffsplit` command. + hiddenoff Do not use diff mode for a buffer when it becomes hidden. -- cgit From 7116a41e30903580b7527203b87f6afb26095654 Mon Sep 17 00:00:00 2001 From: Nikolay Shebanov Date: Sun, 17 Nov 2019 00:35:32 +0100 Subject: tutor: change arrows (--->) to symbols ✗ and ✓ #11404 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Display ✓ or ✗ based on the line user sees * Add vim-tutor-mode expectations to the lines marked with an arrow. * Fix some existing expectations to behave predictably. --- runtime/tutor/en/vim-01-beginner.tutor | 40 +++++++------- runtime/tutor/en/vim-01-beginner.tutor.json | 84 +++++++++++++++-------------- 2 files changed, 63 insertions(+), 61 deletions(-) (limited to 'runtime') diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor index 4e6154b24a..5ae0fde0da 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor +++ b/runtime/tutor/en/vim-01-beginner.tutor @@ -91,7 +91,7 @@ NOTE: [:q!](:q) discards any changes you made. In a few lessons you ** Press `x`{normal} to delete the character under the cursor. ** - 1. Move the cursor to the line below marked --->. + 1. Move the cursor to the line below marked ✗. 2. To fix the errors, move the cursor until it is on top of the character to be deleted. @@ -111,7 +111,7 @@ NOTE: As you go through this tutor, do not try to memorize, learn by ** Press `i`{normal} to insert text. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. 2. To make the first line the same as the second, move the cursor on top of the first character AFTER where the text is to be inserted. @@ -130,7 +130,7 @@ There is some text missing from this line. ** Press `A`{normal} to append text. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. It does not matter on what character the cursor is in that line. 2. Press [A](A) and type in the necessary additions. @@ -138,7 +138,7 @@ There is some text missing from this line. 3. As the text has been appended press ``{normal} to return to Normal mode. - 4. Move the cursor to the second line marked ---> and repeat + 4. Move the cursor to the second line marked ✗ and repeat steps 2 and 3 to correct this sentence. There is some text missing from th @@ -211,7 +211,7 @@ Now continue with Lesson 2. 1. Press ``{normal} to make sure you are in Normal mode. - 2. Move the cursor to the line below marked --->. + 2. Move the cursor to the line below marked ✗. 3. Move the cursor to the beginning of a word that needs to be deleted. @@ -227,7 +227,7 @@ There are a some words fun that don't belong paper in this sentence. 1. Press ``{normal} to make sure you are in Normal mode. - 2. Move the cursor to the line below marked --->. + 2. Move the cursor to the line below marked ✗. 3. Move the cursor to the end of the correct line (AFTER the first . ). @@ -263,7 +263,7 @@ NOTE: Pressing just the motion while in Normal mode without an operator ** Typing a number before a motion repeats it that many times. ** - 1. Move the cursor to the start of the line marked ---> below. + 1. Move the cursor to the start of the line marked ✓ below. 2. Type `2w`{normal} to move the cursor two words forward. @@ -285,7 +285,7 @@ In the combination of the delete operator and a motion mentioned above you insert a count before the motion to delete more: d number motion - 1. Move the cursor to the first UPPER CASE word in the line marked --->. + 1. Move the cursor to the first UPPER CASE word in the line marked ✗. 2. Type `d2w`{normal} to delete the two UPPER CASE words @@ -318,7 +318,7 @@ it would be easier to simply type two d's to delete a line. ** Press `u`{normal} to undo the last commands, `U`{normal} to fix a whole line. ** - 1. Move the cursor to the line below marked ---> and place it on the + 1. Move the cursor to the line below marked ✗ and place it on the first error. 2. Type `x`{normal} to delete the first unwanted character. 3. Now type `u`{normal} to undo the last command executed. @@ -359,7 +359,7 @@ Fiix the errors oon thhis line and reeplace them witth undo. ** Type `p`{normal} to put previously deleted text after the cursor. ** - 1. Move the cursor to the first ---> line below. + 1. Move the cursor to the first ✓ line below. 2. Type `dd`{normal} to delete the line and store it in a Vim register. @@ -378,7 +378,7 @@ a) Roses are red, ** Type `rx`{normal} to replace the character at the cursor with x. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. 2. Move the cursor so that it is on top of the first error. @@ -397,7 +397,7 @@ NOTE: Remember that you should be learning by doing, not memorization. ** To change until the end of a word, type `ce`{normal}. ** - 1. Move the cursor to the first line below marked --->. + 1. Move the cursor to the first line below marked ✗. 2. Place the cursor on the "u" in "lubw". @@ -423,7 +423,7 @@ Notice that [c](c)e deletes the word and places you in Insert mode. 2. The motions are the same, such as `w`{normal} (word) and `$`{normal} (end of line). - 3. Move to the first line below marked --->. + 3. Move to the first line below marked ✗. 4. Move the cursor to the first error. @@ -503,7 +503,7 @@ NOTE: When the search reaches the end of the file it will continue at the ** Type `%`{normal} to find a matching ),], or }. ** - 1. Place the cursor on any (, [, or { in the line below marked --->. + 1. Place the cursor on any (, [, or { in the line below marked ✓. 2. Now type the [%](%) character. @@ -521,7 +521,7 @@ NOTE: This is very useful in debugging a program with unmatched parentheses! ** Type `:s/old/new/g` to substitute "new" for "old". ** - 1. Move the cursor to the line below marked --->. + 1. Move the cursor to the line below marked ✗. 2. Type ~~~ cmd @@ -725,7 +725,7 @@ NOTE: You can also read the output of an external command. For example, ** Type `o`{normal} to open a line below the cursor and place you in Insert mode. ** - 1. Move the cursor to the line below marked --->. + 1. Move the cursor to the line below marked ✓. 2. Type the lowercase letter `o`{normal} to [open](o) up a line BELOW the cursor and place you in Insert mode. @@ -743,7 +743,7 @@ Open up a line above this by typing O while the cursor is on this line. ** Type `a`{normal} to insert text AFTER the cursor. ** - 1. Move the cursor to the start of the line below marked --->. + 1. Move the cursor to the start of the line below marked ✗. 2. Press `e`{normal} until the cursor is on the end of "li". @@ -766,7 +766,7 @@ NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only ** Type a capital `R`{normal} to replace more than one character. ** - 1. Move the cursor to the first line below marked --->. Move the cursor to + 1. Move the cursor to the first line below marked ✗. Move the cursor to the beginning of the first "xxx". 2. Now press `R`{normal} ([capital R](R)) and type the number below it in the @@ -787,7 +787,7 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an ** Use the `y`{normal} operator to copy text and `p`{normal} to paste it. ** - 1. Go to the line marked with ---> below and place the cursor after "a)". + 1. Go to the line marked with ✓ below and place the cursor after "a)". 2. Start Visual mode with `v`{normal} and move the cursor to just before "first". @@ -805,7 +805,7 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an end of the next line with `j$`{normal} and put the text there with `p`{normal} a) This is the first item. - b) +b) NOTE: you can use `y`{normal} as an operator: `yw`{normal} yanks one word. diff --git a/runtime/tutor/en/vim-01-beginner.tutor.json b/runtime/tutor/en/vim-01-beginner.tutor.json index 2f87d7543f..af22cf2aca 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor.json +++ b/runtime/tutor/en/vim-01-beginner.tutor.json @@ -1,43 +1,45 @@ { - "expect": { - "24": -1, - "103": "The cow jumped over the moon.", - "124": "There is some text missing from this line.", - "125": "There is some text missing from this line.", - "144": "There is some text missing from this line.", - "145": "There is some text missing from this line.", - "146": "There is also some text missing here.", - "147": "There is also some text missing here.", - "220": "There are some words that don't belong in this sentence.", - "236": "Somebody typed the end of this line twice.", - "276": -1, - "295": "This line of words is cleaned up.", - "309": -1, - "310": -1, - "311": -1, - "312": -1, - "313": -1, - "314": -1, - "315": -1, - "332": "Fix the errors on this line and replace them with undo.", - "372": -1, - "373": -1, - "374": -1, - "375": -1, - "389": "When this line was typed in, someone pressed some wrong keys!", - "390": "When this line was typed in, someone pressed some wrong keys!", - "411": "This line has a few words that need changing using the change operator.", - "412": "This line has a few words that need changing using the change operator.", - "432": "The end of this line needs to be corrected using the c$ command.", - "433": "The end of this line needs to be corrected using the c$ command.", - "497": -1, - "516": -1, - "541": "Usually the best time to see the flowers is in the spring.", - "759": "This line will allow you to practice appending text to a line.", - "760": "This line will allow you to practice appending text to a line.", - "780": "Adding 123 to 456 gives you 579.", - "781": "Adding 123 to 456 gives you 579.", - "807": "a) This is the first item.", - "808": " b) This is the second item." - } + "expect": { + "24": -1, + "103": "The cow jumped over the moon.", + "124": "There is some text missing from this line.", + "125": "There is some text missing from this line.", + "144": "There is some text missing from this line.", + "145": "There is some text missing from this line.", + "146": "There is also some text missing here.", + "147": "There is also some text missing here.", + "220": "There are some words that don't belong in this sentence.", + "236": "Somebody typed the end of this line twice.", + "276": -1, + "295": "This line of words is cleaned up.", + "309": -1, + "310": -1, + "311": -1, + "312": -1, + "313": -1, + "314": -1, + "315": -1, + "332": "Fix the errors on this line and replace them with undo.", + "372": -1, + "373": -1, + "374": -1, + "375": -1, + "389": "When this line was typed in, someone pressed some wrong keys!", + "390": "When this line was typed in, someone pressed some wrong keys!", + "411": "This line has a few words that need changing using the change operator.", + "412": "This line has a few words that need changing using the change operator.", + "432": "The end of this line needs to be corrected using the `c$` command.", + "433": "The end of this line needs to be corrected using the `c$` command.", + "497": -1, + "516": -1, + "541": "Usually the best time to see the flowers is in the spring.", + "735": -1, + "740": -1, + "759": "This line will allow you to practice appending text to a line.", + "760": "This line will allow you to practice appending text to a line.", + "780": "Adding 123 to 456 gives you 579.", + "781": "Adding 123 to 456 gives you 579.", + "807": "a) This is the first item.", + "808": "b) This is the second item." + } } -- cgit From 97f1222005838912e127e4180d7b779c82e8356d Mon Sep 17 00:00:00 2001 From: Marco Hinz Date: Sun, 17 Nov 2019 14:23:17 +0100 Subject: provider/python: add python3.8 executable (#11402) Python 3.8 was released 2019-10-14: https://www.python.org/dev/peps/pep-0569 --- runtime/autoload/provider/pythonx.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index aec18c0508..23e7ff8f64 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -29,8 +29,8 @@ endfunction function! s:get_python_candidates(major_version) abort return { \ 2: ['python2', 'python2.7', 'python2.6', 'python'], - \ 3: ['python3', 'python3.7', 'python3.6', 'python3.5', 'python3.4', 'python3.3', - \ 'python'] + \ 3: ['python3', 'python3.8', 'python3.7', 'python3.6', 'python3.5', + \ 'python3.4', 'python3.3', 'python'] \ }[a:major_version] endfunction -- cgit From af53a0c0123338575dd59934449d7fe836835d1c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 17 Nov 2019 19:06:59 -0800 Subject: doc: Lua [ci skip] #11378 - Rework :help lua-commands - Rename if_lua.txt => lua.txt --- runtime/doc/api.txt | 24 +- runtime/doc/eval.txt | 2 +- runtime/doc/help.txt | 2 +- runtime/doc/if_lua.txt | 939 +---------------------------------------- runtime/doc/lua.txt | 994 ++++++++++++++++++++++++++++++++++++++++++++ runtime/doc/msgpack_rpc.txt | 7 +- runtime/doc/options.txt | 37 +- runtime/doc/vim_diff.txt | 22 +- 8 files changed, 1045 insertions(+), 982 deletions(-) create mode 100644 runtime/doc/lua.txt (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 4ed0a6aba0..57a72e6173 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -462,7 +462,7 @@ We can get a mark by its id: > We can get all marks in a buffer for our namespace (or by a range): > - echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, -1) + echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, {}) => [[1, 0, 2]] Deleting all text surrounding an extmark does not remove the extmark. To @@ -1516,6 +1516,13 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* Activates buffer-update events on a channel, or as Lua callbacks. + Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its + contents): > + events = {} + vim.api.nvim_buf_attach(0, false, { + on_lines=function(...) table.insert(events, {...}) end}) +< + Parameters: ~ {buffer} Buffer handle, or 0 for current buffer {send_buffer} True if the initial notification should @@ -1804,21 +1811,22 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) range ends can be specified as (row, col) tuples, as well as extmark ids in the same namespace. In addition, 0 and -1 works as shorthands for (0,0) and (-1,-1) respectively, so that all - marks in the buffer can be quieried as: + marks in the buffer can be queried as: - all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1) + all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) If end is a lower position than start, then the range will be - traversed backwards. This is mostly used with limited amount, - to be able to get the first marks prior to a given position. + traversed backwards. This is mostly useful with limited + amount, to be able to get the first marks prior to a given + position. Parameters: ~ {buffer} The buffer handle {ns_id} An id returned previously from nvim_create_namespace - {lower} One of: extmark id, (row, col) or 0, -1 for + {start} One of: extmark id, (row, col) or 0, -1 for buffer ends - {upper} One of: extmark id, (row, col) or 0, -1 for + {end} One of: extmark id, (row, col) or 0, -1 for buffer ends {opts} additional options. Supports the keys: • amount: Maximum number of marks to return @@ -1845,7 +1853,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) {ns_id} a identifier returned previously with nvim_create_namespace {id} The extmark's id or 0 to create a new mark. - {row} The row to set the extmark to. + {line} The row to set the extmark to. {col} The column to set the extmark to. {opts} Optional parameters. Currently not used. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 79bf81dc0e..84a893a205 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1738,7 +1738,7 @@ v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and |sandbox|. *v:lua* *lua-variable* -v:lua Prefix for calling lua functions from expressions. +v:lua Prefix for calling Lua functions from expressions. See |v:lua-call| for more information. *v:mouse_win* *mouse_win-variable* diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 284cd26583..6090fa96bb 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -129,6 +129,7 @@ Advanced editing ~ |autocmd.txt| automatically executing commands on an event |eval.txt| expression evaluation, conditional commands |fold.txt| hide (fold) ranges of lines +|lua.txt| Lua API Special issues ~ |print.txt| printing @@ -157,7 +158,6 @@ GUI ~ Interfaces ~ |if_cscop.txt| using Cscope with Vim -|if_lua.txt| Lua interface |if_pyth.txt| Python interface |if_ruby.txt| Ruby interface |sign.txt| debugging signs diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt index 911197acd4..34bcf0f039 100644 --- a/runtime/doc/if_lua.txt +++ b/runtime/doc/if_lua.txt @@ -1,941 +1,8 @@ -*if_lua.txt* Nvim - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL - -Lua engine *lua* *Lua* - - Type |gO| to see the table of contents. - -============================================================================== -Introduction *lua-intro* - -The Lua 5.1 language is builtin and always available. Try this command to get -an idea of what lurks beneath: > - - :lua print(vim.inspect(package.loaded)) - -Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the -"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can -be used from Lua code. - -Module conflicts are resolved by "last wins". For example if both of these -are on 'runtimepath': - runtime/lua/foo.lua - ~/.config/nvim/lua/foo.lua -then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and -"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim -finds and loads Lua modules. The conventions are similar to VimL plugins, -with some extra features. See |lua-require-example| for a walkthrough. - -============================================================================== -Importing Lua modules *lua-require* - -Nvim automatically adjusts `package.path` and `package.cpath` according to -effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is -changed. `package.path` is adjusted by simply appending `/lua/?.lua` and -`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the -first character of `package.config`). - -Similarly to `package.path`, modified directories from 'runtimepath' are also -added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and -`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of -the existing `package.cpath` are used. Example: - -1. Given that - - 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`; - - initial (defined at compile-time or derived from - `$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains - `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`. -2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in - order: parts of the path starting from the first path component containing - question mark and preceding path separator. -3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same - as the suffix of the first path from `package.path` (i.e. `./?.so`). Which - leaves `/?.so` and `/a?d/j/g.elf`, in this order. -4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The - second one contains semicolon which is a paths separator so it is out, - leaving only `/foo/bar` and `/abc`, in order. -5. The cartesian product of paths from 4. and suffixes from 3. is taken, - giving four variants. In each variant `/lua` path segment is inserted - between path and suffix, leaving - - - `/foo/bar/lua/?.so` - - `/foo/bar/lua/a?d/j/g.elf` - - `/abc/lua/?.so` - - `/abc/lua/a?d/j/g.elf` - -6. New paths are prepended to the original `package.cpath`. - -The result will look like this: - - `/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath') - × `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`) - - = `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` - -Note: - -- To track 'runtimepath' updates, paths added at previous update are - remembered and removed at the next update, while all paths derived from the - new 'runtimepath' are prepended as described above. This allows removing - paths when path is removed from 'runtimepath', adding paths when they are - added and reordering `package.path`/`package.cpath` content if 'runtimepath' - was reordered. - -- Although adjustments happen automatically, Nvim does not track current - values of `package.path` or `package.cpath`. If you happen to delete some - paths from there you can set 'runtimepath' to trigger an update: > - let &runtimepath = &runtimepath - -- Skipping paths from 'runtimepath' which contain semicolons applies both to - `package.path` and `package.cpath`. Given that there are some badly written - plugins using shell which will not work with paths containing semicolons it - is better to not have them in 'runtimepath' at all. - ------------------------------------------------------------------------------- -LUA PLUGIN EXAMPLE *lua-require-example* - -The following example plugin adds a command `:MakeCharBlob` which transforms -current buffer into a long `unsigned char` array. Lua contains transformation -function in a module `lua/charblob.lua` which is imported in -`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed -to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in -this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`). - -autoload/charblob.vim: > - - function charblob#encode_buffer() - call setline(1, luaeval( - \ 'require("charblob").encode(unpack(_A))', - \ [getline(1, '$'), &textwidth, ' '])) - endfunction - -plugin/charblob.vim: > - - if exists('g:charblob_loaded') - finish - endif - let g:charblob_loaded = 1 - - command MakeCharBlob :call charblob#encode_buffer() - -lua/charblob.lua: > - - local function charblob_bytes_iter(lines) - local init_s = { - next_line_idx = 1, - next_byte_idx = 1, - lines = lines, - } - local function next(s, _) - if lines[s.next_line_idx] == nil then - return nil - end - if s.next_byte_idx > #(lines[s.next_line_idx]) then - s.next_line_idx = s.next_line_idx + 1 - s.next_byte_idx = 1 - return ('\n'):byte() - end - local ret = lines[s.next_line_idx]:byte(s.next_byte_idx) - if ret == ('\n'):byte() then - ret = 0 -- See :h NL-used-for-NUL. - end - s.next_byte_idx = s.next_byte_idx + 1 - return ret - end - return next, init_s, nil - end - - local function charblob_encode(lines, textwidth, indent) - local ret = { - 'const unsigned char blob[] = {', - indent, - } - for byte in charblob_bytes_iter(lines) do - -- .- space + number (width 3) + comma - if #(ret[#ret]) + 5 > textwidth then - ret[#ret + 1] = indent - else - ret[#ret] = ret[#ret] .. ' ' - end - ret[#ret] = ret[#ret] .. (('%3u,'):format(byte)) - end - ret[#ret + 1] = '};' - return ret - end - - return { - bytes_iter = charblob_bytes_iter, - encode = charblob_encode, - } - -============================================================================== -Commands *lua-commands* - - *:lua* -:[range]lua {chunk} - Execute Lua chunk {chunk}. - -Examples: -> - :lua vim.api.nvim_command('echo "Hello, Nvim!"') -< -To see the Lua version: > - :lua print(_VERSION) - -To see the LuaJIT version: > - :lua print(jit.version) -< - -:[range]lua << [endmarker] -{script} -{endmarker} - Execute Lua script {script}. Useful for including Lua - code in Vim scripts. - -The {endmarker} must NOT be preceded by any white space. - -If [endmarker] is omitted from after the "<<", a dot '.' must be used after -{script}, like for the |:append| and |:insert| commands. - -Example: -> - function! CurrentLineInfo() - lua << EOF - local linenr = vim.api.nvim_win_get_cursor(0)[1] - local curline = vim.api.nvim_buf_get_lines( - 0, linenr, linenr + 1, false)[1] - print(string.format("Current line [%d] has %d bytes", - linenr, #curline)) - EOF - endfunction - -Note that the `local` variables will disappear when block finishes. This is -not the case for globals. - - *:luado* -:[range]luado {body} Execute Lua function "function (line, linenr) {body} - end" for each line in the [range], with the function - argument being set to the text of each line in turn, - without a trailing , and the current line number. - If the value returned by the function is a string it - becomes the text of the line in the current turn. The - default for [range] is the whole file: "1,$". - -Examples: -> - :luado return string.format("%s\t%d", line:reverse(), #line) - - :lua require"lpeg" - :lua -- balanced parenthesis grammar: - :lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" } - :luado if bp:match(line) then return "-->\t" .. line end -< - - *:luafile* -:[range]luafile {file} - Execute Lua script in {file}. - The whole argument is used as a single file name. - -Examples: -> - :luafile script.lua - :luafile % -< - -All these commands execute a Lua chunk from either the command line (:lua and -:luado) or a file (:luafile) with the given line [range]. Similarly to the Lua -interpreter, each chunk has its own scope and so only global variables are -shared between command calls. All Lua default libraries are available. In -addition, Lua "print" function has its output redirected to the Nvim message -area, with arguments separated by a white space instead of a tab. - -Lua uses the "vim" module (see |lua-vim|) to issue commands to Nvim. However, -procedures that alter buffer content, open new buffers, and change cursor -position are restricted when the command is executed in the |sandbox|. - - -============================================================================== -luaeval() *lua-eval* *luaeval()* - -The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is -"luaeval". "luaeval" takes an expression string and an optional argument used -for _A inside expression and returns the result of the expression. It is -semantically equivalent in Lua to: -> - local chunkheader = "local _A = select(1, ...) return " - function luaeval (expstr, arg) - local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) - return chunk(arg) -- return typval - end - -Lua nils, numbers, strings, tables and booleans are converted to their -respective VimL types. An error is thrown if conversion of any other Lua types -is attempted. - -The magic global "_A" contains the second argument to luaeval(). - -Example: > - :echo luaeval('_A[1] + _A[2]', [40, 2]) - 42 - :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123') - foo - -Lua tables are used as both dictionaries and lists, so it is impossible to -determine whether empty table is meant to be empty list or empty dictionary. -Additionally Lua does not have integer numbers. To distinguish between these -cases there is the following agreement: - -0. Empty table is empty list. -1. Table with N incrementally growing integral numbers, starting from 1 and - ending with N is considered to be a list. -2. Table with string keys, none of which contains NUL byte, is considered to - be a dictionary. -3. Table with string keys, at least one of which contains NUL byte, is also - considered to be a dictionary, but this time it is converted to - a |msgpack-special-map|. - *lua-special-tbl* -4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point - value: - - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to - a floating-point 1.0. Note that by default integral Lua numbers are - converted to |Number|s, non-integral are converted to |Float|s. This - variant allows integral |Float|s. - - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty - dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is - converted to a dictionary `{'a': 42}`: non-string keys are ignored. - Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. - are errors. - - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well - as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not - form a 1-step sequence from 1 to N are ignored, as well as all - non-integral keys. - -Examples: > - - :echo luaeval('math.pi') - :function Rand(x,y) " random uniform between x and y - : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) - : endfunction - :echo Rand(1,10) - -Note: second argument to `luaeval` undergoes VimL to Lua conversion -("marshalled"), so changes to Lua containers do not affect values in VimL. -Return value is also always converted. When converting, -|msgpack-special-dict|s are treated specially. - -============================================================================== -v:lua function calls *v:lua-call* - -The special prefix `v:lua` can be used in vimL expressions to call lua -functions which are global or nested inside global tables. The expression -`v:lua.func(arg1, arg2)` is equivalent to executing the lua code -`return func(...)` where the args have been converted to lua values. In addition -`v:lua.somemod.func(args)` will work like `return somemod.func(...)` . - -`v:lua` can also be used in function options like 'omnifunc'. As an -example, consider the following lua implementation of an omnifunc: > - - function mymod.omnifunc(findstart, base) - if findstart == 1 then - return 0 - else - return {'stuff', 'steam', 'strange things'} - end - end - vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc') - -A limitation is that the plugin module ("mymod" in this case) must -be made available as a global. - -Note: `v:lua` without a call is not allowed in a vimL expression. Funcrefs -to lua functions cannot be created. The following are errors: > - - let g:Myvar = v:lua.myfunc - call SomeFunc(v:lua.mycallback) - let g:foo = v:lua - let g:foo = v:['lua'] - -============================================================================== -Lua standard modules *lua-stdlib* - -The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes -various functions and sub-modules. It is always loaded, thus require("vim") -is unnecessary. - -You can peek at the module properties: > - - :lua print(vim.inspect(vim)) - -Result is something like this: > - - { - _os_proc_children = , - _os_proc_info = , - ... - api = { - nvim__id = , - nvim__id_array = , - ... - }, - deepcopy = , - gsplit = , - ... - } - -To find documentation on e.g. the "deepcopy" function: > - - :help vim.deepcopy() - -Note that underscore-prefixed functions (e.g. "_os_proc_children") are -internal/private and must not be used by plugins. - ------------------------------------------------------------------------------- -VIM.LOOP *lua-loop* *vim.loop* - -`vim.loop` exposes all features of the Nvim event-loop. This is a low-level -API that provides functionality for networking, filesystem, and process -management. Try this command to see available functions: > - - :lua print(vim.inspect(vim.loop)) - -Reference: http://docs.libuv.org -Examples: https://github.com/luvit/luv/tree/master/examples - - *E5560* *lua-loop-callbacks* -It is an error to directly invoke `vim.api` functions (except |api-fast|) in -`vim.loop` callbacks. For example, this is an error: > - - local timer = vim.loop.new_timer() - timer:start(1000, 0, function() - vim.api.nvim_command('echomsg "test"') - end) - -To avoid the error use |vim.schedule_wrap()| to defer the callback: > - - local timer = vim.loop.new_timer() - timer:start(1000, 0, vim.schedule_wrap(function() - vim.api.nvim_command('echomsg "test"') - end)) - -Example: repeating timer - 1. Save this code to a file. - 2. Execute it with ":luafile %". > - - -- Create a timer handle (implementation detail: uv_timer_t). - local timer = vim.loop.new_timer() - local i = 0 - -- Waits 1000ms, then repeats every 750ms until timer:close(). - timer:start(1000, 750, function() - print('timer invoked! i='..tostring(i)) - if i > 4 then - timer:close() -- Always close handles to avoid leaks. - end - i = i + 1 - end) - print('sleeping'); - - -Example: File-change detection *file-change-detect* - 1. Save this code to a file. - 2. Execute it with ":luafile %". - 3. Use ":Watch %" to watch any file. - 4. Try editing the file from another text editor. - 5. Observe that the file reloads in Nvim (because on_change() calls - |:checktime|). > - - local w = vim.loop.new_fs_event() - local function on_change(err, fname, status) - -- Do work... - vim.api.nvim_command('checktime') - -- Debounce: stop/start. - w:stop() - watch_file(fname) - end - function watch_file(fname) - local fullpath = vim.api.nvim_call_function( - 'fnamemodify', {fname, ':p'}) - w:start(fullpath, {}, vim.schedule_wrap(function(...) - on_change(...) end)) - end - vim.api.nvim_command( - "command! -nargs=1 Watch call luaeval('watch_file(_A)', expand(''))") - - -Example: TCP echo-server *tcp-server* - 1. Save this code to a file. - 2. Execute it with ":luafile %". - 3. Note the port number. - 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): > - - local function create_server(host, port, on_connect) - local server = vim.loop.new_tcp() - server:bind(host, port) - server:listen(128, function(err) - assert(not err, err) -- Check for errors. - local sock = vim.loop.new_tcp() - server:accept(sock) -- Accept client connection. - on_connect(sock) -- Start reading messages. - end) - return server - end - local server = create_server('0.0.0.0', 0, function(sock) - sock:read_start(function(err, chunk) - assert(not err, err) -- Check for errors. - if chunk then - sock:write(chunk) -- Echo received messages to the channel. - else -- EOF (stream closed). - sock:close() -- Always close handles to avoid leaks. - end - end) - end) - print('TCP echo-server listening on port: '..server:getsockname().port) - ------------------------------------------------------------------------------- -VIM.TREESITTER *lua-treesitter* - -Nvim integrates the tree-sitter library for incremental parsing of buffers. - -Currently Nvim does not provide the tree-sitter parsers, instead these must -be built separately, for instance using the tree-sitter utility. -The parser is loaded into nvim using > - - vim.treesitter.add_language("/path/to/c_parser.so", "c") - - - - parser = vim.treesitter.get_parser(bufnr, lang) - -<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this -doesn't work yet for some filetypes like "cpp") Currently, the parser will be -retained for the lifetime of a buffer but this is subject to change. A plugin -should keep a reference to the parser object as long as it wants incremental -updates. - -Whenever you need to access the current syntax tree, parse the buffer: > - - tstree = parser:parse() - - - print(tostring(vim.api.nvim_get_current_line())) - -vim.call({func}, {...}) *vim.call()* - Invokes |vim-function| or |user-function| {func} with arguments {...}. - See also |vim.fn|. Equivalent to: > - vim.fn[func]({...}) - -vim.in_fast_event() *vim.in_fast_event()* - Returns true if the code is executing as part of a "fast" event - handler, where most of the API is disabled. These are low-level events - (e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls - for input. When this is `false` most API functions are callable (but - may be subject to other restrictions such as |textlock|). - -vim.NIL *vim.NIL* - Special value used to represent NIL in msgpack-rpc and |v:null| in - vimL interaction, and similar cases. Lua `nil` cannot be used as - part of a lua table representing a Dictionary or Array, as it - is equivalent to a missing value: `{"foo", nil}` is the same as - `{"foo"}` - -vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* - Sends {event} to {channel} via |RPC| and returns immediately. - If {channel} is 0, the event is broadcast to all channels. - - This function also works in a fast callback |lua-loop-callbacks|. - -vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()* - Sends a request to {channel} to invoke {method} via - |RPC| and blocks until a response is received. - - Note: NIL values as part of the return value is represented as - |vim.NIL| special value - -vim.stricmp({a}, {b}) *vim.stricmp()* - Compares strings case-insensitively. Returns 0, 1 or -1 if strings - are equal, {a} is greater than {b} or {a} is lesser than {b}, - 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. - - Embedded NUL bytes are treated as terminating the string. Invalid - UTF-8 bytes, and embedded surrogates are counted as one code - point each. An {index} in the middle of a UTF-8 sequence is rounded - upwards to the end of that sequence. - -vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()* - Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not - supplied, it defaults to false (use UTF-32). Returns the byte index. - - Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index} - in the middle of a UTF-16 sequence is rounded upwards to the end of that - sequence. - -vim.schedule({callback}) *vim.schedule()* - Schedules {callback} to be invoked soon by the main event-loop. Useful - to avoid |textlock| or other temporary restrictions. - -vim.fn.{func}({...}) *vim.fn* - Invokes |vim-function| or |user-function| {func} with arguments {...}. - To call autoload functions, use the syntax: > - vim.fn['some#function']({...}) -< - Unlike vim.api.|nvim_call_function| this converts directly between Vim - objects and Lua objects. If the Vim function returns a float, it will - be represented directly as a Lua number. Empty lists and dictionaries - both are represented by an empty table. - - Note: |v:null| values as part of the return value is represented as - |vim.NIL| special value - - Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only - enumerates functions that were called at least once. - -vim.type_idx *vim.type_idx* - Type index for use in |lua-special-tbl|. Specifying one of the - values from |vim.types| allows typing the empty table (it is - unclear whether empty Lua table represents empty list or empty array) - and forcing integral numbers to be |Float|. See |lua-special-tbl| for - more details. - -vim.val_idx *vim.val_idx* - Value index for tables representing |Float|s. A table representing - floating-point value 1.0 looks like this: > - { - [vim.type_idx] = vim.types.float, - [vim.val_idx] = 1.0, - } -< See also |vim.type_idx| and |lua-special-tbl|. - -vim.types *vim.types* - Table with possible values for |vim.type_idx|. Contains two sets - of key-value pairs: first maps possible values for |vim.type_idx| - to human-readable strings, second maps human-readable type names to - values for |vim.type_idx|. Currently contains pairs for `float`, - `array` and `dictionary` types. - - Note: one must expect that values corresponding to `vim.types.float`, - `vim.types.array` and `vim.types.dictionary` fall under only two - following assumptions: - 1. Value may serve both as a key and as a value in a table. Given the - properties of Lua tables this basically means “value is not `nil`”. - 2. For each value in `vim.types` table `vim.types[vim.types[value]]` - is the same as `value`. - No other restrictions are put on types, and it is not guaranteed that - values corresponding to `vim.types.float`, `vim.types.array` and - `vim.types.dictionary` will not change or that `vim.types` table will - only contain values for these three types. +Moved to |lua.txt| ============================================================================== -Lua module: vim *lua-vim* - -inspect({object}, {options}) *vim.inspect()* - Return a human-readable representation of the given object. - - See also: ~ - https://github.com/kikito/inspect.lua - https://github.com/mpeterv/vinspect - -paste({lines}, {phase}) *vim.paste()* - Paste handler, invoked by |nvim_paste()| when a conforming UI - (such as the |TUI|) pastes text into the editor. - - Example: To remove ANSI color codes when pasting: > - - vim.paste = (function() - local overridden = vim.paste - return function(lines, phase) - for i,line in ipairs(lines) do - -- Scrub ANSI color codes from paste input. - lines[i] = line:gsub('\27%[[0-9;mK]+', '') - end - overridden(lines, phase) - end - end)() -< - - Parameters: ~ - {lines} |readfile()|-style list of lines to paste. - |channel-lines| - {phase} -1: "non-streaming" paste: the call contains all - lines. If paste is "streamed", `phase` indicates the stream state: - • 1: starts the paste (exactly once) - • 2: continues the paste (zero or more times) - • 3: ends the paste (exactly once) - - Return: ~ - false if client should cancel the paste. - - See also: ~ - |paste| - -schedule_wrap({cb}) *vim.schedule_wrap()* - Defers callback `cb` until the Nvim API is safe to call. - - See also: ~ - |lua-loop-callbacks| - |vim.schedule()| - |vim.in_fast_event()| - - - - -deepcopy({orig}) *vim.deepcopy()* - Returns a deep copy of the given object. Non-table objects are - copied as in a typical Lua assignment, whereas table objects - are copied recursively. - - Parameters: ~ - {orig} Table to copy - - Return: ~ - New table of copied keys and (nested) values. - -gsplit({s}, {sep}, {plain}) *vim.gsplit()* - Splits a string at each instance of a separator. - - Parameters: ~ - {s} String to split - {sep} Separator string or pattern - {plain} If `true` use `sep` literally (passed to - String.find) - - Return: ~ - Iterator over the split components - - See also: ~ - |vim.split()| - https://www.lua.org/pil/20.2.html - http://lua-users.org/wiki/StringLibraryTutorial - -split({s}, {sep}, {plain}) *vim.split()* - Splits a string at each instance of a separator. - - Examples: > - split(":aa::b:", ":") --> {'','aa','','bb',''} - split("axaby", "ab?") --> {'','x','y'} - split(x*yz*o, "*", true) --> {'x','yz','o'} -< - - Parameters: ~ - {s} String to split - {sep} Separator string or pattern - {plain} If `true` use `sep` literally (passed to - String.find) - - Return: ~ - List-like table of the split components. - - See also: ~ - |vim.gsplit()| - -tbl_contains({t}, {value}) *vim.tbl_contains()* - Checks if a list-like (vector) table contains `value` . - - Parameters: ~ - {t} Table to check - {value} Value to compare - - Return: ~ - true if `t` contains `value` - -tbl_extend({behavior}, {...}) *vim.tbl_extend()* - Merges two or more map-like tables. - - Parameters: ~ - {behavior} Decides what to do if a key is found in more - than one map: - • "error": raise an error - • "keep": use value from the leftmost map - • "force": use value from the rightmost map - {...} Two or more map-like tables. - - See also: ~ - |extend()| - -tbl_flatten({t}) *vim.tbl_flatten()* - Creates a copy of a list-like table such that any nested - tables are "unrolled" and appended to the result. - - Parameters: ~ - {t} List-like table - - Return: ~ - Flattened copy of the given list-like table. - -trim({s}) *vim.trim()* - Trim whitespace (Lua pattern "%s") from both sides of a - string. - - Parameters: ~ - {s} String to trim - - Return: ~ - String with whitespace removed from its beginning and end - - See also: ~ - https://www.lua.org/pil/20.2.html - -pesc({s}) *vim.pesc()* - Escapes magic chars in a Lua pattern string. - - Parameters: ~ - {s} String to escape - - Return: ~ - %-escaped pattern string - - See also: ~ - https://github.com/rxi/lume - -validate({opt}) *vim.validate()* - Validates a parameter specification (types and values). - - Usage example: > - - function user.new(name, age, hobbies) - vim.validate{ - name={name, 'string'}, - age={age, 'number'}, - hobbies={hobbies, 'table'}, - } - ... - end -< - - Examples with explicit argument values (can be run directly): > - - vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} - => NOP (success) -< -> - vim.validate{arg1={1, 'table'}} - => error('arg1: expected table, got number') -< -> - vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} - => error('arg1: expected even number, got 3') -< - - Parameters: ~ - {opt} Map of parameter names to validations. Each key is - a parameter name; each value is a tuple in one of - these forms: - 1. (arg_value, type_name, optional) - • arg_value: argument value - • type_name: string type name, one of: ("table", - "t", "string", "s", "number", "n", "boolean", - "b", "function", "f", "nil", "thread", - "userdata") - • optional: (optional) boolean, if true, `nil` - is valid - - 2. (arg_value, fn, msg) - • arg_value: argument value - • fn: any function accepting one argument, - returns true if and only if the argument is - valid - • msg: (optional) error string if validation - fails - -is_callable({f}) *vim.is_callable()* - Returns true if object `f` can be called as a function. - - Parameters: ~ - {f} Any object - - Return: ~ - true if `f` is callable, else false - - vim:tw=78:ts=8:ft=help:norl: + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt new file mode 100644 index 0000000000..edcf246295 --- /dev/null +++ b/runtime/doc/lua.txt @@ -0,0 +1,994 @@ +*lua.txt* Nvim + + + NVIM REFERENCE MANUAL + + +Lua engine *lua* *Lua* + + Type |gO| to see the table of contents. + +============================================================================== +Introduction *lua-intro* + +The Lua 5.1 language is builtin and always available. Try this command to get +an idea of what lurks beneath: > + + :lua print(vim.inspect(package.loaded)) + +Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the +"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can +be used from Lua code. + +Module conflicts are resolved by "last wins". For example if both of these +are on 'runtimepath': + runtime/lua/foo.lua + ~/.config/nvim/lua/foo.lua +then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and +"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim +finds and loads Lua modules. The conventions are similar to VimL plugins, +with some extra features. See |lua-require-example| for a walkthrough. + +============================================================================== +Importing Lua modules *lua-require* + + *lua-package-path* +Nvim automatically adjusts `package.path` and `package.cpath` according to +effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is +changed. `package.path` is adjusted by simply appending `/lua/?.lua` and +`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the +first character of `package.config`). + +Similarly to `package.path`, modified directories from 'runtimepath' are also +added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and +`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of +the existing `package.cpath` are used. Example: + +1. Given that + - 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`; + - initial (defined at compile-time or derived from + `$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains + `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`. +2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in + order: parts of the path starting from the first path component containing + question mark and preceding path separator. +3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as it’s the same + as the suffix of the first path from `package.path` (i.e. `./?.so`). Which + leaves `/?.so` and `/a?d/j/g.elf`, in this order. +4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The + second one contains semicolon which is a paths separator so it is out, + leaving only `/foo/bar` and `/abc`, in order. +5. The cartesian product of paths from 4. and suffixes from 3. is taken, + giving four variants. In each variant `/lua` path segment is inserted + between path and suffix, leaving + + - `/foo/bar/lua/?.so` + - `/foo/bar/lua/a?d/j/g.elf` + - `/abc/lua/?.so` + - `/abc/lua/a?d/j/g.elf` + +6. New paths are prepended to the original `package.cpath`. + +The result will look like this: + + `/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath') + × `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`) + + = `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` + +Note: + +- To track 'runtimepath' updates, paths added at previous update are + remembered and removed at the next update, while all paths derived from the + new 'runtimepath' are prepended as described above. This allows removing + paths when path is removed from 'runtimepath', adding paths when they are + added and reordering `package.path`/`package.cpath` content if 'runtimepath' + was reordered. + +- Although adjustments happen automatically, Nvim does not track current + values of `package.path` or `package.cpath`. If you happen to delete some + paths from there you can set 'runtimepath' to trigger an update: > + let &runtimepath = &runtimepath + +- Skipping paths from 'runtimepath' which contain semicolons applies both to + `package.path` and `package.cpath`. Given that there are some badly written + plugins using shell which will not work with paths containing semicolons it + is better to not have them in 'runtimepath' at all. + +------------------------------------------------------------------------------ +LUA PLUGIN EXAMPLE *lua-require-example* + +The following example plugin adds a command `:MakeCharBlob` which transforms +current buffer into a long `unsigned char` array. Lua contains transformation +function in a module `lua/charblob.lua` which is imported in +`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed +to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in +this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`). + +autoload/charblob.vim: > + + function charblob#encode_buffer() + call setline(1, luaeval( + \ 'require("charblob").encode(unpack(_A))', + \ [getline(1, '$'), &textwidth, ' '])) + endfunction + +plugin/charblob.vim: > + + if exists('g:charblob_loaded') + finish + endif + let g:charblob_loaded = 1 + + command MakeCharBlob :call charblob#encode_buffer() + +lua/charblob.lua: > + + local function charblob_bytes_iter(lines) + local init_s = { + next_line_idx = 1, + next_byte_idx = 1, + lines = lines, + } + local function next(s, _) + if lines[s.next_line_idx] == nil then + return nil + end + if s.next_byte_idx > #(lines[s.next_line_idx]) then + s.next_line_idx = s.next_line_idx + 1 + s.next_byte_idx = 1 + return ('\n'):byte() + end + local ret = lines[s.next_line_idx]:byte(s.next_byte_idx) + if ret == ('\n'):byte() then + ret = 0 -- See :h NL-used-for-NUL. + end + s.next_byte_idx = s.next_byte_idx + 1 + return ret + end + return next, init_s, nil + end + + local function charblob_encode(lines, textwidth, indent) + local ret = { + 'const unsigned char blob[] = {', + indent, + } + for byte in charblob_bytes_iter(lines) do + -- .- space + number (width 3) + comma + if #(ret[#ret]) + 5 > textwidth then + ret[#ret + 1] = indent + else + ret[#ret] = ret[#ret] .. ' ' + end + ret[#ret] = ret[#ret] .. (('%3u,'):format(byte)) + end + ret[#ret + 1] = '};' + return ret + end + + return { + bytes_iter = charblob_bytes_iter, + encode = charblob_encode, + } + +============================================================================== +Commands *lua-commands* + +These commands execute a Lua chunk from either the command line (:lua, :luado) +or a file (:luafile) on the given line [range]. As always in Lua, each chunk +has its own scope (closure), so only global variables are shared between +command calls. The |lua-stdlib| modules, user modules, and anything else on +|lua-package-path| are available. + +The Lua print() function redirects its output to the Nvim message area, with +arguments separated by " " (space) instead of "\t" (tab). + + *:lua* +:[range]lua {chunk} + Executes Lua chunk {chunk}. + + Examples: > + :lua vim.api.nvim_command('echo "Hello, Nvim!"') +< To see the Lua version: > + :lua print(_VERSION) +< To see the LuaJIT version: > + :lua print(jit.version) +< + *:lua-heredoc* +:[range]lua << [endmarker] +{script} +{endmarker} + Executes Lua script {script} from within Vimscript. + {endmarker} must NOT be preceded by whitespace. You + can omit [endmarker] after the "<<" and use a dot "." + after {script} (similar to |:append|, |:insert|). + + Example: + > + function! CurrentLineInfo() + lua << EOF + local linenr = vim.api.nvim_win_get_cursor(0)[1] + local curline = vim.api.nvim_buf_get_lines( + 0, linenr, linenr + 1, false)[1] + print(string.format("Current line [%d] has %d bytes", + linenr, #curline)) + EOF + endfunction + +< Note that the `local` variables will disappear when + the block finishes. But not globals. + + *:luado* +:[range]luado {body} Executes Lua chunk "function(line, linenr) {body} end" + for each buffer line in [range], where `line` is the + current line text (without ), and `linenr` is the + current line number. If the function returns a string + that becomes the text of the corresponding buffer + line. Default [range] is the whole file: "1,$". + + Examples: + > + :luado return string.format("%s\t%d", line:reverse(), #line) + + :lua require"lpeg" + :lua -- balanced parenthesis grammar: + :lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" } + :luado if bp:match(line) then return "-->\t" .. line end +< + + *:luafile* +:[range]luafile {file} + Execute Lua script in {file}. + The whole argument is used as a single file name. + + Examples: + > + :luafile script.lua + :luafile % +< + +============================================================================== +luaeval() *lua-eval* *luaeval()* + +The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is +"luaeval". "luaeval" takes an expression string and an optional argument used +for _A inside expression and returns the result of the expression. It is +semantically equivalent in Lua to: +> + local chunkheader = "local _A = select(1, ...) return " + function luaeval (expstr, arg) + local chunk = assert(loadstring(chunkheader .. expstr, "luaeval")) + return chunk(arg) -- return typval + end + +Lua nils, numbers, strings, tables and booleans are converted to their +respective VimL types. An error is thrown if conversion of any other Lua types +is attempted. + +The magic global "_A" contains the second argument to luaeval(). + +Example: > + :echo luaeval('_A[1] + _A[2]', [40, 2]) + 42 + :echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123') + foo + +Lua tables are used as both dictionaries and lists, so it is impossible to +determine whether empty table is meant to be empty list or empty dictionary. +Additionally Lua does not have integer numbers. To distinguish between these +cases there is the following agreement: + +0. Empty table is empty list. +1. Table with N incrementally growing integral numbers, starting from 1 and + ending with N is considered to be a list. +2. Table with string keys, none of which contains NUL byte, is considered to + be a dictionary. +3. Table with string keys, at least one of which contains NUL byte, is also + considered to be a dictionary, but this time it is converted to + a |msgpack-special-map|. + *lua-special-tbl* +4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point + value: + - `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to + a floating-point 1.0. Note that by default integral Lua numbers are + converted to |Number|s, non-integral are converted to |Float|s. This + variant allows integral |Float|s. + - `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty + dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is + converted to a dictionary `{'a': 42}`: non-string keys are ignored. + Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. + are errors. + - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well + as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not + form a 1-step sequence from 1 to N are ignored, as well as all + non-integral keys. + +Examples: > + + :echo luaeval('math.pi') + :function Rand(x,y) " random uniform between x and y + : return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y}) + : endfunction + :echo Rand(1,10) + +Note: second argument to `luaeval` undergoes VimL to Lua conversion +("marshalled"), so changes to Lua containers do not affect values in VimL. +Return value is also always converted. When converting, +|msgpack-special-dict|s are treated specially. + +============================================================================== +Vimscript v:lua interface *v:lua-call* + +From Vimscript the special `v:lua` prefix can be used to call Lua functions +which are global or accessible from global tables. The expression > + v:lua.func(arg1, arg2) +is equivalent to the Lua chunk > + return func(...) +where the args are converted to Lua values. The expression > + v:lua.somemod.func(args) +is equivalent to the Lua chunk > + return somemod.func(...) + +You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc. +For example consider the following Lua omnifunc handler: > + + function mymod.omnifunc(findstart, base) + if findstart == 1 then + return 0 + else + return {'stuff', 'steam', 'strange things'} + end + end + vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc') + +Note: the module ("mymod" in the above example) must be a Lua global. + +Note: `v:lua` without a call is not allowed in a Vimscript expression: +|Funcref|s cannot represent Lua functions. The following are errors: > + + let g:Myvar = v:lua.myfunc " Error + call SomeFunc(v:lua.mycallback) " Error + let g:foo = v:lua " Error + let g:foo = v:['lua'] " Error + + +============================================================================== +Lua standard modules *lua-stdlib* + +The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes +various functions and sub-modules. It is always loaded, thus require("vim") +is unnecessary. + +You can peek at the module properties: > + + :lua print(vim.inspect(vim)) + +Result is something like this: > + + { + _os_proc_children = , + _os_proc_info = , + ... + api = { + nvim__id = , + nvim__id_array = , + ... + }, + deepcopy = , + gsplit = , + ... + } + +To find documentation on e.g. the "deepcopy" function: > + + :help vim.deepcopy() + +Note that underscore-prefixed functions (e.g. "_os_proc_children") are +internal/private and must not be used by plugins. + +------------------------------------------------------------------------------ +VIM.LOOP *lua-loop* *vim.loop* + +`vim.loop` exposes all features of the Nvim event-loop. This is a low-level +API that provides functionality for networking, filesystem, and process +management. Try this command to see available functions: > + + :lua print(vim.inspect(vim.loop)) + +Reference: http://docs.libuv.org +Examples: https://github.com/luvit/luv/tree/master/examples + + *E5560* *lua-loop-callbacks* +It is an error to directly invoke `vim.api` functions (except |api-fast|) in +`vim.loop` callbacks. For example, this is an error: > + + local timer = vim.loop.new_timer() + timer:start(1000, 0, function() + vim.api.nvim_command('echomsg "test"') + end) + +To avoid the error use |vim.schedule_wrap()| to defer the callback: > + + local timer = vim.loop.new_timer() + timer:start(1000, 0, vim.schedule_wrap(function() + vim.api.nvim_command('echomsg "test"') + end)) + +Example: repeating timer + 1. Save this code to a file. + 2. Execute it with ":luafile %". > + + -- Create a timer handle (implementation detail: uv_timer_t). + local timer = vim.loop.new_timer() + local i = 0 + -- Waits 1000ms, then repeats every 750ms until timer:close(). + timer:start(1000, 750, function() + print('timer invoked! i='..tostring(i)) + if i > 4 then + timer:close() -- Always close handles to avoid leaks. + end + i = i + 1 + end) + print('sleeping'); + + +Example: File-change detection *watch-file* + 1. Save this code to a file. + 2. Execute it with ":luafile %". + 3. Use ":Watch %" to watch any file. + 4. Try editing the file from another text editor. + 5. Observe that the file reloads in Nvim (because on_change() calls + |:checktime|). > + + local w = vim.loop.new_fs_event() + local function on_change(err, fname, status) + -- Do work... + vim.api.nvim_command('checktime') + -- Debounce: stop/start. + w:stop() + watch_file(fname) + end + function watch_file(fname) + local fullpath = vim.api.nvim_call_function( + 'fnamemodify', {fname, ':p'}) + w:start(fullpath, {}, vim.schedule_wrap(function(...) + on_change(...) end)) + end + vim.api.nvim_command( + "command! -nargs=1 Watch call luaeval('watch_file(_A)', expand(''))") + + +Example: TCP echo-server *tcp-server* + 1. Save this code to a file. + 2. Execute it with ":luafile %". + 3. Note the port number. + 4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): > + + local function create_server(host, port, on_connect) + local server = vim.loop.new_tcp() + server:bind(host, port) + server:listen(128, function(err) + assert(not err, err) -- Check for errors. + local sock = vim.loop.new_tcp() + server:accept(sock) -- Accept client connection. + on_connect(sock) -- Start reading messages. + end) + return server + end + local server = create_server('0.0.0.0', 0, function(sock) + sock:read_start(function(err, chunk) + assert(not err, err) -- Check for errors. + if chunk then + sock:write(chunk) -- Echo received messages to the channel. + else -- EOF (stream closed). + sock:close() -- Always close handles to avoid leaks. + end + end) + end) + print('TCP echo-server listening on port: '..server:getsockname().port) + +------------------------------------------------------------------------------ +VIM.TREESITTER *lua-treesitter* + +Nvim integrates the tree-sitter library for incremental parsing of buffers. + +Currently Nvim does not provide the tree-sitter parsers, instead these must +be built separately, for instance using the tree-sitter utility. +The parser is loaded into nvim using > + + vim.treesitter.add_language("/path/to/c_parser.so", "c") + + + + parser = vim.treesitter.get_parser(bufnr, lang) + +<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this +doesn't work yet for some filetypes like "cpp") Currently, the parser will be +retained for the lifetime of a buffer but this is subject to change. A plugin +should keep a reference to the parser object as long as it wants incremental +updates. + +Whenever you need to access the current syntax tree, parse the buffer: > + + tstree = parser:parse() + + + print(tostring(vim.api.nvim_get_current_line())) + +vim.call({func}, {...}) *vim.call()* + Invokes |vim-function| or |user-function| {func} with arguments {...}. + See also |vim.fn|. Equivalent to: > + vim.fn[func]({...}) + +vim.in_fast_event() *vim.in_fast_event()* + Returns true if the code is executing as part of a "fast" event + handler, where most of the API is disabled. These are low-level events + (e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls + for input. When this is `false` most API functions are callable (but + may be subject to other restrictions such as |textlock|). + +vim.NIL *vim.NIL* + Special value used to represent NIL in msgpack-rpc and |v:null| in + vimL interaction, and similar cases. Lua `nil` cannot be used as + part of a lua table representing a Dictionary or Array, as it + is equivalent to a missing value: `{"foo", nil}` is the same as + `{"foo"}` + +vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* + Sends {event} to {channel} via |RPC| and returns immediately. + If {channel} is 0, the event is broadcast to all channels. + + This function also works in a fast callback |lua-loop-callbacks|. + +vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()* + Sends a request to {channel} to invoke {method} via + |RPC| and blocks until a response is received. + + Note: NIL values as part of the return value is represented as + |vim.NIL| special value + +vim.stricmp({a}, {b}) *vim.stricmp()* + Compares strings case-insensitively. Returns 0, 1 or -1 if strings + are equal, {a} is greater than {b} or {a} is lesser than {b}, + 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. + + Embedded NUL bytes are treated as terminating the string. Invalid + UTF-8 bytes, and embedded surrogates are counted as one code + point each. An {index} in the middle of a UTF-8 sequence is rounded + upwards to the end of that sequence. + +vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()* + Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not + supplied, it defaults to false (use UTF-32). Returns the byte index. + + Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index} + in the middle of a UTF-16 sequence is rounded upwards to the end of that + sequence. + +vim.schedule({callback}) *vim.schedule()* + Schedules {callback} to be invoked soon by the main event-loop. Useful + to avoid |textlock| or other temporary restrictions. + +vim.fn.{func}({...}) *vim.fn* + Invokes |vim-function| or |user-function| {func} with arguments {...}. + To call autoload functions, use the syntax: > + vim.fn['some#function']({...}) +< + Unlike vim.api.|nvim_call_function| this converts directly between Vim + objects and Lua objects. If the Vim function returns a float, it will + be represented directly as a Lua number. Empty lists and dictionaries + both are represented by an empty table. + + Note: |v:null| values as part of the return value is represented as + |vim.NIL| special value + + Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only + enumerates functions that were called at least once. + +vim.type_idx *vim.type_idx* + Type index for use in |lua-special-tbl|. Specifying one of the + values from |vim.types| allows typing the empty table (it is + unclear whether empty Lua table represents empty list or empty array) + and forcing integral numbers to be |Float|. See |lua-special-tbl| for + more details. + +vim.val_idx *vim.val_idx* + Value index for tables representing |Float|s. A table representing + floating-point value 1.0 looks like this: > + { + [vim.type_idx] = vim.types.float, + [vim.val_idx] = 1.0, + } +< See also |vim.type_idx| and |lua-special-tbl|. + +vim.types *vim.types* + Table with possible values for |vim.type_idx|. Contains two sets + of key-value pairs: first maps possible values for |vim.type_idx| + to human-readable strings, second maps human-readable type names to + values for |vim.type_idx|. Currently contains pairs for `float`, + `array` and `dictionary` types. + + Note: one must expect that values corresponding to `vim.types.float`, + `vim.types.array` and `vim.types.dictionary` fall under only two + following assumptions: + 1. Value may serve both as a key and as a value in a table. Given the + properties of Lua tables this basically means “value is not `nil`”. + 2. For each value in `vim.types` table `vim.types[vim.types[value]]` + is the same as `value`. + No other restrictions are put on types, and it is not guaranteed that + values corresponding to `vim.types.float`, `vim.types.array` and + `vim.types.dictionary` will not change or that `vim.types` table will + only contain values for these three types. + +============================================================================== +Lua module: vim *lua-vim* + +inspect({object}, {options}) *vim.inspect()* + Return a human-readable representation of the given object. + + See also: ~ + https://github.com/kikito/inspect.lua + https://github.com/mpeterv/vinspect + +paste({lines}, {phase}) *vim.paste()* + Paste handler, invoked by |nvim_paste()| when a conforming UI + (such as the |TUI|) pastes text into the editor. + + Example: To remove ANSI color codes when pasting: > + + vim.paste = (function(overridden) + return function(lines, phase) + for i,line in ipairs(lines) do + -- Scrub ANSI color codes from paste input. + lines[i] = line:gsub('\27%[[0-9;mK]+', '') + end + overridden(lines, phase) + end + end)(vim.paste) +< + + Parameters: ~ + {lines} |readfile()|-style list of lines to paste. + |channel-lines| + {phase} -1: "non-streaming" paste: the call contains all + lines. If paste is "streamed", `phase` indicates the stream state: + • 1: starts the paste (exactly once) + • 2: continues the paste (zero or more times) + • 3: ends the paste (exactly once) + + Return: ~ + false if client should cancel the paste. + + See also: ~ + |paste| + +schedule_wrap({cb}) *vim.schedule_wrap()* + Defers callback `cb` until the Nvim API is safe to call. + + See also: ~ + |lua-loop-callbacks| + |vim.schedule()| + |vim.in_fast_event()| + + + + +deepcopy({orig}) *vim.deepcopy()* + Returns a deep copy of the given object. Non-table objects are + copied as in a typical Lua assignment, whereas table objects + are copied recursively. + + Parameters: ~ + {orig} Table to copy + + Return: ~ + New table of copied keys and (nested) values. + +gsplit({s}, {sep}, {plain}) *vim.gsplit()* + Splits a string at each instance of a separator. + + Parameters: ~ + {s} String to split + {sep} Separator string or pattern + {plain} If `true` use `sep` literally (passed to + String.find) + + Return: ~ + Iterator over the split components + + See also: ~ + |vim.split()| + https://www.lua.org/pil/20.2.html + http://lua-users.org/wiki/StringLibraryTutorial + +split({s}, {sep}, {plain}) *vim.split()* + Splits a string at each instance of a separator. + + Examples: > + split(":aa::b:", ":") --> {'','aa','','bb',''} + split("axaby", "ab?") --> {'','x','y'} + split(x*yz*o, "*", true) --> {'x','yz','o'} +< + + Parameters: ~ + {s} String to split + {sep} Separator string or pattern + {plain} If `true` use `sep` literally (passed to + String.find) + + Return: ~ + List-like table of the split components. + + See also: ~ + |vim.gsplit()| + +tbl_keys({t}) *vim.tbl_keys()* + Return a list of all keys used in a table. However, the order + of the return table of keys is not guaranteed. + + Parameters: ~ + {t} Table + + Return: ~ + list of keys + + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + +tbl_values({t}) *vim.tbl_values()* + Return a list of all values used in a table. However, the + order of the return table of values is not guaranteed. + + Parameters: ~ + {t} Table + + Return: ~ + list of values + +tbl_contains({t}, {value}) *vim.tbl_contains()* + Checks if a list-like (vector) table contains `value` . + + Parameters: ~ + {t} Table to check + {value} Value to compare + + Return: ~ + true if `t` contains `value` + +tbl_isempty({t}) *vim.tbl_isempty()* + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua@paramt Table to check + +tbl_extend({behavior}, {...}) *vim.tbl_extend()* + Merges two or more map-like tables. + + Parameters: ~ + {behavior} Decides what to do if a key is found in more + than one map: + • "error": raise an error + • "keep": use value from the leftmost map + • "force": use value from the rightmost map + {...} Two or more map-like tables. + + See also: ~ + |extend()| + +deep_equal({a}, {b}) *vim.deep_equal()* + TODO: Documentation + +tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* + Add the reverse lookup values to an existing table. For + example: `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = + 1 }` + + Parameters: ~ + {o} table The table to add the reverse to. + +list_extend({dst}, {src}) *vim.list_extend()* + Extends a list-like table with the values of another list-like + table. + + Parameters: ~ + {dst} The list which will be modified and appended to. + {src} The list from which values will be inserted. + + See also: ~ + |extend()| + +tbl_flatten({t}) *vim.tbl_flatten()* + Creates a copy of a list-like table such that any nested + tables are "unrolled" and appended to the result. + + Parameters: ~ + {t} List-like table + + Return: ~ + Flattened copy of the given list-like table. + + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + +tbl_islist({t}) *vim.tbl_islist()* + Table + + Return: ~ + true: A non-empty array, false: A non-empty table, nil: An + empty table + +trim({s}) *vim.trim()* + Trim whitespace (Lua pattern "%s") from both sides of a + string. + + Parameters: ~ + {s} String to trim + + Return: ~ + String with whitespace removed from its beginning and end + + See also: ~ + https://www.lua.org/pil/20.2.html + +pesc({s}) *vim.pesc()* + Escapes magic chars in a Lua pattern string. + + Parameters: ~ + {s} String to escape + + Return: ~ + %-escaped pattern string + + See also: ~ + https://github.com/rxi/lume + +validate({opt}) *vim.validate()* + Validates a parameter specification (types and values). + + Usage example: > + + function user.new(name, age, hobbies) + vim.validate{ + name={name, 'string'}, + age={age, 'number'}, + hobbies={hobbies, 'table'}, + } + ... + end +< + + Examples with explicit argument values (can be run directly): > + + vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} + => NOP (success) +< +> + vim.validate{arg1={1, 'table'}} + => error('arg1: expected table, got number') +< +> + vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} + => error('arg1: expected even number, got 3') +< + + Parameters: ~ + {opt} Map of parameter names to validations. Each key is + a parameter name; each value is a tuple in one of + these forms: + 1. (arg_value, type_name, optional) + • arg_value: argument value + • type_name: string type name, one of: ("table", + "t", "string", "s", "number", "n", "boolean", + "b", "function", "f", "nil", "thread", + "userdata") + • optional: (optional) boolean, if true, `nil` + is valid + + 2. (arg_value, fn, msg) + • arg_value: argument value + • fn: any function accepting one argument, + returns true if and only if the argument is + valid + • msg: (optional) error string if validation + fails + +is_callable({f}) *vim.is_callable()* + Returns true if object `f` can be called as a function. + + Parameters: ~ + {f} Any object + + Return: ~ + true if `f` is callable, else false + + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index f5d42dfeb2..5368cf0f4f 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -1,7 +1,8 @@ - NVIM REFERENCE MANUAL by Thiago de Arruda - - + NVIM REFERENCE MANUAL This document was merged into |api.txt| and |develop.txt|. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e64ff1b12c..270232179a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6642,22 +6642,18 @@ A jump table for the options with a short description can be found at |Q_op|. *'wildmenu'* *'wmnu'* *'nowildmenu'* *'nowmnu'* 'wildmenu' 'wmnu' boolean (default on) global - When 'wildmenu' is on, command-line completion operates in an enhanced - mode. On pressing 'wildchar' (usually ) to invoke completion, - the possible matches are shown just above the command line, with the - first match highlighted (overwriting the status line, if there is - one). Keys that show the previous/next match, such as or - CTRL-P/CTRL-N, cause the highlight to move to the appropriate match. - When 'wildmode' is used, "wildmenu" mode is used where "full" is - specified. "longest" and "list" do not start "wildmenu" mode. - You can check the current mode with |wildmenumode()|. - If there are more matches than can fit in the line, a ">" is shown on - the right and/or a "<" is shown on the left. The status line scrolls - as needed. - The "wildmenu" mode is abandoned when a key is hit that is not used - for selecting a completion. - While the "wildmenu" is active the following keys have special - meanings: + Enables "enhanced mode" of command-line completion. When user hits + (or 'wildchar') to invoke completion, the possible matches are + shown in a menu just above the command-line (see 'wildoptions'), with + the first match highlighted (overwriting the statusline). Keys that + show the previous/next match (/CTRL-P/CTRL-N) highlight the + match. + 'wildmode' must specify "full": "longest" and "list" do not start + 'wildmenu' mode. You can check the current mode with |wildmenumode()|. + The menu is canceled when a key is hit that is not used for selecting + a completion. + + While the menu is active these keys have special meanings: - select previous/next match (like CTRL-P/CTRL-N) - in filename/menu name completion: move into a @@ -6667,15 +6663,12 @@ A jump table for the options with a short description can be found at |Q_op|. - in filename/menu name completion: move up into parent directory or parent menu. - This makes the menus accessible from the console |console-menus|. - - If you prefer the and keys to move the cursor instead - of selecting a different match, use this: > + If you want and to move the cursor instead of selecting + a different match, use this: > :cnoremap :cnoremap < - The "WildMenu" highlighting is used for displaying the current match - |hl-WildMenu|. + |hl-WildMenu| highlights the current match. *'wildmode'* *'wim'* 'wildmode' 'wim' string (default: "full") diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 45a94bb961..4267aefbbf 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -296,7 +296,7 @@ coerced to strings. See |id()| for more details, currently it uses |c_CTRL-R| pasting a non-special register into |cmdline| omits the last . -Lua interface (|if_lua.txt|): +Lua interface (|lua.txt|): - `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim that prints `a` and `b` on separate lines, exactly like @@ -307,15 +307,15 @@ Lua interface (|if_lua.txt|): - Lua package.path and package.cpath are automatically updated according to 'runtimepath': |lua-require|. -|input()| and |inputdialog()| support for each other’s features (return on -cancel and completion respectively) via dictionary argument (replaces all -other arguments if used). - -|input()| and |inputdialog()| support user-defined cmdline highlighting. - Commands: |:doautocmd| does not warn about "No matching autocommands". +Functions: + |input()| and |inputdialog()| support for each other’s features (return on + cancel and completion respectively) via dictionary argument (replaces all + other arguments if used). + |input()| and |inputdialog()| support user-defined cmdline highlighting. + Highlight groups: |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other groups @@ -399,10 +399,10 @@ VimL (Vim script) compatibility: Some legacy Vim features are not implemented: -- |if_py|: *python-bindeval* *python-Function* are not supported -- |if_lua|: the `vim` object is missing some legacy methods -- *if_perl* +- |if_lua|: Nvim Lua API is not compatible with Vim's "if_lua" - *if_mzscheme* +- *if_perl* +- |if_py|: *python-bindeval* *python-Function* are not supported - *if_tcl* ============================================================================== @@ -524,4 +524,4 @@ TUI: always uses 7-bit control sequences. ============================================================================== - vim:tw=78:ts=8:sw=2:noet:ft=help:norl: + vim:tw=78:ts=8:sw=2:et:ft=help:norl: -- cgit From 1ff5b60cb96aea3b3f6ef9e2792c5797dfda72dc Mon Sep 17 00:00:00 2001 From: Joe Hermaszewski Date: Mon, 18 Nov 2019 15:38:27 +0800 Subject: vim-patch:8.1.0251: support full paths for 'backupdir' #11269 Problem: Using a full path is supported for 'directory' but not for 'backupdir'. (Mikolaj Machowski) Solution: Support 'backupdir' as well. (Christian Brabandt, closes vim/vim#179) https://github.com/vim/vim/commit/b782ba475a3f8f2b0be99dda164ba4545347f60f --- runtime/doc/options.txt | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 270232179a..386fcdf8c0 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -843,6 +843,14 @@ A jump table for the options with a short description can be found at |Q_op|. name, precede it with a backslash. - To include a comma in a directory name precede it with a backslash. - A directory name may end in an '/'. + - For Unix and Win32, if a directory ends in two path separators "//", + the swap file name will be built from the complete path to the file + with all path separators changed to percent '%' signs. This will + ensure file name uniqueness in the backup directory. + On Win32, it is also possible to end with "\\". However, When a + separating comma is following, you must use "//", since "\\" will + include the comma in the file name. Therefore it is recommended to + use '//', instead of '\\'. - Environment variables are expanded |:set_env|. - Careful with '\' characters, type one before a space, type two to get one in the option (see |option-backslash|), for example: > @@ -1992,12 +2000,14 @@ A jump table for the options with a short description can be found at |Q_op|. - A directory starting with "./" (or ".\" for Windows) means to put the swap file relative to where the edited file is. The leading "." is replaced with the path name of the edited file. - - For Unix and Win32, if a directory ends in two path separators "//" - or "\\", the swap file name will be built from the complete path to - the file with all path separators substituted to percent '%' signs. - This will ensure file name uniqueness in the preserve directory. - On Win32, when a separating comma is following, you must use "//", - since "\\" will include the comma in the file name. + - For Unix and Win32, if a directory ends in two path separators "//", + the swap file name will be built from the complete path to the file + with all path separators substituted to percent '%' signs. This will + ensure file name uniqueness in the preserve directory. + On Win32, it is also possible to end with "\\". However, When a + separating comma is following, you must use "//", since "\\" will + include the comma in the file name. Therefore it is recommended to + use '//', instead of '\\'. - Spaces after the comma are ignored, other spaces are considered part of the directory name. To have a space at the start of a directory name, precede it with a backslash. -- cgit From 69a0712a9c2bae0969f7a84e5ec321ce6120982b Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 11:23:50 -0800 Subject: Reduce code blocks in markdown previews. If the preview is just a code block, then use the language as the filetype instead of using markdown. This reduces the size of the preview. --- runtime/lua/vim/lsp/builtin_callbacks.lua | 54 ++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/builtin_callbacks.lua b/runtime/lua/vim/lsp/builtin_callbacks.lua index cc739ce3ad..717fda395e 100644 --- a/runtime/lua/vim/lsp/builtin_callbacks.lua +++ b/runtime/lua/vim/lsp/builtin_callbacks.lua @@ -81,6 +81,56 @@ builtin_callbacks['textDocument/publishDiagnostics'] = function(_, _, result) -- util.buf_loclist(bufnr, result.diagnostics) end +local function focusable_popup() + local popup_win + return function(winnr) + if popup_win and nvim.win_is_valid(popup_win) then + if nvim.get_current_win() == popup_win then + nvim.ex.wincmd "p" + else + nvim.set_current_win(popup_win) + end + return + end + popup_win = winnr + end +end + +local hover_popup = focusable_popup() + +local function trim_empty_lines(lines) + local result = {} + for _, line in ipairs(lines) do + if #line > 0 then + table.insert(result, line) + end + end + return result +end + +-- Accepts markdown lines and tries to reduce it to a filetype if it is +-- just a single code block. +local function try_trim_code_blocks(lines) + local language_id = lines[1]:match("^```(.*)") + if language_id then + local has_inner_code_fence = false + for i = 2, (#lines - 1) do + local line = lines[i] + if line:sub(1,3) == '```' then + has_inner_code_fence = true + break + end + end + -- No inner code fences + starting with code fence = hooray. + if not has_inner_code_fence then + table.remove(lines, 1) + table.remove(lines) + return lines, language_id + end + end + return lines, 'markdown' +end + -- textDocument/hover -- https://microsoft.github.io/language-server-protocol/specification#textDocument_hover -- @params MarkedString | MarkedString[] | MarkupContent @@ -91,10 +141,12 @@ builtin_callbacks['textDocument/hover'] = function(_, _, result) if result.contents ~= nil then local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = trim_empty_lines(markdown_lines) if vim.tbl_isempty(markdown_lines) then markdown_lines = { 'No information available' } end - util.open_floating_preview(markdown_lines, 'markdown') + local _, winnr = util.open_floating_preview(try_trim_code_blocks(markdown_lines)) + hover_popup(winnr) end end -- cgit From 3ae9b3781e0b66f5874971665b38314d61668627 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 11:34:10 -0800 Subject: Bugfix for floating_preview Don't modify your inputs. --- runtime/lua/vim/lsp/util.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index f96e0f01a8..707e2d0d3b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -262,6 +262,9 @@ function M.open_floating_preview(contents, filetype, opts) opts = { opts, 't', true }; } + -- Don't modify our inputs. + contents = vim.deepcopy(contents) + -- Trim empty lines from the end. for i = #contents, 1, -1 do if #contents[i] == 0 then -- cgit From 0904ffe387f825404d8467f9787678c2251728dd Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 11:36:50 -0800 Subject: Bugfixes. - Return after an error in RPC. - Use an empty vim table for serialization. --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/rpc.lua | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9dbe03dace..4724ff0281 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -400,7 +400,7 @@ function lsp.start_client(config) rpc.request('initialize', initialize_params, function(init_err, result) assert(not init_err, tostring(init_err)) assert(result, "server sent empty result") - rpc.notify('initialized', {}) + rpc.notify('initialized', {[vim.type_idx]=vim.types.dictionary}) client.initialized = true uninitialized_clients[client_id] = nil client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities") diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index e0ec8863d6..a558f66a42 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -340,6 +340,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para local decoded, err = json_decode(body) if not decoded then on_error(client_errors.INVALID_SERVER_JSON, err) + return end local _ = log.debug() and log.debug("decoded", decoded) -- cgit From 568b4540884bb852b733dfcdd6a2da0d4fe2b42e Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 11:39:54 -0800 Subject: Add vim.uri_to_bufnr --- runtime/lua/vim/lsp/builtin_callbacks.lua | 8 ++------ runtime/lua/vim/uri.lua | 7 ++++++- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/builtin_callbacks.lua b/runtime/lua/vim/lsp/builtin_callbacks.lua index 717fda395e..a5e1162d02 100644 --- a/runtime/lua/vim/lsp/builtin_callbacks.lua +++ b/runtime/lua/vim/lsp/builtin_callbacks.lua @@ -62,14 +62,10 @@ builtin_callbacks['textDocument/rename'] = function(_, _, result) util.workspace_apply_workspace_edit(result) end -local function uri_to_bufnr(uri) - return vim.fn.bufadd((vim.uri_to_fname(uri))) -end - builtin_callbacks['textDocument/publishDiagnostics'] = function(_, _, result) if not result then return end local uri = result.uri - local bufnr = uri_to_bufnr(uri) + local bufnr = vim.uri_to_bufnr(uri) if not bufnr then api.nvim_err_writeln(string.format("LSP.publishDiagnostics: Couldn't find buffer for %s", uri)) return @@ -154,7 +150,7 @@ builtin_callbacks['textDocument/peekDefinition'] = function(_, _, result) if result == nil or vim.tbl_isempty(result) then return end -- TODO(ashkan) what to do with multiple locations? result = result[1] - local bufnr = uri_to_bufnr(result.uri) + local bufnr = vim.uri_to_bufnr(result.uri) assert(bufnr) local start = result.range.start local finish = result.range["end"] diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index 0a6e0fcb97..1065f84f4c 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -77,13 +77,18 @@ local function uri_to_fname(uri) else uri = uri:gsub('^file://', '') end - return uri_decode(uri) end +-- Return or create a buffer for a uri. +local function uri_to_bufnr(uri) + return vim.fn.bufadd((uri_to_fname(uri))) +end + return { uri_from_fname = uri_from_fname, uri_from_bufnr = uri_from_bufnr, uri_to_fname = uri_to_fname, + uri_to_bufnr = uri_to_bufnr, } -- vim:sw=2 ts=2 et -- cgit From ff65cc990afba288df6d39d6f95901cf0786c668 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 11:41:45 -0800 Subject: Bugfix. Don't use nvim.lua that doesn't exist :) --- runtime/lua/vim/lsp/builtin_callbacks.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/builtin_callbacks.lua b/runtime/lua/vim/lsp/builtin_callbacks.lua index a5e1162d02..2229547f20 100644 --- a/runtime/lua/vim/lsp/builtin_callbacks.lua +++ b/runtime/lua/vim/lsp/builtin_callbacks.lua @@ -80,11 +80,11 @@ end local function focusable_popup() local popup_win return function(winnr) - if popup_win and nvim.win_is_valid(popup_win) then - if nvim.get_current_win() == popup_win then - nvim.ex.wincmd "p" + if popup_win and api.nvim_win_is_valid(popup_win) then + if api.nvim_get_current_win() == popup_win then + api.nvim_command("wincmd p") else - nvim.set_current_win(popup_win) + api.nvim_set_current_win(popup_win) end return end -- cgit From c83380cf80561d45c067db5e70c7275eda7671af Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 14:21:57 -0800 Subject: Add lsp.buf and hover implementation. --- runtime/lua/vim/lsp/buf.lua | 85 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 runtime/lua/vim/lsp/buf.lua (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua new file mode 100644 index 0000000000..6398bcca74 --- /dev/null +++ b/runtime/lua/vim/lsp/buf.lua @@ -0,0 +1,85 @@ +local validate = vim.validate +local api = vim.api +local util = require 'vim.lsp.util' +local protocol = require 'vim.lsp.protocol' + +local M = {} + +local function resolve_bufnr(bufnr) + validate { bufnr = { bufnr, 'n', true } } + if bufnr == nil or bufnr == 0 then + return api.nvim_get_current_buf() + end + return bufnr +end + +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end + +local function find_window_by_var(name, value) + for _, win in ipairs(api.nvim_list_wins()) do + if npcall(api.nvim_win_get_var, win, name) == value then + return win + end + end +end + +local hover_window_tag = 'lsp_hover' +function M.hover(bufnr) + if npcall(api.nvim_win_get_var, 0, hover_window_tag) then + api.nvim_command("wincmd p") + return + end + + bufnr = resolve_bufnr(bufnr) + do + local win = find_window_by_var(hover_window_tag, bufnr) + if win then + api.nvim_set_current_win(win) + return + end + end + local params = protocol.make_text_document_position_params() + vim.lsp.buf_request(bufnr, 'textDocument/hover', params, function(_, _, result, _) + if result == nil or vim.tbl_isempty(result) then + return + end + + if result.contents ~= nil then + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + markdown_lines = { 'No information available' } + end + local filetype = util.try_trim_markdown_code_blocks(markdown_lines) + local _, winnr = util.open_floating_preview(markdown_lines, filetype) + api.nvim_win_set_var(winnr, hover_window_tag, bufnr) + end + end) +end + +function M.signature_help() +end + +function M.declaration() +end + +function M.type_definition() +end + +function M.implementation() +end + +-- TODO(ashkan) ? +function M.completion() +end + +function M.range_formatting() +end + +return M -- cgit From 2d580756ca707945c01703d404e10f4bf412a72c Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 15:35:18 -0800 Subject: Add everything to lsp.buf and get rid of autoload. --- runtime/autoload/lsp.vim | 45 ------------ runtime/lua/vim/lsp/buf.lua | 161 ++++++++++++++++++++++++++++++++++++------- runtime/lua/vim/lsp/util.lua | 88 +++++++++++++++++------ 3 files changed, 204 insertions(+), 90 deletions(-) delete mode 100644 runtime/autoload/lsp.vim (limited to 'runtime') diff --git a/runtime/autoload/lsp.vim b/runtime/autoload/lsp.vim deleted file mode 100644 index 4c8f8b396a..0000000000 --- a/runtime/autoload/lsp.vim +++ /dev/null @@ -1,45 +0,0 @@ -function! lsp#add_filetype_config(config) abort - call luaeval('vim.lsp.add_filetype_config(_A)', a:config) -endfunction - -function! lsp#set_log_level(level) abort - call luaeval('vim.lsp.set_log_level(_A)', a:level) -endfunction - -function! lsp#get_log_path() abort - return luaeval('vim.lsp.get_log_path()') -endfunction - -function! lsp#omnifunc(findstart, base) abort - return luaeval("vim.lsp.omnifunc(_A[1], _A[2])", [a:findstart, a:base]) -endfunction - -function! lsp#text_document_hover() abort - lua vim.lsp.buf_request(nil, 'textDocument/hover', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_declaration() abort - lua vim.lsp.buf_request(nil, 'textDocument/declaration', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_definition() abort - lua vim.lsp.buf_request(nil, 'textDocument/definition', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_signature_help() abort - lua vim.lsp.buf_request(nil, 'textDocument/signatureHelp', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_type_definition() abort - lua vim.lsp.buf_request(nil, 'textDocument/typeDefinition', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction - -function! lsp#text_document_implementation() abort - lua vim.lsp.buf_request(nil, 'textDocument/implementation', vim.lsp.protocol.make_text_document_position_params()) - return '' -endfunction diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6398bcca74..5f54196dcc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,7 +1,9 @@ local validate = vim.validate local api = vim.api +local vfn = vim.fn local util = require 'vim.lsp.util' local protocol = require 'vim.lsp.protocol' +local log = require 'vim.lsp.log' local M = {} @@ -29,54 +31,165 @@ local function find_window_by_var(name, value) end end -local hover_window_tag = 'lsp_hover' -function M.hover(bufnr) - if npcall(api.nvim_win_get_var, 0, hover_window_tag) then - api.nvim_command("wincmd p") - return +local function request(method, params, callback) + -- TODO(ashkan) enable this. + -- callback = vim.lsp.default_callback[method] or callback + validate { + method = {method, 's'}; + callback = {callback, 'f'}; + } + return vim.lsp.buf_request(0, method, params, function(err, _, result, client_id) + if err then error(tostring(err)) end + return callback(err, method, result, client_id) + end) +end + +local function focusable_preview(method, params, fn) + if npcall(api.nvim_win_get_var, 0, method) then + return api.nvim_command("wincmd p") end - bufnr = resolve_bufnr(bufnr) + local bufnr = api.nvim_get_current_buf() do - local win = find_window_by_var(hover_window_tag, bufnr) + local win = find_window_by_var(method, bufnr) if win then - api.nvim_set_current_win(win) - return + return api.nvim_set_current_win(win) end end - local params = protocol.make_text_document_position_params() - vim.lsp.buf_request(bufnr, 'textDocument/hover', params, function(_, _, result, _) - if result == nil or vim.tbl_isempty(result) then - return + return request(method, params, function(_, _, result, _) + -- TODO(ashkan) could show error in preview... + local lines, filetype, opts = fn(result) + if lines then + local _, winnr = util.open_floating_preview(lines, filetype, opts) + api.nvim_win_set_var(winnr, method, bufnr) end + end) +end + +function M.hover() + local params = protocol.make_text_document_position_params() + focusable_preview('textDocument/hover', params, function(result) + if not (result and result.contents) then return end - if result.contents ~= nil then - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - markdown_lines = util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then - markdown_lines = { 'No information available' } - end - local filetype = util.try_trim_markdown_code_blocks(markdown_lines) - local _, winnr = util.open_floating_preview(markdown_lines, filetype) - api.nvim_win_set_var(winnr, hover_window_tag, bufnr) + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + return { 'No information available' } end + return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) end) end -function M.signature_help() +function M.peek_definition() + local params = protocol.make_text_document_position_params() + request('textDocument/peekDefinition', params, function(_, _, result, _) + if not (result and result[1]) then return end + local loc = result[1] + local bufnr = vim.uri_to_bufnr(loc.uri) or error("couldn't find file "..tostring(loc.uri)) + local start = loc.range.start + local finish = loc.range["end"] + util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) + local headbuf = util.open_floating_preview({"Peek:"}, nil, { + offset_y = -(finish.line - start.line); + width = finish.character - start.character + 2; + }) + -- TODO(ashkan) change highlight group? + api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) + end) +end + + +local function update_tagstack() + local bufnr = api.nvim_get_current_buf() + local line = vfn.line('.') + local col = vfn.col('.') + local tagname = vfn.expand('') + local item = { bufnr = bufnr, from = { bufnr, line, col, 0 }, tagname = tagname } + local winid = vfn.win_getid() + local tagstack = vfn.gettagstack(winid) + local action + if tagstack.length == tagstack.curidx then + action = 'r' + tagstack.items[tagstack.curidx] = item + elseif tagstack.length > tagstack.curidx then + action = 'r' + if tagstack.curidx > 1 then + tagstack.items = table.insert(tagstack.items[tagstack.curidx - 1], item) + else + tagstack.items = { item } + end + else + action = 'a' + tagstack.items = { item } + end + tagstack.curidx = tagstack.curidx + 1 + vfn.settagstack(winid, tagstack, action) +end +local function handle_location(result) + -- We can sometimes get a list of locations, so set the first value as the + -- only value we want to handle + -- TODO(ashkan) was this correct^? We could use location lists. + if result[1] ~= nil then + result = result[1] + end + if result.uri == nil then + api.nvim_err_writeln('[LSP] Could not find a valid location') + return + end + local result_file = vim.uri_to_fname(result.uri) + local bufnr = vfn.bufadd(result_file) + update_tagstack() + api.nvim_set_current_buf(bufnr) + local start = result.range.start + api.nvim_win_set_cursor(0, {start.line + 1, start.character}) + return true +end +local function location_callback(_, method, result) + if result == nil or vim.tbl_isempty(result) then + local _ = log.info() and log.info(method, 'No location found') + return nil + end + return handle_location(result) end function M.declaration() + local params = protocol.make_text_document_position_params() + request('textDocument/declaration', params, location_callback) +end + +function M.definition() + local params = protocol.make_text_document_position_params() + request('textDocument/definition', params, location_callback) end function M.type_definition() + local params = protocol.make_text_document_position_params() + request('textDocument/typeDefinition', params, location_callback) end function M.implementation() + local params = protocol.make_text_document_position_params() + request('textDocument/implementation', params, location_callback) +end + +function M.signature_help() + local params = protocol.make_text_document_position_params() + request('textDocument/signatureHelp', params, location_callback) end -- TODO(ashkan) ? -function M.completion() +function M.completion(context) + local params = protocol.make_text_document_position_params() + params.context = context + return request('textDocument/completion', params, function(_, _, result) + if vim.tbl_isempty(result or {}) then return end + local row, col = unpack(api.nvim_win_get_cursor(0)) + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) + + local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) + vim.fn.complete(col, matches) + end) end function M.range_formatting() diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 707e2d0d3b..13c83fefd6 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -261,32 +261,27 @@ function M.open_floating_preview(contents, filetype, opts) filetype = { filetype, 's', true }; opts = { opts, 't', true }; } - - -- Don't modify our inputs. - contents = vim.deepcopy(contents) + opts = opts or {} -- Trim empty lines from the end. - for i = #contents, 1, -1 do - if #contents[i] == 0 then - table.remove(contents) - else - break + contents = M.trim_empty_lines(contents) + + local width = opts.width + local height = opts.height or #contents + if not width then + width = 0 + for i, line in ipairs(contents) do + -- Clean up the input and add left pad. + line = " "..line:gsub("\r", "") + -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. + local line_width = vim.fn.strdisplaywidth(line) + width = math.max(line_width, width) + contents[i] = line end + -- Add right padding of 1 each. + width = width + 1 end - local width = 0 - local height = #contents - for i, line in ipairs(contents) do - -- Clean up the input and add left pad. - line = " "..line:gsub("\r", "") - -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. - local line_width = vim.fn.strdisplaywidth(line) - width = math.max(line_width, width) - contents[i] = line - end - -- Add right padding of 1 each. - width = width + 1 - local floating_bufnr = api.nvim_create_buf(false, true) if filetype then api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) @@ -556,5 +551,56 @@ function M.buf_loclist(bufnr, locations) vim.fn.setloclist(targetwin, items, ' ', 'Language Server') end +-- Remove empty lines from the beginning and end. +function M.trim_empty_lines(lines) + local result = {} + local start = 1 + for i = 1, #lines do + if #lines[i] > 0 then + start = i + break + end + end + local finish = 1 + for i = #lines, 1, -1 do + if #lines[i] > 0 then + finish = i + break + end + end + -- TODO(ashkan) use tbl_slice. + for i = start, finish do + table.insert(result, lines[i]) + end + return result +end + +-- Accepts markdown lines and tries to reduce it to a filetype if it is +-- just a single code block. +-- Note: This modifies the input. +-- +-- Returns: filetype or 'markdown' if it was unchanged. +function M.try_trim_markdown_code_blocks(lines) + local language_id = lines[1]:match("^```(.*)") + if language_id then + local has_inner_code_fence = false + for i = 2, (#lines - 1) do + local line = lines[i] + if line:sub(1,3) == '```' then + has_inner_code_fence = true + break + end + end + -- No inner code fences + starting with code fence = hooray. + if not has_inner_code_fence then + table.remove(lines, 1) + table.remove(lines) + return language_id + end + end + return 'markdown' +end + + return M -- vim:sw=2 ts=2 et -- cgit From a4b7004f489030d9ee7e3bbfc156ab540744279b Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:03:32 -0800 Subject: Move everything to buf & default_callbacks - Rename builtin_callbacks to default_callbacks and slightly change its semantics: - No longer contains the default implementations. Instead, any default_callbacks will be used in preference for our .buf methods. - Add this to the docs. --- runtime/doc/lsp.txt | 37 ++-- runtime/lua/vim/lsp.lua | 137 +----------- runtime/lua/vim/lsp/buf.lua | 86 +++++++- runtime/lua/vim/lsp/builtin_callbacks.lua | 344 ------------------------------ runtime/lua/vim/lsp/default_callbacks.lua | 57 +++++ runtime/lua/vim/lsp/protocol.lua | 1 - 6 files changed, 167 insertions(+), 495 deletions(-) delete mode 100644 runtime/lua/vim/lsp/builtin_callbacks.lua create mode 100644 runtime/lua/vim/lsp/default_callbacks.lua (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 26850b3683..8bafe0245f 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -346,31 +346,34 @@ vim.lsp.rpc_response_error({code}, [{message}], [{data}]) the server. ================================================================================ - *vim.lsp.builtin_callbacks* + *vim.lsp.default_callbacks* -The |vim.lsp.builtin_callbacks| table contains the default |lsp-callbacks| +The |vim.lsp.default_callbacks| table contains the default |lsp-callbacks| that are used when creating a new client. The keys are the LSP method names. The following requests and notifications have built-in callbacks defined to handle the response in an idiomatic way. - textDocument/completion - textDocument/declaration - textDocument/definition - textDocument/hover - textDocument/implementation - textDocument/rename - textDocument/signatureHelp - textDocument/typeDefinition + textDocument/publishDiagnostics window/logMessage window/showMessage -You can check these via `vim.tbl_keys(vim.lsp.builtin_callbacks)`. +You can check these via `vim.tbl_keys(vim.lsp.default_callbacks)`. -These will be automatically used and can be overridden by users (either by -modifying the |vim.lsp.builtin_callbacks| object or on a per-client basis -by passing in a table via the {callbacks} parameter on |vim.lsp.start_client| -or |vim.lsp.add_filetype_config|. +These will be used preferrentially in `vim.lsp.buf` methods when handling +requests. They will also be used when responding to server requests and +notifications. + +Use cases: +- Users can modify this to customize to their preferences. +- UI plugins can modify this by assigning to + `vim.lsp.default_callbacks[method]` so as to provide more specialized + handling, allowing you to leverage the UI capabilities available. UIs should + try to be conscientious of any existing changes the user may have set + already by checking for existing values. + +Any callbacks passed directly to `request` methods on a server client will +have the highest precedence, followed by the `default_callbacks`. More information about callbacks can be found in |lsp-callbacks|. @@ -379,8 +382,8 @@ More information about callbacks can be found in |lsp-callbacks|. Callbacks are functions which are called in a variety of situations by the client. Their signature is `function(err, method, params, client_id)` They can -be set by the {callbacks} parameter for |vim.lsp.start_client| and -|vim.lsp.add_filetype_config| or via the |vim.lsp.builtin_callbacks|. +be set by the {callbacks} parameter for |vim.lsp.start_client| or via the +|vim.lsp.default_callbacks|. This will be called for: - notifications from the server, where `err` will always be `nil` diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4724ff0281..44909a85ee 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1,4 +1,4 @@ -local builtin_callbacks = require 'vim.lsp.builtin_callbacks' +local default_callbacks = require 'vim.lsp.default_callbacks' local log = require 'vim.lsp.log' local lsp_rpc = require 'vim.lsp.rpc' local protocol = require 'vim.lsp.protocol' @@ -12,7 +12,8 @@ local validate = vim.validate local lsp = { protocol = protocol; - builtin_callbacks = builtin_callbacks; + default_callbacks = default_callbacks; + buf = require'vim.lsp.buf'; util = util; -- Allow raw RPC access. rpc = lsp_rpc; @@ -283,7 +284,7 @@ function lsp.start_client(config) local client_id = next_client_id() - local callbacks = tbl_extend("keep", config.callbacks or {}, builtin_callbacks) + local callbacks = tbl_extend("keep", config.callbacks or {}, default_callbacks) -- Copy metatable if it has one. if config.callbacks and config.callbacks.__metatable then setmetatable(callbacks, getmetatable(config.callbacks)) @@ -869,134 +870,8 @@ function lsp.omnifunc(findstart, base) end end ---- ---- FileType based configuration utility ---- - -local all_filetype_configs = {} - --- Lookup a filetype config client by its name. -function lsp.get_filetype_client_by_name(name) - local config = all_filetype_configs[name] - if config.client_id then - return active_clients[config.client_id] - end -end - -local function start_filetype_config(config) - config.client_id = lsp.start_client(config) - nvim_command(string.format( - "autocmd FileType %s silent lua vim.lsp.buf_attach_client(0, %d)", - table.concat(config.filetypes, ','), - config.client_id)) - return config.client_id -end - --- Easy configuration option for common LSP use-cases. --- This will lazy initialize the client when the filetypes specified are --- encountered and attach to those buffers. --- --- The configuration options are the same as |vim.lsp.start_client()|, but --- with a few additions and distinctions: --- --- Additional parameters: --- - filetype: {string} or {list} of filetypes to attach to. --- - name: A unique string among all other servers configured with --- |vim.lsp.add_filetype_config|. --- --- Differences: --- - root_dir: will default to |getcwd()| --- -function lsp.add_filetype_config(config) - -- Additional defaults. - -- Keep a copy of the user's input for debugging reasons. - local user_config = config - config = tbl_extend("force", {}, user_config) - config.root_dir = config.root_dir or uv.cwd() - -- Validate config. - validate_client_config(config) - validate { - name = { config.name, 's' }; - } - assert(config.filetype, "config must have 'filetype' key") - - local filetypes - if type(config.filetype) == 'string' then - filetypes = { config.filetype } - elseif type(config.filetype) == 'table' then - filetypes = config.filetype - assert(not tbl_isempty(filetypes), "config.filetype must not be an empty table") - else - error("config.filetype must be a string or a list of strings") - end - - if all_filetype_configs[config.name] then - -- If the client exists, then it is likely that they are doing some kind of - -- reload flow, so let's not throw an error here. - if all_filetype_configs[config.name].client_id then - -- TODO log here? It might be unnecessarily annoying. - return - end - error(string.format('A configuration with the name %q already exists. They must be unique', config.name)) - end - - all_filetype_configs[config.name] = tbl_extend("keep", config, { - client_id = nil; - filetypes = filetypes; - user_config = user_config; - }) - - nvim_command(string.format( - "autocmd FileType %s ++once silent lua vim.lsp._start_filetype_config_client(%q)", - table.concat(filetypes, ','), - config.name)) -end - --- Create a copy of an existing configuration, and override config with values --- from new_config. --- This is useful if you wish you create multiple LSPs with different root_dirs --- or other use cases. --- --- You can specify a new unique name, but if you do not, a unique name will be --- created like `name-dup_count`. --- --- existing_name: the name of the existing config to copy. --- new_config: the new configuration options. @see |vim.lsp.start_client()|. --- @returns string the new name. -function lsp.copy_filetype_config(existing_name, new_config) - local config = all_filetype_configs[existing_name] - or error(string.format("Configuration with name %q doesn't exist", existing_name)) - config = tbl_extend("force", config, new_config or {}) - config.client_id = nil - config.original_config_name = existing_name - - -- If the user didn't rename it, we will. - if config.name == existing_name then - -- Create a new, unique name. - local duplicate_count = 0 - for _, conf in pairs(all_filetype_configs) do - if conf.original_config_name == existing_name then - duplicate_count = duplicate_count + 1 - end - end - config.name = string.format("%s-%d", existing_name, duplicate_count + 1) - end - print("New config name:", config.name) - lsp.add_filetype_config(config) - return config.name -end - --- Autocmd handler to actually start the client when an applicable filetype is --- encountered. -function lsp._start_filetype_config_client(name) - local config = all_filetype_configs[name] - -- If it exists and is running, don't make it again. - if config.client_id and active_clients[config.client_id] then - -- TODO log here? - return - end - lsp.buf_attach_client(0, start_filetype_config(config)) - return config.client_id +function lsp.client_is_stopped(client_id) + return active_clients[client_id] == nil end --- diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 5f54196dcc..e8a38aa6ef 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -33,7 +33,7 @@ end local function request(method, params, callback) -- TODO(ashkan) enable this. - -- callback = vim.lsp.default_callback[method] or callback + -- callback = vim.lsp.default_callbacks[method] or callback validate { method = {method, 's'}; callback = {callback, 'f'}; @@ -172,9 +172,78 @@ function M.implementation() request('textDocument/implementation', params, location_callback) end +--- Convert SignatureHelp response to preview contents. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +local function signature_help_to_preview_contents(input) + if not input.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = input.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #input.signatures then + active_signature = 0 + end + local signature = input.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, split_lines(signature.label)) + if signature.documentation then + util.convert_input_to_markdown_lines(signature.documentation, contents) + end + if input.parameters then + local active_parameter = input.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #input.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + util.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + function M.signature_help() local params = protocol.make_text_document_position_params() - request('textDocument/signatureHelp', params, location_callback) + focusable_preview('textDocument/signatureHelp', params, function(result) + if not (result and result.signatures and result.signatures[1]) then return end + + -- TODO show empty popup when signatures is empty? + local lines = signature_help_to_preview_contents(result) + lines = util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then + return { 'No signature available' } + end + return lines, util.try_trim_markdown_code_blocks(lines) + end) end -- TODO(ashkan) ? @@ -195,4 +264,17 @@ end function M.range_formatting() end +function M.rename(new_name) + -- TODO(ashkan) use prepareRename + -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. + local params = protocol.make_text_document_position_params() + new_name = new_name or npcall(vfn.input, "New Name: ") + if not (new_name and #new_name > 0) then return end + params.newName = new_name + request('textDocument/rename', params, function(_, _, result) + if not result then return end + util.workspace_apply_workspace_edit(result) + end) +end + return M diff --git a/runtime/lua/vim/lsp/builtin_callbacks.lua b/runtime/lua/vim/lsp/builtin_callbacks.lua deleted file mode 100644 index 2229547f20..0000000000 --- a/runtime/lua/vim/lsp/builtin_callbacks.lua +++ /dev/null @@ -1,344 +0,0 @@ ---- Implements the following default callbacks: --- --- vim.api.nvim_buf_set_lines(0, 0, 0, false, vim.tbl_keys(vim.lsp.builtin_callbacks)) --- - --- textDocument/completion --- textDocument/declaration --- textDocument/definition --- textDocument/hover --- textDocument/implementation --- textDocument/publishDiagnostics --- textDocument/rename --- textDocument/signatureHelp --- textDocument/typeDefinition --- TODO codeLens/resolve --- TODO completionItem/resolve --- TODO documentLink/resolve --- TODO textDocument/codeAction --- TODO textDocument/codeLens --- TODO textDocument/documentHighlight --- TODO textDocument/documentLink --- TODO textDocument/documentSymbol --- TODO textDocument/formatting --- TODO textDocument/onTypeFormatting --- TODO textDocument/rangeFormatting --- TODO textDocument/references --- window/logMessage --- window/showMessage - -local log = require 'vim.lsp.log' -local protocol = require 'vim.lsp.protocol' -local util = require 'vim.lsp.util' -local api = vim.api - -local function split_lines(value) - return vim.split(value, '\n', true) -end - -local builtin_callbacks = {} - --- textDocument/completion --- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion -builtin_callbacks['textDocument/completion'] = function(_, _, result) - if not result or vim.tbl_isempty(result) then - return - end - local pos = api.nvim_win_get_cursor(0) - local row, col = pos[1], pos[2] - local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) - local line_to_cursor = line:sub(col+1) - - local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) - local match_result = vim.fn.matchstrpos(line_to_cursor, '\\k\\+$') - local match_start, match_finish = match_result[2], match_result[3] - - vim.fn.complete(col + 1 - (match_finish - match_start), matches) -end - --- textDocument/rename -builtin_callbacks['textDocument/rename'] = function(_, _, result) - if not result then return end - util.workspace_apply_workspace_edit(result) -end - -builtin_callbacks['textDocument/publishDiagnostics'] = function(_, _, result) - if not result then return end - local uri = result.uri - local bufnr = vim.uri_to_bufnr(uri) - if not bufnr then - api.nvim_err_writeln(string.format("LSP.publishDiagnostics: Couldn't find buffer for %s", uri)) - return - end - util.buf_clear_diagnostics(bufnr) - util.buf_diagnostics_save_positions(bufnr, result.diagnostics) - util.buf_diagnostics_underline(bufnr, result.diagnostics) - util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) - -- util.buf_loclist(bufnr, result.diagnostics) -end - -local function focusable_popup() - local popup_win - return function(winnr) - if popup_win and api.nvim_win_is_valid(popup_win) then - if api.nvim_get_current_win() == popup_win then - api.nvim_command("wincmd p") - else - api.nvim_set_current_win(popup_win) - end - return - end - popup_win = winnr - end -end - -local hover_popup = focusable_popup() - -local function trim_empty_lines(lines) - local result = {} - for _, line in ipairs(lines) do - if #line > 0 then - table.insert(result, line) - end - end - return result -end - --- Accepts markdown lines and tries to reduce it to a filetype if it is --- just a single code block. -local function try_trim_code_blocks(lines) - local language_id = lines[1]:match("^```(.*)") - if language_id then - local has_inner_code_fence = false - for i = 2, (#lines - 1) do - local line = lines[i] - if line:sub(1,3) == '```' then - has_inner_code_fence = true - break - end - end - -- No inner code fences + starting with code fence = hooray. - if not has_inner_code_fence then - table.remove(lines, 1) - table.remove(lines) - return lines, language_id - end - end - return lines, 'markdown' -end - --- textDocument/hover --- https://microsoft.github.io/language-server-protocol/specification#textDocument_hover --- @params MarkedString | MarkedString[] | MarkupContent -builtin_callbacks['textDocument/hover'] = function(_, _, result) - if result == nil or vim.tbl_isempty(result) then - return - end - - if result.contents ~= nil then - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - markdown_lines = trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then - markdown_lines = { 'No information available' } - end - local _, winnr = util.open_floating_preview(try_trim_code_blocks(markdown_lines)) - hover_popup(winnr) - end -end - -builtin_callbacks['textDocument/peekDefinition'] = function(_, _, result) - if result == nil or vim.tbl_isempty(result) then return end - -- TODO(ashkan) what to do with multiple locations? - result = result[1] - local bufnr = vim.uri_to_bufnr(result.uri) - assert(bufnr) - local start = result.range.start - local finish = result.range["end"] - util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) - util.open_floating_preview({"*Peek:*", string.rep(" ", finish.character - start.character + 1) }, 'markdown', { offset_y = -(finish.line - start.line) }) -end - ---- Convert SignatureHelp response to preview contents. --- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp -local function signature_help_to_preview_contents(input) - if not input.signatures then - return - end - --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about - --the active signature and shouldn't rely on a default value. - local contents = {} - local active_signature = input.activeSignature or 0 - -- If the activeSignature is not inside the valid range, then clip it. - if active_signature >= #input.signatures then - active_signature = 0 - end - local signature = input.signatures[active_signature + 1] - if not signature then - return - end - vim.list_extend(contents, split_lines(signature.label)) - if signature.documentation then - util.convert_input_to_markdown_lines(signature.documentation, contents) - end - if input.parameters then - local active_parameter = input.activeParameter or 0 - -- If the activeParameter is not inside the valid range, then clip it. - if active_parameter >= #input.parameters then - active_parameter = 0 - end - local parameter = signature.parameters and signature.parameters[active_parameter] - if parameter then - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] - -- TODO highlight parameter - if parameter.documentation then - util.convert_input_to_markdown_lines(parameter.documentation, contents) - end - end - end - return contents -end - --- textDocument/signatureHelp --- https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp -builtin_callbacks['textDocument/signatureHelp'] = function(_, _, result) - if result == nil or vim.tbl_isempty(result) then - return - end - - -- TODO show empty popup when signatures is empty? - if #result.signatures > 0 then - local markdown_lines = signature_help_to_preview_contents(result) - if vim.tbl_isempty(markdown_lines) then - markdown_lines = { 'No signature available' } - end - util.open_floating_preview(markdown_lines, 'markdown') - end -end - -local function update_tagstack() - local bufnr = api.nvim_get_current_buf() - local line = vim.fn.line('.') - local col = vim.fn.col('.') - local tagname = vim.fn.expand('') - local item = { bufnr = bufnr, from = { bufnr, line, col, 0 }, tagname = tagname } - local winid = vim.fn.win_getid() - local tagstack = vim.fn.gettagstack(winid) - - local action - - if tagstack.length == tagstack.curidx then - action = 'r' - tagstack.items[tagstack.curidx] = item - elseif tagstack.length > tagstack.curidx then - action = 'r' - if tagstack.curidx > 1 then - tagstack.items = table.insert(tagstack.items[tagstack.curidx - 1], item) - else - tagstack.items = { item } - end - else - action = 'a' - tagstack.items = { item } - end - - tagstack.curidx = tagstack.curidx + 1 - vim.fn.settagstack(winid, tagstack, action) -end - -local function handle_location(result) - -- We can sometimes get a list of locations, so set the first value as the - -- only value we want to handle - -- TODO(ashkan) was this correct^? We could use location lists. - if result[1] ~= nil then - result = result[1] - end - if result.uri == nil then - api.nvim_err_writeln('[LSP] Could not find a valid location') - return - end - local result_file = vim.uri_to_fname(result.uri) - local bufnr = vim.fn.bufadd(result_file) - update_tagstack() - api.nvim_set_current_buf(bufnr) - local start = result.range.start - api.nvim_win_set_cursor(0, {start.line + 1, start.character}) -end - -local function location_callback(_, method, result) - if result == nil or vim.tbl_isempty(result) then - local _ = log.info() and log.info(method, 'No location found') - return nil - end - handle_location(result) - return true -end - -local location_callbacks = { - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration - 'textDocument/declaration'; - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_definition - 'textDocument/definition'; - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_implementation - 'textDocument/implementation'; - -- https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition - 'textDocument/typeDefinition'; -} - -for _, location_method in ipairs(location_callbacks) do - builtin_callbacks[location_method] = location_callback -end - -local function log_message(_, _, result, client_id) - local message_type = result.type - local message = result.message - local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", client_id) - if not client then - api.nvim_err_writeln(string.format("LSP[%s] client has shut down after sending the message", client_name)) - end - if message_type == protocol.MessageType.Error then - -- Might want to not use err_writeln, - -- but displaying a message with red highlights or something - api.nvim_err_writeln(string.format("LSP[%s] %s", client_name, message)) - else - local message_type_name = protocol.MessageType[message_type] - api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) - end - return result -end - -builtin_callbacks['window/showMessage'] = log_message -builtin_callbacks['window/logMessage'] = log_message - --- Add boilerplate error validation and logging for all of these. -for k, fn in pairs(builtin_callbacks) do - builtin_callbacks[k] = function(err, method, params, client_id) - local _ = log.debug() and log.debug('builtin_callback', method, { params = params, client_id = client_id, err = err }) - if err then - error(tostring(err)) - end - return fn(err, method, params, client_id) - end -end - -return builtin_callbacks --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/default_callbacks.lua b/runtime/lua/vim/lsp/default_callbacks.lua new file mode 100644 index 0000000000..63e62075b4 --- /dev/null +++ b/runtime/lua/vim/lsp/default_callbacks.lua @@ -0,0 +1,57 @@ +local log = require 'vim.lsp.log' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' +local api = vim.api + +local M = {} + +M['textDocument/publishDiagnostics'] = function(_, _, result) + if not result then return end + local uri = result.uri + local bufnr = vim.uri_to_bufnr(uri) + if not bufnr then + api.nvim_err_writeln(string.format("LSP.publishDiagnostics: Couldn't find buffer for %s", uri)) + return + end + util.buf_clear_diagnostics(bufnr) + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + util.buf_diagnostics_underline(bufnr, result.diagnostics) + util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + -- util.buf_loclist(bufnr, result.diagnostics) +end + +local function log_message(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + api.nvim_err_writeln(string.format("LSP[%s] client has shut down after sending the message", client_name)) + end + if message_type == protocol.MessageType.Error then + -- Might want to not use err_writeln, + -- but displaying a message with red highlights or something + api.nvim_err_writeln(string.format("LSP[%s] %s", client_name, message)) + else + local message_type_name = protocol.MessageType[message_type] + api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) + end + return result +end + +M['window/showMessage'] = log_message +M['window/logMessage'] = log_message + +-- Add boilerplate error validation and logging for all of these. +for k, fn in pairs(M) do + M[k] = function(err, method, params, client_id) + local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) + if err then + error(tostring(err)) + end + return fn(err, method, params, client_id) + end +end + +return M +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 1413a88ce2..1f51e7bef7 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -10,7 +10,6 @@ end --[=[ -- Useful for interfacing with: --- https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md -- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md function transform_schema_comments() nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] -- cgit From 03eb88848c2bea6c0c1da7acc97754d6f47b5118 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:09:03 -0800 Subject: Change callback resolution to be dynamic. This allows default_callbacks to be specified after client creation to be considered. Also it simplifies the code. --- runtime/doc/lsp.txt | 4 ++-- runtime/lua/vim/lsp.lua | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 8bafe0245f..f4819dc6f8 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -178,8 +178,8 @@ vim.lsp.start_client({config}) `callbacks` A {table} of whose keys are language server method names and the values are `function(err, method, params, client_id)` See |lsp-callbacks| for - more. This will be combined with |lsp-builtin-callbacks| to provide - defaults. + more. This will be combined with |lsp-default-callbacks| to resolve + the callbacks for a client as a fallback. `init_options` A {table} of values to pass in the initialization request as diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 44909a85ee..a284dd1ee7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -284,19 +284,19 @@ function lsp.start_client(config) local client_id = next_client_id() - local callbacks = tbl_extend("keep", config.callbacks or {}, default_callbacks) - -- Copy metatable if it has one. - if config.callbacks and config.callbacks.__metatable then - setmetatable(callbacks, getmetatable(config.callbacks)) - end + local callbacks = config.callbacks or {} local name = config.name or tostring(client_id) local log_prefix = string.format("LSP[%s]", name) local handlers = {} + local function resolve_callback(method) + return callbacks[method] or default_callbacks[method] + end + function handlers.notification(method, params) local _ = log.debug() and log.debug('notification', method, params) - local callback = callbacks[method] + local callback = resolve_callback(method) if callback then -- Method name is provided here for convenience. callback(nil, method, params, client_id) @@ -305,7 +305,7 @@ function lsp.start_client(config) function handlers.server_request(method, params) local _ = log.debug() and log.debug('server_request', method, params) - local callback = callbacks[method] + local callback = resolve_callback(method) if callback then local _ = log.debug() and log.debug("server_request: found callback for", method) return callback(nil, method, params, client_id) @@ -316,7 +316,8 @@ function lsp.start_client(config) function handlers.on_error(code, err) local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) - nvim_err_writeln(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) + print(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) +-- nvim_err_writeln(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) if config.on_error then local status, usererr = pcall(config.on_error, code, err) if not status then @@ -440,7 +441,7 @@ function lsp.start_client(config) --- Checks capabilities before rpc.request-ing. function client.request(method, params, callback) if not callback then - callback = client.callbacks[method] + callback = resolve_callback(method) or error(string.format("request callback is empty and no default was found for client %s", client.name)) end local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback) -- cgit From c40f8600d2418dfdfeacbba3efe11ae7c6c70ad3 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:16:13 -0800 Subject: Change error writer to not be annoying. --- runtime/lua/vim/lsp.lua | 12 ++++++++---- runtime/lua/vim/lsp/buf.lua | 7 ++++++- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a284dd1ee7..1b9d788c43 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -27,6 +27,11 @@ local lsp = { -- TODO consider whether 'eol' or 'fixeol' should change the nvim_buf_get_lines that send. -- TODO improve handling of scratch buffers with LSP attached. +local function err_message(...) + nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + nvim_command("redraw") +end + local function resolve_bufnr(bufnr) validate { bufnr = { bufnr, 'n', true } } if bufnr == nil or bufnr == 0 then @@ -316,13 +321,12 @@ function lsp.start_client(config) function handlers.on_error(code, err) local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) - print(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) --- nvim_err_writeln(string.format('%s: Error %s: %q', log_prefix, lsp.client_errors[code], vim.inspect(err))) + err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) if config.on_error then local status, usererr = pcall(config.on_error, code, err) if not status then local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr }) - nvim_err_writeln(log_prefix.." user on_error failed: "..tostring(usererr)) + err_message(log_prefix, ' user on_error failed: ', tostring(usererr)) end end end @@ -434,7 +438,7 @@ function lsp.start_client(config) local function unsupported_method(method) local msg = "server doesn't support "..method local _ = log.warn() and log.warn(msg) - nvim_err_writeln(msg) + err_message(msg) return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index e8a38aa6ef..ff045cbbfc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -23,6 +23,11 @@ local function npcall(fn, ...) return ok_or_nil(pcall(fn, ...)) end +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + local function find_window_by_var(name, value) for _, win in ipairs(api.nvim_list_wins()) do if npcall(api.nvim_win_get_var, win, name) == value then @@ -133,7 +138,7 @@ local function handle_location(result) result = result[1] end if result.uri == nil then - api.nvim_err_writeln('[LSP] Could not find a valid location') + err_message('[LSP] Could not find a valid location') return end local result_file = vim.uri_to_fname(result.uri) -- cgit From 1e16b3cf281bec73ccbd155dd11b2db048d1219a Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:16:36 -0800 Subject: Spaces not tabs. --- runtime/lua/vim/lsp/buf.lua | 201 ++++++++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 100 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index ff045cbbfc..8b21370800 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -16,11 +16,11 @@ local function resolve_bufnr(bufnr) end local function ok_or_nil(status, ...) - if not status then return end - return ... + if not status then return end + return ... end local function npcall(fn, ...) - return ok_or_nil(pcall(fn, ...)) + return ok_or_nil(pcall(fn, ...)) end local function err_message(...) @@ -29,78 +29,78 @@ local function err_message(...) end local function find_window_by_var(name, value) - for _, win in ipairs(api.nvim_list_wins()) do - if npcall(api.nvim_win_get_var, win, name) == value then - return win - end - end + for _, win in ipairs(api.nvim_list_wins()) do + if npcall(api.nvim_win_get_var, win, name) == value then + return win + end + end end local function request(method, params, callback) - -- TODO(ashkan) enable this. - -- callback = vim.lsp.default_callbacks[method] or callback - validate { - method = {method, 's'}; - callback = {callback, 'f'}; - } - return vim.lsp.buf_request(0, method, params, function(err, _, result, client_id) - if err then error(tostring(err)) end - return callback(err, method, result, client_id) - end) + -- TODO(ashkan) enable this. + -- callback = vim.lsp.default_callbacks[method] or callback + validate { + method = {method, 's'}; + callback = {callback, 'f'}; + } + return vim.lsp.buf_request(0, method, params, function(err, _, result, client_id) + if err then error(tostring(err)) end + return callback(err, method, result, client_id) + end) end local function focusable_preview(method, params, fn) - if npcall(api.nvim_win_get_var, 0, method) then - return api.nvim_command("wincmd p") - end + if npcall(api.nvim_win_get_var, 0, method) then + return api.nvim_command("wincmd p") + end - local bufnr = api.nvim_get_current_buf() - do - local win = find_window_by_var(method, bufnr) - if win then - return api.nvim_set_current_win(win) - end - end - return request(method, params, function(_, _, result, _) - -- TODO(ashkan) could show error in preview... - local lines, filetype, opts = fn(result) - if lines then - local _, winnr = util.open_floating_preview(lines, filetype, opts) - api.nvim_win_set_var(winnr, method, bufnr) - end - end) + local bufnr = api.nvim_get_current_buf() + do + local win = find_window_by_var(method, bufnr) + if win then + return api.nvim_set_current_win(win) + end + end + return request(method, params, function(_, _, result, _) + -- TODO(ashkan) could show error in preview... + local lines, filetype, opts = fn(result) + if lines then + local _, winnr = util.open_floating_preview(lines, filetype, opts) + api.nvim_win_set_var(winnr, method, bufnr) + end + end) end function M.hover() - local params = protocol.make_text_document_position_params() - focusable_preview('textDocument/hover', params, function(result) - if not (result and result.contents) then return end + local params = protocol.make_text_document_position_params() + focusable_preview('textDocument/hover', params, function(result) + if not (result and result.contents) then return end - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - markdown_lines = util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then - return { 'No information available' } - end - return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) - end) + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + return { 'No information available' } + end + return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) + end) end function M.peek_definition() - local params = protocol.make_text_document_position_params() - request('textDocument/peekDefinition', params, function(_, _, result, _) - if not (result and result[1]) then return end - local loc = result[1] - local bufnr = vim.uri_to_bufnr(loc.uri) or error("couldn't find file "..tostring(loc.uri)) - local start = loc.range.start - local finish = loc.range["end"] - util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) - local headbuf = util.open_floating_preview({"Peek:"}, nil, { - offset_y = -(finish.line - start.line); - width = finish.character - start.character + 2; - }) - -- TODO(ashkan) change highlight group? - api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) - end) + local params = protocol.make_text_document_position_params() + request('textDocument/peekDefinition', params, function(_, _, result, _) + if not (result and result[1]) then return end + local loc = result[1] + local bufnr = vim.uri_to_bufnr(loc.uri) or error("couldn't find file "..tostring(loc.uri)) + local start = loc.range.start + local finish = loc.range["end"] + util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) + local headbuf = util.open_floating_preview({"Peek:"}, nil, { + offset_y = -(finish.line - start.line); + width = finish.character - start.character + 2; + }) + -- TODO(ashkan) change highlight group? + api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) + end) end @@ -158,23 +158,23 @@ local function location_callback(_, method, result) end function M.declaration() - local params = protocol.make_text_document_position_params() - request('textDocument/declaration', params, location_callback) + local params = protocol.make_text_document_position_params() + request('textDocument/declaration', params, location_callback) end function M.definition() - local params = protocol.make_text_document_position_params() - request('textDocument/definition', params, location_callback) + local params = protocol.make_text_document_position_params() + request('textDocument/definition', params, location_callback) end function M.type_definition() - local params = protocol.make_text_document_position_params() - request('textDocument/typeDefinition', params, location_callback) + local params = protocol.make_text_document_position_params() + request('textDocument/typeDefinition', params, location_callback) end function M.implementation() - local params = protocol.make_text_document_position_params() - request('textDocument/implementation', params, location_callback) + local params = protocol.make_text_document_position_params() + request('textDocument/implementation', params, location_callback) end --- Convert SignatureHelp response to preview contents. @@ -237,49 +237,50 @@ local function signature_help_to_preview_contents(input) end function M.signature_help() - local params = protocol.make_text_document_position_params() - focusable_preview('textDocument/signatureHelp', params, function(result) - if not (result and result.signatures and result.signatures[1]) then return end + local params = protocol.make_text_document_position_params() + focusable_preview('textDocument/signatureHelp', params, function(result) + if not (result and result.signatures and result.signatures[1]) then return end - -- TODO show empty popup when signatures is empty? - local lines = signature_help_to_preview_contents(result) - lines = util.trim_empty_lines(lines) - if vim.tbl_isempty(lines) then - return { 'No signature available' } - end - return lines, util.try_trim_markdown_code_blocks(lines) - end) + -- TODO show empty popup when signatures is empty? + local lines = signature_help_to_preview_contents(result) + lines = util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then + return { 'No signature available' } + end + return lines, util.try_trim_markdown_code_blocks(lines) + end) end -- TODO(ashkan) ? function M.completion(context) - local params = protocol.make_text_document_position_params() - params.context = context - return request('textDocument/completion', params, function(_, _, result) - if vim.tbl_isempty(result or {}) then return end - local row, col = unpack(api.nvim_win_get_cursor(0)) - local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) - local line_to_cursor = line:sub(col+1) + local params = protocol.make_text_document_position_params() + params.context = context + return request('textDocument/completion', params, function(_, _, result) + if vim.tbl_isempty(result or {}) then return end + local row, col = unpack(api.nvim_win_get_cursor(0)) + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) - local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) - vim.fn.complete(col, matches) - end) + local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) + vim.fn.complete(col, matches) + end) end function M.range_formatting() end function M.rename(new_name) - -- TODO(ashkan) use prepareRename - -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. - local params = protocol.make_text_document_position_params() - new_name = new_name or npcall(vfn.input, "New Name: ") - if not (new_name and #new_name > 0) then return end - params.newName = new_name - request('textDocument/rename', params, function(_, _, result) - if not result then return end - util.workspace_apply_workspace_edit(result) - end) + -- TODO(ashkan) use prepareRename + -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. + local params = protocol.make_text_document_position_params() + new_name = new_name or npcall(vfn.input, "New Name: ") + if not (new_name and #new_name > 0) then return end + params.newName = new_name + request('textDocument/rename', params, function(_, _, result) + if not result then return end + util.workspace_apply_workspace_edit(result) + end) end return M +-- vim:sw=2 ts=2 et -- cgit From 93beae4f31d42dc70c874020011220444d7f979c Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:35:11 -0800 Subject: Fix rename support. --- runtime/lua/vim/lsp/buf.lua | 1 + runtime/lua/vim/lsp/util.lua | 29 +++++++++++------------------ 2 files changed, 12 insertions(+), 18 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8b21370800..01174a1e48 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -44,6 +44,7 @@ local function request(method, params, callback) callback = {callback, 'f'}; } return vim.lsp.buf_request(0, method, params, function(err, _, result, client_id) + local _ = log.debug() and log.debug("vim.lsp.buf", method, client_id, err, result) if err then error(tostring(err)) end return callback(err, method, result, client_id) end) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 13c83fefd6..ab2c02ffcd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -52,16 +52,12 @@ function M.text_document_apply_text_edit(text_edit, bufnr) api.nvim_buf_set_lines(bufnr, start.line, finish.line, false, new_lines) return end - api.nvim_err_writeln('apply_text_edit currently only supports character ranges starting at 0') - error('apply_text_edit currently only supports character ranges starting at 0') - return - -- TODO test and finish this support for character ranges. --- local lines = api.nvim_buf_get_lines(0, start.line, finish.line + 1, false) --- local suffix = lines[#lines]:sub(finish.character+2) --- local prefix = lines[1]:sub(start.character+2) --- new_lines[#new_lines] = new_lines[#new_lines]..suffix --- new_lines[1] = prefix..new_lines[1] --- api.nvim_buf_set_lines(0, start.line, finish.line, false, new_lines) + local lines = api.nvim_buf_get_lines(bufnr, start.line, finish.line + 1, false) + local suffix = lines[#lines]:sub(finish.character+1) + local prefix = lines[1]:sub(1, start.character) + new_lines[#new_lines] = new_lines[#new_lines]..suffix + new_lines[1] = prefix..new_lines[1] + api.nvim_buf_set_lines(bufnr, start.line, finish.line + 1, false, new_lines) end -- textDocument/completion response returns one of CompletionItem[], CompletionList or null. @@ -158,18 +154,15 @@ function M.workspace_apply_workspace_edit(workspace_edit) return end - if workspace_edit.changes == nil or #workspace_edit.changes == 0 then + local all_changes = workspace_edit.changes + if not (all_changes and not vim.tbl_isempty(all_changes)) then return end - for uri, changes in pairs(workspace_edit.changes) do - local fname = vim.uri_to_fname(uri) - -- TODO improve this approach. Try to edit open buffers without switching. - -- Not sure how to handle files which aren't open. This is deprecated - -- anyway, so I guess it could be left as is. - api.nvim_command('edit '..fname) + for uri, changes in pairs(all_changes) do + local bufnr = vim.uri_to_bufnr(uri) for _, change in ipairs(changes) do - M.text_document_apply_text_edit(change) + M.text_document_apply_text_edit(change, bufnr) end end end -- cgit From 4c7ef3754a7679a7f095fa9e739a665ead4c71b4 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:37:23 -0800 Subject: Satisfy lualint. --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 1b9d788c43..b2dc1315dd 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -903,7 +903,7 @@ end -- Print some debug information about all LSP related things. -- The output of this function should not be relied upon and may change. function lsp.print_debug_info() - print(vim.inspect({ clients = active_clients, filetype_configs = all_filetype_configs })) + print(vim.inspect({ clients = active_clients })) end -- Log level dictionary with reverse lookup as well. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 01174a1e48..1c2e60f1ca 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -7,14 +7,6 @@ local log = require 'vim.lsp.log' local M = {} -local function resolve_bufnr(bufnr) - validate { bufnr = { bufnr, 'n', true } } - if bufnr == nil or bufnr == 0 then - return api.nvim_get_current_buf() - end - return bufnr -end - local function ok_or_nil(status, ...) if not status then return end return ... @@ -198,7 +190,7 @@ local function signature_help_to_preview_contents(input) if not signature then return end - vim.list_extend(contents, split_lines(signature.label)) + vim.list_extend(contents, vim.split(signature.label, '\n', true)) if signature.documentation then util.convert_input_to_markdown_lines(signature.documentation, contents) end -- cgit From 9b7f95671559a9189569dcb93778e54eaa87f255 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 16:39:45 -0800 Subject: Use err_message in default_callbacks --- runtime/lua/vim/lsp/default_callbacks.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/default_callbacks.lua b/runtime/lua/vim/lsp/default_callbacks.lua index 63e62075b4..67c33a1667 100644 --- a/runtime/lua/vim/lsp/default_callbacks.lua +++ b/runtime/lua/vim/lsp/default_callbacks.lua @@ -20,18 +20,23 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) -- util.buf_loclist(bufnr, result.diagnostics) end +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + local function log_message(_, _, result, client_id) local message_type = result.type local message = result.message local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format("id=%d", client_id) if not client then - api.nvim_err_writeln(string.format("LSP[%s] client has shut down after sending the message", client_name)) + err_message("LSP[", client_name, "] client has shut down after sending the message") end if message_type == protocol.MessageType.Error then -- Might want to not use err_writeln, -- but displaying a message with red highlights or something - api.nvim_err_writeln(string.format("LSP[%s] %s", client_name, message)) + err_message("LSP[", client_name, "] ", message) else local message_type_name = protocol.MessageType[message_type] api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) -- cgit From b984f613c1e8dadbe59bf0d7093a6ed12af61b37 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 17:09:21 -0800 Subject: Extend list_extend to take start/finish. --- runtime/lua/vim/lsp/default_callbacks.lua | 14 ++++++-------- runtime/lua/vim/lsp/util.lua | 7 +------ runtime/lua/vim/shared.lua | 31 +++++++++++++++++++------------ 3 files changed, 26 insertions(+), 26 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/default_callbacks.lua b/runtime/lua/vim/lsp/default_callbacks.lua index 67c33a1667..5e8552307b 100644 --- a/runtime/lua/vim/lsp/default_callbacks.lua +++ b/runtime/lua/vim/lsp/default_callbacks.lua @@ -5,12 +5,17 @@ local api = vim.api local M = {} +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + M['textDocument/publishDiagnostics'] = function(_, _, result) if not result then return end local uri = result.uri local bufnr = vim.uri_to_bufnr(uri) if not bufnr then - api.nvim_err_writeln(string.format("LSP.publishDiagnostics: Couldn't find buffer for %s", uri)) + err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) return end util.buf_clear_diagnostics(bufnr) @@ -20,11 +25,6 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) -- util.buf_loclist(bufnr, result.diagnostics) end -local function err_message(...) - api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) - api.nvim_command("redraw") -end - local function log_message(_, _, result, client_id) local message_type = result.type local message = result.message @@ -34,8 +34,6 @@ local function log_message(_, _, result, client_id) err_message("LSP[", client_name, "] client has shut down after sending the message") end if message_type == protocol.MessageType.Error then - -- Might want to not use err_writeln, - -- but displaying a message with red highlights or something err_message("LSP[", client_name, "] ", message) else local message_type_name = protocol.MessageType[message_type] diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ab2c02ffcd..3a2142a478 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -546,7 +546,6 @@ end -- Remove empty lines from the beginning and end. function M.trim_empty_lines(lines) - local result = {} local start = 1 for i = 1, #lines do if #lines[i] > 0 then @@ -561,11 +560,7 @@ function M.trim_empty_lines(lines) break end end - -- TODO(ashkan) use tbl_slice. - for i = start, finish do - table.insert(result, lines[i]) - end - return result + return vim.list_extend({}, lines, start, finish) end -- Accepts markdown lines and tries to reduce it to a filetype if it is diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index ff89acc524..25287ed1aa 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -226,18 +226,25 @@ function vim.tbl_add_reverse_lookup(o) return o end ---- Extends a list-like table with the values of another list-like table. ---- ---NOTE: This *mutates* dst! ---@see |extend()| ---- ---@param dst The list which will be modified and appended to. ---@param src The list from which values will be inserted. -function vim.list_extend(dst, src) - assert(type(dst) == 'table', "dst must be a table") - assert(type(src) == 'table', "src must be a table") - for _, v in ipairs(src) do - table.insert(dst, v) +-- Extends a list-like table with the values of another list-like table. +-- +-- NOTE: This *mutates* dst! +-- @see |extend()| +-- +-- @param dst list which will be modified and appended to. +-- @param src list from which values will be inserted. +-- @param start Start index on src. defaults to 1 +-- @param finish Final index on src. defaults to #src +-- @returns dst +function vim.list_extend(dst, src, start, finish) + vim.validate { + dst = {dst, 't'}; + src = {src, 't'}; + start = {start, 'n', true}; + finish = {finish, 'n', true}; + } + for i = start or 1, finish or #src do + table.insert(dst, src[i]) end return dst end -- cgit From 6fc409d5939500b038ba281ac1929fd053f35310 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 20:51:44 -0800 Subject: Add full text_edit implementation. - Implements textDocument/formatting, textDocument/rangeFormatting, workspace/applyEdit. TODO: - still has edge cases around replacement probably. Only tested with inserts on the same position. --- runtime/lua/vim/lsp/buf.lua | 122 ++++++++++++++++++++++++++- runtime/lua/vim/lsp/default_callbacks.lua | 9 ++ runtime/lua/vim/lsp/util.lua | 134 ++++++++++++++++++++++-------- 3 files changed, 228 insertions(+), 37 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 1c2e60f1ca..ea88207b5b 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -259,7 +259,127 @@ function M.completion(context) end) end -function M.range_formatting() +local function apply_edit_to_lines(lines, start_pos, end_pos, new_lines) + -- 0-indexing to 1-indexing makes things look a bit worse. + local i_0 = start_pos[1] + 1 + local i_n = end_pos[1] + 1 + local n = i_n - i_0 + 1 + if not lines[i_0] or not lines[i_n] then + error(vim.inspect{#lines, i_0, i_n, n, start_pos, end_pos, new_lines}) + end + local prefix = "" + local suffix = lines[i_n]:sub(end_pos[2]+1) + lines[i_n] = lines[i_n]:sub(1, end_pos[2]+1) + if start_pos[2] > 0 then + prefix = lines[i_0]:sub(1, start_pos[2]) + -- lines[i_0] = lines[i_0]:sub(start.character+1) + end + -- TODO(ashkan) figure out how to avoid copy here. likely by changing algo. + new_lines = vim.list_extend({}, new_lines) + if #suffix > 0 then + new_lines[#new_lines] = new_lines[#new_lines]..suffix + end + if #prefix > 0 then + new_lines[1] = prefix..new_lines[1] + end + if #new_lines >= n then + for i = 1, n do + lines[i + i_0 - 1] = new_lines[i] + end + for i = n+1,#new_lines do + table.insert(lines, i_n + 1, new_lines[i]) + end + else + for i = 1, #new_lines do + lines[i + i_0 - 1] = new_lines[i] + end + for _ = #new_lines+1, n do + table.remove(lines, i_0 + #new_lines + 1) + end + end +end + +local function apply_text_edits(text_edits, bufnr) + if not next(text_edits) then return end + -- nvim.print("Start", #text_edits) + local start_line, finish_line = math.huge, -1 + local cleaned = {} + for _, e in ipairs(text_edits) do + start_line = math.min(e.range.start.line, start_line) + finish_line = math.max(e.range["end"].line, finish_line) + table.insert(cleaned, { + A = {e.range.start.line; e.range.start.character}; + B = {e.range["end"].line; e.range["end"].character}; + lines = vim.split(e.newText, '\n', true); + }) + end + local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) + for i, e in ipairs(cleaned) do + -- nvim.print(i, "e", e.A, e.B, #e.lines[#e.lines], e.lines) + local y = 0 + local x = 0 + -- TODO(ashkan) this could be done in O(n) with dynamic programming + for j = 1, i-1 do + local o = cleaned[j] + -- nvim.print(i, "o", o.A, o.B, x, y, #o.lines[#o.lines], o.lines) + if o.A[1] <= e.A[1] and o.A[2] <= e.A[2] then + y = y - (o.B[1] - o.A[1] + 1) + #o.lines + -- Same line + if #o.lines > 1 then + x = -e.A[2] + #o.lines[#o.lines] + else + if o.A[1] == e.A[1] then + -- Try to account for insertions. + -- TODO how to account for deletions? + x = x - (o.B[2] - o.A[2]) + #o.lines[#o.lines] + end + end + end + end + local A = {e.A[1] + y - start_line, e.A[2] + x} + local B = {e.B[1] + y - start_line, e.B[2] + x} + -- if x ~= 0 or y ~= 0 then + -- nvim.print(i, "_", e.A, e.B, y, x, A, B, e.lines) + -- end + apply_edit_to_lines(lines, A, B, e.lines) + end + api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines) +end + +function M.formatting(options) + validate { options = {options, 't', true} } + local params = { + textDocument = { uri = vim.uri_from_bufnr(0) }; + options = options or {}; + } + params.options[vim.type_idx] = vim.types.dictionary + return request('textDocument/formatting', params, function(_, _, result) + if not result then return end + apply_text_edits(result) + end) +end + +function M.range_formatting(options, start_pos, end_pos) + validate { + options = {options, 't', true}; + start_pos = {start_pos, 't', true}; + end_pos = {end_pos, 't', true}; + } + start_pos = start_pos or vim.api.nvim_buf_get_mark(0, '<') + end_pos = end_pos or vim.api.nvim_buf_get_mark(0, '>') + local params = { + textDocument = { uri = vim.uri_from_bufnr(0) }; + range = { + start = { line = start_pos[1]; character = start_pos[2]; }; + ["end"] = { line = end_pos[1]; character = end_pos[2]; }; + }; + options = options or {}; + } + params.options[vim.type_idx] = vim.types.dictionary + return request('textDocument/rangeFormatting', params, function(_, _, result) + if not result then return end + apply_text_edits(result) + end) end function M.rename(new_name) diff --git a/runtime/lua/vim/lsp/default_callbacks.lua b/runtime/lua/vim/lsp/default_callbacks.lua index 5e8552307b..d58280e0f2 100644 --- a/runtime/lua/vim/lsp/default_callbacks.lua +++ b/runtime/lua/vim/lsp/default_callbacks.lua @@ -10,6 +10,15 @@ local function err_message(...) api.nvim_command("redraw") end +M['workspace/applyEdit'] = function(_, _, workspace_edit) + if not workspace_edit then return end + -- TODO(ashkan) Do something more with label? + if workspace_edit.label then + print("Workspace edit", workspace_edit.label) + end + util.apply_workspace_edit(workspace_edit.edit) +end + M['textDocument/publishDiagnostics'] = function(_, _, result) if not result then return end local uri = result.uri diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 3a2142a478..004fb81cba 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -33,6 +33,93 @@ local function resolve_bufnr(bufnr) return bufnr end +function M.apply_edit_to_lines(lines, start_pos, end_pos, new_lines) + -- 0-indexing to 1-indexing makes things look a bit worse. + local i_0 = start_pos[1] + 1 + local i_n = end_pos[1] + 1 + local n = i_n - i_0 + 1 + if not lines[i_0] or not lines[i_n] then + error(vim.inspect{#lines, i_0, i_n, n, start_pos, end_pos, new_lines}) + end + local prefix = "" + local suffix = lines[i_n]:sub(end_pos[2]+1) + lines[i_n] = lines[i_n]:sub(1, end_pos[2]+1) + if start_pos[2] > 0 then + prefix = lines[i_0]:sub(1, start_pos[2]) + -- lines[i_0] = lines[i_0]:sub(start.character+1) + end + -- TODO(ashkan) figure out how to avoid copy here. likely by changing algo. + new_lines = vim.list_extend({}, new_lines) + if #suffix > 0 then + new_lines[#new_lines] = new_lines[#new_lines]..suffix + end + if #prefix > 0 then + new_lines[1] = prefix..new_lines[1] + end + if #new_lines >= n then + for i = 1, n do + lines[i + i_0 - 1] = new_lines[i] + end + for i = n+1,#new_lines do + table.insert(lines, i_n + 1, new_lines[i]) + end + else + for i = 1, #new_lines do + lines[i + i_0 - 1] = new_lines[i] + end + for _ = #new_lines+1, n do + table.remove(lines, i_0 + #new_lines + 1) + end + end +end + +function M.apply_text_edits(text_edits, bufnr) + if not next(text_edits) then return end + -- nvim.print("Start", #text_edits) + local start_line, finish_line = math.huge, -1 + local cleaned = {} + for _, e in ipairs(text_edits) do + start_line = math.min(e.range.start.line, start_line) + finish_line = math.max(e.range["end"].line, finish_line) + table.insert(cleaned, { + A = {e.range.start.line; e.range.start.character}; + B = {e.range["end"].line; e.range["end"].character}; + lines = vim.split(e.newText, '\n', true); + }) + end + local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) + for i, e in ipairs(cleaned) do + -- nvim.print(i, "e", e.A, e.B, #e.lines[#e.lines], e.lines) + local y = 0 + local x = 0 + -- TODO(ashkan) this could be done in O(n) with dynamic programming + for j = 1, i-1 do + local o = cleaned[j] + -- nvim.print(i, "o", o.A, o.B, x, y, #o.lines[#o.lines], o.lines) + if o.A[1] <= e.A[1] and o.A[2] <= e.A[2] then + y = y - (o.B[1] - o.A[1] + 1) + #o.lines + -- Same line + if #o.lines > 1 then + x = -e.A[2] + #o.lines[#o.lines] + else + if o.A[1] == e.A[1] then + -- Try to account for insertions. + -- TODO how to account for deletions? + x = x - (o.B[2] - o.A[2]) + #o.lines[#o.lines] + end + end + end + end + local A = {e.A[1] + y - start_line, e.A[2] + x} + local B = {e.B[1] + y - start_line, e.B[2] + x} + -- if x ~= 0 or y ~= 0 then + -- nvim.print(i, "_", e.A, e.B, y, x, A, B, e.lines) + -- end + M.apply_edit_to_lines(lines, A, B, e.lines) + end + api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines) +end + -- local valid_windows_path_characters = "[^<>:\"/\\|?*]" -- local valid_unix_path_characters = "[^/]" -- https://github.com/davidm/lua-glob-pattern @@ -40,26 +127,6 @@ end -- function M.glob_to_regex(glob) -- end ---- Apply the TextEdit response. --- @params TextEdit [table] see https://microsoft.github.io/language-server-protocol/specification -function M.text_document_apply_text_edit(text_edit, bufnr) - bufnr = resolve_bufnr(bufnr) - local range = text_edit.range - local start = range.start - local finish = range['end'] - local new_lines = split_lines(text_edit.newText) - if start.character == 0 and finish.character == 0 then - api.nvim_buf_set_lines(bufnr, start.line, finish.line, false, new_lines) - return - end - local lines = api.nvim_buf_get_lines(bufnr, start.line, finish.line + 1, false) - local suffix = lines[#lines]:sub(finish.character+1) - local prefix = lines[1]:sub(1, start.character) - new_lines[#new_lines] = new_lines[#new_lines]..suffix - new_lines[1] = prefix..new_lines[1] - api.nvim_buf_set_lines(bufnr, start.line, finish.line + 1, false, new_lines) -end - -- textDocument/completion response returns one of CompletionItem[], CompletionList or null. -- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion function M.extract_completion_items(result) @@ -74,18 +141,15 @@ end --- Apply the TextDocumentEdit response. -- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification -function M.text_document_apply_text_document_edit(text_document_edit, bufnr) - -- local text_document = text_document_edit.textDocument - -- TODO use text_document_version? - -- local text_document_version = text_document.version - - -- TODO technically, you could do this without doing multiple buf_get/set - -- by getting the full region (smallest line and largest line) and doing - -- the edits on the buffer, and then applying the buffer at the end. - -- I'm not sure if that's better. - for _, text_edit in ipairs(text_document_edit.edits) do - M.text_document_apply_text_edit(text_edit, bufnr) +function M.apply_text_document_edit(text_document_edit) + local text_document = text_document_edit.textDocument + local bufnr = vim.uri_to_bufnr(text_document.uri) + -- TODO(ashkan) check this is correct. + if api.nvim_buf_get_changedtick(bufnr) > text_document.version then + print("Buffer ", text_document.uri, " newer than edits.") + return end + M.apply_text_edits(text_document_edit.edits, bufnr) end function M.get_current_line_to_cursor() @@ -141,14 +205,14 @@ function M.text_document_completion_list_to_complete_items(result, line_prefix) end -- @params WorkspaceEdit [table] see https://microsoft.github.io/language-server-protocol/specification -function M.workspace_apply_workspace_edit(workspace_edit) +function M.apply_workspace_edit(workspace_edit) if workspace_edit.documentChanges then for _, change in ipairs(workspace_edit.documentChanges) do if change.kind then -- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile error(string.format("Unsupported change: %q", vim.inspect(change))) else - M.text_document_apply_text_document_edit(change) + M.apply_text_document_edit(change) end end return @@ -161,9 +225,7 @@ function M.workspace_apply_workspace_edit(workspace_edit) for uri, changes in pairs(all_changes) do local bufnr = vim.uri_to_bufnr(uri) - for _, change in ipairs(changes) do - M.text_document_apply_text_edit(change, bufnr) - end + M.apply_text_edits(changes, bufnr) end end -- cgit From 6d9f48ddcf3a4d0fbed0674ce180bcfb9340bb22 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 20:57:21 -0800 Subject: Fix reference in rename. --- runtime/lua/vim/lsp/buf.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index ea88207b5b..68af34dac6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -391,7 +391,7 @@ function M.rename(new_name) params.newName = new_name request('textDocument/rename', params, function(_, _, result) if not result then return end - util.workspace_apply_workspace_edit(result) + util.apply_workspace_edit(result) end) end -- cgit From 7bf766ad0903574b6202af0d2e11d306d656b491 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 20:59:12 -0800 Subject: Use the apply_text_edits from util. --- runtime/lua/vim/lsp/buf.lua | 91 +-------------------------------------------- 1 file changed, 2 insertions(+), 89 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 68af34dac6..260bca281f 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -259,93 +259,6 @@ function M.completion(context) end) end -local function apply_edit_to_lines(lines, start_pos, end_pos, new_lines) - -- 0-indexing to 1-indexing makes things look a bit worse. - local i_0 = start_pos[1] + 1 - local i_n = end_pos[1] + 1 - local n = i_n - i_0 + 1 - if not lines[i_0] or not lines[i_n] then - error(vim.inspect{#lines, i_0, i_n, n, start_pos, end_pos, new_lines}) - end - local prefix = "" - local suffix = lines[i_n]:sub(end_pos[2]+1) - lines[i_n] = lines[i_n]:sub(1, end_pos[2]+1) - if start_pos[2] > 0 then - prefix = lines[i_0]:sub(1, start_pos[2]) - -- lines[i_0] = lines[i_0]:sub(start.character+1) - end - -- TODO(ashkan) figure out how to avoid copy here. likely by changing algo. - new_lines = vim.list_extend({}, new_lines) - if #suffix > 0 then - new_lines[#new_lines] = new_lines[#new_lines]..suffix - end - if #prefix > 0 then - new_lines[1] = prefix..new_lines[1] - end - if #new_lines >= n then - for i = 1, n do - lines[i + i_0 - 1] = new_lines[i] - end - for i = n+1,#new_lines do - table.insert(lines, i_n + 1, new_lines[i]) - end - else - for i = 1, #new_lines do - lines[i + i_0 - 1] = new_lines[i] - end - for _ = #new_lines+1, n do - table.remove(lines, i_0 + #new_lines + 1) - end - end -end - -local function apply_text_edits(text_edits, bufnr) - if not next(text_edits) then return end - -- nvim.print("Start", #text_edits) - local start_line, finish_line = math.huge, -1 - local cleaned = {} - for _, e in ipairs(text_edits) do - start_line = math.min(e.range.start.line, start_line) - finish_line = math.max(e.range["end"].line, finish_line) - table.insert(cleaned, { - A = {e.range.start.line; e.range.start.character}; - B = {e.range["end"].line; e.range["end"].character}; - lines = vim.split(e.newText, '\n', true); - }) - end - local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) - for i, e in ipairs(cleaned) do - -- nvim.print(i, "e", e.A, e.B, #e.lines[#e.lines], e.lines) - local y = 0 - local x = 0 - -- TODO(ashkan) this could be done in O(n) with dynamic programming - for j = 1, i-1 do - local o = cleaned[j] - -- nvim.print(i, "o", o.A, o.B, x, y, #o.lines[#o.lines], o.lines) - if o.A[1] <= e.A[1] and o.A[2] <= e.A[2] then - y = y - (o.B[1] - o.A[1] + 1) + #o.lines - -- Same line - if #o.lines > 1 then - x = -e.A[2] + #o.lines[#o.lines] - else - if o.A[1] == e.A[1] then - -- Try to account for insertions. - -- TODO how to account for deletions? - x = x - (o.B[2] - o.A[2]) + #o.lines[#o.lines] - end - end - end - end - local A = {e.A[1] + y - start_line, e.A[2] + x} - local B = {e.B[1] + y - start_line, e.B[2] + x} - -- if x ~= 0 or y ~= 0 then - -- nvim.print(i, "_", e.A, e.B, y, x, A, B, e.lines) - -- end - apply_edit_to_lines(lines, A, B, e.lines) - end - api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines) -end - function M.formatting(options) validate { options = {options, 't', true} } local params = { @@ -355,7 +268,7 @@ function M.formatting(options) params.options[vim.type_idx] = vim.types.dictionary return request('textDocument/formatting', params, function(_, _, result) if not result then return end - apply_text_edits(result) + util.apply_text_edits(result) end) end @@ -378,7 +291,7 @@ function M.range_formatting(options, start_pos, end_pos) params.options[vim.type_idx] = vim.types.dictionary return request('textDocument/rangeFormatting', params, function(_, _, result) if not result then return end - apply_text_edits(result) + util.apply_text_edits(result) end) end -- cgit From b7170f2d722cee24a62eb74ac836d6192e5475dc Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Wed, 20 Nov 2019 20:59:52 -0800 Subject: Remove resolve_bufnr/lualint --- runtime/lua/vim/lsp/util.lua | 7 ------- 1 file changed, 7 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 004fb81cba..dcf2c17df4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -26,13 +26,6 @@ local function remove_prefix(prefix, word) return word:sub(prefix_length + 1) end -local function resolve_bufnr(bufnr) - if bufnr == nil or bufnr == 0 then - return api.nvim_get_current_buf() - end - return bufnr -end - function M.apply_edit_to_lines(lines, start_pos, end_pos, new_lines) -- 0-indexing to 1-indexing makes things look a bit worse. local i_0 = start_pos[1] + 1 -- cgit From 76e0a8bd935b27270f8f6a594c3025bc494f4422 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 21 Nov 2019 10:29:54 +0100 Subject: lsp: transmit "\n" after last line when 'eol' is set Otherwise some servers like clangd will emit spurious "no newline at end of file" warnings. --- runtime/lua/vim/lsp.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9dbe03dace..0c1a145c04 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -23,7 +23,6 @@ local lsp = { -- format_rpc_error = lsp_rpc.format_rpc_error; } --- TODO consider whether 'eol' or 'fixeol' should change the nvim_buf_get_lines that send. -- TODO improve handling of scratch buffers with LSP attached. local function resolve_bufnr(bufnr) @@ -175,6 +174,14 @@ local function validate_client_config(config) } end +local function buf_get_full_text(bufnr) + local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') + if nvim_buf_get_option(bufnr, 'eol') then + text = text .. '\n' + end + return text +end + local function text_document_did_open_handler(bufnr, client) if not client.resolved_capabilities.text_document_open_close then return @@ -188,7 +195,7 @@ local function text_document_did_open_handler(bufnr, client) uri = vim.uri_from_bufnr(bufnr); -- TODO make sure our filetypes are compatible with languageId names. languageId = nvim_buf_get_option(bufnr, 'filetype'); - text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n'); + text = buf_get_full_text(bufnr); } } client.notify('textDocument/didOpen', params) @@ -551,7 +558,7 @@ do end) local full_changes = once(function() return { - text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), "\n"); + text = buf_get_full_text(bufnr); }; end) local uri = vim.uri_from_bufnr(bufnr) -- cgit From bcae04f6c62b23104e85bb08c54a16d0cdc33852 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 15:19:06 -0800 Subject: Updates - Use correct implementation of text_edits. - Send indent options to rangeFormatting and formatting. - Remove references to vim bindings and filetype from lsp.txt - Add more examples to docs. - Add before_init to allow changing initialize_params. --- runtime/doc/lsp.txt | 161 ++++++++++--------------------------------- runtime/lua/vim/lsp.lua | 13 +++- runtime/lua/vim/lsp/buf.lua | 14 ++-- runtime/lua/vim/lsp/util.lua | 110 ++++++++++++++--------------- 4 files changed, 110 insertions(+), 188 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index f4819dc6f8..4f5a9dff9e 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -24,105 +24,14 @@ After installing a language server to your machine, you must let Neovim know how to start and interact with that language server. To do so, you can either: -- Use the |vim.lsp.add_filetype_config()|, which solves the common use-case of - a single server for one or more filetypes. This can also be used from vim - via |lsp#add_filetype_config()|. +- Use https://github.com/neovim/nvim-lsp and one of the existing servers there + or set up a new one using the `nvim_lsp/skeleton` interface (and contribute + it if you find it useful). This uses |vim.lsp.start_client()| under the + hood. - Or |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. These are the backbone of the LSP API. These are easy to use enough for basic or more complex configurations such as in |lsp-advanced-js-example|. -================================================================================ - *lsp-filetype-config* - -These are utilities specific to filetype based configurations. - - *lsp#add_filetype_config()* - *vim.lsp.add_filetype_config()* -lsp#add_filetype_config({config}) for Vim. -vim.lsp.add_filetype_config({config}) for Lua - - These are functions which can be used to create a simple configuration which - will start a language server for a list of filetypes based on the |FileType| - event. - It will lazily start start the server, meaning that it will only start once - a matching filetype is encountered. - - The {config} options are the same as |vim.lsp.start_client()|, but - with a few additions and distinctions: - - Additional parameters:~ - `filetype` - {string} or {list} of filetypes to attach to. - `name` - A unique identifying string among all other servers configured with - |vim.lsp.add_filetype_config|. - - Differences:~ - `root_dir` - Will default to |getcwd()| instead of being required. - - NOTE: the function options in {config} like {config.on_init} are for Lua - callbacks, not Vim callbacks. -> - " Go example - call lsp#add_filetype_config({ - \ 'filetype': 'go', - \ 'name': 'gopls', - \ 'cmd': 'gopls' - \ }) - " Python example - call lsp#add_filetype_config({ - \ 'filetype': 'python', - \ 'name': 'pyls', - \ 'cmd': 'pyls' - \ }) - " Rust example - call lsp#add_filetype_config({ - \ 'filetype': 'rust', - \ 'name': 'rls', - \ 'cmd': 'rls', - \ 'capabilities': { - \ 'clippy_preference': 'on', - \ 'all_targets': v:false, - \ 'build_on_save': v:true, - \ 'wait_to_build': 0 - \ }}) -< -> - -- From Lua - vim.lsp.add_filetype_config { - name = "clangd"; - filetype = {"c", "cpp"}; - cmd = "clangd -background-index"; - capabilities = { - offsetEncoding = {"utf-8", "utf-16"}; - }; - on_init = vim.schedule_wrap(function(client, result) - if result.offsetEncoding then - client.offset_encoding = result.offsetEncoding - end - end) - } -< - *vim.lsp.copy_filetype_config()* -vim.lsp.copy_filetype_config({existing_name}, [{override_config}]) - - You can use this to copy an existing filetype configuration and change it by - specifying {override_config} which will override any properties in the - existing configuration. If you don't specify a new unique name with - {override_config.name} then it will try to create one and return it. - - Returns:~ - `name` the new configuration name. - - *vim.lsp.get_filetype_client_by_name()* -vim.lsp.get_filetype_client_by_name({name}) - - Use this to look up a client by its name created from - |vim.lsp.add_filetype_config()|. - - Returns nil if the client is not active or the name is not valid. - ================================================================================ *lsp-core-api* These are the core api functions for working with clients. You will mainly be @@ -203,6 +112,12 @@ vim.lsp.start_client({config}) `vim.lsp.client_errors[code]` can be used to retrieve a human understandable string. + `before_init(initialize_params, config)` + A function which is called *before* the request `initialize` is completed. + `initialize_params` contains the parameters we are sending to the server + and `config` is the config that was passed to `start_client()` for + convenience. You can use this to modify parameters before they are sent. + `on_init(client, initialize_result)` A function which is called after the request `initialize` is completed. `initialize_result` contains `capabilities` and anything else the server @@ -485,18 +400,16 @@ vim.lsp.buf_notify({bufnr}, {method}, {params}) ================================================================================ *lsp-logging* - *lsp#set_log_level()* -lsp#set_log_level({level}) + *vim.lsp.set_log_level()* +vim.lsp.set_log_level({level}) You can set the log level for language server client logging. Possible values: "trace", "debug", "info", "warn", "error" Default: "warn" - Example: `call lsp#set_log_level("debug")` + Example: `lua vim.lsp.set_log_level("debug")` - *lsp#get_log_path()* *vim.lsp.get_log_path()* -lsp#get_log_path() vim.lsp.get_log_path() Returns the path that LSP logs are written. @@ -511,43 +424,43 @@ vim.lsp.log_levels ================================================================================ *lsp-omnifunc* *vim.lsp.omnifunc()* - *lsp#omnifunc* -lsp#omnifunc({findstart}, {base}) vim.lsp.omnifunc({findstart}, {base}) To configure omnifunc, add the following in your init.vim: > - set omnifunc=lsp#omnifunc - - " This is optional, but you may find it useful - autocmd CompleteDone * pclose + " Configure for python + autocmd Filetype python setl omnifunc=v:lua.vim.lsp.omnifunc + + " Or with on_attach + start_client { + ... + on_attach = function(client, bufnr) + vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') + end; + } + + " This is optional, but you may find it useful + autocmd CompleteDone * pclose < ================================================================================ *lsp-vim-functions* +To use the functions from vim, it is recommended to use |v:lua| to interface +with the Lua functions. No direct vim functions are provided, but the +interface is still easy to use from mappings. + These methods can be used in mappings and are the equivalent of using the request from lua as follows: -> - lua vim.lsp.buf_request(0, "textDocument/hover", vim.lsp.protocol.make_text_document_position_params()) -< - - lsp#text_document_declaration() - lsp#text_document_definition() - lsp#text_document_hover() - lsp#text_document_implementation() - lsp#text_document_signature_help() - lsp#text_document_type_definition() - > " Example config - autocmd Filetype rust,python,go,c,cpp setl omnifunc=lsp#omnifunc - nnoremap ;dc :call lsp#text_document_declaration() - nnoremap ;df :call lsp#text_document_definition() - nnoremap ;h :call lsp#text_document_hover() - nnoremap ;i :call lsp#text_document_implementation() - nnoremap ;s :call lsp#text_document_signature_help() - nnoremap ;td :call lsp#text_document_type_definition() + autocmd Filetype rust,python,go,c,cpp setl omnifunc=v:lua.vim.lsp.omnifunc + nnoremap ;dc lua vim.lsp.buf.declaration() + nnoremap ;df lua vim.lsp.buf.definition() + nnoremap ;h lua vim.lsp.buf.hover() + nnoremap ;i lua vim.lsp.buf.implementation() + nnoremap ;s lua vim.lsp.buf.signature_help() + nnoremap ;td lua vim.lsp.buf.type_definition() < ================================================================================ *lsp-advanced-js-example* diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5a0b320f4c..790f8f8a9d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -160,13 +160,13 @@ local function validate_client_config(config) root_dir = { config.root_dir, is_dir, "directory" }; callbacks = { config.callbacks, "t", true }; capabilities = { config.capabilities, "t", true }; - -- cmd = { config.cmd, "s", false }; cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; cmd_env = { config.cmd_env, "f", true }; name = { config.name, 's', true }; on_error = { config.on_error, "f", true }; on_exit = { config.on_exit, "f", true }; on_init = { config.on_init, "f", true }; + before_init = { config.before_init, "f", true }; offset_encoding = { config.offset_encoding, "s", true }; } local cmd, cmd_args = validate_command(config.cmd) @@ -267,6 +267,12 @@ end -- possible errors. `vim.lsp.client_errors[code]` can be used to retrieve a -- human understandable string. -- +-- before_init(initialize_params, config): A function which is called *before* +-- the request `initialize` is completed. `initialize_params` contains +-- the parameters we are sending to the server and `config` is the config that +-- was passed to `start_client()` for convenience. You can use this to modify +-- parameters before they are sent. +-- -- on_init(client, initialize_result): A function which is called after the -- request `initialize` is completed. `initialize_result` contains -- `capabilities` and anything else the server may send. For example, `clangd` @@ -385,7 +391,6 @@ function lsp.start_client(config) -- The rootUri of the workspace. Is null if no folder is open. If both -- `rootPath` and `rootUri` are set `rootUri` wins. rootUri = vim.uri_from_fname(config.root_dir); --- rootUri = vim.uri_from_fname(vim.fn.expand("%:p:h")); -- User provided initialization options. initializationOptions = config.init_options; -- The capabilities provided by the client (editor or tool) @@ -409,6 +414,10 @@ function lsp.start_client(config) -- } workspaceFolders = nil; } + if config.before_init then + -- TODO(ashkan) handle errors here. + pcall(config.before_init, initialize_params, config) + end local _ = log.debug() and log.debug(log_prefix, "initialize_params", initialize_params) rpc.request('initialize', initialize_params, function(init_err, result) assert(not init_err, tostring(init_err)) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 260bca281f..dbc141a906 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -261,11 +261,14 @@ end function M.formatting(options) validate { options = {options, 't', true} } + options = vim.tbl_extend('keep', options or {}, { + tabSize = api.nvim_buf_get_option(0, 'tabstop'); + insertSpaces = api.nvim_buf_get_option(0, 'expandtab'); + }) local params = { textDocument = { uri = vim.uri_from_bufnr(0) }; - options = options or {}; + options = options; } - params.options[vim.type_idx] = vim.types.dictionary return request('textDocument/formatting', params, function(_, _, result) if not result then return end util.apply_text_edits(result) @@ -278,6 +281,10 @@ function M.range_formatting(options, start_pos, end_pos) start_pos = {start_pos, 't', true}; end_pos = {end_pos, 't', true}; } + options = vim.tbl_extend('keep', options or {}, { + tabSize = api.nvim_buf_get_option(0, 'tabstop'); + insertSpaces = api.nvim_buf_get_option(0, 'expandtab'); + }) start_pos = start_pos or vim.api.nvim_buf_get_mark(0, '<') end_pos = end_pos or vim.api.nvim_buf_get_mark(0, '>') local params = { @@ -286,9 +293,8 @@ function M.range_formatting(options, start_pos, end_pos) start = { line = start_pos[1]; character = start_pos[2]; }; ["end"] = { line = end_pos[1]; character = end_pos[2]; }; }; - options = options or {}; + options = options; } - params.options[vim.type_idx] = vim.types.dictionary return request('textDocument/rangeFormatting', params, function(_, _, result) if not result then return end util.apply_text_edits(result) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index dcf2c17df4..66430fffc7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -26,89 +26,83 @@ local function remove_prefix(prefix, word) return word:sub(prefix_length + 1) end -function M.apply_edit_to_lines(lines, start_pos, end_pos, new_lines) - -- 0-indexing to 1-indexing makes things look a bit worse. - local i_0 = start_pos[1] + 1 - local i_n = end_pos[1] + 1 - local n = i_n - i_0 + 1 - if not lines[i_0] or not lines[i_n] then - error(vim.inspect{#lines, i_0, i_n, n, start_pos, end_pos, new_lines}) +-- TODO(ashkan) @performance this could do less copying. +function M.set_lines(lines, A, B, new_lines) + -- 0-indexing to 1-indexing + local i_0 = A[1] + 1 + local i_n = B[1] + 1 + if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then + error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines}) end local prefix = "" - local suffix = lines[i_n]:sub(end_pos[2]+1) - lines[i_n] = lines[i_n]:sub(1, end_pos[2]+1) - if start_pos[2] > 0 then - prefix = lines[i_0]:sub(1, start_pos[2]) - -- lines[i_0] = lines[i_0]:sub(start.character+1) - end - -- TODO(ashkan) figure out how to avoid copy here. likely by changing algo. - new_lines = vim.list_extend({}, new_lines) + local suffix = lines[i_n]:sub(B[2]+1) + if A[2] > 0 then + prefix = lines[i_0]:sub(1, A[2]) + end + new_lines = list_extend({}, new_lines) if #suffix > 0 then new_lines[#new_lines] = new_lines[#new_lines]..suffix end if #prefix > 0 then new_lines[1] = prefix..new_lines[1] end - if #new_lines >= n then - for i = 1, n do - lines[i + i_0 - 1] = new_lines[i] - end - for i = n+1,#new_lines do - table.insert(lines, i_n + 1, new_lines[i]) - end - else - for i = 1, #new_lines do - lines[i + i_0 - 1] = new_lines[i] - end - for _ = #new_lines+1, n do - table.remove(lines, i_0 + #new_lines + 1) + local result = list_extend({}, lines, 1, i_0 - 1) + list_extend(result, new_lines) + list_extend(result, lines, i_n + 1) + return result +end + +local function sort_by_key(fn) + return function(a,b) + local ka, kb = fn(a), fn(b) + assert(#ka == #kb) + for i = 1, #ka do + if ka[i] ~= kb[i] then + return ka[i] < kb[i] + end end + -- every value must have been equal here, which means it's not less than. + return false end end +local edit_sort_key = sort_by_key(function(e) + return {e.A[1], e.A[2], e.i} +end) function M.apply_text_edits(text_edits, bufnr) if not next(text_edits) then return end - -- nvim.print("Start", #text_edits) local start_line, finish_line = math.huge, -1 local cleaned = {} - for _, e in ipairs(text_edits) do + for i, e in ipairs(text_edits) do start_line = math.min(e.range.start.line, start_line) finish_line = math.max(e.range["end"].line, finish_line) + -- TODO(ashkan) sanity check ranges for overlap. table.insert(cleaned, { + i = i; A = {e.range.start.line; e.range.start.character}; B = {e.range["end"].line; e.range["end"].character}; lines = vim.split(e.newText, '\n', true); }) end + + -- Reverse sort the orders so we can apply them without interfering with + -- eachother. Also add i as a sort key to mimic a stable sort. + table.sort(cleaned, edit_sort_key) local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) - for i, e in ipairs(cleaned) do - -- nvim.print(i, "e", e.A, e.B, #e.lines[#e.lines], e.lines) - local y = 0 - local x = 0 - -- TODO(ashkan) this could be done in O(n) with dynamic programming - for j = 1, i-1 do - local o = cleaned[j] - -- nvim.print(i, "o", o.A, o.B, x, y, #o.lines[#o.lines], o.lines) - if o.A[1] <= e.A[1] and o.A[2] <= e.A[2] then - y = y - (o.B[1] - o.A[1] + 1) + #o.lines - -- Same line - if #o.lines > 1 then - x = -e.A[2] + #o.lines[#o.lines] - else - if o.A[1] == e.A[1] then - -- Try to account for insertions. - -- TODO how to account for deletions? - x = x - (o.B[2] - o.A[2]) + #o.lines[#o.lines] - end - end - end - end - local A = {e.A[1] + y - start_line, e.A[2] + x} - local B = {e.B[1] + y - start_line, e.B[2] + x} - -- if x ~= 0 or y ~= 0 then - -- nvim.print(i, "_", e.A, e.B, y, x, A, B, e.lines) - -- end - M.apply_edit_to_lines(lines, A, B, e.lines) + local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol') + local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) == finish_line + 1 + if set_eol and #lines[#lines] ~= 0 then + table.insert(lines, '') + end + + for i = #cleaned, 1, -1 do + local e = cleaned[i] + local A = {e.A[1] - start_line, e.A[2]} + local B = {e.B[1] - start_line, e.B[2]} + lines = M.set_lines(lines, A, B, e.lines) + end + if set_eol and #lines[#lines] == 0 then + table.remove(lines) end api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines) end -- cgit From b27965538dcc3faa8e1c5b3d64a98108c18981ec Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 15:20:32 -0800 Subject: Fix hovers staying on bufhidden --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 66430fffc7..77f2ae6fbc 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -335,7 +335,7 @@ function M.open_floating_preview(contents, filetype, opts) end api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - api.nvim_command("autocmd CursorMoved ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") + api.nvim_command("autocmd CursorMoved,BufHidden ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") return floating_bufnr, floating_winnr end -- cgit From da07e71b189a51008a93783b5f7894dc495c277e Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 15:34:28 -0800 Subject: Account for character length in jump position. --- runtime/lua/vim/lsp/buf.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index dbc141a906..0b4ac6ca9b 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -134,12 +134,14 @@ local function handle_location(result) err_message('[LSP] Could not find a valid location') return end - local result_file = vim.uri_to_fname(result.uri) - local bufnr = vfn.bufadd(result_file) + local bufnr = vim.uri_to_bufnr(result.uri) update_tagstack() api.nvim_set_current_buf(bufnr) - local start = result.range.start - api.nvim_win_set_cursor(0, {start.line + 1, start.character}) + local row = result.range.start.line + local col = result.range.start.character + local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + col = #line:sub(1, col) + api.nvim_win_set_cursor(0, {row + 1, col}) return true end local function location_callback(_, method, result) -- cgit From 43d73ee884f97861c44adab4797a8fc185af1436 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 15:41:32 -0800 Subject: Fix position params for encoding. --- runtime/doc/lsp.txt | 6 ++---- runtime/lua/vim/lsp/buf.lua | 18 +++++++++--------- runtime/lua/vim/lsp/protocol.lua | 13 ------------- runtime/lua/vim/lsp/util.lua | 11 +++++++++++ 4 files changed, 22 insertions(+), 26 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 4f5a9dff9e..3de2a9e4e6 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -327,12 +327,10 @@ vim.lsp.protocol vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" Utility functions used internally are: - `vim.lsp.make_client_capabilities()` + `vim.lsp.protocol.make_client_capabilities()` Make a ClientCapabilities object. These are the builtin capabilities. - `vim.lsp.make_text_document_position_params()` - Make a TextDocumentPositionParams object. - `vim.lsp.resolve_capabilities(server_capabilites)` + `vim.lsp.protocol.resolve_capabilities(server_capabilites)` Creates a normalized object describing capabilities from the server capabilities. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0b4ac6ca9b..ed26e80c3c 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -65,7 +65,7 @@ local function focusable_preview(method, params, fn) end function M.hover() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() focusable_preview('textDocument/hover', params, function(result) if not (result and result.contents) then return end @@ -79,7 +79,7 @@ function M.hover() end function M.peek_definition() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() request('textDocument/peekDefinition', params, function(_, _, result, _) if not (result and result[1]) then return end local loc = result[1] @@ -153,22 +153,22 @@ local function location_callback(_, method, result) end function M.declaration() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() request('textDocument/declaration', params, location_callback) end function M.definition() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() request('textDocument/definition', params, location_callback) end function M.type_definition() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() request('textDocument/typeDefinition', params, location_callback) end function M.implementation() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() request('textDocument/implementation', params, location_callback) end @@ -232,7 +232,7 @@ local function signature_help_to_preview_contents(input) end function M.signature_help() - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() focusable_preview('textDocument/signatureHelp', params, function(result) if not (result and result.signatures and result.signatures[1]) then return end @@ -248,7 +248,7 @@ end -- TODO(ashkan) ? function M.completion(context) - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() params.context = context return request('textDocument/completion', params, function(_, _, result) if vim.tbl_isempty(result or {}) then return end @@ -306,7 +306,7 @@ end function M.rename(new_name) -- TODO(ashkan) use prepareRename -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. - local params = protocol.make_text_document_position_params() + local params = util.make_position_params() new_name = new_name or npcall(vfn.input, "New Name: ") if not (new_name and #new_name > 0) then return end params.newName = new_name diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 1f51e7bef7..ead90cc75a 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -680,19 +680,6 @@ function protocol.make_client_capabilities() } end -function protocol.make_text_document_position_params() - local position = vim.api.nvim_win_get_cursor(0) - return { - textDocument = { - uri = vim.uri_from_bufnr() - }; - position = { - line = position[1] - 1; - character = position[2]; - } - } -end - --[=[ export interface DocumentFilter { --A language id, like `typescript`. diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 77f2ae6fbc..76681920bd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -638,6 +638,17 @@ function M.try_trim_markdown_code_blocks(lines) return 'markdown' end +function M.make_position_params() + local row, col = unpack(api.nvim_win_get_cursor(0)) + row = row - 1 + local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + col = vim.str_utfindex(line, col) + return { + textDocument = { uri = vim.uri_from_bufnr(0) }; + position = { line = row; character = col; } + } +end + return M -- vim:sw=2 ts=2 et -- cgit From c055ca00ce61499e9161b8d2fa48568e833ee5b0 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 15:55:45 -0800 Subject: Remove comments. --- runtime/lua/vim/lsp.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 790f8f8a9d..17d6001496 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -98,11 +98,7 @@ local function for_each_buffer_client(bufnr, callback) return end for client_id in pairs(client_ids) do - -- This is unlikely to happen. Could only potentially happen in a race - -- condition between literally a single statement. - -- We could skip this error, but let's error for now. local client = active_clients[client_id] - -- or error(string.format("Client %d has already shut down.", client_id)) if client then callback(client, client_id) end -- cgit From a3d67dac5f9de6099890ae4b9fab2b70b2279a29 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 16:23:12 -0800 Subject: Fix encoding translation in other places. --- runtime/lua/vim/lsp.lua | 2 +- runtime/lua/vim/lsp/buf.lua | 22 ++++++++++++++++------ runtime/lua/vim/lsp/util.lua | 7 +++++++ 3 files changed, 24 insertions(+), 7 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 17d6001496..880d811647 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -862,7 +862,7 @@ function lsp.omnifunc(findstart, base) position = { -- 0-indexed for both line and character line = pos[1] - 1, - character = pos[2], + character = vim.str_utfindex(line, pos[2]), }; -- The completion context. This is only available if the client specifies -- to send this using `ClientCapabilities.textDocument.completion.contextSupport === true` diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index ed26e80c3c..b4e0b9cbfc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -2,8 +2,8 @@ local validate = vim.validate local api = vim.api local vfn = vim.fn local util = require 'vim.lsp.util' -local protocol = require 'vim.lsp.protocol' local log = require 'vim.lsp.log' +local list_extend = vim.list_extend local M = {} @@ -192,7 +192,7 @@ local function signature_help_to_preview_contents(input) if not signature then return end - vim.list_extend(contents, vim.split(signature.label, '\n', true)) + list_extend(contents, vim.split(signature.label, '\n', true)) if signature.documentation then util.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -287,13 +287,23 @@ function M.range_formatting(options, start_pos, end_pos) tabSize = api.nvim_buf_get_option(0, 'tabstop'); insertSpaces = api.nvim_buf_get_option(0, 'expandtab'); }) - start_pos = start_pos or vim.api.nvim_buf_get_mark(0, '<') - end_pos = end_pos or vim.api.nvim_buf_get_mark(0, '>') + local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) + local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>')) + -- convert to 0-index + A[1] = A[1] - 1 + B[1] = B[1] - 1 + -- account for encoding. + if A[2] > 0 then + A = {A[1], util.character_offset(0, unpack(A))} + end + if B[2] > 0 then + B = {B[1], util.character_offset(0, unpack(B))} + end local params = { textDocument = { uri = vim.uri_from_bufnr(0) }; range = { - start = { line = start_pos[1]; character = start_pos[2]; }; - ["end"] = { line = end_pos[1]; character = end_pos[2]; }; + start = { line = A[1]; character = A[2]; }; + ["end"] = { line = B[1]; character = B[2]; }; }; options = options; } diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 76681920bd..570c4df1dd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -649,6 +649,13 @@ function M.make_position_params() } end +-- @param buf buffer handle or 0 for current. +-- @param row 0-indexed line +-- @param col 0-indexed byte offset in line +function M.character_offset(buf, row, col) + local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] + return vim.str_utfindex(line, col) +end return M -- vim:sw=2 ts=2 et -- cgit From 78991ffbf4357ba1ad477a13991078bb4a0bdc58 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Thu, 21 Nov 2019 23:58:32 -0800 Subject: Improve performance of util.set_lines + bugfix Also permit character_offset for col past the end of line (useful in range formatting). --- runtime/lua/vim/lsp/util.lua | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 570c4df1dd..94b4223a20 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -11,6 +11,14 @@ end local list_extend = vim.list_extend +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end + --- Find the longest shared prefix between prefix and word. -- e.g. remove_prefix("123tes", "testing") == "ting" local function remove_prefix(prefix, word) @@ -39,17 +47,22 @@ function M.set_lines(lines, A, B, new_lines) if A[2] > 0 then prefix = lines[i_0]:sub(1, A[2]) end - new_lines = list_extend({}, new_lines) + local n = i_n - i_0 + 1 + if n ~= #new_lines then + for _ = 1, n - #new_lines do table.remove(lines, i_0) end + for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end + end + for i = 1, #new_lines do + lines[i - 1 + i_0] = new_lines[i] + end if #suffix > 0 then - new_lines[#new_lines] = new_lines[#new_lines]..suffix + local i = i_0 + #new_lines - 1 + lines[i] = lines[i]..suffix end if #prefix > 0 then - new_lines[1] = prefix..new_lines[1] + lines[i_0] = prefix..lines[i_0] end - local result = list_extend({}, lines, 1, i_0 - 1) - list_extend(result, new_lines) - list_extend(result, lines, i_n + 1) - return result + return lines end local function sort_by_key(fn) @@ -99,7 +112,7 @@ function M.apply_text_edits(text_edits, bufnr) local e = cleaned[i] local A = {e.A[1] - start_line, e.A[2]} local B = {e.B[1] - start_line, e.B[2]} - lines = M.set_lines(lines, A, B, e.lines) + M.set_lines(lines, A, B, e.lines) end if set_eol and #lines[#lines] == 0 then table.remove(lines) @@ -638,11 +651,12 @@ function M.try_trim_markdown_code_blocks(lines) return 'markdown' end +local str_utfindex = vim.str_utfindex function M.make_position_params() local row, col = unpack(api.nvim_win_get_cursor(0)) row = row - 1 local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] - col = vim.str_utfindex(line, col) + col = str_utfindex(line, col) return { textDocument = { uri = vim.uri_from_bufnr(0) }; position = { line = row; character = col; } @@ -654,7 +668,9 @@ end -- @param col 0-indexed byte offset in line function M.character_offset(buf, row, col) local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] - return vim.str_utfindex(line, col) + -- TODO(ashkan) is there a better way to handle col being past line length? + -- If the col is past the EOL, use the line length. + return npcall(str_utfindex, line, col) or str_utfindex(line) end return M -- cgit From 73487f4130581da72c9e838189aab39c79c177c5 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 22 Nov 2019 00:31:10 -0800 Subject: Improve the character_offset code. --- runtime/lua/vim/lsp/util.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 94b4223a20..e2a8748a20 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -11,14 +11,6 @@ end local list_extend = vim.list_extend -local function ok_or_nil(status, ...) - if not status then return end - return ... -end -local function npcall(fn, ...) - return ok_or_nil(pcall(fn, ...)) -end - --- Find the longest shared prefix between prefix and word. -- e.g. remove_prefix("123tes", "testing") == "ting" local function remove_prefix(prefix, word) @@ -668,9 +660,11 @@ end -- @param col 0-indexed byte offset in line function M.character_offset(buf, row, col) local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] - -- TODO(ashkan) is there a better way to handle col being past line length? -- If the col is past the EOL, use the line length. - return npcall(str_utfindex, line, col) or str_utfindex(line) + if col > #line then + return str_utfindex(line) + end + return str_utfindex(line, col) end return M -- cgit From d5f14b8372b3c8d441187eea659156534cb4c9ba Mon Sep 17 00:00:00 2001 From: Dennis B Date: Fri, 22 Nov 2019 20:55:04 +1100 Subject: Clear 'cc' in nvim_open_win 'minimal' style #11361 (#11427) * Clear 'cc' in nvim_open_win 'minimal' style #11361 Add 'colorcolumn' to the list of options that should be cleared when creating a 'minimal'-style floating window. --- runtime/doc/api.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 57a72e6173..f97795b0ee 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -933,10 +933,11 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* 'number', 'relativenumber', 'cursorline', 'cursorcolumn', 'foldcolumn', 'spell' and 'list' options. 'signcolumn' is changed to - `auto` . The end-of-buffer region is hidden - by setting `eob` flag of 'fillchars' to a - space char, and clearing the |EndOfBuffer| - region in 'winhighlight'. + `auto` and 'colorcolumn' is cleared. The + end-of-buffer region is hidden by setting + `eob` flag of 'fillchars' to a space char, and + clearing the |EndOfBuffer| region in + 'winhighlight'. Return: ~ Window handle, or 0 on error -- cgit From d410812311f7b462420690455914ea1316953b3a Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sat, 23 Nov 2019 16:14:24 -0800 Subject: UI tweaks. - Hide diagnostics on client exit - Stop insert on popup focus. - Hide popup on insertchar (for signature_help) --- runtime/lua/vim/lsp.lua | 12 +++++++++++- runtime/lua/vim/lsp/buf.lua | 8 ++++++-- runtime/lua/vim/lsp/util.lua | 5 +++-- 3 files changed, 20 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 880d811647..7076db68d3 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -343,9 +343,19 @@ function lsp.start_client(config) function handlers.on_exit(code, signal) active_clients[client_id] = nil uninitialized_clients[client_id] = nil - for _, client_ids in pairs(all_buffer_active_clients) do + local active_buffers = {} + for bufnr, client_ids in pairs(all_buffer_active_clients) do + if client_ids[client_id] then + table.insert(active_buffers, bufnr) + end client_ids[client_id] = nil end + -- Buffer level cleanup + vim.schedule(function() + for _, bufnr in ipairs(active_buffers) do + util.buf_clear_diagnostics(bufnr) + end + end) if config.on_exit then pcall(config.on_exit, code, signal, client_id) end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index b4e0b9cbfc..79d43fda4a 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -51,7 +51,9 @@ local function focusable_preview(method, params, fn) do local win = find_window_by_var(method, bufnr) if win then - return api.nvim_set_current_win(win) + api.nvim_set_current_win(win) + api.nvim_command("stopinsert") + return end end return request(method, params, function(_, _, result, _) @@ -234,7 +236,9 @@ end function M.signature_help() local params = util.make_position_params() focusable_preview('textDocument/signatureHelp', params, function(result) - if not (result and result.signatures and result.signatures[1]) then return end + if not (result and result.signatures and result.signatures[1]) then + return { 'No signature available' } + end -- TODO show empty popup when signatures is empty? local lines = signature_help_to_preview_contents(result) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e2a8748a20..0c53494f02 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -104,7 +104,7 @@ function M.apply_text_edits(text_edits, bufnr) local e = cleaned[i] local A = {e.A[1] - start_line, e.A[2]} local B = {e.B[1] - start_line, e.B[2]} - M.set_lines(lines, A, B, e.lines) + lines = M.set_lines(lines, A, B, e.lines) end if set_eol and #lines[#lines] == 0 then table.remove(lines) @@ -340,7 +340,8 @@ function M.open_floating_preview(contents, filetype, opts) end api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - api.nvim_command("autocmd CursorMoved,BufHidden ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") + -- TODO make InsertCharPre disappearing optional? + api.nvim_command("autocmd CursorMoved,BufHidden,InsertCharPre ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") return floating_bufnr, floating_winnr end -- cgit From d0d38fc36e0c1602186aa540417070fa6c1e2746 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 24 Nov 2019 02:28:48 -0800 Subject: Lua: vim.env, vim.{g,v,w,bo,wo} #11442 - Add vim variable meta accessors: vim.env, vim.{g,v,w,bo,wo} - Redo gen_char_blob to generate multiple blobs instead of just one so that multiple Lua modules can be inlined. - Reorder vim.lua inclusion so that it can use previously defined C functions and utility functions like vim.shared and vim.inspect things. - Inline shared.lua into nvim, but also keep it available in runtime. --- runtime/lua/vim/shared.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index ff89acc524..6aa5f3bd9b 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -4,7 +4,7 @@ -- test-suite. If, in the future, Nvim itself is used to run the test-suite -- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua -local vim = {} +local vim = vim or {} --- Returns a deep copy of the given object. Non-table objects are copied as --- in a typical Lua assignment, whereas table objects are copied recursively. -- cgit From b35f6aa9dd686d082405132e3455cd0aff334361 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 24 Nov 2019 03:01:18 -0800 Subject: Add support for textDocument/references. Add set_qflist and set_loclist. - Also add locations_to_items, which calculates byte offsets for character positions in files and avoids unnecessary operations. --- runtime/lua/vim/lsp/buf.lua | 15 ++++++ runtime/lua/vim/lsp/default_callbacks.lua | 2 +- runtime/lua/vim/lsp/util.lua | 80 +++++++++++++++++++++++-------- 3 files changed, 77 insertions(+), 20 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 79d43fda4a..51295e5570 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -1,3 +1,4 @@ +local vim = vim local validate = vim.validate local api = vim.api local vfn = vim.fn @@ -330,5 +331,19 @@ function M.rename(new_name) end) end +function M.references(context) + validate { context = { context, 't', true } } + local params = util.make_position_params() + params.context = context or { + includeDeclaration = true; + } + params[vim.type_idx] = vim.types.dictionary + request('textDocument/references', params, function(_, _, result) + if not result then return end + util.set_qflist(result) + vim.api.nvim_command("copen") + end) +end + return M -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/default_callbacks.lua b/runtime/lua/vim/lsp/default_callbacks.lua index d58280e0f2..2a891e7d1d 100644 --- a/runtime/lua/vim/lsp/default_callbacks.lua +++ b/runtime/lua/vim/lsp/default_callbacks.lua @@ -31,7 +31,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_save_positions(bufnr, result.diagnostics) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) - -- util.buf_loclist(bufnr, result.diagnostics) + -- util.set_loclist(result.diagnostics) end local function log_message(_, _, result, client_id) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 0c53494f02..2dfcdfc70c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,4 +1,5 @@ local protocol = require 'vim.lsp.protocol' +local vim = vim local validate = vim.validate local api = vim.api @@ -573,30 +574,71 @@ do end end -function M.buf_loclist(bufnr, locations) - local targetwin - for _, winnr in ipairs(api.nvim_list_wins()) do - local winbuf = api.nvim_win_get_buf(winnr) - if winbuf == bufnr then - targetwin = winnr - break - end - end - if not targetwin then return end +local position_sort = sort_by_key(function(v) + return {v.line, v.character} +end) +-- Returns the items with the byte position calculated correctly and in sorted +-- order. +function M.locations_to_items(locations) local items = {} - local path = api.nvim_buf_get_name(bufnr) + local grouped = setmetatable({}, { + __index = function(t, k) + local v = {} + rawset(t, k, v) + return v + end; + }) for _, d in ipairs(locations) do - -- TODO: URL parsing here? local start = d.range.start - table.insert(items, { - filename = path, - lnum = start.line + 1, - col = start.character + 1, - text = d.message, - }) + local fname = assert(vim.uri_to_fname(d.uri)) + table.insert(grouped[fname], start) + end + local keys = vim.tbl_keys(grouped) + table.sort(keys) + -- TODO(ashkan) I wish we could do this lazily. + for _, fname in ipairs(keys) do + local rows = grouped[fname] + table.sort(rows, position_sort) + local i = 0 + for line in io.lines(fname) do + for _, pos in ipairs(rows) do + local row = pos.line + if i == row then + local col + if pos.character > #line then + col = #line + else + col = vim.str_byteindex(line, pos.character) + end + table.insert(items, { + filename = fname, + lnum = row + 1, + col = col + 1; + }) + end + end + i = i + 1 + end end - vim.fn.setloclist(targetwin, items, ' ', 'Language Server') + return items +end + +-- locations is Location[] +-- Only sets for the current window. +function M.set_loclist(locations) + vim.fn.setloclist(0, {}, ' ', { + title = 'Language Server'; + items = M.locations_to_items(locations); + }) +end + +-- locations is Location[] +function M.set_qflist(locations) + vim.fn.setqflist({}, ' ', { + title = 'Language Server'; + items = M.locations_to_items(locations); + }) end -- Remove empty lines from the beginning and end. -- cgit From a9036502dca7191c8ada70367e44c398d48dd94a Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 24 Nov 2019 03:14:03 -0800 Subject: Bring vim into local scope --- runtime/lua/vim/lsp.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 7076db68d3..928d19177a 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -4,6 +4,7 @@ local lsp_rpc = require 'vim.lsp.rpc' local protocol = require 'vim.lsp.protocol' local util = require 'vim.lsp.util' +local vim = vim local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option = vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option local uv = vim.loop -- cgit From 526798a941b4cf80fd1f128b40e51fb47c77b654 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 24 Nov 2019 20:30:04 -0500 Subject: man.vim: Ensure 'modifiable' in man#init_pager #11450 --- runtime/autoload/man.vim | 8 +++++++- runtime/syntax/man.vim | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 36f42c0003..c559ca9f27 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi +" Maintainer: Anmol Sethi if exists('s:loaded_man') finish @@ -357,6 +357,10 @@ function! s:format_candidate(path, psect) abort endfunction function! man#init_pager() abort + " https://github.com/neovim/neovim/issues/6828 + let og_modifiable = &modifiable + setlocal modifiable + if getline(1) =~# '^\s*$' silent keepjumps 1delete _ else @@ -374,6 +378,8 @@ function! man#init_pager() abort if -1 == match(bufname('%'), 'man:\/\/') " Avoid duplicate buffers, E95. execute 'silent file man://'.tolower(fnameescape(ref)) endif + + let &l:modifiable = og_modifiable endfunction function! man#goto_tag(pattern, flags, info) abort diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index 6afe56a6e3..7ac02c3f63 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi +" Maintainer: Anmol Sethi " Previous Maintainer: SungHyun Nam if exists('b:current_syntax') @@ -30,6 +30,7 @@ endif if !exists('b:man_sect') call man#init_pager() endif + if b:man_sect =~# '^[023]' syntax case match syntax include @c $VIMRUNTIME/syntax/c.vim -- cgit From 078f279691f7d2b87128f7abc932634027d6dbd5 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 24 Nov 2019 19:50:08 -0500 Subject: man.vim: Document how to disable bold highlighting Closes #11435 --- runtime/doc/filetype.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index c579c390c6..e46869d46e 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -549,7 +549,9 @@ Variables: *b:man_default_sects* Comma-separated, ordered list of preferred sections. For example in C one usually wants section 3 or 2: > :let b:man_default_sections = '3,2' -*g:man_hardwrap* Hard-wrap to $MANWIDTH. May improve layout. +*g:man_hardwrap* Hard-wrap to $MANWIDTH or window width if $MANWIDTH is + empty. Enabled by default. Disable to enable soft + wrapping. To use Nvim as a manpager: > export MANPAGER='nvim +Man!' @@ -563,6 +565,9 @@ soft wrapped as with `:Man`. You can set in your environment: > So `groff`'s pre-formatting output will be the same as with `:Man` i.e soft-wrapped. +To disable bold highlighting: > + :highlight link manBold Normal + PDF *ft-pdf-plugin* Two maps, and , are provided to simulate a tag stack for navigating -- cgit From 4ce96e497909505b6253e987812e24e8fa08daec Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 24 Nov 2019 19:55:38 -0500 Subject: man.vim: Hard wrap by default Closes #11436 --- runtime/autoload/man.vim | 2 +- runtime/doc/filetype.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 36f42c0003..0777d2202f 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -139,7 +139,7 @@ function! s:get_page(path) abort " Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065). " Soft-wrap: ftplugin/man.vim sets wrap/breakindent/…. " Hard-wrap: driven by `man`. - let manwidth = !get(g:,'man_hardwrap') ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH) + let manwidth = !get(g:,'man_hardwrap', 1) ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH) " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). " http://comments.gmane.org/gmane.editors.vim.devel/29085 " Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index e46869d46e..c649688d99 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -550,7 +550,7 @@ Variables: For example in C one usually wants section 3 or 2: > :let b:man_default_sections = '3,2' *g:man_hardwrap* Hard-wrap to $MANWIDTH or window width if $MANWIDTH is - empty. Enabled by default. Disable to enable soft + empty. Enabled by default. Set |FALSE| to enable soft wrapping. To use Nvim as a manpager: > @@ -560,10 +560,10 @@ Note that when running `man` from the shell and with that `MANPAGER` in your environment, `man` will pre-format the manpage using `groff`. Thus, Neovim will inevitably display the manual page as it was passed to it from stdin. One of the caveats of this is that the width will _always_ be hard-wrapped and not -soft wrapped as with `:Man`. You can set in your environment: > +soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: > export MANWIDTH=999 -So `groff`'s pre-formatting output will be the same as with `:Man` i.e soft-wrapped. +So `groff`'s pre-formatting output will be the same as with `g:man_hardwrap=0` i.e soft-wrapped. To disable bold highlighting: > :highlight link manBold Normal -- cgit From afaa0626430ba0150729a088234698de85174ce6 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 24 Nov 2019 20:10:50 -0500 Subject: man.vim: Update maintainer email --- runtime/ftplugin/man.vim | 2 +- runtime/plugin/man.vim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index b3b23833ba..7c535dc839 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi +" Maintainer: Anmol Sethi " Previous Maintainer: SungHyun Nam if exists('b:did_ftplugin') || &filetype !=# 'man' diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index e18a5528bb..e762eb3664 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -1,4 +1,4 @@ -" Maintainer: Anmol Sethi +" Maintainer: Anmol Sethi if exists('g:loaded_man') finish -- cgit From f460bae441b2a35964c3535396e251c33c997395 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 24 Nov 2019 17:53:33 -0800 Subject: release.sh [ci skip] --- runtime/nvim.appdata.xml | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index ceffc7ba98..025de1b5a9 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ + -- cgit From fd5710ae9a3bcbc0f9cbb71de9e39253350ff09c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 25 Nov 2019 01:08:02 -0800 Subject: doc + extmarks tweaks #11421 - nvim_buf_get_extmarks: rename "amount" => "limit" - rename `set_extmark_index_from_obj` --- runtime/doc/api.txt | 119 +++++++++++++++++++++++++-------------------- runtime/doc/change.txt | 11 ++--- runtime/doc/eval.txt | 8 +-- runtime/doc/index.txt | 6 +-- runtime/doc/intro.txt | 2 +- runtime/doc/lsp.txt | 89 +++++++++++++++++---------------- runtime/doc/lua.txt | 15 ++++-- runtime/doc/map.txt | 2 +- runtime/doc/message.txt | 4 +- runtime/doc/motion.txt | 52 ++++++++++---------- runtime/doc/starting.txt | 2 +- runtime/doc/visual.txt | 8 +-- runtime/lua/vim/shared.lua | 21 ++++---- 13 files changed, 182 insertions(+), 157 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index f97795b0ee..0d040c154b 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -19,6 +19,7 @@ API Usage *api-rpc* *RPC* *rpc* *msgpack-rpc* RPC is the typical way to control Nvim programmatically. Nvim implements the MessagePack-RPC protocol: + https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md https://github.com/msgpack/msgpack/blob/0b8f5ac/spec.md Many clients use the API: user interfaces (GUIs), remote plugins, scripts like @@ -935,8 +936,8 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* 'list' options. 'signcolumn' is changed to `auto` and 'colorcolumn' is cleared. The end-of-buffer region is hidden by setting - `eob` flag of 'fillchars' to a space char, and - clearing the |EndOfBuffer| region in + `eob` flag of 'fillchars' to a space char, + and clearing the |EndOfBuffer| region in 'winhighlight'. Return: ~ @@ -1022,7 +1023,7 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()* {type} Edit behavior: any |getregtype()| result, or: • "b" |blockwise-visual| mode (may include width, e.g. "b3") - • "c" |characterwise| mode + • "c" |charwise| mode • "l" |linewise| mode • "" guess by contents, see |setreg()| {after} Insert after cursor (like |p|), or before (like @@ -1797,81 +1798,93 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}) Returns position for a given extmark id Parameters: ~ - {buffer} The buffer handle - {namespace} a identifier returned previously with - nvim_create_namespace - {id} the extmark id + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ (row, col) tuple or empty list () if extmark id was absent *nvim_buf_get_extmarks()* nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) - List extmarks in a range (inclusive) - - range ends can be specified as (row, col) tuples, as well as - extmark ids in the same namespace. In addition, 0 and -1 works - as shorthands for (0,0) and (-1,-1) respectively, so that all - marks in the buffer can be queried as: + Gets extmarks in "traversal order" from a |charwise| region + defined by buffer positions (inclusive, 0-indexed + |api-indexing|). + + Region can be given as (row,col) tuples, or valid extmark ids + (whose positions define the bounds). 0 and -1 are understood + as (0,0) and (-1,-1) respectively, thus the following are + equivalent: +> + nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) +< - all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + If `end` is less than `start` , traversal works backwards. + (Useful with `limit` , to get the first marks prior to a given + position.) - If end is a lower position than start, then the range will be - traversed backwards. This is mostly useful with limited - amount, to be able to get the first marks prior to a given - position. + Example: +> + local a = vim.api + local pos = a.nvim_win_get_cursor(0) + local ns = a.nvim_create_namespace('my-plugin') + -- Create new extmark at line 1, column 1. + local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + -- Create new extmark at line 3, column 1. + local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) + -- Get extmarks only from line 3. + local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) + -- Get all marks in this buffer + namespace. + local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) + print(vim.inspect(ms)) +< Parameters: ~ - {buffer} The buffer handle - {ns_id} An id returned previously from - nvim_create_namespace - {start} One of: extmark id, (row, col) or 0, -1 for - buffer ends - {end} One of: extmark id, (row, col) or 0, -1 for - buffer ends - {opts} additional options. Supports the keys: - • amount: Maximum number of marks to return + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {start} Start of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {end} End of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {opts} Optional parameters. Keys: + • limit: Maximum number of marks to return Return: ~ - [[extmark_id, row, col], ...] + List of [extmark_id, row, col] tuples in "traversal + order". *nvim_buf_set_extmark()* nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) - Create or update an extmark at a position - - If an invalid namespace is given, an error will be raised. - - To create a new extmark, pass in id=0. The new extmark id will - be returned. To move an existing mark, pass in its id. + Creates or updates an extmark. - It is also allowed to create a new mark by passing in a - previously unused id, but the caller must then keep track of - existing and unused ids itself. This is mainly useful over - RPC, to avoid needing to wait for the return value. + To create a new extmark, pass id=0. The extmark id will be + returned. It is also allowed to create a new mark by passing + in a previously unused id, but the caller must then keep track + of existing and unused ids itself. (Useful over RPC, to avoid + waiting for the return value.) Parameters: ~ - {buffer} The buffer handle - {ns_id} a identifier returned previously with - nvim_create_namespace - {id} The extmark's id or 0 to create a new mark. - {line} The row to set the extmark to. - {col} The column to set the extmark to. + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id, or 0 to create new + {line} Line number where to place the mark + {col} Column where to place the mark {opts} Optional parameters. Currently not used. Return: ~ - the id of the extmark. + Id of the created/updated extmark nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* - Remove an extmark + Removes an extmark. Parameters: ~ - {buffer} The buffer handle - {ns_id} a identifier returned previously with - nvim_create_namespace - {id} The extmarks's id + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ - true on success, false if the extmark was not found. + true if the extmark was found, else false *nvim_buf_add_highlight()* nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, @@ -1916,8 +1929,8 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, *nvim_buf_clear_namespace()* nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) - Clears namespaced objects, highlights and virtual text, from a - line range + Clears namespaced objects (highlights, extmarks, virtual text) + from a region. Lines are 0-indexed. |api-indexing| To clear the namespace in the entire buffer, specify line_start=0 and line_end=-1. diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index bd3f22a371..dcebbc524c 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -90,7 +90,7 @@ start and end of the motion are not in the same line, and there are only blanks before the start and there are no non-blanks after the end of the motion, the delete becomes linewise. This means that the delete also removes the line of blanks that you might expect to remain. Use the |o_v| operator to -force the motion to be characterwise. +force the motion to be charwise. Trying to delete an empty region of text (e.g., "d0" in the first column) is an error when 'cpoptions' includes the 'E' flag. @@ -1074,7 +1074,7 @@ also use these commands to move text from one file to another, because Vim preserves all registers when changing buffers (the CTRL-^ command is a quick way to toggle between two files). - *linewise-register* *characterwise-register* + *linewise-register* *charwise-register* You can repeat the put commands with "." (except for :put) and undo them. If the command that was used to get the text into the register was |linewise|, Vim inserts the text below ("p") or above ("P") the line where the cursor is. @@ -1116,10 +1116,9 @@ this happen. However, if the width of the block is not a multiple of a width and the text after the inserted block contains s, that text may be misaligned. -Note that after a characterwise yank command, Vim leaves the cursor on the -first yanked character that is closest to the start of the buffer. This means -that "yl" doesn't move the cursor, but "yh" moves the cursor one character -left. +Note that after a charwise yank command, Vim leaves the cursor on the first +yanked character that is closest to the start of the buffer. This means that +"yl" doesn't move the cursor, but "yh" moves the cursor one character left. Rationale: In Vi the "y" command followed by a backwards motion would sometimes not move the cursor to the first yanked character, because redisplaying was skipped. In Vim it always moves to diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 84a893a205..f39c40492b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4727,7 +4727,7 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()* getregtype([{regname}]) *getregtype()* The result is a String, which is type of register {regname}. The value will be one of: - "v" for |characterwise| text + "v" for |charwise| text "V" for |linewise| text "{width}" for |blockwise-visual| text "" for an empty or unknown register @@ -6131,7 +6131,7 @@ mode([expr]) Return a string that indicates the current mode. n Normal no Operator-pending - nov Operator-pending (forced characterwise |o_v|) + nov Operator-pending (forced charwise |o_v|) noV Operator-pending (forced linewise |o_V|) noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|) niI Normal using |i_CTRL-O| in |Insert-mode| @@ -7441,7 +7441,7 @@ setreg({regname}, {value} [, {options}]) If {options} contains "a" or {regname} is upper case, then the value is appended. {options} can also contain a register type specification: - "c" or "v" |characterwise| mode + "c" or "v" |charwise| mode "l" or "V" |linewise| mode "b" or "" |blockwise-visual| mode If a number immediately follows "b" or "" then this is @@ -9708,7 +9708,7 @@ This does NOT work: > register, "@/" for the search pattern. If the result of {expr1} ends in a or , the register will be linewise, otherwise it will be set to - characterwise. + charwise. This can be used to clear the last search pattern: > :let @/ = "" < This is different from searching for an empty string, diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 211b7be2cc..ed9853a8da 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -404,7 +404,7 @@ tag char note action in Normal mode ~ |t| t{char} 1 cursor till before Nth occurrence of {char} to the right |u| u 2 undo changes -|v| v start characterwise Visual mode +|v| v start charwise Visual mode |w| w 1 cursor N words forward |x| ["x]x 2 delete N characters under and after the cursor [into register x] @@ -866,7 +866,7 @@ These can be used after an operator, but before a {motion} has been entered. tag char action in Operator-pending mode ~ ----------------------------------------------------------------------- -|o_v| v force operator to work characterwise +|o_v| v force operator to work charwise |o_V| V force operator to work linewise |o_CTRL-V| CTRL-V force operator to work blockwise @@ -978,7 +978,7 @@ tag command note action in Visual mode ~ |v_r| r 2 replace highlighted area with a character |v_s| s 2 delete highlighted area and start insert |v_u| u 2 make highlighted area lowercase -|v_v| v make Visual mode characterwise or stop +|v_v| v make Visual mode charwise or stop Visual mode |v_x| x 2 delete the highlighted area |v_y| y yank the highlighted area diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 3292489eda..c59ed43a47 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -271,7 +271,7 @@ and <> are part of what you type, the context should make this clear. operator is pending. - Ex commands can be used to move the cursor. This can be used to call a function that does some complicated motion. - The motion is always characterwise exclusive, no matter + The motion is always charwise exclusive, no matter what ":" command is used. This means it's impossible to include the last character of a line without the line break (unless 'virtualedit' is set). diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 3de2a9e4e6..c4c164ab6c 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1,18 +1,18 @@ -*lsp.txt* The Language Server Protocol +*lsp.txt* Nvim LSP API - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL -Neovim Language Server Protocol (LSP) API +Nvim Language Server Protocol (LSP) API *lsp* -Neovim exposes a powerful API that conforms to Microsoft's published Language -Server Protocol specification. The documentation can be found here: +Nvim is a client to the Language Server Protocol: https://microsoft.github.io/language-server-protocol/ + Type |gO| to see the table of contents. ================================================================================ - *lsp-api* +LSP API *lsp-api* Neovim exposes a API for the language server protocol. To get the real benefits of this API, a language server must be installed. @@ -261,13 +261,16 @@ vim.lsp.rpc_response_error({code}, [{message}], [{data}]) the server. ================================================================================ - *vim.lsp.default_callbacks* +LSP CALLBACKS *lsp-callbacks* -The |vim.lsp.default_callbacks| table contains the default |lsp-callbacks| -that are used when creating a new client. The keys are the LSP method names. +DEFAULT CALLBACKS ~ + *vim.lsp.default_callbacks* +The `vim.lsp.default_callbacks` table defines default callbacks used when +creating a new client. Keys are LSP method names: > -The following requests and notifications have built-in callbacks defined to -handle the response in an idiomatic way. + :lua print(vim.inspect(vim.tbl_keys(vim.lsp.default_callbacks))) + +These LSP requests/notifications are defined by default: textDocument/publishDiagnostics window/logMessage @@ -290,38 +293,40 @@ Use cases: Any callbacks passed directly to `request` methods on a server client will have the highest precedence, followed by the `default_callbacks`. -More information about callbacks can be found in |lsp-callbacks|. +You can override the default handlers, +- globally: by modifying the `vim.lsp.default_callbacks` table +- per-client: by passing the {callbacks} table parameter to + |vim.lsp.start_client| -================================================================================ - *lsp-callbacks* +Each handler has this signature: > + + function(err, method, params, client_id) Callbacks are functions which are called in a variety of situations by the client. Their signature is `function(err, method, params, client_id)` They can be set by the {callbacks} parameter for |vim.lsp.start_client| or via the |vim.lsp.default_callbacks|. -This will be called for: -- notifications from the server, where `err` will always be `nil` -- requests initiated by the server. The parameter `err` will be `nil` here as - well. - For these, you can respond by returning two values: `result, err` The - err must be in the format of an RPC error, which is - `{ code, message, data? }` - You can use |vim.lsp.rpc_response_error()| to help with creating this object. -- as a callback for requests initiated by the client if the request doesn't - explicitly specify a callback (such as in |vim.lsp.buf_request|). +Handlers are called for: +- Notifications from the server (`err` is always `nil`). +- Requests initiated by the server (`err` is always `nil`). + The handler can respond by returning two values: `result, err` + where `err` must be shaped like an RPC error: + `{ code, message, data? }` + You can use |vim.lsp.rpc_response_error()| to create this object. +- Handling requests initiated by the client if the request doesn't explicitly + specify a callback (such as in |vim.lsp.buf_request|). ================================================================================ - *vim.lsp.protocol* -vim.lsp.protocol +VIM.LSP.PROTOCOL *vim.lsp.protocol* - Contains constants as described in the Language Server Protocol - specification and helper functions for creating protocol related objects. +The `vim.lsp.protocol` module provides constants defined in the LSP +specification, and helper functions for creating protocol-related objects. https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md - Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow - reverse lookup by either the number or string name. +Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow reverse +lookup by either the number or string name. e.g. vim.lsp.protocol.TextDocumentSyncKind.Full == 1 vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" @@ -428,7 +433,7 @@ To configure omnifunc, add the following in your init.vim: > " Configure for python autocmd Filetype python setl omnifunc=v:lua.vim.lsp.omnifunc - + " Or with on_attach start_client { ... @@ -436,12 +441,12 @@ To configure omnifunc, add the following in your init.vim: vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') end; } - + " This is optional, but you may find it useful autocmd CompleteDone * pclose < ================================================================================ - *lsp-vim-functions* +LSP FUNCTIONS *lsp-vim-functions* To use the functions from vim, it is recommended to use |v:lua| to interface with the Lua functions. No direct vim functions are provided, but the @@ -461,7 +466,7 @@ request from lua as follows: nnoremap ;td lua vim.lsp.buf.type_definition() < ================================================================================ - *lsp-advanced-js-example* +LSP EXAMPLE *lsp-advanced-js-example* For more advanced configurations where just filtering by filetype isn't sufficient, you can use the `vim.lsp.start_client()` and @@ -483,7 +488,7 @@ The example will: local stat = vim.loop.fs_stat(filename) return stat and stat.type == 'directory' or false end - + local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" -- Asumes filepath is a file. local function dirname(filepath) @@ -494,11 +499,11 @@ The example will: end) return result, is_changed end - + local function path_join(...) return table.concat(vim.tbl_flatten {...}, path_sep) end - + -- Ascend the buffer's path until we find the rootdir. -- is_root_path is a function which returns bool local function buffer_find_root_dir(bufnr, is_root_path) @@ -520,7 +525,7 @@ The example will: end end end - + -- A table to store our root_dir to client_id lookup. We want one LSP per -- root directory, and this is how we assert that. local javascript_lsps = {} @@ -531,14 +536,14 @@ The example will: ["typescript"] = true; ["typescript.jsx"] = true; } - + -- Create a template configuration for a server to start, minus the root_dir -- which we will specify later. local javascript_lsp_config = { name = "javascript"; cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") }; } - + -- This needs to be global so that we can call it from the autocmd. function check_start_javascript_lsp() local bufnr = vim.api.nvim_get_current_buf() @@ -555,7 +560,7 @@ The example will: end) -- We couldn't find a root directory, so ignore this file. if not root_dir then return end - + -- Check if we have a client alredy or start and store it. local client_id = javascript_lsps[root_dir] if not client_id then @@ -569,7 +574,7 @@ The example will: -- are already attached. vim.lsp.buf_attach_client(bufnr, client_id) end - + vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]] < diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index edcf246295..c0da06ffe3 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -876,16 +876,23 @@ tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* Parameters: ~ {o} table The table to add the reverse to. -list_extend({dst}, {src}) *vim.list_extend()* +list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* Extends a list-like table with the values of another list-like table. + NOTE: This mutates dst! + Parameters: ~ - {dst} The list which will be modified and appended to. - {src} The list from which values will be inserted. + {dst} list which will be modified and appended to. + {src} list from which values will be inserted. + {start} Start index on src. defaults to 1 + {finish} Final index on src. defaults to #src + + Return: ~ + dst See also: ~ - |extend()| + |vim.tbl_extend()| tbl_flatten({t}) *vim.tbl_flatten()* Creates a copy of a list-like table such that any nested diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index abe86749c4..58c0d832e6 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -786,7 +786,7 @@ g@{motion} Call the function set by the 'operatorfunc' option. character of the text. The function is called with one String argument: "line" {motion} was |linewise| - "char" {motion} was |characterwise| + "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. diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index 965b062728..bcfd985e71 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -679,8 +679,8 @@ no argument has been specified. Invalid argument: {arg} Duplicate argument: {arg} -An Ex command or function has been executed, but an invalid argument has been -specified. +Ex command or function has been executed, but an invalid argument was +specified. Or a non-executable command was given to |system()|. *E488* > Trailing characters diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index e93c833c76..07ff4cf030 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -60,11 +60,11 @@ After applying the operator the cursor is mostly left at the start of the text that was operated upon. For example, "yfe" doesn't move the cursor, but "yFe" moves the cursor leftwards to the "e" where the yank started. - *linewise* *characterwise* + *linewise* *charwise* *characterwise* The operator either affects whole lines, or the characters between the start and end position. Generally, motions that move between lines affect lines (are linewise), and motions that move within a line affect characters (are -characterwise). However, there are some exceptions. +charwise). However, there are some exceptions. *exclusive* *inclusive* Character motion is either inclusive or exclusive. When inclusive, the @@ -106,10 +106,10 @@ This cannot be repeated: > d:if 1 call search("f") endif -Note that when using ":" any motion becomes characterwise exclusive. +Note that when using ":" any motion becomes charwise exclusive. *forced-motion* -FORCING A MOTION TO BE LINEWISE, CHARACTERWISE OR BLOCKWISE +FORCING A MOTION TO BE LINEWISE, CHARWISE OR BLOCKWISE When a motion is not of the type you would like to use, you can force another type by using "v", "V" or CTRL-V just after the operator. @@ -121,22 +121,22 @@ deletes from the cursor position until the character below the cursor > dj deletes the character under the cursor and the character below the cursor. > -Be careful with forcing a linewise movement to be used characterwise or -blockwise, the column may not always be defined. +Be careful with forcing a linewise movement to be used charwise or blockwise, +the column may not always be defined. *o_v* v When used after an operator, before the motion command: Force - the operator to work characterwise, also when the motion is + the operator to work charwise, also when the motion is linewise. If the motion was linewise, it will become |exclusive|. - If the motion already was characterwise, toggle + If the motion already was charwise, toggle inclusive/exclusive. This can be used to make an exclusive motion inclusive and an inclusive motion exclusive. *o_V* V When used after an operator, before the motion command: Force the operator to work linewise, also when the motion is - characterwise. + charwise. *o_CTRL-V* CTRL-V When used after an operator, before the motion command: Force @@ -508,36 +508,36 @@ aw "a word", select [count] words (see |word|). Leading or trailing white space is included, but not counted. When used in Visual linewise mode "aw" switches to - Visual characterwise mode. + Visual charwise mode. *v_iw* *iw* iw "inner word", select [count] words (see |word|). White space between words is counted too. When used in Visual linewise mode "iw" switches to - Visual characterwise mode. + Visual charwise mode. *v_aW* *aW* aW "a WORD", select [count] WORDs (see |WORD|). Leading or trailing white space is included, but not counted. When used in Visual linewise mode "aW" switches to - Visual characterwise mode. + Visual charwise mode. *v_iW* *iW* iW "inner WORD", select [count] WORDs (see |WORD|). White space between words is counted too. When used in Visual linewise mode "iW" switches to - Visual characterwise mode. + Visual charwise mode. *v_as* *as* as "a sentence", select [count] sentences (see |sentence|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_is* *is* is "inner sentence", select [count] sentences (see |sentence|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_ap* *ap* ap "a paragraph", select [count] paragraphs (see @@ -558,14 +558,14 @@ a[ "a [] block", select [count] '[' ']' blocks. This goes backwards to the [count] unclosed '[', and finds the matching ']'. The enclosed text is selected, including the '[' and ']'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i] *v_i]* *v_i[* *i]* *i[* i[ "inner [] block", select [count] '[' ']' blocks. This goes backwards to the [count] unclosed '[', and finds the matching ']'. The enclosed text is selected, excluding the '[' and ']'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a) *v_a)* *a)* *a(* a( *vab* *v_ab* *v_a(* *ab* @@ -573,54 +573,54 @@ ab "a block", select [count] blocks, from "[count] [(" to the matching ')', including the '(' and ')' (see |[(|). Does not include white space outside of the parenthesis. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i) *v_i)* *i)* *i(* i( *vib* *v_ib* *v_i(* *ib* ib "inner block", select [count] blocks, from "[count] [(" to the matching ')', excluding the '(' and ')' (see |[(|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a> *v_a>* *v_a<* *a>* *a<* a< "a <> block", select [count] <> blocks, from the [count]'th unmatched '<' backwards to the matching '>', including the '<' and '>'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i> *v_i>* *v_i<* *i>* *i<* i< "inner <> block", select [count] <> blocks, from the [count]'th unmatched '<' backwards to the matching '>', excluding the '<' and '>'. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_at* *at* at "a tag block", select [count] tag blocks, from the [count]'th unmatched "" backwards to the matching "", including the "" and "". See |tag-blocks| about the details. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. *v_it* *it* it "inner tag block", select [count] tag blocks, from the [count]'th unmatched "" backwards to the matching "", excluding the "" and "". See |tag-blocks| about the details. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a} *v_a}* *a}* *a{* a{ *v_aB* *v_a{* *aB* aB "a Block", select [count] Blocks, from "[count] [{" to the matching '}', including the '{' and '}' (see |[{|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. i} *v_i}* *i}* *i{* i{ *v_iB* *v_i{* *iB* iB "inner Block", select [count] Blocks, from "[count] [{" to the matching '}', excluding the '{' and '}' (see |[{|). - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. a" *v_aquote* *aquote* a' *v_a'* *a'* @@ -634,7 +634,7 @@ a` *v_a`* *a`* start of the line. Any trailing white space is included, unless there is none, then leading white space is included. - When used in Visual mode it is made characterwise. + When used in Visual mode it is made charwise. Repeating this object in Visual mode another string is included. A count is currently not used. diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 7dbbb2d424..e3f0d593a7 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1270,7 +1270,7 @@ exactly four MessagePack objects: Key Type Def Description ~ rt UInteger 0 Register type: No Description ~ - 0 |characterwise-register| + 0 |charwise-register| 1 |linewise-register| 2 |blockwise-register| rw UInteger 0 Register width. Only valid diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index ccbbc092ec..0052382044 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -48,7 +48,7 @@ position. ============================================================================== 2. Starting and stopping Visual mode *visual-start* - *v* *characterwise-visual* + *v* *charwise-visual* [count]v Start Visual mode per character. With [count] select the same number of characters or lines as used for the last Visual operation, but at @@ -74,7 +74,7 @@ position. If you use , click the left mouse button or use any command that does a jump to another buffer while in Visual mode, the highlighting stops -and no text is affected. Also when you hit "v" in characterwise Visual mode, +and no text is affected. Also when you hit "v" in charwise Visual mode, "CTRL-V" in blockwise Visual mode or "V" in linewise Visual mode. If you hit CTRL-Z the highlighting stops and the editor is suspended or a new shell is started |CTRL-Z|. @@ -477,7 +477,7 @@ Commands in Select mode: Otherwise, typed characters are handled as in Visual mode. When using an operator in Select mode, and the selection is linewise, the -selected lines are operated upon, but like in characterwise selection. For +selected lines are operated upon, but like in charwise selection. For example, when a whole line is deleted, it can later be pasted in the middle of a line. @@ -510,7 +510,7 @@ gV Avoid the automatic reselection of the Visual area selection. *gh* -gh Start Select mode, characterwise. This is like "v", +gh Start Select mode, charwise. This is like "v", but starts Select mode instead of Visual mode. Mnemonic: "get highlighted". diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 1912d3708d..631dd04c35 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -226,16 +226,17 @@ function vim.tbl_add_reverse_lookup(o) return o end --- Extends a list-like table with the values of another list-like table. --- --- NOTE: This *mutates* dst! --- @see |extend()| --- --- @param dst list which will be modified and appended to. --- @param src list from which values will be inserted. --- @param start Start index on src. defaults to 1 --- @param finish Final index on src. defaults to #src --- @returns dst +--- Extends a list-like table with the values of another list-like table. +--- +--- NOTE: This mutates dst! +--- +--@see |vim.tbl_extend()| +--- +--@param dst list which will be modified and appended to. +--@param src list from which values will be inserted. +--@param start Start index on src. defaults to 1 +--@param finish Final index on src. defaults to #src +--@returns dst function vim.list_extend(dst, src, start, finish) vim.validate { dst = {dst, 't'}; -- cgit From 36d1335a667d54c26ab7cca23bc60998cb8e0fb2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 25 Nov 2019 08:56:42 -0800 Subject: UI: emit mouse_on/mouse_off on attach #11455 closes #11372 --- runtime/doc/develop.txt | 4 ++-- runtime/doc/ui.txt | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index ba887a83c8..4c1430ab1f 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -349,8 +349,8 @@ External UIs are expected to implement these common features: chords ( ) and patterns ("shift shift") that do not potentially conflict with Nvim defaults, plugins, etc. - Consider the "option_set" |ui-global| event as a hint for other GUI - behaviors. UI-related options ('guifont', 'ambiwidth', …) are published in - this event. + behaviors. Various UI-related options ('guifont', 'ambiwidth', …) are + published in this event. See also "mouse_on", "mouse_off". vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index a2f19593ae..d5f4a59ab3 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -201,8 +201,8 @@ the editor. sent from Nvim, like for |ui-cmdline|. ["mode_change", mode, mode_idx] - The mode changed. The first parameter `mode` is a string representing - the current mode. `mode_idx` is an index into the array received in + Editor mode changed. The `mode` parameter is a string representing + the current mode. `mode_idx` is an index into the array emitted in the `mode_info_set` event. UIs should change the cursor style according to the properties specified in the corresponding item. The set of modes reported will change in new versions of Nvim, for @@ -211,11 +211,11 @@ the editor. ["mouse_on"] ["mouse_off"] - Tells the client whether mouse support, as determined by |'mouse'| - option, is considered to be active in the current mode. This is mostly - useful for a terminal frontend, or other situations where Nvim mouse - would conflict with other usages of the mouse. It is safe for a client - to ignore this and always send mouse events. + |'mouse'| was enabled/disabled in the current editor mode. Useful for + a terminal UI, or other situations where Nvim mouse would conflict + with other usages of the mouse. UIs may ignore this and always send + mouse input, because 'mouse' decides the behavior of |nvim_input()| + implicitly. ["busy_start"] ["busy_stop"] -- cgit From 6e8c5779cf960893850501e4871dc9be671db298 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 26 Nov 2019 05:59:40 -0800 Subject: LSP: Move default buf callbacks to vim.lsp.callbacks (#11452) - In the process, refactored focusable_preview to a util function. - Add text for locations_to_items of the current line. - Improve location callback to handle multiple return values by using set_qflist. - Remove update_tagstack and leave note for future travelers. --- runtime/lua/vim/lsp.lua | 6 +- runtime/lua/vim/lsp/buf.lua | 253 +++--------------------------- runtime/lua/vim/lsp/callbacks.lua | 223 ++++++++++++++++++++++++++ runtime/lua/vim/lsp/default_callbacks.lua | 69 -------- runtime/lua/vim/lsp/util.lua | 56 ++++++- 5 files changed, 300 insertions(+), 307 deletions(-) create mode 100644 runtime/lua/vim/lsp/callbacks.lua delete mode 100644 runtime/lua/vim/lsp/default_callbacks.lua (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 928d19177a..82f4fda66f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1,4 +1,4 @@ -local default_callbacks = require 'vim.lsp.default_callbacks' +local default_callbacks = require 'vim.lsp.callbacks' local log = require 'vim.lsp.log' local lsp_rpc = require 'vim.lsp.rpc' local protocol = require 'vim.lsp.protocol' @@ -13,7 +13,7 @@ local validate = vim.validate local lsp = { protocol = protocol; - default_callbacks = default_callbacks; + callbacks = default_callbacks; buf = require'vim.lsp.buf'; util = util; -- Allow raw RPC access. @@ -469,7 +469,7 @@ function lsp.start_client(config) function client.request(method, params, callback) if not callback then callback = resolve_callback(method) - or error(string.format("request callback is empty and no default was found for client %s", client.name)) + or error("not found: request callback for client "..client.name) end local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback) -- TODO keep these checks or just let it go anyway? diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 51295e5570..a6a05fb095 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -3,7 +3,6 @@ local validate = vim.validate local api = vim.api local vfn = vim.fn local util = require 'vim.lsp.util' -local log = require 'vim.lsp.log' local list_extend = vim.list_extend local M = {} @@ -16,270 +15,68 @@ local function npcall(fn, ...) return ok_or_nil(pcall(fn, ...)) end -local function err_message(...) - api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) - api.nvim_command("redraw") -end - -local function find_window_by_var(name, value) - for _, win in ipairs(api.nvim_list_wins()) do - if npcall(api.nvim_win_get_var, win, name) == value then - return win - end - end -end - local function request(method, params, callback) - -- TODO(ashkan) enable this. - -- callback = vim.lsp.default_callbacks[method] or callback validate { method = {method, 's'}; - callback = {callback, 'f'}; + callback = {callback, 'f', true}; } - return vim.lsp.buf_request(0, method, params, function(err, _, result, client_id) - local _ = log.debug() and log.debug("vim.lsp.buf", method, client_id, err, result) - if err then error(tostring(err)) end - return callback(err, method, result, client_id) - end) -end - -local function focusable_preview(method, params, fn) - if npcall(api.nvim_win_get_var, 0, method) then - return api.nvim_command("wincmd p") - end - - local bufnr = api.nvim_get_current_buf() - do - local win = find_window_by_var(method, bufnr) - if win then - api.nvim_set_current_win(win) - api.nvim_command("stopinsert") - return - end - end - return request(method, params, function(_, _, result, _) - -- TODO(ashkan) could show error in preview... - local lines, filetype, opts = fn(result) - if lines then - local _, winnr = util.open_floating_preview(lines, filetype, opts) - api.nvim_win_set_var(winnr, method, bufnr) - end - end) + return vim.lsp.buf_request(0, method, params, callback) end function M.hover() local params = util.make_position_params() - focusable_preview('textDocument/hover', params, function(result) - if not (result and result.contents) then return end - - local markdown_lines = util.convert_input_to_markdown_lines(result.contents) - markdown_lines = util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then - return { 'No information available' } - end - return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) - end) + request('textDocument/hover', params) end function M.peek_definition() local params = util.make_position_params() - request('textDocument/peekDefinition', params, function(_, _, result, _) - if not (result and result[1]) then return end - local loc = result[1] - local bufnr = vim.uri_to_bufnr(loc.uri) or error("couldn't find file "..tostring(loc.uri)) - local start = loc.range.start - local finish = loc.range["end"] - util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) - local headbuf = util.open_floating_preview({"Peek:"}, nil, { - offset_y = -(finish.line - start.line); - width = finish.character - start.character + 2; - }) - -- TODO(ashkan) change highlight group? - api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) - end) + request('textDocument/peekDefinition', params) end -local function update_tagstack() - local bufnr = api.nvim_get_current_buf() - local line = vfn.line('.') - local col = vfn.col('.') - local tagname = vfn.expand('') - local item = { bufnr = bufnr, from = { bufnr, line, col, 0 }, tagname = tagname } - local winid = vfn.win_getid() - local tagstack = vfn.gettagstack(winid) - local action - if tagstack.length == tagstack.curidx then - action = 'r' - tagstack.items[tagstack.curidx] = item - elseif tagstack.length > tagstack.curidx then - action = 'r' - if tagstack.curidx > 1 then - tagstack.items = table.insert(tagstack.items[tagstack.curidx - 1], item) - else - tagstack.items = { item } - end - else - action = 'a' - tagstack.items = { item } - end - tagstack.curidx = tagstack.curidx + 1 - vfn.settagstack(winid, tagstack, action) -end -local function handle_location(result) - -- We can sometimes get a list of locations, so set the first value as the - -- only value we want to handle - -- TODO(ashkan) was this correct^? We could use location lists. - if result[1] ~= nil then - result = result[1] - end - if result.uri == nil then - err_message('[LSP] Could not find a valid location') - return - end - local bufnr = vim.uri_to_bufnr(result.uri) - update_tagstack() - api.nvim_set_current_buf(bufnr) - local row = result.range.start.line - local col = result.range.start.character - local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] - col = #line:sub(1, col) - api.nvim_win_set_cursor(0, {row + 1, col}) - return true -end -local function location_callback(_, method, result) - if result == nil or vim.tbl_isempty(result) then - local _ = log.info() and log.info(method, 'No location found') - return nil - end - return handle_location(result) -end - function M.declaration() local params = util.make_position_params() - request('textDocument/declaration', params, location_callback) + request('textDocument/declaration', params) end function M.definition() local params = util.make_position_params() - request('textDocument/definition', params, location_callback) + request('textDocument/definition', params) end function M.type_definition() local params = util.make_position_params() - request('textDocument/typeDefinition', params, location_callback) + request('textDocument/typeDefinition', params) end function M.implementation() local params = util.make_position_params() - request('textDocument/implementation', params, location_callback) -end - ---- Convert SignatureHelp response to preview contents. --- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp -local function signature_help_to_preview_contents(input) - if not input.signatures then - return - end - --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about - --the active signature and shouldn't rely on a default value. - local contents = {} - local active_signature = input.activeSignature or 0 - -- If the activeSignature is not inside the valid range, then clip it. - if active_signature >= #input.signatures then - active_signature = 0 - end - local signature = input.signatures[active_signature + 1] - if not signature then - return - end - list_extend(contents, vim.split(signature.label, '\n', true)) - if signature.documentation then - util.convert_input_to_markdown_lines(signature.documentation, contents) - end - if input.parameters then - local active_parameter = input.activeParameter or 0 - -- If the activeParameter is not inside the valid range, then clip it. - if active_parameter >= #input.parameters then - active_parameter = 0 - end - local parameter = signature.parameters and signature.parameters[active_parameter] - if parameter then - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] - -- TODO highlight parameter - if parameter.documentation then - util.convert_input_to_markdown_lines(parameter.documentation, contents) - end - end - end - return contents + request('textDocument/implementation', params) end function M.signature_help() local params = util.make_position_params() - focusable_preview('textDocument/signatureHelp', params, function(result) - if not (result and result.signatures and result.signatures[1]) then - return { 'No signature available' } - end - - -- TODO show empty popup when signatures is empty? - local lines = signature_help_to_preview_contents(result) - lines = util.trim_empty_lines(lines) - if vim.tbl_isempty(lines) then - return { 'No signature available' } - end - return lines, util.try_trim_markdown_code_blocks(lines) - end) + request('textDocument/signatureHelp', params) end -- TODO(ashkan) ? function M.completion(context) local params = util.make_position_params() params.context = context - return request('textDocument/completion', params, function(_, _, result) - if vim.tbl_isempty(result or {}) then return end - local row, col = unpack(api.nvim_win_get_cursor(0)) - local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) - local line_to_cursor = line:sub(col+1) - - local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) - vim.fn.complete(col, matches) - end) + return request('textDocument/completion', params) end function M.formatting(options) validate { options = {options, 't', true} } options = vim.tbl_extend('keep', options or {}, { - tabSize = api.nvim_buf_get_option(0, 'tabstop'); - insertSpaces = api.nvim_buf_get_option(0, 'expandtab'); + tabSize = vim.bo.tabstop; + insertSpaces = vim.bo.expandtab; }) local params = { textDocument = { uri = vim.uri_from_bufnr(0) }; options = options; } - return request('textDocument/formatting', params, function(_, _, result) - if not result then return end - util.apply_text_edits(result) - end) + return request('textDocument/formatting', params) end function M.range_formatting(options, start_pos, end_pos) @@ -289,8 +86,8 @@ function M.range_formatting(options, start_pos, end_pos) end_pos = {end_pos, 't', true}; } options = vim.tbl_extend('keep', options or {}, { - tabSize = api.nvim_buf_get_option(0, 'tabstop'); - insertSpaces = api.nvim_buf_get_option(0, 'expandtab'); + tabSize = vim.bo.tabstop; + insertSpaces = vim.bo.expandtab; }) local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>')) @@ -299,10 +96,10 @@ function M.range_formatting(options, start_pos, end_pos) B[1] = B[1] - 1 -- account for encoding. if A[2] > 0 then - A = {A[1], util.character_offset(0, unpack(A))} + A = {A[1], util.character_offset(0, A[1], A[2])} end if B[2] > 0 then - B = {B[1], util.character_offset(0, unpack(B))} + B = {B[1], util.character_offset(0, B[1], B[2])} end local params = { textDocument = { uri = vim.uri_from_bufnr(0) }; @@ -312,10 +109,7 @@ function M.range_formatting(options, start_pos, end_pos) }; options = options; } - return request('textDocument/rangeFormatting', params, function(_, _, result) - if not result then return end - util.apply_text_edits(result) - end) + return request('textDocument/rangeFormatting', params) end function M.rename(new_name) @@ -325,10 +119,7 @@ function M.rename(new_name) new_name = new_name or npcall(vfn.input, "New Name: ") if not (new_name and #new_name > 0) then return end params.newName = new_name - request('textDocument/rename', params, function(_, _, result) - if not result then return end - util.apply_workspace_edit(result) - end) + request('textDocument/rename', params) end function M.references(context) @@ -338,11 +129,7 @@ function M.references(context) includeDeclaration = true; } params[vim.type_idx] = vim.types.dictionary - request('textDocument/references', params, function(_, _, result) - if not result then return end - util.set_qflist(result) - vim.api.nvim_command("copen") - end) + request('textDocument/references', params) end return M diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua new file mode 100644 index 0000000000..4fc3f74519 --- /dev/null +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -0,0 +1,223 @@ +local log = require 'vim.lsp.log' +local protocol = require 'vim.lsp.protocol' +local util = require 'vim.lsp.util' +local vim = vim +local api = vim.api + +local M = {} + +local function err_message(...) + api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) + api.nvim_command("redraw") +end + +M['workspace/applyEdit'] = function(_, _, workspace_edit) + if not workspace_edit then return end + -- TODO(ashkan) Do something more with label? + if workspace_edit.label then + print("Workspace edit", workspace_edit.label) + end + util.apply_workspace_edit(workspace_edit.edit) +end + +M['textDocument/publishDiagnostics'] = function(_, _, result) + if not result then return end + local uri = result.uri + local bufnr = vim.uri_to_bufnr(uri) + if not bufnr then + err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) + return + end + util.buf_clear_diagnostics(bufnr) + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) + util.buf_diagnostics_underline(bufnr, result.diagnostics) + util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + -- util.set_loclist(result.diagnostics) +end + +M['textDocument/references'] = function(_, _, result) + if not result then return end + util.set_qflist(result) + api.nvim_command("copen") + api.nvim_command("wincmd p") +end + +M['textDocument/rename'] = function(_, _, result) + if not result then return end + util.apply_workspace_edit(result) +end + +M['textDocument/rangeFormatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/formatting'] = function(_, _, result) + if not result then return end + util.apply_text_edits(result) +end + +M['textDocument/completion'] = function(_, _, result) + if vim.tbl_isempty(result or {}) then return end + local row, col = unpack(api.nvim_win_get_cursor(0)) + local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) + local line_to_cursor = line:sub(col+1) + + local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) + vim.fn.complete(col, matches) +end + +M['textDocument/hover'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.contents) then + return { 'No information available' } + end + local markdown_lines = util.convert_input_to_markdown_lines(result.contents) + markdown_lines = util.trim_empty_lines(markdown_lines) + if vim.tbl_isempty(markdown_lines) then + return { 'No information available' } + end + return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) + end) +end + +local function location_callback(_, method, result) + if result == nil or vim.tbl_isempty(result) then + local _ = log.info() and log.info(method, 'No location found') + return nil + end + util.jump_to_location(result[1]) + if #result > 1 then + util.set_qflist(result) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end +end + +M['textDocument/declaration'] = location_callback +M['textDocument/definition'] = location_callback +M['textDocument/typeDefinition'] = location_callback +M['textDocument/implementation'] = location_callback + +--- Convert SignatureHelp response to preview contents. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +local function signature_help_to_preview_contents(input) + if not input.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = input.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #input.signatures then + active_signature = 0 + end + local signature = input.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, vim.split(signature.label, '\n', true)) + if signature.documentation then + util.convert_input_to_markdown_lines(signature.documentation, contents) + end + if input.parameters then + local active_parameter = input.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #input.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + util.convert_input_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + +M['textDocument/signatureHelp'] = function(_, method, result) + util.focusable_preview(method, function() + if not (result and result.signatures and result.signatures[1]) then + return { 'No signature available' } + end + -- TODO show popup when signatures is empty? + local lines = signature_help_to_preview_contents(result) + lines = util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then + return { 'No signature available' } + end + return lines, util.try_trim_markdown_code_blocks(lines) + end) +end + +M['textDocument/peekDefinition'] = function(_, _, result, _) + if not (result and result[1]) then return end + local loc = result[1] + local bufnr = vim.uri_to_bufnr(loc.uri) or error("not found: "..tostring(loc.uri)) + local start = loc.range.start + local finish = loc.range["end"] + util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) + local headbuf = util.open_floating_preview({"Peek:"}, nil, { + offset_y = -(finish.line - start.line); + width = finish.character - start.character + 2; + }) + -- TODO(ashkan) change highlight group? + api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) +end + +local function log_message(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + err_message("LSP[", client_name, "] ", message) + else + local message_type_name = protocol.MessageType[message_type] + api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) + end + return result +end + +M['window/showMessage'] = log_message +M['window/logMessage'] = log_message + +-- Add boilerplate error validation and logging for all of these. +for k, fn in pairs(M) do + M[k] = function(err, method, params, client_id) + local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) + if err then + error(tostring(err)) + end + return fn(err, method, params, client_id) + end +end + +return M +-- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/default_callbacks.lua b/runtime/lua/vim/lsp/default_callbacks.lua deleted file mode 100644 index 2a891e7d1d..0000000000 --- a/runtime/lua/vim/lsp/default_callbacks.lua +++ /dev/null @@ -1,69 +0,0 @@ -local log = require 'vim.lsp.log' -local protocol = require 'vim.lsp.protocol' -local util = require 'vim.lsp.util' -local api = vim.api - -local M = {} - -local function err_message(...) - api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) - api.nvim_command("redraw") -end - -M['workspace/applyEdit'] = function(_, _, workspace_edit) - if not workspace_edit then return end - -- TODO(ashkan) Do something more with label? - if workspace_edit.label then - print("Workspace edit", workspace_edit.label) - end - util.apply_workspace_edit(workspace_edit.edit) -end - -M['textDocument/publishDiagnostics'] = function(_, _, result) - if not result then return end - local uri = result.uri - local bufnr = vim.uri_to_bufnr(uri) - if not bufnr then - err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) - return - end - util.buf_clear_diagnostics(bufnr) - util.buf_diagnostics_save_positions(bufnr, result.diagnostics) - util.buf_diagnostics_underline(bufnr, result.diagnostics) - util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) - -- util.set_loclist(result.diagnostics) -end - -local function log_message(_, _, result, client_id) - local message_type = result.type - local message = result.message - local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format("id=%d", client_id) - if not client then - err_message("LSP[", client_name, "] client has shut down after sending the message") - end - if message_type == protocol.MessageType.Error then - err_message("LSP[", client_name, "] ", message) - else - local message_type_name = protocol.MessageType[message_type] - api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message)) - end - return result -end - -M['window/showMessage'] = log_message -M['window/logMessage'] = log_message - --- Add boilerplate error validation and logging for all of these. -for k, fn in pairs(M) do - M[k] = function(err, method, params, client_id) - local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) - if err then - error(tostring(err)) - end - return fn(err, method, params, client_id) - end -end - -return M --- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 2dfcdfc70c..6e0d3fd4ee 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -2,6 +2,7 @@ local protocol = require 'vim.lsp.protocol' local vim = vim local validate = vim.validate local api = vim.api +local list_extend = vim.list_extend local M = {} @@ -10,7 +11,13 @@ local function split_lines(value) return split(value, '\n', true) end -local list_extend = vim.list_extend +local function ok_or_nil(status, ...) + if not status then return end + return ... +end +local function npcall(fn, ...) + return ok_or_nil(pcall(fn, ...)) +end --- Find the longest shared prefix between prefix and word. -- e.g. remove_prefix("123tes", "testing") == "ting" @@ -303,6 +310,50 @@ function M.make_floating_popup_options(width, height, opts) } end +function M.jump_to_location(location) + if location.uri == nil then return end + local bufnr = vim.uri_to_bufnr(location.uri) + -- TODO(ashkan) use tagfunc here to update tagstack. + api.nvim_set_current_buf(bufnr) + local row = location.range.start.line + local col = location.range.start.character + local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] + col = vim.str_byteindex(line, col) + api.nvim_win_set_cursor(0, {row + 1, col}) + return true +end + +local function find_window_by_var(name, value) + for _, win in ipairs(api.nvim_list_wins()) do + if npcall(api.nvim_win_get_var, win, name) == value then + return win + end + end +end + +-- Check if a window with `unique_name` tagged is associated with the current +-- buffer. If not, make a new preview. +-- +-- fn()'s return values will be passed directly to open_floating_preview in the +-- case that a new floating window should be created. +function M.focusable_preview(unique_name, fn) + if npcall(api.nvim_win_get_var, 0, unique_name) then + return api.nvim_command("wincmd p") + end + local bufnr = api.nvim_get_current_buf() + do + local win = find_window_by_var(unique_name, bufnr) + if win then + api.nvim_set_current_win(win) + api.nvim_command("stopinsert") + return + end + end + local pbufnr, pwinnr = M.open_floating_preview(fn()) + api.nvim_win_set_var(pwinnr, unique_name, bufnr) + return pbufnr, pwinnr +end + function M.open_floating_preview(contents, filetype, opts) validate { contents = { contents, 't' }; @@ -609,12 +660,13 @@ function M.locations_to_items(locations) if pos.character > #line then col = #line else - col = vim.str_byteindex(line, pos.character) + col = vim.str_byteindex(line, pos.character) end table.insert(items, { filename = fname, lnum = row + 1, col = col + 1; + text = line; }) end end -- cgit From 49a40f425d88cbbac09fdf442c22f725bffc2249 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 24 Nov 2019 10:59:15 +0100 Subject: options: make 'fillchars' and 'listchars' global-local These options were previously global. A global-local window option behaves closer to a global option "per default" (i e with :set), but still supports local behavior via :setl Also this restores back-compat for nvim_set_option("fcs", ...) which are currently broken on 0.4.x but worked in earlier versions --- runtime/doc/options.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 386fcdf8c0..209b8a7aee 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2362,7 +2362,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fillchars'* *'fcs'* 'fillchars' 'fcs' string (default "") - local to window + global or local to window |global-local| Characters to fill the statuslines and vertical separators. It is a comma separated list of items: @@ -3671,7 +3671,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'listchars'* *'lcs'* 'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+" Vi default: "eol:$") - local to window + global or local to window |global-local| Strings to use in 'list' mode and for the |:list| command. It is a comma separated list of string settings. -- cgit From d697690e24ae5ea9dc5189de504b03823f7fb0f4 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 26 Nov 2019 19:49:29 -0500 Subject: vim-patch:8.1.2345: .cjs files are not recognized as Javascript Problem: .cjs files are not recognized as Javascript. Solution: Add the *.cjs pattern. (closes vim/vim#5268) https://github.com/vim/vim/commit/c1faf3dc3879e8a5e486f31445b5a5753dcbc6a3 --- runtime/filetype.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 8ce45b6a50..e27d234a96 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar -" Last Change: 2019 Aug 26 +" Last Change: 2019 Nov 26 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -796,8 +796,8 @@ au BufNewFile,BufRead *.java,*.jav setf java " JavaCC au BufNewFile,BufRead *.jj,*.jjt setf javacc -" JavaScript, ECMAScript -au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs setf javascript +" JavaScript, ECMAScript, ES module script, CommonJS script +au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs,*.cjs setf javascript " JavaScript with React au BufNewFile,BufRead *.jsx setf javascriptreact -- cgit From 33beeed4d9c2bda79d48a58ec10e8b05e7de5122 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 28 Nov 2019 05:01:04 +0000 Subject: man.vim: Improve ft=man 'iskeyword' #11457 This addresses a minor quality problem with the recent `'tagfunc'` changes for `man.vim` (see [link]). Currently, with the cursor on a parenthese, hitting `K` will jump us to the man page of the next mentioned entry, instead of the one to which the parenthese (or section number) belongs. ``` pcrepattern(3), terminfo(5), glob(7), regex(7). e.g. ^ e.g. ^ ``` Adding the parentheses to `'iskeyword'` means we correctly handle these cases too. [link]: https://github.com/neovim/neovim/pull/11280#discussion_r348342357 --- runtime/autoload/man.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 809e4a19d8..e4c0080ae9 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -151,6 +151,8 @@ function! s:put_page(page) abort setlocal modifiable setlocal noreadonly setlocal noswapfile + " git-ls-files(1) is all one keyword/tag-target + setlocal iskeyword+=(,) silent keepjumps %delete _ silent put =a:page while getline(1) =~# '^\s*$' -- cgit From 001e69cd4602e84219fd7cfd8ade62f0cb24097c Mon Sep 17 00:00:00 2001 From: Brian Wignall Date: Tue, 26 Nov 2019 07:15:14 -0500 Subject: doc: fix typos close #11459 --- runtime/tutor/tutor.tutor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/tutor/tutor.tutor b/runtime/tutor/tutor.tutor index c937bd686a..b46fcc4836 100644 --- a/runtime/tutor/tutor.tutor +++ b/runtime/tutor/tutor.tutor @@ -118,7 +118,7 @@ and are hidden by default. Links to them look like \[label\]\(\*anchor\*\) -6. Add the appropiate link: +6. Add the appropriate link: A link to the Links section A link to the [Links](*links*) section -- cgit From 7fef0f8db4e04b077e7cd4f195c78ea3fc4c691c Mon Sep 17 00:00:00 2001 From: Von Random Date: Thu, 28 Nov 2019 09:57:11 +0300 Subject: runtime: russian-jcukenwintype.vim keymap #11461 Author: Denis Proskurin --- runtime/keymap/russian-jcukenwintype.vim | 112 +++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 runtime/keymap/russian-jcukenwintype.vim (limited to 'runtime') diff --git a/runtime/keymap/russian-jcukenwintype.vim b/runtime/keymap/russian-jcukenwintype.vim new file mode 100644 index 0000000000..25d6286e24 --- /dev/null +++ b/runtime/keymap/russian-jcukenwintype.vim @@ -0,0 +1,112 @@ +" Vim Keymap file for russian characters, layout 'jcuken', MS Windows +" Typewriter variant (slightly incompatible with XFree86 'ru' keymap - +" makes use of NUMERO SIGN) +" Useful mainly with utf-8 but may work with other encodings + +" Derived from russian-jcuken.vim by Artem Chuprina +" Typewriter variant of standart russian layout +" Maintainer: Denis Proskurin +" Last Changed: 2015 May 15 + +" All characters are given literally, conversion to another encoding (e.g., +" UTF-8) should work. + +scriptencoding utf-8 + +let b:keymap_name = "ru" + +loadkeymap +F А CYRILLIC CAPITAL LETTER A +< Б CYRILLIC CAPITAL LETTER BE +D В CYRILLIC CAPITAL LETTER VE +U Г CYRILLIC CAPITAL LETTER GHE +L Д CYRILLIC CAPITAL LETTER DE +T Е CYRILLIC CAPITAL LETTER IE +? Ё CYRILLIC CAPITAL LETTER IO +: Ж CYRILLIC CAPITAL LETTER ZHE +P З CYRILLIC CAPITAL LETTER ZE +B И CYRILLIC CAPITAL LETTER I +Q Й CYRILLIC CAPITAL LETTER SHORT I +R К CYRILLIC CAPITAL LETTER KA +K Л CYRILLIC CAPITAL LETTER EL +V М CYRILLIC CAPITAL LETTER EM +Y Н CYRILLIC CAPITAL LETTER EN +J О CYRILLIC CAPITAL LETTER O +G П CYRILLIC CAPITAL LETTER PE +H Р CYRILLIC CAPITAL LETTER ER +C С CYRILLIC CAPITAL LETTER ES +N Т CYRILLIC CAPITAL LETTER TE +E У CYRILLIC CAPITAL LETTER U +A Ф CYRILLIC CAPITAL LETTER EF +{ Х CYRILLIC CAPITAL LETTER HA +W Ц CYRILLIC CAPITAL LETTER TSE +X Ч CYRILLIC CAPITAL LETTER CHE +I Ш CYRILLIC CAPITAL LETTER SHA +O Щ CYRILLIC CAPITAL LETTER SHCHA +} Ъ CYRILLIC CAPITAL LETTER HARD SIGN +S Ы CYRILLIC CAPITAL LETTER YERU +M Ь CYRILLIC CAPITAL LETTER SOFT SIGN +\" Э CYRILLIC CAPITAL LETTER E +> Ю CYRILLIC CAPITAL LETTER YU +Z Я CYRILLIC CAPITAL LETTER YA +f а CYRILLIC SMALL LETTER A +, б CYRILLIC SMALL LETTER BE +d в CYRILLIC SMALL LETTER VE +u г CYRILLIC SMALL LETTER GHE +l д CYRILLIC SMALL LETTER DE +t е CYRILLIC SMALL LETTER IE +/ ё CYRILLIC SMALL LETTER IO +; ж CYRILLIC SMALL LETTER ZHE +p з CYRILLIC SMALL LETTER ZE +b и CYRILLIC SMALL LETTER I +q й CYRILLIC SMALL LETTER SHORT I +r к CYRILLIC SMALL LETTER KA +k л CYRILLIC SMALL LETTER EL +v м CYRILLIC SMALL LETTER EM +y н CYRILLIC SMALL LETTER EN +j о CYRILLIC SMALL LETTER O +g п CYRILLIC SMALL LETTER PE +h р CYRILLIC SMALL LETTER ER +c с CYRILLIC SMALL LETTER ES +n т CYRILLIC SMALL LETTER TE +e у CYRILLIC SMALL LETTER U +a ф CYRILLIC SMALL LETTER EF +[ х CYRILLIC SMALL LETTER HA +w ц CYRILLIC SMALL LETTER TSE +x ч CYRILLIC SMALL LETTER CHE +i ш CYRILLIC SMALL LETTER SHA +o щ CYRILLIC SMALL LETTER SHCHA +] ъ CYRILLIC SMALL LETTER HARD SIGN +s ы CYRILLIC SMALL LETTER YERU +m ь CYRILLIC SMALL LETTER SOFT SIGN +' э CYRILLIC SMALL LETTER E +. ю CYRILLIC SMALL LETTER YU +z я CYRILLIC SMALL LETTER YA +` | +1 № +2 - +3 / +4 " +5 : +6 , +7 . +8 _ +9 ? +0 % +- ! += ; +~ + +! 1 +@ 2 +# 3 +$ 4 +% 5 +^ 6 +& 7 +* 8 +( 9 +) 0 +_ = ++ \\ +\\ ) +\| ( -- cgit From 950690e48a6ff4788b3ad6c23caa99d253facd57 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 27 Nov 2019 19:45:03 +0100 Subject: lsp: allow the user to config LspDiagnosticError etc by standard means --- runtime/lua/vim/lsp/util.lua | 71 ++++++++++---------------------------------- 1 file changed, 15 insertions(+), 56 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6e0d3fd4ee..b9990ed082 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -440,6 +440,11 @@ do local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") + local underline_highlight_name = "LspDiagnosticsUnderline" + api.nvim_command(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) + + local severity_highlights = {} + local default_severity_highlight = { [protocol.DiagnosticSeverity.Error] = { guifg = "Red" }; [protocol.DiagnosticSeverity.Warning] = { guifg = "Orange" }; @@ -447,60 +452,17 @@ do [protocol.DiagnosticSeverity.Hint] = { guifg = "LightGrey" }; } - local underline_highlight_name = "LspDiagnosticsUnderline" - api.nvim_command(string.format("highlight %s gui=underline cterm=underline", underline_highlight_name)) - - local function find_color_rgb(color) - local rgb_hex = api.nvim_get_color_by_name(color) - validate { color = {color, function() return rgb_hex ~= -1 end, "valid color name"} } - return rgb_hex - end - - --- Determine whether to use black or white text - -- Ref: https://stackoverflow.com/a/1855903/837964 - -- https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color - local function color_is_bright(r, g, b) - -- Counting the perceptive luminance - human eye favors green color - local luminance = (0.299*r + 0.587*g + 0.114*b)/255 - if luminance > 0.5 then - return true -- Bright colors, black font - else - return false -- Dark colors, white font - end - end - - local severity_highlights = {} - - function M.set_severity_highlights(highlights) - validate {highlights = {highlights, 't'}} - for severity, default_color in pairs(default_severity_highlight) do - local severity_name = protocol.DiagnosticSeverity[severity] - local highlight_name = "LspDiagnostics"..severity_name - local hi_info = highlights[severity] or default_color - -- Try to fill in the foreground color with a sane default. - if not hi_info.guifg and hi_info.guibg then - -- TODO(ashkan) move this out when bitop is guaranteed to be included. - local bit = require 'bit' - local band, rshift = bit.band, bit.rshift - local rgb = find_color_rgb(hi_info.guibg) - local is_bright = color_is_bright(rshift(rgb, 16), band(rshift(rgb, 8), 0xFF), band(rgb, 0xFF)) - hi_info.guifg = is_bright and "Black" or "White" - end - if not hi_info.ctermfg and hi_info.ctermbg then - -- TODO(ashkan) move this out when bitop is guaranteed to be included. - local bit = require 'bit' - local band, rshift = bit.band, bit.rshift - local rgb = find_color_rgb(hi_info.ctermbg) - local is_bright = color_is_bright(rshift(rgb, 16), band(rshift(rgb, 8), 0xFF), band(rgb, 0xFF)) - hi_info.ctermfg = is_bright and "Black" or "White" - end - local cmd_parts = {"highlight", highlight_name} - for k, v in pairs(hi_info) do - table.insert(cmd_parts, k.."="..v) - end - api.nvim_command(table.concat(cmd_parts, ' ')) - severity_highlights[severity] = highlight_name + -- Initialize default severity highlights + for severity, hi_info in pairs(default_severity_highlight) do + local severity_name = protocol.DiagnosticSeverity[severity] + local highlight_name = "LspDiagnostics"..severity_name + -- Try to fill in the foreground color with a sane default. + local cmd_parts = {"highlight", "default", highlight_name} + for k, v in pairs(hi_info) do + table.insert(cmd_parts, k.."="..v) end + api.nvim_command(table.concat(cmd_parts, ' ')) + severity_highlights[severity] = highlight_name end function M.buf_clear_diagnostics(bufnr) @@ -509,9 +471,6 @@ do api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) end - -- Initialize with the defaults. - M.set_severity_highlights(default_severity_highlight) - function M.get_severity_highlight_name(severity) return severity_highlights[severity] end -- cgit From ee1199eaba7cafa6b60611ebece1407bc274e330 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 28 Nov 2019 22:04:40 +0000 Subject: man.vim: remove K mapping #11472 Since #11457 this mapping is no longer necessary. 'keywordprg' defaults to :Man in options.lua --- runtime/ftplugin/man.vim | 1 - 1 file changed, 1 deletion(-) (limited to 'runtime') diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 7c535dc839..081181cfe9 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -26,7 +26,6 @@ if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') nnoremap j gj nnoremap k gk nnoremap gO :call man#show_toc() - nnoremap K :Man if 1 == bufnr('%') || s:pager nnoremap q :lclose:q else -- cgit From f33371c03f526ecbe2d6a1bec744fa37c1b2640c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 29 Nov 2019 18:51:25 +0100 Subject: vim-patch:8.1.2017: cannot execute commands after closing cmdline window #11479 Problem: Cannot execute commands after closing the cmdline window. Solution: Also trigger BufEnter and WinEnter. (closes vim/vim#4762) https://github.com/vim/vim/commit/96e38a86a710fb6daec4550ac1667f019dc3a40e Fixes https://github.com/neovim/neovim/issues/11279. --- runtime/doc/autocmd.txt | 6 ++---- runtime/doc/cmdline.txt | 8 +++----- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 34ea083f96..18dfa587e8 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -559,16 +559,14 @@ CmdlineLeave Before leaving the command-line (including *CmdwinEnter* CmdwinEnter After entering the command-line window. Useful for setting options specifically for - this special type of window. This is - triggered _instead_ of BufEnter and WinEnter. + this special type of window. is set to a single character, indicating the type of command-line. |cmdwin-char| *CmdwinLeave* CmdwinLeave Before leaving the command-line window. Useful to clean up any global setting done - with CmdwinEnter. This is triggered _instead_ - of BufLeave and WinLeave. + with CmdwinEnter. is set to a single character, indicating the type of command-line. |cmdwin-char| diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index ee1f76e4e4..b31177ce0e 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1122,11 +1122,9 @@ edited as described in |cmdwin-char|. AUTOCOMMANDS -Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. Since this -window is of a special type, the WinEnter, WinLeave, BufEnter and BufLeave -events are not triggered. You can use the Cmdwin events to do settings -specifically for the command-line window. Be careful not to cause side -effects! +Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. You can use +the Cmdwin events to do settings specifically for the command-line window. +Be careful not to cause side effects! Example: > :au CmdwinEnter : let b:cpt_save = &cpt | set cpt=. :au CmdwinLeave : let &cpt = b:cpt_save -- cgit From 0a9ecf460a33ad1ee2b756d2ef0417e99bdfb988 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 29 Nov 2019 18:57:27 +0100 Subject: vim-patch:8.1.2315: switchbuf=uselast #11480 Problem: Not always using the right window when jumping to an error. Solution: Add the "uselast" flag in 'switchbuf'. (closes vim/vim#1652) https://github.com/vim/vim/commit/539aa6b25eaea91dfd1a175cd053c0f259fa2e58 --- runtime/doc/options.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 209b8a7aee..e52fc2a2f5 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6014,6 +6014,8 @@ A jump table for the options with a short description can be found at |Q_op|. vsplit Just like "split" but split vertically. newtab Like "split", but open a new tab page. Overrules "split" when both are present. + uselast If included, jump to the previously used window when + jumping to errors with |quickfix| commands. *'synmaxcol'* *'smc'* 'synmaxcol' 'smc' number (default 3000) -- cgit From 6c6afbcddd05336b7a6d98e5025c9c8206d4f903 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 30 Nov 2019 05:52:24 -0500 Subject: doc: mention OS pseudo-features in :h feature-list --- runtime/doc/eval.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index f39c40492b..ab65cc4560 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4969,9 +4969,11 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The < *feature-list* List of supported pseudo-feature names: acl |ACL| support + bsd BSD system (not macOS, use "mac" for that). iconv Can use |iconv()| for conversion. +shellslash Can use backslashes in filenames (Windows) clipboard |clipboard| provider is available. + mac MacOS system. nvim This is Nvim. python2 Legacy Vim |python2| interface. |has-python| python3 Legacy Vim |python3| interface. |has-python| @@ -4981,6 +4983,7 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The unix Unix system. *vim_starting* True during |startup|. win32 Windows system (32 or 64 bit). + win64 Windows system (64 bit). wsl WSL (Windows Subsystem for Linux) system *has-patch* -- cgit From 70b606166640d043fc7b78a52b89ff1bba798b6a Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 1 Dec 2019 05:32:55 -0800 Subject: Add vim.startswith and vim.endswith (#11248) --- runtime/lua/vim/shared.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 631dd04c35..b5b04d7757 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -320,6 +320,26 @@ function vim.pesc(s) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end +--- Test if `prefix` is a prefix of `s` for strings. +-- +-- @param s String to check +-- @param prefix Potential prefix +-- @return boolean True if prefix is a prefix of s +function vim.startswith(s, prefix) + vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } + return s:sub(1, #prefix) == prefix +end + +--- Test if `suffix` is a suffix of `s` for strings. +-- +-- @param s String to check +-- @param suffix Potential suffix +-- @return boolean True if suffix is a suffix of s +function vim.endswith(s, suffix) + vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } + return #suffix == 0 or s:sub(-#suffix) == suffix +end + --- Validates a parameter specification (types and values). --- --- Usage example: -- cgit From c34130d13a842ae0c0c1724d05800a954547d327 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 1 Dec 2019 22:43:16 -0800 Subject: API: deprecate nvim_command_output --- runtime/doc/api.txt | 35 +++++++++++++++++++++++------------ runtime/doc/deprecated.txt | 1 + 2 files changed, 24 insertions(+), 12 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 0d040c154b..afb5630b50 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -479,6 +479,24 @@ created for extmark changes. ============================================================================== Global Functions *api-global* +nvim_exec({src}, {output}) *nvim_exec()* + Executes Vimscript (multiline block of Ex-commands), like + anonymous |:source|. + + Optionally returns (non-error, non-shell |:!|) output. + + On execution error: fails with VimL error, does not update + v:errmsg. + + Parameters: ~ + {src} Vimscript code + {output} Capture and return all (non-error, non-shell + |:!|) output + + See also: ~ + |execute()| + |nvim_command()| + nvim_command({command}) *nvim_command()* Executes an ex-command. @@ -488,6 +506,9 @@ nvim_command({command}) *nvim_command()* Parameters: ~ {command} Ex-command string + See also: ~ + |nvim_exec()| + nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()* Gets a highlight definition by name. @@ -609,19 +630,9 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) replace_termcodes cpoptions -nvim_command_output({command}) *nvim_command_output()* - Executes an ex-command and returns its (non-error) output. - Shell |:!| output is not captured. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {command} Ex-command string - nvim_eval({expr}) *nvim_eval()* - Evaluates a VimL expression (:help expression). Dictionaries - and Lists are recursively expanded. + Evaluates a VimL |expression|. Dictionaries and Lists are + recursively expanded. On execution error: fails with VimL error, does not update v:errmsg. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index b76a37810c..1f9248a130 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -14,6 +14,7 @@ updated. API ~ *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. +*nvim_command_output()* Use |nvim_exec()| instead. Commands ~ *:rv* -- cgit From a3b6c2a3dc5576db45fe4e893cfb8482af591c92 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 2 Dec 2019 00:46:46 -0800 Subject: API: rename nvim_execute_lua => nvim_exec_lua - We already find ourselves renaming nvim_execute_lua in tests and scripts, which suggests "exec" is the verb we actually want. - Add "exec" verb to `:help dev-api`. --- runtime/doc/api.txt | 9 +++++++-- runtime/doc/deprecated.txt | 1 + runtime/doc/develop.txt | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index afb5630b50..d52a9a8409 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -483,7 +483,8 @@ nvim_exec({src}, {output}) *nvim_exec()* Executes Vimscript (multiline block of Ex-commands), like anonymous |:source|. - Optionally returns (non-error, non-shell |:!|) output. + Unlike |nvim_command()| this function supports heredocs, + script-scope (s:), etc. On execution error: fails with VimL error, does not update v:errmsg. @@ -493,6 +494,10 @@ nvim_exec({src}, {output}) *nvim_exec()* {output} Capture and return all (non-error, non-shell |:!|) output + Return: ~ + Output (non-error, non-shell |:!|) if `output` is true, + else empty string. + See also: ~ |execute()| |nvim_command()| @@ -643,7 +648,7 @@ nvim_eval({expr}) *nvim_eval()* Return: ~ Evaluation result or expanded object -nvim_execute_lua({code}, {args}) *nvim_execute_lua()* +nvim_exec_lua({code}, {args}) *nvim_exec_lua()* Execute Lua code. Parameters (if any) are available as `...` inside the chunk. The chunk can return a value. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 1f9248a130..7c6b9ad1d3 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -15,6 +15,7 @@ updated. API ~ *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. *nvim_command_output()* Use |nvim_exec()| instead. +*nvim_execute_lua()* Use |nvim_exec_lua()| instead. Commands ~ *:rv* diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 4c1430ab1f..09c5b7c4ad 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -236,10 +236,11 @@ with a {thing} that groups functions under a common concept). Use existing common {action} names if possible: add Append to, or insert into, a collection - get Get a thing (or group of things by query) - set Set a thing (or group of things) del Delete a thing (or group of things) + exec Execute code + get Get a thing (or group of things by query) list Get all things + set Set a thing (or group of things) Use consistent names for {thing} in all API functions. E.g. a buffer is called "buf" everywhere, not "buffer" in some places and "buf" in others. -- cgit From 3beef8ee1ca9699d75b8877ec9b77493b58b0ed4 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Tue, 1 Oct 2019 23:14:00 +0900 Subject: defaults: set nostartofline Having the cursor change column can be surprising. Force startofline in functional and old tests. Remove the functional breakindent test, as it's a subset of the oldtest one. --- runtime/doc/options.txt | 2 +- runtime/doc/vim_diff.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 386fcdf8c0..30aebfc5ff 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5746,7 +5746,7 @@ A jump table for the options with a short description can be found at |Q_op|. current one. |:vsplit| *'startofline'* *'sol'* *'nostartofline'* *'nosol'* -'startofline' 'sol' boolean (default on) +'startofline' 'sol' boolean (default off) global When "on" the commands listed below move the cursor to the first non-blank of the line. When off the cursor is kept in the same column diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 4267aefbbf..3646034dba 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -55,6 +55,7 @@ the differences. - 'showcmd' is enabled - 'sidescroll' defaults to 1 - 'smarttab' is enabled +- 'startofline' is disabled - 'tabpagemax' defaults to 50 - 'tags' defaults to "./tags;,tags" - 'ttimeoutlen' defaults to 50 -- cgit From fbae9f854153acc15a36015f0f1985749751e5fe Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Fri, 6 Dec 2019 07:14:54 +0100 Subject: runtime/syntax/vim.vim: highlight "blend" keyword #11520 --- runtime/syntax/vim.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index b177fe641d..6f0818c845 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -558,7 +558,7 @@ syn match vimHiGuiFontname contained "'[a-zA-Z\-* ]\+'" syn match vimHiGuiRgb contained "#\x\{6}" " Highlighting: hi group key=arg ... {{{2 -syn cluster vimHiCluster contains=vimGroup,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation +syn cluster vimHiCluster contains=vimGroup,vimHiBlend,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation syn region vimHiKeyList contained oneline start="\i\+" skip="\\\\\|\\|" end="$\||" contains=@vimHiCluster if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_vimhikeyerror") syn match vimHiKeyError contained "\i\+="he=e-1 @@ -571,6 +571,7 @@ syn match vimHiGui contained "\cgui="he=e-1 nextgroup=vimHiAttribList syn match vimHiGuiFont contained "\cfont="he=e-1 nextgroup=vimHiFontname syn match vimHiGuiFgBg contained "\cgui\%([fb]g\|sp\)="he=e-1 nextgroup=vimHiGroup,vimHiGuiFontname,vimHiGuiRgb,vimFgBgAttrib syn match vimHiTermcap contained "\S\+" contains=vimNotation +syn match vimHiBlend contained "\cblend="he=e-1 nextgroup=vimHiNmbr syn match vimHiNmbr contained '\d\+' " Highlight: clear {{{2 @@ -850,6 +851,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimGroupSpecial Special hi def link vimGroup Type hi def link vimHiAttrib PreProc + hi def link vimHiBlend vimHiTerm hi def link vimHiClear vimHighlight hi def link vimHiCtermFgBg vimHiTerm hi def link vimHiCTerm vimHiTerm -- cgit From 30ed245d008045a505a87ab21d22ada45dd1f4b5 Mon Sep 17 00:00:00 2001 From: Jakub Łuczyński Date: Sat, 7 Dec 2019 12:34:22 +0100 Subject: LSP: Add jump when calling gotodef (#11521) --- runtime/lua/vim/lsp/util.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b9990ed082..3798b30a46 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -313,6 +313,8 @@ end function M.jump_to_location(location) if location.uri == nil then return end local bufnr = vim.uri_to_bufnr(location.uri) + -- Save position in jumplist + vim.cmd "normal! m'" -- TODO(ashkan) use tagfunc here to update tagstack. api.nvim_set_current_buf(bufnr) local row = location.range.start.line -- cgit From 6a4bdeb572bef8592561a3947305b6daf6857b1b Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 7 Dec 2019 17:38:34 -0500 Subject: vim-patch:8.1.2408: syntax menu and build instructions outdated Problem: Syntax menu and build instructions outdated. Solution: Update build instructions and syntax menu. https://github.com/vim/vim/commit/bfd34333302c0b0115ab2a9c995b5adbdb8f4ef8 --- runtime/makemenu.vim | 15 +- runtime/synmenu.vim | 421 ++++++++++++++++++++++++++------------------------- 2 files changed, 229 insertions(+), 207 deletions(-) (limited to 'runtime') diff --git a/runtime/makemenu.vim b/runtime/makemenu.vim index ff29d59063..27fa46fe25 100644 --- a/runtime/makemenu.vim +++ b/runtime/makemenu.vim @@ -1,6 +1,6 @@ " Script to define the syntax menu in synmenu.vim " Maintainer: Bram Moolenaar -" Last Change: 2018 May 17 +" Last Change: 2019 Dec 07 " This is used by "make menu" in the src directory. edit :p:h/synmenu.vim @@ -101,6 +101,7 @@ SynMenu AB.AYacc:ayacc SynMenu AB.B:b SynMenu AB.Baan:baan +SynMenu AB.Bash:bash SynMenu AB.Basic.FreeBasic:freebasic SynMenu AB.Basic.IBasic:ibasic SynMenu AB.Basic.QBasic:basic @@ -128,8 +129,9 @@ SynMenu C.Century\ Term:cterm SynMenu C.CH\ script:ch SynMenu C.ChaiScript:chaiscript SynMenu C.ChangeLog:changelog -SynMenu C.Cheetah\ template:cheetah SynMenu C.CHILL:chill +SynMenu C.Cheetah\ template:cheetah +SynMenu C.Chicken:chicken SynMenu C.ChordPro:chordpro SynMenu C.Clean:clean SynMenu C.Clever:cl @@ -160,6 +162,7 @@ SynMenu C.Cyn++:cynpp SynMenu C.Cynlib:cynlib SynMenu DE.D:d +SynMenu DE.Dart:dart SynMenu DE.Datascript:datascript SynMenu DE.Debian.Debian\ ChangeLog:debchangelog SynMenu DE.Debian.Debian\ Control:debcontrol @@ -192,12 +195,14 @@ SynMenu DE.DTD:dtd SynMenu DE.DTML\ (Zope):dtml SynMenu DE.DTrace:dtrace SynMenu DE.Dts/dtsi:dts +SynMenu DE.Dune:dune SynMenu DE.Dylan.Dylan:dylan SynMenu DE.Dylan.Dylan\ interface:dylanintr SynMenu DE.Dylan.Dylan\ lid:dylanlid SynMenu DE.EDIF:edif SynMenu DE.Eiffel:eiffel +SynMenu DE.Eight:8th SynMenu DE.Elinks\ config:elinks SynMenu DE.Elm\ filter\ rules:elmfilt SynMenu DE.Embedix\ Component\ Description:ecd @@ -307,6 +312,7 @@ SynMenu HIJK.Java.JavaCC:javacc SynMenu HIJK.Java.Java\ Server\ Pages:jsp SynMenu HIJK.Java.Java\ Properties:jproperties SynMenu HIJK.JavaScript:javascript +SynMenu HIJK.JavaScriptReact:javascriptreact SynMenu HIJK.Jess:jess SynMenu HIJK.Jgraph:jgraph SynMenu HIJK.Jovial:jovial @@ -365,6 +371,7 @@ SynMenu M.Mathematica:mma SynMenu M.Matlab:matlab SynMenu M.Maxima:maxima SynMenu M.MEL\ (for\ Maya):mel +SynMenu M.Meson:meson SynMenu M.Messages\ (/var/log):messages SynMenu M.Metafont:mf SynMenu M.MetaPost:mp @@ -467,6 +474,7 @@ SynMenu R.R.R\ help:rhelp SynMenu R.R.R\ noweb:rnoweb SynMenu R.Racc\ input:racc SynMenu R.Radiance:radiance +SynMenu R.Raml:raml SynMenu R.Ratpoison:ratpoison SynMenu R.RCS.RCS\ log\ output:rcslog SynMenu R.RCS.RCS\ file:rcs @@ -609,6 +617,8 @@ SynMenu T.Trustees:trustees SynMenu T.TSS.Command\ Line:tsscl SynMenu T.TSS.Geometry:tssgm SynMenu T.TSS.Optics:tssop +SynMenu T.Typescript:typescript +SynMenu T.TypescriptReact:typescriptreact SynMenu UV.Udev\ config:udevconf SynMenu UV.Udev\ permissions:udevperm @@ -637,6 +647,7 @@ SynMenu UV.VSE\ JCL:vsejcl SynMenu WXYZ.WEB.CWEB:cweb SynMenu WXYZ.WEB.WEB:web SynMenu WXYZ.WEB.WEB\ Changes:change +SynMenu WXYZ.WebAssembly:wast SynMenu WXYZ.Webmacro:webmacro SynMenu WXYZ.Website\ MetaLanguage:wml SynMenu WXYZ.wDiff:wdiff diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim index 70e0e83ba7..3367c68d3b 100644 --- a/runtime/synmenu.vim +++ b/runtime/synmenu.vim @@ -87,19 +87,20 @@ an 50.10.580 &Syntax.AB.Awk :cal SetSyn("awk") an 50.10.590 &Syntax.AB.AYacc :cal SetSyn("ayacc") an 50.10.610 &Syntax.AB.B :cal SetSyn("b") an 50.10.620 &Syntax.AB.Baan :cal SetSyn("baan") -an 50.10.630 &Syntax.AB.Basic.FreeBasic :cal SetSyn("freebasic") -an 50.10.640 &Syntax.AB.Basic.IBasic :cal SetSyn("ibasic") -an 50.10.650 &Syntax.AB.Basic.QBasic :cal SetSyn("basic") -an 50.10.660 &Syntax.AB.Basic.Visual\ Basic :cal SetSyn("vb") -an 50.10.670 &Syntax.AB.Bazaar\ commit\ file :cal SetSyn("bzr") -an 50.10.680 &Syntax.AB.Bazel :cal SetSyn("bzl") -an 50.10.690 &Syntax.AB.BC\ calculator :cal SetSyn("bc") -an 50.10.700 &Syntax.AB.BDF\ font :cal SetSyn("bdf") -an 50.10.710 &Syntax.AB.BibTeX.Bibliography\ database :cal SetSyn("bib") -an 50.10.720 &Syntax.AB.BibTeX.Bibliography\ Style :cal SetSyn("bst") -an 50.10.730 &Syntax.AB.BIND.BIND\ config :cal SetSyn("named") -an 50.10.740 &Syntax.AB.BIND.BIND\ zone :cal SetSyn("bindzone") -an 50.10.750 &Syntax.AB.Blank :cal SetSyn("blank") +an 50.10.630 &Syntax.AB.Bash :cal SetSyn("bash") +an 50.10.640 &Syntax.AB.Basic.FreeBasic :cal SetSyn("freebasic") +an 50.10.650 &Syntax.AB.Basic.IBasic :cal SetSyn("ibasic") +an 50.10.660 &Syntax.AB.Basic.QBasic :cal SetSyn("basic") +an 50.10.670 &Syntax.AB.Basic.Visual\ Basic :cal SetSyn("vb") +an 50.10.680 &Syntax.AB.Bazaar\ commit\ file :cal SetSyn("bzr") +an 50.10.690 &Syntax.AB.Bazel :cal SetSyn("bzl") +an 50.10.700 &Syntax.AB.BC\ calculator :cal SetSyn("bc") +an 50.10.710 &Syntax.AB.BDF\ font :cal SetSyn("bdf") +an 50.10.720 &Syntax.AB.BibTeX.Bibliography\ database :cal SetSyn("bib") +an 50.10.730 &Syntax.AB.BibTeX.Bibliography\ Style :cal SetSyn("bst") +an 50.10.740 &Syntax.AB.BIND.BIND\ config :cal SetSyn("named") +an 50.10.750 &Syntax.AB.BIND.BIND\ zone :cal SetSyn("bindzone") +an 50.10.760 &Syntax.AB.Blank :cal SetSyn("blank") an 50.20.100 &Syntax.C.C :cal SetSyn("c") an 50.20.110 &Syntax.C.C++ :cal SetSyn("cpp") an 50.20.120 &Syntax.C.C# :cal SetSyn("cs") @@ -113,89 +114,93 @@ an 50.20.190 &Syntax.C.Century\ Term :cal SetSyn("cterm") an 50.20.200 &Syntax.C.CH\ script :cal SetSyn("ch") an 50.20.210 &Syntax.C.ChaiScript :cal SetSyn("chaiscript") an 50.20.220 &Syntax.C.ChangeLog :cal SetSyn("changelog") -an 50.20.230 &Syntax.C.Cheetah\ template :cal SetSyn("cheetah") -an 50.20.240 &Syntax.C.CHILL :cal SetSyn("chill") -an 50.20.250 &Syntax.C.ChordPro :cal SetSyn("chordpro") -an 50.20.260 &Syntax.C.Clean :cal SetSyn("clean") -an 50.20.270 &Syntax.C.Clever :cal SetSyn("cl") -an 50.20.280 &Syntax.C.Clipper :cal SetSyn("clipper") -an 50.20.290 &Syntax.C.Clojure :cal SetSyn("clojure") -an 50.20.300 &Syntax.C.Cmake :cal SetSyn("cmake") -an 50.20.310 &Syntax.C.Cmod :cal SetSyn("cmod") -an 50.20.320 &Syntax.C.Cmusrc :cal SetSyn("cmusrc") -an 50.20.330 &Syntax.C.Cobol :cal SetSyn("cobol") -an 50.20.340 &Syntax.C.Coco/R :cal SetSyn("coco") -an 50.20.350 &Syntax.C.Cold\ Fusion :cal SetSyn("cf") -an 50.20.360 &Syntax.C.Conary\ Recipe :cal SetSyn("conaryrecipe") -an 50.20.370 &Syntax.C.Config.Cfg\ Config\ file :cal SetSyn("cfg") -an 50.20.380 &Syntax.C.Config.Configure\.in :cal SetSyn("config") -an 50.20.390 &Syntax.C.Config.Generic\ Config\ file :cal SetSyn("conf") -an 50.20.400 &Syntax.C.CRM114 :cal SetSyn("crm") -an 50.20.410 &Syntax.C.Crontab :cal SetSyn("crontab") -an 50.20.420 &Syntax.C.CSDL :cal SetSyn("csdl") -an 50.20.430 &Syntax.C.CSP :cal SetSyn("csp") -an 50.20.440 &Syntax.C.Ctrl-H :cal SetSyn("ctrlh") -an 50.20.450 &Syntax.C.Cucumber :cal SetSyn("cucumber") -an 50.20.460 &Syntax.C.CUDA :cal SetSyn("cuda") -an 50.20.470 &Syntax.C.CUPL.CUPL :cal SetSyn("cupl") -an 50.20.480 &Syntax.C.CUPL.Simulation :cal SetSyn("cuplsim") -an 50.20.490 &Syntax.C.CVS.commit\ file :cal SetSyn("cvs") -an 50.20.500 &Syntax.C.CVS.cvsrc :cal SetSyn("cvsrc") -an 50.20.510 &Syntax.C.Cyn++ :cal SetSyn("cynpp") -an 50.20.520 &Syntax.C.Cynlib :cal SetSyn("cynlib") +an 50.20.230 &Syntax.C.CHILL :cal SetSyn("chill") +an 50.20.240 &Syntax.C.Cheetah\ template :cal SetSyn("cheetah") +an 50.20.250 &Syntax.C.Chicken :cal SetSyn("chicken") +an 50.20.260 &Syntax.C.ChordPro :cal SetSyn("chordpro") +an 50.20.270 &Syntax.C.Clean :cal SetSyn("clean") +an 50.20.280 &Syntax.C.Clever :cal SetSyn("cl") +an 50.20.290 &Syntax.C.Clipper :cal SetSyn("clipper") +an 50.20.300 &Syntax.C.Clojure :cal SetSyn("clojure") +an 50.20.310 &Syntax.C.Cmake :cal SetSyn("cmake") +an 50.20.320 &Syntax.C.Cmod :cal SetSyn("cmod") +an 50.20.330 &Syntax.C.Cmusrc :cal SetSyn("cmusrc") +an 50.20.340 &Syntax.C.Cobol :cal SetSyn("cobol") +an 50.20.350 &Syntax.C.Coco/R :cal SetSyn("coco") +an 50.20.360 &Syntax.C.Cold\ Fusion :cal SetSyn("cf") +an 50.20.370 &Syntax.C.Conary\ Recipe :cal SetSyn("conaryrecipe") +an 50.20.380 &Syntax.C.Config.Cfg\ Config\ file :cal SetSyn("cfg") +an 50.20.390 &Syntax.C.Config.Configure\.in :cal SetSyn("config") +an 50.20.400 &Syntax.C.Config.Generic\ Config\ file :cal SetSyn("conf") +an 50.20.410 &Syntax.C.CRM114 :cal SetSyn("crm") +an 50.20.420 &Syntax.C.Crontab :cal SetSyn("crontab") +an 50.20.430 &Syntax.C.CSDL :cal SetSyn("csdl") +an 50.20.440 &Syntax.C.CSP :cal SetSyn("csp") +an 50.20.450 &Syntax.C.Ctrl-H :cal SetSyn("ctrlh") +an 50.20.460 &Syntax.C.Cucumber :cal SetSyn("cucumber") +an 50.20.470 &Syntax.C.CUDA :cal SetSyn("cuda") +an 50.20.480 &Syntax.C.CUPL.CUPL :cal SetSyn("cupl") +an 50.20.490 &Syntax.C.CUPL.Simulation :cal SetSyn("cuplsim") +an 50.20.500 &Syntax.C.CVS.commit\ file :cal SetSyn("cvs") +an 50.20.510 &Syntax.C.CVS.cvsrc :cal SetSyn("cvsrc") +an 50.20.520 &Syntax.C.Cyn++ :cal SetSyn("cynpp") +an 50.20.530 &Syntax.C.Cynlib :cal SetSyn("cynlib") an 50.30.100 &Syntax.DE.D :cal SetSyn("d") -an 50.30.110 &Syntax.DE.Datascript :cal SetSyn("datascript") -an 50.30.120 &Syntax.DE.Debian.Debian\ ChangeLog :cal SetSyn("debchangelog") -an 50.30.130 &Syntax.DE.Debian.Debian\ Control :cal SetSyn("debcontrol") -an 50.30.140 &Syntax.DE.Debian.Debian\ Copyright :cal SetSyn("debcopyright") -an 50.30.150 &Syntax.DE.Debian.Debian\ Sources\.list :cal SetSyn("debsources") -an 50.30.160 &Syntax.DE.Denyhosts :cal SetSyn("denyhosts") -an 50.30.170 &Syntax.DE.Desktop :cal SetSyn("desktop") -an 50.30.180 &Syntax.DE.Dict\ config :cal SetSyn("dictconf") -an 50.30.190 &Syntax.DE.Dictd\ config :cal SetSyn("dictdconf") -an 50.30.200 &Syntax.DE.Diff :cal SetSyn("diff") -an 50.30.210 &Syntax.DE.Digital\ Command\ Lang :cal SetSyn("dcl") -an 50.30.220 &Syntax.DE.Dircolors :cal SetSyn("dircolors") -an 50.30.230 &Syntax.DE.Dirpager :cal SetSyn("dirpager") -an 50.30.240 &Syntax.DE.Django\ template :cal SetSyn("django") -an 50.30.250 &Syntax.DE.DNS/BIND\ zone :cal SetSyn("bindzone") -an 50.30.260 &Syntax.DE.Dnsmasq\ config :cal SetSyn("dnsmasq") -an 50.30.270 &Syntax.DE.DocBook.auto-detect :cal SetSyn("docbk") -an 50.30.280 &Syntax.DE.DocBook.SGML :cal SetSyn("docbksgml") -an 50.30.290 &Syntax.DE.DocBook.XML :cal SetSyn("docbkxml") -an 50.30.300 &Syntax.DE.Dockerfile :cal SetSyn("dockerfile") -an 50.30.310 &Syntax.DE.Dot :cal SetSyn("dot") -an 50.30.320 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen") -an 50.30.330 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen") -an 50.30.340 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen") -an 50.30.350 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen") -an 50.30.360 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen") -an 50.30.370 &Syntax.DE.Dracula :cal SetSyn("dracula") -an 50.30.380 &Syntax.DE.DSSSL :cal SetSyn("dsl") -an 50.30.390 &Syntax.DE.DTD :cal SetSyn("dtd") -an 50.30.400 &Syntax.DE.DTML\ (Zope) :cal SetSyn("dtml") -an 50.30.410 &Syntax.DE.DTrace :cal SetSyn("dtrace") -an 50.30.420 &Syntax.DE.Dts/dtsi :cal SetSyn("dts") -an 50.30.430 &Syntax.DE.Dylan.Dylan :cal SetSyn("dylan") -an 50.30.440 &Syntax.DE.Dylan.Dylan\ interface :cal SetSyn("dylanintr") -an 50.30.450 &Syntax.DE.Dylan.Dylan\ lid :cal SetSyn("dylanlid") -an 50.30.470 &Syntax.DE.EDIF :cal SetSyn("edif") -an 50.30.480 &Syntax.DE.Eiffel :cal SetSyn("eiffel") -an 50.30.490 &Syntax.DE.Elinks\ config :cal SetSyn("elinks") -an 50.30.500 &Syntax.DE.Elm\ filter\ rules :cal SetSyn("elmfilt") -an 50.30.510 &Syntax.DE.Embedix\ Component\ Description :cal SetSyn("ecd") -an 50.30.520 &Syntax.DE.ERicsson\ LANGuage :cal SetSyn("erlang") -an 50.30.530 &Syntax.DE.ESMTP\ rc :cal SetSyn("esmtprc") -an 50.30.540 &Syntax.DE.ESQL-C :cal SetSyn("esqlc") -an 50.30.550 &Syntax.DE.Essbase\ script :cal SetSyn("csc") -an 50.30.560 &Syntax.DE.Esterel :cal SetSyn("esterel") -an 50.30.570 &Syntax.DE.Eterm\ config :cal SetSyn("eterm") -an 50.30.580 &Syntax.DE.Euphoria\ 3 :cal SetSyn("euphoria3") -an 50.30.590 &Syntax.DE.Euphoria\ 4 :cal SetSyn("euphoria4") -an 50.30.600 &Syntax.DE.Eviews :cal SetSyn("eviews") -an 50.30.610 &Syntax.DE.Exim\ conf :cal SetSyn("exim") -an 50.30.620 &Syntax.DE.Expect :cal SetSyn("expect") -an 50.30.630 &Syntax.DE.Exports :cal SetSyn("exports") +an 50.30.110 &Syntax.DE.Dart :cal SetSyn("dart") +an 50.30.120 &Syntax.DE.Datascript :cal SetSyn("datascript") +an 50.30.130 &Syntax.DE.Debian.Debian\ ChangeLog :cal SetSyn("debchangelog") +an 50.30.140 &Syntax.DE.Debian.Debian\ Control :cal SetSyn("debcontrol") +an 50.30.150 &Syntax.DE.Debian.Debian\ Copyright :cal SetSyn("debcopyright") +an 50.30.160 &Syntax.DE.Debian.Debian\ Sources\.list :cal SetSyn("debsources") +an 50.30.170 &Syntax.DE.Denyhosts :cal SetSyn("denyhosts") +an 50.30.180 &Syntax.DE.Desktop :cal SetSyn("desktop") +an 50.30.190 &Syntax.DE.Dict\ config :cal SetSyn("dictconf") +an 50.30.200 &Syntax.DE.Dictd\ config :cal SetSyn("dictdconf") +an 50.30.210 &Syntax.DE.Diff :cal SetSyn("diff") +an 50.30.220 &Syntax.DE.Digital\ Command\ Lang :cal SetSyn("dcl") +an 50.30.230 &Syntax.DE.Dircolors :cal SetSyn("dircolors") +an 50.30.240 &Syntax.DE.Dirpager :cal SetSyn("dirpager") +an 50.30.250 &Syntax.DE.Django\ template :cal SetSyn("django") +an 50.30.260 &Syntax.DE.DNS/BIND\ zone :cal SetSyn("bindzone") +an 50.30.270 &Syntax.DE.Dnsmasq\ config :cal SetSyn("dnsmasq") +an 50.30.280 &Syntax.DE.DocBook.auto-detect :cal SetSyn("docbk") +an 50.30.290 &Syntax.DE.DocBook.SGML :cal SetSyn("docbksgml") +an 50.30.300 &Syntax.DE.DocBook.XML :cal SetSyn("docbkxml") +an 50.30.310 &Syntax.DE.Dockerfile :cal SetSyn("dockerfile") +an 50.30.320 &Syntax.DE.Dot :cal SetSyn("dot") +an 50.30.330 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen") +an 50.30.340 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen") +an 50.30.350 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen") +an 50.30.360 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen") +an 50.30.370 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen") +an 50.30.380 &Syntax.DE.Dracula :cal SetSyn("dracula") +an 50.30.390 &Syntax.DE.DSSSL :cal SetSyn("dsl") +an 50.30.400 &Syntax.DE.DTD :cal SetSyn("dtd") +an 50.30.410 &Syntax.DE.DTML\ (Zope) :cal SetSyn("dtml") +an 50.30.420 &Syntax.DE.DTrace :cal SetSyn("dtrace") +an 50.30.430 &Syntax.DE.Dts/dtsi :cal SetSyn("dts") +an 50.30.440 &Syntax.DE.Dune :cal SetSyn("dune") +an 50.30.450 &Syntax.DE.Dylan.Dylan :cal SetSyn("dylan") +an 50.30.460 &Syntax.DE.Dylan.Dylan\ interface :cal SetSyn("dylanintr") +an 50.30.470 &Syntax.DE.Dylan.Dylan\ lid :cal SetSyn("dylanlid") +an 50.30.490 &Syntax.DE.EDIF :cal SetSyn("edif") +an 50.30.500 &Syntax.DE.Eiffel :cal SetSyn("eiffel") +an 50.30.510 &Syntax.DE.Eight :cal SetSyn("8th") +an 50.30.520 &Syntax.DE.Elinks\ config :cal SetSyn("elinks") +an 50.30.530 &Syntax.DE.Elm\ filter\ rules :cal SetSyn("elmfilt") +an 50.30.540 &Syntax.DE.Embedix\ Component\ Description :cal SetSyn("ecd") +an 50.30.550 &Syntax.DE.ERicsson\ LANGuage :cal SetSyn("erlang") +an 50.30.560 &Syntax.DE.ESMTP\ rc :cal SetSyn("esmtprc") +an 50.30.570 &Syntax.DE.ESQL-C :cal SetSyn("esqlc") +an 50.30.580 &Syntax.DE.Essbase\ script :cal SetSyn("csc") +an 50.30.590 &Syntax.DE.Esterel :cal SetSyn("esterel") +an 50.30.600 &Syntax.DE.Eterm\ config :cal SetSyn("eterm") +an 50.30.610 &Syntax.DE.Euphoria\ 3 :cal SetSyn("euphoria3") +an 50.30.620 &Syntax.DE.Euphoria\ 4 :cal SetSyn("euphoria4") +an 50.30.630 &Syntax.DE.Eviews :cal SetSyn("eviews") +an 50.30.640 &Syntax.DE.Exim\ conf :cal SetSyn("exim") +an 50.30.650 &Syntax.DE.Expect :cal SetSyn("expect") +an 50.30.660 &Syntax.DE.Exports :cal SetSyn("exports") an 50.40.100 &Syntax.FG.Falcon :cal SetSyn("falcon") an 50.40.110 &Syntax.FG.Fantom :cal SetSyn("fan") an 50.40.120 &Syntax.FG.Fetchmail :cal SetSyn("fetchmail") @@ -259,43 +264,44 @@ an 50.50.290 &Syntax.HIJK.HTML.XHTML :cal SetSyn("xhtml") an 50.50.300 &Syntax.HIJK.Host\.conf :cal SetSyn("hostconf") an 50.50.310 &Syntax.HIJK.Hosts\ access :cal SetSyn("hostsaccess") an 50.50.320 &Syntax.HIJK.Hyper\ Builder :cal SetSyn("hb") -an 50.50.330 &Syntax.HIJK.Icewm\ menu :cal SetSyn("icemenu") -an 50.50.340 &Syntax.HIJK.Icon :cal SetSyn("icon") -an 50.50.350 &Syntax.HIJK.IDL\Generic\ IDL :cal SetSyn("idl") -an 50.50.360 &Syntax.HIJK.IDL\Microsoft\ IDL :cal SetSyn("msidl") -an 50.50.370 &Syntax.HIJK.Indent\ profile :cal SetSyn("indent") -an 50.50.380 &Syntax.HIJK.Inform :cal SetSyn("inform") -an 50.50.390 &Syntax.HIJK.Informix\ 4GL :cal SetSyn("fgl") -an 50.50.400 &Syntax.HIJK.Initng :cal SetSyn("initng") -an 50.50.410 &Syntax.HIJK.Inittab :cal SetSyn("inittab") -an 50.50.420 &Syntax.HIJK.Inno\ setup :cal SetSyn("iss") -an 50.50.430 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ dat :cal SetSyn("upstreamdat") -an 50.50.440 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ log :cal SetSyn("upstreamlog") -an 50.50.450 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ rpt :cal SetSyn("upstreamrpt") -an 50.50.460 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ Install\ log :cal SetSyn("upstreaminstalllog") -an 50.50.470 &Syntax.HIJK.Innovation\ Data\ Processing.Usserver\ log :cal SetSyn("usserverlog") -an 50.50.480 &Syntax.HIJK.Innovation\ Data\ Processing.USW2KAgt\ log :cal SetSyn("usw2kagtlog") -an 50.50.490 &Syntax.HIJK.InstallShield\ script :cal SetSyn("ishd") -an 50.50.500 &Syntax.HIJK.Interactive\ Data\ Lang :cal SetSyn("idlang") -an 50.50.510 &Syntax.HIJK.IPfilter :cal SetSyn("ipfilter") -an 50.50.530 &Syntax.HIJK.J :cal SetSyn("j") -an 50.50.540 &Syntax.HIJK.JAL :cal SetSyn("jal") -an 50.50.550 &Syntax.HIJK.JAM :cal SetSyn("jam") -an 50.50.560 &Syntax.HIJK.Jargon :cal SetSyn("jargon") -an 50.50.570 &Syntax.HIJK.Java.Java :cal SetSyn("java") -an 50.50.580 &Syntax.HIJK.Java.JavaCC :cal SetSyn("javacc") -an 50.50.590 &Syntax.HIJK.Java.Java\ Server\ Pages :cal SetSyn("jsp") -an 50.50.600 &Syntax.HIJK.Java.Java\ Properties :cal SetSyn("jproperties") -an 50.50.610 &Syntax.HIJK.JavaScript :cal SetSyn("javascript") -an 50.50.620 &Syntax.HIJK.Jess :cal SetSyn("jess") -an 50.50.630 &Syntax.HIJK.Jgraph :cal SetSyn("jgraph") -an 50.50.640 &Syntax.HIJK.Jovial :cal SetSyn("jovial") -an 50.50.650 &Syntax.HIJK.JSON :cal SetSyn("json") -an 50.50.670 &Syntax.HIJK.Kconfig :cal SetSyn("kconfig") -an 50.50.680 &Syntax.HIJK.KDE\ script :cal SetSyn("kscript") -an 50.50.690 &Syntax.HIJK.Kimwitu++ :cal SetSyn("kwt") -an 50.50.700 &Syntax.HIJK.Kivy :cal SetSyn("kivy") -an 50.50.710 &Syntax.HIJK.KixTart :cal SetSyn("kix") +an 50.50.340 &Syntax.HIJK.Icewm\ menu :cal SetSyn("icemenu") +an 50.50.350 &Syntax.HIJK.Icon :cal SetSyn("icon") +an 50.50.360 &Syntax.HIJK.IDL\Generic\ IDL :cal SetSyn("idl") +an 50.50.370 &Syntax.HIJK.IDL\Microsoft\ IDL :cal SetSyn("msidl") +an 50.50.380 &Syntax.HIJK.Indent\ profile :cal SetSyn("indent") +an 50.50.390 &Syntax.HIJK.Inform :cal SetSyn("inform") +an 50.50.400 &Syntax.HIJK.Informix\ 4GL :cal SetSyn("fgl") +an 50.50.410 &Syntax.HIJK.Initng :cal SetSyn("initng") +an 50.50.420 &Syntax.HIJK.Inittab :cal SetSyn("inittab") +an 50.50.430 &Syntax.HIJK.Inno\ setup :cal SetSyn("iss") +an 50.50.440 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ dat :cal SetSyn("upstreamdat") +an 50.50.450 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ log :cal SetSyn("upstreamlog") +an 50.50.460 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ rpt :cal SetSyn("upstreamrpt") +an 50.50.470 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ Install\ log :cal SetSyn("upstreaminstalllog") +an 50.50.480 &Syntax.HIJK.Innovation\ Data\ Processing.Usserver\ log :cal SetSyn("usserverlog") +an 50.50.490 &Syntax.HIJK.Innovation\ Data\ Processing.USW2KAgt\ log :cal SetSyn("usw2kagtlog") +an 50.50.500 &Syntax.HIJK.InstallShield\ script :cal SetSyn("ishd") +an 50.50.510 &Syntax.HIJK.Interactive\ Data\ Lang :cal SetSyn("idlang") +an 50.50.520 &Syntax.HIJK.IPfilter :cal SetSyn("ipfilter") +an 50.50.540 &Syntax.HIJK.J :cal SetSyn("j") +an 50.50.550 &Syntax.HIJK.JAL :cal SetSyn("jal") +an 50.50.560 &Syntax.HIJK.JAM :cal SetSyn("jam") +an 50.50.570 &Syntax.HIJK.Jargon :cal SetSyn("jargon") +an 50.50.580 &Syntax.HIJK.Java.Java :cal SetSyn("java") +an 50.50.590 &Syntax.HIJK.Java.JavaCC :cal SetSyn("javacc") +an 50.50.600 &Syntax.HIJK.Java.Java\ Server\ Pages :cal SetSyn("jsp") +an 50.50.610 &Syntax.HIJK.Java.Java\ Properties :cal SetSyn("jproperties") +an 50.50.620 &Syntax.HIJK.JavaScript :cal SetSyn("javascript") +an 50.50.630 &Syntax.HIJK.JavaScriptReact :cal SetSyn("javascriptreact") +an 50.50.640 &Syntax.HIJK.Jess :cal SetSyn("jess") +an 50.50.650 &Syntax.HIJK.Jgraph :cal SetSyn("jgraph") +an 50.50.660 &Syntax.HIJK.Jovial :cal SetSyn("jovial") +an 50.50.670 &Syntax.HIJK.JSON :cal SetSyn("json") +an 50.50.690 &Syntax.HIJK.Kconfig :cal SetSyn("kconfig") +an 50.50.700 &Syntax.HIJK.KDE\ script :cal SetSyn("kscript") +an 50.50.710 &Syntax.HIJK.Kimwitu++ :cal SetSyn("kwt") +an 50.50.720 &Syntax.HIJK.Kivy :cal SetSyn("kivy") +an 50.50.730 &Syntax.HIJK.KixTart :cal SetSyn("kix") an 50.60.100 &Syntax.L.Lace :cal SetSyn("lace") an 50.60.110 &Syntax.L.LamdaProlog :cal SetSyn("lprolog") an 50.60.120 &Syntax.L.Latte :cal SetSyn("latte") @@ -343,34 +349,35 @@ an 50.70.240 &Syntax.M.Mathematica :cal SetSyn("mma") an 50.70.250 &Syntax.M.Matlab :cal SetSyn("matlab") an 50.70.260 &Syntax.M.Maxima :cal SetSyn("maxima") an 50.70.270 &Syntax.M.MEL\ (for\ Maya) :cal SetSyn("mel") -an 50.70.280 &Syntax.M.Messages\ (/var/log) :cal SetSyn("messages") -an 50.70.290 &Syntax.M.Metafont :cal SetSyn("mf") -an 50.70.300 &Syntax.M.MetaPost :cal SetSyn("mp") -an 50.70.310 &Syntax.M.MGL :cal SetSyn("mgl") -an 50.70.320 &Syntax.M.MIX :cal SetSyn("mix") -an 50.70.330 &Syntax.M.MMIX :cal SetSyn("mmix") -an 50.70.340 &Syntax.M.Modconf :cal SetSyn("modconf") -an 50.70.350 &Syntax.M.Model :cal SetSyn("model") -an 50.70.360 &Syntax.M.Modsim\ III :cal SetSyn("modsim3") -an 50.70.370 &Syntax.M.Modula\ 2 :cal SetSyn("modula2") -an 50.70.380 &Syntax.M.Modula\ 3 :cal SetSyn("modula3") -an 50.70.390 &Syntax.M.Monk :cal SetSyn("monk") -an 50.70.400 &Syntax.M.Motorola\ S-Record :cal SetSyn("srec") -an 50.70.410 &Syntax.M.Mplayer\ config :cal SetSyn("mplayerconf") -an 50.70.420 &Syntax.M.MOO :cal SetSyn("moo") -an 50.70.430 &Syntax.M.Mrxvtrc :cal SetSyn("mrxvtrc") -an 50.70.440 &Syntax.M.MS-DOS/Windows.4DOS\ \.bat\ file :cal SetSyn("btm") -an 50.70.450 &Syntax.M.MS-DOS/Windows.\.bat\/\.cmd\ file :cal SetSyn("dosbatch") -an 50.70.460 &Syntax.M.MS-DOS/Windows.\.ini\ file :cal SetSyn("dosini") -an 50.70.470 &Syntax.M.MS-DOS/Windows.Message\ text :cal SetSyn("msmessages") -an 50.70.480 &Syntax.M.MS-DOS/Windows.Module\ Definition :cal SetSyn("def") -an 50.70.490 &Syntax.M.MS-DOS/Windows.Registry :cal SetSyn("registry") -an 50.70.500 &Syntax.M.MS-DOS/Windows.Resource\ file :cal SetSyn("rc") -an 50.70.510 &Syntax.M.Msql :cal SetSyn("msql") -an 50.70.520 &Syntax.M.MuPAD :cal SetSyn("mupad") -an 50.70.530 &Syntax.M.Murphi :cal SetSyn("murphi") -an 50.70.540 &Syntax.M.MUSHcode :cal SetSyn("mush") -an 50.70.550 &Syntax.M.Muttrc :cal SetSyn("muttrc") +an 50.70.280 &Syntax.M.Meson :cal SetSyn("meson") +an 50.70.290 &Syntax.M.Messages\ (/var/log) :cal SetSyn("messages") +an 50.70.300 &Syntax.M.Metafont :cal SetSyn("mf") +an 50.70.310 &Syntax.M.MetaPost :cal SetSyn("mp") +an 50.70.320 &Syntax.M.MGL :cal SetSyn("mgl") +an 50.70.330 &Syntax.M.MIX :cal SetSyn("mix") +an 50.70.340 &Syntax.M.MMIX :cal SetSyn("mmix") +an 50.70.350 &Syntax.M.Modconf :cal SetSyn("modconf") +an 50.70.360 &Syntax.M.Model :cal SetSyn("model") +an 50.70.370 &Syntax.M.Modsim\ III :cal SetSyn("modsim3") +an 50.70.380 &Syntax.M.Modula\ 2 :cal SetSyn("modula2") +an 50.70.390 &Syntax.M.Modula\ 3 :cal SetSyn("modula3") +an 50.70.400 &Syntax.M.Monk :cal SetSyn("monk") +an 50.70.410 &Syntax.M.Motorola\ S-Record :cal SetSyn("srec") +an 50.70.420 &Syntax.M.Mplayer\ config :cal SetSyn("mplayerconf") +an 50.70.430 &Syntax.M.MOO :cal SetSyn("moo") +an 50.70.440 &Syntax.M.Mrxvtrc :cal SetSyn("mrxvtrc") +an 50.70.450 &Syntax.M.MS-DOS/Windows.4DOS\ \.bat\ file :cal SetSyn("btm") +an 50.70.460 &Syntax.M.MS-DOS/Windows.\.bat\/\.cmd\ file :cal SetSyn("dosbatch") +an 50.70.470 &Syntax.M.MS-DOS/Windows.\.ini\ file :cal SetSyn("dosini") +an 50.70.480 &Syntax.M.MS-DOS/Windows.Message\ text :cal SetSyn("msmessages") +an 50.70.490 &Syntax.M.MS-DOS/Windows.Module\ Definition :cal SetSyn("def") +an 50.70.500 &Syntax.M.MS-DOS/Windows.Registry :cal SetSyn("registry") +an 50.70.510 &Syntax.M.MS-DOS/Windows.Resource\ file :cal SetSyn("rc") +an 50.70.520 &Syntax.M.Msql :cal SetSyn("msql") +an 50.70.530 &Syntax.M.MuPAD :cal SetSyn("mupad") +an 50.70.540 &Syntax.M.Murphi :cal SetSyn("murphi") +an 50.70.550 &Syntax.M.MUSHcode :cal SetSyn("mush") +an 50.70.560 &Syntax.M.Muttrc :cal SetSyn("muttrc") an 50.80.100 &Syntax.NO.N1QL :cal SetSyn("n1ql") an 50.80.110 &Syntax.NO.Nanorc :cal SetSyn("nanorc") an 50.80.120 &Syntax.NO.Nastran\ input/DMAP :cal SetSyn("nastran") @@ -442,25 +449,26 @@ an 50.100.110 &Syntax.R.R.R\ help :cal SetSyn("rhelp") an 50.100.120 &Syntax.R.R.R\ noweb :cal SetSyn("rnoweb") an 50.100.130 &Syntax.R.Racc\ input :cal SetSyn("racc") an 50.100.140 &Syntax.R.Radiance :cal SetSyn("radiance") -an 50.100.150 &Syntax.R.Ratpoison :cal SetSyn("ratpoison") -an 50.100.160 &Syntax.R.RCS.RCS\ log\ output :cal SetSyn("rcslog") -an 50.100.170 &Syntax.R.RCS.RCS\ file :cal SetSyn("rcs") -an 50.100.180 &Syntax.R.Readline\ config :cal SetSyn("readline") -an 50.100.190 &Syntax.R.Rebol :cal SetSyn("rebol") -an 50.100.200 &Syntax.R.ReDIF :cal SetSyn("redif") -an 50.100.210 &Syntax.R.Relax\ NG :cal SetSyn("rng") -an 50.100.220 &Syntax.R.Remind :cal SetSyn("remind") -an 50.100.230 &Syntax.R.Relax\ NG\ compact :cal SetSyn("rnc") -an 50.100.240 &Syntax.R.Renderman.Renderman\ Shader\ Lang :cal SetSyn("sl") -an 50.100.250 &Syntax.R.Renderman.Renderman\ Interface\ Bytestream :cal SetSyn("rib") -an 50.100.260 &Syntax.R.Resolv\.conf :cal SetSyn("resolv") -an 50.100.270 &Syntax.R.Reva\ Forth :cal SetSyn("reva") -an 50.100.280 &Syntax.R.Rexx :cal SetSyn("rexx") -an 50.100.290 &Syntax.R.Robots\.txt :cal SetSyn("robots") -an 50.100.300 &Syntax.R.RockLinux\ package\ desc\. :cal SetSyn("desc") -an 50.100.310 &Syntax.R.Rpcgen :cal SetSyn("rpcgen") -an 50.100.320 &Syntax.R.RPL/2 :cal SetSyn("rpl") -an 50.100.330 &Syntax.R.ReStructuredText :cal SetSyn("rst") +an 50.100.150 &Syntax.R.Raml :cal SetSyn("raml") +an 50.100.160 &Syntax.R.Ratpoison :cal SetSyn("ratpoison") +an 50.100.170 &Syntax.R.RCS.RCS\ log\ output :cal SetSyn("rcslog") +an 50.100.180 &Syntax.R.RCS.RCS\ file :cal SetSyn("rcs") +an 50.100.190 &Syntax.R.Readline\ config :cal SetSyn("readline") +an 50.100.200 &Syntax.R.Rebol :cal SetSyn("rebol") +an 50.100.210 &Syntax.R.ReDIF :cal SetSyn("redif") +an 50.100.220 &Syntax.R.Relax\ NG :cal SetSyn("rng") +an 50.100.230 &Syntax.R.Remind :cal SetSyn("remind") +an 50.100.240 &Syntax.R.Relax\ NG\ compact :cal SetSyn("rnc") +an 50.100.250 &Syntax.R.Renderman.Renderman\ Shader\ Lang :cal SetSyn("sl") +an 50.100.260 &Syntax.R.Renderman.Renderman\ Interface\ Bytestream :cal SetSyn("rib") +an 50.100.270 &Syntax.R.Resolv\.conf :cal SetSyn("resolv") +an 50.100.280 &Syntax.R.Reva\ Forth :cal SetSyn("reva") +an 50.100.290 &Syntax.R.Rexx :cal SetSyn("rexx") +an 50.100.300 &Syntax.R.Robots\.txt :cal SetSyn("robots") +an 50.100.310 &Syntax.R.RockLinux\ package\ desc\. :cal SetSyn("desc") +an 50.100.320 &Syntax.R.Rpcgen :cal SetSyn("rpcgen") +an 50.100.330 &Syntax.R.RPL/2 :cal SetSyn("rpl") +an 50.100.340 &Syntax.R.ReStructuredText :cal SetSyn("rst") an 50.110.100 &Syntax.M.ReStructuredText\ with\ R\ statements :cal SetSyn("rrst") an 50.120.100 &Syntax.R.RTF :cal SetSyn("rtf") an 50.120.110 &Syntax.R.Ruby :cal SetSyn("ruby") @@ -581,6 +589,8 @@ an 50.150.370 &Syntax.T.Trustees :cal SetSyn("trustees") an 50.150.380 &Syntax.T.TSS.Command\ Line :cal SetSyn("tsscl") an 50.150.390 &Syntax.T.TSS.Geometry :cal SetSyn("tssgm") an 50.150.400 &Syntax.T.TSS.Optics :cal SetSyn("tssop") +an 50.150.410 &Syntax.T.Typescript :cal SetSyn("typescript") +an 50.150.420 &Syntax.T.TypescriptReact :cal SetSyn("typescriptreact") an 50.160.100 &Syntax.UV.Udev\ config :cal SetSyn("udevconf") an 50.160.110 &Syntax.UV.Udev\ permissions :cal SetSyn("udevperm") an 50.160.120 &Syntax.UV.Udev\ rules :cal SetSyn("udevrules") @@ -607,32 +617,33 @@ an 50.160.330 &Syntax.UV.VSE\ JCL :cal SetSyn("vsejcl") an 50.170.100 &Syntax.WXYZ.WEB.CWEB :cal SetSyn("cweb") an 50.170.110 &Syntax.WXYZ.WEB.WEB :cal SetSyn("web") an 50.170.120 &Syntax.WXYZ.WEB.WEB\ Changes :cal SetSyn("change") -an 50.170.130 &Syntax.WXYZ.Webmacro :cal SetSyn("webmacro") -an 50.170.140 &Syntax.WXYZ.Website\ MetaLanguage :cal SetSyn("wml") -an 50.170.160 &Syntax.WXYZ.wDiff :cal SetSyn("wdiff") -an 50.170.180 &Syntax.WXYZ.Wget\ config :cal SetSyn("wget") -an 50.170.190 &Syntax.WXYZ.Whitespace\ (add) :cal SetSyn("whitespace") -an 50.170.200 &Syntax.WXYZ.WildPackets\ EtherPeek\ Decoder :cal SetSyn("dcd") -an 50.170.210 &Syntax.WXYZ.WinBatch/Webbatch :cal SetSyn("winbatch") -an 50.170.220 &Syntax.WXYZ.Windows\ Scripting\ Host :cal SetSyn("wsh") -an 50.170.230 &Syntax.WXYZ.WSML :cal SetSyn("wsml") -an 50.170.240 &Syntax.WXYZ.WvDial :cal SetSyn("wvdial") -an 50.170.260 &Syntax.WXYZ.X\ Keyboard\ Extension :cal SetSyn("xkb") -an 50.170.270 &Syntax.WXYZ.X\ Pixmap :cal SetSyn("xpm") -an 50.170.280 &Syntax.WXYZ.X\ Pixmap\ (2) :cal SetSyn("xpm2") -an 50.170.290 &Syntax.WXYZ.X\ resources :cal SetSyn("xdefaults") -an 50.170.300 &Syntax.WXYZ.XBL :cal SetSyn("xbl") -an 50.170.310 &Syntax.WXYZ.Xinetd\.conf :cal SetSyn("xinetd") -an 50.170.320 &Syntax.WXYZ.Xmodmap :cal SetSyn("xmodmap") -an 50.170.330 &Syntax.WXYZ.Xmath :cal SetSyn("xmath") -an 50.170.340 &Syntax.WXYZ.XML :cal SetSyn("xml") -an 50.170.350 &Syntax.WXYZ.XML\ Schema\ (XSD) :cal SetSyn("xsd") -an 50.170.360 &Syntax.WXYZ.XQuery :cal SetSyn("xquery") -an 50.170.370 &Syntax.WXYZ.Xslt :cal SetSyn("xslt") -an 50.170.380 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf") -an 50.170.400 &Syntax.WXYZ.YAML :cal SetSyn("yaml") -an 50.170.410 &Syntax.WXYZ.Yacc :cal SetSyn("yacc") -an 50.170.430 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu") +an 50.170.130 &Syntax.WXYZ.WebAssembly :cal SetSyn("wast") +an 50.170.140 &Syntax.WXYZ.Webmacro :cal SetSyn("webmacro") +an 50.170.150 &Syntax.WXYZ.Website\ MetaLanguage :cal SetSyn("wml") +an 50.170.170 &Syntax.WXYZ.wDiff :cal SetSyn("wdiff") +an 50.170.190 &Syntax.WXYZ.Wget\ config :cal SetSyn("wget") +an 50.170.200 &Syntax.WXYZ.Whitespace\ (add) :cal SetSyn("whitespace") +an 50.170.210 &Syntax.WXYZ.WildPackets\ EtherPeek\ Decoder :cal SetSyn("dcd") +an 50.170.220 &Syntax.WXYZ.WinBatch/Webbatch :cal SetSyn("winbatch") +an 50.170.230 &Syntax.WXYZ.Windows\ Scripting\ Host :cal SetSyn("wsh") +an 50.170.240 &Syntax.WXYZ.WSML :cal SetSyn("wsml") +an 50.170.250 &Syntax.WXYZ.WvDial :cal SetSyn("wvdial") +an 50.170.270 &Syntax.WXYZ.X\ Keyboard\ Extension :cal SetSyn("xkb") +an 50.170.280 &Syntax.WXYZ.X\ Pixmap :cal SetSyn("xpm") +an 50.170.290 &Syntax.WXYZ.X\ Pixmap\ (2) :cal SetSyn("xpm2") +an 50.170.300 &Syntax.WXYZ.X\ resources :cal SetSyn("xdefaults") +an 50.170.310 &Syntax.WXYZ.XBL :cal SetSyn("xbl") +an 50.170.320 &Syntax.WXYZ.Xinetd\.conf :cal SetSyn("xinetd") +an 50.170.330 &Syntax.WXYZ.Xmodmap :cal SetSyn("xmodmap") +an 50.170.340 &Syntax.WXYZ.Xmath :cal SetSyn("xmath") +an 50.170.350 &Syntax.WXYZ.XML :cal SetSyn("xml") +an 50.170.360 &Syntax.WXYZ.XML\ Schema\ (XSD) :cal SetSyn("xsd") +an 50.170.370 &Syntax.WXYZ.XQuery :cal SetSyn("xquery") +an 50.170.380 &Syntax.WXYZ.Xslt :cal SetSyn("xslt") +an 50.170.390 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf") +an 50.170.410 &Syntax.WXYZ.YAML :cal SetSyn("yaml") +an 50.170.420 &Syntax.WXYZ.Yacc :cal SetSyn("yacc") +an 50.170.440 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu") " The End Of The Syntax Menu -- cgit From 7111fe94597339e165307220ea62d478b8aaea32 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 10 Dec 2019 00:29:39 -0800 Subject: doc: LSP [ci skip] #11524 --- runtime/doc/lsp.txt | 130 ++++++++++++++++++++++------------------------- runtime/doc/vim_diff.txt | 2 + 2 files changed, 64 insertions(+), 68 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index c4c164ab6c..d6d16b8481 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -3,7 +3,7 @@ NVIM REFERENCE MANUAL -Nvim Language Server Protocol (LSP) API *lsp* +Nvim Language Server Protocol (LSP) API *lsp* Nvim is a client to the Language Server Protocol: @@ -12,25 +12,61 @@ Nvim is a client to the Language Server Protocol: Type |gO| to see the table of contents. ================================================================================ -LSP API *lsp-api* +LANGUAGE SERVER PROTOCOL (LSP) CLIENT *lsp-intro* -Neovim exposes a API for the language server protocol. To get the real benefits -of this API, a language server must be installed. -Many examples can be found here: +The `vim.lsp` Lua module provides a flexible API for consuming LSP servers. +To use LSP in practice, a language server must be installed. https://microsoft.github.io/language-server-protocol/implementors/servers/ -After installing a language server to your machine, you must let Neovim know -how to start and interact with that language server. +After installing a language server to your machine, you must tell Nvim how to +start and interact with that language server. +- Easy way: use the configs provided here by the nvim-lsp plugin. + https://github.com/neovim/nvim-lsp +- Low-level way: use |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| + directly. Useful if you want to build advanced LSP plugins based on the + Nvim LSP module. |lsp-advanced-js-example| + + *lsp-config* +Nvim LSP client will automatically provide inline diagnostics when available. +|lsp-callbacks| But you probably want to use other features too, such as +go-to-definition, "hover", etc. Example config: > + + nnoremap gd lua vim.lsp.buf.declaration() + nnoremap lua vim.lsp.buf.definition() + nnoremap K lua vim.lsp.buf.hover() + nnoremap gD lua vim.lsp.buf.implementation() + nnoremap lua vim.lsp.buf.signature_help() + nnoremap 1gD lua vim.lsp.buf.type_definition() -To do so, you can either: -- Use https://github.com/neovim/nvim-lsp and one of the existing servers there - or set up a new one using the `nvim_lsp/skeleton` interface (and contribute - it if you find it useful). This uses |vim.lsp.start_client()| under the - hood. -- Or |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. These are the - backbone of the LSP API. These are easy to use enough for basic or more - complex configurations such as in |lsp-advanced-js-example|. +< + *vim.lsp.omnifunc()* +Nvim provides the vim.lsp.omnifunc 'omnifunc' handler which allows +|i_CTRL-X_CTRL-O| to consume LSP completion features. Example config (note the +use of |v:lua| to call Lua from Vimscript): > + + " Use LSP omni-completion in Python files. + autocmd Filetype python setlocal omnifunc=v:lua.vim.lsp.omnifunc + + +FAQ ~ + +> How to force-reload LSP? + +Stop all clients, then reload the buffer. > + + :lua vim.lsp.stop_all_clients() + :edit + +> Why isn't completion working? + +In the buffer where you want to use LSP, check that 'omnifunc' is set to +"v:lua.vim.lsp.omnifunc": > + + :verbose set omnifunc? + +Some other plugin may be overriding the option. To avoid that, you could set +the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". ================================================================================ *lsp-core-api* @@ -75,7 +111,7 @@ vim.lsp.start_client({config}) `capabilities` A {table} which will be used instead of - `vim.lsp.protocol.make_client_capabilities()` which contains neovim's + `vim.lsp.protocol.make_client_capabilities()` which contains Nvim's default capabilities and passed to the language server on initialization. You'll probably want to use make_client_capabilities() and modify the result. @@ -264,11 +300,11 @@ vim.lsp.rpc_response_error({code}, [{message}], [{data}]) LSP CALLBACKS *lsp-callbacks* DEFAULT CALLBACKS ~ - *vim.lsp.default_callbacks* -The `vim.lsp.default_callbacks` table defines default callbacks used when + *vim.lsp.callbacks* +The `vim.lsp.callbacks` table defines default callbacks used when creating a new client. Keys are LSP method names: > - :lua print(vim.inspect(vim.tbl_keys(vim.lsp.default_callbacks))) + :lua print(vim.inspect(vim.tbl_keys(vim.lsp.callbacks))) These LSP requests/notifications are defined by default: @@ -276,7 +312,7 @@ These LSP requests/notifications are defined by default: window/logMessage window/showMessage -You can check these via `vim.tbl_keys(vim.lsp.default_callbacks)`. +You can check these via `vim.tbl_keys(vim.lsp.callbacks)`. These will be used preferrentially in `vim.lsp.buf` methods when handling requests. They will also be used when responding to server requests and @@ -285,16 +321,16 @@ notifications. Use cases: - Users can modify this to customize to their preferences. - UI plugins can modify this by assigning to - `vim.lsp.default_callbacks[method]` so as to provide more specialized + `vim.lsp.callbacks[method]` so as to provide more specialized handling, allowing you to leverage the UI capabilities available. UIs should try to be conscientious of any existing changes the user may have set already by checking for existing values. Any callbacks passed directly to `request` methods on a server client will -have the highest precedence, followed by the `default_callbacks`. +have the highest precedence, followed by the `callbacks`. You can override the default handlers, -- globally: by modifying the `vim.lsp.default_callbacks` table +- globally: by modifying the `vim.lsp.callbacks` table - per-client: by passing the {callbacks} table parameter to |vim.lsp.start_client| @@ -305,7 +341,7 @@ Each handler has this signature: > Callbacks are functions which are called in a variety of situations by the client. Their signature is `function(err, method, params, client_id)` They can be set by the {callbacks} parameter for |vim.lsp.start_client| or via the -|vim.lsp.default_callbacks|. +|vim.lsp.callbacks|. Handlers are called for: - Notifications from the server (`err` is always `nil`). @@ -420,51 +456,9 @@ vim.lsp.get_log_path() vim.lsp.log_levels 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 + Can be used to lookup the number from the name or vice-versa. + Levels: "trace" (0), "debug" (1), "info" (2), "warn" (3), "error" (4) -================================================================================ - *lsp-omnifunc* - *vim.lsp.omnifunc()* -vim.lsp.omnifunc({findstart}, {base}) - -To configure omnifunc, add the following in your init.vim: -> - " Configure for python - autocmd Filetype python setl omnifunc=v:lua.vim.lsp.omnifunc - - " Or with on_attach - start_client { - ... - on_attach = function(client, bufnr) - vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') - end; - } - - " This is optional, but you may find it useful - autocmd CompleteDone * pclose -< -================================================================================ -LSP FUNCTIONS *lsp-vim-functions* - -To use the functions from vim, it is recommended to use |v:lua| to interface -with the Lua functions. No direct vim functions are provided, but the -interface is still easy to use from mappings. - -These methods can be used in mappings and are the equivalent of using the -request from lua as follows: - -> - " Example config - autocmd Filetype rust,python,go,c,cpp setl omnifunc=v:lua.vim.lsp.omnifunc - nnoremap ;dc lua vim.lsp.buf.declaration() - nnoremap ;df lua vim.lsp.buf.definition() - nnoremap ;h lua vim.lsp.buf.hover() - nnoremap ;i lua vim.lsp.buf.implementation() - nnoremap ;s lua vim.lsp.buf.signature_help() - nnoremap ;td lua vim.lsp.buf.type_definition() -< ================================================================================ LSP EXAMPLE *lsp-advanced-js-example* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 3646034dba..9c106077ab 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -169,6 +169,7 @@ Functions: |system()|, |systemlist()| can run {cmd} directly (without 'shell') Highlight groups: + |highlight-blend| controls blend level for a highlight group |expr-highlight| highlight groups (prefixed with "Nvim") |hl-NormalFloat| highlights floating window |hl-NormalNC| highlights non-current windows @@ -207,6 +208,7 @@ Options: 'statusline' supports unlimited alignment sections 'tabline' %@Func@foo%X can call any function on mouse-click 'wildoptions' `pum` flag to use popupmenu for wildmode completion + 'winblend' pseudo-transparency in floating windows |api-floatwin| 'winhighlight' window-local highlights Signs: -- cgit From 6c22c7ab97cca9f8dda6863ee7f1db1ce30a3451 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 10 Dec 2019 00:46:04 -0800 Subject: netrw.vim: do not save +/* registers netrw shouldn't be touching these in the first place. fix #11089 ref #6892 ref #6695 --- runtime/autoload/netrw.vim | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index a5b47e06d5..9b1266c4ca 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -688,10 +688,6 @@ fun! netrw#Explore(indx,dosplit,style,...) endif " save registers - if has("clipboard") - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif sil! let keepregslash= @/ " if dosplit @@ -915,10 +911,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " call Decho("..case Nexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or improperly; see help for netrw-starstar",40) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore") return @@ -940,10 +932,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " call Decho("case Pexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or improperly; see help for netrw-starstar",41) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore") return @@ -995,10 +983,6 @@ fun! netrw#Explore(indx,dosplit,style,...) catch /^Vim\%((\a\+)\)\=:E480/ keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45) if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : no files matched pattern") return @@ -1031,10 +1015,6 @@ fun! netrw#Explore(indx,dosplit,style,...) if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/') keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : no files matched") return @@ -1079,10 +1059,6 @@ fun! netrw#Explore(indx,dosplit,style,...) if !exists("g:netrw_quiet") keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44) endif - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : missing +path_extra") return @@ -1152,10 +1128,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " there's no danger of a late FocusGained event on initialization. " Consequently, set s:netrw_events to 2. let s:netrw_events= 2 - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : @/<".@/.">") endfun @@ -9559,10 +9531,6 @@ fun! s:NetrwWideListing() let newcolstart = w:netrw_bannercnt + fpc let newcolend = newcolstart + fpc - 1 " call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("")) - if has("clipboard") - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif while line("$") >= newcolstart if newcolend > line("$") | let newcolend= line("$") | endif let newcolqty= newcolend - newcolstart @@ -9575,10 +9543,6 @@ fun! s:NetrwWideListing() exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _' exe 'sil! NetrwKeepj '.w:netrw_bannercnt endwhile - if has("clipboard") - sil! let @*= keepregstar - sil! let @+= keepregplus - endif exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e' NetrwKeepj call histdel("/",-1) exe 'nno w :call search(''^.\\|\s\s\zs\S'',''W'')'."\" -- cgit From 39094b3faedde9601160806901941e4808925410 Mon Sep 17 00:00:00 2001 From: butwerenotthereyet <58348703+butwerenotthereyet@users.noreply.github.com> Date: Tue, 10 Dec 2019 00:56:56 -0800 Subject: jumplist: browser-style (or 'tagstack') navigation #11530 Traditionally, when navigating to a specific location from the middle of the jumplist results in shifting the current location to the bottom of the list and adding the new location after it. This behavior is not desireable to all users--see, for example https://vi.stackexchange.com/questions/18344/how-to-change-jumplist-behavior. Here, another jumplist behavior is introduced. When jumpoptions (a new option set added here) includes stack, the jumplist behaves like the tagstack or like history in a web browser. That is, when navigating to a location from the middle of the jumplist 2 first 1 second 0 third <-- current location 1 fourth 2 fifth to a new location the locations after the current location in the jump list are discarded 2 first 1 second 0 third <-- current location The result is that when moving forward from that location, the new location will be appended to the jumplist: 3 first 2 second 1 third 0 new If the new location is the same new == second as some previous (but not immediately prior) entry in the jumplist, 2 first 1 second 0 third <-- current location 1 fourth 2 fifth both occurrences preserved 3 first 2 second 1 third 0 second (new) when moving forward from that location. It would be desireable to go farther and, when the new location is the same as the location that is currently next in the jumplist, new == fourth make the result of navigating to the new location by jumping (e.g. 50gg) be the same as moving forward in the jumplist 2 first 1 second 0 third 1 new <-- current location 2 fifth and simply increment the jumplist index. That change is NOT part of this patch because it would require passing the new cursor location to the function (setpcmark) from all of its callees. That in turn would require those callees to know *before* calling what the new cursor location is, which do they do not currently. --- runtime/doc/motion.txt | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ runtime/doc/options.txt | 11 ++++++++++ runtime/doc/quickref.txt | 1 + runtime/doc/vim_diff.txt | 5 +++++ 4 files changed, 71 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 07ff4cf030..3947e583b7 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -1083,6 +1083,60 @@ When you split a window, the jumplist will be copied to the new window. If you have included the ' item in the 'shada' option the jumplist will be stored in the ShaDa file and restored when starting Vim. + *jumplist-stack* +When jumpoptions includes "stack", the jumplist behaves like the history in a +web browser and like the tag stack. When jumping to a new location from the +middle of the jumplist, the locations after the current position will be +discarded. + +This behavior corresponds to the following situation in a web browser. +Navigate to first.com, second.com, third.com, fourth.com and then fifth.com. +Then navigate backwards twice so that third.com is displayed. At that point, +the history is: +- first.com +- second.com +- third.com <-- +- fourth.com +- fifth.com + +Finally, navigate to a different webpage, new.com. The history is +- first.com +- second.com +- third.com +- new.com <-- + +When the jumpoptions includes "stack", this is the behavior of neovim as well. +That is, given a jumplist like the following in which CTRL-O has been used to +move back three times to location X + + jump line col file/text + 2 1260 8 src/nvim/mark.c <-- location X-2 + 1 685 0 src/nvim/option_defs.h <-- location X-1 +> 0 462 36 src/nvim/option_defs.h <-- location X + 1 479 39 src/nvim/option_defs.h + 2 213 2 src/nvim/mark.c + 3 181 0 src/nvim/mark.c + +jumping to location Y results in the locations after the current locations being +removed: + + jump line col file/text + 3 1260 8 src/nvim/mark.c + 2 685 0 src/nvim/option_defs.h + 1 462 36 src/nvim/option_defs.h <-- location X +> + +Then, when yet another location Z is jumped to, the new location Y appears +directly after location X in the jumplist and location X remains in the same +position relative to the locations (X-1, X-2, etc., ...) that had been before it +prior to the original jump from X to Y: + + jump line col file/text + 4 1260 8 src/nvim/mark.c <-- location X-2 + 3 685 0 src/nvim/option_defs.h <-- location X-1 + 2 462 36 src/nvim/option_defs.h <-- location X + 1 100 0 src/nvim/option_defs.h <-- location Y +> CHANGE LIST JUMPS *changelist* *change-list-jumps* *E664* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 95265fa153..4b8740c5d2 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3457,6 +3457,17 @@ A jump table for the options with a short description can be found at |Q_op|. Unprintable and zero-width Unicode characters are displayed as . There is no option to specify these characters. + *'jumpoptions'* *'jop'* +'jumpoptions' 'jop' string (default "") + global + List of words that change the behavior of the |jumplist|. + stack Make the jumplist behave like the tagstack or like a + web browser. Relative location of entries in the + jumplist is preserved at the cost of discarding + subsequent entries when navigating backwards in the + jumplist and then jumping to a location. + |jumplist-stack| + *'joinspaces'* *'js'* *'nojoinspaces'* *'nojs'* 'joinspaces' 'js' boolean (default on) global diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index dfa7218bdf..224f14a18b 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -743,6 +743,7 @@ Short explanation of each option: *option-list* 'iskeyword' 'isk' characters included in keywords 'isprint' 'isp' printable characters 'joinspaces' 'js' two spaces after a period with a join command +'jumpoptions' 'jop' specifies how jumping is done 'keymap' 'kmp' name of a keyboard mapping 'keymodel' 'km' enable starting/stopping selection with keys 'keywordprg' 'kp' program to use for the "K" command diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 9c106077ab..64b5830575 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -336,6 +336,11 @@ Macro/|recording| behavior Motion: The |jumplist| avoids useless/phantom jumps. + When the new option |jumpoptions| includes 'stack', the jumplist behaves + like the tagstack or history in a web browser--jumping from the middle of + the jumplist discards the locations after the jumped-from position + (|jumplist-stack|). + Normal commands: |Q| is the same as |gQ| -- cgit From 19b6237087ebcf45427ceb6943d23ce33b39567f Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Fri, 9 Nov 2018 23:57:00 +0900 Subject: jobstart now supports env/clear_env to modify the environment of the launched job. --- runtime/doc/eval.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index ab65cc4560..597175b5e5 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5451,6 +5451,9 @@ jobstart({cmd}[, {opts}]) *jobstart()* |on_exit| : exit event handler (function name or |Funcref|) cwd : Working directory of the job; defaults to |current-directory|. + env : A dict of strings to append (or replace see + |clear_env|) to the current environment. + clear_env: If set, use the exact values passed in |env| rpc : If set, |msgpack-rpc| will be used to communicate with the job over stdin and stdout. "on_stdout" is then ignored, but "on_stderr" can still be used. -- cgit From 251b20e5334e1ff8af7fdd37ca1770ad485f031b Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Mon, 16 Dec 2019 14:08:55 -0500 Subject: Add support for the pum_getpos() API (#11562) Add support for the pum_getpos() API --- runtime/doc/autocmd.txt | 3 +++ runtime/doc/eval.txt | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 18dfa587e8..ac61297767 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -607,6 +607,9 @@ CompleteChanged *CompleteChanged* It is not allowed to change the text |textlock|. + The size and position of the popup are also + available by calling |pum_getpos()|. + *CursorHold* CursorHold When the user doesn't press a key for the time specified with 'updatetime'. Not re-triggered diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 597175b5e5..d02ed54e27 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2280,6 +2280,7 @@ pathshorten({expr}) String shorten directory names in a path pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} printf({fmt}, {expr1}...) String format text +pum_getpos() Dict position and size of pum if visible pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression py3eval({expr}) any evaluate |python3| expression @@ -3120,6 +3121,9 @@ complete_info([{what}]) the items listed in {what} are returned. Unsupported items in {what} are silently ignored. + To get the position of the popup menu, see |pum_getpos()|. It's + also available in |v:event| during the |CompleteChanged| event. + Examples: > " Get all items call complete_info() @@ -6521,6 +6525,19 @@ printf({fmt}, {expr1} ...) *printf()* arguments an error is given. Up to 18 arguments can be used. +pum_getpos() *pum_getpos()* + If the popup menu (see |ins-completion-menu|) is not visible, + returns an empty |Dictionary|, otherwise, returns a + |Dictionary| with the following keys: + height nr of items visible + width screen cells + row top screen row (0 first row) + col leftmost screen column (0 first col) + size total nr of items + scrollbar |TRUE| if visible + + The values are the same as in |v:event| during |CompleteChanged|. + pumvisible() *pumvisible()* Returns non-zero when the popup menu is visible, zero otherwise. See |ins-completion-menu|. -- cgit From 65aca4d857ab9ff278f410b17ef31d91e48a37b7 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Thu, 19 Dec 2019 21:27:21 +0100 Subject: TUI: can make the cursor transparent #11519 when setting 'guicursor' highlight blend=100. --- runtime/doc/term.txt | 2 ++ runtime/doc/ui.txt | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 4f4d379f01..6a271c08d3 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -149,6 +149,8 @@ Nvim will adjust the shape of the cursor from a block to a line when in insert mode (or as specified by the 'guicursor' option), on terminals that support it. It uses the same |terminfo| extensions that were pioneered by tmux for this: "Ss" and "Se". +Similarly, if you set the cursor highlight group with blend=100, Nvim hides +the cursor through the "cvvis" and "civis" extensions. If your terminfo definition is missing them, then Nvim will decide whether to add them to your terminfo definition, by looking at $TERM and other diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index d5f4a59ab3..de54ce59b6 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -294,7 +294,8 @@ numerical highlight ids to the actual attributes. `underline`: underlined text. The line has `special` color. `undercurl`: undercurled text. The curl has `special` color. `blend`: Blend level (0-100). Could be used by UIs to support - blending floating windows to the background. + blending floating windows to the background or to + signal a transparent cursor. For absent color keys the default color should be used. Don't store the default value in the table, rather a sentinel value, so that -- cgit From d00c624ba4731ba7bfe9bd71e2cc32e54f886342 Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Fri, 20 Dec 2019 05:46:47 -0500 Subject: LSP: fix omnifunc findstart (#11522) --- runtime/lua/vim/lsp.lua | 37 ++++++++++++++++--------------------- runtime/lua/vim/lsp/util.lua | 25 ++----------------------- 2 files changed, 18 insertions(+), 44 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 82f4fda66f..501cc6f670 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -859,29 +859,21 @@ function lsp.omnifunc(findstart, base) end if findstart == 1 then + -- First, just return the current cursor column, we only really need that return vim.fn.col('.') else + -- Then, perform standard completion request + log.info("base ", base) + local pos = vim.api.nvim_win_get_cursor(0) - local line = assert(nvim_buf_get_lines(bufnr, pos[1]-1, pos[1], false)[1]) + local line = vim.api.nvim_get_current_line() + local line_to_cursor = line:sub(1, pos[2]) local _ = log.trace() and log.trace("omnifunc.line", pos, line) - local line_to_cursor = line:sub(1, pos[2]+1) - local _ = log.trace() and log.trace("omnifunc.line_to_cursor", line_to_cursor) - local params = { - textDocument = { - uri = vim.uri_from_bufnr(bufnr); - }; - position = { - -- 0-indexed for both line and character - line = pos[1] - 1, - character = vim.str_utfindex(line, pos[2]), - }; - -- The completion context. This is only available if the client specifies - -- to send this using `ClientCapabilities.textDocument.completion.contextSupport === true` - -- context = nil or { - -- triggerKind = protocol.CompletionTriggerKind.Invoked; - -- triggerCharacter = nil or ""; - -- }; - } + + -- Get the start postion of the current keyword + local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local params = util.make_position_params() + -- TODO handle timeout error differently? Like via an error? local client_responses = lsp.buf_request_sync(bufnr, 'textDocument/completion', params) or {} local matches = {} @@ -889,12 +881,15 @@ function lsp.omnifunc(findstart, base) -- TODO how to handle errors? if not response.error then local data = response.result - local completion_items = util.text_document_completion_list_to_complete_items(data or {}, line_to_cursor) + local completion_items = util.text_document_completion_list_to_complete_items(data or {}) local _ = log.trace() and log.trace("omnifunc.completion_items", completion_items) vim.list_extend(matches, completion_items) end end - return matches + + -- Instead of returning matches call complete instead + vim.fn.complete(textMatch+1, matches) + return {} end end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 3798b30a46..8f27353698 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -19,21 +19,6 @@ local function npcall(fn, ...) return ok_or_nil(pcall(fn, ...)) end ---- Find the longest shared prefix between prefix and word. --- e.g. remove_prefix("123tes", "testing") == "ting" -local function remove_prefix(prefix, word) - local max_prefix_length = math.min(#prefix, #word) - local prefix_length = 0 - for i = 1, max_prefix_length do - local current_line_suffix = prefix:sub(-i) - local word_prefix = word:sub(1, i) - if current_line_suffix == word_prefix then - prefix_length = i - end - end - return word:sub(prefix_length + 1) -end - -- TODO(ashkan) @performance this could do less copying. function M.set_lines(lines, A, B, new_lines) -- 0-indexing to 1-indexing @@ -161,15 +146,11 @@ end --- Getting vim complete-items with incomplete flag. -- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) -- @return { matches = complete-items table, incomplete = boolean } -function M.text_document_completion_list_to_complete_items(result, line_prefix) +function M.text_document_completion_list_to_complete_items(result) local items = M.extract_completion_items(result) if vim.tbl_isempty(items) then return {} end - -- Only initialize if we have some items. - if not line_prefix then - line_prefix = M.get_current_line_to_cursor() - end local matches = {} @@ -187,10 +168,8 @@ function M.text_document_completion_list_to_complete_items(result, line_prefix) end local word = completion_item.insertText or completion_item.label - - -- Ref: `:h complete-items` table.insert(matches, { - word = remove_prefix(line_prefix, word), + word = word, abbr = completion_item.label, kind = protocol.CompletionItemKind[completion_item.kind] or '', menu = completion_item.detail or '', -- cgit From 026ba804d173c41ab99ee270c93f7975c1d6d713 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 20 Dec 2019 02:50:37 -0800 Subject: LSP: Improve the display of the default hover callback. (#11576) Strips the code blocks from markdown and does syntax highlighting. --- runtime/lua/vim/lsp/callbacks.lua | 14 ++-- runtime/lua/vim/lsp/util.lua | 136 +++++++++++++++++++++++++++++++++++--- 2 files changed, 138 insertions(+), 12 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 4fc3f74519..a3cd521b86 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -68,16 +68,22 @@ M['textDocument/completion'] = function(_, _, result) end M['textDocument/hover'] = function(_, method, result) - util.focusable_preview(method, function() + util.focusable_float(method, function() if not (result and result.contents) then - return { 'No information available' } + -- return { 'No information available' } + return end local markdown_lines = util.convert_input_to_markdown_lines(result.contents) markdown_lines = util.trim_empty_lines(markdown_lines) if vim.tbl_isempty(markdown_lines) then - return { 'No information available' } + -- return { 'No information available' } + return end - return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines) + local bufnr, winnr = util.fancy_floating_markdown(markdown_lines, { + pad_left = 1; pad_right = 1; + }) + util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, winnr) + return bufnr, winnr end) end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 8f27353698..4a781359d4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -19,11 +19,13 @@ local function npcall(fn, ...) return ok_or_nil(pcall(fn, ...)) end --- TODO(ashkan) @performance this could do less copying. function M.set_lines(lines, A, B, new_lines) -- 0-indexing to 1-indexing local i_0 = A[1] + 1 - local i_n = B[1] + 1 + -- If it extends past the end, truncate it to the end. This is because the + -- way the LSP describes the range including the last newline is by + -- specifying a line number after what we would call the last line. + local i_n = math.min(B[1] + 1, #lines) if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines}) end @@ -88,7 +90,7 @@ function M.apply_text_edits(text_edits, bufnr) table.sort(cleaned, edit_sort_key) local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol') - local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) == finish_line + 1 + local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) <= finish_line + 1 if set_eol and #lines[#lines] ~= 0 then table.insert(lines, '') end @@ -315,9 +317,9 @@ end -- Check if a window with `unique_name` tagged is associated with the current -- buffer. If not, make a new preview. -- --- fn()'s return values will be passed directly to open_floating_preview in the +-- fn()'s return bufnr, winnr -- case that a new floating window should be created. -function M.focusable_preview(unique_name, fn) +function M.focusable_float(unique_name, fn) if npcall(api.nvim_win_get_var, 0, unique_name) then return api.nvim_command("wincmd p") end @@ -330,9 +332,127 @@ function M.focusable_preview(unique_name, fn) return end end - local pbufnr, pwinnr = M.open_floating_preview(fn()) - api.nvim_win_set_var(pwinnr, unique_name, bufnr) - return pbufnr, pwinnr + local pbufnr, pwinnr = fn() + if pbufnr then + api.nvim_win_set_var(pwinnr, unique_name, bufnr) + return pbufnr, pwinnr + end +end + +-- Check if a window with `unique_name` tagged is associated with the current +-- buffer. If not, make a new preview. +-- +-- fn()'s return values will be passed directly to open_floating_preview in the +-- case that a new floating window should be created. +function M.focusable_preview(unique_name, fn) + return M.focusable_float(unique_name, function() + return M.open_floating_preview(fn()) + end) +end + +-- Convert markdown into syntax highlighted regions by stripping the code +-- blocks and converting them into highlighted code. +-- This will by default insert a blank line separator after those code block +-- regions to improve readability. +function M.fancy_floating_markdown(contents, opts) + local pad_left = opts and opts.pad_left + local pad_right = opts and opts.pad_right + local stripped = {} + local highlights = {} + do + local i = 1 + while i <= #contents do + local line = contents[i] + -- TODO(ashkan): use a more strict regex for filetype? + local ft = line:match("^```([a-zA-Z0-9_]*)$") + -- local ft = line:match("^```(.*)$") + -- TODO(ashkan): validate the filetype here. + if ft then + local start = #stripped + i = i + 1 + while i <= #contents do + line = contents[i] + if line == "```" then + i = i + 1 + break + end + table.insert(stripped, line) + i = i + 1 + end + table.insert(highlights, { + ft = ft; + start = start + 1; + finish = #stripped + 1 - 1; + }) + else + table.insert(stripped, line) + i = i + 1 + end + end + end + local width = 0 + for i, v in ipairs(stripped) do + v = v:gsub("\r", "") + if pad_left then v = (" "):rep(pad_left)..v end + if pad_right then v = v..(" "):rep(pad_right) end + stripped[i] = v + width = math.max(width, #v) + end + if opts and opts.max_width then + width = math.min(opts.max_width, width) + end + -- TODO(ashkan): decide how to make this customizable. + local insert_separator = true + if insert_separator then + for i, h in ipairs(highlights) do + h.start = h.start + i - 1 + h.finish = h.finish + i - 1 + if h.finish + 1 <= #stripped then + table.insert(stripped, h.finish + 1, string.rep("─", width)) + end + end + end + + -- Make the floating window. + local height = #stripped + local bufnr = api.nvim_create_buf(false, true) + local winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) + + -- Switch to the floating window to apply the syntax highlighting. + -- This is because the syntax command doesn't accept a target. + local cwin = vim.api.nvim_get_current_win() + vim.api.nvim_set_current_win(winnr) + + vim.cmd("ownsyntax markdown") + local idx = 1 + local function highlight_region(ft, start, finish) + if ft == '' then return end + local name = ft..idx + idx = idx + 1 + local lang = "@"..ft:upper() + -- TODO(ashkan): better validation before this. + if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then + return + end + vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s", name, start, finish + 1, lang)) + end + -- Previous highlight region. + -- TODO(ashkan): this wasn't working for some reason, but I would like to + -- make sure that regions between code blocks are definitely markdown. + -- local ph = {start = 0; finish = 1;} + for _, h in ipairs(highlights) do + -- highlight_region('markdown', ph.finish, h.start) + highlight_region(h.ft, h.start, h.finish) + -- ph = h + end + + vim.api.nvim_set_current_win(cwin) + return bufnr, winnr +end + +function M.close_preview_autocmd(events, winnr) + api.nvim_command("autocmd "..table.concat(events, ',').." ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") end function M.open_floating_preview(contents, filetype, opts) -- cgit From c147806e23d0ade781b8a298924150fe8d5887ad Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 20 Dec 2019 00:02:36 -0500 Subject: vim-patch:8.2.0019: cannot number of lines of another buffer Problem: Cannot number of lines of another buffer. Solution: Add "linecount" to getbufinfo(). (Yasuhiro Matsumoto, closes vim/vim#5370) https://github.com/vim/vim/commit/a9e9679de3ef082ee29868ab404283dfc53258f2 --- runtime/doc/eval.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d02ed54e27..77589b732d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4138,6 +4138,8 @@ getbufinfo([{dict}]) hidden TRUE if the buffer is hidden. listed TRUE if the buffer is listed. lnum current line number in buffer. + linecount number of lines in the buffer (only + valid when loaded) loaded TRUE if the buffer is loaded. name full path to the file in the buffer. signs list of signs placed in the buffer. -- cgit From a115f2314387706242ce5b9e5b2d7090a902d9cb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 20 Dec 2019 00:31:56 -0500 Subject: vim-patch:8.2.0024: filetype Rego not recognized Problem: Filetype Rego not recognized. Solution: Add *.rego. (Matt Dunford, closes vim/vim#5376) https://github.com/vim/vim/commit/a4ce82fe2e990eb9eaabf6ad400e2a74cce50ce6 --- runtime/filetype.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index e27d234a96..e66630259e 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1299,6 +1299,9 @@ au BufNewFile,BufRead *.reg " Renderman Interface Bytestream au BufNewFile,BufRead *.rib setf rib +" Rego Policy Language +au BufNewFile,BufRead *.rego setf rego + " Rexx au BufNewFile,BufRead *.rex,*.orx,*.rxo,*.rxj,*.jrexx,*.rexxj,*.rexx,*.testGroup,*.testUnit setf rexx -- cgit From ee7ac469c6f0f15a2d75991ef053a18d93e01756 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 20 Dec 2019 22:49:29 -0800 Subject: LSP: Use async completion for omnifunc. (#11578) --- runtime/lua/vim/lsp.lua | 55 ++++++++++++++++----------------------- runtime/lua/vim/lsp/callbacks.lua | 5 ++-- 2 files changed, 26 insertions(+), 34 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 501cc6f670..0ecf57f50c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -858,39 +858,30 @@ function lsp.omnifunc(findstart, base) end end - if findstart == 1 then - -- First, just return the current cursor column, we only really need that - return vim.fn.col('.') - else - -- Then, perform standard completion request - log.info("base ", base) - - local pos = vim.api.nvim_win_get_cursor(0) - local line = vim.api.nvim_get_current_line() - local line_to_cursor = line:sub(1, pos[2]) - local _ = log.trace() and log.trace("omnifunc.line", pos, line) - - -- Get the start postion of the current keyword - local textMatch = vim.fn.match(line_to_cursor, '\\k*$') - local params = util.make_position_params() - - -- TODO handle timeout error differently? Like via an error? - local client_responses = lsp.buf_request_sync(bufnr, 'textDocument/completion', params) or {} - local matches = {} - for _, response in pairs(client_responses) do - -- TODO how to handle errors? - if not response.error then - local data = response.result - local completion_items = util.text_document_completion_list_to_complete_items(data or {}) - local _ = log.trace() and log.trace("omnifunc.completion_items", completion_items) - vim.list_extend(matches, completion_items) - end - end + -- Then, perform standard completion request + local _ = log.info() and log.info("base ", base) + + local pos = vim.api.nvim_win_get_cursor(0) + local line = vim.api.nvim_get_current_line() + local line_to_cursor = line:sub(1, pos[2]) + local _ = log.trace() and log.trace("omnifunc.line", pos, line) + + -- Get the start postion of the current keyword + local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local params = util.make_position_params() + + local items = {} + lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, _, result) + if err or not result then return end + local matches = util.text_document_completion_list_to_complete_items(result) + -- TODO(ashkan): is this the best way to do this? + vim.list_extend(items, matches) + vim.fn.complete(textMatch+1, items) + end) - -- Instead of returning matches call complete instead - vim.fn.complete(textMatch+1, matches) - return {} - end + -- Return -2 to signal that we should continue completion so that we can + -- async complete. + return -2 end function lsp.client_is_stopped(client_id) diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index a3cd521b86..794140ee2e 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -62,9 +62,10 @@ M['textDocument/completion'] = function(_, _, result) local row, col = unpack(api.nvim_win_get_cursor(0)) local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) local line_to_cursor = line:sub(col+1) + local textMatch = vim.fn.match(line_to_cursor, '\\k*$') - local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor) - vim.fn.complete(col, matches) + local matches = util.text_document_completion_list_to_complete_items(result) + vim.fn.complete(textMatch+1, matches) end M['textDocument/hover'] = function(_, method, result) -- cgit From c60f656839304a4217117d3f3e28402f292bf069 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Sun, 22 Dec 2019 04:45:11 +0100 Subject: termdebug.vim: Comment out Winbar related things #11552 --- runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index b9fc77dc37..7a757ef7d6 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -442,7 +442,7 @@ func s:InstallCommands() command Gdb call win_gotoid(s:gdbwin) command Program call win_gotoid(s:ptywin) command Source call s:GotoSourcewinOrCreateIt() - command Winbar call s:InstallWinbar() + " command Winbar call s:InstallWinbar() " TODO: can the K mapping be restored? nnoremap K :Evaluate @@ -450,7 +450,7 @@ func s:InstallCommands() let &cpo = save_cpo endfunc -let s:winbar_winids = [] +" let s:winbar_winids = [] " Delete installed debugger commands in the current window. func s:DeleteCommands() @@ -467,7 +467,7 @@ func s:DeleteCommands() delcommand Gdb delcommand Program delcommand Source - delcommand Winbar + " delcommand Winbar nunmap K @@ -733,7 +733,7 @@ func s:GotoSourcewinOrCreateIt() if !win_gotoid(s:sourcewin) new let s:sourcewin = win_getid(winnr()) - call s:InstallWinbar() + " call s:InstallWinbar() endif endfunc @@ -764,7 +764,7 @@ func s:HandleCursor(msg) " TODO: find existing window exe 'split ' . fnameescape(fname) let s:sourcewin = win_getid(winnr()) - call s:InstallWinbar() + " call s:InstallWinbar() else exe 'edit ' . fnameescape(fname) endif -- cgit From 440695c29696f261337227e5c419aa1cf313c2dd Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 28 Sep 2019 14:27:20 +0200 Subject: tree-sitter: implement query functionality and highlighting prototype [skip.lint] --- runtime/doc/lua.txt | 96 ++++++++++++++++++++++++++ runtime/lua/vim/treesitter.lua | 120 +++++++++++++++++++++++++++++--- runtime/lua/vim/tshighlighter.lua | 142 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 350 insertions(+), 8 deletions(-) create mode 100644 runtime/lua/vim/tshighlighter.lua (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index c0da06ffe3..1c3a7f70c9 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -594,6 +594,102 @@ tsnode:named_descendant_for_range(start_row, start_col, end_row, end_col) Get the smallest named node within this node that spans the given range of (row, column) positions +Query methods *lua-treesitter-query* + +Tree-sitter queries are supported, with some limitations. Currently, the only +supported match predicate is `eq?` (both comparing a capture against a string +and two captures against each other). + +vim.treesitter.parse_query(lang, query) + *vim.treesitter.parse_query(()* + Parse the query as a string. (If the query is in a file, the caller + should read the contents into a string before calling). + +query:iter_captures(node, bufnr, start_row, end_row) + *query:iter_captures()* + Iterate over all captures from all matches inside a `node`. + `bufnr` is needed if the query contains predicates, then the caller + must ensure to use a freshly parsed tree consistent with the current + text of the buffer. `start_row` and `end_row` can be used to limit + matches inside a row range (this is typically used with root node + as the node, i e to get syntax highlight matches in the current + viewport) + + The iterator returns two values, a numeric id identifying the capture + and the captured node. The following example shows how to get captures + by name: +> + for id, node in query:iter_captures(tree:root(), bufnr, first, last) do + local name = query.captures[id] -- name of the capture in the query + -- typically useful info about the node: + local type = node:type() -- type of the captured node + local row1, col1, row2, col2 = node:range() -- range of the capture + ... use the info here ... + end +< +query:iter_matches(node, bufnr, start_row, end_row) + *query:iter_matches()* + Iterate over all matches within a node. The arguments are the same as + for |query:iter_captures()| but the iterated values are different: + an (1-based) index of the pattern in the query, and a table mapping + capture indices to nodes. If the query has more than one pattern + the capture table might be sparse, and e.g. `pairs` should be used and not + `ipairs`. Here an example iterating over all captures in + every match: +> + for pattern, match in cquery:iter_matches(tree:root(), bufnr, first, last) do + for id,node in pairs(match) do + local name = query.captures[id] + -- `node` was captured by the `name` capture in the match + ... use the info here ... + end + end +> +Treesitter syntax highlighting (WIP) *lua-treesitter-highlight* + +NOTE: This is a partially implemented feature, and not usable as a default +solution yet. What is documented here is a temporary interface indented +for those who want to experiment with this feature and contribute to +its development. + +Highlights are defined in the same query format as in the tree-sitter highlight +crate, which some limitations and additions. Set a highlight query for a +buffer with this code: > + + local query = [[ + "for" @keyword + "if" @keyword + "return" @keyword + + (string_literal) @string + (number_literal) @number + (comment) @comment + + (preproc_function_def name: (identifier) @function) + + ; ... more definitions + ]] + + highlighter = vim.treesitter.TSHighlighter.new(query, bufnr, lang) + -- alternatively, to use the current buffer and its filetype: + -- highlighter = vim.treesitter.TSHighlighter.new(query) + + -- Don't recreate the highlighter for the same buffer, instead + -- modify the query like this: + local query2 = [[ ... ]] + highlighter:set_query(query2) + +As mentioned above the supported predicate is currently only `eq?`. `match?` +predicates behave like matching always fails. As an addition a capture which +begin with an upper-case letter like `@WarningMsg` will map directly to this +highlight group, if defined. Also if the predicate begins with upper-case and +contains a dot only the part before the first will be interpreted as the +highlight group. As an example, this warns of a binary expression with two +identical identifiers, highlighting both as |hl-WarningMsg|: > + + ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) + (eq? @WarningMsg.left @WarningMsg.right)) + ------------------------------------------------------------------------------ VIM *lua-builtin* diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index e0202927bb..aa8b8fcdd1 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -12,9 +12,13 @@ function Parser:parse() if self.valid then return self.tree end - self.tree = self._parser:parse_buf(self.bufnr) + local changes + self.tree, changes = self._parser:parse_buf(self.bufnr) self.valid = true - return self.tree + for _, cb in ipairs(self.change_cbs) do + cb(changes) + end + return self.tree, changes end function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_size) @@ -26,17 +30,28 @@ function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_ self.valid = false end -local module = { +local M = { add_language=vim._ts_add_language, inspect_language=vim._ts_inspect_language, + parse_query = vim._ts_parse_query, } -function module.create_parser(bufnr, ft, id) +setmetatable(M, { + __index = function (t, k) + if k == "TSHighlighter" then + t[k] = require'vim.tshighlighter' + return t[k] + end + end + }) + +function M.create_parser(bufnr, ft, id) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end - local self = setmetatable({bufnr=bufnr, valid=false}, Parser) + local self = setmetatable({bufnr=bufnr, lang=ft, valid=false}, Parser) self._parser = vim._create_ts_parser(ft) + self.change_cbs = {} self:parse() -- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is -- using it. @@ -55,7 +70,7 @@ function module.create_parser(bufnr, ft, id) return self end -function module.get_parser(bufnr, ft) +function M.get_parser(bufnr, ft, cb) if bufnr == nil or bufnr == 0 then bufnr = a.nvim_get_current_buf() end @@ -65,9 +80,98 @@ function module.get_parser(bufnr, ft) local id = tostring(bufnr)..'_'..ft if parsers[id] == nil then - parsers[id] = module.create_parser(bufnr, ft, id) + parsers[id] = M.create_parser(bufnr, ft, id) + end + if cb ~= nil then + table.insert(parsers[id].change_cbs, cb) end return parsers[id] end -return module +-- query: pattern matching on trees +-- predicate matching is implemented in lua +local Query = {} +Query.__index = Query + +function M.parse_query(lang, query) + local self = setmetatable({}, Query) + self.query = vim._ts_parse_query(lang, query) + self.info = self.query:inspect() + self.captures = self.info.captures + return self +end + +local function get_node_text(node, bufnr) + local start_row, start_col, end_row, end_col = node:range() + if start_row ~= end_row then + return nil + end + local line = a.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1] + return string.sub(line, start_col+1, end_col) +end + +local function match_preds(match, preds, bufnr) + for _, pred in pairs(preds) do + if pred[1] == "eq?" then + local node = match[pred[2]] + local node_text = get_node_text(node, bufnr) + + local str + if type(pred[3]) == "string" then + -- (eq? @aa "foo") + str = pred[3] + else + -- (eq? @aa @bb) + str = get_node_text(match[pred[3]], bufnr) + end + + if node_text ~= str or str == nil then + return false + end + else + return false + end + end + return true +end + +function Query:iter_captures(node, bufnr, start, stop) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local raw_iter = node:_rawquery(self.query,true,start,stop) + local function iter() + local capture, captured_node, match = raw_iter() + if match ~= nil then + local preds = self.info.patterns[match.pattern] + local active = match_preds(match, preds, bufnr) + match.active = active + if not active then + return iter() -- tail call: try next match + end + end + return capture, captured_node + end + return iter +end + +function Query:iter_matches(node, bufnr, start, stop) + if bufnr == 0 then + bufnr = vim.api.nvim_get_current_buf() + end + local raw_iter = node:_rawquery(self.query,false,start,stop) + local function iter() + local pattern, match = raw_iter() + if match ~= nil then + local preds = self.info.patterns[pattern] + local active = (not preds) or match_preds(match, preds, bufnr) + if not active then + return iter() -- tail call: try next match + end + end + return pattern, match + end + return iter +end + +return M diff --git a/runtime/lua/vim/tshighlighter.lua b/runtime/lua/vim/tshighlighter.lua new file mode 100644 index 0000000000..1544ecbf49 --- /dev/null +++ b/runtime/lua/vim/tshighlighter.lua @@ -0,0 +1,142 @@ +local a = vim.api + +-- support reload for quick experimentation +local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} +TSHighlighter.__index = TSHighlighter + +-- These are conventions defined by tree-sitter, though it +-- needs to be user extensible also. +-- TODO(bfredl): this is very much incomplete, we will need to +-- go through a few tree-sitter provided queries and decide +-- on translations that makes the most sense. +TSHighlighter.hl_map = { + keyword="Keyword", + string="String", + type="Type", + comment="Comment", + constant="Constant", + operator="Operator", + number="Number", + label="Label", + ["function"]="Function", + ["function.special"]="Function", +} + +function TSHighlighter.new(query, bufnr, ft) + local self = setmetatable({}, TSHighlighter) + self.parser = vim.treesitter.get_parser(bufnr, ft, function(...) self:on_change(...) end) + self.buf = self.parser.bufnr + -- TODO(bfredl): perhaps on_start should be called uncondionally, instead for only on mod? + local tree = self.parser:parse() + self.root = tree:root() + self:set_query(query) + self.edit_count = 0 + self.redraw_count = 0 + self.line_count = {} + a.nvim_buf_set_option(self.buf, "syntax", "") + a.nvim__buf_set_luahl(self.buf, { + on_start=function(...) return self:on_start(...) end, + on_window=function(...) return self:on_window(...) end, + on_line=function(...) return self:on_line(...) end, + }) + + -- Tricky: if syntax hasn't been enabled, we need to reload color scheme + -- but use synload.vim rather than syntax.vim to not enable + -- syntax FileType autocmds. Later on we should integrate with the + -- `:syntax` and `set syntax=...` machinery properly. + if vim.g.syntax_on ~= 1 then + vim.api.nvim_command("runtime! syntax/synload.vim") + end + return self +end + +function TSHighlighter:set_query(query) + if type(query) == "string" then + query = vim.treesitter.parse_query(self.parser.lang, query) + end + self.query = query + + self.id_map = {} + for i, capture in ipairs(self.query.captures) do + local hl = 0 + local firstc = string.sub(capture, 1, 1) + local hl_group = self.hl_map[capture] + if firstc ~= string.lower(firstc) then + hl_group = vim.split(capture, '.', true)[1] + end + if hl_group then + hl = a.nvim_get_hl_id_by_name(hl_group) + end + self.id_map[i] = hl + end +end + +function TSHighlighter:on_change(changes) + for _, ch in ipairs(changes or {}) do + a.nvim__buf_redraw_range(self.buf, ch[1], ch[3]+1) + end + self.edit_count = self.edit_count + 1 +end + +function TSHighlighter:on_start(_, _buf, _tick) + local tree = self.parser:parse() + self.root = tree:root() +end + +function TSHighlighter:on_window(_, _win, _buf, _topline, botline) + self.iter = nil + self.active_nodes = {} + self.nextrow = 0 + self.botline = botline + self.redraw_count = self.redraw_count + 1 +end + +function TSHighlighter:on_line(_, _win, buf, line) + if self.iter == nil then + self.iter = self.query:iter_captures(self.root,buf,line,self.botline) + end + while line >= self.nextrow do + local capture, node, match = self.iter() + local active = true + if capture == nil then + break + end + if match ~= nil then + active = self:run_pred(match) + match.active = active + end + local start_row, start_col, end_row, end_col = node:range() + local hl = self.id_map[capture] + if hl > 0 and active then + if start_row == line and end_row == line then + a.nvim__put_attr(hl, start_col, end_col) + elseif end_row >= line then + -- TODO(bfredl): this is quite messy. Togheter with multiline bufhl we should support + -- luahl generating multiline highlights (and other kinds of annotations) + self.active_nodes[{hl=hl, start_row=start_row, start_col=start_col, end_row=end_row, end_col=end_col}] = true + end + end + if start_row > line then + self.nextrow = start_row + end + end + for node,_ in pairs(self.active_nodes) do + if node.start_row <= line and node.end_row >= line then + local start_col, end_col = node.start_col, node.end_col + if node.start_row < line then + start_col = 0 + end + if node.end_row > line then + end_col = 9000 + end + a.nvim__put_attr(node.hl, start_col, end_col) + end + if node.end_row <= line then + self.active_nodes[node] = nil + end + end + self.line_count[line] = (self.line_count[line] or 0) + 1 + --return tostring(self.line_count[line]) +end + +return TSHighlighter -- cgit From b3686b1597ea202de464df72a88fb5c76fd1b814 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 24 Dec 2019 07:53:56 +0100 Subject: system(), jobstart(): raise error on non-executable #11234 * tv_to_argv: error when cmd is not executable Callers always assume that emsg was emitted: - https://github.com/neovim/neovim/blob/57fbf288/src/nvim/eval.c#L12509 - https://github.com/neovim/neovim/blob/57fbf288/src/nvim/eval.c#L17923 - https://github.com/neovim/neovim/blob/57fbf288/src/nvim/eval.c#L18202 * test/functional/provider: display reason from missing_provider * provider#node#Detect: skip / handle non-existing node executable --- runtime/autoload/provider/node.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index b2a3b3ee08..c5d5e87729 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -51,6 +51,9 @@ function! provider#node#Detect() abort if exists('g:node_host_prog') return expand(g:node_host_prog) endif + if !executable('node') + return '' + endif if !s:is_minimum_version(v:null, 6, 0) return '' endif -- cgit From 07a2260e1d5fa8c7c4e2ec1ff8679a120fe399e8 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 24 Dec 2019 14:28:09 -0800 Subject: LSP: Handle rpc RequestCancelled specifically. (#11606) This was creating extra noise in errors that we should've been handling internally. Fixes #11515 --- runtime/lua/vim/lsp/rpc.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index a558f66a42..72a0bf8d6f 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -1,3 +1,4 @@ +local vim = vim local uv = vim.loop local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') @@ -377,6 +378,22 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para decoded.error = convert_NIL(decoded.error) decoded.result = convert_NIL(decoded.result) + -- Do not surface RequestCancelled to users, it is RPC-internal. + if decoded.error + and decoded.error.code == protocol.ErrorCodes.RequestCancelled then + local _ = log.debug() and log.debug("Received cancellation ack", decoded) + local result_id = tonumber(decoded.id) + -- Clear any callback since this is cancelled now. + -- This is safe to do assuming that these conditions hold: + -- - The server will not send a result callback after this cancellation. + -- - If the server sent this cancellation ACK after sending the result, the user of this RPC + -- client will ignore the result themselves. + if result_id then + message_callbacks[result_id] = nil + end + return + end + -- We sent a number, so we expect a number. local result_id = tonumber(decoded.id) local callback = message_callbacks[result_id] -- cgit From bbad324b175f1bd35201f0ba73fba1b11af7093d Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Sun, 8 Dec 2019 01:05:49 +0100 Subject: fillchars: adding foldopen, foldsep, foldclose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You can try it with set fillchars+=foldopen:▾,foldsep:│,foldclose:▸ --- runtime/doc/options.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4b8740c5d2..519cbf66b1 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2371,6 +2371,9 @@ A jump table for the options with a short description can be found at |Q_op|. stlnc:c ' ' or '=' statusline of the non-current windows vert:c '│' or '|' vertical separators |:vsplit| fold:c '·' or '-' filling 'foldtext' + foldopen:c '-' mark the beginning of a fold + foldclose:c '+' show a closed fold + foldsep:c '|' open fold middle marker diff:c '-' deleted lines of the 'diff' option msgsep:c ' ' message separator 'display' eob:c '~' empty lines at the end of a buffer -- cgit From 4f4c06a7a46e13e59381183818e839691b55702b Mon Sep 17 00:00:00 2001 From: Rafik Draoui Date: Sat, 28 Dec 2019 02:07:27 -0500 Subject: doc: update 'cpoptions' default value #11619 Since version 0.2, the `_` compatibility option is turned on by default. ref 1743df82f9003514c384ff99779d82179e6cb999 ref https://github.com/neovim/neovim/blob/b0196586debd55b9b8be62966084eee12bf0e4ad/src/nvim/option_defs.h#L135 --- runtime/doc/options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 519cbf66b1..57e3a00f89 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1486,7 +1486,7 @@ A jump table for the options with a short description can be found at |Q_op|. See 'preserveindent'. *'cpoptions'* *'cpo'* *cpo* -'cpoptions' 'cpo' string (Vim default: "aABceFs", +'cpoptions' 'cpo' string (Vim default: "aABceFs_", Vi default: all flags) global A sequence of single character flags. When a character is present -- cgit From 680693e263576e34d5947c43ab0ae3ff0ebfeab5 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sun, 29 Dec 2019 02:28:00 +0900 Subject: runtime: Add vim.lsp.get_client_by_name (#11603) Since the client name is more obvious than the client id for the user, add an API to get the lsp client by the client name. --- runtime/doc/lsp.txt | 10 ++++++++-- runtime/lua/vim/lsp.lua | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d6d16b8481..d54c227973 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -72,8 +72,9 @@ the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". *lsp-core-api* These are the core api functions for working with clients. You will mainly be using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations -and |vim.lsp.get_client_by_id()| to retrieve a client by its id after it has -initialized (or {config.on_init}. see below) +and |vim.lsp.get_client_by_id()| and |vim.lsp.get_client_by_name()| to retrieve +a client by its id or name after it has initialized (or {config.on_init}. see +below) *vim.lsp.start_client()* @@ -265,6 +266,11 @@ vim.lsp.get_client_by_id({client_id}) Look up an active client by its id, returns nil if it is not yet initialized or is not a valid id. Returns |lsp-client| + *vim.lsp.get_client_by_name()* +vim.lsp.get_client_by_name({client_name}) + + Look up an active client by its name, returns nil if it is not yet initialized + or is not a valid name. Returns |lsp-client| *vim.lsp.stop_client()* vim.lsp.stop_client({client_id}, [{force}]) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0ecf57f50c..042ed7bcfe 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -697,6 +697,16 @@ function lsp.get_client_by_id(client_id) return active_clients[client_id] end +-- Look up an active client by its name, returns nil if it is not yet initialized +-- or is not a valid name. +-- @param client_name string the client name. +function lsp.get_client_by_name(client_name) + for _, client in pairs(active_clients) do + if client.name == client_name then return client end + end + return nil +end + -- Stop a client by its id, optionally with force. -- You can also use the `stop()` function on a client if you already have -- access to it. -- cgit From ddffd3173730e744b4c25094be45e3673f7a186d Mon Sep 17 00:00:00 2001 From: artem-nefedov Date: Sun, 29 Dec 2019 10:57:28 +0300 Subject: netrw.vim: do not save +/* registers p.2 #11625 remove last place where system clipboard was used by netrw (extends 6c22c7ab97cca9f8dda6863ee7f1db1ce30a3451) fix #11592 --- runtime/autoload/netrw.vim | 8 -------- 1 file changed, 8 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index 9b1266c4ca..fa86223d53 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -1648,10 +1648,6 @@ fun! s:NetrwOptionsSave(vt) if g:netrw_keepdir let {a:vt}netrw_dirkeep = getcwd() endif - if has("clipboard") - sil! let {a:vt}netrw_starkeep = @* - sil! let {a:vt}netrw_pluskeep = @+ - endif sil! let {a:vt}netrw_slashkeep= @/ " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("")) @@ -1800,10 +1796,6 @@ fun! s:NetrwOptionsRestore(vt) unlet {a:vt}netrw_dirkeep endif endif - if has("clipboard") - call s:NetrwRestoreSetting(a:vt."netrw_starkeep","@*") - call s:NetrwRestoreSetting(a:vt."netrw_pluskeep","@+") - endif call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/") " call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("")) -- cgit From 34a59242a0d42687a49119cca590e7b4203496ef Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sun, 29 Dec 2019 00:05:32 -0800 Subject: Revert "runtime: Add vim.lsp.get_client_by_name" #11623 reverts 680693e263576e34d5947c43ab0ae3ff0ebfeab5 #11603 --- runtime/doc/lsp.txt | 10 ++-------- runtime/lua/vim/lsp.lua | 10 ---------- 2 files changed, 2 insertions(+), 18 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d54c227973..d6d16b8481 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -72,9 +72,8 @@ the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". *lsp-core-api* These are the core api functions for working with clients. You will mainly be using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations -and |vim.lsp.get_client_by_id()| and |vim.lsp.get_client_by_name()| to retrieve -a client by its id or name after it has initialized (or {config.on_init}. see -below) +and |vim.lsp.get_client_by_id()| to retrieve a client by its id after it has +initialized (or {config.on_init}. see below) *vim.lsp.start_client()* @@ -266,11 +265,6 @@ vim.lsp.get_client_by_id({client_id}) Look up an active client by its id, returns nil if it is not yet initialized or is not a valid id. Returns |lsp-client| - *vim.lsp.get_client_by_name()* -vim.lsp.get_client_by_name({client_name}) - - Look up an active client by its name, returns nil if it is not yet initialized - or is not a valid name. Returns |lsp-client| *vim.lsp.stop_client()* vim.lsp.stop_client({client_id}, [{force}]) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 042ed7bcfe..0ecf57f50c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -697,16 +697,6 @@ function lsp.get_client_by_id(client_id) return active_clients[client_id] end --- Look up an active client by its name, returns nil if it is not yet initialized --- or is not a valid name. --- @param client_name string the client name. -function lsp.get_client_by_name(client_name) - for _, client in pairs(active_clients) do - if client.name == client_name then return client end - end - return nil -end - -- Stop a client by its id, optionally with force. -- You can also use the `stop()` function on a client if you already have -- access to it. -- cgit From 703ed11c97256997aa0ce8aa5fe04b6e89e8e829 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 10 Sep 2019 00:42:59 -0400 Subject: vim-patch:8.0.1491: the minimum width of the popup menu is hard coded Problem: The minimum width of the popup menu is hard coded. Solution: Add the 'pumwidth' option. (Christian Brabandt, James McCoy, closes vim/vim#2314) https://github.com/vim/vim/commit/a8f04aa275984183bab5bb583b128f38c64abb69 --- runtime/doc/options.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 57e3a00f89..65981ceff5 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4529,6 +4529,13 @@ A jump table for the options with a short description can be found at |Q_op|. global Determines the maximum number of items to show in the popup menu for Insert mode completion. When zero as much space as available is used. + |ins-completion-menu|. + + *'pumwidth'* *'pw'* +'pumwidth' 'pw' number (default 0) + global + Determines the minium width to use for the popup menu for Insert mode + completion. When zero the default of 15 screen cells is used. |ins-completion-menu|. *'pumblend'* *'pb'* -- cgit From 669d675ef3f7c7ed4c8b702f53e3a77a986bc7cb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 10 Sep 2019 18:54:31 -0400 Subject: vim-patch:8.0.1495: having 'pumwidth' default to zero has no merit Problem: Having 'pumwidth' default to zero has no merit. Solution: Make the default 15, as the actual default value. https://github.com/vim/vim/commit/42443c7d7fecc3a2a72154bb6139b028438617c2 Includes 'pumwidth' documentation changes from 8.0.1531. Sort 'pum*' option in alphabetical order. --- runtime/doc/options.txt | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 65981ceff5..4b832a8606 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4524,20 +4524,6 @@ A jump table for the options with a short description can be found at |Q_op|. global When on a ":" prompt is used in Ex mode. - *'pumheight'* *'ph'* -'pumheight' 'ph' number (default 0) - global - Determines the maximum number of items to show in the popup menu for - Insert mode completion. When zero as much space as available is used. - |ins-completion-menu|. - - *'pumwidth'* *'pw'* -'pumwidth' 'pw' number (default 0) - global - Determines the minium width to use for the popup menu for Insert mode - completion. When zero the default of 15 screen cells is used. - |ins-completion-menu|. - *'pumblend'* *'pb'* 'pumblend' 'pb' number (default 0) global @@ -4554,6 +4540,19 @@ A jump table for the options with a short description can be found at |Q_op|. < UI-dependent. Works best with RGB colors. 'termguicolors' + *'pumheight'* *'ph'* +'pumheight' 'ph' number (default 0) + global + Determines the maximum number of items to show in the popup menu for + Insert mode completion. When zero as much space as available is used. + |ins-completion-menu|. + + *'pumwidth'* *'pw'* +'pumwidth' 'pw' number (default 15) + global + Determines the minium width to use for the popup menu for Insert mode + completion. |ins-completion-menu|. + *'pyxversion'* *'pyx'* 'pyxversion' 'pyx' number (default depends on the build) global -- cgit From 1e693ac97d44f0cd0edcdb31d883d2a27c9d30ce Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 26 Dec 2019 23:21:13 -0500 Subject: vim-patch:8.1.1303: not possible to hide a balloon Problem: Not possible to hide a balloon. Solution: Hide the balloon when balloon_show() is called with an empty string or list. Add balloon_gettext(). https://github.com/vim/vim/commit/be0a2597ae0d9eb0b8a8a2fc9ae1784faa929844 --- runtime/doc/eval.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 77589b732d..c376e07cff 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3770,6 +3770,8 @@ feedkeys({string} [, {mode}]) *feedkeys()* and "\..." notation |expr-quote|. For example, feedkeys("\") simulates pressing of the key. But feedkeys('\') pushes 5 characters. + A special code that might be useful is , it exits the + wait for a character without doing anything. ** {mode} is a String, which can contain these character flags: 'm' Remap keys. This is default. If {mode} is absent, -- cgit From 6c606c1191287752d174439874fc7e820272cc49 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 29 Dec 2019 00:44:11 -0500 Subject: vim-patch:8.1.1875: cannot get size and position of the popup menu Problem: Cannot get size and position of the popup menu. Solution: Add pum_getpos(). (Ben Jackson, closes vim/vim#4827) https://github.com/vim/vim/commit/e9bd57286a5cbb0e1ec18b5d194dc4af1bda9f3a https://github.com/neovim/neovim/pull/11562 backported the vim patch. This patch only updates the runtime/doc/ files to match Vim. --- runtime/doc/autocmd.txt | 14 +++++++------- runtime/doc/eval.txt | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index ac61297767..97379bfa27 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -583,13 +583,6 @@ ColorSchemePre Before loading a color scheme. |:colorscheme| Useful to setup removing things added by a color scheme, before another one is loaded. - *CompleteDone* -CompleteDone After Insert mode completion is done. Either - when something was completed or abandoning - completion. |ins-completion| - The |v:completed_item| variable contains the - completed item. - CompleteChanged *CompleteChanged* After each time the Insert mode completion menu changed. Not fired on popup menu hide, @@ -610,6 +603,13 @@ CompleteChanged *CompleteChanged* The size and position of the popup are also available by calling |pum_getpos()|. + *CompleteDone* +CompleteDone After Insert mode completion is done. Either + when something was completed or abandoning + completion. |ins-completion| + The |v:completed_item| variable contains the + completed item. + *CursorHold* CursorHold When the user doesn't press a key for the time specified with 'updatetime'. Not re-triggered diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c376e07cff..fecb8b5f74 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3121,8 +3121,9 @@ complete_info([{what}]) the items listed in {what} are returned. Unsupported items in {what} are silently ignored. - To get the position of the popup menu, see |pum_getpos()|. It's - also available in |v:event| during the |CompleteChanged| event. + To get the position and size of the popup menu, see + |pum_getpos()|. It's also available in |v:event| during the + |CompleteChanged| event. Examples: > " Get all items -- cgit From b81547ce6d182e2a7518e5a2f80d1ee90c5cdb8f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 24 Dec 2019 00:04:14 -0800 Subject: gen_vimdoc.py: better handling of inline (non-block) nodes --- runtime/doc/api.txt | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index d52a9a8409..aa69186cf0 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -540,6 +540,11 @@ nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()* See also: ~ nvim_get_hl_by_name +nvim_get_hl_id_by_name({name}) *nvim_get_hl_id_by_name()* + Gets a highlight group by name + + similar to |hlID()|, but allocates a new ID if not present. + nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* Sends input-keys to Nvim, subject to various quirks controlled by `mode` flags. This is a blocking call, unlike @@ -903,7 +908,7 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* {buffer} Buffer to display, or 0 for current buffer {enter} Enter the window (make it the current window) {config} Map defining the window configuration. Keys: - • `relative` : Sets the window layout to "floating", placed + • `relative`: Sets the window layout to "floating", placed at (row,col) coordinates relative to: • "editor" The global editor grid • "win" Window given by the `win` field, or @@ -1504,6 +1509,13 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* TODO: Documentation +nvim__put_attr({id}, {c0}, {c1}) *nvim__put_attr()* + Set attrs in nvim__buf_set_lua_hl callbacks + + TODO(bfredl): This is rather pedestrian. The final interface + should probably be derived from a reformed bufhl/virttext + interface with full support for multi-line ranges etc + ============================================================================== Buffer Functions *api-buffer* @@ -1599,6 +1611,22 @@ nvim_buf_detach({buffer}) *nvim_buf_detach()* |nvim_buf_attach()| |api-lua-detach| for detaching Lua callbacks +nvim__buf_set_luahl({buffer}, {opts}) *nvim__buf_set_luahl()* + Unstabilized interface for defining syntax hl in lua. + + This is not yet safe for general use, lua callbacks will need + to be restricted, like textlock and probably other stuff. + + The API on_line/nvim__put_attr is quite raw and not intended + to be the final shape. Ideally this should operate on chunks + larger than a single line to reduce interpreter overhead, and + generate annotation objects (bufhl/virttext) on the fly but + using the same representation. + + *nvim__buf_redraw_range()* +nvim__buf_redraw_range({buffer}, {first}, {last}) + TODO: Documentation + *nvim_buf_get_lines()* nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) Gets a line-range from the buffer. -- cgit From c24f8f46b45f097dbe0abf7e6ae686bc9bec5568 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 28 Dec 2019 03:27:25 -0800 Subject: gen_vimdoc.py: sort by name --- runtime/doc/api.txt | 2026 +++++++++++++++++++++++++-------------------------- runtime/doc/lua.txt | 146 ++-- 2 files changed, 1086 insertions(+), 1086 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index aa69186cf0..7d45330b66 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -479,193 +479,110 @@ created for extmark changes. ============================================================================== Global Functions *api-global* -nvim_exec({src}, {output}) *nvim_exec()* - Executes Vimscript (multiline block of Ex-commands), like - anonymous |:source|. - - Unlike |nvim_command()| this function supports heredocs, - script-scope (s:), etc. +nvim__id({obj}) *nvim__id()* + Returns object given as argument. - On execution error: fails with VimL error, does not update - v:errmsg. + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {src} Vimscript code - {output} Capture and return all (non-error, non-shell - |:!|) output + {obj} Object to return. Return: ~ - Output (non-error, non-shell |:!|) if `output` is true, - else empty string. - - See also: ~ - |execute()| - |nvim_command()| - -nvim_command({command}) *nvim_command()* - Executes an ex-command. - - On execution error: fails with VimL error, does not update - v:errmsg. - - Parameters: ~ - {command} Ex-command string + its argument. - See also: ~ - |nvim_exec()| +nvim__id_array({arr}) *nvim__id_array()* + Returns array given as argument. -nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()* - Gets a highlight definition by name. + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {name} Highlight group name - {rgb} Export RGB colors + {arr} Array to return. Return: ~ - Highlight definition map + its argument. - See also: ~ - nvim_get_hl_by_id +nvim__id_dictionary({dct}) *nvim__id_dictionary()* + Returns dictionary given as argument. -nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()* - Gets a highlight definition by id. |hlID()| + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {hl_id} Highlight id as returned by |hlID()| - {rgb} Export RGB colors + {dct} Dictionary to return. Return: ~ - Highlight definition map - - See also: ~ - nvim_get_hl_by_name - -nvim_get_hl_id_by_name({name}) *nvim_get_hl_id_by_name()* - Gets a highlight group by name - - similar to |hlID()|, but allocates a new ID if not present. + its argument. -nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* - Sends input-keys to Nvim, subject to various quirks controlled - by `mode` flags. This is a blocking call, unlike - |nvim_input()|. +nvim__id_float({flt}) *nvim__id_float()* + Returns floating-point value given as argument. - On execution error: does not fail, but updates v:errmsg. + This API function is used for testing. One should not rely on + its presence in plugins. Parameters: ~ - {keys} to be typed - {mode} behavior flags, see |feedkeys()| - {escape_csi} If true, escape K_SPECIAL/CSI bytes in - `keys` - - See also: ~ - feedkeys() - vim_strsave_escape_csi + {flt} Value to return. -nvim_input({keys}) *nvim_input()* - Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a - low-level input buffer and the call is non-blocking (input is - processed asynchronously by the eventloop). + Return: ~ + its argument. - On execution error: does not fail, but updates v:errmsg. +nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* + TODO: Documentation - Note: - |keycodes| like are translated, so "<" is special. To - input a literal "<", send . - Note: - For mouse events use |nvim_input_mouse()|. The pseudokey - form "" is deprecated since - |api-level| 6. +nvim__put_attr({id}, {c0}, {c1}) *nvim__put_attr()* + Set attrs in nvim__buf_set_lua_hl callbacks - Attributes: ~ - {fast} + TODO(bfredl): This is rather pedestrian. The final interface + should probably be derived from a reformed bufhl/virttext + interface with full support for multi-line ranges etc - Parameters: ~ - {keys} to be typed +nvim__stats() *nvim__stats()* + Gets internal stats. Return: ~ - Number of bytes actually written (can be fewer than - requested if the buffer becomes full). - - *nvim_input_mouse()* -nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col}) - Send mouse event from GUI. - - Non-blocking: does not wait on any result, but queues the - event to be processed soon by the event loop. - - Note: - Currently this doesn't support "scripting" multiple mouse - events by calling it multiple times in a loop: the - intermediate mouse positions will be ignored. It should be - used to implement real-time mouse input in a GUI. The - deprecated pseudokey form ("") of - |nvim_input()| has the same limitiation. - - Attributes: ~ - {fast} + Map of various internal stats. - Parameters: ~ - {button} Mouse button: one of "left", "right", - "middle", "wheel". - {action} For ordinary buttons, one of "press", "drag", - "release". For the wheel, one of "up", "down", - "left", "right". - {modifier} String of modifiers each represented by a - single char. The same specifiers are used as - for a key press, except that the "-" separator - is optional, so "C-A-", "c-a" and "CA" can all - be used to specify Ctrl+Alt+click. - {grid} Grid number if the client uses |ui-multigrid|, - else 0. - {row} Mouse row-position (zero-based, like redraw - events) - {col} Mouse column-position (zero-based, like redraw - events) +nvim_call_atomic({calls}) *nvim_call_atomic()* + Calls many API methods atomically. - *nvim_replace_termcodes()* -nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) - Replaces terminal codes and |keycodes| (, , ...) in a - string with the internal representation. + This has two main usages: + 1. To perform several requests from an async context + atomically, i.e. without interleaving redraws, RPC requests + from other clients, or user interactions (however API + methods may trigger autocommands or event processing which + have such side-effects, e.g. |:sleep| may wake timers). + 2. To minimize RPC overhead (roundtrips) of a sequence of many + requests. Parameters: ~ - {str} String to be converted. - {from_part} Legacy Vim parameter. Usually true. - {do_lt} Also translate . Ignored if `special` is - false. - {special} Replace |keycodes|, e.g. becomes a "\n" - char. + {calls} an array of calls, where each call is described + by an array with two elements: the request name, + and an array of arguments. - See also: ~ - replace_termcodes - cpoptions + Return: ~ + Array of two elements. The first is an array of return + values. The second is NIL if all calls succeeded. If a + call resulted in an error, it is a three-element array + with the zero-based index of the call which resulted in an + error, the error type and the error message. If an error + occurred, the values from all preceding calls will still + be returned. -nvim_eval({expr}) *nvim_eval()* - Evaluates a VimL |expression|. Dictionaries and Lists are - recursively expanded. +nvim_call_dict_function({dict}, {fn}, {args}) *nvim_call_dict_function()* + Calls a VimL |Dictionary-function| with the given arguments. On execution error: fails with VimL error, does not update v:errmsg. Parameters: ~ - {expr} VimL expression string - - Return: ~ - Evaluation result or expanded object - -nvim_exec_lua({code}, {args}) *nvim_exec_lua()* - Execute Lua code. Parameters (if any) are available as `...` - inside the chunk. The chunk can return a value. - - Only statements are executed. To evaluate an expression, - prefix it with `return` : return my_function(...) - - Parameters: ~ - {code} Lua code to execute - {args} Arguments to the code + {dict} Dictionary, or String evaluating to a VimL |self| + dict + {fn} Name of the function defined on the VimL dict + {args} Function arguments packed in an Array Return: ~ - Return value of Lua code if present or NIL. + Result of the function call nvim_call_function({fn}, {args}) *nvim_call_function()* Calls a VimL function with the given arguments. @@ -680,146 +597,237 @@ nvim_call_function({fn}, {args}) *nvim_call_function()* Return: ~ Result of the function call -nvim_call_dict_function({dict}, {fn}, {args}) *nvim_call_dict_function()* - Calls a VimL |Dictionary-function| with the given arguments. +nvim_command({command}) *nvim_command()* + Executes an ex-command. On execution error: fails with VimL error, does not update v:errmsg. Parameters: ~ - {dict} Dictionary, or String evaluating to a VimL |self| - dict - {fn} Name of the function defined on the VimL dict - {args} Function arguments packed in an Array + {command} Ex-command string - Return: ~ - Result of the function call + See also: ~ + |nvim_exec()| -nvim_strwidth({text}) *nvim_strwidth()* - Calculates the number of display cells occupied by `text` . - counts as one cell. +nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* + Creates a new, empty, unnamed buffer. Parameters: ~ - {text} Some text + {listed} Sets 'buflisted' + {scratch} Creates a "throwaway" |scratch-buffer| for + temporary work (always 'nomodified') Return: ~ - Number of cells + Buffer handle, or 0 on error -nvim_list_runtime_paths() *nvim_list_runtime_paths()* - Gets the paths contained in 'runtimepath'. + See also: ~ + buf_open_scratch - Return: ~ - List of paths +nvim_create_namespace({name}) *nvim_create_namespace()* + Creates a new namespace, or gets an existing one. -nvim_set_current_dir({dir}) *nvim_set_current_dir()* - Changes the global working directory. + Namespaces are used for buffer highlights and virtual text, + see |nvim_buf_add_highlight()| and + |nvim_buf_set_virtual_text()|. - Parameters: ~ - {dir} Directory path + Namespaces can be named or anonymous. If `name` matches an + existing namespace, the associated id is returned. If `name` + is an empty string a new, anonymous namespace is created. -nvim_get_current_line() *nvim_get_current_line()* - Gets the current line. + Parameters: ~ + {name} Namespace name or empty string Return: ~ - Current line string - -nvim_set_current_line({line}) *nvim_set_current_line()* - Sets the current line. - - Parameters: ~ - {line} Line contents + Namespace id nvim_del_current_line() *nvim_del_current_line()* Deletes the current line. -nvim_get_var({name}) *nvim_get_var()* - Gets a global (g:) variable. +nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()* + Unmaps a global |mapping| for the given mode. + + To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|. + + See also: ~ + |nvim_set_keymap()| + +nvim_del_var({name}) *nvim_del_var()* + Removes a global (g:) variable. Parameters: ~ {name} Variable name - Return: ~ - Variable value - -nvim_set_var({name}, {value}) *nvim_set_var()* - Sets a global (g:) variable. +nvim_err_write({str}) *nvim_err_write()* + Writes a message to the Vim error buffer. Does not append + "\n", the message is buffered (won't display) until a linefeed + is written. Parameters: ~ - {name} Variable name - {value} Variable value + {str} Message -nvim_del_var({name}) *nvim_del_var()* - Removes a global (g:) variable. +nvim_err_writeln({str}) *nvim_err_writeln()* + Writes a message to the Vim error buffer. Appends "\n", so the + buffer is flushed (and displayed). Parameters: ~ - {name} Variable name + {str} Message -nvim_get_vvar({name}) *nvim_get_vvar()* - Gets a v: variable. + See also: ~ + nvim_err_write() + +nvim_eval({expr}) *nvim_eval()* + Evaluates a VimL |expression|. Dictionaries and Lists are + recursively expanded. + + On execution error: fails with VimL error, does not update + v:errmsg. Parameters: ~ - {name} Variable name + {expr} VimL expression string Return: ~ - Variable value + Evaluation result or expanded object -nvim_set_vvar({name}, {value}) *nvim_set_vvar()* - Sets a v: variable, if it is not readonly. +nvim_exec({src}, {output}) *nvim_exec()* + Executes Vimscript (multiline block of Ex-commands), like + anonymous |:source|. - Parameters: ~ - {name} Variable name - {value} Variable value + Unlike |nvim_command()| this function supports heredocs, + script-scope (s:), etc. -nvim_get_option({name}) *nvim_get_option()* - Gets an option value string. + On execution error: fails with VimL error, does not update + v:errmsg. Parameters: ~ - {name} Option name + {src} Vimscript code + {output} Capture and return all (non-error, non-shell + |:!|) output Return: ~ - Option value (global) + Output (non-error, non-shell |:!|) if `output` is true, + else empty string. -nvim_set_option({name}, {value}) *nvim_set_option()* - Sets an option value. + See also: ~ + |execute()| + |nvim_command()| + +nvim_exec_lua({code}, {args}) *nvim_exec_lua()* + Execute Lua code. Parameters (if any) are available as `...` + inside the chunk. The chunk can return a value. + + Only statements are executed. To evaluate an expression, + prefix it with `return` : return my_function(...) Parameters: ~ - {name} Option name - {value} New option value + {code} Lua code to execute + {args} Arguments to the code -nvim_out_write({str}) *nvim_out_write()* - Writes a message to the Vim output buffer. Does not append - "\n", the message is buffered (won't display) until a linefeed - is written. + Return: ~ + Return value of Lua code if present or NIL. + +nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* + Sends input-keys to Nvim, subject to various quirks controlled + by `mode` flags. This is a blocking call, unlike + |nvim_input()|. + + On execution error: does not fail, but updates v:errmsg. Parameters: ~ - {str} Message + {keys} to be typed + {mode} behavior flags, see |feedkeys()| + {escape_csi} If true, escape K_SPECIAL/CSI bytes in + `keys` -nvim_err_write({str}) *nvim_err_write()* - Writes a message to the Vim error buffer. Does not append - "\n", the message is buffered (won't display) until a linefeed - is written. + See also: ~ + feedkeys() + vim_strsave_escape_csi + +nvim_get_api_info() *nvim_get_api_info()* + Returns a 2-tuple (Array), where item 0 is the current channel + id and item 1 is the |api-metadata| map (Dictionary). + + Return: ~ + 2-tuple [{channel-id}, {api-metadata}] + + Attributes: ~ + {fast} + +nvim_get_chan_info({chan}) *nvim_get_chan_info()* + Get information about a channel. + + Return: ~ + Dictionary describing a channel, with these keys: + • "stream" the stream underlying the channel + • "stdio" stdin and stdout of this Nvim instance + • "stderr" stderr of this Nvim instance + • "socket" TCP/IP socket or named pipe + • "job" job with communication over its stdio + + • "mode" how data received on the channel is interpreted + • "bytes" send and receive raw bytes + • "terminal" a |terminal| instance interprets ASCII + sequences + • "rpc" |RPC| communication on the channel is active + + • "pty" Name of pseudoterminal, if one is used (optional). + On a POSIX system, this will be a device path like + /dev/pts/1. Even if the name is unknown, the key will + still be present to indicate a pty is used. This is + currently the case when using winpty on windows. + • "buffer" buffer with connected |terminal| instance + (optional) + • "client" information about the client on the other end + of the RPC channel, if it has added it using + |nvim_set_client_info()|. (optional) + +nvim_get_color_by_name({name}) *nvim_get_color_by_name()* + Returns the 24-bit RGB value of a |nvim_get_color_map()| color + name or "#rrggbb" hexadecimal string. + + Example: > + :echo nvim_get_color_by_name("Pink") + :echo nvim_get_color_by_name("#cbcbcb") +< Parameters: ~ - {str} Message + {name} Color name or "#rrggbb" string -nvim_err_writeln({str}) *nvim_err_writeln()* - Writes a message to the Vim error buffer. Appends "\n", so the - buffer is flushed (and displayed). + Return: ~ + 24-bit RGB value, or -1 for invalid argument. + +nvim_get_color_map() *nvim_get_color_map()* + Returns a map of color names and RGB values. + + Keys are color names (e.g. "Aqua") and values are 24-bit RGB + color values (e.g. 65535). + + Return: ~ + Map of color names and RGB values. + +nvim_get_commands({opts}) *nvim_get_commands()* + Gets a map of global (non-buffer-local) Ex commands. + + Currently only |user-commands| are supported, not builtin Ex + commands. Parameters: ~ - {str} Message + {opts} Optional parameters. Currently only supports + {"builtin":false} - See also: ~ - nvim_err_write() + Return: ~ + Map of maps describing commands. -nvim_list_bufs() *nvim_list_bufs()* - Gets the current list of buffer handles +nvim_get_context({opts}) *nvim_get_context()* + Gets a map of the current editor state. - Includes unlisted (unloaded/deleted) buffers, like `:ls!` . - Use |nvim_buf_is_loaded()| to check if a buffer is loaded. + Parameters: ~ + {opts} Optional parameters. + • types: List of |context-types| ("regs", "jumps", + "bufs", "gvars", …) to gather, or empty for + "all". Return: ~ - List of buffer handles + map of global |context|. nvim_get_current_buf() *nvim_get_current_buf()* Gets the current buffer. @@ -827,17 +835,17 @@ nvim_get_current_buf() *nvim_get_current_buf()* Return: ~ Buffer handle -nvim_set_current_buf({buffer}) *nvim_set_current_buf()* - Sets the current buffer. +nvim_get_current_line() *nvim_get_current_line()* + Gets the current line. - Parameters: ~ - {buffer} Buffer handle + Return: ~ + Current line string -nvim_list_wins() *nvim_list_wins()* - Gets the current list of window handles. +nvim_get_current_tabpage() *nvim_get_current_tabpage()* + Gets the current tabpage. Return: ~ - List of window handles + Tabpage handle nvim_get_current_win() *nvim_get_current_win()* Gets the current window. @@ -845,37 +853,227 @@ nvim_get_current_win() *nvim_get_current_win()* Return: ~ Window handle -nvim_set_current_win({window}) *nvim_set_current_win()* - Sets the current window. +nvim_get_hl_by_id({hl_id}, {rgb}) *nvim_get_hl_by_id()* + Gets a highlight definition by id. |hlID()| Parameters: ~ - {window} Window handle + {hl_id} Highlight id as returned by |hlID()| + {rgb} Export RGB colors -nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* - Creates a new, empty, unnamed buffer. + Return: ~ + Highlight definition map + + See also: ~ + nvim_get_hl_by_name + +nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()* + Gets a highlight definition by name. Parameters: ~ - {listed} Sets 'buflisted' - {scratch} Creates a "throwaway" |scratch-buffer| for - temporary work (always 'nomodified') + {name} Highlight group name + {rgb} Export RGB colors Return: ~ - Buffer handle, or 0 on error + Highlight definition map See also: ~ - buf_open_scratch + nvim_get_hl_by_id -nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* - Open a new window. +nvim_get_hl_id_by_name({name}) *nvim_get_hl_id_by_name()* + Gets a highlight group by name - Currently this is used to open floating and external windows. - Floats are windows that are drawn above the split layout, at - some anchor position in some other window. Floats can be drawn - internally or by external GUI with the |ui-multigrid| - extension. External windows are only supported with multigrid - GUIs, and are displayed as separate top-level windows. + similar to |hlID()|, but allocates a new ID if not present. - For a general overview of floats, see |api-floatwin|. +nvim_get_keymap({mode}) *nvim_get_keymap()* + Gets a list of global (non-buffer-local) |mapping| + definitions. + + Parameters: ~ + {mode} Mode short-name ("n", "i", "v", ...) + + Return: ~ + Array of maparg()-like dictionaries describing mappings. + The "buffer" key is always zero. + +nvim_get_mode() *nvim_get_mode()* + Gets the current mode. |mode()| "blocking" is true if Nvim is + waiting for input. + + Return: ~ + Dictionary { "mode": String, "blocking": Boolean } + + Attributes: ~ + {fast} + +nvim_get_namespaces() *nvim_get_namespaces()* + Gets existing, non-anonymous namespaces. + + Return: ~ + dict that maps from names to namespace ids. + +nvim_get_option({name}) *nvim_get_option()* + Gets an option value string. + + Parameters: ~ + {name} Option name + + Return: ~ + Option value (global) + +nvim_get_proc({pid}) *nvim_get_proc()* + Gets info describing process `pid` . + + Return: ~ + Map of process properties, or NIL if process not found. + +nvim_get_proc_children({pid}) *nvim_get_proc_children()* + Gets the immediate children of process `pid` . + + Return: ~ + Array of child process ids, empty if process not found. + +nvim_get_var({name}) *nvim_get_var()* + Gets a global (g:) variable. + + Parameters: ~ + {name} Variable name + + Return: ~ + Variable value + +nvim_get_vvar({name}) *nvim_get_vvar()* + Gets a v: variable. + + Parameters: ~ + {name} Variable name + + Return: ~ + Variable value + +nvim_input({keys}) *nvim_input()* + Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a + low-level input buffer and the call is non-blocking (input is + processed asynchronously by the eventloop). + + On execution error: does not fail, but updates v:errmsg. + + Note: + |keycodes| like are translated, so "<" is special. To + input a literal "<", send . + Note: + For mouse events use |nvim_input_mouse()|. The pseudokey + form "" is deprecated since + |api-level| 6. + + Attributes: ~ + {fast} + + Parameters: ~ + {keys} to be typed + + Return: ~ + Number of bytes actually written (can be fewer than + requested if the buffer becomes full). + + *nvim_input_mouse()* +nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col}) + Send mouse event from GUI. + + Non-blocking: does not wait on any result, but queues the + event to be processed soon by the event loop. + + Note: + Currently this doesn't support "scripting" multiple mouse + events by calling it multiple times in a loop: the + intermediate mouse positions will be ignored. It should be + used to implement real-time mouse input in a GUI. The + deprecated pseudokey form ("") of + |nvim_input()| has the same limitiation. + + Attributes: ~ + {fast} + + Parameters: ~ + {button} Mouse button: one of "left", "right", + "middle", "wheel". + {action} For ordinary buttons, one of "press", "drag", + "release". For the wheel, one of "up", "down", + "left", "right". + {modifier} String of modifiers each represented by a + single char. The same specifiers are used as + for a key press, except that the "-" separator + is optional, so "C-A-", "c-a" and "CA" can all + be used to specify Ctrl+Alt+click. + {grid} Grid number if the client uses |ui-multigrid|, + else 0. + {row} Mouse row-position (zero-based, like redraw + events) + {col} Mouse column-position (zero-based, like redraw + events) + +nvim_list_bufs() *nvim_list_bufs()* + Gets the current list of buffer handles + + Includes unlisted (unloaded/deleted) buffers, like `:ls!` . + Use |nvim_buf_is_loaded()| to check if a buffer is loaded. + + Return: ~ + List of buffer handles + +nvim_list_chans() *nvim_list_chans()* + Get information about all open channels. + + Return: ~ + Array of Dictionaries, each describing a channel with the + format specified at |nvim_get_chan_info()|. + +nvim_list_runtime_paths() *nvim_list_runtime_paths()* + Gets the paths contained in 'runtimepath'. + + Return: ~ + List of paths + +nvim_list_tabpages() *nvim_list_tabpages()* + Gets the current list of tabpage handles. + + Return: ~ + List of tabpage handles + +nvim_list_uis() *nvim_list_uis()* + Gets a list of dictionaries representing attached UIs. + + Return: ~ + Array of UI dictionaries, each with these keys: + • "height" Requested height of the UI + • "width" Requested width of the UI + • "rgb" true if the UI uses RGB colors (false implies + |cterm-colors|) + • "ext_..." Requested UI extensions, see |ui-option| + • "chan" Channel id of remote UI (not present for TUI) + +nvim_list_wins() *nvim_list_wins()* + Gets the current list of window handles. + + Return: ~ + List of window handles + +nvim_load_context({dict}) *nvim_load_context()* + Sets the current editor state from the given |context| map. + + Parameters: ~ + {dict} |Context| map. + +nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* + Open a new window. + + Currently this is used to open floating and external windows. + Floats are windows that are drawn above the split layout, at + some anchor position in some other window. Floats can be drawn + internally or by external GUI with the |ui-multigrid| + extension. External windows are only supported with multigrid + GUIs, and are displayed as separate top-level windows. + + For a general overview of floats, see |api-floatwin|. Exactly one of `external` and `relative` must be specified. The `width` and `height` of the new window must be specified. @@ -916,7 +1114,7 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • "cursor" Cursor position in current window. • `win` : |window-ID| for relative="win". - • `anchor` : Decides which corner of the float to place + • `anchor`: Decides which corner of the float to place at (row,col): • "NW" northwest (default) • "NE" northeast @@ -946,7 +1144,7 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* an external top-level window. Currently accepts no other positioning configuration together with this. - • `style` : Configure the appearance of the window. + • `style`: Configure the appearance of the window. Currently only takes one non-empty value: • "minimal" Nvim will display the window with many UI options disabled. This is useful @@ -964,46 +1162,110 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* Return: ~ Window handle, or 0 on error -nvim_list_tabpages() *nvim_list_tabpages()* - Gets the current list of tabpage handles. - - Return: ~ - List of tabpage handles - -nvim_get_current_tabpage() *nvim_get_current_tabpage()* - Gets the current tabpage. - - Return: ~ - Tabpage handle - -nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()* - Sets the current tabpage. +nvim_out_write({str}) *nvim_out_write()* + Writes a message to the Vim output buffer. Does not append + "\n", the message is buffered (won't display) until a linefeed + is written. Parameters: ~ - {tabpage} Tabpage handle - -nvim_create_namespace({name}) *nvim_create_namespace()* - Creates a new namespace, or gets an existing one. + {str} Message - Namespaces are used for buffer highlights and virtual text, - see |nvim_buf_add_highlight()| and - |nvim_buf_set_virtual_text()|. + *nvim_parse_expression()* +nvim_parse_expression({expr}, {flags}, {highlight}) + Parse a VimL expression. - Namespaces can be named or anonymous. If `name` matches an - existing namespace, the associated id is returned. If `name` - is an empty string a new, anonymous namespace is created. + Attributes: ~ + {fast} Parameters: ~ - {name} Namespace name or empty string + {expr} Expression to parse. Always treated as a + single line. + {flags} Flags: + • "m" if multiple expressions in a row are + allowed (only the first one will be + parsed), + • "E" if EOC tokens are not allowed + (determines whether they will stop parsing + process or be recognized as an + operator/space, though also yielding an + error). + • "l" when needing to start parsing with + lvalues for ":let" or ":for". Common flag + sets: + • "m" to parse like for ":echo". + • "E" to parse like for "=". + • empty string for ":call". + • "lm" to parse for ":let". + {highlight} If true, return value will also include + "highlight" key containing array of 4-tuples + (arrays) (Integer, Integer, Integer, String), + where first three numbers define the + highlighted region and represent line, + starting column and ending column (latter + exclusive: one should highlight region + [start_col, end_col)). Return: ~ - Namespace id -nvim_get_namespaces() *nvim_get_namespaces()* - Gets existing, non-anonymous namespaces. - - Return: ~ - dict that maps from names to namespace ids. + • AST: top-level dictionary with these keys: + • "error": Dictionary with error, present only if parser + saw some error. Contains the following keys: + • "message": String, error message in printf format, + translated. Must contain exactly one "%.*s". + • "arg": String, error message argument. + + • "len": Amount of bytes successfully parsed. With flags + equal to "" that should be equal to the length of expr + string. (“Sucessfully parsed” here means “participated + in AST creation”, not “till the first error”.) + • "ast": AST, either nil or a dictionary with these + keys: + • "type": node type, one of the value names from + ExprASTNodeType stringified without "kExprNode" + prefix. + • "start": a pair [line, column] describing where node + is "started" where "line" is always 0 (will not be 0 + if you will be using nvim_parse_viml() on e.g. + ":let", but that is not present yet). Both elements + are Integers. + • "len": “length” of the node. This and "start" are + there for debugging purposes primary (debugging + parser and providing debug information). + • "children": a list of nodes described in top/"ast". + There always is zero, one or two children, key will + not be present if node has no children. Maximum + number of children may be found in node_maxchildren + array. + + • Local values (present only for certain nodes): + • "scope": a single Integer, specifies scope for + "Option" and "PlainIdentifier" nodes. For "Option" it + is one of ExprOptScope values, for "PlainIdentifier" + it is one of ExprVarScope values. + • "ident": identifier (without scope, if any), present + for "Option", "PlainIdentifier", "PlainKey" and + "Environment" nodes. + • "name": Integer, register name (one character) or -1. + Only present for "Register" nodes. + • "cmp_type": String, comparison type, one of the value + names from ExprComparisonType, stringified without + "kExprCmp" prefix. Only present for "Comparison" + nodes. + • "ccs_strategy": String, case comparison strategy, one + of the value names from ExprCaseCompareStrategy, + stringified without "kCCStrategy" prefix. Only present + for "Comparison" nodes. + • "augmentation": String, augmentation type for + "Assignment" nodes. Is either an empty string, "Add", + "Subtract" or "Concat" for "=", "+=", "-=" or ".=" + respectively. + • "invert": Boolean, true if result of comparison needs + to be inverted. Only present for "Comparison" nodes. + • "ivalue": Integer, integer value for "Integer" nodes. + • "fvalue": Float, floating-point value for "Float" + nodes. + • "svalue": String, value for "SingleQuotedString" and + "DoubleQuotedString" nodes. nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* Pastes at cursor, in any mode. @@ -1051,139 +1313,42 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()* |P|). {follow} Place cursor at end of inserted text. -nvim_subscribe({event}) *nvim_subscribe()* - Subscribes to event broadcasts. - - Parameters: ~ - {event} Event type string - -nvim_unsubscribe({event}) *nvim_unsubscribe()* - Unsubscribes to event broadcasts. - - Parameters: ~ - {event} Event type string - -nvim_get_color_by_name({name}) *nvim_get_color_by_name()* - Returns the 24-bit RGB value of a |nvim_get_color_map()| color - name or "#rrggbb" hexadecimal string. - - Example: > - :echo nvim_get_color_by_name("Pink") - :echo nvim_get_color_by_name("#cbcbcb") -< - - Parameters: ~ - {name} Color name or "#rrggbb" string - - Return: ~ - 24-bit RGB value, or -1 for invalid argument. - -nvim_get_color_map() *nvim_get_color_map()* - Returns a map of color names and RGB values. - - Keys are color names (e.g. "Aqua") and values are 24-bit RGB - color values (e.g. 65535). - - Return: ~ - Map of color names and RGB values. - -nvim_get_context({opts}) *nvim_get_context()* - Gets a map of the current editor state. - - Parameters: ~ - {opts} Optional parameters. - • types: List of |context-types| ("regs", "jumps", - "bufs", "gvars", …) to gather, or empty for - "all". - - Return: ~ - map of global |context|. - -nvim_load_context({dict}) *nvim_load_context()* - Sets the current editor state from the given |context| map. - - Parameters: ~ - {dict} |Context| map. - -nvim_get_mode() *nvim_get_mode()* - Gets the current mode. |mode()| "blocking" is true if Nvim is - waiting for input. - - Return: ~ - Dictionary { "mode": String, "blocking": Boolean } - - Attributes: ~ - {fast} - -nvim_get_keymap({mode}) *nvim_get_keymap()* - Gets a list of global (non-buffer-local) |mapping| - definitions. - - Parameters: ~ - {mode} Mode short-name ("n", "i", "v", ...) - - Return: ~ - Array of maparg()-like dictionaries describing mappings. - The "buffer" key is always zero. - -nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* - Sets a global |mapping| for the given mode. - - To set a buffer-local mapping, use |nvim_buf_set_keymap()|. - - Unlike |:map|, leading/trailing whitespace is accepted as part - of the {lhs} or {rhs}. Empty {rhs} is ||. |keycodes| are - replaced as usual. - - Example: > - call nvim_set_keymap('n', ' ', '', {'nowait': v:true}) -< - - is equivalent to: > - nmap -< + *nvim_replace_termcodes()* +nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) + Replaces terminal codes and |keycodes| (, , ...) in a + string with the internal representation. Parameters: ~ - {mode} Mode short-name (map command prefix: "n", "i", - "v", "x", …) or "!" for |:map!|, or empty string - for |:map|. - {lhs} Left-hand-side |{lhs}| of the mapping. - {rhs} Right-hand-side |{rhs}| of the mapping. - {opts} Optional parameters map. Accepts all - |:map-arguments| as keys excluding || but - including |noremap|. Values are Booleans. Unknown - key is an error. - -nvim_del_keymap({mode}, {lhs}) *nvim_del_keymap()* - Unmaps a global |mapping| for the given mode. - - To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|. + {str} String to be converted. + {from_part} Legacy Vim parameter. Usually true. + {do_lt} Also translate . Ignored if `special` is + false. + {special} Replace |keycodes|, e.g. becomes a "\n" + char. See also: ~ - |nvim_set_keymap()| + replace_termcodes + cpoptions -nvim_get_commands({opts}) *nvim_get_commands()* - Gets a map of global (non-buffer-local) Ex commands. + *nvim_select_popupmenu_item()* +nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) + Selects an item in the completion popupmenu. - Currently only |user-commands| are supported, not builtin Ex - commands. + If |ins-completion| is not active this API call is silently + ignored. Useful for an external UI using |ui-popupmenu| to + control the popupmenu with the mouse. Can also be used in a + mapping; use |:map-cmd| to ensure the mapping doesn't + end completion mode. Parameters: ~ - {opts} Optional parameters. Currently only supports - {"builtin":false} - - Return: ~ - Map of maps describing commands. - -nvim_get_api_info() *nvim_get_api_info()* - Returns a 2-tuple (Array), where item 0 is the current channel - id and item 1 is the |api-metadata| map (Dictionary). - - Return: ~ - 2-tuple [{channel-id}, {api-metadata}] - - Attributes: ~ - {fast} + {item} Index (zero-based) of the item to select. Value + of -1 selects nothing and restores the original + text. + {insert} Whether the selection should be inserted in the + buffer. + {finish} Finish the completion and dismiss the popupmenu. + Implies `insert` . + {opts} Optional parameters. Reserved for future use. *nvim_set_client_info()* nvim_set_client_info({name}, {version}, {type}, {methods}, @@ -1250,271 +1415,106 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, small logo or icon. .png or .svg format is preferred. -nvim_get_chan_info({chan}) *nvim_get_chan_info()* - Get information about a channel. - - Return: ~ - Dictionary describing a channel, with these keys: - • "stream" the stream underlying the channel - • "stdio" stdin and stdout of this Nvim instance - • "stderr" stderr of this Nvim instance - • "socket" TCP/IP socket or named pipe - • "job" job with communication over its stdio - - • "mode" how data received on the channel is interpreted - • "bytes" send and receive raw bytes - • "terminal" a |terminal| instance interprets ASCII - sequences - • "rpc" |RPC| communication on the channel is active - - • "pty" Name of pseudoterminal, if one is used (optional). - On a POSIX system, this will be a device path like - /dev/pts/1. Even if the name is unknown, the key will - still be present to indicate a pty is used. This is - currently the case when using winpty on windows. - • "buffer" buffer with connected |terminal| instance - (optional) - • "client" information about the client on the other end - of the RPC channel, if it has added it using - |nvim_set_client_info()|. (optional) +nvim_set_current_buf({buffer}) *nvim_set_current_buf()* + Sets the current buffer. -nvim_list_chans() *nvim_list_chans()* - Get information about all open channels. + Parameters: ~ + {buffer} Buffer handle - Return: ~ - Array of Dictionaries, each describing a channel with the - format specified at |nvim_get_chan_info()|. +nvim_set_current_dir({dir}) *nvim_set_current_dir()* + Changes the global working directory. -nvim_call_atomic({calls}) *nvim_call_atomic()* - Calls many API methods atomically. + Parameters: ~ + {dir} Directory path - This has two main usages: - 1. To perform several requests from an async context - atomically, i.e. without interleaving redraws, RPC requests - from other clients, or user interactions (however API - methods may trigger autocommands or event processing which - have such side-effects, e.g. |:sleep| may wake timers). - 2. To minimize RPC overhead (roundtrips) of a sequence of many - requests. +nvim_set_current_line({line}) *nvim_set_current_line()* + Sets the current line. Parameters: ~ - {calls} an array of calls, where each call is described - by an array with two elements: the request name, - and an array of arguments. + {line} Line contents - Return: ~ - Array of two elements. The first is an array of return - values. The second is NIL if all calls succeeded. If a - call resulted in an error, it is a three-element array - with the zero-based index of the call which resulted in an - error, the error type and the error message. If an error - occurred, the values from all preceding calls will still - be returned. +nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()* + Sets the current tabpage. - *nvim_parse_expression()* -nvim_parse_expression({expr}, {flags}, {highlight}) - Parse a VimL expression. + Parameters: ~ + {tabpage} Tabpage handle - Attributes: ~ - {fast} +nvim_set_current_win({window}) *nvim_set_current_win()* + Sets the current window. Parameters: ~ - {expr} Expression to parse. Always treated as a - single line. - {flags} Flags: - • "m" if multiple expressions in a row are - allowed (only the first one will be - parsed), - • "E" if EOC tokens are not allowed - (determines whether they will stop parsing - process or be recognized as an - operator/space, though also yielding an - error). - • "l" when needing to start parsing with - lvalues for ":let" or ":for". Common flag - sets: - • "m" to parse like for ":echo". - • "E" to parse like for "=". - • empty string for ":call". - • "lm" to parse for ":let". - {highlight} If true, return value will also include - "highlight" key containing array of 4-tuples - (arrays) (Integer, Integer, Integer, String), - where first three numbers define the - highlighted region and represent line, - starting column and ending column (latter - exclusive: one should highlight region - [start_col, end_col)). - - Return: ~ + {window} Window handle - • AST: top-level dictionary with these keys: - • "error": Dictionary with error, present only if parser - saw some error. Contains the following keys: - • "message": String, error message in printf format, - translated. Must contain exactly one "%.*s". - • "arg": String, error message argument. +nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()* + Sets a global |mapping| for the given mode. - • "len": Amount of bytes successfully parsed. With flags - equal to "" that should be equal to the length of expr - string. (“Sucessfully parsed” here means “participated - in AST creation”, not “till the first error”.) - • "ast": AST, either nil or a dictionary with these - keys: - • "type": node type, one of the value names from - ExprASTNodeType stringified without "kExprNode" - prefix. - • "start": a pair [line, column] describing where node - is "started" where "line" is always 0 (will not be 0 - if you will be using nvim_parse_viml() on e.g. - ":let", but that is not present yet). Both elements - are Integers. - • "len": “length” of the node. This and "start" are - there for debugging purposes primary (debugging - parser and providing debug information). - • "children": a list of nodes described in top/"ast". - There always is zero, one or two children, key will - not be present if node has no children. Maximum - number of children may be found in node_maxchildren - array. + To set a buffer-local mapping, use |nvim_buf_set_keymap()|. - • Local values (present only for certain nodes): - • "scope": a single Integer, specifies scope for - "Option" and "PlainIdentifier" nodes. For "Option" it - is one of ExprOptScope values, for "PlainIdentifier" - it is one of ExprVarScope values. - • "ident": identifier (without scope, if any), present - for "Option", "PlainIdentifier", "PlainKey" and - "Environment" nodes. - • "name": Integer, register name (one character) or -1. - Only present for "Register" nodes. - • "cmp_type": String, comparison type, one of the value - names from ExprComparisonType, stringified without - "kExprCmp" prefix. Only present for "Comparison" - nodes. - • "ccs_strategy": String, case comparison strategy, one - of the value names from ExprCaseCompareStrategy, - stringified without "kCCStrategy" prefix. Only present - for "Comparison" nodes. - • "augmentation": String, augmentation type for - "Assignment" nodes. Is either an empty string, "Add", - "Subtract" or "Concat" for "=", "+=", "-=" or ".=" - respectively. - • "invert": Boolean, true if result of comparison needs - to be inverted. Only present for "Comparison" nodes. - • "ivalue": Integer, integer value for "Integer" nodes. - • "fvalue": Float, floating-point value for "Float" - nodes. - • "svalue": String, value for "SingleQuotedString" and - "DoubleQuotedString" nodes. + Unlike |:map|, leading/trailing whitespace is accepted as part + of the {lhs} or {rhs}. Empty {rhs} is ||. |keycodes| are + replaced as usual. -nvim__id({obj}) *nvim__id()* - Returns object given as argument. + Example: > + call nvim_set_keymap('n', ' ', '', {'nowait': v:true}) +< - This API function is used for testing. One should not rely on - its presence in plugins. + is equivalent to: > + nmap +< Parameters: ~ - {obj} Object to return. - - Return: ~ - its argument. - -nvim__id_array({arr}) *nvim__id_array()* - Returns array given as argument. + {mode} Mode short-name (map command prefix: "n", "i", + "v", "x", …) or "!" for |:map!|, or empty string + for |:map|. + {lhs} Left-hand-side |{lhs}| of the mapping. + {rhs} Right-hand-side |{rhs}| of the mapping. + {opts} Optional parameters map. Accepts all + |:map-arguments| as keys excluding || but + including |noremap|. Values are Booleans. Unknown + key is an error. - This API function is used for testing. One should not rely on - its presence in plugins. +nvim_set_option({name}, {value}) *nvim_set_option()* + Sets an option value. Parameters: ~ - {arr} Array to return. - - Return: ~ - its argument. - -nvim__id_dictionary({dct}) *nvim__id_dictionary()* - Returns dictionary given as argument. + {name} Option name + {value} New option value - This API function is used for testing. One should not rely on - its presence in plugins. +nvim_set_var({name}, {value}) *nvim_set_var()* + Sets a global (g:) variable. Parameters: ~ - {dct} Dictionary to return. - - Return: ~ - its argument. - -nvim__id_float({flt}) *nvim__id_float()* - Returns floating-point value given as argument. + {name} Variable name + {value} Variable value - This API function is used for testing. One should not rely on - its presence in plugins. +nvim_set_vvar({name}, {value}) *nvim_set_vvar()* + Sets a v: variable, if it is not readonly. Parameters: ~ - {flt} Value to return. - - Return: ~ - its argument. - -nvim__stats() *nvim__stats()* - Gets internal stats. - - Return: ~ - Map of various internal stats. - -nvim_list_uis() *nvim_list_uis()* - Gets a list of dictionaries representing attached UIs. - - Return: ~ - Array of UI dictionaries, each with these keys: - • "height" Requested height of the UI - • "width" Requested width of the UI - • "rgb" true if the UI uses RGB colors (false implies - |cterm-colors|) - • "ext_..." Requested UI extensions, see |ui-option| - • "chan" Channel id of remote UI (not present for TUI) - -nvim_get_proc_children({pid}) *nvim_get_proc_children()* - Gets the immediate children of process `pid` . - - Return: ~ - Array of child process ids, empty if process not found. - -nvim_get_proc({pid}) *nvim_get_proc()* - Gets info describing process `pid` . - - Return: ~ - Map of process properties, or NIL if process not found. - - *nvim_select_popupmenu_item()* -nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) - Selects an item in the completion popupmenu. + {name} Variable name + {value} Variable value - If |ins-completion| is not active this API call is silently - ignored. Useful for an external UI using |ui-popupmenu| to - control the popupmenu with the mouse. Can also be used in a - mapping; use |:map-cmd| to ensure the mapping doesn't - end completion mode. +nvim_strwidth({text}) *nvim_strwidth()* + Calculates the number of display cells occupied by `text` . + counts as one cell. Parameters: ~ - {item} Index (zero-based) of the item to select. Value - of -1 selects nothing and restores the original - text. - {insert} Whether the selection should be inserted in the - buffer. - {finish} Finish the completion and dismiss the popupmenu. - Implies `insert` . - {opts} Optional parameters. Reserved for future use. + {text} Some text -nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* - TODO: Documentation + Return: ~ + Number of cells -nvim__put_attr({id}, {c0}, {c1}) *nvim__put_attr()* - Set attrs in nvim__buf_set_lua_hl callbacks +nvim_subscribe({event}) *nvim_subscribe()* + Subscribes to event broadcasts. - TODO(bfredl): This is rather pedestrian. The final interface - should probably be derived from a reformed bufhl/virttext - interface with full support for multi-line ranges etc + Parameters: ~ + {event} Event type string + +nvim_unsubscribe({event}) *nvim_unsubscribe()* + Unsubscribes to event broadcasts. + + Parameters: ~ + {event} Event type string ============================================================================== @@ -1533,14 +1533,65 @@ affected. You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check whether a buffer is loaded. -nvim_buf_line_count({buffer}) *nvim_buf_line_count()* - Gets the buffer line count + *nvim__buf_redraw_range()* +nvim__buf_redraw_range({buffer}, {first}, {last}) + TODO: Documentation + +nvim__buf_set_luahl({buffer}, {opts}) *nvim__buf_set_luahl()* + Unstabilized interface for defining syntax hl in lua. + + This is not yet safe for general use, lua callbacks will need + to be restricted, like textlock and probably other stuff. + + The API on_line/nvim__put_attr is quite raw and not intended + to be the final shape. Ideally this should operate on chunks + larger than a single line to reduce interpreter overhead, and + generate annotation objects (bufhl/virttext) on the fly but + using the same representation. + +nvim__buf_stats({buffer}) *nvim__buf_stats()* + TODO: Documentation + + *nvim_buf_add_highlight()* +nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, + {col_start}, {col_end}) + Adds a highlight to buffer. + + Useful for plugins that dynamically generate highlights to a + buffer (like a semantic highlighter or linter). The function + adds a single highlight to a buffer. Unlike |matchaddpos()| + highlights follow changes to line numbering (as lines are + inserted/removed above the highlighted line), like signs and + marks do. + + Namespaces are used for batch deletion/updating of a set of + highlights. To create a namespace, use |nvim_create_namespace| + which returns a namespace id. Pass it in to this function as + `ns_id` to add highlights to the namespace. All highlights in + the same namespace can then be cleared with single call to + |nvim_buf_clear_namespace|. If the highlight never will be + deleted by an API call, pass `ns_id = -1` . + + As a shorthand, `ns_id = 0` can be used to create a new + namespace for the highlight, the allocated id is then + returned. If `hl_group` is the empty string no highlight is + added, but a new `ns_id` is still returned. This is supported + for backwards compatibility, new code should use + |nvim_create_namespace| to create a new empty namespace. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer + {buffer} Buffer handle, or 0 for current buffer + {ns_id} namespace to use or -1 for ungrouped + highlight + {hl_group} Name of the highlight group to use + {line} Line to highlight (zero-indexed) + {col_start} Start of (byte-indexed) column range to + highlight + {col_end} End of (byte-indexed) column range to + highlight, or -1 to highlight to end of line Return: ~ - Line count, or 0 for unloaded buffer. |api-buffer| + The ns_id that was used nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* Activates buffer-update events on a channel, or as Lua @@ -1563,7 +1614,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* callbacks. {opts} Optional parameters. • on_lines: Lua callback invoked on change. - Return `true` to detach. Args: + Return`true`to detach. Args: • buffer handle • b:changedtick • first line that changed (zero-indexed) @@ -1597,119 +1648,142 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* |nvim_buf_detach()| |api-buffer-updates-lua| -nvim_buf_detach({buffer}) *nvim_buf_detach()* - Deactivates buffer-update events on the channel. + *nvim_buf_clear_namespace()* +nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) + Clears namespaced objects (highlights, extmarks, virtual text) + from a region. + + Lines are 0-indexed. |api-indexing| To clear the namespace in + the entire buffer, specify line_start=0 and line_end=-1. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace to clear, or -1 to clear all + namespaces. + {line_start} Start of range of lines to clear + {line_end} End of range of lines to clear (exclusive) + or -1 to clear to end of buffer. - Return: ~ - False if detach failed (because the buffer isn't loaded); - otherwise True. +nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* + Removes an extmark. - See also: ~ - |nvim_buf_attach()| - |api-lua-detach| for detaching Lua callbacks + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id -nvim__buf_set_luahl({buffer}, {opts}) *nvim__buf_set_luahl()* - Unstabilized interface for defining syntax hl in lua. + Return: ~ + true if the extmark was found, else false - This is not yet safe for general use, lua callbacks will need - to be restricted, like textlock and probably other stuff. +nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()* + Unmaps a buffer-local |mapping| for the given mode. - The API on_line/nvim__put_attr is quite raw and not intended - to be the final shape. Ideally this should operate on chunks - larger than a single line to reduce interpreter overhead, and - generate annotation objects (bufhl/virttext) on the fly but - using the same representation. + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer - *nvim__buf_redraw_range()* -nvim__buf_redraw_range({buffer}, {first}, {last}) - TODO: Documentation + See also: ~ + |nvim_del_keymap()| - *nvim_buf_get_lines()* -nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) - Gets a line-range from the buffer. +nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()* + Removes a buffer-scoped (b:) variable - Indexing is zero-based, end-exclusive. Negative indices are - interpreted as length+1+index: -1 refers to the index past the - end. So to get the last element use start=-2 and end=-1. + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Variable name - Out-of-bounds indices are clamped to the nearest valid value, - unless `strict_indexing` is set. +nvim_buf_detach({buffer}) *nvim_buf_detach()* + Deactivates buffer-update events on the channel. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {start} First line index - {end} Last line index (exclusive) - {strict_indexing} Whether out-of-bounds should be an - error. + {buffer} Buffer handle, or 0 for current buffer Return: ~ - Array of lines, or empty array for unloaded buffer. - - *nvim_buf_set_lines()* -nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, - {replacement}) - Sets (replaces) a line-range in the buffer. - - Indexing is zero-based, end-exclusive. Negative indices are - interpreted as length+1+index: -1 refers to the index past the - end. So to change or delete the last element use start=-2 and - end=-1. + False if detach failed (because the buffer isn't loaded); + otherwise True. - To insert lines at a given index, set `start` and `end` to the - same index. To delete a range of lines, set `replacement` to - an empty array. + See also: ~ + |nvim_buf_attach()| + |api-lua-detach| for detaching Lua callbacks - Out-of-bounds indices are clamped to the nearest valid value, - unless `strict_indexing` is set. +nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()* + Gets a changed tick of a buffer Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {start} First line index - {end} Last line index (exclusive) - {strict_indexing} Whether out-of-bounds should be an - error. - {replacement} Array of lines to use as replacement - -nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()* - Returns the byte offset of a line (0-indexed). |api-indexing| + {buffer} Buffer handle, or 0 for current buffer - Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is - one byte. 'fileformat' and 'fileencoding' are ignored. The - line index just after the last line gives the total byte-count - of the buffer. A final EOL byte is counted if it would be - written, see 'eol'. + Return: ~ + `b:changedtick` value. - Unlike |line2byte()|, throws error for out-of-bounds indexing. - Returns -1 for unloaded buffer. +nvim_buf_get_commands({buffer}, {opts}) *nvim_buf_get_commands()* + Gets a map of buffer-local |user-commands|. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {index} Line index + {opts} Optional parameters. Currently not used. Return: ~ - Integer byte offset, or -1 for unloaded buffer. + Map of maps describing commands. -nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()* - Gets a buffer-scoped (b:) variable. + *nvim_buf_get_extmark_by_id()* +nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}) + Returns position for a given extmark id Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Variable name + {ns_id} Namespace id from |nvim_create_namespace()| + {id} Extmark id Return: ~ - Variable value + (row, col) tuple or empty list () if extmark id was absent -nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()* - Gets a changed tick of a buffer + *nvim_buf_get_extmarks()* +nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) + Gets extmarks in "traversal order" from a |charwise| region + defined by buffer positions (inclusive, 0-indexed + |api-indexing|). + + Region can be given as (row,col) tuples, or valid extmark ids + (whose positions define the bounds). 0 and -1 are understood + as (0,0) and (-1,-1) respectively, thus the following are + equivalent: +> + nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) + nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) +< + + If `end` is less than `start` , traversal works backwards. + (Useful with `limit` , to get the first marks prior to a given + position.) + + Example: +> + local a = vim.api + local pos = a.nvim_win_get_cursor(0) + local ns = a.nvim_create_namespace('my-plugin') + -- Create new extmark at line 1, column 1. + local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + -- Create new extmark at line 3, column 1. + local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) + -- Get extmarks only from line 3. + local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) + -- Get all marks in this buffer + namespace. + local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) + print(vim.inspect(ms)) +< Parameters: ~ {buffer} Buffer handle, or 0 for current buffer + {ns_id} Namespace id from |nvim_create_namespace()| + {start} Start of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {end} End of range, given as (row, col) or valid + extmark id (whose position defines the bound) + {opts} Optional parameters. Keys: + • limit: Maximum number of marks to return Return: ~ - `b:changedtick` value. + List of [extmark_id, row, col] tuples in "traversal + order". nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()* Gets a list of buffer-local |mapping| definitions. @@ -1722,49 +1796,67 @@ nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()* Array of maparg()-like dictionaries describing mappings. The "buffer" key holds the associated buffer handle. - *nvim_buf_set_keymap()* -nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {opts}) - Sets a buffer-local |mapping| for the given mode. + *nvim_buf_get_lines()* +nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing}) + Gets a line-range from the buffer. + + Indexing is zero-based, end-exclusive. Negative indices are + interpreted as length+1+index: -1 refers to the index past the + end. So to get the last element use start=-2 and end=-1. + + Out-of-bounds indices are clamped to the nearest valid value, + unless `strict_indexing` is set. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer + {buffer} Buffer handle, or 0 for current buffer + {start} First line index + {end} Last line index (exclusive) + {strict_indexing} Whether out-of-bounds should be an + error. - See also: ~ - |nvim_set_keymap()| + Return: ~ + Array of lines, or empty array for unloaded buffer. -nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()* - Unmaps a buffer-local |mapping| for the given mode. +nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()* + Return a tuple (row,col) representing the position of the + named mark. + + Marks are (1,0)-indexed. |api-indexing| Parameters: ~ {buffer} Buffer handle, or 0 for current buffer + {name} Mark name - See also: ~ - |nvim_del_keymap()| + Return: ~ + (row, col) tuple -nvim_buf_get_commands({buffer}, {opts}) *nvim_buf_get_commands()* - Gets a map of buffer-local |user-commands|. +nvim_buf_get_name({buffer}) *nvim_buf_get_name()* + Gets the full file name for the buffer Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {opts} Optional parameters. Currently not used. Return: ~ - Map of maps describing commands. + Buffer name -nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* - Sets a buffer-scoped (b:) variable +nvim_buf_get_offset({buffer}, {index}) *nvim_buf_get_offset()* + Returns the byte offset of a line (0-indexed). |api-indexing| - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {name} Variable name - {value} Variable value + Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is + one byte. 'fileformat' and 'fileencoding' are ignored. The + line index just after the last line gives the total byte-count + of the buffer. A final EOL byte is counted if it would be + written, see 'eol'. -nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()* - Removes a buffer-scoped (b:) variable + Unlike |line2byte()|, throws error for out-of-bounds indexing. + Returns -1 for unloaded buffer. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Variable name + {index} Line index + + Return: ~ + Integer byte offset, or -1 for unloaded buffer. nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()* Gets a buffer option value @@ -1776,30 +1868,36 @@ nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()* Return: ~ Option value -nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* - Sets a buffer option value. Passing 'nil' as value deletes the - option (only works if there's a global fallback) +nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()* + Gets a buffer-scoped (b:) variable. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Option name - {value} Option value + {name} Variable name -nvim_buf_get_name({buffer}) *nvim_buf_get_name()* - Gets the full file name for the buffer + Return: ~ + Variable value - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer +nvim_buf_get_virtual_text({buffer}, {lnum}) *nvim_buf_get_virtual_text()* + Get the virtual text (annotation) for a buffer line. - Return: ~ - Buffer name + The virtual text is returned as list of lists, whereas the + inner lists have either one or two elements. The first element + is the actual text, the optional second element is the + highlight group. -nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* - Sets the full file name for a buffer + The format is exactly the same as given to + nvim_buf_set_virtual_text(). + + If there is no virtual text associated with the given line, an + empty list is returned. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {name} Buffer name + {line} Line to get the virtual text from (zero-indexed) + + Return: ~ + List of virtual text chunks nvim_buf_is_loaded({buffer}) *nvim_buf_is_loaded()* Checks if a buffer is valid and loaded. See |api-buffer| for @@ -1824,79 +1922,14 @@ nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()* Return: ~ true if the buffer is valid, false otherwise. -nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()* - Return a tuple (row,col) representing the position of the - named mark. - - Marks are (1,0)-indexed. |api-indexing| - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {name} Mark name - - Return: ~ - (row, col) tuple - - *nvim_buf_get_extmark_by_id()* -nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}) - Returns position for a given extmark id - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace id from |nvim_create_namespace()| - {id} Extmark id - - Return: ~ - (row, col) tuple or empty list () if extmark id was absent - - *nvim_buf_get_extmarks()* -nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) - Gets extmarks in "traversal order" from a |charwise| region - defined by buffer positions (inclusive, 0-indexed - |api-indexing|). - - Region can be given as (row,col) tuples, or valid extmark ids - (whose positions define the bounds). 0 and -1 are understood - as (0,0) and (-1,-1) respectively, thus the following are - equivalent: -> - nvim_buf_get_extmarks(0, my_ns, 0, -1, {}) - nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {}) -< - - If `end` is less than `start` , traversal works backwards. - (Useful with `limit` , to get the first marks prior to a given - position.) - - Example: -> - local a = vim.api - local pos = a.nvim_win_get_cursor(0) - local ns = a.nvim_create_namespace('my-plugin') - -- Create new extmark at line 1, column 1. - local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) - -- Create new extmark at line 3, column 1. - local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) - -- Get extmarks only from line 3. - local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) - -- Get all marks in this buffer + namespace. - local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {}) - print(vim.inspect(ms)) -< +nvim_buf_line_count({buffer}) *nvim_buf_line_count()* + Gets the buffer line count Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace id from |nvim_create_namespace()| - {start} Start of range, given as (row, col) or valid - extmark id (whose position defines the bound) - {end} End of range, given as (row, col) or valid - extmark id (whose position defines the bound) - {opts} Optional parameters. Keys: - • limit: Maximum number of marks to return Return: ~ - List of [extmark_id, row, col] tuples in "traversal - order". + Line count, or 0 for unloaded buffer. |api-buffer| *nvim_buf_set_extmark()* nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) @@ -1919,73 +1952,64 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts}) Return: ~ Id of the created/updated extmark -nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()* - Removes an extmark. + *nvim_buf_set_keymap()* +nvim_buf_set_keymap({buffer}, {mode}, {lhs}, {rhs}, {opts}) + Sets a buffer-local |mapping| for the given mode. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace id from |nvim_create_namespace()| - {id} Extmark id - Return: ~ - true if the extmark was found, else false + See also: ~ + |nvim_set_keymap()| - *nvim_buf_add_highlight()* -nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, - {col_start}, {col_end}) - Adds a highlight to buffer. + *nvim_buf_set_lines()* +nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, + {replacement}) + Sets (replaces) a line-range in the buffer. - Useful for plugins that dynamically generate highlights to a - buffer (like a semantic highlighter or linter). The function - adds a single highlight to a buffer. Unlike |matchaddpos()| - highlights follow changes to line numbering (as lines are - inserted/removed above the highlighted line), like signs and - marks do. + Indexing is zero-based, end-exclusive. Negative indices are + interpreted as length+1+index: -1 refers to the index past the + end. So to change or delete the last element use start=-2 and + end=-1. - Namespaces are used for batch deletion/updating of a set of - highlights. To create a namespace, use |nvim_create_namespace| - which returns a namespace id. Pass it in to this function as - `ns_id` to add highlights to the namespace. All highlights in - the same namespace can then be cleared with single call to - |nvim_buf_clear_namespace|. If the highlight never will be - deleted by an API call, pass `ns_id = -1` . + To insert lines at a given index, set `start` and `end` to the + same index. To delete a range of lines, set `replacement` to + an empty array. - As a shorthand, `ns_id = 0` can be used to create a new - namespace for the highlight, the allocated id is then - returned. If `hl_group` is the empty string no highlight is - added, but a new `ns_id` is still returned. This is supported - for backwards compatibility, new code should use - |nvim_create_namespace| to create a new empty namespace. + Out-of-bounds indices are clamped to the nearest valid value, + unless `strict_indexing` is set. Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} namespace to use or -1 for ungrouped - highlight - {hl_group} Name of the highlight group to use - {line} Line to highlight (zero-indexed) - {col_start} Start of (byte-indexed) column range to - highlight - {col_end} End of (byte-indexed) column range to - highlight, or -1 to highlight to end of line + {buffer} Buffer handle, or 0 for current buffer + {start} First line index + {end} Last line index (exclusive) + {strict_indexing} Whether out-of-bounds should be an + error. + {replacement} Array of lines to use as replacement - Return: ~ - The ns_id that was used +nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* + Sets the full file name for a buffer - *nvim_buf_clear_namespace()* -nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) - Clears namespaced objects (highlights, extmarks, virtual text) - from a region. + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Buffer name - Lines are 0-indexed. |api-indexing| To clear the namespace in - the entire buffer, specify line_start=0 and line_end=-1. +nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()* + Sets a buffer option value. Passing 'nil' as value deletes the + option (only works if there's a global fallback) + + Parameters: ~ + {buffer} Buffer handle, or 0 for current buffer + {name} Option name + {value} Option value + +nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* + Sets a buffer-scoped (b:) variable Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace to clear, or -1 to clear all - namespaces. - {line_start} Start of range of lines to clear - {line_end} End of range of lines to clear (exclusive) - or -1 to clear to end of buffer. + {buffer} Buffer handle, or 0 for current buffer + {name} Variable name + {value} Variable value *nvim_buf_set_virtual_text()* nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts}) @@ -2025,33 +2049,26 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts}) Return: ~ The ns_id that was used -nvim_buf_get_virtual_text({buffer}, {lnum}) *nvim_buf_get_virtual_text()* - Get the virtual text (annotation) for a buffer line. - - The virtual text is returned as list of lists, whereas the - inner lists have either one or two elements. The first element - is the actual text, the optional second element is the - highlight group. - The format is exactly the same as given to - nvim_buf_set_virtual_text(). +============================================================================== +Window Functions *api-window* - If there is no virtual text associated with the given line, an - empty list is returned. +nvim_win_close({window}, {force}) *nvim_win_close()* + Closes the window (like |:close| with a |window-ID|). Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {line} Line to get the virtual text from (zero-indexed) - - Return: ~ - List of virtual text chunks - -nvim__buf_stats({buffer}) *nvim__buf_stats()* - TODO: Documentation + {window} Window handle, or 0 for current window + {force} Behave like `:close!` The last window of a + buffer with unwritten changes can be closed. The + buffer will become hidden, even if 'hidden' is + not set. +nvim_win_del_var({window}, {name}) *nvim_win_del_var()* + Removes a window-scoped (w:) variable -============================================================================== -Window Functions *api-window* + Parameters: ~ + {window} Window handle, or 0 for current window + {name} Variable name nvim_win_get_buf({window}) *nvim_win_get_buf()* Gets the current buffer in a window @@ -2062,12 +2079,19 @@ nvim_win_get_buf({window}) *nvim_win_get_buf()* Return: ~ Buffer handle -nvim_win_set_buf({window}, {buffer}) *nvim_win_set_buf()* - Sets the current buffer in a window, without side-effects +nvim_win_get_config({window}) *nvim_win_get_config()* + Gets window configuration. + + The returned value may be given to |nvim_open_win()|. + + `relative` is empty for normal windows. Parameters: ~ {window} Window handle, or 0 for current window - {buffer} Buffer handle + + Return: ~ + Map defining the window configuration, see + |nvim_open_win()| nvim_win_get_cursor({window}) *nvim_win_get_cursor()* Gets the (1,0)-indexed cursor position in the window. @@ -2079,14 +2103,6 @@ nvim_win_get_cursor({window}) *nvim_win_get_cursor()* Return: ~ (row, col) tuple -nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()* - Sets the (1,0)-indexed cursor position in the window. - |api-indexing| - - Parameters: ~ - {window} Window handle, or 0 for current window - {pos} (row, col) tuple representing the new position - nvim_win_get_height({window}) *nvim_win_get_height()* Gets the window height @@ -2096,55 +2112,14 @@ nvim_win_get_height({window}) *nvim_win_get_height()* Return: ~ Height as a count of rows -nvim_win_set_height({window}, {height}) *nvim_win_set_height()* - Sets the window height. This will only succeed if the screen - is split horizontally. - - Parameters: ~ - {window} Window handle, or 0 for current window - {height} Height as a count of rows - -nvim_win_get_width({window}) *nvim_win_get_width()* - Gets the window width - - Parameters: ~ - {window} Window handle, or 0 for current window - - Return: ~ - Width as a count of columns - -nvim_win_set_width({window}, {width}) *nvim_win_set_width()* - Sets the window width. This will only succeed if the screen is - split vertically. - - Parameters: ~ - {window} Window handle, or 0 for current window - {width} Width as a count of columns - -nvim_win_get_var({window}, {name}) *nvim_win_get_var()* - Gets a window-scoped (w:) variable +nvim_win_get_number({window}) *nvim_win_get_number()* + Gets the window number Parameters: ~ {window} Window handle, or 0 for current window - {name} Variable name Return: ~ - Variable value - -nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()* - Sets a window-scoped (w:) variable - - Parameters: ~ - {window} Window handle, or 0 for current window - {name} Variable name - {value} Variable value - -nvim_win_del_var({window}, {name}) *nvim_win_del_var()* - Removes a window-scoped (w:) variable - - Parameters: ~ - {window} Window handle, or 0 for current window - {name} Variable name + Window number nvim_win_get_option({window}, {name}) *nvim_win_get_option()* Gets a window option value @@ -2156,15 +2131,6 @@ nvim_win_get_option({window}, {name}) *nvim_win_get_option()* Return: ~ Option value -nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()* - Sets a window option value. Passing 'nil' as value deletes the - option(only works if there's a global fallback) - - Parameters: ~ - {window} Window handle, or 0 for current window - {name} Option name - {value} Option value - nvim_win_get_position({window}) *nvim_win_get_position()* Gets the window position in display cells. First position is zero. @@ -2184,14 +2150,24 @@ nvim_win_get_tabpage({window}) *nvim_win_get_tabpage()* Return: ~ Tabpage that contains the window -nvim_win_get_number({window}) *nvim_win_get_number()* - Gets the window number +nvim_win_get_var({window}, {name}) *nvim_win_get_var()* + Gets a window-scoped (w:) variable Parameters: ~ {window} Window handle, or 0 for current window + {name} Variable name Return: ~ - Window number + Variable value + +nvim_win_get_width({window}) *nvim_win_get_width()* + Gets the window width + + Parameters: ~ + {window} Window handle, or 0 for current window + + Return: ~ + Width as a count of columns nvim_win_is_valid({window}) *nvim_win_is_valid()* Checks if a window is valid @@ -2202,6 +2178,13 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()* Return: ~ 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 + + Parameters: ~ + {window} Window handle, or 0 for current window + {buffer} Buffer handle + nvim_win_set_config({window}, {config}) *nvim_win_set_config()* Configures window layout. Currently only for floating and external windows (including changing a split window to those @@ -2219,67 +2202,76 @@ nvim_win_set_config({window}, {config}) *nvim_win_set_config()* See also: ~ |nvim_open_win()| -nvim_win_get_config({window}) *nvim_win_get_config()* - Gets window configuration. +nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()* + Sets the (1,0)-indexed cursor position in the window. + |api-indexing| - The returned value may be given to |nvim_open_win()|. + Parameters: ~ + {window} Window handle, or 0 for current window + {pos} (row, col) tuple representing the new position - `relative` is empty for normal windows. +nvim_win_set_height({window}, {height}) *nvim_win_set_height()* + Sets the window height. This will only succeed if the screen + is split horizontally. Parameters: ~ {window} Window handle, or 0 for current window + {height} Height as a count of rows - Return: ~ - Map defining the window configuration, see - |nvim_open_win()| +nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()* + Sets a window option value. Passing 'nil' as value deletes the + option(only works if there's a global fallback) -nvim_win_close({window}, {force}) *nvim_win_close()* - Closes the window (like |:close| with a |window-ID|). + Parameters: ~ + {window} Window handle, or 0 for current window + {name} Option name + {value} Option value + +nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()* + Sets a window-scoped (w:) variable Parameters: ~ {window} Window handle, or 0 for current window - {force} Behave like `:close!` The last window of a - buffer with unwritten changes can be closed. The - buffer will become hidden, even if 'hidden' is - not set. + {name} Variable name + {value} Variable value + +nvim_win_set_width({window}, {width}) *nvim_win_set_width()* + Sets the window width. This will only succeed if the screen is + split vertically. + + Parameters: ~ + {window} Window handle, or 0 for current window + {width} Width as a count of columns ============================================================================== Tabpage Functions *api-tabpage* -nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()* - Gets the windows in a tabpage +nvim_tabpage_del_var({tabpage}, {name}) *nvim_tabpage_del_var()* + Removes a tab-scoped (t:) variable Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage + {name} Variable name - Return: ~ - List of windows in `tabpage` - -nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()* - Gets a tab-scoped (t:) variable +nvim_tabpage_get_number({tabpage}) *nvim_tabpage_get_number()* + Gets the tabpage number Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage - {name} Variable name Return: ~ - Variable value + Tabpage number -nvim_tabpage_set_var({tabpage}, {name}, {value}) *nvim_tabpage_set_var()* - Sets a tab-scoped (t:) variable +nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()* + Gets a tab-scoped (t:) variable Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage {name} Variable name - {value} Variable value - -nvim_tabpage_del_var({tabpage}, {name}) *nvim_tabpage_del_var()* - Removes a tab-scoped (t:) variable - Parameters: ~ - {tabpage} Tabpage handle, or 0 for current tabpage - {name} Variable name + Return: ~ + Variable value nvim_tabpage_get_win({tabpage}) *nvim_tabpage_get_win()* Gets the current window in a tabpage @@ -2290,23 +2282,31 @@ nvim_tabpage_get_win({tabpage}) *nvim_tabpage_get_win()* Return: ~ Window handle -nvim_tabpage_get_number({tabpage}) *nvim_tabpage_get_number()* - Gets the tabpage number +nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()* + Checks if a tabpage is valid Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage Return: ~ - Tabpage number + true if the tabpage is valid, false otherwise -nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()* - Checks if a tabpage is valid +nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()* + Gets the windows in a tabpage Parameters: ~ {tabpage} Tabpage handle, or 0 for current tabpage Return: ~ - true if the tabpage is valid, false otherwise + List of windows in `tabpage` + +nvim_tabpage_set_var({tabpage}, {name}, {value}) *nvim_tabpage_set_var()* + Sets a tab-scoped (t:) variable + + Parameters: ~ + {tabpage} Tabpage handle, or 0 for current tabpage + {name} Variable name + {value} Variable value ============================================================================== @@ -2335,10 +2335,17 @@ nvim_ui_detach() *nvim_ui_detach()* Removes the client from the list of UIs. |nvim_list_uis()| -nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()* - TODO: Documentation +nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()* + Tells Nvim the number of elements displaying in the popumenu, + to decide and movement. + + Parameters: ~ + {height} Popupmenu height, must be greater than zero. nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()* + TODO: Documentation + +nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()* TODO: Documentation *nvim_ui_try_resize_grid()* @@ -2354,11 +2361,4 @@ nvim_ui_try_resize_grid({grid}, {width}, {height}) {width} The new requested width. {height} The new requested height. -nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()* - Tells Nvim the number of elements displaying in the popumenu, - to decide and movement. - - Parameters: ~ - {height} Popupmenu height, must be greater than zero. - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 1c3a7f70c9..9601537c8d 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -861,6 +861,9 @@ schedule_wrap({cb}) *vim.schedule_wrap()* +deep_equal({a}, {b}) *vim.deep_equal()* + TODO: Documentation + deepcopy({orig}) *vim.deepcopy()* Returns a deep copy of the given object. Non-table objects are copied as in a typical Lua assignment, whereas table objects @@ -889,6 +892,45 @@ gsplit({s}, {sep}, {plain}) *vim.gsplit()* https://www.lua.org/pil/20.2.html http://lua-users.org/wiki/StringLibraryTutorial +is_callable({f}) *vim.is_callable()* + Returns true if object `f` can be called as a function. + + Parameters: ~ + {f} Any object + + Return: ~ + true if `f` is callable, else false + +list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* + Extends a list-like table with the values of another list-like + table. + + NOTE: This mutates dst! + + Parameters: ~ + {dst} list which will be modified and appended to. + {src} list from which values will be inserted. + {start} Start index on src. defaults to 1 + {finish} Final index on src. defaults to #src + + Return: ~ + dst + + See also: ~ + |vim.tbl_extend()| + +pesc({s}) *vim.pesc()* + Escapes magic chars in a Lua pattern string. + + Parameters: ~ + {s} String to escape + + Return: ~ + %-escaped pattern string + + See also: ~ + https://github.com/rxi/lume + split({s}, {sep}, {plain}) *vim.split()* Splits a string at each instance of a separator. @@ -910,28 +952,13 @@ split({s}, {sep}, {plain}) *vim.split()* See also: ~ |vim.gsplit()| -tbl_keys({t}) *vim.tbl_keys()* - Return a list of all keys used in a table. However, the order - of the return table of keys is not guaranteed. - - Parameters: ~ - {t} Table - - Return: ~ - list of keys - - See also: ~ - Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua - -tbl_values({t}) *vim.tbl_values()* - Return a list of all values used in a table. However, the - order of the return table of values is not guaranteed. +tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* + Add the reverse lookup values to an existing table. For + example: `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = + 1 }` Parameters: ~ - {t} Table - - Return: ~ - list of values + {o} table The table to add the reverse to. tbl_contains({t}, {value}) *vim.tbl_contains()* Checks if a list-like (vector) table contains `value` . @@ -943,10 +970,6 @@ tbl_contains({t}, {value}) *vim.tbl_contains()* Return: ~ true if `t` contains `value` -tbl_isempty({t}) *vim.tbl_isempty()* - See also: ~ - Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua@paramt Table to check - tbl_extend({behavior}, {...}) *vim.tbl_extend()* Merges two or more map-like tables. @@ -961,35 +984,6 @@ tbl_extend({behavior}, {...}) *vim.tbl_extend()* See also: ~ |extend()| -deep_equal({a}, {b}) *vim.deep_equal()* - TODO: Documentation - -tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* - Add the reverse lookup values to an existing table. For - example: `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = - 1 }` - - Parameters: ~ - {o} table The table to add the reverse to. - -list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* - Extends a list-like table with the values of another list-like - table. - - NOTE: This mutates dst! - - Parameters: ~ - {dst} list which will be modified and appended to. - {src} list from which values will be inserted. - {start} Start index on src. defaults to 1 - {finish} Final index on src. defaults to #src - - Return: ~ - dst - - See also: ~ - |vim.tbl_extend()| - tbl_flatten({t}) *vim.tbl_flatten()* Creates a copy of a list-like table such that any nested tables are "unrolled" and appended to the result. @@ -1003,6 +997,10 @@ tbl_flatten({t}) *vim.tbl_flatten()* See also: ~ Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua +tbl_isempty({t}) *vim.tbl_isempty()* + See also: ~ + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua@paramt Table to check + tbl_islist({t}) *vim.tbl_islist()* Table @@ -1010,30 +1008,41 @@ tbl_islist({t}) *vim.tbl_islist()* true: A non-empty array, false: A non-empty table, nil: An empty table -trim({s}) *vim.trim()* - Trim whitespace (Lua pattern "%s") from both sides of a - string. +tbl_keys({t}) *vim.tbl_keys()* + Return a list of all keys used in a table. However, the order + of the return table of keys is not guaranteed. Parameters: ~ - {s} String to trim + {t} Table Return: ~ - String with whitespace removed from its beginning and end + list of keys See also: ~ - https://www.lua.org/pil/20.2.html + Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua -pesc({s}) *vim.pesc()* - Escapes magic chars in a Lua pattern string. +tbl_values({t}) *vim.tbl_values()* + Return a list of all values used in a table. However, the + order of the return table of values is not guaranteed. Parameters: ~ - {s} String to escape + {t} Table Return: ~ - %-escaped pattern string + list of values + +trim({s}) *vim.trim()* + Trim whitespace (Lua pattern "%s") from both sides of a + string. + + Parameters: ~ + {s} String to trim + + Return: ~ + String with whitespace removed from its beginning and end See also: ~ - https://github.com/rxi/lume + https://www.lua.org/pil/20.2.html validate({opt}) *vim.validate()* Validates a parameter specification (types and values). @@ -1085,13 +1094,4 @@ validate({opt}) *vim.validate()* • msg: (optional) error string if validation fails -is_callable({f}) *vim.is_callable()* - Returns true if object `f` can be called as a function. - - Parameters: ~ - {f} Any object - - Return: ~ - true if `f` is callable, else false - vim:tw=78:ts=8:ft=help:norl: -- cgit From 93e7c7e3bd30ae141b613e71a6a3a863e6064d91 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 10 Dec 2019 01:24:20 -0800 Subject: doc [ci skip] --- runtime/doc/eval.txt | 4 ++-- runtime/doc/intro.txt | 2 ++ runtime/doc/lsp.txt | 1 + runtime/doc/map.txt | 5 ++--- runtime/doc/message.txt | 9 ++++----- runtime/doc/motion.txt | 6 +++--- runtime/doc/options.txt | 10 +++++----- runtime/doc/vim_diff.txt | 32 ++++++++++++++------------------ 8 files changed, 33 insertions(+), 36 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index fecb8b5f74..960a42ecbf 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3771,8 +3771,8 @@ feedkeys({string} [, {mode}]) *feedkeys()* and "\..." notation |expr-quote|. For example, feedkeys("\") simulates pressing of the key. But feedkeys('\') pushes 5 characters. - A special code that might be useful is , it exits the - wait for a character without doing anything. ** + The || keycode may be used to exit the + wait-for-character without doing anything. {mode} is a String, which can contain these character flags: 'm' Remap keys. This is default. If {mode} is absent, diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index c59ed43a47..3c3753df78 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -339,6 +339,8 @@ notation meaning equivalent decimal value(s) ~ end-of-line (can be , or , depends on system and 'fileformat') ** + cancel wait-for-character ** + no-op: do nothing (useful in mappings) ** cursor-up *cursor-up* *cursor_up* cursor-down *cursor-down* *cursor_down* diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d6d16b8481..f1b321b20f 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -38,6 +38,7 @@ go-to-definition, "hover", etc. Example config: > nnoremap gD lua vim.lsp.buf.implementation() nnoremap lua vim.lsp.buf.signature_help() nnoremap 1gD lua vim.lsp.buf.type_definition() + nnoremap gr lua vim.lsp.buf.references() < *vim.lsp.omnifunc()* diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 58c0d832e6..ed31ecc42e 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -522,10 +522,9 @@ single CTRL-V (you have to type CTRL-V two times). You can create an empty {rhs} by typing nothing after a single CTRL-V (you have to type CTRL-V two times). Unfortunately, you cannot do this in a vimrc file. - ** + || An easier way to get a mapping that doesn't produce anything, is to use -"" for the {rhs}. This only works when the |<>| notation is enabled. -For example, to make sure that function key 8 does nothing at all: > +"" for the {rhs}. For example, to disable function key 8: > :map :map! < diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index bcfd985e71..98e45ac6da 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -671,21 +671,20 @@ being disabled. Remove the 'C' flag from the 'cpoptions' option to enable it. *E471* > Argument required -This happens when an Ex command with mandatory argument(s) was executed, but -no argument has been specified. +Ex command was executed without a mandatory argument(s). *E474* *E475* *E983* > Invalid argument Invalid argument: {arg} Duplicate argument: {arg} -Ex command or function has been executed, but an invalid argument was -specified. Or a non-executable command was given to |system()|. +Ex command or function was given an invalid argument. Or |jobstart()| or +|system()| was given a non-executable command. *E488* > Trailing characters -An argument has been added to an Ex command that does not permit one. +An argument was given to an Ex command that does not permit one. *E477* *E478* > No ! allowed diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 3947e583b7..a6c072e489 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -1105,7 +1105,7 @@ Finally, navigate to a different webpage, new.com. The history is - third.com - new.com <-- -When the jumpoptions includes "stack", this is the behavior of neovim as well. +When the jumpoptions includes "stack", this is the behavior of Nvim as well. That is, given a jumplist like the following in which CTRL-O has been used to move back three times to location X @@ -1117,8 +1117,8 @@ move back three times to location X 2 213 2 src/nvim/mark.c 3 181 0 src/nvim/mark.c -jumping to location Y results in the locations after the current locations being -removed: +jumping to (new) location Y results in the locations after the current +locations being removed: jump line col file/text 3 1260 8 src/nvim/mark.c diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4b832a8606..494a265338 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4543,15 +4543,15 @@ A jump table for the options with a short description can be found at |Q_op|. *'pumheight'* *'ph'* 'pumheight' 'ph' number (default 0) global - Determines the maximum number of items to show in the popup menu for - Insert mode completion. When zero as much space as available is used. - |ins-completion-menu|. + Maximum number of items to show in the popup menu + (|ins-completion-menu|). Zero means "use available screen space". *'pumwidth'* *'pw'* 'pumwidth' 'pw' number (default 15) global - Determines the minium width to use for the popup menu for Insert mode - completion. |ins-completion-menu|. + Minimum width for the popup menu (|ins-completion-menu|). If the + cursor column + 'pumwidth' exceeds screen width, the popup menu is + nudged to fit on the screen. *'pyxversion'* *'pyx'* 'pyxversion' 'pyx' number (default depends on the build) diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 64b5830575..e19659a4c4 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -195,20 +195,20 @@ Normal commands: "Outline": Type |gO| in |:Man| and |:help| pages to see a document outline. Options: - 'cpoptions' flags: |cpo-_| - 'display' flag `msgsep` to minimize scrolling when showing messages - 'guicursor' works in the terminal - 'fillchars' local to window. flags: `msgsep` (see 'display' above) and `eob` - for |hl-EndOfBuffer| marker - 'inccommand' shows interactive results for |:substitute|-like commands - 'listchars' local to window - 'pumblend' pseudo-transparent popupmenu + 'cpoptions' flags: |cpo-_| + 'display' flags: "msgsep" minimizes scrolling when showing messages + 'guicursor' works in the terminal + 'fillchars' flags: "msgsep" (see 'display'), "eob" for |hl-EndOfBuffer| + marker, "foldopen", "foldsep", "foldclose" + 'inccommand' shows interactive results for |:substitute|-like commands + 'listchars' local to window + 'pumblend' pseudo-transparent popupmenu 'scrollback' - 'signcolumn' supports up to 9 dynamic/fixed columns - 'statusline' supports unlimited alignment sections - 'tabline' %@Func@foo%X can call any function on mouse-click - 'wildoptions' `pum` flag to use popupmenu for wildmode completion - 'winblend' pseudo-transparency in floating windows |api-floatwin| + 'signcolumn' supports up to 9 dynamic/fixed columns + 'statusline' supports unlimited alignment sections + 'tabline' %@Func@foo%X can call any function on mouse-click + 'wildoptions' "pum" flag to use popupmenu for wildmode completion + 'winblend' pseudo-transparency in floating windows |api-floatwin| 'winhighlight' window-local highlights Signs: @@ -336,16 +336,12 @@ Macro/|recording| behavior Motion: The |jumplist| avoids useless/phantom jumps. - When the new option |jumpoptions| includes 'stack', the jumplist behaves - like the tagstack or history in a web browser--jumping from the middle of - the jumplist discards the locations after the jumped-from position - (|jumplist-stack|). - Normal commands: |Q| is the same as |gQ| Options: 'ttimeout', 'ttimeoutlen' behavior was simplified + |jumpoptions| "stack" behavior Shell: Shell output (|:!|, |:make|, …) is always routed through the UI, so it -- cgit From d839c35871cb8f91705244455676217e0d76980e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 31 Dec 2019 02:55:12 -0800 Subject: doc: LSP --- runtime/doc/lsp.txt | 103 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 39 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index f1b321b20f..d51c79318a 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1,36 +1,45 @@ -*lsp.txt* Nvim LSP API +*lsp.txt* LSP - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL -Nvim Language Server Protocol (LSP) API *lsp* -Nvim is a client to the Language Server Protocol: +LSP client/framework *lsp* +Nvim supports the Language Server Protocol (LSP), which means it acts as +a client to LSP servers and includes a Lua framework `vim.lsp` for building +enhanced LSP tools. https://microsoft.github.io/language-server-protocol/ +LSP facilitates features like go-to-definition, find-references, hover, +completion, rename, format, refactor, etc., using semantic whole-project +analysis (unlike |ctags|). + Type |gO| to see the table of contents. -================================================================================ -LANGUAGE SERVER PROTOCOL (LSP) CLIENT *lsp-intro* +============================================================================== +QUICKSTART *lsp-quickstart* -The `vim.lsp` Lua module provides a flexible API for consuming LSP servers. +Nvim provides a LSP client, but the servers are provided by third parties. +Follow these steps to get LSP features: -To use LSP in practice, a language server must be installed. - https://microsoft.github.io/language-server-protocol/implementors/servers/ + 1. Install the nvim-lsp plugin. It provides common configuration for + various servers so you can get started quickly. + https://github.com/neovim/nvim-lsp + 2. Install a language server. Try ":LspInstall " or use your system + package manager to install the relevant language server: + https://microsoft.github.io/language-server-protocol/implementors/servers/ + 3. Add `nvim_lsp.xx.setup{…}` to your vimrc, where "xx" is the name of the + relevant config. See the nvim-lsp README for details. -After installing a language server to your machine, you must tell Nvim how to -start and interact with that language server. -- Easy way: use the configs provided here by the nvim-lsp plugin. - https://github.com/neovim/nvim-lsp -- Low-level way: use |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| - directly. Useful if you want to build advanced LSP plugins based on the - Nvim LSP module. |lsp-advanced-js-example| +To check LSP clients attached to the current buffer: > + :lua print(vim.inspect(vim.lsp.buf_get_clients())) +< *lsp-config* -Nvim LSP client will automatically provide inline diagnostics when available. -|lsp-callbacks| But you probably want to use other features too, such as -go-to-definition, "hover", etc. Example config: > +Inline diagnostics are enabled automatically, e.g. syntax errors will be +annotated in the buffer. But you probably want to use other features like +go-to-definition, hover, etc. Example config: > nnoremap gd lua vim.lsp.buf.declaration() nnoremap lua vim.lsp.buf.definition() @@ -71,6 +80,14 @@ the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". ================================================================================ *lsp-core-api* + +The `vim.lsp` Lua module is a framework for building LSP plugins. + + 1. Start with |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. + 2. Peek at the API: > + :lua print(vim.inspect(vim.lsp)) +< 3. See |lsp-advanced-js-example| for a full example. + These are the core api functions for working with clients. You will mainly be using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations and |vim.lsp.get_client_by_id()| to retrieve a client by its id after it has @@ -149,17 +166,17 @@ vim.lsp.start_client({config}) `vim.lsp.client_errors[code]` can be used to retrieve a human understandable string. - `before_init(initialize_params, config)` - A function which is called *before* the request `initialize` is completed. - `initialize_params` contains the parameters we are sending to the server - and `config` is the config that was passed to `start_client()` for - convenience. You can use this to modify parameters before they are sent. + `before_init(params, config)` + Callback invoked before the LSP "initialize" phase, where `params` + contains the parameters being sent to the server and `config` is the + config that was passed to `start_client()`. You can use this to modify + parameters before they are sent. - `on_init(client, initialize_result)` - A function which is called after the request `initialize` is completed. - `initialize_result` contains `capabilities` and anything else the server - may send. For example, `clangd` sends `initialize_result.offsetEncoding` - if `capabilities.offsetEncoding` was sent to it. You can *only* modify the + `on_init(client, result)` + Callback invoked after the LSP "initialize" phase, where `result` is + a table of `capabilities` and anything else the server may send. For + example, clangd sends `initialize_result.offsetEncoding` if + `capabilities.offsetEncoding` was sent to it. You can only modify the `client.offset_encoding` here before any notifications are sent. `on_attach(client, bufnr)` @@ -180,13 +197,11 @@ vim.lsp.start_client({config}) You can use |vim.lsp.get_client_by_id()| to get the actual client object. See |lsp-client| for what the client structure will be. - NOTE: The client is only available *after* it has been initialized, which + NOTE: The client is only available after it has been initialized, which may happen after a small delay (or never if there is an error). For this reason, you may want to use `on_init` to do any actions once the client has been initialized. - *lsp-client* - The client object has some methods and members related to using the client. Methods:~ @@ -357,10 +372,9 @@ Handlers are called for: ================================================================================ VIM.LSP.PROTOCOL *vim.lsp.protocol* -The `vim.lsp.protocol` module provides constants defined in the LSP -specification, and helper functions for creating protocol-related objects. - - https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md +Module `vim.lsp.protocol` defines constants dictated by the LSP specification, +and helper functions for creating protocol-related objects. +https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow reverse lookup by either the number or string name. @@ -383,8 +397,9 @@ TODO: Describe the utils here for handling/applying things from LSP. ================================================================================ *lsp-buf-methods* -There are methods which operate on the buffer level for all of the active -clients attached to the buffer. + +The "vim.lsp.buf_" functions perform operations for all LSP clients attached +to the given buffer. *vim.lsp.buf_request()* vim.lsp.buf_request({bufnr}, {method}, {params}, [{callback}]) @@ -463,6 +478,9 @@ vim.lsp.log_levels ================================================================================ LSP EXAMPLE *lsp-advanced-js-example* +This example is for plugin authors who want to work with "vim.lsp" framework. +If you only want to use (not develop) LSP features, see |lsp-quickstart|. + For more advanced configurations where just filtering by filetype isn't sufficient, you can use the `vim.lsp.start_client()` and `vim.lsp.buf_attach_client()` commands to easily customize the configuration @@ -573,4 +591,11 @@ The example will: vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]] < -vim:tw=78:ts=8:ft=help:norl: + + +================================================================================ +LSP API *lsp-api* + + + + vim:tw=78:ts=8:ft=help:norl: -- cgit From b112fe828fd2457692f556626d7657615e53cb0b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 31 Dec 2019 06:52:14 -0800 Subject: gen_vimdoc.py: generate LSP docs --- runtime/doc/api.txt | 9 +- runtime/doc/lsp.txt | 1072 ++++++++++++++++++++++++++------------ runtime/lua/vim/lsp.lua | 394 ++++++++------ runtime/lua/vim/lsp/protocol.lua | 4 + runtime/lua/vim/lsp/rpc.lua | 7 +- 5 files changed, 990 insertions(+), 496 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 7d45330b66..f8fdd64a9b 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -569,7 +569,8 @@ nvim_call_atomic({calls}) *nvim_call_atomic()* occurred, the values from all preceding calls will still be returned. -nvim_call_dict_function({dict}, {fn}, {args}) *nvim_call_dict_function()* + *nvim_call_dict_function()* +nvim_call_dict_function({dict}, {fn}, {args}) Calls a VimL |Dictionary-function| with the given arguments. On execution error: fails with VimL error, does not update @@ -1878,7 +1879,8 @@ nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()* Return: ~ Variable value -nvim_buf_get_virtual_text({buffer}, {lnum}) *nvim_buf_get_virtual_text()* + *nvim_buf_get_virtual_text()* +nvim_buf_get_virtual_text({buffer}, {lnum}) Get the virtual text (annotation) for a buffer line. The virtual text is returned as list of lists, whereas the @@ -2300,7 +2302,8 @@ nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()* Return: ~ List of windows in `tabpage` -nvim_tabpage_set_var({tabpage}, {name}, {value}) *nvim_tabpage_set_var()* + *nvim_tabpage_set_var()* +nvim_tabpage_set_var({tabpage}, {name}, {value}) Sets a tab-scoped (t:) variable Parameters: ~ diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d51c79318a..4d04e50998 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -49,8 +49,6 @@ go-to-definition, hover, etc. Example config: > nnoremap 1gD lua vim.lsp.buf.type_definition() nnoremap gr lua vim.lsp.buf.references() -< - *vim.lsp.omnifunc()* Nvim provides the vim.lsp.omnifunc 'omnifunc' handler which allows |i_CTRL-X_CTRL-O| to consume LSP completion features. Example config (note the use of |v:lua| to call Lua from Vimscript): > @@ -79,246 +77,24 @@ Some other plugin may be overriding the option. To avoid that, you could set the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". ================================================================================ - *lsp-core-api* +LSP API *lsp-api* The `vim.lsp` Lua module is a framework for building LSP plugins. 1. Start with |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|. 2. Peek at the API: > :lua print(vim.inspect(vim.lsp)) -< 3. See |lsp-advanced-js-example| for a full example. - -These are the core api functions for working with clients. You will mainly be -using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations -and |vim.lsp.get_client_by_id()| to retrieve a client by its id after it has -initialized (or {config.on_init}. see below) - - *vim.lsp.start_client()* - -vim.lsp.start_client({config}) - - The main function used for starting clients. - Start a client and initialize it. - - Its arguments are passed via a configuration object {config}. - - Mandatory parameters:~ - - `root_dir` - {string} specifying the directory where the LSP server will base - as its rootUri on initialization. - - `cmd` - {string} or {list} which is the base command to execute for the LSP. A - string will be run using |'shell'| and a list will be interpreted as a - bare command with arguments passed. This is the same as |jobstart()|. - - Optional parameters:~ - - `cmd_cwd` - {string} specifying the directory to launch the `cmd` process. This is not - related to `root_dir`. - By default, |getcwd()| is used. - - `cmd_env` - {table} specifying the environment flags to pass to the LSP on spawn. - This can be specified using keys like a map or as a list with `k=v` pairs - or both. Non-string values are coerced to a string. - For example: - `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }` - - `capabilities` - A {table} which will be used instead of - `vim.lsp.protocol.make_client_capabilities()` which contains Nvim's - default capabilities and passed to the language server on initialization. - You'll probably want to use make_client_capabilities() and modify the - result. - NOTE: - To send an empty dictionary, you should use - `{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as - an array. - - `callbacks` - A {table} of whose keys are language server method names and the values - are `function(err, method, params, client_id)` See |lsp-callbacks| for - more. This will be combined with |lsp-default-callbacks| to resolve - the callbacks for a client as a fallback. - - `init_options` - A {table} of values to pass in the initialization request as - `initializationOptions`. See the `initialize` in the LSP spec. - - `name` - A {string} used in log messages. Defaults to {client_id} - - `offset_encoding` - One of "utf-8", "utf-16", or "utf-32" which is the encoding that the LSP - server expects. - The default encoding for Language Server Protocol is UTF-16, but there are - language servers which may use other encodings. - By default, it is "utf-16" as specified in the LSP specification. The - client does not verify this is correct. - - `on_error(code, ...)` - A function for handling errors thrown by client operation. {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. - `vim.lsp.client_errors[code]` can be used to retrieve a human - understandable string. - - `before_init(params, config)` - Callback invoked before the LSP "initialize" phase, where `params` - contains the parameters being sent to the server and `config` is the - config that was passed to `start_client()`. You can use this to modify - parameters before they are sent. - - `on_init(client, result)` - Callback invoked after the LSP "initialize" phase, where `result` is - a table of `capabilities` and anything else the server may send. For - example, clangd sends `initialize_result.offsetEncoding` if - `capabilities.offsetEncoding` was sent to it. You can only modify the - `client.offset_encoding` here before any notifications are sent. - - `on_attach(client, bufnr)` - A function which is called after the client is attached to a buffer. - - `on_exit(code, signal, client_id)` - A function which is called after the client has exited. code is the exit - code of the process, and signal is a number describing the signal used to - terminate (if any). - - `trace` - "off" | "messages" | "verbose" | nil passed directly to the language - server in the initialize request. - Invalid/empty values will default to "off" - - Returns:~ - {client_id} - You can use |vim.lsp.get_client_by_id()| to get the actual client object. - See |lsp-client| for what the client structure will be. - - NOTE: The client is only available after it has been initialized, which - may happen after a small delay (or never if there is an error). For this - reason, you may want to use `on_init` to do any actions once the client has - been initialized. - -The client object has some methods and members related to using the client. - - Methods:~ - - `request(method, params, [callback])` - Send a request to the server. If callback is not specified, it will use - {client.callbacks} to try to find a callback. If one is not found there, - then an error will occur. - This is a thin wrapper around {client.rpc.request} with some additional - checking. - Returns a boolean to indicate if the notification was successful. If it - is false, then it will always be false (the client has shutdown). - If it was successful, then it will return the request id as the second - result. You can use this with `notify("$/cancel", { id = request_id })` - to cancel the request. This helper is made automatically with - |vim.lsp.buf_request()| - Returns: status, [client_id] - - `notify(method, params)` - This is just {client.rpc.notify}() - Returns a boolean to indicate if the notification was successful. If it - is false, then it will always be false (the client has shutdown). - Returns: status - - `cancel_request(id)` - This is just {client.rpc.notify}("$/cancelRequest", { id = id }) - Returns the same as `notify()`. - - `stop([force])` - Stop a client, optionally with force. - By default, it will just ask the server to shutdown without force. - If you request to stop a client which has previously been requested to - shutdown, it will automatically escalate and force shutdown. - - `is_stopped()` - Returns true if the client is fully stopped. - - Members: ~ - `id` (number) - The id allocated to the client. - - `name` (string) - If a name is specified on creation, that will be used. Otherwise it is - just the client id. This is used for logs and messages. - - `offset_encoding` (string) - The encoding used for communicating with the server. You can modify this - in the `on_init` method before text is sent to the server. - - `callbacks` (table) - The callbacks used by the client as described in |lsp-callbacks|. - - `config` (table) - A copy of the table that was passed by the user to - |vim.lsp.start_client()|. - - `server_capabilities` (table) - The response from the server sent on `initialize` describing the - server's capabilities. - - `resolved_capabilities` (table) - A normalized table of capabilities that we have detected based on the - initialize response from the server in `server_capabilities`. - - - *vim.lsp.buf_attach_client()* -vim.lsp.buf_attach_client({bufnr}, {client_id}) - - Implements the `textDocument/did*` notifications required to track a buffer - for any language server. - - Without calling this, the server won't be notified of changes to a buffer. - - *vim.lsp.get_client_by_id()* -vim.lsp.get_client_by_id({client_id}) - - Look up an active client by its id, returns nil if it is not yet initialized - or is not a valid id. Returns |lsp-client| - - *vim.lsp.stop_client()* -vim.lsp.stop_client({client_id}, [{force}]) - - Stop a client, optionally with force. - By default, it will just ask the server to shutdown without force. - If you request to stop a client which has previously been requested to - shutdown, it will automatically escalate and force shutdown. - - You can also use `client.stop()` if you have access to the client. - - *vim.lsp.stop_all_clients()* -vim.lsp.stop_all_clients([{force}]) - - |vim.lsp.stop_client()|, but for all active clients. - - *vim.lsp.get_active_clients()* -vim.lsp.get_active_clients() +< 3. See |lsp-extension-example| for a full example. - Return a list of all of the active clients. See |lsp-client| for a - description of what a client looks like. +LSP core API is described at |lsp-core|. Those are the core functions for +creating and managing clients. - *vim.lsp.rpc_response_error()* -vim.lsp.rpc_response_error({code}, [{message}], [{data}]) +The `vim.lsp.buf_…` functions perform operations for all LSP clients attached +to the given buffer. |lsp-buf| - Helper function to create an RPC response object/table. This is an alias for - |vim.lsp.rpc.rpc_response_error|. Code must be an RPC error code as - described in `vim.lsp.protocol.ErrorCodes`. - - You can describe an optional {message} string or arbitrary {data} to send to - the server. - -================================================================================ -LSP CALLBACKS *lsp-callbacks* - -DEFAULT CALLBACKS ~ - *vim.lsp.callbacks* -The `vim.lsp.callbacks` table defines default callbacks used when -creating a new client. Keys are LSP method names: > +LSP request/response handlers are implemented as Lua callbacks. +|lsp-callbacks| The `vim.lsp.callbacks` table defines default callbacks used +when creating a new client. Keys are LSP method names: > :lua print(vim.inspect(vim.tbl_keys(vim.lsp.callbacks))) @@ -330,7 +106,7 @@ These LSP requests/notifications are defined by default: You can check these via `vim.tbl_keys(vim.lsp.callbacks)`. -These will be used preferrentially in `vim.lsp.buf` methods when handling +These will be used preferentially in `vim.lsp.buf_…` methods for handling requests. They will also be used when responding to server requests and notifications. @@ -369,117 +145,24 @@ Handlers are called for: - Handling requests initiated by the client if the request doesn't explicitly specify a callback (such as in |vim.lsp.buf_request|). -================================================================================ + VIM.LSP.PROTOCOL *vim.lsp.protocol* Module `vim.lsp.protocol` defines constants dictated by the LSP specification, and helper functions for creating protocol-related objects. https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md -Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow reverse -lookup by either the number or string name. - - e.g. vim.lsp.protocol.TextDocumentSyncKind.Full == 1 - vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" - - Utility functions used internally are: - `vim.lsp.protocol.make_client_capabilities()` - Make a ClientCapabilities object. These are the builtin - capabilities. - `vim.lsp.protocol.resolve_capabilities(server_capabilites)` - Creates a normalized object describing capabilities from the server - capabilities. - -================================================================================ - *vim.lsp.util* - -TODO: Describe the utils here for handling/applying things from LSP. - -================================================================================ - *lsp-buf-methods* - -The "vim.lsp.buf_" functions perform operations for all LSP clients attached -to the given buffer. - - *vim.lsp.buf_request()* -vim.lsp.buf_request({bufnr}, {method}, {params}, [{callback}]) - Send a async request for all the clients active and attached to the buffer. - - Parameters: ~ - {bufnr}: The buffer handle or 0 for the current buffer. - - {method}: The LSP method name. - - {params}: The parameters to send. - - {callback}: An optional `function(err, method, params, client_id)` which - will be called for this request. If you do not specify it, then it will - use the client's callback in {client.callbacks}. See |lsp-callbacks| for - more information. - - Returns:~ - - A table from client id to the request id for all of the successful - requests. - - The second result is a function which can be used to cancel all the - requests. You can do this individually with `client.cancel_request()` - - *vim.lsp.buf_request_sync()* -vim.lsp.buf_request_sync({bufnr}, {method}, {params}, [{timeout_ms}]) - Calls |vim.lsp.buf_request()|, but it will wait for the result and block Vim - in the process. - The parameters are the same as |vim.lsp.buf_request()|, but the return - result is different. - It will wait maximum of {timeout_ms} which defaults to 100ms. - - Returns:~ - - If the timeout is exceeded or a cancel is sent or an error, it will cancel - the request and return `nil, err` where `err` is a string that describes - the reason why it failed. - - If it is successful, it will return a table from client id to result id. - - *vim.lsp.buf_notify()* -vim.lsp.buf_notify({bufnr}, {method}, {params}) - Send a notification to all servers on the buffer. - - Parameters: ~ - {bufnr}: The buffer handle or 0 for the current buffer. - - {method}: The LSP method name. - - {params}: The parameters to send. - -================================================================================ - *lsp-logging* - - *vim.lsp.set_log_level()* -vim.lsp.set_log_level({level}) - You can set the log level for language server client logging. - Possible values: "trace", "debug", "info", "warn", "error" - - Default: "warn" - - Example: `lua vim.lsp.set_log_level("debug")` +For example `vim.lsp.protocol.ErrorCodes` allows reverse lookup by number or +name: > - *vim.lsp.get_log_path()* -vim.lsp.get_log_path() - Returns the path that LSP logs are written. - - *vim.lsp.log_levels* -vim.lsp.log_levels - Log level dictionary with reverse lookup as well. - - Can be used to lookup the number from the name or vice-versa. - Levels: "trace" (0), "debug" (1), "info" (2), "warn" (3), "error" (4) + vim.lsp.protocol.TextDocumentSyncKind.Full == 1 + vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" ================================================================================ -LSP EXAMPLE *lsp-advanced-js-example* +LSP EXAMPLE *lsp-extension-example* -This example is for plugin authors who want to work with "vim.lsp" framework. -If you only want to use (not develop) LSP features, see |lsp-quickstart|. +This example is for plugin authors or users who want a lot of control. If you +are just getting started see |lsp-quickstart|. For more advanced configurations where just filtering by filetype isn't sufficient, you can use the `vim.lsp.start_client()` and @@ -593,9 +276,724 @@ The example will: -================================================================================ -LSP API *lsp-api* +============================================================================== +Lua module: vim.lsp *lsp-core* + +buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()* + Implements the `textDocument/did…` notifications required to + track a buffer for any language server. + + Without calling this, the server won't be notified of changes + to a buffer. + + 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. + + Parameters: ~ + {bufnr} (optional, number): Buffer handle, or 0 for + current + +buf_get_full_text({bufnr}) *vim.lsp.buf_get_full_text()* + TODO: Documentation + +buf_is_attached({bufnr}, {client_id}) *vim.lsp.buf_is_attached()* + Checks if a buffer is attached for a particular client. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current + {client_id} (number) the client id + +buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()* + Sends a notification to all servers attached to the buffer. + + Parameters: ~ + {bufnr} (optional, number) Buffer handle, or 0 for + current + {method} (string) LSP method name + {params} (string) Parameters to send to the server + + Return: ~ + nil + +buf_print_debug_info({bufnr}) *vim.lsp.buf_print_debug_info()* + TODO: Documentation + + *vim.lsp.buf_request()* +buf_request({bufnr}, {method}, {params}, {callback}) + Sends an async request for all active clients attached to the + buffer. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current. + {method} (string) LSP method name + {params} (optional, table) Parameters to send to the + server + {callback} (optional, functionnil) Handler + + Return: ~ + 2-tuple: + • Map of client-id:request-id pairs for all successful + requests. + • Function which can be used to cancel all the requests. + You could instead iterate all clients and call their + `cancel_request()` methods. + + *vim.lsp.buf_request_sync()* +buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) + Sends a request to a server and waits for the response. + + Calls |vim.lsp.buf_request()| but blocks Nvim while awaiting + the result. Parameters are the same as |vim.lsp.buf_request()| + but the return result is different. Wait maximum of + {timeout_ms} (default 100) ms. + + Parameters: ~ + {bufnr} (number) Buffer handle, or 0 for current. + {method} (string) LSP method name + {params} (optional, table) Parameters to send to the + server + {timeout_ms} (optional, number, default=100) Maximum time + in milliseconds to wait for a result. + + Return: ~ + Map of client_id:request_result. On timeout, cancel or + error, returns `(nil, err)` where `err` is a string + describing the failure reason. + +cancel_request({id}) *vim.lsp.cancel_request()* + TODO: Documentation + +client() *vim.lsp.client* + LSP client object. + + • Methods: + • request(method, params, [callback]) Send a request to the + server. If callback is not specified, it will use + {client.callbacks} to try to find a callback. If one is + not found there, then an error will occur. This is a thin + wrapper around {client.rpc.request} with some additional + checking. Returns a boolean to indicate if the + notification was successful. If it is false, then it will + always be false (the client has shutdown). If it was + successful, then it will return the request id as the + second result. You can use this with `notify("$/cancel", { + id = request_id })` to cancel the request. This helper is + made automatically with |vim.lsp.buf_request()| Returns: + status, [client_id] + • notify(method, params) This is just {client.rpc.notify}() + Returns a boolean to indicate if the notification was + successful. If it is false, then it will always be false + (the client has shutdown). Returns: status + • cancel_request(id) This is just + {client.rpc.notify}("$/cancelRequest", { id = id }) + Returns the same as `notify()` . + • stop([force]) Stop a client, optionally with force. By + default, it will just ask the server to shutdown without + force. If you request to stop a client which has + previously been requested to shutdown, it will + automatically escalate and force shutdown. + • is_stopped() Returns true if the client is fully stopped. + + • Members + • id (number): The id allocated to the client. + • name (string): If a name is specified on creation, that + will be used. Otherwise it is just the client id. This is + used for logs and messages. + • offset_encoding (string): The encoding used for + communicating with the server. You can modify this in the + `on_init` method before text is sent to the server. + • callbacks (table): The callbacks used by the client as + described in |lsp-callbacks|. + • config (table): copy of the table that was passed by the + user to |vim.lsp.start_client()|. + • server_capabilities (table): Response from the server sent + on `initialize` describing the server's capabilities. + • resolved_capabilities (table): Normalized table of + capabilities that we have detected based on the initialize + response from the server in `server_capabilities` . + +client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* + TODO: Documentation + +err_message({...}) *vim.lsp.err_message()* + TODO: Documentation + + *vim.lsp.for_each_buffer_client()* +for_each_buffer_client({bufnr}, {callback}) + TODO: Documentation + +get_active_clients() *vim.lsp.get_active_clients()* + Gets all active clients. + + Return: ~ + Table of |vim.lsp.client| objects + +get_client_by_id({client_id}) *vim.lsp.get_client_by_id()* + Gets an active client by id, or nil if the id is invalid or + the client is not yet initialized. + + Parameters: ~ + {client_id} client id number + + Return: ~ + |vim.lsp.client| object, or nil + +get_log_path() *vim.lsp.get_log_path()* + TODO: Documentation + +initialize() *vim.lsp.initialize()* + TODO: Documentation + +is_dir({filename}) *vim.lsp.is_dir()* + TODO: Documentation + +is_stopped() *vim.lsp.is_stopped()* + TODO: Documentation + +next_client_id() *vim.lsp.next_client_id()* + TODO: Documentation + +notification({method}, {params}) *vim.lsp.notification()* + TODO: Documentation + +notify({...}) *vim.lsp.notify()* + TODO: Documentation + +omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* + TODO: Documentation + +on_error({code}, {err}) *vim.lsp.on_error()* + TODO: Documentation + +on_exit({code}, {signal}) *vim.lsp.on_exit()* + TODO: Documentation + +once({fn}) *vim.lsp.once()* + TODO: Documentation + +optional_validator({fn}) *vim.lsp.optional_validator()* + TODO: Documentation + +print_debug_info() *vim.lsp.print_debug_info()* + TODO: Documentation + +request({method}, {params}, {callback}) *vim.lsp.request()* + TODO: Documentation + +resolve_bufnr({bufnr}) *vim.lsp.resolve_bufnr()* + TODO: Documentation + +resolve_callback({method}) *vim.lsp.resolve_callback()* + TODO: Documentation + +server_request({method}, {params}) *vim.lsp.server_request()* + TODO: Documentation + +set_log_level({level}) *vim.lsp.set_log_level()* + Sets the global log level for LSP logging. + + Levels by name: "trace", "debug", "info", "warn", "error" + Level numbers begin with "trace" at 0 + + Use `lsp.log_levels` for reverse lookup. + + Parameters: ~ + {level} [number|string] the case insensitive level name + or number + + See also: ~ + |vim.lsp.log_levels| + +start_client({config}) *vim.lsp.start_client()* + Start a client and initialize it. Its arguments are passed via + a configuration object. + + Mandatory parameters: + + root_dir: {string} specifying the directory where the LSP + server will base as its rootUri on initialization. + + cmd: {string} or {list} which is the base command to execute + for the LSP. A string will be run using 'shell' and a list + will be interpreted as a bare command with arguments passed. + This is the same as |jobstart()|. + + Parameters: ~ + {cmd_cwd} {string} specifying the directory to + launch the `cmd` process. This is not + related to `root_dir` . By default, + |getcwd()| is used. + {cmd_env} {table} specifying the environment + flags to pass to the LSP on spawn. This + can be specified using keys like a map + or as a list with `k=v` pairs or both. + Non-string values are coerced to a + string. For example: `{ + "PRODUCTION=true"; "TEST=123"; PORT = + 8080; HOST = "0.0.0.0"; }` . + {capabilities} Map overriding the default capabilities + defined by + |vim.lsp.protocol.make_client_capabilities()|, + passed to the language server on + initialization. Hint: use + make_client_capabilities() and modify + its result. + • Note: To send an empty dictionary use + `{[vim.type_idx]=vim.types.dictionary}` + , else it will be encoded as an + array. + {callbacks} Map of language server method names to `function(err, method, params, + client_id)` handler. Invoked for: + • Notifications from the server, where + `err` will always be `nil` . + • Requests initiated by the server. For + these you can respond by returning + two values: `result, err` where err + must be shaped like a RPC error, i.e. + `{ code, message, data? }` . Use + |vim.lsp.rpc_response_error()| to + help with this. + • Default callback for client requests + not explicitly specifying a callback. + {init_options} values to pass in the initialization + request as `initializationOptions` . + See `initialize` in the LSP spec. + {name} string used in log messages. Defaults + to {client_id} + {offset_encoding} One of "utf-8", "utf-16", or "utf-32" + which is the encoding that the LSP + server expects. By default, it is + "utf-16" as specified in the LSP + specification. The client does not + verify this is correct. + {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. + {before_init} Callback with parameters + (initialize_params, config) invoked + before the LSP "initialize" phase, + where `params` contains the parameters + being sent to the server and `config` + is the config that was passed to + `start_client()` . You can use this to + modify parameters before they are sent. + {on_init} Callback (client, initialize_result) + invoked after LSP "initialize", where + `result` is a table of `capabilities` + and anything else the server may send. + For example, clangd sends + `initialize_result.offsetEncoding` if + `capabilities.offsetEncoding` was sent + to it. You can only modify the + `client.offset_encoding` here before + any notifications are sent. + {on_exit} Callback (code, signal, client_id) + invoked on client exit. + • code: exit code of the process + • signal: number describing the signal + used to terminate (if any) + • client_id: client handle + {on_attach} Callback (client, bufnr) invoked when + client attaches to a buffer. + {trace} "off" | "messages" | "verbose" | nil + passed directly to the language server + in the initialize request. + Invalid/empty values will default to + "off" + + Return: ~ + Client id. |vim.lsp.get_client_by_id()| Note: client is + only available after it has been initialized, which may + happen after a small delay (or never if there is an + error). Use `on_init` to do any actions once the client + has been initialized. + +stop({force}) *vim.lsp.stop()* + TODO: Documentation + +stop_all_clients({force}) *vim.lsp.stop_all_clients()* + Stops all clients. + + Parameters: ~ + {force} boolean (optional) shutdown forcefully + +stop_client({client_id}, {force}) *vim.lsp.stop_client()* + Stops a client. + + You can also use the `stop()` function on a |vim.lsp.client| + object. + + By default asks the server to shutdown, unless stop was + requested already for this client, then force-shutdown is + attempted. + + Parameters: ~ + {client_id} client id number + {force} boolean (optional) shutdown forcefully + + *vim.lsp.text_document_did_open_handler()* +text_document_did_open_handler({bufnr}, {client}) + TODO: Documentation + +unsupported_method({method}) *vim.lsp.unsupported_method()* + TODO: Documentation + +validate_client_config({config}) *vim.lsp.validate_client_config()* + TODO: Documentation + +validate_command({input}) *vim.lsp.validate_command()* + TODO: Documentation + +validate_encoding({encoding}) *vim.lsp.validate_encoding()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.protocol *lsp-protocol* + +ifnil({a}, {b}) *vim.lsp.protocol.ifnil()* + TODO: Documentation + + *vim.lsp.protocol.make_client_capabilities()* +make_client_capabilities() + Gets a new ClientCapabilities object describing the LSP client + capabilities. + + *vim.lsp.protocol.resolve_capabilities()* +resolve_capabilities({server_capabilities}) + `*` to match one or more characters in a path segment `?` to + match on one character in a path segment `**` to match any + number of path segments, including none `{}` to group + conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and + JavaScript files) `[]` to declare a range of characters to + match in a path segment (e.g., `example.[0-9]` to match on + `example.0` , `example.1` , …) `[!...]` to negate a range of + characters to match in a path segment (e.g., `example.[!0-9]` + to match on `example.a` , `example.b` , but not `example.0` ) + + *vim.lsp.protocol.transform_schema_comments()* +transform_schema_comments() + TODO: Documentation + + *vim.lsp.protocol.transform_schema_to_table()* +transform_schema_to_table() + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.buf *lsp-buf* + +completion({context}) *vim.lsp.buf.completion()* + TODO: Documentation + +declaration() *vim.lsp.buf.declaration()* + TODO: Documentation + +definition() *vim.lsp.buf.definition()* + TODO: Documentation + +formatting({options}) *vim.lsp.buf.formatting()* + TODO: Documentation + +hover() *vim.lsp.buf.hover()* + TODO: Documentation + +implementation() *vim.lsp.buf.implementation()* + TODO: Documentation + +npcall({fn}, {...}) *vim.lsp.buf.npcall()* + TODO: Documentation + +ok_or_nil({status}, {...}) *vim.lsp.buf.ok_or_nil()* + TODO: Documentation + +peek_definition() *vim.lsp.buf.peek_definition()* + TODO: Documentation + + *vim.lsp.buf.range_formatting()* +range_formatting({options}, {start_pos}, {end_pos}) + TODO: Documentation + +references({context}) *vim.lsp.buf.references()* + TODO: Documentation + +rename({new_name}) *vim.lsp.buf.rename()* + TODO: Documentation + +request({method}, {params}, {callback}) *vim.lsp.buf.request()* + TODO: Documentation + +signature_help() *vim.lsp.buf.signature_help()* + TODO: Documentation + +type_definition() *vim.lsp.buf.type_definition()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.callbacks *lsp-callbacks* + +err_message({...}) *vim.lsp.callbacks.err_message()* + TODO: Documentation + + *vim.lsp.callbacks.location_callback()* +location_callback({_}, {method}, {result}) + TODO: Documentation + + *vim.lsp.callbacks.log_message()* +log_message({_}, {_}, {result}, {client_id}) + TODO: Documentation + + *vim.lsp.callbacks.signature_help_to_preview_contents()* +signature_help_to_preview_contents({input}) + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.log *lsp-log* + +get_filename() *vim.lsp.log.get_filename()* + TODO: Documentation + +path_join({...}) *vim.lsp.log.path_join()* + TODO: Documentation + +set_level({level}) *vim.lsp.log.set_level()* + TODO: Documentation + +should_log({level}) *vim.lsp.log.should_log()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.rpc *lsp-rpc* + +convert_NIL({v}) *vim.lsp.rpc.convert_NIL()* + TODO: Documentation + + *vim.lsp.rpc.create_and_start_client()* +create_and_start_client({cmd}, {cmd_args}, {handlers}, + {extra_spawn_params}) + TODO: Documentation + +encode_and_send({payload}) *vim.lsp.rpc.encode_and_send()* + TODO: Documentation + +force_env_list({final_env}) *vim.lsp.rpc.force_env_list()* + TODO: Documentation + + *vim.lsp.rpc.format_message_with_content_length()* +format_message_with_content_length({encoded_message}) + TODO: Documentation + +format_rpc_error({err}) *vim.lsp.rpc.format_rpc_error()* + TODO: Documentation + +handle_body({body}) *vim.lsp.rpc.handle_body()* + TODO: Documentation + +is_dir({filename}) *vim.lsp.rpc.is_dir()* + TODO: Documentation + +json_decode({data}) *vim.lsp.rpc.json_decode()* + TODO: Documentation + +json_encode({data}) *vim.lsp.rpc.json_encode()* + TODO: Documentation + +notification({method}, {params}) *vim.lsp.rpc.notification()* + TODO: Documentation + +on_error({errkind}, {...}) *vim.lsp.rpc.on_error()* + TODO: Documentation + +on_exit({code}, {signal}) *vim.lsp.rpc.on_exit()* + TODO: Documentation + +onexit({code}, {signal}) *vim.lsp.rpc.onexit()* + TODO: Documentation + +parse_headers({header}) *vim.lsp.rpc.parse_headers()* + TODO: Documentation + + *vim.lsp.rpc.pcall_handler()* +pcall_handler({errkind}, {status}, {head}, {...}) + TODO: Documentation + +request_parser_loop() *vim.lsp.rpc.request_parser_loop()* + TODO: Documentation + + *vim.lsp.rpc.rpc_response_error()* +rpc_response_error({code}, {message}, {data}) + Creates an RPC response object/table. + + Parameters: ~ + {code} RPC error code defined in + `vim.lsp.protocol.ErrorCodes` + {message} (optional) arbitrary message to send to server + {data} (optional) arbitrary data to send to server + +send_notification({method}, {params}) *vim.lsp.rpc.send_notification()* + TODO: Documentation + + *vim.lsp.rpc.send_request()* +send_request({method}, {params}, {callback}) + TODO: Documentation + + *vim.lsp.rpc.send_response()* +send_response({request_id}, {err}, {result}) + TODO: Documentation + +server_request({method}, {params}) *vim.lsp.rpc.server_request()* + TODO: Documentation + +try_call({errkind}, {fn}, {...}) *vim.lsp.rpc.try_call()* + TODO: Documentation + + +============================================================================== +Lua module: vim.lsp.util *lsp-util* + + *vim.lsp.util.apply_text_document_edit()* +apply_text_document_edit({text_document_edit}) + TODO: Documentation + + *vim.lsp.util.apply_text_edits()* +apply_text_edits({text_edits}, {bufnr}) + TODO: Documentation + + *vim.lsp.util.apply_workspace_edit()* +apply_workspace_edit({workspace_edit}) + TODO: Documentation + +buf_clear_diagnostics({bufnr}) *vim.lsp.util.buf_clear_diagnostics()* + TODO: Documentation + + *vim.lsp.util.buf_diagnostics_save_positions()* +buf_diagnostics_save_positions({bufnr}, {diagnostics}) + TODO: Documentation + + *vim.lsp.util.buf_diagnostics_underline()* +buf_diagnostics_underline({bufnr}, {diagnostics}) + TODO: Documentation + + *vim.lsp.util.buf_diagnostics_virtual_text()* +buf_diagnostics_virtual_text({bufnr}, {diagnostics}) + TODO: Documentation + +character_offset({buf}, {row}, {col}) *vim.lsp.util.character_offset()* + TODO: Documentation + + *vim.lsp.util.close_preview_autocmd()* +close_preview_autocmd({events}, {winnr}) + TODO: Documentation + + *vim.lsp.util.convert_input_to_markdown_lines()* +convert_input_to_markdown_lines({input}, {contents}) + TODO: Documentation + + *vim.lsp.util.extract_completion_items()* +extract_completion_items({result}) + TODO: Documentation + + *vim.lsp.util.fancy_floating_markdown()* +fancy_floating_markdown({contents}, {opts}) + TODO: Documentation + +find_window_by_var({name}, {value}) *vim.lsp.util.find_window_by_var()* + TODO: Documentation + +focusable_float({unique_name}, {fn}) *vim.lsp.util.focusable_float()* + TODO: Documentation + + *vim.lsp.util.focusable_preview()* +focusable_preview({unique_name}, {fn}) + TODO: Documentation + + *vim.lsp.util.get_current_line_to_cursor()* +get_current_line_to_cursor() + TODO: Documentation + + *vim.lsp.util.get_severity_highlight_name()* +get_severity_highlight_name({severity}) + TODO: Documentation + + *vim.lsp.util.highlight_range()* +highlight_range({bufnr}, {ns}, {hiname}, {start}, {finish}) + TODO: Documentation + + *vim.lsp.util.highlight_region()* +highlight_region({ft}, {start}, {finish}) + TODO: Documentation + +jump_to_location({location}) *vim.lsp.util.jump_to_location()* + TODO: Documentation + +locations_to_items({locations}) *vim.lsp.util.locations_to_items()* + TODO: Documentation + + *vim.lsp.util.make_floating_popup_options()* +make_floating_popup_options({width}, {height}, {opts}) + TODO: Documentation + +make_position_params() *vim.lsp.util.make_position_params()* + TODO: Documentation + +npcall({fn}, {...}) *vim.lsp.util.npcall()* + TODO: Documentation + +ok_or_nil({status}, {...}) *vim.lsp.util.ok_or_nil()* + TODO: Documentation + + *vim.lsp.util.open_floating_peek_preview()* +open_floating_peek_preview({bufnr}, {start}, {finish}, {opts}) + TODO: Documentation + + *vim.lsp.util.open_floating_preview()* +open_floating_preview({contents}, {filetype}, {opts}) + TODO: Documentation + +set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()* + TODO: Documentation + +set_loclist({locations}) *vim.lsp.util.set_loclist()* + TODO: Documentation + +set_qflist({locations}) *vim.lsp.util.set_qflist()* + TODO: Documentation + +show_line_diagnostics() *vim.lsp.util.show_line_diagnostics()* + TODO: Documentation + +sort_by_key({fn}) *vim.lsp.util.sort_by_key()* + TODO: Documentation + +split_lines({value}) *vim.lsp.util.split_lines()* + TODO: Documentation + + *vim.lsp.util.text_document_completion_list_to_complete_items()* +text_document_completion_list_to_complete_items({result}) + TODO: Documentation + +trim_empty_lines({lines}) *vim.lsp.util.trim_empty_lines()* + TODO: Documentation + *vim.lsp.util.try_trim_markdown_code_blocks()* +try_trim_markdown_code_blocks({lines}) + TODO: Documentation +validate_lsp_position({pos}) *vim.lsp.util.validate_lsp_position()* + TODO: Documentation vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0ecf57f50c..a14c432f0c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -204,95 +204,155 @@ local function text_document_did_open_handler(bufnr, client) client.notify('textDocument/didOpen', params) end +--- LSP client object. +--- +--- - Methods: +--- +--- - request(method, params, [callback]) +--- Send a request to the server. If callback is not specified, it will use +--- {client.callbacks} to try to find a callback. If one is not found there, +--- then an error will occur. +--- This is a thin wrapper around {client.rpc.request} with some additional +--- checking. +--- Returns a boolean to indicate if the notification was successful. If it +--- is false, then it will always be false (the client has shutdown). +--- If it was successful, then it will return the request id as the second +--- result. You can use this with `notify("$/cancel", { id = request_id })` +--- to cancel the request. This helper is made automatically with +--- |vim.lsp.buf_request()| +--- Returns: status, [client_id] +--- +--- - notify(method, params) +--- This is just {client.rpc.notify}() +--- Returns a boolean to indicate if the notification was successful. If it +--- is false, then it will always be false (the client has shutdown). +--- Returns: status +--- +--- - cancel_request(id) +--- This is just {client.rpc.notify}("$/cancelRequest", { id = id }) +--- Returns the same as `notify()`. +--- +--- - stop([force]) +--- Stop a client, optionally with force. +--- By default, it will just ask the server to shutdown without force. +--- If you request to stop a client which has previously been requested to +--- shutdown, it will automatically escalate and force shutdown. +--- +--- - is_stopped() +--- Returns true if the client is fully stopped. +--- +--- - Members +--- - id (number): The id allocated to the client. +--- +--- - name (string): If a name is specified on creation, that will be +--- used. Otherwise it is just the client id. This is used for +--- logs and messages. +--- +--- - offset_encoding (string): The encoding used for communicating +--- with the server. You can modify this in the `on_init` method +--- before text is sent to the server. +--- +--- - callbacks (table): The callbacks used by the client as +--- described in |lsp-callbacks|. +--- +--- - config (table): copy of the table that was passed by the user +--- to |vim.lsp.start_client()|. +--- +--- - server_capabilities (table): Response from the server sent on +--- `initialize` describing the server's capabilities. +--- +--- - resolved_capabilities (table): Normalized table of +--- capabilities that we have detected based on the initialize +--- response from the server in `server_capabilities`. +function lsp.client() + error() +end --- Start a client and initialize it. --- Its arguments are passed via a configuration object. --- --- Mandatory parameters: --- --- root_dir: {string} specifying the directory where the LSP server will base --- as its rootUri on initialization. --- --- cmd: {string} or {list} which is the base command to execute for the LSP. A --- string will be run using |'shell'| and a list will be interpreted as a bare --- command with arguments passed. This is the same as |jobstart()|. --- --- Optional parameters: - --- cmd_cwd: {string} specifying the directory to launch the `cmd` process. This --- is not related to `root_dir`. By default, |getcwd()| is used. --- --- cmd_env: {table} specifying the environment flags to pass to the LSP on --- spawn. This can be specified using keys like a map or as a list with `k=v` --- pairs or both. Non-string values are coerced to a string. --- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`. --- --- capabilities: A {table} which will be used instead of --- `vim.lsp.protocol.make_client_capabilities()` which contains neovim's --- default capabilities and passed to the language server on initialization. --- You'll probably want to use make_client_capabilities() and modify the --- result. --- NOTE: --- To send an empty dictionary, you should use --- `{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as --- an array. --- --- callbacks: A {table} of whose keys are language server method names and the --- values are `function(err, method, params, client_id)`. --- This will be called for: --- - notifications from the server, where `err` will always be `nil` --- - requests initiated by the server. For these, you can respond by returning --- two values: `result, err`. The err must be in the format of an RPC error, --- which is `{ code, message, data? }`. You can use |vim.lsp.rpc_response_error()| --- to help with this. --- - as a callback for requests initiated by the client if the request doesn't --- explicitly specify a callback. --- --- init_options: A {table} of values to pass in the initialization request --- as `initializationOptions`. See the `initialize` in the LSP spec. --- --- name: A {string} used in log messages. Defaults to {client_id} --- --- offset_encoding: One of 'utf-8', 'utf-16', or 'utf-32' which is the --- encoding that the LSP server expects. By default, it is 'utf-16' as --- specified in the LSP specification. The client does not verify this --- is correct. --- --- on_error(code, ...): A function for handling errors thrown by client --- operation. {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. `vim.lsp.client_errors[code]` can be used to retrieve a --- human understandable string. --- --- before_init(initialize_params, config): A function which is called *before* --- the request `initialize` is completed. `initialize_params` contains --- the parameters we are sending to the server and `config` is the config that --- was passed to `start_client()` for convenience. You can use this to modify --- parameters before they are sent. --- --- on_init(client, initialize_result): A function which is called after the --- request `initialize` is completed. `initialize_result` contains --- `capabilities` and anything else the server may send. For example, `clangd` --- sends `result.offsetEncoding` if `capabilities.offsetEncoding` was sent to --- it. --- --- on_exit(code, signal, client_id): A function which is called after the --- client has exited. code is the exit code of the process, and signal is a --- number describing the signal used to terminate (if any). --- --- on_attach(client, bufnr): A function which is called after the client is --- attached to a buffer. --- --- trace: 'off' | 'messages' | 'verbose' | nil passed directly to the language --- server in the initialize request. Invalid/empty values will default to 'off' --- --- @returns client_id You can use |vim.lsp.get_client_by_id()| to get the --- actual client. --- --- NOTE: The client is only available *after* it has been initialized, which --- may happen after a small delay (or never if there is an error). --- For this reason, you may want to use `on_init` to do any actions once the --- client has been initialized. +--- Its arguments are passed via a configuration object. +--- +--- Mandatory parameters: +--- +--- root_dir: {string} specifying the directory where the LSP server will base +--- as its rootUri on initialization. +--- +--- cmd: {string} or {list} which is the base command to execute for the LSP. A +--- string will be run using 'shell' and a list will be interpreted as a bare +--- command with arguments passed. This is the same as |jobstart()|. +--- +--@param cmd_cwd: {string} specifying the directory to launch the `cmd` process. This +--- is not related to `root_dir`. By default, |getcwd()| is used. +--- +--@param cmd_env: {table} specifying the environment flags to pass to the LSP on +--- spawn. This can be specified using keys like a map or as a list with `k=v` +--- pairs or both. Non-string values are coerced to a string. +--- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`. +--- +--@param capabilities: Map overriding the default capabilities defined by +--- |vim.lsp.protocol.make_client_capabilities()|, passed to the language +--- server on initialization. Hint: use make_client_capabilities() and modify +--- its result. +--- - Note: To send an empty dictionary use +--- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an +--- array. +--- +--@param callbacks: Map of language server method names to +--- `function(err, method, params, client_id)` handler. +--- Invoked for: +--- - Notifications from the server, where `err` will always be `nil`. +--- - Requests initiated by the server. For these you can respond by returning +--- two values: `result, err` where err must be shaped like a RPC error, +--- i.e. `{ code, message, data? }`. Use |vim.lsp.rpc_response_error()| to +--- help with this. +--- - Default callback for client requests not explicitly specifying +--- a callback. +--- +--@param init_options values to pass in the initialization request +--- as `initializationOptions`. See `initialize` in the LSP spec. +--- +--@param name: string used in log messages. Defaults to {client_id} +--- +--@param offset_encoding: One of "utf-8", "utf-16", or "utf-32" which is the +--- encoding that the LSP server expects. By default, it is "utf-16" as +--- specified in the LSP specification. The client does not verify this +--- is correct. +--- +--@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. +--- +--@param before_init Callback with parameters (initialize_params, config) invoked +--- before the LSP "initialize" phase, where `params` contains the +--- parameters being sent to the server and `config` is the config +--- that was passed to `start_client()`. You can use this to modify +--- parameters before they are sent. +--- +--@param on_init Callback (client, initialize_result) invoked after LSP +--- "initialize", where `result` is a table of `capabilities` and +--- anything else the server may send. For example, clangd sends +--- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was +--- sent to it. You can only modify the `client.offset_encoding` here before +--- any notifications are sent. +--- +--@param on_exit Callback (code, signal, client_id) invoked on client +--- exit. +--- - code: exit code of the process +--- - signal: number describing the signal used to terminate (if any) +--- - client_id: client handle +--- +--@param on_attach Callback (client, bufnr) invoked when client +--- attaches to a buffer. +--- +--@param trace: "off" | "messages" | "verbose" | nil passed directly to the language +--- server in the initialize request. Invalid/empty values will default to "off" +--- +--@returns Client id. |vim.lsp.get_client_by_id()| Note: client is only +--- available after it has been initialized, which may happen after a small +--- delay (or never if there is an error). Use `on_init` to do any actions once +--- the client has been initialized. function lsp.start_client(config) local cleaned_config = validate_client_config(config) local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding @@ -402,8 +462,8 @@ function lsp.start_client(config) initializationOptions = config.init_options; -- The capabilities provided by the client (editor or tool) capabilities = config.capabilities or protocol.make_client_capabilities(); - -- The initial trace setting. If omitted trace is disabled ('off'). - -- trace = 'off' | 'messages' | 'verbose'; + -- The initial trace setting. If omitted trace is disabled ("off"). + -- trace = "off" | "messages" | "verbose"; trace = valid_traces[config.trace] or 'off'; -- The workspace folders configured in the client when the server starts. -- This property is only available if the client supports workspace folders. @@ -634,10 +694,13 @@ function lsp._text_document_did_save_handler(bufnr) end) end --- Implements the textDocument/did* notifications required to track a buffer --- for any language server. --- @param bufnr [number] buffer handle or 0 for current --- @param client_id [number] the client id +--- Implements the `textDocument/did…` notifications required to track a buffer +--- for any language server. +--- +--- Without calling this, the server won't be notified of changes to a buffer. +--- +--- @param bufnr (number) Buffer handle, or 0 for current +--- @param client_id (number) Client id function lsp.buf_attach_client(bufnr, client_id) validate { bufnr = {bufnr, 'n', true}; @@ -683,28 +746,33 @@ function lsp.buf_attach_client(bufnr, client_id) return true end --- Check if a buffer is attached for a particular client. --- @param bufnr [number] buffer handle or 0 for current --- @param client_id [number] the client id +--- Checks if a buffer is attached for a particular client. +--- +---@param bufnr (number) Buffer handle, or 0 for current +---@param client_id (number) the client id function lsp.buf_is_attached(bufnr, client_id) return (all_buffer_active_clients[bufnr] or {})[client_id] == true end --- Look up an active client by its id, returns nil if it is not yet initialized --- or is not a valid id. --- @param client_id number the client id. +--- Gets an active client by id, or nil if the id is invalid or the +--- client is not yet initialized. +--- +--@param client_id client id number +--- +--@return |vim.lsp.client| object, or nil function lsp.get_client_by_id(client_id) return active_clients[client_id] end --- Stop a client by its id, optionally with force. --- You can also use the `stop()` function on a client if you already have --- access to it. --- By default, it will just ask the server to shutdown without force. --- If you request to stop a client which has previously been requested to shutdown, --- it will automatically force shutdown. --- @param client_id number the client id. --- @param force boolean (optional) whether to use force or request shutdown +--- Stops a client. +--- +--- You can also use the `stop()` function on a |vim.lsp.client| object. +--- +--- By default asks the server to shutdown, unless stop was requested +--- already for this client, then force-shutdown is attempted. +--- +--@param client_id client id number +--@param force boolean (optional) shutdown forcefully function lsp.stop_client(client_id, force) local client client = active_clients[client_id] @@ -718,18 +786,16 @@ function lsp.stop_client(client_id, force) end end --- Returns a list of all the active clients. +--- Gets all active clients. +--- +--@return Table of |vim.lsp.client| objects function lsp.get_active_clients() return vim.tbl_values(active_clients) end --- Stop all the clients, optionally with force. --- You can also use the `stop()` function on a client if you already have --- access to it. --- By default, it will just ask the server to shutdown without force. --- If you request to stop a client which has previously been requested to shutdown, --- it will automatically force shutdown. --- @param force boolean (optional) whether to use force or request shutdown +--- Stops all clients. +--- +--@param force boolean (optional) shutdown forcefully function lsp.stop_all_clients(force) for _, client in pairs(uninitialized_clients) do client.stop(true) @@ -761,17 +827,21 @@ end nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") ---- ---- Buffer level client functions. ---- ---- Send a request to a server and return the response --- @param bufnr [number] Buffer handle or 0 for current. --- @param method [string] Request method name --- @param params [table|nil] Parameters to send to the server --- @param callback [function|nil] Request callback (or uses the client's callbacks) +--- Sends an async request for all active clients attached to the +--- buffer. +--- +--@param bufnr (number) Buffer handle, or 0 for current. +--@param method (string) LSP method name +--@param params (optional, table) Parameters to send to the server +--@param callback (optional, functionnil) Handler +-- `function(err, method, params, client_id)` for this request. Defaults +-- to the client callback in `client.callbacks`. See |lsp-callbacks|. -- --- @returns: client_request_ids, cancel_all_requests +--@returns 2-tuple: +--- - Map of client-id:request-id pairs for all successful requests. +--- - Function which can be used to cancel all the requests. You could instead +--- iterate all clients and call their `cancel_request()` methods. function lsp.buf_request(bufnr, method, params, callback) validate { bufnr = { bufnr, 'n', true }; @@ -789,31 +859,39 @@ function lsp.buf_request(bufnr, method, params, callback) end end) - local function cancel_all_requests() + local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do local client = active_clients[client_id] client.cancel_request(request_id) end end - return client_request_ids, cancel_all_requests + return client_request_ids, _cancel_all_requests end ---- Send a request to a server and wait for the response. --- @param bufnr [number] Buffer handle or 0 for current. --- @param method [string] Request method name --- @param params [string] Parameters to send to the server --- @param timeout_ms [number|100] Maximum ms to wait for a result --- --- @returns: The table of {[client_id] = request_result} +--- Sends a request to a server and waits for the response. +--- +--- Calls |vim.lsp.buf_request()| but blocks Nvim while awaiting the result. +--- Parameters are the same as |vim.lsp.buf_request()| but the return result is +--- different. Wait maximum of {timeout_ms} (default 100) ms. +--- +--@param bufnr (number) Buffer handle, or 0 for current. +--@param method (string) LSP method name +--@param params (optional, table) Parameters to send to the server +--@param timeout_ms (optional, number, default=100) Maximum time in +--- milliseconds to wait for a result. +--- +--@returns Map of client_id:request_result. On timeout, cancel or error, +--- returns `(nil, err)` where `err` is a string describing the failure +--- reason. function lsp.buf_request_sync(bufnr, method, params, timeout_ms) local request_results = {} local result_count = 0 - local function callback(err, _method, result, client_id) + local function _callback(err, _method, result, client_id) request_results[client_id] = { error = err, result = result } result_count = result_count + 1 end - local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, callback) + local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, _callback) local expected_result_count = 0 for _ in pairs(client_request_ids) do expected_result_count = expected_result_count + 1 @@ -828,12 +906,13 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) return request_results end ---- Send a notification to a server --- @param bufnr [number] (optional): The number of the buffer --- @param method [string]: Name of the request method --- @param params [string]: Arguments to send to the server --- --- @returns nil +--- Sends a notification to all servers attached to the buffer. +--- +--@param bufnr (optional, number) Buffer handle, or 0 for current +--@param method (string) LSP method name +--@param params (string) Parameters to send to the server +--- +--@returns nil function lsp.buf_notify(bufnr, method, params) validate { bufnr = { bufnr, 'n', true }; @@ -888,12 +967,10 @@ function lsp.client_is_stopped(client_id) return active_clients[client_id] == nil end +--- Gets a map of client_id:client pairs for the given buffer, where each value +--- is a |vim.lsp.client| object. --- ---- Miscellaneous utilities. ---- - --- Retrieve a map from client_id to client of all active buffer clients. --- @param bufnr [number] (optional): buffer handle or 0 for current +--@param bufnr (optional, number): Buffer handle, or 0 for current function lsp.buf_get_clients(bufnr) bufnr = resolve_bufnr(bufnr) local result = {} @@ -903,8 +980,9 @@ function lsp.buf_get_clients(bufnr) return result end --- Print some debug information about the current buffer clients. --- The output of this function should not be relied upon and may change. +--- Prints debug info about the current buffer clients. +--- +--- Result of this function cannot be relied upon and may change. function lsp.buf_print_debug_info(bufnr) print(vim.inspect(lsp.buf_get_clients(bufnr))) end @@ -919,14 +997,20 @@ 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 --- Set the log level for lsp logging. --- Levels by name: 'trace', 'debug', 'info', 'warn', 'error' --- Level numbers begin with 'trace' at 0 --- @param level [number|string] the case insensitive level name or number @see |vim.lsp.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 +--- +--- Use `lsp.log_levels` for reverse lookup. +--- +--@see |vim.lsp.log_levels| +--- +--@param level [number|string] the case insensitive level name or number function lsp.set_log_level(level) if type(level) == 'string' or type(level) == 'number' then log.set_level(level) @@ -935,7 +1019,7 @@ function lsp.set_log_level(level) end end --- Return the path of the logfile used by the LSP client. +--- Gets the path of the logfile used by the LSP client. function lsp.get_log_path() return log.get_filename() end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index ead90cc75a..f64b0b50e7 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -603,6 +603,8 @@ export interface WorkspaceClientCapabilities { } --]=] +--- Gets a new ClientCapabilities object describing the LSP client +--- capabilities. function protocol.make_client_capabilities() return { textDocument = { @@ -821,6 +823,8 @@ interface ServerCapabilities { experimental?: any; } --]] + +--- Creates a normalized object describing LSP server capabilities. function protocol.resolve_capabilities(server_capabilities) local general_properties = {} local text_document_sync_properties diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 72a0bf8d6f..e13b05610b 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -166,9 +166,14 @@ local function format_rpc_error(err) return table.concat(message_parts, ' ') end +--- Creates an RPC response object/table. +--- +--@param code RPC error code defined in `vim.lsp.protocol.ErrorCodes` +--@param message (optional) arbitrary message to send to server +--@param data (optional) arbitrary data to send to server local function rpc_response_error(code, message, data) -- TODO should this error or just pick a sane error (like InternalError)? - local code_name = assert(protocol.ErrorCodes[code], 'Invalid rpc error code') + local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code') return setmetatable({ code = code; message = message or code_name; -- cgit From 8b841196504f96a5c14126667fb4f4a0769914dd Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 31 Dec 2019 07:51:54 -0800 Subject: LSP: eliminate lsp.stop_all_clients() Reduce API surface. We don't need so many variations of functions. Too many functions means verbose, largely redundant documentation, tests, and cognitive burden. --- runtime/doc/lsp.txt | 76 +++++++++++++++------------------ runtime/lua/vim/lsp.lua | 109 ++++++++++++++++++++++-------------------------- 2 files changed, 84 insertions(+), 101 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 4d04e50998..37ede277eb 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -510,32 +510,27 @@ set_log_level({level}) *vim.lsp.set_log_level()* |vim.lsp.log_levels| start_client({config}) *vim.lsp.start_client()* - Start a client and initialize it. Its arguments are passed via - a configuration object. + Starts and initializes a client with the given configuration. - Mandatory parameters: - - root_dir: {string} specifying the directory where the LSP - server will base as its rootUri on initialization. - - cmd: {string} or {list} which is the base command to execute - for the LSP. A string will be run using 'shell' and a list - will be interpreted as a bare command with arguments passed. - This is the same as |jobstart()|. + Parameters `cmd` and `root_dir` are required. Parameters: ~ - {cmd_cwd} {string} specifying the directory to - launch the `cmd` process. This is not - related to `root_dir` . By default, - |getcwd()| is used. - {cmd_env} {table} specifying the environment - flags to pass to the LSP on spawn. This - can be specified using keys like a map - or as a list with `k=v` pairs or both. - Non-string values are coerced to a - string. For example: `{ - "PRODUCTION=true"; "TEST=123"; PORT = - 8080; HOST = "0.0.0.0"; }` . + {root_dir} (required, string) Directory where the + LSP server will base its rootUri on + initialization. + {cmd} (required, string or list treated like + |jobstart()|) Base command that + initiates the LSP client. + {cmd_cwd} (string, default=|getcwd()|) Directory + to launch the `cmd` process. Not + related to `root_dir` . + {cmd_env} (table) Environment flags to pass to + the LSP on spawn. Can be specified + using keys like a map or as a list with `k=v` pairs or both. Non-string values are + coerced to string. Example: > + + { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } +< {capabilities} Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, @@ -560,20 +555,18 @@ start_client({config}) *vim.lsp.start_client()* help with this. • Default callback for client requests not explicitly specifying a callback. - {init_options} values to pass in the initialization + {init_options} Values to pass in the initialization request as `initializationOptions` . See `initialize` in the LSP spec. - {name} string used in log messages. Defaults - to {client_id} - {offset_encoding} One of "utf-8", "utf-16", or "utf-32" - which is the encoding that the LSP - server expects. By default, it is - "utf-16" as specified in the LSP - specification. The client does not - verify this is correct. + {name} (string, default=client-id) Name in log + messages. + {offset_encoding} (default="utf-16") One of "utf-8", + "utf-16", or "utf-32" which is the + encoding that the LSP server expects. + Client does not verify this is correct. {on_error} Callback with parameters (code, ...), invoked when the client operation - throws an error. {code} is a number + 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 @@ -622,24 +615,23 @@ start_client({config}) *vim.lsp.start_client()* stop({force}) *vim.lsp.stop()* TODO: Documentation -stop_all_clients({force}) *vim.lsp.stop_all_clients()* - Stops all clients. - - Parameters: ~ - {force} boolean (optional) shutdown forcefully - stop_client({client_id}, {force}) *vim.lsp.stop_client()* - Stops a client. + Stops a client(s). You can also use the `stop()` function on a |vim.lsp.client| - object. + object. To stop all clients: +> + + vim.lsp.stop_client(lsp.get_active_clients()) +< By default asks the server to shutdown, unless stop was requested already for this client, then force-shutdown is attempted. Parameters: ~ - {client_id} client id number + {client_id} client id or |vim.lsp.client| object, or list + thereof {force} boolean (optional) shutdown forcefully *vim.lsp.text_document_did_open_handler()* diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a14c432f0c..670de7a996 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -268,27 +268,28 @@ function lsp.client() error() end ---- Start a client and initialize it. ---- Its arguments are passed via a configuration object. +--- Starts and initializes a client with the given configuration. --- ---- Mandatory parameters: +--- Parameters `cmd` and `root_dir` are required. --- ---- root_dir: {string} specifying the directory where the LSP server will base ---- as its rootUri on initialization. +--@param root_dir: (required, string) Directory where the LSP server will base +--- its rootUri on initialization. --- ---- cmd: {string} or {list} which is the base command to execute for the LSP. A ---- string will be run using 'shell' and a list will be interpreted as a bare ---- command with arguments passed. This is the same as |jobstart()|. +--@param cmd: (required, string or list treated like |jobstart()|) Base command +--- that initiates the LSP client. --- ---@param cmd_cwd: {string} specifying the directory to launch the `cmd` process. This ---- is not related to `root_dir`. By default, |getcwd()| is used. +--@param cmd_cwd: (string, default=|getcwd()|) Directory to launch +--- the `cmd` process. Not related to `root_dir`. --- ---@param cmd_env: {table} specifying the environment flags to pass to the LSP on ---- spawn. This can be specified using keys like a map or as a list with `k=v` ---- pairs or both. Non-string values are coerced to a string. ---- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`. +--@param cmd_env: (table) Environment flags to pass to the LSP on +--- spawn. Can be specified using keys like a map or as a list with `k=v` +--- pairs or both. Non-string values are coerced to string. +--- Example: +---
+--- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
+--- 
--- ---@param capabilities: Map overriding the default capabilities defined by +--@param capabilities Map overriding the default capabilities defined by --- |vim.lsp.protocol.make_client_capabilities()|, passed to the language --- server on initialization. Hint: use make_client_capabilities() and modify --- its result. @@ -296,9 +297,8 @@ end --- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an --- array. --- ---@param callbacks: Map of language server method names to ---- `function(err, method, params, client_id)` handler. ---- Invoked for: +--@param callbacks Map of language server method names to +--- `function(err, method, params, client_id)` handler. Invoked for: --- - Notifications from the server, where `err` will always be `nil`. --- - Requests initiated by the server. For these you can respond by returning --- two values: `result, err` where err must be shaped like a RPC error, @@ -307,32 +307,30 @@ end --- - Default callback for client requests not explicitly specifying --- a callback. --- ---@param init_options values to pass in the initialization request +--@param init_options Values to pass in the initialization request --- as `initializationOptions`. See `initialize` in the LSP spec. --- ---@param name: string used in log messages. Defaults to {client_id} +--@param name (string, default=client-id) Name in log messages. --- ---@param offset_encoding: One of "utf-8", "utf-16", or "utf-32" which is the ---- encoding that the LSP server expects. By default, it is "utf-16" as ---- specified in the LSP specification. The client does not verify this ---- is correct. +--@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", +--- or "utf-32" which is the encoding that the LSP server expects. Client does +--- not verify this is correct. --- --@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. +--- 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. --- ---@param before_init Callback with parameters (initialize_params, config) invoked ---- before the LSP "initialize" phase, where `params` contains the ---- parameters being sent to the server and `config` is the config ---- that was passed to `start_client()`. You can use this to modify ---- parameters before they are sent. +--@param before_init Callback with parameters (initialize_params, config) +--- invoked before the LSP "initialize" phase, where `params` contains the +--- parameters being sent to the server and `config` is the config that was +--- passed to `start_client()`. You can use this to modify parameters before +--- they are sent. --- --@param on_init Callback (client, initialize_result) invoked after LSP ---- "initialize", where `result` is a table of `capabilities` and ---- anything else the server may send. For example, clangd sends +--- "initialize", where `result` is a table of `capabilities` and anything else +--- the server may send. For example, clangd sends --- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was --- sent to it. You can only modify the `client.offset_encoding` here before --- any notifications are sent. @@ -764,25 +762,30 @@ function lsp.get_client_by_id(client_id) return active_clients[client_id] end ---- Stops a client. +--- Stops a client(s). --- --- You can also use the `stop()` function on a |vim.lsp.client| object. +--- To stop all clients: +--- +---
+--- vim.lsp.stop_client(lsp.get_active_clients())
+--- 
--- --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. --- ---@param client_id client id number +--@param client_id client id or |vim.lsp.client| object, or list thereof --@param force boolean (optional) shutdown forcefully function lsp.stop_client(client_id, force) - local client - client = active_clients[client_id] - if client then - client.stop(force) - return - end - client = uninitialized_clients[client_id] - if client then - client.stop(true) + local ids = type(client_id) == 'table' and client_id or {client_id} + for _, id in ipairs(ids) do + if type(id) == 'table' and id.stop ~= nil then + id.stop(force) + elseif active_clients[id] then + active_clients[id].stop(force) + elseif uninitialized_clients[id] then + uninitialized_clients[id].stop(true) + end end end @@ -793,18 +796,6 @@ function lsp.get_active_clients() return vim.tbl_values(active_clients) end ---- Stops all clients. ---- ---@param force boolean (optional) shutdown forcefully -function lsp.stop_all_clients(force) - for _, client in pairs(uninitialized_clients) do - client.stop(true) - end - for _, client in pairs(active_clients) do - client.stop(force) - end -end - function lsp._vim_exit_handler() log.info("exit_handler", active_clients) for _, client in pairs(uninitialized_clients) do -- cgit From ac6ebfcc1db8daf30533c42d5c4246bb95ec3d85 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 31 Dec 2019 08:03:06 -0800 Subject: LSP: eliminate lsp.print_debug_info…() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce API surface. We should not add functions unless they are really needed. Users should be nudged to use vim.inspect() directly. --- runtime/doc/lsp.txt | 6 ------ runtime/lua/vim/lsp.lua | 13 ------------- 2 files changed, 19 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 37ede277eb..70a3110042 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -320,9 +320,6 @@ buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()* Return: ~ nil -buf_print_debug_info({bufnr}) *vim.lsp.buf_print_debug_info()* - TODO: Documentation - *vim.lsp.buf_request()* buf_request({bufnr}, {method}, {params}, {callback}) Sends an async request for all active clients attached to the @@ -479,9 +476,6 @@ once({fn}) *vim.lsp.once()* optional_validator({fn}) *vim.lsp.optional_validator()* TODO: Documentation -print_debug_info() *vim.lsp.print_debug_info()* - TODO: Documentation - request({method}, {params}, {callback}) *vim.lsp.request()* TODO: Documentation diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 670de7a996..c193fad6a4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -971,19 +971,6 @@ function lsp.buf_get_clients(bufnr) return result end ---- Prints debug info about the current buffer clients. ---- ---- Result of this function cannot be relied upon and may change. -function lsp.buf_print_debug_info(bufnr) - print(vim.inspect(lsp.buf_get_clients(bufnr))) -end - --- Print some debug information about all LSP related things. --- The output of this function should not be relied upon and may change. -function lsp.print_debug_info() - print(vim.inspect({ clients = active_clients })) -end - -- Log level dictionary with reverse lookup as well. -- -- Can be used to lookup the number from the name or the -- cgit From 0da7774cf0e503efc93c15033a94ff756d5252e1 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 31 Dec 2019 22:29:40 -0500 Subject: doc: powershell is 'pwsh' on non-Windows OS --- runtime/doc/options.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4b832a8606..9c875a3a34 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5202,8 +5202,9 @@ A jump table for the options with a short description can be found at |Q_op|. Note that such processing is done after |:set| did its own round of unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* - To use powershell (on Windows): > - set shell=powershell shellquote= shellpipe=\| shellxquote= + To use powershell: > + let &shell = has('win32') ? 'powershell' : 'pwsh' + set shellquote= shellpipe=\| shellxquote= set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command set shellredir=\|\ Out-File\ -Encoding\ UTF8 -- cgit From abaabd1d03fd723630f6addeadee9928faa4cdde Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 1 Jan 2020 13:03:06 -0500 Subject: vim-patch:8.2.0074: Python 3 unicode test someitmes fails Problem: Python 3 unicode test someitmes fails. Solution: Make 'termencoding' empty. Correct number of error message. https://github.com/vim/vim/commit/4b7cdca23035eacf6cd0e30b90546cf32f7efe9e --- runtime/doc/message.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index 98e45ac6da..43b1eb5e0c 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -69,7 +69,7 @@ See `:messages` above. LIST OF MESSAGES *E222* *E228* *E232* *E256* *E293* *E298* *E304* *E317* *E318* *E356* *E438* *E439* *E440* *E316* *E320* *E322* - *E323* *E341* *E473* *E570* *E685* *E950* > + *E323* *E341* *E473* *E570* *E685* *E292* > Add to read buffer makemap: Illegal mode Cannot create BalloonEval with both message and callback -- cgit From ea4127e9a7a624484f51c21e17f37c766da15da0 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 27 Nov 2019 20:45:41 +0100 Subject: lua: metatable for empty dict value --- runtime/doc/lua.txt | 10 ++++++++++ runtime/lua/vim/inspect.lua | 5 +++++ runtime/lua/vim/shared.lua | 15 +++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 9601537c8d..d1f244c76f 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -717,6 +717,16 @@ vim.NIL *vim.NIL* is equivalent to a missing value: `{"foo", nil}` is the same as `{"foo"}` +vim.empty_dict() *vim.empty_dict()* + Creates a special table which will be converted to an empty + dictionary when converting lua values to vimL or API types. The + table is empty, and this property is marked using a metatable. An + empty table `{}` without this metatable will default to convert to + an array/list. + + Note: if numeric keys are added to the table, the metatable will be + ignored and the dict converted to a list/array anyway. + vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. If {channel} is 0, the event is broadcast to all channels. diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index 0f3b908dc1..0448ea487f 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -244,6 +244,11 @@ function Inspector:putTable(t) local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t) local mt = getmetatable(t) + if (vim and sequenceLength == 0 and nonSequentialKeysLength == 0 + and mt == vim._empty_dict_mt) then + self:puts(tostring(t)) + return + end self:puts('{') self:down(function() diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index b5b04d7757..6df9bf1c2f 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -275,9 +275,15 @@ function vim.tbl_flatten(t) end -- Determine whether a Lua table can be treated as an array. +-- +-- An empty table `{}` will default to being treated as an array. +-- Use `vim.emtpy_dict()` to create a table treated as an +-- empty dict. Empty tables returned by `rpcrequest()` and +-- `vim.fn` functions can be checked using this function +-- whether they represent empty API arrays and vimL lists. --- --@params Table ---@returns true: A non-empty array, false: A non-empty table, nil: An empty table +--@returns true: An array-like table, false: A dict-like or mixed table function vim.tbl_islist(t) if type(t) ~= 'table' then return false @@ -296,7 +302,12 @@ function vim.tbl_islist(t) if count > 0 then return true else - return nil + -- TODO(bfredl): in the future, we will always be inside nvim + -- then this check can be deleted. + if vim._empty_dict_mt == nil then + return nil + end + return getmetatable(t) ~= vim._empty_dict_mt end end -- cgit From 391706450445fc040d3b3766ace7d91ec13b1936 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Thu, 2 Jan 2020 00:38:49 -0800 Subject: doc: mention `*_host_prog` ordering sensitivity #11639 Any uses of `has("python3")` will cause the `g:loaded_python3_provider` variable to be set if the system path does not have a Python with Neovim support. Subsequent assignments to `g:python3_host_program` will therefore not cause the provider to be activated. --- runtime/doc/provider.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 0083bb63a4..46ff075cef 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -58,12 +58,14 @@ If you run into problems, uninstall _both_ then install "pynvim" again: > PYTHON PROVIDER CONFIGURATION ~ *g:python_host_prog* Command to start Python 2 (executable, not directory). Setting this makes -startup faster. Useful for working with virtualenvs. > +startup faster. Useful for working with virtualenvs. Must be set before any +check for has("python2"). > let g:python_host_prog = '/path/to/python' < *g:python3_host_prog* Command to start Python 3 (executable, not directory). Setting this makes -startup faster. Useful for working with virtualenvs. > +startup faster. Useful for working with virtualenvs. Must be set before any +check for has("python3"). > let g:python3_host_prog = '/path/to/python3' < *g:loaded_python_provider* -- cgit From 67d7906652e8511f28882fb64b5b3317da93e29a Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 2 Jan 2020 03:41:36 -0500 Subject: clipboard: close stdout when copying via xclip #11617 test_registers.vim can fail even if a clipboard manager is running. If a clipboard manager is not running, this test always fails with xclip. Use xsel as a workaround. https://github.com/astrand/xclip/issues/20 suggests closing stdout when sending input via stdin. Environment - Ubuntu Xenial - Vim 7.4 (any app with broken clipboard code will do) - Neovim nightly Steps to reproduce: 0. Start the clipboard manager. 1. Open a file in Vim on Linux. Vim should have +clipboard enabled. 'set clipboard=' 2. Yank some text to the clipboard register. 3. Quit Vim. 4. Run 'cd /path/to/neovim/repo/' 5. Run 'make oldtest'. Do not run any individual tests. They likely pass with or without this fix. Before fix: test_registers.vim can fail. After fix: test_registers.vim always passes. Close https://github.com/neovim/neovim/issues/7958 https://wiki.ubuntu.com/ClipboardPersistence#The_state_of_things --- runtime/autoload/provider/clipboard.vim | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index e33dc31f6d..e54d6ad95c 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -172,6 +172,11 @@ function! s:clipboard.set(lines, regtype, reg) abort if jobid > 0 call jobsend(jobid, a:lines) call jobclose(jobid, 'stdin') + " xclip does not close stdout,stderr when receiving input via stdin + if argv[0] ==# 'xclip' + call jobclose(jobid, 'stdout') + call jobclose(jobid, 'stderr') + endif let selection.owner = jobid let ret = 1 else -- cgit From cbc8d72fde4b19176028490934ff7a447afe523c Mon Sep 17 00:00:00 2001 From: butwerenotthereyet <58348703+butwerenotthereyet@users.noreply.github.com> Date: Thu, 2 Jan 2020 06:06:11 -0800 Subject: tabpage: track last-used tabpage #11626 In a multi-window scenario, it is possible to return focus to the last accessed window via n_CTRL-W_p. However, in the case of a multi-tab scenario, there was previously no way to return focus to the last accessed *tab*. Here, that ability is added via n_g. Additionally, the index of the previous tab is exposed via tabpagenr('#'), mirroring the existing functionality of winnr('#'). --- runtime/doc/eval.txt | 8 ++++++-- runtime/doc/index.txt | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 960a42ecbf..67277d19dd 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -8591,8 +8591,12 @@ tabpagebuflist([{arg}]) *tabpagebuflist()* tabpagenr([{arg}]) *tabpagenr()* The result is a Number, which is the number of the current tab page. The first tab page has number 1. - When the optional argument is "$", the number of the last tab - page is returned (the tab page count). + The optional argument {arg} supports the following values: + $ the number of the last tab page (the tab page + count). + # the number of the last accessed tab page (where + |g| goes to). If there is no previous + tab page, 0 is returned. The number can be used with the |:tab| command. diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index ed9853a8da..3b057575a8 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -222,6 +222,8 @@ tag char note action in Normal mode ~ |CTRL-]| CTRL-] :ta to ident under cursor |CTRL-^| CTRL-^ edit Nth alternate file (equivalent to ":e #N") +|CTRL-| CTRL- same as `g` : go to last accessed tab + page CTRL-_ not used || 1 same as "l" @@ -570,6 +572,8 @@ tag command action in Normal mode ~ following the file name. |CTRL-W_gt| CTRL-W g t same as `gt`: go to next tab page |CTRL-W_gT| CTRL-W g T same as `gT`: go to previous tab page +|CTRL-W_g| CTRL-W g same as `g` : go to last accessed tab + page |CTRL-W_h| CTRL-W h go to Nth left window (stop at first window) |CTRL-W_i| CTRL-W i split window and jump to declaration of identifier under the cursor @@ -788,6 +792,7 @@ tag char note action in Normal mode ~ |g| g same as g same as |g| g same as +|g| g go to last accessed tab page |g| g 1 same as "gk" ============================================================================== -- cgit From 6e3793bf116f46930b9fcc4eb248f4589029d5ce Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 2 Jan 2020 17:37:39 +0100 Subject: clipboard: do not close stderr together with stdout (fixup #11617) stderr is needed to get error messages in case of failure, and job handler expects it to be open. --- runtime/autoload/provider/clipboard.vim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index e54d6ad95c..c86f7d0c2f 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -172,10 +172,9 @@ function! s:clipboard.set(lines, regtype, reg) abort if jobid > 0 call jobsend(jobid, a:lines) call jobclose(jobid, 'stdin') - " xclip does not close stdout,stderr when receiving input via stdin + " xclip does not close stdout when receiving input via stdin if argv[0] ==# 'xclip' call jobclose(jobid, 'stdout') - call jobclose(jobid, 'stderr') endif let selection.owner = jobid let ret = 1 -- cgit From c241395b3dcc86cd1dd6358105d71cab524d6e50 Mon Sep 17 00:00:00 2001 From: Ville Hakulinen Date: Fri, 3 Jan 2020 14:39:25 +0200 Subject: LSP: place hover window by vertical space #11657 Make the hover window position itself vertically wherever is the most space available. --- runtime/lua/vim/lsp/util.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4a781359d4..0de5fdad46 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -264,11 +264,16 @@ function M.make_floating_popup_options(width, height, opts) local anchor = '' local row, col - if vim.fn.winline() <= height then + local lines_above = vim.fn.winline() - 1 + local lines_below = vim.fn.winheight(0) - lines_above + + if lines_above < lines_below then anchor = anchor..'N' + height = math.min(lines_below, height) row = 1 else anchor = anchor..'S' + height = math.min(lines_above, height) row = 0 end -- cgit From 3c764aabb5d4be71a5ea0d50bf8dae0a7285677f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 5 Jan 2020 22:51:49 -0500 Subject: vim-patch:8.1.1308: the Normal highlight is not defined when compiled with GUI Problem: The Normal highlight is not defined when compiled with GUI. Solution: Always define Normal. (Christian Brabandt, closes vim/vim#4072) https://github.com/vim/vim/commit/f90b6e03a983b62b66564fc449e32724d6456769 --- runtime/doc/syntax.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 5424ad00ec..30ccb699cd 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4920,6 +4920,8 @@ Conceal placeholder characters substituted for concealed text (see 'conceallevel') *hl-Cursor* Cursor character under the cursor +lCursor the character under the cursor when |language-mapping| + is used (see 'guicursor') *hl-CursorIM* CursorIM like Cursor, but used when in IME mode |CursorIM| *hl-CursorColumn* -- cgit From 4a7d84ae603f75f974ab9a9d2f4d1b4a06da79bf Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 7 Jan 2020 05:57:36 +0000 Subject: man.vim: workaround for 'cscopetag' #11679 The old `:Man` implementation would take either the word under the cursor, or the argument passed in, and load that as a man page. Since we now use 'tagfunc' and look for all relevant man-pages, if your system has several (i.e. same name, different sections), we return several, giving the user an option. This works for most tag commands except `:tjump`, which will fail if there's multiple tags to choose from. This just happens to be what the cscope code uses (it actually attempts to prompt the user, but this fails). --- runtime/autoload/man.vim | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index e4c0080ae9..5feab0ce70 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -398,6 +398,11 @@ function! man#goto_tag(pattern, flags, info) abort " sort by relevance - exact matches first, then the previous order call sort(l:structured, { a, b -> a.name ==? l:name ? -1 : b.name ==? l:name ? 1 : 0 }) + if &cscopetag + " return only a single entry so we work well with :cstag (#11675) + let l:structured = l:structured[:0] + endif + return map(l:structured, { \ _, entry -> { \ 'name': entry.name, -- cgit From e616ec439436816bb39b1d5e720593e6c66580c9 Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Fri, 3 Jan 2020 23:35:09 +0100 Subject: LSP: differentiate diagnostic underline by severity --- runtime/lua/vim/lsp/util.lua | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 0de5fdad46..338aedb4e0 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -547,7 +547,10 @@ do local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") local underline_highlight_name = "LspDiagnosticsUnderline" - api.nvim_command(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) + vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) + for kind, _ in pairs(protocol.DiagnosticSeverity) do + vim.cmd(string.format("highlight default link %s%s %s", underline_highlight_name, kind, underline_highlight_name)) + end local severity_highlights = {} @@ -657,13 +660,21 @@ do function M.buf_diagnostics_underline(bufnr, diagnostics) for _, diagnostic in ipairs(diagnostics) do - local start = diagnostic.range.start + local start = diagnostic.range["start"] local finish = diagnostic.range["end"] + local hlmap = { + [protocol.DiagnosticSeverity.Error]='Error', + [protocol.DiagnosticSeverity.Warning]='Warning', + [protocol.DiagnosticSeverity.Information]='Information', + [protocol.DiagnosticSeverity.Hint]='Hint', + } + -- TODO care about encoding here since this is in byte index? - highlight_range(bufnr, diagnostic_ns, underline_highlight_name, - {start.line, start.character}, - {finish.line, finish.character} + highlight_range(bufnr, diagnostic_ns, + underline_highlight_name..hlmap[diagnostic.severity], + {start.line, start.character}, + {finish.line, finish.character} ) end end -- cgit From 0a1c6d9a374a0c984515d0af43b1c71af6c55eb2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 8 Jan 2020 09:46:25 -0800 Subject: LSP: highlight groups test, doc --- runtime/doc/lsp.txt | 35 +++++++++++++++++++++++++---------- runtime/lua/vim/lsp/util.lua | 4 +++- 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 70a3110042..c173ecead3 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -57,24 +57,39 @@ use of |v:lua| to call Lua from Vimscript): > autocmd Filetype python setlocal omnifunc=v:lua.vim.lsp.omnifunc -FAQ ~ - -> How to force-reload LSP? +================================================================================ +FAQ *lsp-faq* -Stop all clients, then reload the buffer. > +- Q: How to force-reload LSP? + A: Stop all clients, then reload the buffer. > :lua vim.lsp.stop_all_clients() :edit -> Why isn't completion working? - -In the buffer where you want to use LSP, check that 'omnifunc' is set to -"v:lua.vim.lsp.omnifunc": > +- Q: Why isn't completion working? + A: In the buffer where you want to use LSP, check that 'omnifunc' is set to + "v:lua.vim.lsp.omnifunc": > :verbose set omnifunc? -Some other plugin may be overriding the option. To avoid that, you could set -the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". +< Some other plugin may be overriding the option. To avoid that, you could + set the option in an |after-directory| ftplugin, e.g. + "after/ftplugin/python.vim". + +================================================================================ +LSP HIGHLIGHT *lsp-highlight* + +When LSP is activated these highlight groups are defined: + + LspDiagnosticsError + LspDiagnosticsHint + LspDiagnosticsInformation + LspDiagnosticsUnderline + LspDiagnosticsUnderlineError + LspDiagnosticsUnderlineHint + LspDiagnosticsUnderlineInformation + LspDiagnosticsUnderlineWarning + LspDiagnosticsWarning ================================================================================ LSP API *lsp-api* diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 338aedb4e0..df82e2d412 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -549,7 +549,9 @@ do local underline_highlight_name = "LspDiagnosticsUnderline" vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) for kind, _ in pairs(protocol.DiagnosticSeverity) do - vim.cmd(string.format("highlight default link %s%s %s", underline_highlight_name, kind, underline_highlight_name)) + if type(kind) == 'string' then + vim.cmd(string.format("highlight default link %s%s %s", underline_highlight_name, kind, underline_highlight_name)) + end end local severity_highlights = {} -- cgit From 05ea3c1997a5f8c1c28192cb67a1306485c11b9a Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Sun, 12 Jan 2020 01:36:11 +0100 Subject: defaults: set fillchars "foldsep" to box line #11702 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "│" U+2502 BOX DRAWINGS LIGHT VERTICAL Fallback to old default | if 'ambiwidth' is set. --- runtime/doc/options.txt | 4 ++-- runtime/doc/vim_diff.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e32a52cdbb..70af23ee29 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2373,7 +2373,7 @@ A jump table for the options with a short description can be found at |Q_op|. fold:c '·' or '-' filling 'foldtext' foldopen:c '-' mark the beginning of a fold foldclose:c '+' show a closed fold - foldsep:c '|' open fold middle marker + foldsep:c '│' or '|' open fold middle marker diff:c '-' deleted lines of the 'diff' option msgsep:c ' ' message separator 'display' eob:c '~' empty lines at the end of a buffer @@ -2382,7 +2382,7 @@ A jump table for the options with a short description can be found at |Q_op|. "stlnc" the space will be used when there is highlighting, '^' or '=' otherwise. - If 'ambiwidth' is "double" then "vert" and "fold" default to + If 'ambiwidth' is "double" then "vert", "foldsep" and "fold" default to single-byte alternatives. Example: > diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index e19659a4c4..c988756154 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -38,7 +38,7 @@ the differences. - 'directory' defaults to ~/.local/share/nvim/swap// (|xdg|), auto-created - 'display' defaults to "lastline,msgsep" - 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding) -- 'fillchars' defaults (in effect) to "vert:│,fold:·" +- 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│" - 'formatoptions' defaults to "tcqj" - 'fsync' is disabled - 'history' defaults to 10000 (the maximum) -- cgit From 92316849863bb2661ee5b4bb284f56163fed27ad Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 12 Jan 2020 23:41:55 -0800 Subject: doc [ci skip] #11656 --- runtime/doc/api.txt | 20 ++++++-------- runtime/doc/autocmd.txt | 67 +++++++++++++++++++--------------------------- runtime/doc/deprecated.txt | 1 + runtime/doc/eval.txt | 59 +++++++++++++++++++++++----------------- runtime/doc/help.txt | 3 ++- runtime/doc/index.txt | 2 +- runtime/doc/lsp.txt | 24 ++++++++++++++--- runtime/doc/lua.txt | 20 ++++++++++++++ runtime/doc/tabpage.txt | 3 +++ runtime/doc/vim_diff.txt | 10 +++---- runtime/lua/vim/lsp.lua | 15 +++++++++-- runtime/lua/vim/shared.lua | 20 +++++++------- 12 files changed, 145 insertions(+), 99 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index f8fdd64a9b..39b6c6417d 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -183,21 +183,17 @@ External programs (clients) can use the metadata to discover the API, using any of these approaches: 1. Connect to a running Nvim instance and call |nvim_get_api_info()| via - msgpack-rpc. This is best for clients written in dynamic languages which + msgpack-RPC. This is best for clients written in dynamic languages which can define functions at runtime. - 2. Start Nvim with the |--api-info| option. Useful for clients written in - statically-compiled languages. - - 3. Use the |api_info()| Vimscript function. - -Example: To get a human-readable list of API functions: > - :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val.name') -< -Example: To get a formatted dump of the API using python (requires the -"pyyaml" and "msgpack-python" modules): > - nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' + 2. Start Nvim with |--api-info|. Useful for statically-compiled clients. + Example (requires Python "pyyaml" and "msgpack-python" modules): > + nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' < + 3. Use the |api_info()| Vimscript function. > + :lua print(vim.inspect(vim.fn.api_info())) +< Example using |filter()| to exclude non-deprecated API functions: > + :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val.name') ============================================================================== API contract *api-contract* diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 97379bfa27..45bead968d 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -251,7 +251,6 @@ Name triggered by ~ Buffers |BufAdd| just after adding a buffer to the buffer list -|BufCreate| just after adding a buffer to the buffer list |BufDelete| before deleting a buffer from the buffer list |BufWipeout| before completely deleting a buffer @@ -367,32 +366,29 @@ Name triggered by ~ The alphabetical list of autocommand events: *autocmd-events-abc* - *BufCreate* *BufAdd* -BufAdd or BufCreate Just after creating a new buffer which is + *BufAdd* +BufAdd Just after creating a new buffer which is added to the buffer list, or adding a buffer - to the buffer list. - Also used just after a buffer in the buffer - list has been renamed. - The BufCreate event is for historic reasons. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being created "". + to the buffer list, a buffer in the buffer + list was renamed. + Before |BufEnter|. + NOTE: Current buffer "%" may be different from + the buffer being created "". *BufDelete* BufDelete Before deleting a buffer from the buffer list. The BufUnload may be called first (if the buffer was loaded). Also used just before a buffer in the buffer list is renamed. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being deleted "" and "". - Don't change to another buffer, it will cause - problems. + NOTE: Current buffer "%" may be different from + the buffer being deleted "" and "". + Do not change to another buffer. *BufEnter* BufEnter After entering a buffer. Useful for setting options for a file type. Also executed when starting to edit a buffer, after the - BufReadPost autocommands. + After |BufAdd|. + After |BufReadPost|. *BufFilePost* BufFilePost After changing the name of the current buffer with the ":file" or ":saveas" command. @@ -405,9 +401,8 @@ BufHidden Just before a buffer becomes hidden. That is, the buffer, but the buffer is not unloaded or deleted. Not used for ":qa" or ":q" when exiting Vim. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being unloaded "". + NOTE: current buffer "%" may be different from + the buffer being unloaded "". *BufLeave* BufLeave Before leaving to another buffer. Also when leaving or closing the current window and the @@ -418,9 +413,8 @@ BufNew Just after creating a new buffer. Also used just after a buffer has been renamed. When the buffer is added to the buffer list BufAdd will be triggered too. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being created "". + NOTE: Current buffer "%" may be different from + the buffer being created "". *BufNewFile* BufNewFile When starting to edit a file that doesn't exist. Can be used to read in a skeleton @@ -451,13 +445,11 @@ BufUnload Before unloading a buffer. This is when the may be after a BufWritePost and before a BufDelete. Also used for all buffers that are loaded when Vim is going to exit. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being unloaded "". - Don't change to another buffer or window, it - will cause problems! - When exiting and v:dying is 2 or more this - event is not triggered. + NOTE: Current buffer "%" may be different from + the buffer being unloaded "". + Do not change to another buffer or window! + Not triggered when exiting and v:dying is 2 or + more. *BufWinEnter* BufWinEnter After a buffer is displayed in a window. This can be when the buffer is loaded (after @@ -476,11 +468,10 @@ BufWinLeave Before a buffer is removed from a window. Not when it's still visible in another window. Also triggered when exiting. It's triggered before BufUnload or BufHidden. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being unloaded "". - When exiting and v:dying is 2 or more this - event is not triggered. + NOTE: Current buffer "%" may be different from + the buffer being unloaded "". + Not triggered when exiting and v:dying is 2 or + more. *BufWipeout* BufWipeout Before completely deleting a buffer. The BufUnload and BufDelete events may be called @@ -488,11 +479,9 @@ BufWipeout Before completely deleting a buffer. The buffer list). Also used just before a buffer is renamed (also when it's not in the buffer list). - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer being deleted "". - Don't change to another buffer, it will cause - problems. + NOTE: Current buffer "%" may be different from + the buffer being deleted "". + Do not change to another buffer. *BufWrite* *BufWritePre* BufWrite or BufWritePre Before writing the whole buffer to a file. *BufWriteCmd* diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 7c6b9ad1d3..ce075e1bee 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -28,6 +28,7 @@ Environment Variables ~ $NVIM_LISTEN_ADDRESS is ignored. Events ~ +*BufCreate* Use |BufAdd| instead. *EncodingChanged* Never fired; 'encoding' is always "utf-8". *FileEncoding* Never fired; equivalent to |EncodingChanged|. *GUIEnter* Never fired; use |UIEnter| instead. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 67277d19dd..cd02449475 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2520,6 +2520,9 @@ and({expr}, {expr}) *and()* api_info() *api_info()* Returns Dictionary of |api-metadata|. + View it in a nice human-readable format: > + :lua print(vim.inspect(vim.fn.api_info())) + append({lnum}, {text}) *append()* When {text} is a |List|: Append each item of the |List| as a text line below line {lnum} in the current buffer. @@ -5453,35 +5456,41 @@ jobstart({cmd}[, {opts}]) *jobstart()* *jobstart-options* {opts} is a dictionary with these keys: - |on_stdout|: stdout event handler (function name or |Funcref|) - stdout_buffered : read stdout in |channel-buffered| mode. - |on_stderr|: stderr event handler (function name or |Funcref|) - stderr_buffered : read stderr in |channel-buffered| mode. - |on_exit| : exit event handler (function name or |Funcref|) - cwd : Working directory of the job; defaults to - |current-directory|. - env : A dict of strings to append (or replace see - |clear_env|) to the current environment. - clear_env: If set, use the exact values passed in |env| - rpc : If set, |msgpack-rpc| will be used to communicate - with the job over stdin and stdout. "on_stdout" is - then ignored, but "on_stderr" can still be used. - pty : If set, the job will be connected to a new pseudo - terminal and the job streams are connected to the - master file descriptor. "on_stderr" is ignored, - "on_stdout" receives all output. - - width : (pty only) Width of the terminal screen - height : (pty only) Height of the terminal screen - TERM : (pty only) $TERM environment variable - detach : (non-pty only) Detach the job process: it will - not be killed when Nvim exits. If the process - exits before Nvim, "on_exit" will be invoked. + clear_env: (boolean) `env` defines the job environment + exactly, instead of merging current environment. + cwd: (string, default=|current-directory|) Working + directory of the job. + detach: (boolean) Detach the job process: it will not be + killed when Nvim exits. If the process exits + before Nvim, `on_exit` will be invoked. + env: (dict) Map of environment variable name:value + pairs extending (or replacing if |clear_env|) + the current environment. + height: (number) Height of the `pty` terminal. + |on_exit|: (function) Callback invoked when the job exits. + |on_stdout|: (function) Callback invoked when the job emits + stdout data. + |on_stderr|: (function) Callback invoked when the job emits + stderr data. + pty: (boolean) Connect the job to a new pseudo + terminal, and its streams to the master file + descriptor. Then `on_stderr` is ignored, + `on_stdout` receives all output. + rpc: (boolean) Use |msgpack-rpc| to communicate with + the job over stdio. Then `on_stdout` is ignored, + but `on_stderr` can still be used. + stderr_buffered: (boolean) Collect data until EOF (stream closed) + before invoking `on_stderr`. |channel-buffered| + stdout_buffered: (boolean) Collect data until EOF (stream + closed) before invoking `on_stdout`. |channel-buffered| + TERM: (string) Sets the `pty` $TERM environment variable. + width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the caller may set other keys to pass application-specific data. + Returns: - - The channel ID on success + - |channel-id| on success - 0 on invalid arguments - -1 if {cmd}[0] is not executable. See also |job-control|, |channel|, |msgpack-rpc|. diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 6090fa96bb..a384b5f876 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -137,6 +137,7 @@ Special issues ~ Programming language support ~ |indent.txt| automatic indenting for C and other languages +|lsp.txt| Language Server Protocol (LSP) |syntax.txt| syntax highlighting |filetype.txt| settings done specifically for a type of file |quickfix.txt| commands for a quick edit-compile-fix cycle @@ -169,7 +170,7 @@ Versions ~ Standard plugins ~ |pi_gzip.txt| Reading and writing compressed files |pi_health.txt| Healthcheck framework -|pi_matchit.txt| Extended "%" matching +|pi_matchit.txt| Extended |%| matching |pi_msgpack.txt| msgpack utilities |pi_netrw.txt| Reading and writing files over a network |pi_paren.txt| Highlight matching parens diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 3b057575a8..bdab10c0e4 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -10,7 +10,7 @@ short description. The lists are sorted on ASCII value. Tip: When looking for certain functionality, use a search command. E.g., to look for deleting something, use: "/delete". -For an overview of options see help.txt |option-list|. +For an overview of options see |option-list|. For an overview of built-in functions see |functions|. For a list of Vim variables see |vim-variable|. For a complete listing of all help items see |help-tags|. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index c173ecead3..016a8be7e6 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -49,9 +49,9 @@ go-to-definition, hover, etc. Example config: > nnoremap 1gD lua vim.lsp.buf.type_definition() nnoremap gr lua vim.lsp.buf.references() -Nvim provides the vim.lsp.omnifunc 'omnifunc' handler which allows -|i_CTRL-X_CTRL-O| to consume LSP completion features. Example config (note the -use of |v:lua| to call Lua from Vimscript): > +Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows +|i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of +|v:lua| to call Lua from Vimscript): > " Use LSP omni-completion in Python files. autocmd Filetype python setlocal omnifunc=v:lua.vim.lsp.omnifunc @@ -477,7 +477,23 @@ notify({...}) *vim.lsp.notify()* TODO: Documentation omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* - TODO: Documentation + Implements 'omnifunc' compatible LSP completion. + + Parameters: ~ + {findstart} 0 or 1, decides behavior + {base} If findstart=0, text to match against + + Return: ~ + (number) Decided by`findstart`: + • findstart=0: column where the completion starts, or -2 + or -3 + • findstart=1: list of matches (actually just calls + |complete()|) + + See also: ~ + |complete-functions| + |complete-items| + |CompleteDone| on_error({code}, {err}) *vim.lsp.on_error()* TODO: Documentation diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index d1f244c76f..af1f4a8c1f 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -885,6 +885,16 @@ deepcopy({orig}) *vim.deepcopy()* Return: ~ New table of copied keys and (nested) values. +endswith({s}, {suffix}) *vim.endswith()* + Tests if `s` ends with `suffix` . + + Parameters: ~ + {s} (string) a string + {suffix} (string) a suffix + + Return: ~ + (boolean) true if `suffix` is a suffix of s + gsplit({s}, {sep}, {plain}) *vim.gsplit()* Splits a string at each instance of a separator. @@ -962,6 +972,16 @@ split({s}, {sep}, {plain}) *vim.split()* See also: ~ |vim.gsplit()| +startswith({s}, {prefix}) *vim.startswith()* + Tests if `s` starts with `prefix` . + + Parameters: ~ + {s} (string) a string + {prefix} (string) a prefix + + Return: ~ + (boolean) true if `prefix` is a prefix of s + tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()* Add the reverse lookup values to an existing table. For example: `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index d0a5678179..1407fdd968 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -190,6 +190,9 @@ gt *i_CTRL-* *i_* {count} {count}gt Go to tab page {count}. The first tab page has number one. +CTRL- *CTRL-* +CTRL-W g *g* *CTRL-W_g* +g Go to previous (last accessed) tab page. :tabp[revious] *:tabp* *:tabprevious* *gT* *:tabN* :tabN[ext] *:tabNext* *CTRL-* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index c988756154..2bb798a6e6 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -270,11 +270,6 @@ are always available and may be used simultaneously. See |provider-python|. |json_encode()| behaviour slightly changed: now |msgpack-special-dict| values are accepted, but |v:none| is not. -*v:none* variable is absent. In Vim it represents “no value” in “js” strings -like "[,]" parsed as "[v:none]" by |js_decode()|. - -*js_encode()* and *js_decode()* functions are also absent. - Viminfo text files were replaced with binary (messagepack) ShaDa files. Additional differences: @@ -443,6 +438,11 @@ Compile-time features: Emacs tags support X11 integration (see |x11-selection|) +Eval: + *js_encode()* + *js_decode()* + *v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead. + Highlight groups: *hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim supports 'winhighlight' window-local highlights. diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c193fad6a4..cfa208f21c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -914,7 +914,18 @@ function lsp.buf_notify(bufnr, method, params) end) end ---- Function which can be called to generate omnifunc compatible completion. +--- Implements 'omnifunc' compatible LSP completion. +--- +--@see |complete-functions| +--@see |complete-items| +--@see |CompleteDone| +--- +--@param findstart 0 or 1, decides behavior +--@param base If findstart=0, text to match against +--- +--@return (number) Decided by `findstart`: +--- - findstart=0: column where the completion starts, or -2 or -3 +--- - findstart=1: list of matches (actually just calls |complete()|) function lsp.omnifunc(findstart, base) local _ = log.debug() and log.debug("omnifunc.findstart", { findstart = findstart, base = base }) @@ -936,7 +947,7 @@ function lsp.omnifunc(findstart, base) local line_to_cursor = line:sub(1, pos[2]) local _ = log.trace() and log.trace("omnifunc.line", pos, line) - -- Get the start postion of the current keyword + -- Get the start position of the current keyword local textMatch = vim.fn.match(line_to_cursor, '\\k*$') local params = util.make_position_params() diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 6df9bf1c2f..a71e9878bb 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -331,21 +331,21 @@ function vim.pesc(s) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end ---- Test if `prefix` is a prefix of `s` for strings. --- --- @param s String to check --- @param prefix Potential prefix --- @return boolean True if prefix is a prefix of s +--- Tests if `s` starts with `prefix`. +--- +--@param s (string) a string +--@param prefix (string) a prefix +--@return (boolean) true if `prefix` is a prefix of s function vim.startswith(s, prefix) vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } return s:sub(1, #prefix) == prefix end ---- Test if `suffix` is a suffix of `s` for strings. --- --- @param s String to check --- @param suffix Potential suffix --- @return boolean True if suffix is a suffix of s +--- Tests if `s` ends with `suffix`. +--- +--@param s (string) a string +--@param suffix (string) a suffix +--@return (boolean) true if `suffix` is a suffix of s function vim.endswith(s, suffix) vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } return #suffix == 0 or s:sub(-#suffix) == suffix -- cgit From ad35cbca76222deb33357b63a61a29945ef084f7 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 17 Jan 2020 23:31:18 -0500 Subject: vim-patch:8.2.0123: complete_info() does not work when CompleteDone is triggered Problem: complete_info() does not work when CompleteDone is triggered. Solution: Trigger CompleteDone before clearing the info. https://github.com/vim/vim/commit/17e04781f26c24769e202351c194ee252927eee1 --- runtime/doc/autocmd.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 45bead968d..774e6a5d92 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -596,6 +596,8 @@ CompleteChanged *CompleteChanged* CompleteDone After Insert mode completion is done. Either when something was completed or abandoning completion. |ins-completion| + |complete_info()| can be used, the info is + cleared after triggering CompleteDone. The |v:completed_item| variable contains the completed item. -- cgit From 757aad92e84709a08320a06870b6acb086bc6876 Mon Sep 17 00:00:00 2001 From: Marcos ALMEIDA Date: Sat, 29 Sep 2018 20:40:53 +0200 Subject: autocmd: add WinClosed event - only fire once, just before freeing mem - trigger when on a different buffer - avoid recursive calls in another tab --- runtime/doc/autocmd.txt | 4 +++- runtime/doc/vim_diff.txt | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 774e6a5d92..d15f5e7900 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -317,6 +317,7 @@ Name triggered by ~ |CursorMoved| the cursor was moved in Normal mode |CursorMovedI| the cursor was moved in Insert mode +|WinClosed| after closing a window |WinNew| after creating a new window |WinEnter| after entering another window |WinLeave| before leaving a window @@ -1131,6 +1132,8 @@ VimResized After the Vim window was resized, thus 'lines' VimResume After Nvim resumes from |suspend| state. *VimSuspend* VimSuspend Before Nvim enters |suspend| state. + *WinClosed* +WinClosed After closing a window. *WinEnter* WinEnter After entering another window. Not done for the first window, when Vim has just started. @@ -1148,7 +1151,6 @@ WinLeave Before leaving a window. If the window to be executes the BufLeave autocommands before the WinLeave autocommands (but not for ":new"). Not used for ":qa" or ":q" when exiting Vim. - *WinNew* WinNew When a new window was created. Not done for the first window, when Vim has just started. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 2bb798a6e6..5835c7f314 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -159,6 +159,7 @@ Events: |UILeave| |VimResume| |VimSuspend| + |WinClosed| Functions: |dictwatcheradd()| notifies a callback whenever a |Dict| is modified -- cgit From 156c25e4983d4c106ba70e5e3bcc6bbb012d8065 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 13 Jan 2020 00:19:20 -0800 Subject: WinClosed: sort auevents.lua; improve tests - test: reduce verbosity, condense redundancy, improve readability - auevents.lua: keep events sorted by name. ref afd1d412fa91 --- runtime/doc/autocmd.txt | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index d15f5e7900..608f8bbf3d 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -576,8 +576,7 @@ ColorSchemePre Before loading a color scheme. |:colorscheme| CompleteChanged *CompleteChanged* After each time the Insert mode completion menu changed. Not fired on popup menu hide, - use |CompleteDone| for that. Never triggered - recursively. + use |CompleteDone| for that. Sets these |v:event| keys: completed_item See |complete-items|. @@ -588,7 +587,8 @@ CompleteChanged *CompleteChanged* size total nr of items scrollbar TRUE if visible - It is not allowed to change the text |textlock|. + Non-recursive (event cannot trigger itself). + Cannot change the text. |textlock| The size and position of the popup are also available by calling |pum_getpos()|. @@ -661,7 +661,7 @@ DirChanged After the |current-directory| was changed. Sets these |v:event| keys: cwd: current working directory scope: "global", "tab", "window" - Recursion is ignored. + Non-recursive (event cannot trigger itself). *FileAppendCmd* FileAppendCmd Before appending to a file. Should do the appending to the file. Use the '[ and '] @@ -697,6 +697,7 @@ ExitPre When using `:quit`, `:wq` in a way it makes cancelled if there is a modified buffer that isn't automatically saved, use |VimLeavePre| for really exiting. + See also |QuitPre|, |WinClosed|. *FileChangedShell* FileChangedShell When Vim notices that the modification time of a file has changed since editing started. @@ -841,7 +842,7 @@ TextYankPost Just after a |yank| or |deleting| command, but not and |']| marks can be used to calculate the precise region of the operation. - Recursion is ignored. + Non-recursive (event cannot trigger itself). Cannot change the text. |textlock| *InsertEnter* InsertEnter Just before starting Insert mode. Also for @@ -908,8 +909,8 @@ OptionSet After setting an option (except during always use |:noautocmd| to prevent triggering OptionSet. - Recursion is ignored, thus |:set| in the - autocommand does not trigger OptionSet again. + Non-recursive: |:set| in the autocommand does + not trigger OptionSet again. *QuickFixCmdPre* QuickFixCmdPre Before a quickfix command is run (|:make|, @@ -941,7 +942,7 @@ QuitPre When using `:quit`, `:wq` or `:qall`, before or quits Vim. Can be used to close any non-essential window if the current window is the last ordinary window. - Also see |ExitPre|. + See also |ExitPre|, ||WinClosed|. *RemoteReply* RemoteReply When a reply from a Vim that functions as server was received |server2client()|. The @@ -1042,8 +1043,8 @@ TabNew When creating a new tab page. |tab-page| TabNewEntered After entering a new tab page. |tab-page| After BufEnter. *TabClosed* -TabClosed After closing a tab page. can be used - for the tab page number. +TabClosed After closing a tab page. expands to + the tab page number. *TermOpen* TermOpen When a |terminal| job is starting. Can be used to configure the terminal buffer. @@ -1133,7 +1134,10 @@ VimResume After Nvim resumes from |suspend| state. *VimSuspend* VimSuspend Before Nvim enters |suspend| state. *WinClosed* -WinClosed After closing a window. +WinClosed After closing a window. expands to the + |window-ID|. + Non-recursive (event cannot trigger itself). + See also |ExitPre|, |QuitPre|. *WinEnter* WinEnter After entering another window. Not done for the first window, when Vim has just started. -- cgit From b648b38bb1a447c1e757f61ddd248a686cd32807 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 18 Jan 2020 19:38:48 -0800 Subject: doc: autocmd.txt --- runtime/doc/autocmd.txt | 190 +++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 99 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 608f8bbf3d..8006a45899 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -397,17 +397,18 @@ BufFilePost After changing the name of the current buffer BufFilePre Before changing the name of the current buffer with the ":file" or ":saveas" command. *BufHidden* -BufHidden Just before a buffer becomes hidden. That is, - when there are no longer windows that show - the buffer, but the buffer is not unloaded or - deleted. Not used for ":qa" or ":q" when - exiting Vim. +BufHidden Before a buffer becomes hidden: when there are + no longer windows that show the buffer, but + the buffer is not unloaded or deleted. + + Not used for ":qa" or ":q" when exiting Vim. NOTE: current buffer "%" may be different from the buffer being unloaded "". *BufLeave* BufLeave Before leaving to another buffer. Also when leaving or closing the current window and the new current window is not for the same buffer. + Not used for ":qa" or ":q" when exiting Vim. *BufNew* BufNew Just after creating a new buffer. Also used @@ -423,16 +424,17 @@ BufNewFile When starting to edit a file that doesn't *BufRead* *BufReadPost* BufRead or BufReadPost When starting to edit a new buffer, after reading the file into the buffer, before - executing the modelines. See |BufWinEnter| - for when you need to do something after - processing the modelines. - This does NOT work for ":r file". Not used - when the file doesn't exist. Also used after - successfully recovering a file. - Also triggered for the filetypedetect group - when executing ":filetype detect" and when - writing an unnamed buffer in a way that the - buffer gets a name. + processing modelines. See |BufWinEnter| to do + something after processing modelines. + Also triggered: + - when writing an unnamed buffer such that the + buffer gets a name + - after successfully recovering a file + - for the "filetypedetect" group when + executing ":filetype detect" + Not triggered: + - for ":r file" + - if the file doesn't exist *BufReadCmd* BufReadCmd Before starting to edit a new buffer. Should read the file into the buffer. |Cmd-event| @@ -441,34 +443,33 @@ BufReadPre When starting to edit a new buffer, before reading the file into the buffer. Not used if the file doesn't exist. *BufUnload* -BufUnload Before unloading a buffer. This is when the - text in the buffer is going to be freed. This - may be after a BufWritePost and before a - BufDelete. Also used for all buffers that are - loaded when Vim is going to exit. +BufUnload Before unloading a buffer, when the text in + the buffer is going to be freed. + After BufWritePost. + Before BufDelete. + Triggers for all loaded buffers when Vim is + going to exit. NOTE: Current buffer "%" may be different from the buffer being unloaded "". - Do not change to another buffer or window! + Do not switch buffers or windows! Not triggered when exiting and v:dying is 2 or more. *BufWinEnter* BufWinEnter After a buffer is displayed in a window. This - can be when the buffer is loaded (after - processing the modelines) or when a hidden - buffer is displayed in a window (and is no - longer hidden). - Does not happen for |:split| without - arguments, since you keep editing the same - buffer, or ":split" with a file that's already - open in a window, because it re-uses an - existing buffer. But it does happen for a - ":split" with the name of the current buffer, - since it reloads that buffer. + may be when the buffer is loaded (after + processing modelines) or when a hidden buffer + is displayed (and is no longer hidden). + + Not triggered for |:split| without arguments, + since the buffer does not change, or :split + with a file already open in a window. + Triggered for ":split" with the name of the + current buffer, since it reloads that buffer. *BufWinLeave* BufWinLeave Before a buffer is removed from a window. Not when it's still visible in another window. - Also triggered when exiting. It's triggered - before BufUnload or BufHidden. + Also triggered when exiting. + Before BufUnload, BufHidden. NOTE: Current buffer "%" may be different from the buffer being unloaded "". Not triggered when exiting and v:dying is 2 or @@ -516,7 +517,7 @@ CmdUndefined When a user command is used but it isn't defined. Useful for defining a command only when it's used. The pattern is matched against the command name. Both and - are set to the name of the command. + expand to the command name. NOTE: Autocompletion won't work until the command is defined. An alternative is to always define the user command and have it @@ -525,12 +526,12 @@ CmdUndefined When a user command is used but it isn't CmdlineChanged After a change was made to the text inside command line. Be careful not to mess up the command line, it may cause Vim to lock up. - is set to the |cmdline-char|. + expands to the |cmdline-char|. *CmdlineEnter* CmdlineEnter After entering the command-line (including non-interactive use of ":" in a mapping: use || instead to avoid this). - is set to the |cmdline-char|. + expands to the |cmdline-char|. Sets these |v:event| keys: cmdlevel cmdtype @@ -538,26 +539,26 @@ CmdlineEnter After entering the command-line (including CmdlineLeave Before leaving the command-line (including non-interactive use of ":" in a mapping: use || instead to avoid this). - is set to the |cmdline-char|. + expands to the |cmdline-char|. Sets these |v:event| keys: abort (mutable) cmdlevel cmdtype Note: `abort` can only be changed from false - to true. An autocmd cannot execute an already - aborted cmdline by changing it to false. + to true: cannot execute an already aborted + cmdline by changing it to false. *CmdwinEnter* CmdwinEnter After entering the command-line window. Useful for setting options specifically for this special type of window. - is set to a single character, + expands to a single character, indicating the type of command-line. |cmdwin-char| *CmdwinLeave* CmdwinLeave Before leaving the command-line window. Useful to clean up any global setting done with CmdwinEnter. - is set to a single character, + expands to a single character, indicating the type of command-line. |cmdwin-char| *ColorScheme* @@ -599,8 +600,7 @@ CompleteDone After Insert mode completion is done. Either completion. |ins-completion| |complete_info()| can be used, the info is cleared after triggering CompleteDone. - The |v:completed_item| variable contains the - completed item. + |v:completed_item| gives the completed item. *CursorHold* CursorHold When the user doesn't press a key for the time @@ -630,9 +630,9 @@ CursorHold When the user doesn't press a key for the time :let &ro = &ro *CursorHoldI* -CursorHoldI Just like CursorHold, but in Insert mode. - Not triggered when waiting for another key, - e.g. after CTRL-V, and not when in CTRL-X mode +CursorHoldI Like CursorHold, but in Insert mode. Not + triggered when waiting for another key, e.g. + after CTRL-V, and not in CTRL-X mode |insert_expand|. *CursorMoved* @@ -643,7 +643,7 @@ CursorMoved After the cursor was moved in Normal or Visual Not triggered when there is typeahead or when an operator is pending. For an example see |match-parens|. - Note: Cannot be skipped with `:noautocmd`. + Note: Cannot be skipped with |:noautocmd|. Careful: This is triggered very often, don't do anything that the user does not expect or that is slow. @@ -665,7 +665,7 @@ DirChanged After the |current-directory| was changed. *FileAppendCmd* FileAppendCmd Before appending to a file. Should do the appending to the file. Use the '[ and '] - marks for the range of lines.|Cmd-event| + marks for the range of lines. |Cmd-event| *FileAppendPost* FileAppendPost After appending to a file. *FileAppendPre* @@ -673,19 +673,18 @@ FileAppendPre Before appending to a file. Use the '[ and '] marks for the range of lines. *FileChangedRO* FileChangedRO Before making the first change to a read-only - file. Can be used to check-out the file from + file. Can be used to checkout the file from a source control system. Not triggered when the change was caused by an autocommand. - This event is triggered when making the first - change in a buffer or the first change after - 'readonly' was set, just before the change is - applied to the text. + Triggered when making the first change in + a buffer or the first change after 'readonly' + was set, just before the change is applied to + the text. WARNING: If the autocommand moves the cursor the effect of the change is undefined. *E788* - It is not allowed to change to another buffer - here. You can reload the buffer but not edit - another one. + Cannot switch buffers. You can reload the + buffer but not edit another one. *E881* If the number of lines changes saving for undo may fail and the change will be aborted. @@ -704,28 +703,21 @@ FileChangedShell When Vim notices that the modification time of Also when the file attributes of the file change or when the size of the file changes. |timestamp| - Mostly triggered after executing a shell - command, but also with a |:checktime| command - or when gvim regains input focus. - This autocommand is triggered for each changed - file. It is not used when 'autoread' is set - and the buffer was not changed. If a - FileChangedShell autocommand is present the - warning message and prompt is not given. - The |v:fcs_reason| variable is set to indicate - what happened and |v:fcs_choice| can be used - to tell Vim what to do next. - NOTE: When this autocommand is executed, the - current buffer "%" may be different from the - buffer that was changed, which is in "". - NOTE: The commands must not change the current - buffer, jump to another buffer or delete a - buffer. *E246* *E811* - NOTE: This event never nests, to avoid an - endless loop. This means that while executing - commands for the FileChangedShell event no - other FileChangedShell event will be - triggered. + Triggered for each changed file, after: + - executing a shell command + - |:checktime| + - |FocusGained| + Not used when 'autoread' is set and the buffer + was not changed. If a FileChangedShell + autocommand exists the warning message and + prompt is not given. + |v:fcs_reason| indicates what happened. Set + |v:fcs_choice| to control what happens next. + NOTE: Current buffer "%" may be different from + the buffer that was changed "". + *E246* *E811* + Cannot switch, jump to or delete buffers. + Non-recursive (event cannot trigger itself). *FileChangedShellPost* FileChangedShellPost After handling a file that was changed outside of Vim. Can be used to update the statusline. @@ -742,10 +734,10 @@ FileReadPre Before reading a file with a ":read" command. *FileType* FileType When the 'filetype' option has been set. The pattern is matched against the filetype. - can be used for the name of the file - where this option was set, and for - the new value of 'filetype'. Navigating to - another window or buffer is not allowed. + is the name of the file where this + option was set. is the new value of + 'filetype'. + Cannot switch windows or buffers. See |filetypes|. *FileWriteCmd* FileWriteCmd Before writing to a file, when not writing the @@ -1018,27 +1010,26 @@ SwapExists Detected an existing swap file when starting When set to an empty string the user will be asked, as if there was no SwapExists autocmd. *E812* - It is not allowed to change to another buffer, - change a buffer name or change directory - here. + Cannot change to another buffer, change + the buffer name or change directory. *Syntax* Syntax When the 'syntax' option has been set. The pattern is matched against the syntax name. - can be used for the name of the file - where this option was set, and for - the new value of 'syntax'. + expands to the name of the file where + this option was set. expands to the + new value of 'syntax'. See |:syn-on|. *TabEnter* TabEnter Just after entering a tab page. |tab-page| - After triggering the WinEnter and before - triggering the BufEnter event. + After WinEnter. + Before BufEnter. *TabLeave* TabLeave Just before leaving a tab page. |tab-page| - A WinLeave event will have been triggered - first. + After WinLeave. *TabNew* TabNew When creating a new tab page. |tab-page| - After WinEnter and before TabEnter. + After WinEnter. + Before TabEnter. *TabNewEntered* TabNewEntered After entering a new tab page. |tab-page| After BufEnter. @@ -1060,10 +1051,9 @@ TermClose When a |terminal| job ends. TermResponse After the response to t_RV is received from the terminal. The value of |v:termresponse| can be used to do things depending on the - terminal version. Note that this event may be - triggered halfway through another event - (especially if file I/O, a shell command, or - anything else that takes time is involved). + terminal version. May be triggered halfway + through another event (file I/O, a shell + command, or anything else that takes time). *TextChanged* TextChanged After a change was made to the text in the current buffer in Normal mode. That is after @@ -1136,6 +1126,7 @@ VimSuspend Before Nvim enters |suspend| state. *WinClosed* WinClosed After closing a window. expands to the |window-ID|. + After WinLeave. Non-recursive (event cannot trigger itself). See also |ExitPre|, |QuitPre|. *WinEnter* @@ -1155,10 +1146,11 @@ WinLeave Before leaving a window. If the window to be executes the BufLeave autocommands before the WinLeave autocommands (but not for ":new"). Not used for ":qa" or ":q" when exiting Vim. + After WinClosed. *WinNew* WinNew When a new window was created. Not done for the first window, when Vim has just started. - Before a WinEnter event. + Before WinEnter. ============================================================================== 6. Patterns *autocmd-pattern* *{pat}* -- cgit From c25b5a1576eca912671d5f2fe47043055fbca2b0 Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Mon, 4 Nov 2019 17:33:07 +0000 Subject: remote plugins: add support for perl hosts --- runtime/autoload/provider/perl.vim | 73 ++++++++++++++++++++++++++++++++++++++ runtime/autoload/remote/host.vim | 4 +++ 2 files changed, 77 insertions(+) create mode 100644 runtime/autoload/provider/perl.vim (limited to 'runtime') diff --git a/runtime/autoload/provider/perl.vim b/runtime/autoload/provider/perl.vim new file mode 100644 index 0000000000..4546a85100 --- /dev/null +++ b/runtime/autoload/provider/perl.vim @@ -0,0 +1,73 @@ +if exists('s:loaded_perl_provider') + finish +endif + +let s:loaded_perl_provider = 1 + +function! provider#perl#Detect() abort + " use g:perl_host_prof if set or check if perl is on the path + let prog = get(g:, 'perl_host_prog', executable('perl') ? 'perl' : '') + + " if perl is available, make sure the required module is available + if prog != '' + let job_id = jobstart(prog.' -MNeovim::Ext -e "exit 0"', {'stdout_buffered': v:true}) + let result = jobwait([job_id]) + if result[0] != 0 + let prog = '' + endif + endif + + return prog +endfunction + +function! provider#perl#Prog() abort + return s:prog +endfunction + +function! provider#perl#Require(host) abort + if s:err != '' + echoerr s:err + return + endif + + let prog = provider#perl#Prog() + let args = [s:prog, '-e', 'use Neovim::Ext; start_host();'] + + " Collect registered perl plugins into args + let perl_plugins = remote#host#PluginsForHost(a:host.name) + for plugin in perl_plugins + call add(args, plugin.path) + endfor + + return provider#Poll(args, a:host.orig_name, '$NVIM_PERL_LOG_FILE') +endfunction + +function! provider#perl#Call(method, args) abort + if s:err != '' + echoerr s:err + return + endif + + if !exists('s:host') + try + let s:host = remote#host#Require('perl') + catch + let s:err = v:exception + echohl WarningMsg + echomsg v:exception + echohl None + return + endtry + endif + return call('rpcrequest', insert(insert(a:args, 'perl_'.a:method), s:host)) +endfunction + +let s:err = '' +let s:prog = provider#perl#Detect() +let g:loaded_perl_provider = empty(s:prog) ? 1 : 2 + +if g:loaded_perl_provider != 2 + let s:err = 'Cannot find perl or the required perl module' +endif + +call remote#host#RegisterPlugin('perl-provider', 'perl', []) diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index 1cf328e08d..c34ff4bee7 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -203,3 +203,7 @@ call remote#host#Register('ruby', '*.rb', " nodejs call remote#host#Register('node', '*', \ function('provider#node#Require')) + +" perl +call remote#host#Register('perl', '*', + \ function('provider#perl#Require')) -- cgit From ef3e61013882ed92e0208b9d1ac3488494c7ac53 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 15 Jan 2020 21:12:36 -0500 Subject: provider/perl: simplify detection --- runtime/autoload/provider/perl.vim | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/provider/perl.vim b/runtime/autoload/provider/perl.vim index 4546a85100..36ca2bbf14 100644 --- a/runtime/autoload/provider/perl.vim +++ b/runtime/autoload/provider/perl.vim @@ -6,18 +6,14 @@ let s:loaded_perl_provider = 1 function! provider#perl#Detect() abort " use g:perl_host_prof if set or check if perl is on the path - let prog = get(g:, 'perl_host_prog', executable('perl') ? 'perl' : '') - - " if perl is available, make sure the required module is available - if prog != '' - let job_id = jobstart(prog.' -MNeovim::Ext -e "exit 0"', {'stdout_buffered': v:true}) - let result = jobwait([job_id]) - if result[0] != 0 - let prog = '' - endif + let prog = exepath(get(g:, 'perl_host_prog', 'perl')) + if empty(prog) + return '' endif - return prog + " if perl is available, make sure the required module is available + call system([prog, '-W', '-MNeovim::Ext', '-e', '']) + return v:shell_error ? '' : prog endfunction function! provider#perl#Prog() abort -- cgit From 2063af3c94834ed35155ae9cb3d7daa1564bb1a9 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 15 Jan 2020 21:56:46 -0500 Subject: provider/perl: add health check --- runtime/autoload/health/provider.vim | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index ad7a614ff5..29abf02760 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -566,7 +566,7 @@ function! s:check_node() abort endif let node_v = get(split(s:system('node -v'), "\n"), 0, '') call health#report_info('Node.js: '. node_v) - if !s:shell_error && s:version_cmp(node_v[1:], '6.0.0') < 0 + if s:shell_error || s:version_cmp(node_v[1:], '6.0.0') < 0 call health#report_warn('Neovim node.js host does not support '.node_v) " Skip further checks, they are nonsense if nodejs is too old. return @@ -623,10 +623,51 @@ function! s:check_node() abort endif endfunction +function! s:check_perl() abort + call health#report_start('Perl provider (optional)') + + if s:disabled_via_loaded_var('perl') + return + endif + + if !executable('perl') || !executable('cpanm') + call health#report_warn( + \ '`perl` and `cpanm` must be in $PATH.', + \ ['Install Perl and cpanminus and verify that `perl` and `cpanm` commands work.']) + return + endif + let perl_v = get(split(s:system(['perl', '-W', '-e', 'print $^V']), "\n"), 0, '') + call health#report_info('Perl: '. perl_v) + if s:shell_error + call health#report_warn('Neovim perl host does not support '.perl_v) + " Skip further checks, they are nonsense if perl is too old. + return + endif + + let host = provider#perl#Detect() + if empty(host) + call health#report_warn('Missing "Neovim::Ext" cpan package.', + \ ['Run in shell: cpanm Neovim::Ext']) + return + endif + call health#report_info('Neovim perl host: '. host) + + let current_perl_cmd = [host, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION'] + let current_perl = s:system(current_perl_cmd) + if s:shell_error + call health#report_error('Failed to run: '. string(current_perl_cmd), + \ ['Report this issue with the output of: ', string(current_perl_cmd)]) + return + endif + + call health#report_ok('"Neovim::Ext" cpan package is installed: '. current_perl) +endfunction + function! health#provider#check() abort call s:check_clipboard() call s:check_python(2) call s:check_python(3) call s:check_ruby() call s:check_node() + call s:check_perl() endfunction -- cgit From da0cd5436b4a50e681f490ec6fc7730a894b1542 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 18 Jan 2020 10:59:17 -0500 Subject: doc: provider-perl Enable 'has("perl")' as an alias for 'g:loaded_perl_provider'. TODO: - +perl interface --- runtime/doc/provider.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 46ff075cef..0a6cdc60e8 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -126,6 +126,31 @@ To use an absolute path (e.g. to an rbenv installation): > To use the RVM "system" Ruby installation: > let g:ruby_host_prog = 'rvm system do neovim-ruby-host' +============================================================================== +Perl integration *provider-perl* + +Nvim supports Perl |remote-plugin|s. +https://github.com/jacquesg/p5-Neovim-Ext + + +PERL QUICKSTART~ + +To use perl remote-plugins with Nvim, install the "Neovim::Ext" cpan package: > + cpanm -n Neovim::Ext + +Run |:checkhealth| to see if your system is up-to-date. + + +PERL PROVIDER CONFIGURATION~ + *g:loaded_perl_provider* +To disable Perl support: > + :let g:loaded_perl_provider = 0 +< + *g:perl_host_prog* +Command to start the Perl executable. Must be set before any +check for has("perl"). > + let g:perl_host_prog = '/path/to/perl' +< ============================================================================== Node.js integration *provider-nodejs* -- cgit From 670a14a2a6a2c9abc97227f0a34712fa8e2e6465 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 18 Jan 2020 13:45:04 -0500 Subject: provider/perl: add latest version health check --- runtime/autoload/health/provider.vim | 43 ++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 29abf02760..cc7d86d0c1 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -595,14 +595,12 @@ function! s:check_node() abort \ 'Are you behind a firewall or proxy?']) return endif - if !empty(latest_npm) - try - let pkg_data = json_decode(latest_npm) - catch /E474/ - return 'error: '.latest_npm - endtry - let latest_npm = get(get(pkg_data, 'dist-tags', {}), 'latest', 'unable to parse') - endif + try + let pkg_data = json_decode(latest_npm) + catch /E474/ + return 'error: '.latest_npm + endtry + let latest_npm = get(get(pkg_data, 'dist-tags', {}), 'latest', 'unable to parse') let current_npm_cmd = ['node', host, '--version'] let current_npm = s:system(current_npm_cmd) @@ -646,21 +644,38 @@ function! s:check_perl() abort let host = provider#perl#Detect() if empty(host) - call health#report_warn('Missing "Neovim::Ext" cpan package.', + call health#report_warn('Missing "Neovim::Ext" cpan module.', \ ['Run in shell: cpanm Neovim::Ext']) return endif call health#report_info('Neovim perl host: '. host) - let current_perl_cmd = [host, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION'] - let current_perl = s:system(current_perl_cmd) + let latest_cpan_cmd = 'cpanm --info Neovim::Ext' + let latest_cpan = s:system(latest_cpan_cmd) + if s:shell_error || empty(latest_cpan) + call health#report_error('Failed to run: '. latest_cpan_cmd, + \ ["Make sure you're connected to the internet.", + \ 'Are you behind a firewall or proxy?']) + return + endif + let latest_cpan = matchstr(latest_cpan, '\(\.\?\d\)\+') + + let current_cpan_cmd = [host, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION'] + let current_cpan = s:system(current_cpan_cmd) if s:shell_error - call health#report_error('Failed to run: '. string(current_perl_cmd), - \ ['Report this issue with the output of: ', string(current_perl_cmd)]) + call health#report_error('Failed to run: '. string(current_cpan_cmd), + \ ['Report this issue with the output of: ', string(current_cpan_cmd)]) return endif - call health#report_ok('"Neovim::Ext" cpan package is installed: '. current_perl) + if s:version_cmp(current_cpan, latest_cpan) == -1 + call health#report_warn( + \ printf('Module "Neovim::Ext" is out-of-date. Installed: %s, latest: %s', + \ current_cpan, latest_cpan), + \ ['Run in shell: cpanm Neovim::Ext']) + else + call health#report_ok('Latest "Neovim::Ext" cpan module is installed: '. current_cpan) + endif endfunction function! health#provider#check() abort -- cgit From 041ec8997a7f613f2ca13bf339c652f117fcb809 Mon Sep 17 00:00:00 2001 From: Billy Su Date: Mon, 20 Jan 2020 20:06:38 +0800 Subject: Fix f_jobstop() failed loudly The return value of jobstop() @return 1 for valid job id 0 for invalid id, including jobs have exited or stopped --- runtime/doc/eval.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index cd02449475..69b8b3cf39 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5502,6 +5502,9 @@ jobstop({id}) *jobstop()* (if any) will be invoked. See |job-control|. + Returns 1 for valid job id, 0 for invalid id, including jobs have + exited or stopped. + jobwait({jobs}[, {timeout}]) *jobwait()* Waits for jobs and their |on_exit| handlers to complete. -- cgit From 71ee46accfd235bad2b8460065100b31bb2d8165 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 22 Jan 2020 20:21:05 -0500 Subject: vim-patch:8.2.0141: no swift filetype detection (#11747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: No swift filetype detection. Solution: Add swift, swiftgyb and sil. (Emir Sarı, closes vim/vim#5517) https://github.com/vim/vim/commit/0d76683e094c6cac2e879601aff3acf1163cbe0b --- runtime/filetype.vim | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index e66630259e..656ee36484 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1623,6 +1623,13 @@ au BufNewFile,BufRead *.sml setf sml " Sratus VOS command macro au BufNewFile,BufRead *.cm setf voscm +" Swift +au BufNewFile,BufRead *.swift setf swift +au BufNewFile,BufRead *.swift.gyb setf swiftgyb + +" Swift Intermediate Language +au BufNewFile,BufRead *.sil setf sil + " Sysctl au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl -- cgit From c6ff23d7a0d5ccf0d8995e3204c18df55d28fc7f Mon Sep 17 00:00:00 2001 From: Chris LaRose Date: Sun, 26 Jan 2020 00:24:42 -0800 Subject: terminal: absolute CWD in term:// URI #11289 This makes it possible to restore the working directory of :terminal buffers when reading those buffers from a session file. Fixes #11288 Co-authored-by: Justin M. Keyes --- runtime/lua/vim/shared.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index a71e9878bb..ea1117a906 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -321,7 +321,7 @@ function vim.trim(s) return s:match('^%s*(.*%S)') or '' end ---- Escapes magic chars in a Lua pattern string. +--- Escapes magic chars in a Lua pattern. --- --@see https://github.com/rxi/lume --@param s String to escape -- cgit From 1c3ca4f18fdc403813d8959b49626ac1c99e2c59 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 26 Jan 2020 14:26:01 -0800 Subject: mksession: always unix slashes "/" for filepaths --- runtime/doc/usr_21.txt | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/usr_21.txt b/runtime/doc/usr_21.txt index 99a78e7b05..44d653a1a7 100644 --- a/runtime/doc/usr_21.txt +++ b/runtime/doc/usr_21.txt @@ -339,21 +339,6 @@ window, move the cursor to the filename and press "O". Double clicking with the mouse will also do this. -UNIX AND MS-WINDOWS - -Some people have to do work on MS-Windows systems one day and on Unix another -day. If you are one of them, consider adding "slash" and "unix" to -'sessionoptions'. The session files will then be written in a format that can -be used on both systems. This is the command to put in your |init.vim| file: -> - :set sessionoptions+=unix,slash - -Vim will use the Unix format then, because the MS-Windows Vim can read and -write Unix files, but Unix Vim can't read MS-Windows format session files. -Similarly, MS-Windows Vim understands file names with / to separate names, but -Unix Vim doesn't understand \. - - SESSIONS AND SHADA Sessions store many things, but not the position of marks, contents of -- cgit From 5ede2766c8d994f9e299288d46cefdd12ffee47f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 26 Jan 2020 21:34:32 -0500 Subject: vim-patch:8.2.0158: triggering CompleteDone earlier is not backwards compatible Problem: Triggering CompleteDone earlier is not backwards compatible. (Daniel Hahler) Solution: Add CompleteDonePre instead. https://github.com/vim/vim/commit/3f169ce17e8b779d105c96138a8b4246f2d270b9 --- runtime/doc/autocmd.txt | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 8006a45899..64ca7b6a45 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -358,7 +358,10 @@ Name triggered by ~ |MenuPopup| just before showing the popup menu |CompleteChanged| after popup menu changed, not fired on popup menu hide -|CompleteDone| after Insert mode completion is done +|CompleteDonePre| after Insert mode completion is done, before clearing + info +|CompleteDone| after Insert mode completion is done, after clearing + info |User| to be used in combination with ":doautocmd" |Signal| after Nvim receives a signal @@ -577,7 +580,8 @@ ColorSchemePre Before loading a color scheme. |:colorscheme| CompleteChanged *CompleteChanged* After each time the Insert mode completion menu changed. Not fired on popup menu hide, - use |CompleteDone| for that. + use |CompleteDonePre| or |CompleteDone| for + that. Sets these |v:event| keys: completed_item See |complete-items|. @@ -594,12 +598,22 @@ CompleteChanged *CompleteChanged* The size and position of the popup are also available by calling |pum_getpos()|. + *CompleteDonePre* +CompleteDonePre After Insert mode completion is done. Either + when something was completed or abandoning + completion. |ins-completion| + |complete_info()| can be used, the info is + cleared after triggering CompleteDonePre. + The |v:completed_item| variable contains + information about the completed item. + *CompleteDone* CompleteDone After Insert mode completion is done. Either when something was completed or abandoning completion. |ins-completion| - |complete_info()| can be used, the info is - cleared after triggering CompleteDone. + |complete_info()| cannot be used, the info is + cleared before triggering CompleteDone. Use + CompleteDonePre if you need it. |v:completed_item| gives the completed item. *CursorHold* -- cgit From e956ea767241268861f0a8f7556700516849b112 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Tue, 28 Jan 2020 10:45:25 +0100 Subject: LSP: show diagnostic in qf/loclist #11777 instead of the content of the file at this line. ref https://github.com/neovim/nvim-lsp/issues/69 --- runtime/lua/vim/lsp/util.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index df82e2d412..6a1b73045e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -704,7 +704,7 @@ do end local position_sort = sort_by_key(function(v) - return {v.line, v.character} + return {v.start.line, v.start.character} end) -- Returns the items with the byte position calculated correctly and in sorted @@ -721,17 +721,21 @@ function M.locations_to_items(locations) for _, d in ipairs(locations) do local start = d.range.start local fname = assert(vim.uri_to_fname(d.uri)) - table.insert(grouped[fname], start) + table.insert(grouped[fname], {start = start, msg= d.message }) end + + local keys = vim.tbl_keys(grouped) table.sort(keys) -- TODO(ashkan) I wish we could do this lazily. for _, fname in ipairs(keys) do local rows = grouped[fname] + table.sort(rows, position_sort) local i = 0 for line in io.lines(fname) do - for _, pos in ipairs(rows) do + for _, temp in ipairs(rows) do + local pos = temp.start local row = pos.line if i == row then local col @@ -744,7 +748,7 @@ function M.locations_to_items(locations) filename = fname, lnum = row + 1, col = col + 1; - text = line; + text = temp.msg; }) end end -- cgit From 31f31b40a8af67a3a55e85fa5dfa63d5a5999acc Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 29 Jan 2020 20:44:23 -0500 Subject: vim-patch:8.2.0077: settagstack() cannot truncate at current index Problem: settagstack() cannot truncate at current index. Solution: Add the "t" action. (Yegappan Lakshmanan, closes vim/vim#5417) https://github.com/vim/vim/commit/271fa08a35b8d320d3a40db4ddae83b698fdd4fb --- runtime/doc/eval.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 69b8b3cf39..e0ce83f8d2 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7550,11 +7550,21 @@ settagstack({nr}, {dict} [, {action}]) *settagstack()* {nr} can be the window number or the |window-ID|. For a list of supported items in {dict}, refer to - |gettagstack()| + |gettagstack()|. "curidx" takes effect before changing the tag + stack. *E962* - If {action} is not present or is set to 'r', then the tag - stack is replaced. If {action} is set to 'a', then new entries - from {dict} are pushed onto the tag stack. + How the tag stack is modified depends on the {action} + argument: + - If {action} is not present or is set to 'r', then the tag + stack is replaced. + - If {action} is set to 'a', then new entries from {dict} are + pushed (added) onto the tag stack. + - If {action} is set to 't', then all the entries from the + current entry in the tag stack or "curidx" in {dict} are + removed and then new entries are pushed to the stack. + + The current index is set to one after the length of the tag + stack after the modification. Returns zero for success, -1 for failure. -- cgit From 26199fedca0c90bdfb1b3279de2ad737fd308e52 Mon Sep 17 00:00:00 2001 From: Alkeryn Date: Sat, 1 Feb 2020 23:15:36 +0100 Subject: vim-patch:8.2.0190: detect Kotlin files [ci skip] #11796 Problem: Kotlin files are not recognized. Solution: Detect Kotlin files. (Alkeryn, closes vim/vim#5560) https://github.com/vim/vim/commit/ab067a21b9622513ed75f4801b001606eeaf2474 --- runtime/filetype.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 656ee36484..1121ea4460 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -829,6 +829,9 @@ au BufNewFile,BufRead *.k setf kwt " Kivy au BufNewFile,BufRead *.kv setf kivy +" Kotlin +au BufNewFile,BufRead *.kt,*.ktm,*.kts setf kotlin + " KDE script au BufNewFile,BufRead *.ks setf kscript -- cgit From 9c1a31927d946e6e3d25cb31cbf62e2ba1c0e8f2 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 1 Feb 2020 12:39:52 -0500 Subject: vim-patch:8.2.0161: not recognizing .gv file as dot filetype Problem: Not recognizing .gv file as dot filetype. Solution: Add *.gv to dot pattern. (closes vim/vim#5544) https://github.com/vim/vim/commit/f8ddb25789a6af530e69f499907979dfbff1c1ea --- runtime/filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 1121ea4460..49e1e9909b 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -487,7 +487,7 @@ au BufNewFile,BufRead *.rul au BufNewFile,BufRead *.com call dist#ft#BindzoneCheck('dcl') " DOT -au BufNewFile,BufRead *.dot setf dot +au BufNewFile,BufRead *.dot,*.gv setf dot " Dylan - lid files au BufNewFile,BufRead *.lid setf dylanlid -- cgit From bf85cc09098fcbe1209d0951262ef78ef08b9ac2 Mon Sep 17 00:00:00 2001 From: David Lukes Date: Mon, 27 Jan 2020 09:38:44 +0100 Subject: checkhealth: better $VIRTUAL_ENV validation #11781 fix #11753 close #11781 The virtualenv troubleshooting in the Python provider health checks is supposed to help the user determine whether running Python from Neovim (as in `system('python')` or `system(exepath('python'))`) will use the correct executable when a virtualenv is active. Currently however, it issues spurious warnings in legitimate setups, and conversely, fails to warn about potentially problematic ones. See https://github.com/neovim/neovim/issues/11753#issuecomment-578715584 for a more detailed analysis, but at a high level, this is due to two things: - the virtualenv check is part of the Python provider check defined in `s:check_python`, which uses a roundabout and sometimes erroneous way of determining the Python executable - more generally, it shouldn't be part of the provider check at all, because it's not really related to the Python *provider*, i.e. the Python executable which can communicate with Neovim via `pynvim`, but to the Python the user is editing source files for, which typically shouldn't even have `pynvim` installed This patch reimplements the virtualenv check and factors it out into its own separate function, which is however still kept in `health/provider.vim` alongside the rest of the Python troubleshooting, since troubleshooting all Python-related stuff in one place is probably a good idea in order to alleviate any potential confusion (e.g. users who run only provider checks might be left wondering whether their virtualenv Python was properly detected if the report only shows their global Python as the provider used by Neovim). --- runtime/autoload/health/provider.vim | 81 ++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 12 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index cc7d86d0c1..f602684fb5 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -392,18 +392,6 @@ function! s:check_python(version) abort let python_exe = '' endif - " Check if $VIRTUAL_ENV is valid. - if exists('$VIRTUAL_ENV') && !empty(python_exe) - if $VIRTUAL_ENV ==# matchstr(python_exe, '^\V'.$VIRTUAL_ENV) - call health#report_info('$VIRTUAL_ENV matches executable') - else - call health#report_warn( - \ '$VIRTUAL_ENV exists but appears to be inactive. ' - \ . 'This could lead to unexpected results.', - \ [ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654' ]) - endif - endif - " Diagnostic output call health#report_info('Executable: ' . (empty(python_exe) ? 'Not found' : python_exe)) if len(python_multiple) @@ -497,6 +485,72 @@ function! s:check_for_pyenv() abort return [pyenv_path, pyenv_root] endfunction +" Locate Python executable by running invocation and checking +" sys.executable. +function! s:locate_pythonx(invocation) abort + return s:normalize_path(system(a:invocation + \ . ' -c "from __future__ import print_function; import sys; print(sys.executable, end=\"\")"')) +endfunction + +" If $VIRTUAL_ENV is set, check whether its Python executables will be +" the first on the $PATH of both Neovim and subshells spawned from +" Neovim. +function! s:check_active_virtualenv() abort + call health#report_start('Python active virtualenv') + let errors = [] + " hints are kept as Dictionary keys in order to discard duplicates + let hints = {} + " the virtualenv should contain some Python executables, and those + " executables should be first both on Neovim's path and on the path of + " subshells launched from Neovim + let venv_pythonxs = glob($VIRTUAL_ENV . '/bin/python*', v:true, v:true) + if len(venv_pythonxs) + for venv_pythonx in venv_pythonxs + let venv_pythonx = s:normalize_path(venv_pythonx) + let pythonx_basename = fnamemodify(venv_pythonx, ':t') + let neovim_pythonx = s:locate_pythonx(exepath(pythonx_basename)) + let subshell_pythonx = s:locate_pythonx(pythonx_basename) + if venv_pythonx !=# neovim_pythonx + call add(errors, 'according to Neovim''s $PATH, the ' . pythonx_basename + \ . ' executable is found outside the virtualenv, here: ' . neovim_pythonx) + let hint = 'Problems with Neovim''s $PATH are caused by the virtualenv not being ' + \ . 'properly activated prior to launching Neovim. Close Neovim, activate the virtualenv ' + \ . 'properly, check that invoking Python from the command line launches the correct one, ' + \ . 'and relaunch Neovim.' + let hints[hint] = v:true + endif + if venv_pythonx !=# subshell_pythonx + call add(errors, 'according to the $PATH of subshells launched from Neovim, the ' + \ . pythonx_basename . ' executable is found outside the virtualenv, here: ' + \ . subshell_pythonx) + let hint = 'Problems with the $PATH of subshells launched from Neovim can be ' + \ . 'caused by your shell''s startup files overriding the $PATH previously set by the ' + \ . 'virtualenv. Either prevent them from doing so, or use ' + \ . 'https://vi.stackexchange.com/a/7654/18339 as a workaround.' + let hints[hint] = v:true + endif + endfor + else + call add(errors, 'no Python executables were found in the virtualenv''s bin directory.') + endif + + if len(errors) + call health#report_warn('$VIRTUAL_ENV is set to: ' . $VIRTUAL_ENV) + if len(venv_pythonxs) + call health#report_warn('And its bin directory contains the following executables: ' + \ . join(map(venv_pythonxs, "fnamemodify(v:val, ':t')"), ', ')) + endif + let conj = 'However, ' + for error in errors + call health#report_warn(conj . error) + let conj = 'And ' + endfor + call health#report_warn('So invoking Python may lead to unexpected results.', keys(hints)) + else + call health#report_ok('$VIRTUAL_ENV is active and invoking Python from within Neovim will honor it.') + endif +endfunction + function! s:check_ruby() abort call health#report_start('Ruby provider (optional)') @@ -682,6 +736,9 @@ function! health#provider#check() abort call s:check_clipboard() call s:check_python(2) call s:check_python(3) + if exists('$VIRTUAL_ENV') + call s:check_active_virtualenv() + endif call s:check_ruby() call s:check_node() call s:check_perl() -- cgit From 1b20014972bb24d60bc16f07e5c4066421bc7e0a Mon Sep 17 00:00:00 2001 From: David Lukes Date: Wed, 29 Jan 2020 13:31:37 +0100 Subject: checkhealth: print -> sys.stdout.write Co-Authored-By: Peter Lithammer --- runtime/autoload/health/provider.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index f602684fb5..246bfb5322 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -489,7 +489,7 @@ endfunction " sys.executable. function! s:locate_pythonx(invocation) abort return s:normalize_path(system(a:invocation - \ . ' -c "from __future__ import print_function; import sys; print(sys.executable, end=\"\")"')) + \ . ' -c "import sys; sys.stdout.write(sys.executable)"')) endfunction " If $VIRTUAL_ENV is set, check whether its Python executables will be -- cgit From 370a33a85dae069d2f8cf40472fed2cf9cee3ea6 Mon Sep 17 00:00:00 2001 From: David Lukes Date: Fri, 31 Jan 2020 11:09:06 +0100 Subject: checkhealth: bin directory is Scripts/ on Windows --- runtime/autoload/health/provider.vim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 246bfb5322..abc301c4c3 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -503,7 +503,8 @@ function! s:check_active_virtualenv() abort " the virtualenv should contain some Python executables, and those " executables should be first both on Neovim's path and on the path of " subshells launched from Neovim - let venv_pythonxs = glob($VIRTUAL_ENV . '/bin/python*', v:true, v:true) + let bin_dir = has('win32') ? '/Scripts' : '/bin' + let venv_pythonxs = glob($VIRTUAL_ENV . bin_dir . '/python*', v:true, v:true) if len(venv_pythonxs) for venv_pythonx in venv_pythonxs let venv_pythonx = s:normalize_path(venv_pythonx) @@ -531,13 +532,14 @@ function! s:check_active_virtualenv() abort endif endfor else - call add(errors, 'no Python executables were found in the virtualenv''s bin directory.') + call add(errors, 'no Python executables were found in the virtualenv''s ' + \ . bin_dir . ' directory.') endif if len(errors) call health#report_warn('$VIRTUAL_ENV is set to: ' . $VIRTUAL_ENV) if len(venv_pythonxs) - call health#report_warn('And its bin directory contains the following executables: ' + call health#report_warn('And its ' . bin_dir . ' directory contains the following executables: ' \ . join(map(venv_pythonxs, "fnamemodify(v:val, ':t')"), ', ')) endif let conj = 'However, ' -- cgit From 3cd5a8d149e74255f89a8424b20622c9dc79daca Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 2 Feb 2020 16:19:32 -0800 Subject: checkhealth: cleanup, brevity --- runtime/autoload/health/provider.vim | 124 +++++++++++++++++------------------ 1 file changed, 61 insertions(+), 63 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index abc301c4c3..1dd7f57f5f 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -163,7 +163,7 @@ function! s:check_clipboard() abort endif endfunction -" Get the latest Neovim Python client (pynvim) version from PyPI. +" Get the latest Nvim Python client (pynvim) version from PyPI. function! s:latest_pypi_version() abort let pypi_version = 'unable to get pypi response' let pypi_response = s:download('https://pypi.python.org/pypi/pynvim/json') @@ -180,7 +180,7 @@ endfunction " Get version information using the specified interpreter. The interpreter is " used directly in case breaking changes were introduced since the last time -" Neovim's Python client was updated. +" Nvim's Python client was updated. " " Returns: [ " {python executable version}, @@ -224,7 +224,7 @@ function! s:version_info(python) abort \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'], \ '', 1, 1) if empty(nvim_version) - let nvim_version = 'unable to find neovim Python module version' + let nvim_version = 'unable to find pynvim module version' let base = fnamemodify(nvim_path, ':h') let metas = glob(base.'-*/METADATA', 1, 1) \ + glob(base.'-*/PKG-INFO', 1, 1) @@ -363,7 +363,7 @@ function! s:check_python(version) abort \ && !empty(pyenv_root) && resolve(python_exe) !~# '^'.pyenv_root.'/' call health#report_warn('pyenv is not set up optimally.', [ \ printf('Create a virtualenv specifically ' - \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid ' + \ . 'for Nvim using pyenv, and set `g:%s`. This will avoid ' \ . 'the need to install the pynvim module in each ' \ . 'version/virtualenv.', host_prog_var) \ ]) @@ -377,7 +377,7 @@ function! s:check_python(version) abort if resolve(python_exe) !~# '^'.venv_root.'/' call health#report_warn('Your virtualenv is not set up optimally.', [ \ printf('Create a virtualenv specifically ' - \ . 'for Neovim and use `g:%s`. This will avoid ' + \ . 'for Nvim and use `g:%s`. This will avoid ' \ . 'the need to install the pynvim module in each ' \ . 'virtualenv.', host_prog_var) \ ]) @@ -485,71 +485,71 @@ function! s:check_for_pyenv() abort return [pyenv_path, pyenv_root] endfunction -" Locate Python executable by running invocation and checking -" sys.executable. -function! s:locate_pythonx(invocation) abort +" Resolves Python executable path by invoking and checking `sys.executable`. +function! s:python_exepath(invocation) abort return s:normalize_path(system(a:invocation \ . ' -c "import sys; sys.stdout.write(sys.executable)"')) endfunction -" If $VIRTUAL_ENV is set, check whether its Python executables will be -" the first on the $PATH of both Neovim and subshells spawned from -" Neovim. -function! s:check_active_virtualenv() abort - call health#report_start('Python active virtualenv') +" Checks that $VIRTUAL_ENV Python executables are found at front of $PATH in +" Nvim and subshells. +function! s:check_virtualenv() abort + call health#report_start('Python virtualenv') + if !exists('$VIRTUAL_ENV') + call health#report_ok('no $VIRTUAL_ENV') + return + endif let errors = [] - " hints are kept as Dictionary keys in order to discard duplicates + " Keep hints as dict keys in order to discard duplicates. let hints = {} - " the virtualenv should contain some Python executables, and those - " executables should be first both on Neovim's path and on the path of - " subshells launched from Neovim + " The virtualenv should contain some Python executables, and those + " executables should be first both on Nvim's $PATH and the $PATH of + " subshells launched from Nvim. let bin_dir = has('win32') ? '/Scripts' : '/bin' - let venv_pythonxs = glob($VIRTUAL_ENV . bin_dir . '/python*', v:true, v:true) - if len(venv_pythonxs) - for venv_pythonx in venv_pythonxs - let venv_pythonx = s:normalize_path(venv_pythonx) - let pythonx_basename = fnamemodify(venv_pythonx, ':t') - let neovim_pythonx = s:locate_pythonx(exepath(pythonx_basename)) - let subshell_pythonx = s:locate_pythonx(pythonx_basename) - if venv_pythonx !=# neovim_pythonx - call add(errors, 'according to Neovim''s $PATH, the ' . pythonx_basename - \ . ' executable is found outside the virtualenv, here: ' . neovim_pythonx) - let hint = 'Problems with Neovim''s $PATH are caused by the virtualenv not being ' - \ . 'properly activated prior to launching Neovim. Close Neovim, activate the virtualenv ' - \ . 'properly, check that invoking Python from the command line launches the correct one, ' - \ . 'and relaunch Neovim.' + let venv_bins = glob($VIRTUAL_ENV . bin_dir . '/python*', v:true, v:true) + if len(venv_bins) + for venv_bin in venv_bins + let venv_bin = s:normalize_path(venv_bin) + let py_bin_basename = fnamemodify(venv_bin, ':t') + let nvim_py_bin = s:python_exepath(exepath(py_bin_basename)) + let subshell_py_bin = s:python_exepath(py_bin_basename) + if venv_bin !=# nvim_py_bin + call add(errors, '$PATH yields this '.py_bin_basename.' executable: '.nvim_py_bin) + let hint = '$PATH ambiguities arise if the virtualenv is not ' + \.'properly activated prior to launching Nvim. Close Nvim, activate the virtualenv, ' + \.'check that invoking Python from the command line launches the correct one, ' + \.'then relaunch Nvim.' let hints[hint] = v:true endif - if venv_pythonx !=# subshell_pythonx - call add(errors, 'according to the $PATH of subshells launched from Neovim, the ' - \ . pythonx_basename . ' executable is found outside the virtualenv, here: ' - \ . subshell_pythonx) - let hint = 'Problems with the $PATH of subshells launched from Neovim can be ' - \ . 'caused by your shell''s startup files overriding the $PATH previously set by the ' - \ . 'virtualenv. Either prevent them from doing so, or use ' - \ . 'https://vi.stackexchange.com/a/7654/18339 as a workaround.' + if venv_bin !=# subshell_py_bin + call add(errors, '$PATH in subshells yields this ' + \.py_bin_basename . ' executable: '.subshell_py_bin) + 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' let hints[hint] = v:true endif endfor else - call add(errors, 'no Python executables were found in the virtualenv''s ' - \ . bin_dir . ' directory.') + call add(errors, 'no Python executables found in the virtualenv '.bin_dir.' directory.') endif if len(errors) - call health#report_warn('$VIRTUAL_ENV is set to: ' . $VIRTUAL_ENV) - if len(venv_pythonxs) - call health#report_warn('And its ' . bin_dir . ' directory contains the following executables: ' - \ . join(map(venv_pythonxs, "fnamemodify(v:val, ':t')"), ', ')) + let msg = '$VIRTUAL_ENV is set to: '.$VIRTUAL_ENV + if len(venv_bins) + let msg .= "\nAnd its ".bin_dir.' directory contains: ' + \.join(map(venv_bins, "fnamemodify(v:val, ':t')"), ', ') endif - let conj = 'However, ' + let conj = "\nBut " for error in errors - call health#report_warn(conj . error) - let conj = 'And ' + let msg .= conj.error + let conj = "\nAnd " endfor - call health#report_warn('So invoking Python may lead to unexpected results.', keys(hints)) + let msg .= "\nSo invoking Python may lead to unexpected results." + call health#report_warn(msg, keys(hints)) else - call health#report_ok('$VIRTUAL_ENV is active and invoking Python from within Neovim will honor it.') + call health#report_ok('$VIRTUAL_ENV provides :python, :python3, et al.') endif endfunction @@ -623,7 +623,7 @@ function! s:check_node() abort let node_v = get(split(s:system('node -v'), "\n"), 0, '') call health#report_info('Node.js: '. node_v) if s:shell_error || s:version_cmp(node_v[1:], '6.0.0') < 0 - call health#report_warn('Neovim node.js host does not support '.node_v) + call health#report_warn('Nvim node.js host does not support '.node_v) " Skip further checks, they are nonsense if nodejs is too old. return endif @@ -638,7 +638,7 @@ function! s:check_node() abort \ 'Run in shell (if you use yarn): yarn global add neovim']) return endif - call health#report_info('Neovim node.js host: '. host) + call health#report_info('Nvim node.js host: '. host) let manager = executable('npm') ? 'npm' : 'yarn' let latest_npm_cmd = has('win32') ? @@ -693,7 +693,7 @@ function! s:check_perl() abort let perl_v = get(split(s:system(['perl', '-W', '-e', 'print $^V']), "\n"), 0, '') call health#report_info('Perl: '. perl_v) if s:shell_error - call health#report_warn('Neovim perl host does not support '.perl_v) + call health#report_warn('Nvim perl host does not support '.perl_v) " Skip further checks, they are nonsense if perl is too old. return endif @@ -704,7 +704,7 @@ function! s:check_perl() abort \ ['Run in shell: cpanm Neovim::Ext']) return endif - call health#report_info('Neovim perl host: '. host) + call health#report_info('Nvim perl host: '. host) let latest_cpan_cmd = 'cpanm --info Neovim::Ext' let latest_cpan = s:system(latest_cpan_cmd) @@ -735,13 +735,11 @@ function! s:check_perl() abort endfunction function! health#provider#check() abort - call s:check_clipboard() - call s:check_python(2) - call s:check_python(3) - if exists('$VIRTUAL_ENV') - call s:check_active_virtualenv() - endif - call s:check_ruby() - call s:check_node() - call s:check_perl() + " call s:check_clipboard() + " call s:check_python(2) + " call s:check_python(3) + call s:check_virtualenv() + " call s:check_ruby() + " call s:check_node() + " call s:check_perl() endfunction -- cgit From c8abe931db4264e4805cdd9e6564df5d3057ccea Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 2 Feb 2020 18:19:42 -0800 Subject: checkhealth: avoid irrelevant virtualenv executables --- runtime/autoload/health/provider.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 1dd7f57f5f..c0faf2e9e4 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -507,6 +507,8 @@ function! s:check_virtualenv() abort " subshells launched from Nvim. let bin_dir = has('win32') ? '/Scripts' : '/bin' let venv_bins = glob($VIRTUAL_ENV . bin_dir . '/python*', v:true, v:true) + " XXX: Remove irrelevant executables found in bin/. + let venv_bins = filter(venv_bins, 'v:val !~# "python-config"') if len(venv_bins) for venv_bin in venv_bins let venv_bin = s:normalize_path(venv_bin) -- cgit From 983086f42e5841b84d5b24623a51ffc8c725e78f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 2 Feb 2020 18:33:47 -0800 Subject: checkhealth: fix accidental change [ci skip] --- runtime/autoload/health/provider.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index c0faf2e9e4..1d720b5876 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -737,11 +737,11 @@ function! s:check_perl() abort endfunction function! health#provider#check() abort - " call s:check_clipboard() - " call s:check_python(2) - " call s:check_python(3) + call s:check_clipboard() + call s:check_python(2) + call s:check_python(3) call s:check_virtualenv() - " call s:check_ruby() - " call s:check_node() - " call s:check_perl() + call s:check_ruby() + call s:check_node() + call s:check_perl() endfunction -- cgit From 405f49a9b16c5668a033b8be959564abc5f852ba Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Wed, 5 Feb 2020 06:07:01 -0300 Subject: doc: Fix {spell,mlang}.txt files text encoding #11814 These had broken encodings, set it to UTF-8. All remianing Neovim non-ASCII documentation files are UTF-8 encoded. And so are their Vim original versions. --- runtime/doc/mlang.txt | 4 ++-- runtime/doc/spell.txt | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt index 03c48b962d..2a10a7051d 100644 --- a/runtime/doc/mlang.txt +++ b/runtime/doc/mlang.txt @@ -185,8 +185,8 @@ you can do it without restarting Vim: > :source $VIMRUNTIME/menu.vim Each part of a menu path is translated separately. The result is that when -"Help" is translated to "Hilfe" and "Overview" to "berblick" then -"Help.Overview" will be translated to "Hilfe.berblick". +"Help" is translated to "Hilfe" and "Overview" to "Überblick" then +"Help.Overview" will be translated to "Hilfe.Überblick". ============================================================================== 3. Scripts *multilang-scripts* diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 110c6ef221..1f1599f560 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -888,9 +888,9 @@ when using "cp1250" on Unix. *spell-LOW* *spell-UPP* Three lines in the affix file are needed. Simplistic example: - FOL ~ - LOW ~ - UPP ~ + FOL áëñ ~ + LOW áëñ ~ + UPP ÁËÑ ~ All three lines must have exactly the same number of characters. @@ -905,9 +905,9 @@ The "UPP" line specifies the characters with upper-case. That is, a character is upper-case where it's different from the character at the same position in "FOL". -An exception is made for the German sharp s . The upper-case version is +An exception is made for the German sharp s ß. The upper-case version is "SS". In the FOL/LOW/UPP lines it should be included, so that it's recognized -as a word character, but use the character in all three. +as a word character, but use the ß character in all three. ASCII characters should be omitted, Vim always handles these in the same way. When the encoding is UTF-8 no word characters need to be specified. @@ -1377,7 +1377,7 @@ suggestions would spend most time trying all kind of weird compound words. *spell-SYLLABLE* The SYLLABLE item defines characters or character sequences that are used to count the number of syllables in a word. Example: - SYLLABLE aeiouy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui ~ + SYLLABLE aáeéiíoóöõuúüûy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui ~ Before the first slash is the set of characters that are counted for one syllable, also when repeated and mixed, until the next character that is not @@ -1458,8 +1458,8 @@ alike. This is mostly used for a letter with different accents. This is used to prefer suggestions with these letters substituted. Example: MAP 2 ~ - MAP e ~ - MAP u ~ + MAP eéëêè ~ + MAP uüùúû ~ The first line specifies the number of MAP lines following. Vim ignores the number, but the line must be there. -- cgit From 00c57c98dfb2df58875a3d9ad8fc557ec9a24cba Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 25 Jan 2020 13:43:41 +0100 Subject: treesitter: add standard &rtp/parser/ search path for parsers --- runtime/doc/lua.txt | 8 +++++--- runtime/lua/vim/treesitter.lua | 31 ++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index af1f4a8c1f..c113a70027 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -494,10 +494,12 @@ VIM.TREESITTER *lua-treesitter* Nvim integrates the tree-sitter library for incremental parsing of buffers. Currently Nvim does not provide the tree-sitter parsers, instead these must -be built separately, for instance using the tree-sitter utility. -The parser is loaded into nvim using > +be built separately, for instance using the tree-sitter utility. The only +exception is a C parser being included in official builds for testing +purposes. Parsers are searched for as `parser/{lang}.*` in any 'runtimepath' +directory. A parser can also be loaded manually using a full path: > - vim.treesitter.add_language("/path/to/c_parser.so", "c") + vim.treesitter.require_language("python", "/path/to/python.so") diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index aa8b8fcdd1..0d0e22adb3 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -31,8 +31,6 @@ function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_ end local M = { - add_language=vim._ts_add_language, - inspect_language=vim._ts_inspect_language, parse_query = vim._ts_parse_query, } @@ -45,12 +43,34 @@ setmetatable(M, { end }) -function M.create_parser(bufnr, ft, id) +function M.require_language(lang, path) + if vim._ts_has_language(lang) then + return true + end + if path == nil then + local fname = 'parser/' .. lang .. '.*' + local paths = a.nvim_get_runtime_file(fname, false) + if #paths == 0 then + -- TODO(bfredl): help tag? + error("no parser for '"..lang.."' language") + end + path = paths[1] + end + vim._ts_add_language(path, lang) +end + +function M.inspect_language(lang) + M.require_language(lang) + return vim._ts_inspect_language(lang) +end + +function M.create_parser(bufnr, lang, id) + M.require_language(lang) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end - local self = setmetatable({bufnr=bufnr, lang=ft, valid=false}, Parser) - self._parser = vim._create_ts_parser(ft) + local self = setmetatable({bufnr=bufnr, lang=lang, valid=false}, Parser) + self._parser = vim._create_ts_parser(lang) self.change_cbs = {} self:parse() -- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is @@ -94,6 +114,7 @@ local Query = {} Query.__index = Query function M.parse_query(lang, query) + M.require_language(lang) local self = setmetatable({}, Query) self.query = vim._ts_parse_query(lang, query) self.info = self.query:inspect() -- cgit From dd8b29cfe25604c062b76bb3a9347c5d740365ba Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sun, 9 Feb 2020 15:51:02 +0900 Subject: LSP: set InitializeParams.rootPath value #11838 InitializeParams.rootPath is deprecated now. But some language servers still use it. --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index cfa208f21c..304686a816 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -452,7 +452,7 @@ function lsp.start_client(config) -- The rootPath of the workspace. Is null if no folder is open. -- -- @deprecated in favour of rootUri. - rootPath = nil; + rootPath = config.root_dir; -- The rootUri of the workspace. Is null if no folder is open. If both -- `rootPath` and `rootUri` are set `rootUri` wins. rootUri = vim.uri_from_fname(config.root_dir); -- cgit From 174f7a29801be2d6680c8e7e5a3625c8c7dfe458 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 10 Feb 2020 01:05:42 +0100 Subject: lsp: Support text edit on inactive buffer (#11843) Using `vim.lsp.buf.rename()` can result in receiving a TextEdit that affects a file for which there is no active or loaded buffer. In that case `api.nvim_buf_get_lines(...)` returned an empty result, leading to an error. Closes https://github.com/neovim/neovim/issues/11790 --- runtime/lua/vim/lsp/util.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6a1b73045e..428874f2b7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -88,6 +88,9 @@ function M.apply_text_edits(text_edits, bufnr) -- Reverse sort the orders so we can apply them without interfering with -- eachother. Also add i as a sort key to mimic a stable sort. table.sort(cleaned, edit_sort_key) + if not api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol') local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) <= finish_line + 1 -- cgit From 95fd28f4a1ef4e298695e0204bdb5b347aa0f57c Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 17 Jan 2020 22:27:17 +0100 Subject: treesitter: use internal "decorations" buffer --- runtime/lua/vim/tshighlighter.lua | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/tshighlighter.lua b/runtime/lua/vim/tshighlighter.lua index 1544ecbf49..9d094f0f9a 100644 --- a/runtime/lua/vim/tshighlighter.lua +++ b/runtime/lua/vim/tshighlighter.lua @@ -85,7 +85,6 @@ end function TSHighlighter:on_window(_, _win, _buf, _topline, botline) self.iter = nil - self.active_nodes = {} self.nextrow = 0 self.botline = botline self.redraw_count = self.redraw_count + 1 @@ -107,34 +106,13 @@ function TSHighlighter:on_line(_, _win, buf, line) end local start_row, start_col, end_row, end_col = node:range() local hl = self.id_map[capture] - if hl > 0 and active then - if start_row == line and end_row == line then - a.nvim__put_attr(hl, start_col, end_col) - elseif end_row >= line then - -- TODO(bfredl): this is quite messy. Togheter with multiline bufhl we should support - -- luahl generating multiline highlights (and other kinds of annotations) - self.active_nodes[{hl=hl, start_row=start_row, start_col=start_col, end_row=end_row, end_col=end_col}] = true - end + if hl > 0 and active and end_row >= line then + a.nvim__put_attr(hl, start_row, start_col, end_row, end_col) end if start_row > line then self.nextrow = start_row end end - for node,_ in pairs(self.active_nodes) do - if node.start_row <= line and node.end_row >= line then - local start_col, end_col = node.start_col, node.end_col - if node.start_row < line then - start_col = 0 - end - if node.end_row > line then - end_col = 9000 - end - a.nvim__put_attr(node.hl, start_col, end_col) - end - if node.end_row <= line then - self.active_nodes[node] = nil - end - end self.line_count[line] = (self.line_count[line] or 0) + 1 --return tostring(self.line_count[line]) end -- cgit From 5d5b068d5bdd2fe3787eb619097a5af08ef929e2 Mon Sep 17 00:00:00 2001 From: Jesse-Bakker Date: Tue, 11 Feb 2020 07:53:14 +0100 Subject: LSP: Refine formatting tabSize #11834 Use the logic explained in the softtabstop help section for defining the tabSize parameter in formatting requests. This means that: - if softtabstop is 0, tabstop is used - if softtabstop < 0, shiftwidth is used - if softtabstop > 0, softtabstop is used When inserting spaces instead of tabs, softtabstop is used in vim. Therefor it would be more logical to use it when formatting instead of the current tabstop. --- runtime/lua/vim/lsp/buf.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index a6a05fb095..19deb5df45 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -68,8 +68,9 @@ end function M.formatting(options) validate { options = {options, 't', true} } + local sts = vim.bo.softtabstop; options = vim.tbl_extend('keep', options or {}, { - tabSize = vim.bo.tabstop; + tabSize = (sts > 0 and sts) or (sts < 0 and vim.bo.shiftwidth) or vim.bo.tabstop; insertSpaces = vim.bo.expandtab; }) local params = { @@ -85,8 +86,9 @@ function M.range_formatting(options, start_pos, end_pos) start_pos = {start_pos, 't', true}; end_pos = {end_pos, 't', true}; } + local sts = vim.bo.softtabstop; options = vim.tbl_extend('keep', options or {}, { - tabSize = vim.bo.tabstop; + tabSize = (sts > 0 and sts) or (sts < 0 and vim.bo.shiftwidth) or vim.bo.tabstop; insertSpaces = vim.bo.expandtab; }) local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<')) -- cgit From 58ec72f9fdfd186e5154ce82be1da7c65b9c8ea0 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Wed, 12 Feb 2020 06:48:25 +0100 Subject: LSP: rename validate_command to _cmd_parts #11847 and now only accepts a list of strings (instead of string or list). --- runtime/lua/vim/lsp.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 304686a816..e5b6653346 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -121,13 +121,9 @@ local function validate_encoding(encoding) or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) end -local function validate_command(input) +function lsp._cmd_parts(input) local cmd, cmd_args - if type(input) == 'string' then - -- Use a shell to execute the command if it is a string. - cmd = vim.api.nvim_get_option('shell') - cmd_args = {vim.api.nvim_get_option('shellcmdflag'), input} - elseif vim.tbl_islist(input) then + if vim.tbl_islist(input) then cmd = input[1] cmd_args = {} -- Don't mutate our input. @@ -138,7 +134,7 @@ local function validate_command(input) end end else - error("cmd type must be string or list.") + error("cmd type must be list.") end return cmd, cmd_args end @@ -166,7 +162,7 @@ local function validate_client_config(config) before_init = { config.before_init, "f", true }; offset_encoding = { config.offset_encoding, "s", true }; } - local cmd, cmd_args = validate_command(config.cmd) + local cmd, cmd_args = lsp._cmd_parts(config.cmd) local offset_encoding = valid_encodings.UTF16 if config.offset_encoding then offset_encoding = validate_encoding(config.offset_encoding) -- cgit From 4813ad48cd12a03ca50c01ac1b20518bf4df57f2 Mon Sep 17 00:00:00 2001 From: erw7 Date: Mon, 20 May 2019 11:57:45 +0900 Subject: vim-patch:8.1.0027: difficult to make a plugin that feeds a line to a job Problem: Difficult to make a plugin that feeds a line to a job. Solution: Add the nitial code for the "prompt" buftype. https://github.com/vim/vim/commit/f273245f6433d5d43a5671306b520a3230c35787 --- runtime/doc/eval.txt | 42 +++++++++++++++++++++++++++++++++++++++--- runtime/doc/options.txt | 2 ++ 2 files changed, 41 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e0ce83f8d2..a9d9c83867 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2280,6 +2280,9 @@ pathshorten({expr}) String shorten directory names in a path pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} printf({fmt}, {expr1}...) String format text +prompt_addtext({buf}, {expr}) none add text to a prompt buffer +prompt_setprompt({buf}, {text}) none set prompt text +prompt_setcallback({buf}, {expr}) none set prompt callback function pum_getpos() Dict position and size of pum if visible pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression @@ -2290,7 +2293,7 @@ range({expr} [, {max} [, {stride}]]) readdir({dir} [, {expr}]) List file names in {dir} selected by {expr} readfile({fname} [, {binary} [, {max}]]) List get list of lines from file {fname} -reg_executing() Number get the executing register name +reg_executing() String get the executing register name reg_recording() String get the recording register name reltime([{start} [, {end}]]) List get time value reltimefloat({time}) Float turn the time value into a Float @@ -4541,7 +4544,7 @@ getline({lnum} [, {end}]) from the current buffer. Example: > getline(1) < When {lnum} is a String that doesn't start with a - digit, line() is called to translate the String into a Number. + digit, |line()| is called to translate the String into a Number. To get the line under the cursor: > getline(".") < When {lnum} is smaller than 1 or bigger than the number of @@ -6541,6 +6544,40 @@ printf({fmt}, {expr1} ...) *printf()* of "%" items. If there are not sufficient or too many arguments an error is given. Up to 18 arguments can be used. +prompt_setprompt({buf}, {text}) *prompt_setprompt()* + Set prompt for buffer {buf} to {text}. You most likely want + {text} to end in a space. + The result is only visible if {buf} has 'buftype' set to + "prompt". Example: > + call prompt_setprompt(bufnr(''), 'command: ') + + +prompt_setcallback({buf}, {expr}) *prompt_setcallback()* + Set prompt callback for buffer {buf} to {expr}. This has only + effect if {buf} has 'buftype' set to "prompt". + The callback is invoked when pressing Enter. The current + buffer will always be the prompt buffer. A new line for a + prompt is added before invoking the callback, thus the prompt + for which the callback was invoked will be in the last but one + line. + If the callback wants to add text to the buffer, it must + insert it above the last line, since that is where the current + prompt is. This can also be done asynchronously. + The callback is invoked with one argument, which is the text + that was entered at the prompt. This can be an empty string + if the user only typed Enter. + Example: > + call prompt_setcallback(bufnr(''), function('s:TextEntered')) + func s:TextEntered(text) + if a:text == 'exit' || a:text == 'quit' + stopinsert + close + else + call append(line('$') - 1, 'Entered: "' . a:text . '"') + " Reset 'modified' to allow the buffer to be closed. + set nomodified + endif + endfunc pum_getpos() *pum_getpos()* If the popup menu (see |ins-completion-menu|) is not visible, @@ -6554,7 +6591,6 @@ pum_getpos() *pum_getpos()* scrollbar |TRUE| if visible The values are the same as in |v:event| during |CompleteChanged|. - pumvisible() *pumvisible()* Returns non-zero when the popup menu is visible, zero otherwise. See |ins-completion-menu|. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 70af23ee29..7107a0135d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1093,6 +1093,8 @@ A jump table for the options with a short description can be found at |Q_op|. nowrite buffer will not be written quickfix list of errors |:cwindow| or locations |:lwindow| terminal |terminal-emulator| buffer + prompt buffer where only the last line can be edited, meant + to be used by a plugin, see |prompt-buffer| This option is used together with 'bufhidden' and 'swapfile' to specify special kinds of buffers. See |special-buffers|. -- cgit From fe395ae210914eeec0f8592268b1960cb1b819b8 Mon Sep 17 00:00:00 2001 From: erw7 Date: Thu, 23 May 2019 06:38:02 +0900 Subject: vim-patch:8.1.0069: cannot handle pressing CTRL-C in a prompt buffer Problem: Cannot handle pressing CTRL-C in a prompt buffer. Solution: Add prompt_setinterrupt(). https://github.com/vim/vim/commit/0e5979a6d491f68c4a8c86fab489016919329a6b --- runtime/doc/eval.txt | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index a9d9c83867..b0b684064e 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2281,8 +2281,9 @@ pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} printf({fmt}, {expr1}...) String format text prompt_addtext({buf}, {expr}) none add text to a prompt buffer -prompt_setprompt({buf}, {text}) none set prompt text prompt_setcallback({buf}, {expr}) none set prompt callback function +prompt_setinterrupt({buf}, {text}) none set prompt interrupt function +prompt_setprompt({buf}, {text}) none set prompt text pum_getpos() Dict position and size of pum if visible pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression @@ -6544,17 +6545,11 @@ printf({fmt}, {expr1} ...) *printf()* of "%" items. If there are not sufficient or too many arguments an error is given. Up to 18 arguments can be used. -prompt_setprompt({buf}, {text}) *prompt_setprompt()* - Set prompt for buffer {buf} to {text}. You most likely want - {text} to end in a space. - The result is only visible if {buf} has 'buftype' set to - "prompt". Example: > - call prompt_setprompt(bufnr(''), 'command: ') - - prompt_setcallback({buf}, {expr}) *prompt_setcallback()* - Set prompt callback for buffer {buf} to {expr}. This has only + Set prompt callback for buffer {buf} to {expr}. When {expr} + is an empty string the callback is removed. This has only effect if {buf} has 'buftype' set to "prompt". + The callback is invoked when pressing Enter. The current buffer will always be the prompt buffer. A new line for a prompt is added before invoking the callback, thus the prompt @@ -6579,6 +6574,22 @@ prompt_setcallback({buf}, {expr}) *prompt_setcallback()* endif endfunc +prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()* + Set a callback for buffer {buf} to {expr}. When {expr} is an + empty string the callback is removed. This has only effect if + {buf} has 'buftype' set to "prompt". + + This callback will be invoked when pressing CTRL-C in Insert + mode. Without setting a callback Vim will exit Insert mode, + as in any buffer. + +prompt_setprompt({buf}, {text}) *prompt_setprompt()* + Set prompt for buffer {buf} to {text}. You most likely want + {text} to end in a space. + The result is only visible if {buf} has 'buftype' set to + "prompt". Example: > + call prompt_setprompt(bufnr(''), 'command: ') + pum_getpos() *pum_getpos()* If the popup menu (see |ins-completion-menu|) is not visible, returns an empty |Dictionary|, otherwise, returns a @@ -6591,6 +6602,7 @@ pum_getpos() *pum_getpos()* scrollbar |TRUE| if visible The values are the same as in |v:event| during |CompleteChanged|. + pumvisible() *pumvisible()* Returns non-zero when the popup menu is visible, zero otherwise. See |ins-completion-menu|. -- cgit From b015c4741caef37b3f89661e1d5d6a4a033b2856 Mon Sep 17 00:00:00 2001 From: erw7 Date: Thu, 23 May 2019 19:30:28 +0900 Subject: vim-patch:8.1.0071: terminal debugger only works with the terminal feature Problem: Terminal debugger only works with the terminal feature. Solution: Make it also work with a prompt buffer. Makes it possible to use on MS-Windows. Various other improvements. (closes vim/vim#3012) https://github.com/vim/vim/commit/b3307b5e7e7bd3962b0d5c61a94e638564c146b0 --- runtime/doc/nvim_terminal_emulator.txt | 17 ++ .../pack/dist/opt/termdebug/plugin/termdebug.vim | 229 +++++++++++++++++++-- 2 files changed, 230 insertions(+), 16 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 1a5e6421e7..55c5335a60 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -307,6 +307,23 @@ Other commands ~ isn't one +Prompt mode ~ + *termdebug-prompt* +When on MS-Windows, gdb will run in a buffer with 'buftype' set to "prompt". +This works slightly differently: +- The gdb window will be in Insert mode while typing commands. Go to Normal + mode with , then you can move around in the buffer, copy/paste, etc. + Go back to editing the gdb command with any command that starts Insert mode, + such as `a` or `i`. +- The program being debugged will run in a separate window. On MS-Windows + this is a new console window. On Unix, if the |+terminal| feature is + available a Terminal window will be opened to run the debugged program in. + + *termdebug_use_prompt* +Prompt mode can be used even when the |+terminal| feature is present with: > + let g:termdebug_use_prompt = 1 + + Communication ~ *termdebug-communication* There is another, hidden, buffer, which is used for Vim to communicate with diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 7a757ef7d6..63c167d79a 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -37,7 +37,7 @@ " For neovim compatibility, the vim specific calls were replaced with neovim " specific calls: " term_start -> term_open -" term_sendkeys -> jobsend +" term_sendkeys -> chansend " term_getline -> getbufline " job_info && term_getjob -> using linux command ps to get the tty " balloon -> nvim floating window @@ -47,8 +47,6 @@ " https://github.com/autozimu/LanguageClient-neovim/blob/0ed9b69dca49c415390a8317b19149f97ae093fa/autoload/LanguageClient.vim#L304 " " Neovim terminal also works seamlessly on windows, which is why the ability -" to use the prompt buffer was removed. -" " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" @@ -57,6 +55,12 @@ if exists(':Termdebug') finish endif +" The terminal feature does not work with gdb on win32. +if !has('win32') + let s:way = 'terminal' +else + let s:way = 'prompt' +endif let s:keepcpo = &cpo set cpo&vim @@ -138,7 +142,19 @@ func s:StartDebug_internal(dict) let s:vertical = 0 endif - call s:StartDebug_term(a:dict) + " Override using a terminal window by setting g:termdebug_use_prompt to 1. + let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt + if !has('win32') && !use_prompt + let s:way = 'terminal' + else + let s:way = 'prompt' + endif + + if s:way == 'prompt' + call s:StartDebug_prompt(a:dict) + else + call s:StartDebug_term(a:dict) + endif endfunc " Use when debugger didn't start or ended. @@ -214,11 +230,11 @@ func s:StartDebug_term(dict) " Set arguments to be run if len(proc_args) - call jobsend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") + call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") endif " Connect gdb to the communication pty, using the GDB/MI interface - call jobsend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") + call chansend(s:gdb_job_id, 'new-ui mi ' . commpty . "\r") " Wait for the response to show up, users may not notice the error and wonder " why the debugger doesn't work. @@ -275,6 +291,100 @@ func s:StartDebug_term(dict) call s:StartDebugCommon(a:dict) endfunc +func s:StartDebug_prompt(dict) + " Open a window with a prompt buffer to run gdb in. + if s:vertical + vertical new + else + new + endif + let s:gdbwin = win_getid(winnr()) + let s:promptbuf = bufnr('') + call prompt_setprompt(s:promptbuf, 'gdb> ') + set buftype=prompt + file gdb + call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) + call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) + + if s:vertical + " Assuming the source code window will get a signcolumn, use two more + " columns for that, thus one less for the terminal window. + exe (&columns / 2 - 1) . "wincmd |" + endif + + " Add -quiet to avoid the intro message causing a hit-enter prompt. + let gdb_args = get(a:dict, 'gdb_args', []) + let proc_args = get(a:dict, 'proc_args', []) + + let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args + "call ch_log('executing "' . join(cmd) . '"') + + let s:gdbjob = jobstart(cmd, { + \ 'on_exit': function('s:EndPromptDebug'), + \ 'on_stdout': function('s:GdbOutCallback'), + \ }) + if s:gdbjob == 0 + echoerr 'invalid argument (or job table is full) while starting gdb job' + exe 'bwipe! ' . s:ptybuf + return + elseif s:gdbjob == -1 + echoerr 'Failed to start the gdb job' + call s:CloseBuffers() + return + endif + + " Interpret commands while the target is running. This should usualy only + " be exec-interrupt, since many commands don't work properly while the + " target is running. + call s:SendCommand('-gdb-set mi-async on') + " Older gdb uses a different command. + call s:SendCommand('-gdb-set target-async on') + + let s:ptybuf = 0 + if has('win32') + " MS-Windows: run in a new console window for maximum compatibility + call s:SendCommand('set new-console on') + else + " Unix: Run the debugged program in a terminal window. Open it below the + " gdb window. + execute 'new' + wincmd x | wincmd j + belowright let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') + if s:pty_job_id == 0 + echoerr 'invalid argument (or job table is full) while opening terminal window' + return + elseif s:pty_job_id == -1 + echoerr 'Failed to open the program terminal window' + return + endif + let pty_job_info = nvim_get_chan_info(s:pty_job_id) + let s:ptybuf = pty_job_info['buffer'] + let pty = pty_job_info['pty'] + let s:ptywin = win_getid(winnr()) + call s:SendCommand('tty ' . pty) + + " Since GDB runs in a prompt window, the environment has not been set to + " match a terminal window, need to do that now. + call s:SendCommand('set env TERM = xterm-color') + call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) + call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) + call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) + call s:SendCommand('set env COLORS = ' . &t_Co) + call s:SendCommand('set env VIM_TERMINAL = ' . v:version) + endif + call s:SendCommand('set print pretty on') + call s:SendCommand('set breakpoint pending on') + " Disable pagination, it causes everything to stop at the gdb + call s:SendCommand('set pagination off') + + " Set arguments to be run + if len(proc_args) + call s:SendCommand('set args ' . join(proc_args)) + endif + + call s:StartDebugCommon(a:dict) + startinsert +endfunc func s:StartDebugCommon(dict) " Sign used to highlight the line where the program has stopped. @@ -316,21 +426,91 @@ endfunc " Send a command to gdb. "cmd" is the string without line terminator. func s:SendCommand(cmd) "call ch_log('sending to gdb: ' . a:cmd) - call jobsend(s:comm_job_id, a:cmd . "\r") + if s:way == 'prompt' + call chansend(s:gdbjob, a:cmd . "\n") + else + call chansend(s:comm_job_id, a:cmd . "\r") + endif endfunc " This is global so that a user can create their mappings with this. func TermDebugSendCommand(cmd) - let do_continue = 0 - if !s:stopped - let do_continue = 1 - call s:SendCommand('-exec-interrupt') - sleep 10m + if s:way == 'prompt' + call chansend(s:gdbjob, a:cmd . "\n") + else + let do_continue = 0 + if !s:stopped + let do_continue = 1 + if s:way == 'prompt' + " Need to send a signal to get the UI to listen. Strangely this is only + " needed once. + call jobstop(s:gdbjob) + else + call s:SendCommand('-exec-interrupt') + endif + sleep 10m + endif + call chansend(s:gdb_job_id, a:cmd . "\r") + if do_continue + Continue + endif endif - call jobsend(s:gdb_job_id, a:cmd . "\r") - if do_continue - Continue +endfunc + +" Function called when entering a line in the prompt buffer. +func s:PromptCallback(text) + call s:SendCommand(a:text) +endfunc + +" Function called when pressing CTRL-C in the prompt buffer. +func s:PromptInterrupt() + "call ch_log('Interrupting gdb') + call system('kill -SIGINT ' . s:pid) +endfunc + +" Function called when gdb outputs text. +func s:GdbOutCallback(job_id, msgs, event) + "call ch_log('received from gdb: ' . a:text) + + " Drop the gdb prompt, we have our own. + " Drop status and echo'd commands. + call filter(a:msgs, { index, val -> + \ val !=# '(gdb)' && val !=# '^done' && val[0] !=# '&'}) + + let lines = [] + let index = 0 + + for msg in a:msgs + if msg =~ '^^error,msg=' + if exists('s:evalexpr') + \ && s:DecodeMessage(msg[11:]) + \ =~ 'A syntax error in expression, near\|No symbol .* in current context' + " Silently drop evaluation errors. + call remove(a:msgs, index) + unlet s:evalexpr + continue + endif + elseif msg[0] == '~' + call add(lines, s:DecodeMessage(msg[1:])) + call remove(a:msgs, index) + continue + endif + let index += 1 + endfor + + let curwinid = win_getid(winnr()) + call win_gotoid(s:gdbwin) + + " Add the output above the current prompt. + for line in lines + call append(line('$') - 1, line) + endfor + if !empty(lines) + set modified endif + + call win_gotoid(curwinid) + call s:CommOutput(a:job_id, a:msgs, a:event) endfunc " Decode a message from gdb. quotedText starts with a ", return the text up @@ -396,6 +576,19 @@ func s:EndDebugCommon() au! TermDebug endfunc +func s:EndPromptDebug(job_id, exit_code, event) + let curwinid = win_getid(winnr()) + call win_gotoid(s:gdbwin) + close + if curwinid != s:gdbwin + call win_gotoid(curwinid) + endif + + call s:EndDebugCommon() + unlet s:gdbwin + "call ch_log("Returning from EndPromptDebug()") +endfunc + func s:CommOutput(job_id, msgs, event) for msg in a:msgs @@ -436,7 +629,11 @@ func s:InstallCommands() command Stop call s:SendCommand('-exec-interrupt') " using -exec-continue results in CTRL-C in gdb window not working - command Continue call jobsend(s:gdb_job_id, "continue\r") + if s:way == 'prompt' + command Continue call s:SendCommand('continue') + else + command Continue call chansend(s:gdb_job_id, "continue\r") + endif command -range -nargs=* Evaluate call s:Evaluate(, ) command Gdb call win_gotoid(s:gdbwin) -- cgit From aec3d7915c55fc0b7dc73f6186cf0ae45d416d33 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sat, 25 May 2019 11:14:35 +0900 Subject: vim-patch:8.1.0091: MS-Windows: Cannot interrupt gdb when program is running Problem: MS-Windows: Cannot interrupt gdb when program is running. Solution: Add debugbreak() and use it in the terminal debugger. Respect 'modified' in a prompt buffer. https://github.com/vim/vim/commit/4551c0a9fcdbdef52836d4852686d54b5e47fdaf --- runtime/doc/eval.txt | 6 ++++++ runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index b0b684064e..b8739ff9b3 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2084,6 +2084,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 deepcopy({expr} [, {noref}]) any make a full copy of {expr} delete({fname} [, {flags}]) Number delete the file or directory {fname} deletebufline({expr}, {first}[, {last}]) @@ -3641,6 +3642,11 @@ exp({expr}) *exp()* :echo exp(-1) < 0.367879 +debugbreak({pid}) *debugbreak()* + Specifically used to interrupt a program being debugged. It + will cause process {pid} to get a SIGTRAP. Behavior for other + processes is undefined. See |terminal-debugger|. + {Sends a SIGINT to a process {pid} other than MS-Windows} expand({expr} [, {nosuf} [, {list}]]) *expand()* Expand wildcards and the following special keywords in {expr}. diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 63c167d79a..aa2b69ad97 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -462,10 +462,16 @@ func s:PromptCallback(text) call s:SendCommand(a:text) endfunc -" Function called when pressing CTRL-C in the prompt buffer. +" Function called when pressing CTRL-C in the prompt buffer and when placing a +" breakpoint. func s:PromptInterrupt() - "call ch_log('Interrupting gdb') - call system('kill -SIGINT ' . s:pid) + if s:pid == 0 + echoerr 'Cannot interrupt gdb, did not find a process ID' + else + "call ch_log('Interrupting gdb') + " Using job_stop(s:gdbjob, 'int') does not work. + call debugbreak(s:pid) + endif endfunc " Function called when gdb outputs text. @@ -691,7 +697,11 @@ func s:SetBreakpoint() let do_continue = 0 if !s:stopped let do_continue = 1 - call s:SendCommand('-exec-interrupt') + if s:way == 'prompt' + call s:PromptInterrupt() + else + call s:SendCommand('-exec-interrupt') + endif sleep 10m endif " Use the fname:lnum format, older gdb can't handle --source. -- cgit From 417fc6ccf78801aef79a8731c5a85db6b12cd407 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Thu, 13 Feb 2020 11:55:43 +0900 Subject: lua: vim.deepcopy uses empty_dict() instead of {} for empty_dict() fix: https://github.com/neovim/nvim-lsp/issues/94 --- runtime/lua/vim/shared.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index ea1117a906..36df24d0c1 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -20,6 +20,11 @@ vim.deepcopy = (function() local deepcopy_funcs = { table = function(orig) local copy = {} + + if getmetatable(orig) == vim._empty_dict_mt then + copy = vim.empty_dict() + end + for k, v in pairs(orig) do copy[vim.deepcopy(k)] = vim.deepcopy(v) end -- cgit From d9657b3ae206435d93dacb2025d9b600da975088 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 13 Feb 2020 20:38:47 +0100 Subject: doc/lsp: start_client config cmd must be a list (#11866) doc/lsp: start_client config cmd must be a list After the merge of https://github.com/neovim/neovim/pull/11847 cmd can no longer be a string but must be a list. --- runtime/doc/lsp.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 016a8be7e6..9de2aaf592 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -543,7 +543,7 @@ start_client({config}) *vim.lsp.start_client()* {root_dir} (required, string) Directory where the LSP server will base its rootUri on initialization. - {cmd} (required, string or list treated like + {cmd} (required, list treated like |jobstart()|) Base command that initiates the LSP client. {cmd_cwd} (string, default=|getcwd()|) Directory -- cgit From 1b200d99360c1a4737f1d36206f3385a77d7d93e Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 14 Feb 2020 03:43:28 -0500 Subject: checkhealth: ignore cpamn "!" output #11869 cpanm outputs a warning that suggest to use 'sudo' or use local::lib. cpanm exits with 0 so nvim thinks that the command worked. cpanm output that starts with "!" is likely an error. Close #11858 --- runtime/autoload/health/provider.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 1d720b5876..601a8f83ef 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -708,9 +708,9 @@ function! s:check_perl() abort endif call health#report_info('Nvim perl host: '. host) - let latest_cpan_cmd = 'cpanm --info Neovim::Ext' + let latest_cpan_cmd = 'cpanm --info -q Neovim::Ext' let latest_cpan = s:system(latest_cpan_cmd) - if s:shell_error || empty(latest_cpan) + if s:shell_error || empty(latest_cpan) || latest_cpan[0] ==# '!' call health#report_error('Failed to run: '. latest_cpan_cmd, \ ["Make sure you're connected to the internet.", \ 'Are you behind a firewall or proxy?']) -- cgit From c230c7d1a6b744efb673f516f0c6cc2a17c2305b Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Thu, 13 Feb 2020 15:02:30 +0900 Subject: lua: if second argument is vim.empty_dict(), vim.tbl_extend uses empty_dict() instead of {} --- runtime/lua/vim/shared.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 36df24d0c1..6eb7a970e4 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -21,7 +21,7 @@ vim.deepcopy = (function() table = function(orig) local copy = {} - if getmetatable(orig) == vim._empty_dict_mt then + if vim._empty_dict_mt ~= nil and getmetatable(orig) == vim._empty_dict_mt then copy = vim.empty_dict() end @@ -174,9 +174,19 @@ function vim.tbl_extend(behavior, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) end + + if select('#', ...) < 2 then + error('wrong number of arguments (given '..tostring(1 + select('#', ...))..', expected at least 3)') + end + local ret = {} + if vim._empty_dict_mt ~= nil and getmetatable(select(1, ...)) == vim._empty_dict_mt then + ret = vim.empty_dict() + end + for i = 1, select('#', ...) do local tbl = select(i, ...) + vim.validate{["after the second argument"] = {tbl,'t'}} if tbl then for k, v in pairs(tbl) do if behavior ~= 'force' and ret[k] ~= nil then -- cgit From a1d6c2f5c9e7834ec6eb087b72354a1257582f3d Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 15 Feb 2020 19:02:52 -0500 Subject: checkhealth: allow 'sudo install' of 'Neovim::Ext' #11874 cpanm cannot look for Perl modules from root directories without sudo so it creates '~/perl5/' and look for Perl modules in there. Whether this directory existed before running cpanm or not, cpanm returns a warning to advice the user to setup local::lib in order to use modules in '~/perl5/' and exits with error code 0. Each line in the warning always starts with '!'. Display this warning to the user. Continue parsing the version number if the warning can be ignored because lines that are not prefixed with '!' are valid output. Fix #11858 --- runtime/autoload/health/provider.vim | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 601a8f83ef..86f9f060ff 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -710,13 +710,27 @@ function! s:check_perl() abort let latest_cpan_cmd = 'cpanm --info -q Neovim::Ext' let latest_cpan = s:system(latest_cpan_cmd) - if s:shell_error || empty(latest_cpan) || latest_cpan[0] ==# '!' + if s:shell_error || empty(latest_cpan) call health#report_error('Failed to run: '. latest_cpan_cmd, \ ["Make sure you're connected to the internet.", \ 'Are you behind a firewall or proxy?']) return + elseif latest_cpan[0] ==# '!' + let cpanm_errs = split(latest_cpan, '!') + if cpanm_errs[0] =~# "Can't write to " + call health#report_warn(cpanm_errs[0], cpanm_errs[1:-2]) + " Last line is the package info + let latest_cpan = cpanm_errs[-1] + else + call health#report_error('Unknown warning from command: ' . latest_cpan_cmd, cpanm_errs) + return + endif endif let latest_cpan = matchstr(latest_cpan, '\(\.\?\d\)\+') + if empty(latest_cpan) + call health#report_error('Cannot parse version number from cpanm output: ' . latest_cpan) + return + endif let current_cpan_cmd = [host, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION'] let current_cpan = s:system(current_cpan_cmd) -- cgit From 1eb0f5371ae8cee90b97f586a99505cfa5913504 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 17:25:53 -0800 Subject: LSP: fix validate_client_config - `cmd_env` is a table not a function. - tests: Set $NVIM_LOG_FILE for fake LSP server. --- runtime/lua/vim/lsp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index e5b6653346..94f0d62d8d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -154,7 +154,7 @@ local function validate_client_config(config) callbacks = { config.callbacks, "t", true }; capabilities = { config.capabilities, "t", true }; cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; - cmd_env = { config.cmd_env, "f", true }; + cmd_env = { config.cmd_env, "t", true }; name = { config.name, 's', true }; on_error = { config.on_error, "f", true }; on_exit = { config.on_exit, "f", true }; -- cgit From a446fbc8fa278d9c1e4144dc9767c9dc0b184583 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 9 Feb 2020 00:05:48 -0800 Subject: lsp/rpc.lua: fix `env` application function Env vars must be merged with the current env. --- runtime/lua/vim/lsp/rpc.lua | 51 +++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index e13b05610b..74d73da31f 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -33,38 +33,25 @@ local function convert_NIL(v) return v end --- If a dictionary is passed in, turn it into a list of string of "k=v" --- Accepts a table which can be composed of k=v strings or map-like --- specification, such as: --- --- ``` --- { --- "PRODUCTION=false"; --- "PATH=/usr/bin/"; --- PORT = 123; --- HOST = "0.0.0.0"; --- } --- ``` --- --- Non-string values will be cast with `tostring` -local function force_env_list(final_env) - if final_env then - local env = final_env - final_env = {} - for k,v in pairs(env) do - -- If it's passed in as a dict, then convert to list of "k=v" - if type(k) == "string" then - table.insert(final_env, k..'='..tostring(v)) - elseif type(v) == 'string' then - table.insert(final_env, v) - else - -- TODO is this right or should I exception here? - -- Try to coerce other values to string. - table.insert(final_env, tostring(v)) - end - end - return final_env +--- Merges current process env with the given env and returns the result as +--- a list of "k=v" strings. +--- +--- Example: +--- +--- { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } +--- => { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } +local function env_merge(env) + if env == nil then + return env + end + -- Merge. + env = vim.tbl_extend('force', vim.fn.environ(), env) + local final_env = {} + for k,v in pairs(env) do + assert(type(k) == 'string', 'env must be a dict') + table.insert(final_env, k..'='..tostring(v)) end + return final_env end local function format_message_with_content_length(encoded_message) @@ -262,7 +249,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para if spawn_params.cwd then assert(is_dir(spawn_params.cwd), "cwd must be a directory") end - spawn_params.env = force_env_list(extra_spawn_params.env) + spawn_params.env = env_merge(extra_spawn_params.env) end handle, pid = uv.spawn(cmd, spawn_params, onexit) end -- cgit From e2ed8053bf722d4d111fac7dcdb07179fdea8752 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 18 Feb 2020 17:41:29 +0900 Subject: lua: move test helper function, map and filter, to vim.shared module --- runtime/lua/vim/shared.lua | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 6eb7a970e4..498992aa2e 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -135,6 +135,36 @@ function vim.tbl_values(t) return values end +--- Apply a function to all values of a table. +--- +--@param func function or callable table +--@param t table +function vim.tbl_map(func, t) + vim.validate{func={func,'c'},t={t,'t'}} + + local rettab = {} + for k, v in pairs(t) do + rettab[k] = func(v) + end + return rettab +end + +--- Filter a table using a predicate function +--- +--@param func function or callable table +--@param t table +function vim.tbl_filter(func, t) + vim.validate{func={func,'c'},t={t,'t'}} + + local rettab = {} + for _, entry in pairs(t) do + if func(entry) then + table.insert(rettab, entry) + end + end + return rettab +end + --- Checks if a list-like (vector) table contains `value`. --- --@param t Table to check -- cgit From dd5d00aaa9af33d62271d989b8f1f709500a570b Mon Sep 17 00:00:00 2001 From: Billy Su Date: Tue, 18 Feb 2020 17:10:29 +0800 Subject: doc: C-Y and C-E in wildmenu completion Add description of C-Y and C-E in wildmenu completion --- runtime/doc/options.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 70af23ee29..9d044406c8 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6689,6 +6689,9 @@ A jump table for the options with a short description can be found at |Q_op|. While the menu is active these keys have special meanings: - select previous/next match (like CTRL-P/CTRL-N) + - Accet the currenty selected match and stop + completion. + - End completion, back to original typed text. - in filename/menu name completion: move into a subdirectory or submenu. - in menu completion, when the cursor is just after a -- cgit From 4ac376740c85ee337fc10627a793452300801ce0 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 18 Feb 2020 13:38:52 +0900 Subject: lsp: fix textDocument/completion handling fix: #11826 Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. So we exclude completion candidates whose prefix does not match. ex) Microsoft python-language-server, rust-analyzer --- runtime/lua/vim/lsp.lua | 4 +++- runtime/lua/vim/lsp/callbacks.lua | 3 ++- runtime/lua/vim/lsp/util.lua | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 94f0d62d8d..bc0da25ae5 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -945,12 +945,14 @@ function lsp.omnifunc(findstart, base) -- Get the start position of the current keyword local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local prefix = line_to_cursor:sub(textMatch+1) + local params = util.make_position_params() local items = {} lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, _, result) if err or not result then return end - local matches = util.text_document_completion_list_to_complete_items(result) + local matches = util.text_document_completion_list_to_complete_items(result, prefix) -- TODO(ashkan): is this the best way to do this? vim.list_extend(items, matches) vim.fn.complete(textMatch+1, items) diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 794140ee2e..e76e07ca96 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -63,8 +63,9 @@ M['textDocument/completion'] = function(_, _, result) local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) local line_to_cursor = line:sub(col+1) local textMatch = vim.fn.match(line_to_cursor, '\\k*$') + local prefix = line_to_cursor:sub(textMatch+1) - local matches = util.text_document_completion_list_to_complete_items(result) + local matches = util.text_document_completion_list_to_complete_items(result, prefix) vim.fn.complete(textMatch+1, matches) end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 428874f2b7..620037d24d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -129,6 +129,19 @@ function M.extract_completion_items(result) end end +-- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. +-- So we exclude completion candidates whose prefix does not match. +function M.remove_unmatch_completion_items(items, prefix) + local matched_items = {} + for _, item in ipairs(items) do + local word = item.insertText or item.label + if vim.startswith(word, prefix) then + table.insert(matched_items, item) + end + end + return matched_items +end + --- Apply the TextDocumentEdit response. -- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification function M.apply_text_document_edit(text_document_edit) @@ -151,12 +164,14 @@ end --- Getting vim complete-items with incomplete flag. -- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) -- @return { matches = complete-items table, incomplete = boolean } -function M.text_document_completion_list_to_complete_items(result) +function M.text_document_completion_list_to_complete_items(result, prefix) local items = M.extract_completion_items(result) if vim.tbl_isempty(items) then return {} end + items = M.remove_unmatch_completion_items(items, prefix) + local matches = {} for _, completion_item in ipairs(items) do -- cgit From c1bfc8093f4fb4487a2293f09558f46c7de49315 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 18 Feb 2020 13:40:24 +0900 Subject: lsp: respect the sort order if there is sortText --- runtime/lua/vim/lsp/util.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 620037d24d..0a24444328 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -129,6 +129,15 @@ function M.extract_completion_items(result) end end +-- Sort by CompletionItem.sortText +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +function M.sort_completion_items(items) + if items[1] and items[1].sortText then + table.sort(items, function(a, b) return a.sortText < b.sortText + end) + end +end + -- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. -- So we exclude completion candidates whose prefix does not match. function M.remove_unmatch_completion_items(items, prefix) @@ -171,6 +180,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) end items = M.remove_unmatch_completion_items(items, prefix) + M.sort_completion_items(items) local matches = {} -- cgit From f3d4ddd0f8b654d58fb4653d88ac7f652e3ad364 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Wed, 19 Feb 2020 07:39:56 +0900 Subject: lsp: make functions private and use filter function --- runtime/lua/vim/lsp/util.lua | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 0a24444328..6b12b37ec2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -129,28 +129,6 @@ function M.extract_completion_items(result) end end --- Sort by CompletionItem.sortText --- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion -function M.sort_completion_items(items) - if items[1] and items[1].sortText then - table.sort(items, function(a, b) return a.sortText < b.sortText - end) - end -end - --- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. --- So we exclude completion candidates whose prefix does not match. -function M.remove_unmatch_completion_items(items, prefix) - local matched_items = {} - for _, item in ipairs(items) do - local word = item.insertText or item.label - if vim.startswith(word, prefix) then - table.insert(matched_items, item) - end - end - return matched_items -end - --- Apply the TextDocumentEdit response. -- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification function M.apply_text_document_edit(text_document_edit) @@ -170,6 +148,24 @@ function M.get_current_line_to_cursor() return line:sub(pos[2]+1) end +-- Sort by CompletionItem.sortText +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +local function sort_completion_items(items) + if items[1] and items[1].sortText then + table.sort(items, function(a, b) return a.sortText < b.sortText + end) + end +end + +-- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. +-- So we exclude completion candidates whose prefix does not match. +local function remove_unmatch_completion_items(items, prefix) + return vim.tbl_filter(function(item) + local word = item.insertText or item.label + return vim.startswith(word, prefix) + end, items) +end + --- Getting vim complete-items with incomplete flag. -- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) -- @return { matches = complete-items table, incomplete = boolean } @@ -179,8 +175,8 @@ function M.text_document_completion_list_to_complete_items(result, prefix) return {} end - items = M.remove_unmatch_completion_items(items, prefix) - M.sort_completion_items(items) + items = remove_unmatch_completion_items(items, prefix) + sort_completion_items(items) local matches = {} -- cgit From 7ed4837298414488e238763953d40a4c7f2d7976 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 20 Feb 2020 22:53:38 -0500 Subject: vim-patch:8.1.1122: char2nr() does not handle composing characters Problem: char2nr() does not handle composing characters. Solution: Add str2list() and list2str(). (Ozaki Kiichi, closes vim/vim#4190) https://github.com/vim/vim/commit/9d40128afd7fcd038ff6532722b55b1a8c189ce8 'utf8' optional param is noop unlike Vim. --- runtime/doc/eval.txt | 29 +++++++++++++++++++++++++++++ runtime/doc/usr_41.txt | 6 ++++-- 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index b8739ff9b3..113a92d5e4 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2240,6 +2240,7 @@ libcallnr({lib}, {func}, {arg}) Number idem, but return a Number line({expr}) 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} +list2str({list} [, {utf8}]) String turn numbers in {list} into a String localtime() Number current time log({expr}) Float natural logarithm (base e) of {expr} log10({expr}) Float logarithm of Float {expr} to base 10 @@ -2394,6 +2395,8 @@ 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} str2float({expr}) Float convert String to Float +str2list({expr} [, {utf8}]) List convert each character of {expr} to + ASCII/UTF8 value str2nr({expr} [, {base}]) Number convert String to Number strchars({expr} [, {skipcc}]) Number character length of the String {expr} strcharpart({str}, {start} [, {len}]) @@ -5687,6 +5690,20 @@ lispindent({lnum}) *lispindent()* When {lnum} is invalid or Vim was not compiled the |+lispindent| feature, -1 is returned. +list2str({list} [, {utf8}]) *list2str()* + Convert each number in {list} to a character string can + concatenate them all. Examples: > + list2str([32]) returns " " + list2str([65, 66, 67]) returns "ABC" +< The same can be done (slowly) with: > + join(map(list, {nr, val -> nr2char(val)}), '') +< |str2list()| does the opposite. + + When {utf8} is omitted or zero, the current 'encoding' is used. + With {utf8} is 1, always return utf-8 characters. + With utf-8 composing characters work as expected: > + list2str([97, 769]) returns "á" +< localtime() *localtime()* Return the current time, measured as seconds since 1st Jan 1970. See also |strftime()| and |getftime()|. @@ -8197,6 +8214,18 @@ str2float({expr}) *str2float()* |substitute()|: > let f = str2float(substitute(text, ',', '', 'g')) +str2list({expr} [, {utf8}]) *str2list()* + Return a list containing the number values which represent + each character in String {expr}. Examples: > + str2list(" ") returns [32] + str2list("ABC") returns [65, 66, 67] +< |list2str()| does the opposite. + + When {utf8} is omitted or zero, the current 'encoding' is used. + With {utf8} set to 1, always treat the String as utf-8 + characters. With utf-8 composing characters are handled + properly: > + str2list("á") returns [97, 769] str2nr({expr} [, {base}]) *str2nr()* Convert string {expr} to a number. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index c770950a96..5f9253cbd0 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -578,8 +578,10 @@ used for. You can find an alphabetical list here: |functions|. Use CTRL-] on the function name to jump to detailed help on it. String manipulation: *string-functions* - nr2char() get a character by its ASCII value - char2nr() get ASCII value of a character + nr2char() get a character by its number value + list2str() get a character string from a list of numbers + char2nr() get number value of a character + str2list() get list of numbers from a string str2nr() convert a string to a Number str2float() convert a string to a Float printf() format a string according to % items -- cgit From ff1730373c6139db14b8f2f9b24d4ccd7fcfb01d Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Fri, 21 Feb 2020 09:34:07 +0100 Subject: lsp/completion: show duplicates in completion popup #11920 Allow duplicates so that in languages with overloaded functions it will show all signatures. E.g. instead of having a single (last one wins) add(int index, String element) It shows all signatures: add(String e) : boolean add(int index, String element) : void --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6b12b37ec2..b7c7b7f75d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -201,7 +201,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) menu = completion_item.detail or '', info = info, icase = 1, - dup = 0, + dup = 1, empty = 1, }) end -- cgit From 08af82b9cbd74016b96db0d133f8f85aa2960d0b Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 22 Feb 2020 19:02:08 +0100 Subject: treesitter: redraw on changed query --- runtime/lua/vim/tshighlighter.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/tshighlighter.lua b/runtime/lua/vim/tshighlighter.lua index 9d094f0f9a..1440acf0d0 100644 --- a/runtime/lua/vim/tshighlighter.lua +++ b/runtime/lua/vim/tshighlighter.lua @@ -69,6 +69,8 @@ function TSHighlighter:set_query(query) end self.id_map[i] = hl end + + a.nvim__buf_redraw_range(self.buf, 0, a.nvim_buf_line_count(self.buf)) end function TSHighlighter:on_change(changes) -- cgit From 9c00fea585ccab56a6044a174ce8d9a2c605c6cd Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 4 Nov 2019 20:40:30 +0100 Subject: lua: add regex support, and `@match` support in treesitter queries --- runtime/doc/lua.txt | 29 ++++++++++++++++++++++++++ runtime/lua/vim/treesitter.lua | 46 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index c113a70027..800f24b5c9 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -692,6 +692,35 @@ identical identifiers, highlighting both as |hl-WarningMsg|: > ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (eq? @WarningMsg.left @WarningMsg.right)) +------------------------------------------------------------------------------ +VIM.REGEX *lua-regex* + +Vim regexes can be used directly from lua. Currently they only allow +matching within a single line. + +vim.regex({re}) *vim.regex()* + + Parse the regex {re} and return a regex object. 'magic' and + 'ignorecase' options are ignored, lua regexes always defaults to magic + and ignoring case. The behavior can be changed with flags in + the beginning of the string |/magic|. + +Regex objects support the following methods: + +regex:match_str({str}) *regex:match_str()* + Match the string against the regex. If the string should match the + regex precisely, surround the regex with `^` and `$`. + If the was a match, the byte indices for the beginning and end of + the match is returned. When there is no match, `nil` is returned. + As any integer is truth-y, `regex:match()` can be directly used + as a condition in an if-statement. + +regex:match_line({bufnr}, {line_idx}[, {start}, {end}]) *regex:match_line()* + Match line {line_idx} (zero-based) in buffer {bufnr}. If {start} and + {end} are supplied, match only this byte index range. Otherwise see + |regex:match_str()|. If {start} is used, then the returned byte + indices will be relative {start}. + ------------------------------------------------------------------------------ VIM *lua-builtin* diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 0d0e22adb3..8dacfa11cf 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -113,12 +113,33 @@ end local Query = {} Query.__index = Query +local magic_prefixes = {['\\v']=true, ['\\m']=true, ['\\M']=true, ['\\V']=true} +local function check_magic(str) + if string.len(str) < 2 or magic_prefixes[string.sub(str,1,2)] then + return str + end + return '\\v'..str +end + function M.parse_query(lang, query) M.require_language(lang) local self = setmetatable({}, Query) self.query = vim._ts_parse_query(lang, query) self.info = self.query:inspect() self.captures = self.info.captures + self.regexes = {} + for id,preds in pairs(self.info.patterns) do + local regexes = {} + for i, pred in ipairs(preds) do + if (pred[1] == "match?" and type(pred[2]) == "number" + and type(pred[3]) == "string") then + regexes[i] = vim.regex(check_magic(pred[3])) + end + end + if next(regexes) then + self.regexes[id] = regexes + end + end return self end @@ -131,8 +152,13 @@ local function get_node_text(node, bufnr) return string.sub(line, start_col+1, end_col) end -local function match_preds(match, preds, bufnr) - for _, pred in pairs(preds) do +function Query:match_preds(match, pattern, bufnr) + local preds = self.info.patterns[pattern] + if not preds then + return true + end + local regexes = self.regexes[pattern] + for i, pred in pairs(preds) do if pred[1] == "eq?" then local node = match[pred[2]] local node_text = get_node_text(node, bufnr) @@ -149,6 +175,16 @@ local function match_preds(match, preds, bufnr) if node_text ~= str or str == nil then return false end + elseif pred[1] == "match?" then + if not regexes or not regexes[i] then + return false + end + local node = match[pred[2]] + local start_row, start_col, end_row, end_col = node:range() + if start_row ~= end_row then + return false + end + return regexes[i]:match_line(bufnr, start_row, start_col, end_col) else return false end @@ -164,8 +200,7 @@ function Query:iter_captures(node, bufnr, start, stop) local function iter() local capture, captured_node, match = raw_iter() if match ~= nil then - local preds = self.info.patterns[match.pattern] - local active = match_preds(match, preds, bufnr) + local active = self:match_preds(match, match.pattern, bufnr) match.active = active if not active then return iter() -- tail call: try next match @@ -184,8 +219,7 @@ function Query:iter_matches(node, bufnr, start, stop) local function iter() local pattern, match = raw_iter() if match ~= nil then - local preds = self.info.patterns[pattern] - local active = (not preds) or match_preds(match, preds, bufnr) + local active = self:match_preds(match, pattern, bufnr) if not active then return iter() -- tail call: try next match end -- cgit From ca8699378c765017575c102f3da8347833159a6c Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Wed, 26 Feb 2020 20:10:16 +0100 Subject: LSP: implement documentHighlight (#11638) * implement documentHighlight * fix bug * document highlight groups * use uppercase for help section title * documentation --- runtime/doc/lsp.txt | 40 ++++++++++++++++++++++++--------------- runtime/lua/vim/lsp/buf.lua | 18 ++++++++++++++++++ runtime/lua/vim/lsp/callbacks.lua | 6 ++++++ runtime/lua/vim/lsp/util.lua | 22 +++++++++++++++++++-- 4 files changed, 69 insertions(+), 17 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 9de2aaf592..2abb2d2d92 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -76,21 +76,6 @@ FAQ *lsp-faq* set the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim". -================================================================================ -LSP HIGHLIGHT *lsp-highlight* - -When LSP is activated these highlight groups are defined: - - LspDiagnosticsError - LspDiagnosticsHint - LspDiagnosticsInformation - LspDiagnosticsUnderline - LspDiagnosticsUnderlineError - LspDiagnosticsUnderlineHint - LspDiagnosticsUnderlineInformation - LspDiagnosticsUnderlineWarning - LspDiagnosticsWarning - ================================================================================ LSP API *lsp-api* @@ -173,6 +158,25 @@ name: > vim.lsp.protocol.TextDocumentSyncKind.Full == 1 vim.lsp.protocol.TextDocumentSyncKind[1] == "Full" +================================================================================ +LSP HIGHLIGHT *lsp-highlight* + + *hl-LspDiagnosticsError* +LspDiagnosticsError used for "Error" diagnostic virtual text + *hl-LspDiagnosticsWarning* +LspDiagnosticsWarning used for "Warning" diagnostic virtual text + *hl-LspDiagnosticsInformation* +LspDiagnosticInformation used for "Information" diagnostic virtual text + *hl-LspDiagnosticsHint* +LspDiagnosticHint used for "Hint" diagnostic virtual text + *hl-LspReferenceText* +LspReferenceText used for highlighting "text" references + *hl-LspReferenceRead* +LspReferenceRead used for highlighting "read" references + *hl-LspReferenceWrite* +LspReferenceWrite used for highlighting "write" references + + ================================================================================ LSP EXAMPLE *lsp-extension-example* @@ -720,6 +724,9 @@ declaration() *vim.lsp.buf.declaration()* definition() *vim.lsp.buf.definition()* TODO: Documentation +document_highlight() *vim.lsp.buf.document_highlight()* + TODO: Documentation + formatting({options}) *vim.lsp.buf.formatting()* TODO: Documentation @@ -897,6 +904,9 @@ apply_workspace_edit({workspace_edit}) buf_clear_diagnostics({bufnr}) *vim.lsp.util.buf_clear_diagnostics()* TODO: Documentation +buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* + TODO: Documentation + *vim.lsp.util.buf_diagnostics_save_positions()* buf_diagnostics_save_positions({bufnr}, {diagnostics}) TODO: Documentation diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 19deb5df45..52fa2ec93b 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -134,5 +134,23 @@ function M.references(context) request('textDocument/references', params) end +--- Send request to server to resolve document highlights for the +--- current text document position. This request can be associated +--- to key mapping or to events such as `CursorHold`, eg: +--- +---
+--- vim.api.nvim_command [[autocmd CursorHold   lua vim.lsp.buf.document_highlight()]]
+--- vim.api.nvim_command [[autocmd CursorHoldI  lua vim.lsp.buf.document_highlight()]]
+--- vim.api.nvim_command [[autocmd CursorMoved  lua vim.lsp.buf.clear_references()]]
+--- 
+function M.document_highlight() + local params = util.make_position_params() + request('textDocument/documentHighlight', params) +end + +function M.clear_references() + util.buf_clear_references() +end + return M -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index e76e07ca96..d7d74862b6 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -196,6 +196,12 @@ M['textDocument/peekDefinition'] = function(_, _, result, _) api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) end +M['textDocument/documentHighlight'] = function(_, _, result, _) + if not result then return end + local bufnr = api.nvim_get_current_buf() + util.buf_highlight_references(bufnr, result) +end + local function log_message(_, _, result, client_id) local message_type = result.type local message = result.message diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b7c7b7f75d..9c67f62e21 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -569,6 +569,7 @@ do local all_buffer_diagnostics = {} local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") + local reference_ns = api.nvim_create_namespace("vim_lsp_references") local underline_highlight_name = "LspDiagnosticsUnderline" vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) @@ -602,7 +603,6 @@ do function M.buf_clear_diagnostics(bufnr) validate { bufnr = {bufnr, 'n', true} } - bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) end @@ -683,7 +683,6 @@ do end end - function M.buf_diagnostics_underline(bufnr, diagnostics) for _, diagnostic in ipairs(diagnostics) do local start = diagnostic.range["start"] @@ -705,6 +704,25 @@ do end end + function M.buf_clear_references(bufnr) + validate { bufnr = {bufnr, 'n', true} } + api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) + end + + function M.buf_highlight_references(bufnr, references) + validate { bufnr = {bufnr, 'n', true} } + for _, reference in ipairs(references) do + local start_pos = {reference["range"]["start"]["line"], reference["range"]["start"]["character"]} + local end_pos = {reference["range"]["end"]["line"], reference["range"]["end"]["character"]} + local document_highlight_kind = { + [protocol.DocumentHighlightKind.Text] = "LspReferenceText"; + [protocol.DocumentHighlightKind.Read] = "LspReferenceRead"; + [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; + } + highlight_range(bufnr, reference_ns, document_highlight_kind[reference["kind"]], start_pos, end_pos) + end + end + function M.buf_diagnostics_virtual_text(bufnr, diagnostics) local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] if not buffer_line_diagnostics then -- cgit From ad745f9da289a56035b8c759cc8fac1b40a44558 Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Wed, 26 Feb 2020 20:22:14 +0100 Subject: add support to show diagnostics count in statusline (#11641) * add support to show diagnostics count in statusline * documentation --- runtime/doc/lsp.txt | 36 +++++++++++++++++++++++++++++++++++- runtime/lua/vim/lsp.lua | 17 +++++++++-------- runtime/lua/vim/lsp/buf.lua | 4 ++++ runtime/lua/vim/lsp/callbacks.lua | 1 + runtime/lua/vim/lsp/util.lua | 12 ++++++++++++ 5 files changed, 61 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 2abb2d2d92..31644cf37b 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -294,6 +294,12 @@ The example will: < +============================================================================== +AUTOCOMMANDS *lsp-autocommands* + + *LspDiagnosticsChanged* +LspDiagnosticsChanged After receiving publishDiagnostics server response + ============================================================================== Lua module: vim.lsp *lsp-core* @@ -337,7 +343,7 @@ buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()* {params} (string) Parameters to send to the server Return: ~ - nil + true if any client returns true; false otherwise *vim.lsp.buf_request()* buf_request({bufnr}, {method}, {params}, {callback}) @@ -758,6 +764,10 @@ rename({new_name}) *vim.lsp.buf.rename()* request({method}, {params}, {callback}) *vim.lsp.buf.request()* TODO: Documentation +server_ready() *vim.lsp.buf.server_ready()* + Sends a notification through all clients associated with current + buffer and returns `true` if server responds. + signature_help() *vim.lsp.buf.signature_help()* TODO: Documentation @@ -903,6 +913,30 @@ apply_workspace_edit({workspace_edit}) buf_clear_diagnostics({bufnr}) *vim.lsp.util.buf_clear_diagnostics()* TODO: Documentation + + *vim.lsp.util.buf_diagnostics_count()* +buf_diagnostics_count({kind}) + Returns the number of diagnostics of given kind for current buffer. + Useful for showing diagnostics counts in statusline. eg: + +> + function! LspStatus() abort + let sl = '' + if luaeval('vim.lsp.buf.server_ready()') + let sl.='%#MyStatuslineLSP#E:' + let sl.='%#MyStatuslineLSPErrors#%{luaeval("vim.lsp.util.buf_diagnostics_count(\"Error\")")}' + let sl.='%#MyStatuslineLSP# W:' + let sl.='%#MyStatuslineLSPWarnings#%{luaeval("vim.lsp.util.buf_diagnostics_count(\"Warning\")")}' + else + let sl.='%#MyStatuslineLSPErrors#off' + endif + return sl + endfunction + let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus() +< + + Parameters: ~ + {kind} Diagnostic severity kind: Error, Warning, Information or Hint. buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* TODO: Documentation diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index bc0da25ae5..71ec3cb6c4 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -893,21 +893,22 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) return request_results end ---- Sends a notification to all servers attached to the buffer. ---- ---@param bufnr (optional, number) Buffer handle, or 0 for current ---@param method (string) LSP method name ---@param params (string) Parameters to send to the server ---- ---@returns nil +--- Send a notification to a server +-- @param bufnr [number] (optional): The number of the buffer +-- @param method [string]: Name of the request method +-- @param params [string]: Arguments to send to the server +-- +-- @returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) validate { bufnr = { bufnr, 'n', true }; method = { method, 's' }; } + local resp = false for_each_buffer_client(bufnr, function(client, _client_id) - client.rpc.notify(method, params) + if client.rpc.notify(method, params) then resp = true end end) + return resp end --- Implements 'omnifunc' compatible LSP completion. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 52fa2ec93b..82aeccd4db 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -23,6 +23,10 @@ local function request(method, params, callback) return vim.lsp.buf_request(0, method, params, callback) end +function M.server_ready() + return not not vim.lsp.buf_notify(0, "window/progress", {}) +end + function M.hover() local params = util.make_position_params() request('textDocument/hover', params) diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index d7d74862b6..457eccb985 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -33,6 +33,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) -- util.set_loclist(result.diagnostics) + vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") end M['textDocument/references'] = function(_, _, result) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 9c67f62e21..59300647b5 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -743,6 +743,18 @@ do api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {}) end end + function M.buf_diagnostics_count(kind) + local bufnr = vim.api.nvim_get_current_buf() + local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] + if not buffer_line_diagnostics then return end + local count = 0 + for _, line_diags in pairs(buffer_line_diagnostics) do + for _, diag in ipairs(line_diags) do + if protocol.DiagnosticSeverity[kind] == diag.severity then count = count + 1 end + end + end + return count + end end local position_sort = sort_by_key(function(v) -- cgit From cb8b9428ca796c68025afbba65a81381b2f6a304 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 27 Feb 2020 00:00:06 +0100 Subject: LSP/hover: Do not throw away contents if first line is empty (#11939) haskell-ide-engine sends `hover` payloads as follows: { contents = { kind = "markdown", value = "\n```haskell\nfoo :: Either String (Integer, Text)\n```\n`foo` *local*" }, range = { end = { character = 5, line = 57 }, start = { character = 2, line = 57 } } } `value` starts with `\n`. The logic in `convert_input_to_markdown_lines` threw away the whole information. --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 59300647b5..3eadefe051 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -269,7 +269,7 @@ function M.convert_input_to_markdown_lines(input, contents) end end end - if contents[1] == '' or contents[1] == nil then + if (contents[1] == '' or contents[1] == nil) and #contents == 1 then return {} end return contents -- cgit From 83add102cff8ac2ce2dda02cf1ac9225079f6529 Mon Sep 17 00:00:00 2001 From: Alvaro Muñoz Date: Thu, 27 Feb 2020 12:12:53 +0100 Subject: Add signs for Lsp diagnostics (#11668) * Add signs for Lsp diagnostics * defer sign definition until init.vim is loaded --- runtime/doc/lsp.txt | 12 ++++++++++++ runtime/lua/vim/lsp/callbacks.lua | 1 + runtime/lua/vim/lsp/util.lua | 24 +++++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 31644cf37b..d5ed857f32 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -953,6 +953,18 @@ buf_diagnostics_underline({bufnr}, {diagnostics}) buf_diagnostics_virtual_text({bufnr}, {diagnostics}) TODO: Documentation + *vim.lsp.util.buf_diagnostics_signs()* +buf_diagnostics_signs({bufnr}, {diagnostics}) + Place signs for each diagnostic in the sign column. + Sign characters can be customized with the following options: +> +let g:LspDiagnosticsErrorSign = 'E' +let g:LspDiagnosticsWarningSign = 'W' +let g:LspDiagnosticsInformationSign = 'I' +let g:LspDiagnosticsHintSign = 'H' +< + + character_offset({buf}, {row}, {col}) *vim.lsp.util.character_offset()* TODO: Documentation diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 457eccb985..c9d63625fd 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -32,6 +32,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_save_positions(bufnr, result.diagnostics) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) + util.buf_diagnostics_signs(bufnr, result.diagnostics) -- util.set_loclist(result.diagnostics) vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 3eadefe051..21e0dbfd1f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -570,7 +570,7 @@ do local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") local reference_ns = api.nvim_create_namespace("vim_lsp_references") - + local sign_ns = 'vim_lsp_signs' local underline_highlight_name = "LspDiagnosticsUnderline" vim.cmd(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name)) for kind, _ in pairs(protocol.DiagnosticSeverity) do @@ -603,6 +603,12 @@ do function M.buf_clear_diagnostics(bufnr) validate { bufnr = {bufnr, 'n', true} } + bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + + -- clear sign group + vim.fn.sign_unplace(sign_ns, {buffer=bufnr}) + + -- clear virtual text namespace api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) end @@ -755,6 +761,22 @@ do end return count end + function M.buf_diagnostics_signs(bufnr, diagnostics) + vim.fn.sign_define('LspDiagnosticsErrorSign', {text=vim.g['LspDiagnosticsErrorSign'] or 'E', texthl='LspDiagnosticsError', linehl='', numhl=''}) + vim.fn.sign_define('LspDiagnosticsWarningSign', {text=vim.g['LspDiagnosticsWarningSign'] or 'W', texthl='LspDiagnosticsWarning', linehl='', numhl=''}) + vim.fn.sign_define('LspDiagnosticsInformationSign', {text=vim.g['LspDiagnosticsInformationSign'] or 'I', texthl='LspDiagnosticsInformation', linehl='', numhl=''}) + vim.fn.sign_define('LspDiagnosticsHintSign', {text=vim.g['LspDiagnosticsHintSign'] or 'H', texthl='LspDiagnosticsHint', linehl='', numhl=''}) + + for _, diagnostic in ipairs(diagnostics) do + local diagnostic_severity_map = { + [protocol.DiagnosticSeverity.Error] = "LspDiagnosticsErrorSign"; + [protocol.DiagnosticSeverity.Warning] = "LspDiagnosticsWarningSign"; + [protocol.DiagnosticSeverity.Information] = "LspDiagnosticsInformationSign"; + [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; + } + vim.fn.sign_place(0, sign_ns, diagnostic_severity_map[diagnostic.severity], bufnr, {lnum=(diagnostic.range.start.line+1)}) + end + end end local position_sort = sort_by_key(function(v) -- cgit From 220a2b05c6c42dcbaa0c84292cbb318ddee4ff49 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Wed, 19 Feb 2020 20:24:56 +0100 Subject: LSP/references: Add context to locations returned by server This changes the `textDocument/references' callback to annotate the locations returned by the server with the content present at the locations range. The quickfix list then looks as follows: cr8/insert_fake_data.py|17 col 32| from .misc import parse_table, parse_version cr8/insert_fake_data.py|43 col 15| version = parse_version(r['rows'][0][0]) cr8/java_magic.py|8 col 22| from cr8.misc import parse_version cr8/java_magic.py|30 col 19| version = parse_version(fst) cr8/java_magic.py|33 col 16| return parse_version(version_str) Instead of: cr8/insert_fake_data.py|17 col 32| cr8/insert_fake_data.py|43 col 15| cr8/java_magic.py|8 col 22| cr8/java_magic.py|30 col 19| cr8/java_magic.py|33 col 16| --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 21e0dbfd1f..803d9443b7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -824,7 +824,7 @@ function M.locations_to_items(locations) filename = fname, lnum = row + 1, col = col + 1; - text = temp.msg; + text = temp.msg or line; }) end end -- cgit From 38201650cd60c001c4b639f30ca45f1b8eba98ca Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Sun, 23 Feb 2020 13:00:48 +0100 Subject: LSP: Remove diagnostic message handling in locations_to_items `locations_to_items` is for turning `Location[]` into items, not for `Diagnostic[]` --- runtime/lua/vim/lsp/callbacks.lua | 1 - runtime/lua/vim/lsp/util.lua | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index c9d63625fd..99093216d9 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -33,7 +33,6 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) util.buf_diagnostics_signs(bufnr, result.diagnostics) - -- util.set_loclist(result.diagnostics) vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") end diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 803d9443b7..5f0fe8ceb4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -797,7 +797,7 @@ function M.locations_to_items(locations) for _, d in ipairs(locations) do local start = d.range.start local fname = assert(vim.uri_to_fname(d.uri)) - table.insert(grouped[fname], {start = start, msg= d.message }) + table.insert(grouped[fname], {start = start}) end @@ -824,7 +824,7 @@ function M.locations_to_items(locations) filename = fname, lnum = row + 1, col = col + 1; - text = temp.msg or line; + text = line; }) end end -- cgit From 1fe01b36de72a937ac7be430077df918a659345d Mon Sep 17 00:00:00 2001 From: Jesse Bakker Date: Fri, 24 Jan 2020 12:31:52 +0100 Subject: Use buffer version instead of changedtick for edits --- runtime/lua/vim/lsp.lua | 2 ++ runtime/lua/vim/lsp/util.lua | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index bc0da25ae5..8190542955 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -613,6 +613,8 @@ do if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then return end + + util.buf_versions[bufnr] = changedtick -- Lazy initialize these because clients may not even need them. local incremental_changes = once(function(client) local size_index = encoding_index[client.offset_encoding] diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b7c7b7f75d..3dfe4d7d02 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -135,7 +135,7 @@ function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) -- TODO(ashkan) check this is correct. - if api.nvim_buf_get_changedtick(bufnr) > text_document.version then + if (M.buf_versions[bufnr] or 0) > text_document.version then print("Buffer ", text_document.uri, " newer than edits.") return end @@ -868,5 +868,7 @@ function M.character_offset(buf, row, col) return str_utfindex(line, col) end +M.buf_versions = {} + return M -- vim:sw=2 ts=2 et -- cgit From f157fdef7eccba6601c6d3b1475847466b60c25b Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 22 Feb 2020 21:14:10 +0900 Subject: lsp: add bufnr to callback function arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DocumentSymbol type doesn't have location field. So when we'll add 'textDocument/documentSymbol’ handler, we can't decide which file have we jump to. --- runtime/lua/vim/lsp.lua | 12 ++++++------ runtime/lua/vim/lsp/buf.lua | 5 +++++ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 71ec3cb6c4..23eac45046 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -101,7 +101,7 @@ local function for_each_buffer_client(bufnr, callback) for client_id in pairs(client_ids) do local client = active_clients[client_id] if client then - callback(client, client_id) + callback(client, client_id, bufnr) end end end @@ -520,7 +520,7 @@ function lsp.start_client(config) end --- Checks capabilities before rpc.request-ing. - function client.request(method, params, callback) + function client.request(method, params, callback, bufnr) if not callback then callback = resolve_callback(method) or error("not found: request callback for client "..client.name) @@ -536,7 +536,7 @@ function lsp.start_client(config) return end return rpc.request(method, params, function(err, result) - callback(err, method, result, client_id) + callback(err, method, result, client_id, bufnr) end) end @@ -836,8 +836,8 @@ function lsp.buf_request(bufnr, method, params, callback) callback = { callback, 'f', true }; } local client_request_ids = {} - for_each_buffer_client(bufnr, function(client, client_id) - local request_success, request_id = client.request(method, params, callback) + for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr) + local request_success, request_id = client.request(method, params, callback, resolved_bufnr) -- This could only fail if the client shut down in the time since we looked -- it up and we did the request, which should be rare. @@ -874,7 +874,7 @@ end function lsp.buf_request_sync(bufnr, method, params, timeout_ms) local request_results = {} local result_count = 0 - local function _callback(err, _method, result, client_id) + local function _callback(err, _method, result, client_id, bufnr) request_results[client_id] = { error = err, result = result } result_count = result_count + 1 end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 82aeccd4db..fc9e10cb73 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -138,6 +138,11 @@ function M.references(context) request('textDocument/references', params) end +function M.document_symbol() + local params = { textDocument = util.make_text_document_params() } + request('textDocument/documentSymbol', params) +end + --- Send request to server to resolve document highlights for the --- current text document position. This request can be associated --- to key mapping or to events such as `CursorHold`, eg: -- cgit From 3c12ee333a519c5be1d8f63d7978b483a51a3da7 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 2 Feb 2020 15:55:15 -0500 Subject: vim-patch:8.1.0619: :echomsg and :echoerr do not handle List and Dict Problem: :echomsg and :echoerr do not handle List and Dict like :echo does. (Daniel Hahler) Solution: Be more tolerant about the expression result type. https://github.com/vim/vim/commit/461a7fcfce3cd6414f990037e6468af3b5ccf119 Add lua functional tests for :echo,:echon,:echomsg,:echoerr because nvim did not port "test_" functions from Vim that modify internal state. Testing :echoerr via try/catch is sufficient. --- runtime/doc/eval.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 113a92d5e4..ce0218f1dd 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -10370,8 +10370,8 @@ text... The parsing works slightly different from |:echo|, more like |:execute|. All the expressions are first evaluated and concatenated before echoing anything. - The expressions must evaluate to a Number or String, a - Dictionary or List causes an error. + If expressions does not evaluate to a Number or + String, string() is used to turn it into a string. Uses the highlighting set by the |:echohl| command. Example: > :echomsg "It's a Zizzer Zazzer Zuzz, as you can plainly see." @@ -10382,7 +10382,7 @@ text... message in the |message-history|. When used in a script or function the line number will be added. Spaces are placed between the arguments as with the - :echo command. When used inside a try conditional, + |:echomsg| command. When used inside a try conditional, the message is raised as an error exception instead (see |try-echoerr|). Example: > -- cgit From 16262472cda88d0a4dc5c6729cfef36264569324 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 22 Feb 2020 21:20:38 +0900 Subject: lsp: add 'textDocument/documentSymbol’ callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol --- runtime/doc/lsp.txt | 4 +++ runtime/lua/vim/lsp.lua | 9 ++++--- runtime/lua/vim/lsp/callbacks.lua | 10 +++++-- runtime/lua/vim/lsp/protocol.lua | 26 +++++++++--------- runtime/lua/vim/lsp/util.lua | 57 +++++++++++++++++++++++++++++++++------ 5 files changed, 79 insertions(+), 27 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index d5ed857f32..2f5427f6fc 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -48,6 +48,7 @@ go-to-definition, hover, etc. Example config: > nnoremap lua vim.lsp.buf.signature_help() nnoremap 1gD lua vim.lsp.buf.type_definition() nnoremap gr lua vim.lsp.buf.references() + nnoremap g0 lua vim.lsp.buf.document_symbol() Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows |i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of @@ -733,6 +734,9 @@ definition() *vim.lsp.buf.definition()* document_highlight() *vim.lsp.buf.document_highlight()* TODO: Documentation +document_symbol() *vim.lsp.buf.document_symbol()* + TODO: Documentation + formatting({options}) *vim.lsp.buf.formatting()* TODO: Documentation diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 23eac45046..8af20ea1f9 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -525,14 +525,15 @@ function lsp.start_client(config) callback = resolve_callback(method) or error("not found: request callback for client "..client.name) end - local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback) + local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr) -- TODO keep these checks or just let it go anyway? if (not client.resolved_capabilities.hover and method == 'textDocument/hover') or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp') or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') + or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') then - callback(unsupported_method(method), method, nil, client_id) + callback(unsupported_method(method), method, nil, client_id, bufnr) return end return rpc.request(method, params, function(err, result) @@ -874,7 +875,7 @@ end function lsp.buf_request_sync(bufnr, method, params, timeout_ms) local request_results = {} local result_count = 0 - local function _callback(err, _method, result, client_id, bufnr) + local function _callback(err, _method, result, client_id) request_results[client_id] = { error = err, result = result } result_count = result_count + 1 end @@ -905,7 +906,7 @@ function lsp.buf_notify(bufnr, method, params) method = { method, 's' }; } local resp = false - for_each_buffer_client(bufnr, function(client, _client_id) + for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr) if client.rpc.notify(method, params) then resp = true end end) return resp diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 99093216d9..63b5c4d493 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -38,7 +38,13 @@ end M['textDocument/references'] = function(_, _, result) if not result then return end - util.set_qflist(result) + util.set_qflist(util.locations_to_items(result)) +end + +M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr) + if not result or vim.tbl_isempty(result) then return end + + util.set_qflist(util.symbols_to_items(result, bufnr)) api.nvim_command("copen") api.nvim_command("wincmd p") end @@ -97,7 +103,7 @@ local function location_callback(_, method, result) end util.jump_to_location(result[1]) if #result > 1 then - util.set_qflist(result) + util.set_qflist(util.locations_to_items(result)) api.nvim_command("copen") api.nvim_command("wincmd p") end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index f64b0b50e7..41e8119c8c 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -663,19 +663,19 @@ function protocol.make_client_capabilities() documentHighlight = { dynamicRegistration = false }; - -- documentSymbol = { - -- dynamicRegistration = false; - -- symbolKind = { - -- valueSet = (function() - -- local res = {} - -- for k in pairs(protocol.SymbolKind) do - -- if type(k) == 'string' then table.insert(res, k) end - -- end - -- return res - -- end)(); - -- }; - -- hierarchicalDocumentSymbolSupport = false; - -- }; + documentSymbol = { + dynamicRegistration = false; + symbolKind = { + valueSet = (function() + local res = {} + for k in pairs(protocol.SymbolKind) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + hierarchicalDocumentSymbolSupport = true; + }; }; workspace = nil; experimental = nil; diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5f0fe8ceb4..72c84b8471 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -834,23 +834,60 @@ function M.locations_to_items(locations) return items end --- locations is Location[] --- Only sets for the current window. -function M.set_loclist(locations) +function M.set_loclist(items) vim.fn.setloclist(0, {}, ' ', { title = 'Language Server'; - items = M.locations_to_items(locations); + items = items; }) end --- locations is Location[] -function M.set_qflist(locations) +function M.set_qflist(items) vim.fn.setqflist({}, ' ', { title = 'Language Server'; - items = M.locations_to_items(locations); + items = items; }) end +--- Convert symbols to quickfix list items +--- +--@symbols DocumentSymbol[] or SymbolInformation[] +function M.symbols_to_items(symbols, bufnr) + local function _symbols_to_items(_symbols, _items, _bufnr) + for _, symbol in ipairs(_symbols) do + if symbol.location then -- SymbolInformation type + local range = symbol.location.range + local kind = protocol.SymbolKind[symbol.kind] + table.insert(_items, { + filename = vim.uri_to_fname(symbol.location.uri), + lnum = range.start.line + 1, + col = range.start.character + 1, + kind = kind, + text = '['..kind..'] '..symbol.name, + }) + elseif symbol.range then -- DocumentSymbole type + local kind = protocol.SymbolKind[symbol.kind] + table.insert(_items, { + -- bufnr = _bufnr, + filename = vim.api.nvim_buf_get_name(_bufnr), + lnum = symbol.range.start.line + 1, + col = symbol.range.start.character + 1, + kind = kind, + text = '['..kind..'] '..symbol.name + }) + if symbol.children then + for _, child in ipairs(symbol) do + for _, v in ipairs(_symbols_to_items(child, _items, _bufnr)) do + vim.list_extend(_items, v) + end + end + end + end + end + return _items + end + return _symbols_to_items(symbols, {}, bufnr) +end + -- Remove empty lines from the beginning and end. function M.trim_empty_lines(lines) local start = 1 @@ -903,11 +940,15 @@ function M.make_position_params() local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] col = str_utfindex(line, col) return { - textDocument = { uri = vim.uri_from_bufnr(0) }; + textDocument = M.make_text_document_params(); position = { line = row; character = col; } } end +function M.make_text_document_params() + return { uri = vim.uri_from_bufnr(0) } +end + -- @param buf buffer handle or 0 for current. -- @param row 0-indexed line -- @param col 0-indexed byte offset in line -- cgit From 5e0c435ca1b711e80f78429431b4d400d789c618 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Thu, 20 Feb 2020 20:19:49 +0000 Subject: man.vim: Handle `man` errors when looking for man-paths Fallback to simply globbing the tag we're given. This matches the original behaviour of `man.vim`, prior to c6afad78d39aa. fixes #11794 closes #11918 --- runtime/autoload/man.vim | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 5feab0ce70..122ae357bc 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -328,18 +328,24 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort return s:complete(sect, sect, name) endfunction -function! s:get_paths(sect, name) abort +function! s:get_paths(sect, name, do_fallback) abort + " callers must try-catch this, as some `man` implementations don't support `s:find_arg` try let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',') + return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) catch - call s:error(v:exception) - return + if !a:do_fallback + throw v:exception + endif + + " fallback to a single path, with the page we're trying to find + let [l:sect, l:name, l:path] = s:verify_exists(a:sect, a:name) + return [l:path] endtry - return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1) endfunction function! s:complete(sect, psect, name) abort - let pages = s:get_paths(a:sect, a:name) + let pages = s:get_paths(a:sect, a:name, v:false) " We remove duplicates in case the same manpage in different languages was found. return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i')) endfunction @@ -387,7 +393,7 @@ endfunction function! man#goto_tag(pattern, flags, info) abort let [l:sect, l:name] = man#extract_sect_and_name_ref(a:pattern) - let l:paths = s:get_paths(l:sect, l:name) + let l:paths = s:get_paths(l:sect, l:name, v:true) let l:structured = [] for l:path in l:paths -- cgit From eba8a9ca1d94f43b7fe2e92d5a2caa144dd68d0d Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 21 Feb 2020 22:05:59 -0500 Subject: vim-patch:8.1.1510: a plugin cannot easily expand a command like done internally Problem: A plugin cannot easily expand a command like done internally. Solution: Add the expandcmd() function. (Yegappan Lakshmanan, closes vim/vim#4514) https://github.com/vim/vim/commit/80dad48c5095d30873a42ec82628bdb213125d8e --- runtime/doc/eval.txt | 9 +++++++++ runtime/doc/usr_41.txt | 1 + 2 files changed, 10 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index ce0218f1dd..c687d15689 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2110,6 +2110,7 @@ extend({expr1}, {expr2} [, {expr3}]) exp({expr}) Float exponential of {expr} expand({expr} [, {nosuf} [, {list}]]) any expand special keywords in {expr} +expandcmd({expr}) String expand {expr} like with `:edit` feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer filereadable({file}) Number |TRUE| if {file} is a readable file filewritable({file}) Number |TRUE| if {file} is a writable file @@ -3733,6 +3734,14 @@ expand({expr} [, {nosuf} [, {list}]]) *expand()* See |glob()| for finding existing files. See |system()| for getting the raw output of an external command. +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}. Returns the expanded string. + Example: > + :echo expandcmd('make %<.o') +< extend({expr1}, {expr2} [, {expr3}]) *extend()* {expr1} and {expr2} must be both |Lists| or both |Dictionaries|. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 5f9253cbd0..234f7801ab 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -609,6 +609,7 @@ String manipulation: *string-functions* strcharpart() get part of a string using char index strgetchar() get character from a string using char index expand() expand special keywords + expandcmd() expand a command like done for `:edit` iconv() convert text from one encoding to another byteidx() byte index of a character in a string byteidxcomp() like byteidx() but count composing characters -- cgit From d846f47cc88cd36da5ad8fd7e716552c80e3b418 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 24 Feb 2020 20:33:43 -0500 Subject: vim-patch:8.1.0881: can execute shell commands in rvim through interfaces Problem: Can execute shell commands in rvim through interfaces. Solution: Disable using interfaces in restricted mode. Allow for writing file with writefile(), histadd() and a few others. https://github.com/vim/vim/commit/8c62a08faf89663e5633dc5036cd8695c80f1075 --- runtime/doc/starting.txt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index e3f0d593a7..af7d233619 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -184,12 +184,17 @@ argument. the 'modifiable' and 'write' options can be set to enable changes and writing. - *-Z* *restricted-mode* *E145* + *-Z* *restricted-mode* *E145* *E981* -Z Restricted mode. All commands that make use of an external shell are disabled. This includes suspending with CTRL-Z, - ":sh", filtering, the system() function, backtick expansion, - delete(), rename(), mkdir(), writefile(), libcall(), - jobstart(), etc. + ":sh", filtering, the system() function, backtick expansion + and libcall(). + Also disallowed are delete(), rename(), mkdir(), jobstart(), + etc. + Interfaces, such as Python, Ruby and Lua, are also disabled, + since they could be used to execute shell commands. + Note that the user may still find a loophole to execute a + shell command, it has only been made difficult. -e *-e* *-E* -E Start Nvim in Ex mode |gQ|. -- cgit From 1caa85b677bc287883895558281d23e1fd55817e Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 25 Feb 2020 00:30:42 -0500 Subject: vim-patch:8.1.1466: not updating priority on existing sign Problem: Not updating priority on existing sign. Solution: Set the sign priority. Add a test. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/58a7f87c8653b4cb5b0794b6b88e2ec140d3d2c3 --- runtime/doc/eval.txt | 2 +- runtime/doc/sign.txt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c687d15689..29c5c37bcd 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7818,7 +7818,7 @@ sign_getplaced([{expr} [, {dict}]]) *sign_getplaced()* priority sign priority The returned signs in a buffer are ordered by their line - number. + number and priority. Returns an empty list on failure or if there are no placed signs. diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 4e0d91dae0..e3ba4ba181 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -176,9 +176,9 @@ See |sign_place()| for the equivalent Vim script function. By default, the sign is assigned a default priority of 10. To assign a different priority value, use "priority={prio}" to - specify a value. The priority is used to determine the - highlight group used when multiple signs are placed on the - same line. + specify a value. The priority is used to determine the sign + that is displayed when multiple signs are placed on the same + line. Examples: > :sign place 5 line=3 name=sign1 file=a.py @@ -198,7 +198,9 @@ See |sign_place()| for the equivalent Vim script function. it (e.g., when the debugger has stopped at a breakpoint). The optional "group={group}" attribute can be used before - "file=" to select a sign in a particular group. + "file=" to select a sign in a particular group. The optional + "priority={prio}" attribute can be used to change the priority + of an existing sign. :sign place {id} name={name} [buffer={nr}] Same, but use buffer {nr}. If the buffer argument is not -- cgit From e35ff7371f4a61621587744a7620200380abbbe9 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 2 Mar 2020 16:38:43 +0900 Subject: lua: add vim.tbl_len() #11889 --- runtime/lua/vim/shared.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 498992aa2e..1bf1c63fd7 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -356,6 +356,24 @@ function vim.tbl_islist(t) end end +--- Counts the number of non-nil values in table `t`. +--- +---
+--- vim.tbl_count({ a=1, b=2 }) => 2
+--- vim.tbl_count({ 1, 2 }) => 2
+--- 
+--- +--@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua +--@param Table +--@returns Number that is the number of the value in table +function vim.tbl_count(t) + vim.validate{t={t,'t'}} + + local count = 0 + for _ in pairs(t) do count = count + 1 end + return count +end + --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- --@see https://www.lua.org/pil/20.2.html -- cgit From 6f261d23942feb22e5aa2881bc645bce5c5ce946 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Sun, 1 Mar 2020 16:43:18 +0100 Subject: updating doc --- runtime/doc/fold.txt | 3 +-- runtime/doc/options.txt | 11 +++++++---- runtime/doc/vim_diff.txt | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index f2f6c70b0c..8e2cb2f728 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -527,8 +527,7 @@ FOLDCOLUMN *fold-foldcolumn* 'foldcolumn' is a number, which sets the width for a column on the side of the window to indicate folds. When it is zero, there is no foldcolumn. A normal -value is 4 or 5. The minimal useful value is 2, although 1 still provides -some information. The maximum is 12. +value is auto:9. The maximum is 9. An open fold is indicated with a column that has a '-' at the top and '|' characters below it. This column stops where the open fold stops. When folds diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7107a0135d..283b2c3f12 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2421,11 +2421,14 @@ A jump table for the options with a short description can be found at |Q_op|. automatically close when moving out of them. *'foldcolumn'* *'fdc'* -'foldcolumn' 'fdc' number (default 0) +'foldcolumn' 'fdc' string (default "0") local to window - When non-zero, a column with the specified width is shown at the side - of the window which indicates open and closed folds. The maximum - value is 12. + When and how to draw the foldcolumn. Valid values are: + "auto": resize to the maximum amount of folds to display. + "auto:[1-9]": resize to accommodate multiple folds up to the + selected level + 0: to disable foldcolumn + "[1-9]": to display a fixed number of columns See |folding|. *'foldenable'* *'fen'* *'nofoldenable'* *'nofen'* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 5835c7f314..376375e4ef 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -201,6 +201,7 @@ Options: 'guicursor' works in the terminal 'fillchars' flags: "msgsep" (see 'display'), "eob" for |hl-EndOfBuffer| marker, "foldopen", "foldsep", "foldclose" + 'foldcolumn' supports up to 9 dynamic/fixed columns 'inccommand' shows interactive results for |:substitute|-like commands 'listchars' local to window 'pumblend' pseudo-transparent popupmenu -- cgit From fbc4c4fd36bef6f10f75af675985167d5fab4699 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 2 Mar 2020 22:27:30 +0900 Subject: lsp: make showMessage and logMessage callbacks different (#11942) According to the LSP specification, showMessage is what is displayed and logMessage is what is stored. Since these are different things, I devide the callback into those that match. --- runtime/lua/vim/lsp/callbacks.lua | 25 +++++++++++++++++++++---- runtime/lua/vim/lsp/log.lua | 1 - 2 files changed, 21 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 63b5c4d493..644c12f98c 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -209,7 +209,27 @@ M['textDocument/documentHighlight'] = function(_, _, result, _) util.buf_highlight_references(bufnr, result) end -local function log_message(_, _, result, client_id) +M['window/logMessage'] = function(_, _, result, client_id) + local message_type = result.type + local message = result.message + local client = vim.lsp.get_client_by_id(client_id) + local client_name = client and client.name or string.format("id=%d", client_id) + if not client then + err_message("LSP[", client_name, "] client has shut down after sending the message") + end + if message_type == protocol.MessageType.Error then + log.error(message) + elseif message_type == protocol.MessageType.Warning then + log.warn(message) + elseif message_type == protocol.MessageType.Info then + log.info(message) + else + log.debug(message) + end + return result +end + +M['window/showMessage'] = function(_, _, result, client_id) local message_type = result.type local message = result.message local client = vim.lsp.get_client_by_id(client_id) @@ -226,9 +246,6 @@ local function log_message(_, _, result, client_id) return result end -M['window/showMessage'] = log_message -M['window/logMessage'] = log_message - -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do M[k] = function(err, method, params, client_id) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 974eaae38c..c0db5e5485 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -13,7 +13,6 @@ log.levels = { INFO = 2; WARN = 3; ERROR = 4; - -- FATAL = 4; } -- Default log level is warn. -- cgit From ccb038dc6aaf72e052ef8afb810f84a331d4ccec Mon Sep 17 00:00:00 2001 From: Andrey Avramenko Date: Fri, 6 Mar 2020 19:40:46 +0200 Subject: LSP/completion: add textEdit support According to lsp specification, value of insertText should be ignored if textEdit is provided. --- runtime/lua/vim/lsp/util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 72c84b8471..533bcf2779 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -161,7 +161,7 @@ end -- So we exclude completion candidates whose prefix does not match. local function remove_unmatch_completion_items(items, prefix) return vim.tbl_filter(function(item) - local word = item.insertText or item.label + local word = (item.textEdit and item.textEdit.newText) or item.insertText or item.label return vim.startswith(word, prefix) end, items) end @@ -193,7 +193,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) end end - local word = completion_item.insertText or completion_item.label + local word = (completion_item.textEdit and completion_item.textEdit.newText) or completion_item.insertText or completion_item.label table.insert(matches, { word = word, abbr = completion_item.label, -- cgit From 716bebad486a4364ae5383f7c25aa9919f15143a Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 6 Mar 2020 19:04:42 -0500 Subject: vim-patch:8.2.0360: yaml files are only recognized by the file extension Problem: Yaml files are only recognized by the file extension. Solution: Check for a line starting with "%YAML". (Jason Franklin) https://github.com/vim/vim/commit/8eab73132838e977092d7b46f70b4ecf6274fd6a --- runtime/scripts.vim | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'runtime') diff --git a/runtime/scripts.vim b/runtime/scripts.vim index a690431014..c552f0202f 100644 --- a/runtime/scripts.vim +++ b/runtime/scripts.vim @@ -376,6 +376,10 @@ else elseif s:line1 =~? '-\*-.*erlang.*-\*-' set ft=erlang + " YAML + elseif s:line1 =~# '^%YAML' + set ft=yaml + " CVS diff else let s:lnum = 1 -- cgit From 87d892afa0475644e91d9c8a57b7c35491c4dc32 Mon Sep 17 00:00:00 2001 From: Will Eccles Date: Tue, 17 Mar 2020 15:05:34 -0400 Subject: vim-patch:8.1.0864 Make 'scrolloff' and 'sidescrolloff' options window local (#11854) Problem: cannot have a local value for 'scrolloff' and 'sidescrolloff' Author: Bram Moolenar https://github.com/vim/vim/commit/375e3390078e740d3c83b0c118c50d9a920036c7 --- runtime/doc/options.txt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 283b2c3f12..06977ac454 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4900,13 +4900,17 @@ A jump table for the options with a short description can be found at |Q_op|. *'scrolloff'* *'so'* 'scrolloff' 'so' number (default 0) - global + global or local to window |global-local| Minimal number of screen lines to keep above and below the cursor. This will make some context visible around where you are working. If you set it to a very large value (999) the cursor line will always be in the middle of the window (except at the start or end of the file or when long lines wrap). - For scrolling horizontally see 'sidescrolloff'. + After using the local value, go back the global value with one of + these two: > + setlocal scrolloff< + setlocal scrolloff=-1 +< For scrolling horizontally see 'sidescrolloff'. *'scrollopt'* *'sbo'* 'scrollopt' 'sbo' string (default "ver,jump") @@ -5515,7 +5519,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'sidescrolloff'* *'siso'* 'sidescrolloff' 'siso' number (default 0) - global + global or local to window |global-local| The minimal number of screen columns to keep to the left and to the right of the cursor if 'nowrap' is set. Setting this option to a value greater than 0 while having |'sidescroll'| also at a non-zero @@ -5524,7 +5528,11 @@ A jump table for the options with a short description can be found at |Q_op|. to a large value (like 999) has the effect of keeping the cursor horizontally centered in the window, as long as one does not come too close to the beginning of the line. - + After using the local value, go back the global value with one of + these two: > + setlocal sidescrolloff< + setlocal sidescrolloff=-1 +< Example: Try this together with 'sidescroll' and 'listchars' as in the following example to never allow the cursor to move onto the "extends" character: > -- cgit From 4d53bdccc60a4bc39e6ca2c626f4b511881559ef Mon Sep 17 00:00:00 2001 From: Zantox Date: Tue, 24 Mar 2020 22:40:55 +0100 Subject: doc: Fix wildmenu doc inconsistencies and typos Fix typos, capitalization to match block, and remove the angle brackets on the CTRL keys to match other documentation. --- runtime/doc/options.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 9d044406c8..12af911d6d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6688,10 +6688,11 @@ A jump table for the options with a short description can be found at |Q_op|. While the menu is active these keys have special meanings: + CTRL-Y - accept the currently selected match and stop + completion. + CTRL-E - end completion, go back to what was there before + selecting a match. - select previous/next match (like CTRL-P/CTRL-N) - - Accet the currenty selected match and stop - completion. - - End completion, back to original typed text. - in filename/menu name completion: move into a subdirectory or submenu. - in menu completion, when the cursor is just after a -- cgit From 4139678f97ee556ab142031a1ed5c7580278b64f Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 30 Mar 2020 21:30:24 +0900 Subject: vim.uri: fix uri_to_fname (#12059) fix: #12056 If the colon of the drive letter of windows is URI encoded, it doesn't match the expected pattern, so decode it first. --- runtime/lua/vim/uri.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index 1065f84f4c..d91fb7ffd3 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -70,6 +70,7 @@ local function uri_from_bufnr(bufnr) end local function uri_to_fname(uri) + uri = uri_decode(uri) -- TODO improve this. if is_windows_file_uri(uri) then uri = uri:gsub('^file:///', '') @@ -77,7 +78,7 @@ local function uri_to_fname(uri) else uri = uri:gsub('^file://', '') end - return uri_decode(uri) + return uri end -- Return or create a buffer for a uri. -- cgit From 1fe0b329fe1d40e5837a43f53da0e0fff38477bc Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 23 Jan 2020 18:05:04 +0100 Subject: api/ui: win_viewport event for visible range and cursor position in window --- runtime/doc/ui.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index de54ce59b6..b243d9ba50 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -592,6 +592,12 @@ tabs. When |ext_messages| is active, no message grid is used, and this event will not be sent. +["win_viewport", grid, win, topline, botline, curline, curcol] + Indicates the range of buffer text displayed in the window, as well + as the cursor position in the buffer. All positions are zero-based. + `botline` is set to one more than the line count of the buffer, if + there are filler lines past the end. + ============================================================================== Popupmenu Events *ui-popupmenu* -- cgit From 1f56f9a4b32c7d7aebafea226c148e9ed8bbabdb Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Wed, 8 Apr 2020 10:57:32 -0500 Subject: netrw.vim: gx should ignore terminal buffers #12091 netrw thinks it's a remote file due the name of a terminal buffer (term://), but a terminal buffer isn't a file. Fixes https://github.com/neovim/neovim/issues/4612#issuecomment-600321171 --- runtime/autoload/netrw.vim | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index fa86223d53..b69ad7187a 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -5460,6 +5460,11 @@ fun! netrw#CheckIfRemote(...) else let curfile= expand("%") endif + + " Ignore terminal buffers + if &buftype ==# 'terminal' + return 0 + endif " call Decho("curfile<".curfile.">") if curfile =~ '^\a\{3,}://' " call Dret("netrw#CheckIfRemote 1") -- cgit From dfb8dc8b45d125d3083ffae3898e39c0f4e96e53 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 19 Mar 2020 22:48:23 -0400 Subject: vim-patch:8.2.0415: bsdl filetype is not detected Problem: Bsdl filetype is not detected. Solution: Add an entry in the filetype list. (Daniel Kho, closes vim/vim#5810) https://github.com/vim/vim/commit/40d235e7305ce6ff696e8301c62a41f1f07f8aca --- runtime/filetype.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 49e1e9909b..dccd0119be 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -229,6 +229,9 @@ au BufNewFile,BufRead *.bl setf blank " Blkid cache file au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml +" BSDL +au BufNewFile,BufRead *.bsdl setf bsdl + " Bazel (http://bazel.io) autocmd BufRead,BufNewFile *.bzl,WORKSPACE,BUILD.bazel setf bzl if has("fname_case") @@ -1772,7 +1775,7 @@ au BufNewFile,BufRead *.va,*.vams setf verilogams au BufNewFile,BufRead *.sv,*.svh setf systemverilog " VHDL -au BufNewFile,BufRead *.hdl,*.vhd,*.vhdl,*.vbe,*.vst setf vhdl +au BufNewFile,BufRead *.hdl,*.vhd,*.vhdl,*.vbe,*.vst,*.vho setf vhdl " Vim script au BufNewFile,BufRead *.vim,*.vba,.exrc,_exrc setf vim -- cgit From cf223e7d78df5e8a63b84d423be55a29eefd8883 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 12 Apr 2020 09:15:50 -0400 Subject: vim-patch:8.2.0549: user systemd files not recognized Problem: User systemd files not recognized. Solution: Add filetype patterns. (Kevin Locke, closes vim/vim#5914) https://github.com/vim/vim/commit/3b922f11380c7dbf94ee45bc09d688b772d9707f --- runtime/filetype.vim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index dccd0119be..dbdd90041c 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar -" Last Change: 2019 Nov 26 +" Last Change: 2020 Apr 12 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -1642,9 +1642,11 @@ au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl " Systemd unit files au BufNewFile,BufRead */systemd/*.{automount,mount,path,service,socket,swap,target,timer} setf systemd " Systemd overrides -au BufNewFile,BufRead /etc/systemd/system/*.d/*.conf setf systemd +au BufNewFile,BufRead */etc/systemd/system/*.d/*.conf setf systemd +au BufNewFile,BufRead */.config/systemd/user/*.d/*.conf setf systemd " Systemd temp files -au BufNewFile,BufRead /etc/systemd/system/*.d/.#* setf systemd +au BufNewFile,BufRead */etc/systemd/system/*.d/.#* setf systemd +au BufNewFile,BufRead */.config/systemd/user/*.d/.#* setf systemd " Synopsys Design Constraints au BufNewFile,BufRead *.sdc setf sdc -- cgit From e8269a3ab5f4505f27997b46b0eaf9464d186cf9 Mon Sep 17 00:00:00 2001 From: erw7 Date: Wed, 15 Apr 2020 21:54:23 +0900 Subject: win,runtime: Fix problem when win32yank was a symbolic link in WSL [skip ci] (#12124) On some versions of Windows, WSL is unable to execute symbolic links to Windows executables (microsoft/WSL#3999). As a workaround for that problem this changes to use resolve() on WSL if win32yank was a symbolic link. fixes #12113. --- runtime/autoload/provider/clipboard.vim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index c86f7d0c2f..a96a0a61b7 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -113,8 +113,13 @@ function! provider#clipboard#Executable() abort let s:paste['*'] = s:paste['+'] return 'doitclient' elseif executable('win32yank.exe') - let s:copy['+'] = 'win32yank.exe -i --crlf' - let s:paste['+'] = 'win32yank.exe -o --lf' + if has('wsl') && getftype(exepath('win32yank.exe')) == 'link' + let win32yank = resolve(exepath('win32yank.exe')) + else + let win32yank = 'win32yank.exe' + endif + let s:copy['+'] = win32yank.' -i --crlf' + let s:paste['+'] = win32yank.' -o --lf' let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'win32yank' -- cgit From 51b4fc4778896775da5854fe6a04d00445b72919 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Fri, 17 Apr 2020 00:30:03 +0800 Subject: lsp: provide a default for missing reference kind (#12127) Fix #12122 >Error executing vim.schedule lua callback: /usr/local/share/nvim/runtime/lua/vim/lsp/util.lua:560: Expected lua string Some lsp server(e.g. https://github.com/bash-lsp/bash-language-server) not have kind in reference, reference["kind"] is nil --- runtime/lua/vim/lsp/util.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5dd010f2a4..1a8d7dc084 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -725,7 +725,8 @@ do [protocol.DocumentHighlightKind.Read] = "LspReferenceRead"; [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; } - highlight_range(bufnr, reference_ns, document_highlight_kind[reference["kind"]], start_pos, end_pos) + local kind = reference["kind"] or protocol.DocumentHighlightKind.Text + highlight_range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos) end end -- cgit From a5818204b1124efea432377fc447a742cfec9ad1 Mon Sep 17 00:00:00 2001 From: Micah Halter Date: Thu, 16 Apr 2020 14:49:05 -0400 Subject: doc:Fix incorrect nvim config paths in documentation (#12134) Updates remaining ~/.vim with ~/.config/nvim --- runtime/doc/filetype.txt | 2 +- runtime/doc/spell.txt | 2 +- runtime/doc/syntax.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index c649688d99..1fce37c594 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -275,7 +275,7 @@ Note that the last one is the value of $VIMRUNTIME which has been expanded. Note that when using a plugin manager or |packages| many directories will be added to 'runtimepath'. These plugins each require their own directory, don't -put them directly in ~/.vim/plugin. +put them directly in ~/.config/nvim/plugin. What if it looks like your plugin is not being loaded? You can find out what happens when Vim starts up by using the |-V| argument: > diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 1f1599f560..b88e26cdff 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -504,7 +504,7 @@ then Vim will try to guess. Multiple {inname} arguments can be given to combine regions into one Vim spell file. Example: > - :mkspell ~/.vim/spell/en /tmp/en_US /tmp/en_CA /tmp/en_AU + :mkspell ~/.config/nvim/spell/en /tmp/en_US /tmp/en_CA /tmp/en_AU < This combines the English word lists for US, CA and AU into one en.spl file. Up to eight regions can be combined. *E754* *E755* diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 30ccb699cd..57337aeac2 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4647,8 +4647,8 @@ in their own color. ":colorscheme" in a color scheme script. To customize a colorscheme use another name, e.g. - "~/.vim/colors/mine.vim", and use `:runtime` to load - the original colorscheme: > + "~/.config/nvim/colors/mine.vim", and use `:runtime` to + load the original colorscheme: > runtime colors/evening.vim hi Statement ctermfg=Blue guifg=Blue -- cgit From b3d669b40bd4736d42c52430ec84d76c2f5151eb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 17 Apr 2020 21:14:03 -0400 Subject: vim-patch:8.2.0589: .bsd file type not recognized Problem: .bsd file type not recognized. Solution: Recognize .bsd as BSDL. (Daniel Kho, closes vim/vim#5945) https://github.com/vim/vim/commit/0fc1288aefce25da0a2fb2a0aab75b0bd314403f --- runtime/filetype.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index dbdd90041c..5068f9be76 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -230,13 +230,13 @@ au BufNewFile,BufRead *.bl setf blank au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml " BSDL -au BufNewFile,BufRead *.bsdl setf bsdl +au BufNewFile,BufRead *bsd,*.bsdl setf bsdl " Bazel (http://bazel.io) autocmd BufRead,BufNewFile *.bzl,WORKSPACE,BUILD.bazel setf bzl if has("fname_case") " There is another check for BUILD further below. - autocmd BufRead,BufNewFile BUILD setf bzl + autocmd BufRead,BufNewFile BUILD setf bzl endif " C or lpc -- cgit From 1fb44ba8356e06b9d90a43599c4526ad4d625618 Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Fri, 17 Apr 2020 17:24:04 +0200 Subject: treesitter: escape backslashes in queries Treesitter changed their decoders and apparently thus causing this change. This decoder change happened on ee9a3c0ebb218990cf391ed987be7f2448c54a73. --- runtime/lua/vim/treesitter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 8dacfa11cf..1836227540 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -124,7 +124,7 @@ end function M.parse_query(lang, query) M.require_language(lang) local self = setmetatable({}, Query) - self.query = vim._ts_parse_query(lang, query) + self.query = vim._ts_parse_query(lang, vim.fn.escape(query,'\\')) self.info = self.query:inspect() self.captures = self.info.captures self.regexes = {} -- cgit From 7d4451c65785ef7e781bf5029da059fb77b728b7 Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Sat, 18 Apr 2020 18:21:08 +0200 Subject: LSP: fix breakage when severity isn't specified (#12027) Before this commit, the LSP client would throw errors when messages without severity would be sent by the server. We make severity default to `Error` as a kludge before proper heuristics to discover the severity of a message are found. --- runtime/lua/vim/lsp/callbacks.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 644c12f98c..c403a3fd51 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -29,6 +29,17 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) return end util.buf_clear_diagnostics(bufnr) + + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic + -- The diagnostic's severity. Can be omitted. If omitted it is up to the + -- client to interpret diagnostics as error, warning, info or hint. + -- TODO: Replace this with server-specific heuristics to infer severity. + for _, diagnostic in ipairs(result.diagnostics) do + if diagnostic.severity == nil then + diagnostic.severity = protocol.DiagnosticSeverity.Error + end + end + util.buf_diagnostics_save_positions(bufnr, result.diagnostics) util.buf_diagnostics_underline(bufnr, result.diagnostics) util.buf_diagnostics_virtual_text(bufnr, result.diagnostics) -- cgit From c5466ba6ef8333183e1c43c7e762e44539fb2358 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sun, 19 Apr 2020 02:04:47 +0900 Subject: lsp: replace the event that closes the signature help preview window from InsertCharPre to CursolMovedI (#11954) In the case of InsertCharPre, it is inconvenient because the signature help is displayed when backspaced in insert mode, so change it to CursolMovedI. --- runtime/lua/vim/lsp/util.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 1a8d7dc084..763e9719a7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -522,8 +522,7 @@ function M.open_floating_preview(contents, filetype, opts) end api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents) api.nvim_buf_set_option(floating_bufnr, 'modifiable', false) - -- TODO make InsertCharPre disappearing optional? - api.nvim_command("autocmd CursorMoved,BufHidden,InsertCharPre ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") + M.close_preview_autocmd({"CursorMoved", "CursorMovedI", "BufHidden"}, floating_winnr) return floating_bufnr, floating_winnr end -- cgit From bf0f74586153dfa8d550e1cfefd83ca9e0354171 Mon Sep 17 00:00:00 2001 From: Tristan Konolige Date: Sat, 18 Apr 2020 17:04:37 -0600 Subject: lua: allow deepcopy of functions (#12136) --- runtime/doc/lua.txt | 5 ++++- runtime/lua/vim/shared.lua | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 800f24b5c9..7f376cbbf0 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -908,7 +908,10 @@ deep_equal({a}, {b}) *vim.deep_equal()* deepcopy({orig}) *vim.deepcopy()* Returns a deep copy of the given object. Non-table objects are copied as in a typical Lua assignment, whereas table objects - are copied recursively. + are copied recursively. Functions are naively copied, so + functions in the copied table point to the same functions as + those in the input table. Userdata and threads are not copied + and will throw an error. Parameters: ~ {orig} Table to copy diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 1bf1c63fd7..d18fcfaf95 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -8,6 +8,9 @@ local vim = vim or {} --- Returns a deep copy of the given object. Non-table objects are copied as --- in a typical Lua assignment, whereas table objects are copied recursively. +--- Functions are naively copied, so functions in the copied table point to the +--- same functions as those in the input table. Userdata and threads are not +--- copied and will throw an error. --- --@param orig Table to copy --@returns New table of copied keys and (nested) values. @@ -34,10 +37,16 @@ vim.deepcopy = (function() string = _id, ['nil'] = _id, boolean = _id, + ['function'] = _id, } return function(orig) - return deepcopy_funcs[type(orig)](orig) + local f = deepcopy_funcs[type(orig)] + if f then + return f(orig) + else + error("Cannot deepcopy object of type "..type(orig)) + end end end)() -- cgit From 9d59f066cbbe8893559586eee5bfca9378cf6385 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 22 Mar 2020 21:23:33 +0000 Subject: vim-patch:8.0.1651: cannot filter :ls output for terminal buffers Problem: Cannot filter :ls output for terminal buffers. Solution: Add flags for terminal buffers. (Marcin Szamotulski, closes vim/vim#2751) https://github.com/vim/vim/commit/0751f51a5b428805a8c1e9fe529693d032bec991 --- runtime/doc/windows.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 977e0daef7..dbe08a71ef 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1021,6 +1021,8 @@ list of buffers. |unlisted-buffer| x buffers with a read error % current buffer # alternate buffer + R terminal buffers with a running job + F terminal buffers with a finished job Combining flags means they are "and"ed together, e.g.: h+ hidden buffers which are modified a+ active buffers which are modified -- cgit From adec9fb444d4a50637f097eb2fef1abbced3f385 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 20 Apr 2020 06:40:54 +0900 Subject: lsp: export convert_signature_help_to_markdown_lines (#11950) This function is also useful for users to create their own `textDocument/signatureHelp` callback function. --- runtime/lua/vim/lsp/callbacks.lua | 61 +-------------------------------------- runtime/lua/vim/lsp/util.lua | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 60 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index c403a3fd51..b6396f5798 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -125,72 +125,13 @@ M['textDocument/definition'] = location_callback M['textDocument/typeDefinition'] = location_callback M['textDocument/implementation'] = location_callback ---- Convert SignatureHelp response to preview contents. --- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp -local function signature_help_to_preview_contents(input) - if not input.signatures then - return - end - --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about - --the active signature and shouldn't rely on a default value. - local contents = {} - local active_signature = input.activeSignature or 0 - -- If the activeSignature is not inside the valid range, then clip it. - if active_signature >= #input.signatures then - active_signature = 0 - end - local signature = input.signatures[active_signature + 1] - if not signature then - return - end - vim.list_extend(contents, vim.split(signature.label, '\n', true)) - if signature.documentation then - util.convert_input_to_markdown_lines(signature.documentation, contents) - end - if input.parameters then - local active_parameter = input.activeParameter or 0 - -- If the activeParameter is not inside the valid range, then clip it. - if active_parameter >= #input.parameters then - active_parameter = 0 - end - local parameter = signature.parameters and signature.parameters[active_parameter] - if parameter then - --[=[ - --Represents a parameter of a callable-signature. A parameter can - --have a label and a doc-comment. - interface ParameterInformation { - --The label of this parameter information. - -- - --Either a string or an inclusive start and exclusive end offsets within its containing - --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 - --string representation as `Position` and `Range` does. - -- - --*Note*: a label of type string should be a substring of its containing signature label. - --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. - label: string | [number, number]; - --The human-readable doc-comment of this parameter. Will be shown - --in the UI but can be omitted. - documentation?: string | MarkupContent; - } - --]=] - -- TODO highlight parameter - if parameter.documentation then - util.convert_input_to_markdown_lines(parameter.documentation, contents) - end - end - end - return contents -end - M['textDocument/signatureHelp'] = function(_, method, result) util.focusable_preview(method, function() if not (result and result.signatures and result.signatures[1]) then return { 'No signature available' } end -- TODO show popup when signatures is empty? - local lines = signature_help_to_preview_contents(result) + local lines = util.convert_signature_help_to_markdown_lines(result) lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then return { 'No signature available' } diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 763e9719a7..82a4223102 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -275,6 +275,65 @@ function M.convert_input_to_markdown_lines(input, contents) return contents end +--- Convert SignatureHelp response to markdown lines. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp +function M.convert_signature_help_to_markdown_lines(signature_help) + if not signature_help.signatures then + return + end + --The active signature. If omitted or the value lies outside the range of + --`signatures` the value defaults to zero or is ignored if `signatures.length + --=== 0`. Whenever possible implementors should make an active decision about + --the active signature and shouldn't rely on a default value. + local contents = {} + local active_signature = signature_help.activeSignature or 0 + -- If the activeSignature is not inside the valid range, then clip it. + if active_signature >= #signature_help.signatures then + active_signature = 0 + end + local signature = signature_help.signatures[active_signature + 1] + if not signature then + return + end + vim.list_extend(contents, vim.split(signature.label, '\n', true)) + if signature.documentation then + M.convert_input_to_markdown_lines(signature.documentation, contents) + end + if signature_help.parameters then + local active_parameter = signature_help.activeParameter or 0 + -- If the activeParameter is not inside the valid range, then clip it. + if active_parameter >= #signature_help.parameters then + active_parameter = 0 + end + local parameter = signature.parameters and signature.parameters[active_parameter] + if parameter then + --[=[ + --Represents a parameter of a callable-signature. A parameter can + --have a label and a doc-comment. + interface ParameterInformation { + --The label of this parameter information. + -- + --Either a string or an inclusive start and exclusive end offsets within its containing + --signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + --string representation as `Position` and `Range` does. + -- + --*Note*: a label of type string should be a substring of its containing signature label. + --Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + label: string | [number, number]; + --The human-readable doc-comment of this parameter. Will be shown + --in the UI but can be omitted. + documentation?: string | MarkupContent; + } + --]=] + -- TODO highlight parameter + if parameter.documentation then + M.convert_input_help_to_markdown_lines(parameter.documentation, contents) + end + end + end + return contents +end + function M.make_floating_popup_options(width, height, opts) validate { opts = { opts, 't', true }; -- cgit From e6cfc1b158506f8d2a4b598d07051b7f3de5f964 Mon Sep 17 00:00:00 2001 From: Andrey Avramenko Date: Mon, 20 Apr 2020 01:59:09 +0300 Subject: LSP/completion: Add completion text helper function --- runtime/lua/vim/lsp/util.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 533bcf2779..5217464c22 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -157,11 +157,23 @@ local function sort_completion_items(items) end end +-- Returns text that should be inserted when selecting completion item. The precedence is as follows: +-- textEdit.newText > insertText > label +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +local function get_completion_word(item) + if item.textEdit ~= nil and item.textEdit.newText ~= nil then + return item.textEdit.newText + elseif item.insertText ~= nil then + return item.insertText + end + return item.label +end + -- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. -- So we exclude completion candidates whose prefix does not match. local function remove_unmatch_completion_items(items, prefix) return vim.tbl_filter(function(item) - local word = (item.textEdit and item.textEdit.newText) or item.insertText or item.label + local word = get_completion_word(item) return vim.startswith(word, prefix) end, items) end @@ -193,7 +205,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) end end - local word = (completion_item.textEdit and completion_item.textEdit.newText) or completion_item.insertText or completion_item.label + local word = get_completion_word(completion_item) table.insert(matches, { word = word, abbr = completion_item.label, -- cgit From 2a5d766581e8be0908d4d045d3bb9fa56a821b63 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 21 Apr 2020 21:51:51 +0900 Subject: doc: fix vim.lsp.stop_all_clients doc (#12055) --- runtime/doc/lsp.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 2f5427f6fc..4c59e53343 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -64,7 +64,7 @@ FAQ *lsp-faq* - Q: How to force-reload LSP? A: Stop all clients, then reload the buffer. > - :lua vim.lsp.stop_all_clients() + :lua vim.lsp.stop_client(vim.lsp.get_active_clients()) :edit - Q: Why isn't completion working? -- cgit From 0c637898f991ff9b90e695065a0136dd9c37106a Mon Sep 17 00:00:00 2001 From: Khangal Date: Tue, 21 Apr 2020 21:16:58 +0800 Subject: lsp: textDocument/definition can return Location or Location[] (#12014) * https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition Co-authored-by: Khangal Jargalsaikhan --- runtime/lua/vim/lsp/callbacks.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index b6396f5798..bcadd0e2c3 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -112,11 +112,20 @@ local function location_callback(_, method, result) local _ = log.info() and log.info(method, 'No location found') return nil end - util.jump_to_location(result[1]) - if #result > 1 then - util.set_qflist(util.locations_to_items(result)) - api.nvim_command("copen") - api.nvim_command("wincmd p") + + -- textDocument/definition can return Location or Location[] + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition + + if vim.tbl_islist(result) then + util.jump_to_location(result[1]) + + if #result > 1 then + util.set_qflist(util.locations_to_items(result)) + api.nvim_command("copen") + api.nvim_command("wincmd p") + end + else + util.jump_to_location(result) end end -- cgit From 633322a020b0253aeb8c6eaeeed17d8a8fb53229 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 21 Apr 2020 22:42:48 +0900 Subject: lsp: do not assert even if the code does not exist in ErrorCodes (#11981) There is ErrorCodes in the LSP specification, but in ResponseError.code it is not used and the actual type is number. Some language servers response original error cods and this is valid spec. So we shouldn't assert even if the code does not exist in ErrorCodes. ref: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#responseMessage --- runtime/lua/vim/lsp/rpc.lua | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 74d73da31f..dad1dc11f1 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -140,14 +140,23 @@ local function format_rpc_error(err) validate { err = { err, 't' }; } - local code_name = assert(protocol.ErrorCodes[err.code], "err.code is invalid") - local message_parts = {"RPC", code_name} + + -- There is ErrorCodes in the LSP specification, + -- but in ResponseError.code it is not used and the actual type is number. + local code + if protocol.ErrorCodes[err.code] then + code = string.format("code_name = %s,", protocol.ErrorCodes[err.code]) + else + code = string.format("code_name = unknown, code = %s,", err.code) + end + + local message_parts = {"RPC[Error]", code} if err.message then - table.insert(message_parts, "message = ") + table.insert(message_parts, "message =") table.insert(message_parts, string.format("%q", err.message)) end if err.data then - table.insert(message_parts, "data = ") + table.insert(message_parts, "data =") table.insert(message_parts, vim.inspect(err.data)) end return table.concat(message_parts, ' ') -- cgit From 978a6bcaf2b98b3c89381a3eacf642b4f61db033 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 22 Mar 2020 21:40:12 +0000 Subject: vim-patch:8.1.2225: the "last used" info of a buffer is under used Problem: The "last used" info of a buffer is under used. Solution: Add "lastused" to getbufinfo(). List buffers sorted by last-used field. (Andi Massimino, closes vim/vim#4722) https://github.com/vim/vim/commit/52410575be50d5c40bbe6380159df48cfc382ceb --- runtime/doc/eval.txt | 3 +++ runtime/doc/options.txt | 2 ++ runtime/doc/windows.txt | 1 + 3 files changed, 6 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 29c5c37bcd..4ff6269004 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4164,6 +4164,9 @@ getbufinfo([{dict}]) changed TRUE if the buffer is modified. changedtick number of changes made to the buffer. hidden TRUE if the buffer is hidden. + lastused timestamp in seconds, like + |localtime()|, when the buffer was + last used. listed TRUE if the buffer is listed. lnum current line number in buffer. linecount number of lines in the buffer (only diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 5fb80d75ce..009695c13c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6742,6 +6742,8 @@ A jump table for the options with a short description can be found at |Q_op|. complete first match. "list:longest" When more than one match, list all matches and complete till longest common string. + "list:lastused" When more than one buffer matches, sort buffers + by time last used (other than the current buffer). When there is only a single match, it is fully completed in all cases. Examples: > diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index dbe08a71ef..b5623f4ea4 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1023,6 +1023,7 @@ list of buffers. |unlisted-buffer| # alternate buffer R terminal buffers with a running job F terminal buffers with a finished job + t show time last used and sort buffers Combining flags means they are "and"ed together, e.g.: h+ hidden buffers which are modified a+ active buffers which are modified -- cgit From deb4566cab91a487f2b0b0b5b4b427138f377afe Mon Sep 17 00:00:00 2001 From: Thore Weilbier Date: Thu, 23 Apr 2020 13:36:19 +0200 Subject: lsp: callback for references now opens qf (#12171) In contrast to other callbacks for LSP requests like `textDocument/documentSymbols`, does the one for references not open the quickfix window after the quickfix list was filled. This left the user in a situation he don't know what or if something had happen. Related to: neovim/neovim#12170 --- runtime/lua/vim/lsp/callbacks.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index bcadd0e2c3..9c30085f37 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -50,6 +50,8 @@ end M['textDocument/references'] = function(_, _, result) if not result then return end util.set_qflist(util.locations_to_items(result)) + api.nvim_command("copen") + api.nvim_command("wincmd p") end M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr) -- cgit From 78d58eaf61b361ed9b2849ecc1afc99897c01058 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 25 Apr 2020 21:58:35 +0900 Subject: lsp: remove buffer version on buffer_detach (#12029) When we save the buffer, the buffer is detached and attached again. So the client also needs to remove the buffer version once. --- runtime/lua/vim/lsp.lua | 2 ++ runtime/lua/vim/lsp/util.lua | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index afff4d9900..17135e078c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -198,6 +198,7 @@ local function text_document_did_open_handler(bufnr, client) } } client.notify('textDocument/didOpen', params) + util.buf_versions[bufnr] = params.textDocument.version end --- LSP client object. @@ -722,6 +723,7 @@ function lsp.buf_attach_client(bufnr, client_id) client.notify('textDocument/didClose', params) end end) + util.buf_versions[bufnr] = nil all_buffer_active_clients[bufnr] = nil end; -- TODO if we know all of the potential clients ahead of time, then we diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index ce5baf5b4b..4c73bf8c5f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -134,8 +134,7 @@ end function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - -- TODO(ashkan) check this is correct. - if (M.buf_versions[bufnr] or 0) > text_document.version then + if M.buf_versions[bufnr] > text_document.version then print("Buffer ", text_document.uri, " newer than edits.") return end -- cgit From ef0398fe88e6cc74f33fb20519997774168d7832 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 25 Apr 2020 15:46:58 +0200 Subject: LSP: Expose diagnostics grouped by bufnr (#11932) Expose `vim.lsp.buf.diagnostics_by_buf` This makes it easier to customize the diagnostics behavior. For example to defer the update they can override the `textDocument/publishDiagnostics` callback to only call `buf_diagnostics_save_positions` and then defer the other actions to a autocmd event. --- runtime/doc/lsp.txt | 24 +++++++++++-- runtime/lua/vim/lsp/util.lua | 86 +++++++++++++++++++++++++++----------------- 2 files changed, 76 insertions(+), 34 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 4c59e53343..c7baec0491 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -915,6 +915,21 @@ apply_text_edits({text_edits}, {bufnr}) apply_workspace_edit({workspace_edit}) TODO: Documentation + *vim.lsp.util.diagnostics_by_buf* +diagnostics_by_buf + A table containing diagnostics grouped by buf. + + {: {diagnostics}} + + {diagnostics} is an array of diagnostics. + + By default this is populated by the + `textDocument/publishDiagnostics` callback via + |vim.lsp.util.buf_diagnostics_save_positions|. + + It contains entries for active buffers. Once a buffer is + detached the entries for it are discarded. + buf_clear_diagnostics({bufnr}) *vim.lsp.util.buf_clear_diagnostics()* TODO: Documentation @@ -945,9 +960,14 @@ buf_diagnostics_count({kind}) buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* TODO: Documentation - *vim.lsp.util.buf_diagnostics_save_positions()* + *vim.lsp.util.buf_diagnostics_save()* buf_diagnostics_save_positions({bufnr}, {diagnostics}) - TODO: Documentation + Stores the diagnostics into |vim.lsp.util.diagnostics_by_buf| + + Parameters: ~ + {bufr} bufnr for which the diagnostics are for. + {diagnostics} Diagnostics[] received from the + langauge server. *vim.lsp.util.buf_diagnostics_underline()* buf_diagnostics_underline({bufnr}, {diagnostics}) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4c73bf8c5f..80e4399fe1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -6,6 +6,31 @@ local list_extend = vim.list_extend local M = {} +--- Diagnostics received from the server via `textDocument/publishDiagnostics` +-- by buffer. +-- +-- {: {diagnostics}} +-- +-- This contains only entries for active buffers. Entries for detached buffers +-- are discarded. +-- +-- If you override the `textDocument/publishDiagnostic` callback, +-- this will be empty unless you call `buf_diagnostics_save_positions`. +-- +-- +-- Diagnostic is: +-- +-- { +-- range: Range +-- message: string +-- severity?: DiagnosticSeverity +-- code?: number | string +-- source?: string +-- tags?: DiagnosticTag[] +-- relatedInformation?: DiagnosticRelatedInformation[] +-- } +M.diagnostics_by_buf = {} + local split = vim.split local function split_lines(value) return split(value, '\n', true) @@ -635,8 +660,6 @@ local function highlight_range(bufnr, ns, hiname, start, finish) end do - local all_buffer_diagnostics = {} - local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") local reference_ns = api.nvim_create_namespace("vim_lsp_references") local sign_ns = 'vim_lsp_signs' @@ -692,13 +715,12 @@ do -- if #marks == 0 then -- return -- end - -- local buffer_diagnostics = all_buffer_diagnostics[bufnr] local lines = {"Diagnostics:"} local highlights = {{0, "Bold"}} - local buffer_diagnostics = all_buffer_diagnostics[bufnr] + local buffer_diagnostics = M.diagnostics_by_buf[bufnr] if not buffer_diagnostics then return end - local line_diagnostics = buffer_diagnostics[line] + local line_diagnostics = M.diagnostics_group_by_line(buffer_diagnostics[line]) if not line_diagnostics then return end for i, diagnostic in ipairs(line_diagnostics) do @@ -726,6 +748,8 @@ do return popup_bufnr, winnr end + --- Saves the diagnostics (Diagnostic[]) into diagnostics_by_buf + -- function M.buf_diagnostics_save_positions(bufnr, diagnostics) validate { bufnr = {bufnr, 'n', true}; @@ -734,28 +758,15 @@ do if not diagnostics then return end bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr - if not all_buffer_diagnostics[bufnr] then + if not M.diagnostics_by_buf[bufnr] then -- Clean up our data when the buffer unloads. api.nvim_buf_attach(bufnr, false, { on_detach = function(b) - all_buffer_diagnostics[b] = nil + M.diagnostics_by_buf[b] = nil end }) end - all_buffer_diagnostics[bufnr] = {} - local buffer_diagnostics = all_buffer_diagnostics[bufnr] - - for _, diagnostic in ipairs(diagnostics) do - local start = diagnostic.range.start - -- local mark_id = api.nvim_buf_set_extmark(bufnr, diagnostic_ns, 0, start.line, 0, {}) - -- buffer_diagnostics[mark_id] = diagnostic - local line_diagnostics = buffer_diagnostics[start.line] - if not line_diagnostics then - line_diagnostics = {} - buffer_diagnostics[start.line] = line_diagnostics - end - table.insert(line_diagnostics, diagnostic) - end + M.diagnostics_by_buf[bufnr] = diagnostics end function M.buf_diagnostics_underline(bufnr, diagnostics) @@ -799,15 +810,26 @@ do end end - function M.buf_diagnostics_virtual_text(bufnr, diagnostics) - local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] - if not buffer_line_diagnostics then - M.buf_diagnostics_save_positions(bufnr, diagnostics) + function M.diagnostics_group_by_line(diagnostics) + if not diagnostics then return end + local diagnostics_by_line = {} + for _, diagnostic in ipairs(diagnostics) do + local start = diagnostic.range.start + local line_diagnostics = diagnostics_by_line[start.line] + if not line_diagnostics then + line_diagnostics = {} + diagnostics_by_line[start.line] = line_diagnostics + end + table.insert(line_diagnostics, diagnostic) end - buffer_line_diagnostics = all_buffer_diagnostics[bufnr] - if not buffer_line_diagnostics then + return diagnostics_by_line + end + + function M.buf_diagnostics_virtual_text(bufnr, diagnostics) + if not diagnostics then return end + local buffer_line_diagnostics = M.diagnostics_group_by_line(diagnostics) for line, line_diags in pairs(buffer_line_diagnostics) do local virt_texts = {} for i = 1, #line_diags - 1 do @@ -821,12 +843,12 @@ do end function M.buf_diagnostics_count(kind) local bufnr = vim.api.nvim_get_current_buf() - local buffer_line_diagnostics = all_buffer_diagnostics[bufnr] - if not buffer_line_diagnostics then return end + local diagnostics = M.diagnostics_by_buf[bufnr] + if not diagnostics then return end local count = 0 - for _, line_diags in pairs(buffer_line_diagnostics) do - for _, diag in ipairs(line_diags) do - if protocol.DiagnosticSeverity[kind] == diag.severity then count = count + 1 end + for _, diagnostic in pairs(diagnostics) do + if protocol.DiagnosticSeverity[kind] == diagnostic.severity then + count = count + 1 end end return count -- cgit From 68511924d02af03812ae4b56aea6d94009f8096e Mon Sep 17 00:00:00 2001 From: Thore Weilbier Date: Sun, 26 Apr 2020 06:47:48 +0200 Subject: LSP: remove obsolete "peek definition" code #12178 The method with the name 'textDocument/peekDefinition' is not part of the official language server protocol specification. Therefore no language server can/will support this. Thereby all related code and documentation as been removed. --- runtime/doc/lsp.txt | 7 ------- runtime/lua/vim/lsp/buf.lua | 6 ------ runtime/lua/vim/lsp/callbacks.lua | 15 --------------- runtime/lua/vim/lsp/util.lua | 25 ------------------------- 4 files changed, 53 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index c7baec0491..e53853b1b1 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -750,9 +750,6 @@ npcall({fn}, {...}) *vim.lsp.buf.npcall()* TODO: Documentation ok_or_nil({status}, {...}) *vim.lsp.buf.ok_or_nil()* - TODO: Documentation - -peek_definition() *vim.lsp.buf.peek_definition()* TODO: Documentation *vim.lsp.buf.range_formatting()* @@ -1051,10 +1048,6 @@ npcall({fn}, {...}) *vim.lsp.util.npcall()* TODO: Documentation ok_or_nil({status}, {...}) *vim.lsp.util.ok_or_nil()* - TODO: Documentation - - *vim.lsp.util.open_floating_peek_preview()* -open_floating_peek_preview({bufnr}, {start}, {finish}, {opts}) TODO: Documentation *vim.lsp.util.open_floating_preview()* diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index fc9e10cb73..587d1f52e9 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -32,12 +32,6 @@ function M.hover() request('textDocument/hover', params) end -function M.peek_definition() - local params = util.make_position_params() - request('textDocument/peekDefinition', params) -end - - function M.declaration() local params = util.make_position_params() request('textDocument/declaration', params) diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 9c30085f37..bd2cbf1ea7 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -151,21 +151,6 @@ M['textDocument/signatureHelp'] = function(_, method, result) end) end -M['textDocument/peekDefinition'] = function(_, _, result, _) - if not (result and result[1]) then return end - local loc = result[1] - local bufnr = vim.uri_to_bufnr(loc.uri) or error("not found: "..tostring(loc.uri)) - local start = loc.range.start - local finish = loc.range["end"] - util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 }) - local headbuf = util.open_floating_preview({"Peek:"}, nil, { - offset_y = -(finish.line - start.line); - width = finish.character - start.character + 2; - }) - -- TODO(ashkan) change highlight group? - api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1) -end - M['textDocument/documentHighlight'] = function(_, _, result, _) if not result then return end local bufnr = api.nvim_get_current_buf() diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 80e4399fe1..49798a452f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -621,31 +621,6 @@ function M.open_floating_preview(contents, filetype, opts) return floating_bufnr, floating_winnr end -local function validate_lsp_position(pos) - validate { pos = {pos, 't'} } - validate { - line = {pos.line, 'n'}; - character = {pos.character, 'n'}; - } - return true -end - -function M.open_floating_peek_preview(bufnr, start, finish, opts) - validate { - bufnr = {bufnr, 'n'}; - start = {start, validate_lsp_position, 'valid start Position'}; - finish = {finish, validate_lsp_position, 'valid finish Position'}; - opts = { opts, 't', true }; - } - local width = math.max(finish.character - start.character + 1, 1) - local height = math.max(finish.line - start.line + 1, 1) - local floating_winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) - api.nvim_win_set_cursor(floating_winnr, {start.line+1, start.character}) - api.nvim_command("autocmd CursorMoved * ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)") - return floating_winnr -end - - local function highlight_range(bufnr, ns, hiname, start, finish) if start[1] == finish[1] then -- TODO care about encoding here since this is in byte index? -- cgit From 663b83814d3317fd44b59e5a0ac9732494d6e4c9 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 26 Apr 2020 16:51:41 +0200 Subject: LSP: Add a check for null version in VersionedTextDocumentIdentifier (#12185) According to the spec there is the possibility that when a VersionedTextDocumentIdentifier is used in a TextEdit the value may be null. Currently we don't check for this and always assume that it's set. So currently if a TextEdit comes in for a rename for example with the version null, it fails as we are comparing the bufnumber with nil. https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier --- runtime/lua/vim/lsp/util.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 49798a452f..be391c8b5b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -159,7 +159,8 @@ end function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - if M.buf_versions[bufnr] > text_document.version then + -- `VersionedTextDocumentIdentifier`s version may be nil https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier + if text_document.version ~= nil and M.buf_versions[bufnr] > text_document.version then print("Buffer ", text_document.uri, " newer than edits.") return end -- cgit From 50ff37308abde6c33c2800529cd58f1d7413d7b3 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 26 Apr 2020 23:56:30 +0200 Subject: LSP: Fix show_line_diagnostics #12186 Messed this up in ef0398fe88e6cc74f33fb20519997774168d7832 --- runtime/lua/vim/lsp/util.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index be391c8b5b..e45104789e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -696,7 +696,7 @@ do local buffer_diagnostics = M.diagnostics_by_buf[bufnr] if not buffer_diagnostics then return end - local line_diagnostics = M.diagnostics_group_by_line(buffer_diagnostics[line]) + local line_diagnostics = M.diagnostics_group_by_line(buffer_diagnostics)[line] if not line_diagnostics then return end for i, diagnostic in ipairs(line_diagnostics) do @@ -707,6 +707,7 @@ do -- TODO(ashkan) make format configurable? local prefix = string.format("%d. ", i) local hiname = severity_highlights[diagnostic.severity] + assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity)) local message_lines = split_lines(diagnostic.message) table.insert(lines, prefix..message_lines[1]) table.insert(highlights, {#prefix + 1, hiname}) -- cgit From 5f41717838f4cd9d1087e452640ba554500279ab Mon Sep 17 00:00:00 2001 From: jakbyte Date: Sun, 26 Apr 2020 18:36:40 -0400 Subject: LSP: don't redefine LspDiagnostics signs #12164 fix #12162 --- runtime/doc/lsp.txt | 10 +++++----- runtime/lua/vim/lsp.lua | 16 ++++++++++++++++ runtime/lua/vim/lsp/util.lua | 20 +++++++++----------- 3 files changed, 30 insertions(+), 16 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index e53853b1b1..2d0bba0ffb 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -977,12 +977,12 @@ buf_diagnostics_virtual_text({bufnr}, {diagnostics}) *vim.lsp.util.buf_diagnostics_signs()* buf_diagnostics_signs({bufnr}, {diagnostics}) Place signs for each diagnostic in the sign column. - Sign characters can be customized with the following options: + Sign characters can be customized with the following commands: > -let g:LspDiagnosticsErrorSign = 'E' -let g:LspDiagnosticsWarningSign = 'W' -let g:LspDiagnosticsInformationSign = 'I' -let g:LspDiagnosticsHintSign = 'H' +sign define LspDiagnosticsErrorSign text=E texthl=LspDiagnosticsError linehl= numhl= +sign define LspDiagnosticsWarningSign text=W texthl=LspDiagnosticsWarning linehl= numhl= +sign define LspDiagnosticsInformationSign text=I texthl=LspDiagnosticsInformation linehl= numhl= +sign define LspDiagnosticsHintSign text=H texthl=LspDiagnosticsHint linehl= numhl= < diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 17135e078c..c7de2df25f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1017,5 +1017,21 @@ function lsp.get_log_path() return log.get_filename() end +local function define_default_sign(name, properties) + if not vim.fn.sign_getdefined(name) then + vim.fn.sign_define(name, properties) + end +end + +-- Define the LspDiagnostics signs if they're not defined already. +local function define_default_lsp_diagnostics_signs() + define_default_sign('LspDiagnosticsErrorSign', {text='E', texthl='LspDiagnosticsError', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsWarningSign', {text='W', texthl='LspDiagnosticsWarning', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsInformationSign', {text='I', texthl='LspDiagnosticsInformation', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsHintSign', {text='H', texthl='LspDiagnosticsHint', linehl='', numhl=''}) +end + +define_default_lsp_diagnostics_signs() + return lsp -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e45104789e..53e2240ff5 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -818,6 +818,7 @@ do api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {}) end end + function M.buf_diagnostics_count(kind) local bufnr = vim.api.nvim_get_current_buf() local diagnostics = M.diagnostics_by_buf[bufnr] @@ -830,19 +831,16 @@ do end return count end - function M.buf_diagnostics_signs(bufnr, diagnostics) - vim.fn.sign_define('LspDiagnosticsErrorSign', {text=vim.g['LspDiagnosticsErrorSign'] or 'E', texthl='LspDiagnosticsError', linehl='', numhl=''}) - vim.fn.sign_define('LspDiagnosticsWarningSign', {text=vim.g['LspDiagnosticsWarningSign'] or 'W', texthl='LspDiagnosticsWarning', linehl='', numhl=''}) - vim.fn.sign_define('LspDiagnosticsInformationSign', {text=vim.g['LspDiagnosticsInformationSign'] or 'I', texthl='LspDiagnosticsInformation', linehl='', numhl=''}) - vim.fn.sign_define('LspDiagnosticsHintSign', {text=vim.g['LspDiagnosticsHintSign'] or 'H', texthl='LspDiagnosticsHint', linehl='', numhl=''}) + local diagnostic_severity_map = { + [protocol.DiagnosticSeverity.Error] = "LspDiagnosticsErrorSign"; + [protocol.DiagnosticSeverity.Warning] = "LspDiagnosticsWarningSign"; + [protocol.DiagnosticSeverity.Information] = "LspDiagnosticsInformationSign"; + [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; + } + + function M.buf_diagnostics_signs(bufnr, diagnostics) for _, diagnostic in ipairs(diagnostics) do - local diagnostic_severity_map = { - [protocol.DiagnosticSeverity.Error] = "LspDiagnosticsErrorSign"; - [protocol.DiagnosticSeverity.Warning] = "LspDiagnosticsWarningSign"; - [protocol.DiagnosticSeverity.Information] = "LspDiagnosticsInformationSign"; - [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; - } vim.fn.sign_place(0, sign_ns, diagnostic_severity_map[diagnostic.severity], bufnr, {lnum=(diagnostic.range.start.line+1)}) end end -- cgit From 630ec6cfb8670607ddfc67dd4e56e98c17746ca6 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 24 Feb 2020 16:40:58 +0800 Subject: API/UI: Allow UI to set PUM position and size, and pass the position to CompleteChanged --- runtime/doc/api.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 39b6c6417d..c5ccf35c8e 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2341,6 +2341,22 @@ nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()* Parameters: ~ {height} Popupmenu height, must be greater than zero. + *nvim_ui_pum_set_bounds()* +nvim_ui_pum_set_bounds({width}, {height}, {row}, {col}) + + Tells Nvim the geometry of the popumenu, to align floating + windows with an external popup menu. Note that this method + is not to be confused with |nvim_ui_pum_set_height()|, which + sets the number of visible items in the popup menu, while + this function sets the bounding box of the popup menu, + including visual decorations such as boarders and sliders. + + Parameters: ~ + {width} Popupmenu width, must be greater than zero. + {height} Popupmenu height, must be greater than zero. + {row} Popupmenu row, must be greater or equal to zero. + {height} Popupmenu height, must be greater or equal to zero. + nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()* TODO: Documentation -- cgit From 9c85caa390ccf6295233c4201a60ccfa66417816 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Tue, 3 Mar 2020 17:43:02 +0800 Subject: ui_pum_get_pos: return internal pum position if external pum pos not found --- runtime/doc/api.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c5ccf35c8e..a2e0c56f85 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2352,10 +2352,10 @@ nvim_ui_pum_set_bounds({width}, {height}, {row}, {col}) including visual decorations such as boarders and sliders. Parameters: ~ - {width} Popupmenu width, must be greater than zero. - {height} Popupmenu height, must be greater than zero. - {row} Popupmenu row, must be greater or equal to zero. - {height} Popupmenu height, must be greater or equal to zero. + {width} Popupmenu width. + {height} Popupmenu height. + {row} Popupmenu row. + {height} Popupmenu height. nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()* TODO: Documentation -- cgit From 4e6531ddbdce9944f9986ca5357b71171a14f05e Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 28 Apr 2020 23:41:39 +0900 Subject: lsp: use vim.tbl_isempty to check sign (#12190) ref: #12164 fix #12201 sign_getdefined() returns a list, {} if the sign is not defined. --- runtime/lua/vim/lsp.lua | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c7de2df25f..c01b5d8e0c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1017,21 +1017,18 @@ function lsp.get_log_path() return log.get_filename() end -local function define_default_sign(name, properties) - if not vim.fn.sign_getdefined(name) then - vim.fn.sign_define(name, properties) - end -end - -- Define the LspDiagnostics signs if they're not defined already. -local function define_default_lsp_diagnostics_signs() +do + local function define_default_sign(name, properties) + if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then + vim.fn.sign_define(name, properties) + end + end define_default_sign('LspDiagnosticsErrorSign', {text='E', texthl='LspDiagnosticsError', linehl='', numhl=''}) define_default_sign('LspDiagnosticsWarningSign', {text='W', texthl='LspDiagnosticsWarning', linehl='', numhl=''}) define_default_sign('LspDiagnosticsInformationSign', {text='I', texthl='LspDiagnosticsInformation', linehl='', numhl=''}) define_default_sign('LspDiagnosticsHintSign', {text='H', texthl='LspDiagnosticsHint', linehl='', numhl=''}) end -define_default_lsp_diagnostics_signs() - return lsp -- vim:sw=2 ts=2 et -- cgit From e9cc383614d449b7269632c991525db77c387154 Mon Sep 17 00:00:00 2001 From: Yen3 Date: Tue, 28 Apr 2020 16:47:22 +0200 Subject: LSP: support tagstack #12096 --- runtime/lua/vim/lsp/util.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 53e2240ff5..9ca18cb2b1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -421,7 +421,13 @@ function M.jump_to_location(location) local bufnr = vim.uri_to_bufnr(location.uri) -- Save position in jumplist vim.cmd "normal! m'" - -- TODO(ashkan) use tagfunc here to update tagstack. + + -- Push a new item into tagstack + local items = {} + table.insert(items, {tagname=vim.fn.expand(""), from=vim.fn.getpos('.')}) + vim.fn.settagstack(vim.fn.bufnr('%'), {items=items}, 't') + + --- Jump to new location api.nvim_set_current_buf(bufnr) local row = location.range.start.line local col = location.range.start.character -- cgit From c477b19bdca0cc57e08f5fb93f647767ea84dd3e Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 29 Apr 2020 00:27:14 +0900 Subject: vim-patch:8.2.0084: complete item "user_data" can only be a string Problem: Complete item "user_data" can only be a string. Solution: Accept any type of variable. (closes vim/vim#5412) https://github.com/vim/vim/commit/0892832bb6c7e322fcae8560eaad5a8140ee4a06 --- runtime/doc/insert.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index fce4d8628e..e53af5074b 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1083,7 +1083,8 @@ items: empty when non-zero this match will be added even when it is an empty string user_data custom data which is associated with the item and - available in |v:completed_item| + available in |v:completed_item|; it can be any type; + defaults to an empty string All of these except "icase", "equal", "dup" and "empty" must be a string. If an item does not meet these requirements then an error message is given and -- cgit From e4a1be779b9a6bb9a47700ebf92f8dd934bb1712 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 29 Apr 2020 10:32:34 +0900 Subject: lsp/completion: Expose completion_item under completed_items.user_data. By passing through completion_item it's now possible for snippet plugins to add LSP snippet support. --- runtime/lua/vim/lsp/util.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 53e2240ff5..9a51bc2557 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -240,6 +240,13 @@ function M.text_document_completion_list_to_complete_items(result, prefix) icase = 1, dup = 1, empty = 1, + user_data = { + nvim = { + lsp = { + completion_item = completion_item + } + } + }, }) end -- cgit From f9055c585f597fe4ea8ecb990927a2826234393c Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Wed, 29 Apr 2020 16:53:13 +0200 Subject: LSP: enable using different highlighting rules for LSP signs (#12176) This commit creates 4 new highlight groups: - LspDiagnosticsErrorSign - LspDiagnosticsWarningSign - LspDiagnosticsInformationSign - LspDiagnosticsHintSign These highlight groups are linked to their corresponding LspDiagnostics highlight groups by default. This lets users choose a different color for their sign columns and virtualtext diagnostics. --- runtime/doc/lsp.txt | 16 ++++++++++++---- runtime/lua/vim/lsp.lua | 8 ++++---- runtime/lua/vim/lsp/util.lua | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 2d0bba0ffb..8140b6a15e 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -163,13 +163,21 @@ name: > LSP HIGHLIGHT *lsp-highlight* *hl-LspDiagnosticsError* -LspDiagnosticsError used for "Error" diagnostic virtual text +LspDiagnosticsError used for "Error" diagnostic virtual text + *hl-LspDiagnosticsErrorSign* +LspDiagnosticsErrorSign used for "Error" diagnostic signs in sign column *hl-LspDiagnosticsWarning* -LspDiagnosticsWarning used for "Warning" diagnostic virtual text +LspDiagnosticsWarning used for "Warning" diagnostic virtual text + *hl-LspDiagnosticsWarningSign* +LspDiagnosticsWarningSign used for "Warning" diagnostic signs in sign column *hl-LspDiagnosticsInformation* -LspDiagnosticInformation used for "Information" diagnostic virtual text +LspDiagnosticInformation used for "Information" diagnostic virtual text + *hl-LspDiagnosticsInformationSign* +LspDiagnosticInformationSign used for "Information" signs in sign column *hl-LspDiagnosticsHint* -LspDiagnosticHint used for "Hint" diagnostic virtual text +LspDiagnosticHint used for "Hint" diagnostic virtual text + *hl-LspDiagnosticsHintSign* +LspDiagnosticHintSign used for "Hint" diagnostic signs in sign column *hl-LspReferenceText* LspReferenceText used for highlighting "text" references *hl-LspReferenceRead* diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index c01b5d8e0c..4c1c52c796 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1024,10 +1024,10 @@ do vim.fn.sign_define(name, properties) end end - define_default_sign('LspDiagnosticsErrorSign', {text='E', texthl='LspDiagnosticsError', linehl='', numhl=''}) - define_default_sign('LspDiagnosticsWarningSign', {text='W', texthl='LspDiagnosticsWarning', linehl='', numhl=''}) - define_default_sign('LspDiagnosticsInformationSign', {text='I', texthl='LspDiagnosticsInformation', linehl='', numhl=''}) - define_default_sign('LspDiagnosticsHintSign', {text='H', texthl='LspDiagnosticsHint', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsErrorSign', {text='E', texthl='LspDiagnosticsErrorSign', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsWarningSign', {text='W', texthl='LspDiagnosticsWarningSign', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsInformationSign', {text='I', texthl='LspDiagnosticsInformationSign', linehl='', numhl=''}) + define_default_sign('LspDiagnosticsHintSign', {text='H', texthl='LspDiagnosticsHintSign', linehl='', numhl=''}) end return lsp diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 9ca18cb2b1..7d4dc072e5 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -672,6 +672,7 @@ do table.insert(cmd_parts, k.."="..v) end api.nvim_command(table.concat(cmd_parts, ' ')) + api.nvim_command('highlight link ' .. highlight_name .. 'Sign ' .. highlight_name) severity_highlights[severity] = highlight_name end -- cgit From e5022c61ed769153dab5a91752c52a8f9ad3b504 Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux <39092278+vigoux@users.noreply.github.com> Date: Fri, 1 May 2020 07:43:30 +0200 Subject: treesitter: unknown predicates always match #12173 --- runtime/lua/vim/treesitter.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 1836227540..d3b78a7f73 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -159,6 +159,9 @@ function Query:match_preds(match, pattern, bufnr) end local regexes = self.regexes[pattern] for i, pred in pairs(preds) do + -- Here we only want to return if a predicate DOES NOT match, and + -- continue on the other case. This way unknown predicates will not be considered, + -- which allows some testing and easier user extensibility (#12173). if pred[1] == "eq?" then local node = match[pred[2]] local node_text = get_node_text(node, bufnr) @@ -184,9 +187,9 @@ function Query:match_preds(match, pattern, bufnr) if start_row ~= end_row then return false end - return regexes[i]:match_line(bufnr, start_row, start_col, end_col) - else - return false + if not regexes[i]:match_line(bufnr, start_row, start_col, end_col) then + return false + end end end return true -- cgit From fe4383216b7a487d540172a9f5a4beade65056ba Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Fri, 1 May 2020 16:34:28 +0200 Subject: doc/UI: mode_info_set: mention colors should be swapped #12211 When attr_id is 0, the cursor's colors should be swapped, otherwise the cursor might be invisible. Closes #12198 --- runtime/doc/ui.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index b243d9ba50..2817c1015b 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -161,7 +161,9 @@ the editor. `cursor_shape`: "block", "horizontal", "vertical" `cell_percentage`: Cell % occupied by the cursor. `blinkwait`, `blinkon`, `blinkoff`: See |cursor-blinking|. - `attr_id`: Cursor attribute id (defined by `hl_attr_define`) + `attr_id`: Cursor attribute id (defined by `hl_attr_define`). + When attr_id is 0, the background and foreground + colors should be swapped. `attr_id_lm`: Cursor attribute id for when 'langmap' is active. `short_name`: Mode code name, see 'guicursor'. `name`: Mode descriptive name. -- cgit From 6dc8398944fd86038b07d77fcab92cd282555dee Mon Sep 17 00:00:00 2001 From: ckipp01 Date: Mon, 27 Apr 2020 10:55:06 +0200 Subject: [LSP] check for vim.NIL and add apply_text_document_edit tests --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 68f3b35df3..82b9a0b3aa 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -160,7 +160,7 @@ function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) -- `VersionedTextDocumentIdentifier`s version may be nil https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier - if text_document.version ~= nil and M.buf_versions[bufnr] > text_document.version then + if text_document.version ~= vim.NIL and M.buf_versions[bufnr] > text_document.version then print("Buffer ", text_document.uri, " newer than edits.") return end -- cgit From d0af0f5c9e12e0d9070c0709412eb9fd55534295 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 2 May 2020 15:08:52 +0900 Subject: lsp: fix lsp.util.symbols_to_items fix: https://github.com/neovim/neovim/pull/11931#issuecomment-622422581 There was an error in the process of flattening the hierarchical structure. So when DocumentSymbol has children, our client can't handle it correctly. --- runtime/lua/vim/lsp/util.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 68f3b35df3..bd4f55846c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -956,10 +956,8 @@ function M.symbols_to_items(symbols, bufnr) text = '['..kind..'] '..symbol.name }) if symbol.children then - for _, child in ipairs(symbol) do - for _, v in ipairs(_symbols_to_items(child, _items, _bufnr)) do - vim.list_extend(_items, v) - end + for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do + vim.list_extend(_items, v) end end end -- cgit From 2f42e4d0c86163e64eb56077939fe405dc434e42 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 2 May 2020 15:21:07 +0200 Subject: LSP: Support LocationLink (#12231) * support LocationLink in callbacks * announce linkSupport in client capabilities --- runtime/lua/vim/lsp/protocol.lua | 12 ++++++++++++ runtime/lua/vim/lsp/util.lua | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 41e8119c8c..bca4840674 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -644,6 +644,18 @@ function protocol.make_client_capabilities() -- TODO(tjdevries): Implement this contextSupport = false; }; + declaration = { + linkSupport = true; + }; + definition = { + linkSupport = true; + }; + implementation = { + linkSupport = true; + }; + typeDefinition = { + linkSupport = true; + }; hover = { dynamicRegistration = false; contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 68f3b35df3..9f1275b43c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -424,8 +424,10 @@ function M.make_floating_popup_options(width, height, opts) end function M.jump_to_location(location) - if location.uri == nil then return end - local bufnr = vim.uri_to_bufnr(location.uri) + -- location may be Location or LocationLink + local uri = location.uri or location.targetUri + if uri == nil then return end + local bufnr = vim.uri_to_bufnr(uri) -- Save position in jumplist vim.cmd "normal! m'" @@ -436,8 +438,9 @@ function M.jump_to_location(location) --- Jump to new location api.nvim_set_current_buf(bufnr) - local row = location.range.start.line - local col = location.range.start.character + local range = location.range or location.targetSelectionRange + local row = range.start.line + local col = range.start.character local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] col = vim.str_byteindex(line, col) api.nvim_win_set_cursor(0, {row + 1, col}) @@ -876,9 +879,11 @@ function M.locations_to_items(locations) end; }) for _, d in ipairs(locations) do - local start = d.range.start - local fname = assert(vim.uri_to_fname(d.uri)) - table.insert(grouped[fname], {start = start}) + -- locations may be Location or LocationLink + local uri = d.uri or d.targetUri + local fname = assert(vim.uri_to_fname(uri)) + local range = d.range or d.targetSelectionRange + table.insert(grouped[fname], {start = range.start}) end -- cgit From ea347b18d883999ed9a8d2e7c00068058135232f Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 2 May 2020 17:56:05 +0200 Subject: lsp: add workspace/symbol (#12224) * lsp: add workspace/symbol * refactor symbol callback * set hierarchical symbol support to true * add documentation and default mapping Co-authored-by: Hirokazu Hata --- runtime/doc/lsp.txt | 8 ++++++++ runtime/lua/vim/lsp.lua | 1 + runtime/lua/vim/lsp/buf.lua | 6 ++++++ runtime/lua/vim/lsp/callbacks.lua | 4 +++- runtime/lua/vim/lsp/protocol.lua | 13 +++++++++++++ 5 files changed, 31 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 8140b6a15e..249136f32f 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -49,6 +49,7 @@ go-to-definition, hover, etc. Example config: > nnoremap 1gD lua vim.lsp.buf.type_definition() nnoremap gr lua vim.lsp.buf.references() nnoremap g0 lua vim.lsp.buf.document_symbol() + nnoremap gW lua vim.lsp.buf.workspace_symbol() Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows |i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of @@ -783,6 +784,13 @@ signature_help() *vim.lsp.buf.signature_help()* type_definition() *vim.lsp.buf.type_definition()* TODO: Documentation +workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()* + Lists all symbols in the current workspace in the quickfix + window. The list is filtered against the optional argument + {query}; if the argument is omitted from the call, the user + is prompted to enter a string on the command line. An empty + string means no filtering is done. + ============================================================================== Lua module: vim.lsp.callbacks *lsp-callbacks* diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4c1c52c796..61da2130c8 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -533,6 +533,7 @@ function lsp.start_client(config) or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') + or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol') then callback(unsupported_method(method), method, nil, client_id, bufnr) return diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 587d1f52e9..0b45951a56 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -137,6 +137,12 @@ function M.document_symbol() request('textDocument/documentSymbol', params) end +function M.workspace_symbol(query) + query = query or npcall(vfn.input, "Query: ") + local params = {query = query} + request('workspace/symbol', params) +end + --- Send request to server to resolve document highlights for the --- current text document position. This request can be associated --- to key mapping or to events such as `CursorHold`, eg: diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index bd2cbf1ea7..70d21be8e7 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -54,13 +54,15 @@ M['textDocument/references'] = function(_, _, result) api.nvim_command("wincmd p") end -M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr) +local symbol_callback = function(_, _, result, _, bufnr) if not result or vim.tbl_isempty(result) then return end util.set_qflist(util.symbols_to_items(result, bufnr)) api.nvim_command("copen") api.nvim_command("wincmd p") end +M['textDocument/documentSymbol'] = symbol_callback +M['workspace/symbol'] = symbol_callback M['textDocument/rename'] = function(_, _, result) if not result then return end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index bca4840674..1c499d23a6 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -688,6 +688,19 @@ function protocol.make_client_capabilities() }; hierarchicalDocumentSymbolSupport = true; }; + workspaceSymbol = { + dynamicRegistration = false; + symbolKind = { + valueSet = (function() + local res = {} + for k in pairs(protocol.SymbolKind) do + if type(k) == 'number' then table.insert(res, k) end + end + return res + end)(); + }; + hierarchicalWorkspaceSymbolSupport = true; + }; }; workspace = nil; experimental = nil; -- cgit From 501ef952983dba09b160d2e910d3842364502d7c Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 3 May 2020 18:01:04 +0200 Subject: lsp: fixup workspace symbol capabilities (#12233) use workspace.symbol instead of workspaceSymbol to mimic the lsp spec. --- runtime/lua/vim/lsp/protocol.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 1c499d23a6..76817e3a4a 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -688,7 +688,9 @@ function protocol.make_client_capabilities() }; hierarchicalDocumentSymbolSupport = true; }; - workspaceSymbol = { + }; + workspace = { + symbol = { dynamicRegistration = false; symbolKind = { valueSet = (function() @@ -702,7 +704,6 @@ function protocol.make_client_capabilities() hierarchicalWorkspaceSymbolSupport = true; }; }; - workspace = nil; experimental = nil; } end -- cgit From 560ce2535914cc45fe5c12fdd09665dd24a80310 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 29 Apr 2020 19:31:26 -0400 Subject: vim-patch:8.2.0663: not all systemd temp files are recognized Problem: Not all systemd temp files are recognized. Solution: Add two more patterns. (Jamie Macdonald, closes vim/vim#6003) https://github.com/vim/vim/commit/512fe833c3988bfe0de22135aef67faf51927a0e --- runtime/filetype.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 5068f9be76..dc0bca2c60 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar -" Last Change: 2020 Apr 12 +" Last Change: 2020 Apr 29 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -1646,7 +1646,9 @@ au BufNewFile,BufRead */etc/systemd/system/*.d/*.conf setf systemd au BufNewFile,BufRead */.config/systemd/user/*.d/*.conf setf systemd " Systemd temp files au BufNewFile,BufRead */etc/systemd/system/*.d/.#* setf systemd +au BufNewFile,BufRead */etc/systemd/system/.#* setf systemd au BufNewFile,BufRead */.config/systemd/user/*.d/.#* setf systemd +au BufNewFile,BufRead */.config/systemd/user/.#* setf systemd " Synopsys Design Constraints au BufNewFile,BufRead *.sdc setf sdc -- cgit From 88f46501144801736c22db49eb96f88b98903028 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 3 May 2020 13:35:10 -0400 Subject: vim-patch:8.2.0688: output clobbered if setting 'verbose' to see shell commands Problem: Output clobbered if setting 'verbose' to see shell commands. Solution: Only output "Searching for" when 'verbose' is 11 or higher. https://github.com/vim/vim/commit/647a530b33d9d767f591159c24c62de48e57dad7 --- runtime/doc/options.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 009695c13c..8d4f76d3dd 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6516,9 +6516,11 @@ A jump table for the options with a short description can be found at |Q_op|. >= 1 When the shada file is read or written. >= 2 When a file is ":source"'ed. >= 3 UI info, terminal capabilities + >= 4 Shell commands. >= 5 Every searched tags file and include file. >= 8 Files for which a group of autocommands is executed. >= 9 Every executed autocommand. + >= 11 Finding items in a path >= 12 Every executed function. >= 13 When an exception is thrown, caught, finished, or discarded. >= 14 Anything pending in a ":finally" clause. -- cgit From ebee77e73c2977e8b13e0b8c98ad65840bfa2eb3 Mon Sep 17 00:00:00 2001 From: Booome <604772159@qq.com> Date: Tue, 5 May 2020 10:58:45 +0800 Subject: checkhealth/ruby: fix off-by-one error #12245 Co-authored-by: BodongLiKolmostar --- runtime/autoload/health/provider.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 86f9f060ff..6d481e9f49 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -589,7 +589,7 @@ function! s:check_ruby() abort \ 'Are you behind a firewall or proxy?']) return endif - let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 1, 'not found') + let latest_gem = get(split(latest_gem, 'neovim (\|, \|)$' ), 0, 'not found') let current_gem_cmd = host .' --version' let current_gem = s:system(current_gem_cmd) -- cgit From 2c40a38b39a944a3e1a90302c1061b4e6e3ba6ac Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 5 May 2020 05:06:40 +0200 Subject: LSP: Avoid URI-to-fname conversion for non-file URIs #12243 Fixes https://github.com/neovim/neovim/issues/12210 --- runtime/lua/vim/uri.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index d91fb7ffd3..e28cc9e20f 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -66,7 +66,13 @@ local function uri_from_fname(path) end local function uri_from_bufnr(bufnr) - return uri_from_fname(vim.api.nvim_buf_get_name(bufnr)) + local fname = vim.api.nvim_buf_get_name(bufnr) + local scheme = fname:match("^([a-z]+)://.*") + if scheme then + return fname + else + return uri_from_fname(fname) + end end local function uri_to_fname(uri) @@ -83,7 +89,12 @@ end -- Return or create a buffer for a uri. local function uri_to_bufnr(uri) - return vim.fn.bufadd((uri_to_fname(uri))) + local scheme = assert(uri:match("^([a-z]+)://.*"), 'Uri must contain a scheme: ' .. uri) + if scheme == 'file' then + return vim.fn.bufadd(uri_to_fname(uri)) + else + return vim.fn.bufadd(uri) + end end return { -- cgit From f605eeec65a3f4923e02f97fcef713365cd2e588 Mon Sep 17 00:00:00 2001 From: Gabriel Sanches Date: Tue, 5 May 2020 00:12:35 -0300 Subject: lsp: fix tagstack for location jump #12248 --- runtime/lua/vim/lsp/util.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6a1e799489..494eebf9ea 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -432,9 +432,9 @@ function M.jump_to_location(location) vim.cmd "normal! m'" -- Push a new item into tagstack - local items = {} - table.insert(items, {tagname=vim.fn.expand(""), from=vim.fn.getpos('.')}) - vim.fn.settagstack(vim.fn.bufnr('%'), {items=items}, 't') + local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0} + local items = {{tagname=vim.fn.expand(''), from=from}} + vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't') --- Jump to new location api.nvim_set_current_buf(bufnr) -- cgit From 7b764bb43d9c45e04a8dbb0146b3529991007949 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 4 May 2020 21:04:47 -0700 Subject: terminal: disable 'scrolloff' (fixes flicker) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Besides the special-case in get_scrolloff_value(), it makes sense for 'scrolloff' and 'sidescrolloff' to reflect the correct values (for plugins, scripts, …). ref 53d607af9c53accfd634435908fb79061f1212b9 ref #11915 ref #12230 --- runtime/doc/nvim_terminal_emulator.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 55c5335a60..a96d118667 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -50,6 +50,13 @@ mode" in a normal buffer, such as |i| or |:startinsert|. In this mode all keys except are sent to the underlying program. Use to return to normal-mode. |CTRL-\_CTRL-N| +Terminal-mode forces these local options: + + 'nocursorline' + 'nocursorcolumn' + 'scrolloff' = 0 + 'sidescrolloff' = 0 + Terminal-mode has its own |:tnoremap| namespace for mappings, this can be used to automate any terminal interaction. -- cgit From d2766b06c8fc50d06765c5c607744cc6b5f5ef0a Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Tue, 5 May 2020 18:15:45 +0300 Subject: vim-patch:8.1.1120: cannot easily get directory entry matches #12222 Problem: Cannot easily get directory entry matches. Solution: Add the readdir() function. (Yasuhiro Matsumoto, closes vim/vim#2439) https://github.com/vim/vim/commit/543c9b1921d7605498b54afdef518e312f1b4515 closes #12212 --- runtime/doc/eval.txt | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4ff6269004..6f13b34876 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -6688,6 +6688,33 @@ range({expr} [, {max} [, {stride}]]) *range()* range(2, -2, -1) " [2, 1, 0, -1, -2] range(0) " [] range(2, 0) " error! +< + *readdir()* +readdir({directory} [, {expr}]) + Return a list with file and directory names in {directory}. + + When {expr} is omitted all entries are included. + When {expr} is given, it is evaluated to check what to do: + If {expr} results in -1 then no further entries will + be handled. + If {expr} results in 0 then this entry will not be + added to the list. + If {expr} results in 1 then this entry will be added + to the list. + Each time {expr} is evaluated |v:val| is set to the entry name. + When {expr} is a function the name is passed as the argument. + For example, to get a list of files ending in ".txt": > + readdir(dirname, {n -> n =~ '.txt$'}) +< To skip hidden and backup files: > + readdir(dirname, {n -> n !~ '^\.\|\~$'}) + +< If you want to get a directory tree: > + function! s:tree(dir) + return {a:dir : map(readdir(a:dir), + \ {_, x -> isdirectory(x) ? + \ {x : s:tree(a:dir . '/' . x)} : x})} + endfunction + echo s:tree(".") < *readfile()* readfile({fname} [, {binary} [, {max}]]) @@ -6720,17 +6747,6 @@ readfile({fname} [, {binary} [, {max}]]) the result is an empty list. Also see |writefile()|. - *readdir()* -readdir({directory} [, {expr}]) - Return a list with file and directory names in {directory}. - You can also use |glob()| if you don't need to do complicated - things, such as limiting the number of matches. - - When {expr} is omitted all entries are included. - When {expr} is given, it is evaluated to check what to do: - If {expr} results in -1 then no further entries will - be handled. - reg_executing() *reg_executing()* Returns the single letter name of the register being executed. Returns an empty string when no register is being executed. -- cgit From 7432b3ca4ca6a5f8389c79fd17f20de22fc8e54b Mon Sep 17 00:00:00 2001 From: Gabriel Sanches Date: Thu, 7 May 2020 10:30:42 -0300 Subject: lsp: set buflisted when jumping to location (#12253) --- runtime/lua/vim/lsp/util.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 494eebf9ea..eaa5f0f02f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -438,6 +438,7 @@ function M.jump_to_location(location) --- Jump to new location api.nvim_set_current_buf(bufnr) + api.nvim_buf_set_option(0, 'buflisted', true) local range = location.range or location.targetSelectionRange local row = range.start.line local col = range.start.character -- cgit From 9a67b030d9a054648296b45b615684dee768582d Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Fri, 8 May 2020 05:23:25 +0900 Subject: lsp: Handle unknown CompletionItemKind and SymbolKind (#12257) * lsp: handle kinds not specified in protocol fix: #12200 If the client set "symbolKind.valueSet", the client must handle it properly even if it receives a value outside the specification. * test: add lsp.util.{get_completion_item_kind_name, get_symbol_kind_name} test case * lsp: make lsp.util.{get_completion_item_kind_name, get_symbol_kind_name} private --- runtime/lua/vim/lsp/util.lua | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index eaa5f0f02f..4d4762dac8 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -203,6 +203,13 @@ local function remove_unmatch_completion_items(items, prefix) end, items) end +-- Acording to LSP spec, if the client set "completionItemKind.valueSet", +-- the client must handle it properly even if it receives a value outside the specification. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +function M._get_completion_item_kind_name(completion_item_kind) + return protocol.CompletionItemKind[completion_item_kind] or "Unknown" +end + --- Getting vim complete-items with incomplete flag. -- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) -- @return { matches = complete-items table, incomplete = boolean } @@ -234,7 +241,7 @@ function M.text_document_completion_list_to_complete_items(result, prefix) table.insert(matches, { word = word, abbr = completion_item.label, - kind = protocol.CompletionItemKind[completion_item.kind] or '', + kind = M._get_completion_item_kind_name(completion_item.kind), menu = completion_item.detail or '', info = info, icase = 1, @@ -935,6 +942,13 @@ function M.set_qflist(items) }) end +-- Acording to LSP spec, if the client set "symbolKind.valueSet", +-- the client must handle it properly even if it receives a value outside the specification. +-- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol +function M._get_symbol_kind_name(symbol_kind) + return protocol.SymbolKind[symbol_kind] or "Unknown" +end + --- Convert symbols to quickfix list items --- --@symbols DocumentSymbol[] or SymbolInformation[] @@ -943,7 +957,7 @@ function M.symbols_to_items(symbols, bufnr) for _, symbol in ipairs(_symbols) do if symbol.location then -- SymbolInformation type local range = symbol.location.range - local kind = protocol.SymbolKind[symbol.kind] + local kind = M._get_symbol_kind_name(symbol.kind) table.insert(_items, { filename = vim.uri_to_fname(symbol.location.uri), lnum = range.start.line + 1, @@ -952,7 +966,7 @@ function M.symbols_to_items(symbols, bufnr) text = '['..kind..'] '..symbol.name, }) elseif symbol.range then -- DocumentSymbole type - local kind = protocol.SymbolKind[symbol.kind] + local kind = M._get_symbol_kind_name(symbol.kind) table.insert(_items, { -- bufnr = _bufnr, filename = vim.api.nvim_buf_get_name(_bufnr), -- cgit From 281e44f7bb24866d4a580d32aaeab8c8033f1fb0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 8 May 2020 16:04:41 +0200 Subject: lsp: Make apply_text_edits non-ASCII safe (#12223) * Make apply_text_edits non-ASCII safe Use `vim.str_byteindex` to correct starting and ending positions for text edits if the line contains non-ASCII characters. Fixes #12221 * text_edit may be applied to other buffers * make sure the buffer is loaded * add comments * add test for non-ASCII edits --- runtime/lua/vim/lsp/util.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 4d4762dac8..9efab73c2b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -96,16 +96,28 @@ end) function M.apply_text_edits(text_edits, bufnr) if not next(text_edits) then return end + if not api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end local start_line, finish_line = math.huge, -1 local cleaned = {} for i, e in ipairs(text_edits) do + -- adjust start and end column for UTF-16 encoding of non-ASCII characters + local start_row = e.range.start.line + local start_col = e.range.start.character + local start_bline = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1] + start_col = vim.str_byteindex(start_bline, start_col) + local end_row = e.range["end"].line + local end_col = e.range["end"].character + local end_bline = api.nvim_buf_get_lines(bufnr, end_row, end_row+1, true)[1] + end_col = vim.str_byteindex(end_bline, end_col) start_line = math.min(e.range.start.line, start_line) finish_line = math.max(e.range["end"].line, finish_line) -- TODO(ashkan) sanity check ranges for overlap. table.insert(cleaned, { i = i; - A = {e.range.start.line; e.range.start.character}; - B = {e.range["end"].line; e.range["end"].character}; + A = {start_row; start_col}; + B = {end_row; end_col}; lines = vim.split(e.newText, '\n', true); }) end @@ -113,9 +125,6 @@ function M.apply_text_edits(text_edits, bufnr) -- Reverse sort the orders so we can apply them without interfering with -- eachother. Also add i as a sort key to mimic a stable sort. table.sort(cleaned, edit_sort_key) - if not api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) - end local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false) local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol') local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) <= finish_line + 1 @@ -443,7 +452,7 @@ function M.jump_to_location(location) local items = {{tagname=vim.fn.expand(''), from=from}} vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't') - --- Jump to new location + --- Jump to new location (adjusting for UTF-16 encoding of characters) api.nvim_set_current_buf(bufnr) api.nvim_buf_set_option(0, 'buflisted', true) local range = location.range or location.targetSelectionRange -- cgit From 2f818eb9ee6a17ec5897f28e167248efb02f1c0e Mon Sep 17 00:00:00 2001 From: Felipe Morales Date: Fri, 8 May 2020 18:48:25 +0200 Subject: runtime/tutor: fix broken inline spans #12282 * runtime/syntax/tutor: fix broken inline spans Also, conceal the ends of code blocks. Re: #11401 and #12273 * fixup --- runtime/syntax/tutor.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim index 6305eef734..83ca547fdd 100644 --- a/runtime/syntax/tutor.vim +++ b/runtime/syntax/tutor.vim @@ -33,16 +33,16 @@ syn keyword tutorMarks Todo Note Tip Excersise syn region tutorCodeblock matchgroup=Delimiter start=/^\~\{3}.*$/ end=/^\~\{3}/ -syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL +syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL concealends syn match tutorShellPrompt /\(^\s*\)\@<=[$#]/ contained containedin=tutorShell -syn region tutorInlineCode matchgroup=Delimiter start=/\\\@ Date: Mon, 11 May 2020 16:56:35 +0200 Subject: LSP: Make applyEdit return a response (#12270) According to the specification workspace/applyEdit needs to respond with a `ApplyWorkspaceEditResponse` See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit This is a subset of https://github.com/neovim/neovim/pull/11607 --- runtime/lua/vim/lsp/callbacks.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 70d21be8e7..09ca4b61e4 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -17,7 +17,11 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) if workspace_edit.label then print("Workspace edit", workspace_edit.label) end - util.apply_workspace_edit(workspace_edit.edit) + local status, result = pcall(util.apply_workspace_edit, workspace_edit.edit) + return { + applied = status; + failureReason = result; + } end M['textDocument/publishDiagnostics'] = function(_, _, result) -- cgit From 03cc818fdcd1649b64d42c743c5293c06e3ac07a Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 6 May 2020 18:43:40 -0400 Subject: vim-patch:8.2.0705: indent tests don't run on CI for FreeBSD Problem: Indent tests don't run on CI for FreeBSD. Solution: Set modeline. (Ozaki Kiichi, closes vim/vim#6048) https://github.com/vim/vim/commit/3657686a0e8fd07428a25c2cf9efcc8b5d33f895 --- runtime/indent/testdir/runtest.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim index 9502c42f3e..945c2753e9 100644 --- a/runtime/indent/testdir/runtest.vim +++ b/runtime/indent/testdir/runtest.vim @@ -10,6 +10,7 @@ filetype indent on syn on set nowrapscan set report=9999 +set modeline au! SwapExists * call HandleSwapExists() func HandleSwapExists() -- cgit From 393dc2b0f5f6bdbaa4499d07e9a1d90d924d6441 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 7 May 2020 23:10:39 -0400 Subject: vim-patch:8.2.0713: the pam_environment file is not recognized Problem: The pam_environment file is not recognized. Solution: Add a filetype pattern for pamenv. (closes vim/vim#6051) https://github.com/vim/vim/commit/611548105394fdb76827cd431230c9fbfed39929 --- runtime/filetype.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index dc0bca2c60..f2ae298b48 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1129,6 +1129,9 @@ au BufNewFile,BufRead pf.conf setf pf " Pam conf au BufNewFile,BufRead */etc/pam.conf setf pamconf +" Pam environment +au BufNewFile,BufRead .pam_environment setf pamenv + " PApp au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp -- cgit From 076b0949d045d19decb2625c04128200ce8a987f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Mon, 11 May 2020 23:56:53 -0400 Subject: vim-patch:8.2.0736: some files not recognized as pamenv Problem: Some files not recognized as pamenv. Solution: Add pam_inv.conf. (closes vim/vim#6065) https://github.com/vim/vim/commit/aacc6afdb8cdeb2558e6942dcd65ca0079bec1ee --- runtime/filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index f2ae298b48..0b5003dc44 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1130,7 +1130,7 @@ au BufNewFile,BufRead pf.conf setf pf au BufNewFile,BufRead */etc/pam.conf setf pamconf " Pam environment -au BufNewFile,BufRead .pam_environment setf pamenv +au BufNewFile,BufRead pam_env.conf,.pam_environment setf pamenv " PApp au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp -- cgit From 02155f5c102539d5052912956606a452a78ad78c Mon Sep 17 00:00:00 2001 From: landerlo Date: Thu, 14 May 2020 04:14:52 +0100 Subject: lsp: fix bug when documentEdit version=null for unattached buffer (#12272) --- runtime/lua/vim/lsp/util.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 9efab73c2b..099a77099b 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -168,10 +168,12 @@ end function M.apply_text_document_edit(text_document_edit) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - -- `VersionedTextDocumentIdentifier`s version may be nil https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier - if text_document.version ~= vim.NIL and M.buf_versions[bufnr] > text_document.version then - print("Buffer ", text_document.uri, " newer than edits.") - return + if text_document.version then + -- `VersionedTextDocumentIdentifier`s version may be null https://microsoft.github.io/language-server-protocol/specification#versionedTextDocumentIdentifier + if text_document.version ~= vim.NIL and M.buf_versions[bufnr] ~= nil and M.buf_versions[bufnr] > text_document.version then + print("Buffer ", text_document.uri, " newer than edits.") + return + end end M.apply_text_edits(text_document_edit.edits, bufnr) end -- cgit From da6f38ab3cd87615e212b83ff9bbb9d585e4768e Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 14 May 2020 18:56:33 +0200 Subject: LSP: Add workspace.applyEdit client capabilities (#12313) applyEdit is supported by the built-in client. --- runtime/lua/vim/lsp/protocol.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 76817e3a4a..ee6e29bac0 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -703,6 +703,7 @@ function protocol.make_client_capabilities() }; hierarchicalWorkspaceSymbolSupport = true; }; + applyEdit = true; }; experimental = nil; } -- cgit From f559e5249e3aa155687b335272da8f0c73255ee4 Mon Sep 17 00:00:00 2001 From: Jesse Date: Sat, 16 May 2020 01:18:59 +0200 Subject: LSP: Add textDocument/codeAction support (#11607) * Add textDocument/codeAction * Add callback for workspace/executeCommand * Escape newlines in codeAction titles * Return empty list in get_line_diagnostics if no buffer diagnostics * Add stub documentation * Validate context parameter in code_action * Add support for edit in CodeAction responses * Group diagnostics by line in vim.lsp.util.get_line_diagnostics() * Advertise code action literal support --- runtime/doc/lsp.txt | 3 +++ runtime/lua/vim/lsp/buf.lua | 16 ++++++++++++++++ runtime/lua/vim/lsp/callbacks.lua | 40 +++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/lsp/protocol.lua | 9 +++++++++ runtime/lua/vim/lsp/util.lua | 37 ++++++++++++++++++++++++++++-------- 5 files changed, 97 insertions(+), 8 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 249136f32f..9460e600e3 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -731,6 +731,9 @@ transform_schema_to_table() ============================================================================== Lua module: vim.lsp.buf *lsp-buf* +code_action({context}) *vim.lsp.buf.code_action()* + TODO: Documentation + completion({context}) *vim.lsp.buf.completion()* TODO: Documentation diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 0b45951a56..7a819f3c3d 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -161,5 +161,21 @@ function M.clear_references() util.buf_clear_references() end +function M.code_action(context) + validate { context = { context, 't', true } } + context = context or { diagnostics = util.get_line_diagnostics() } + local params = util.make_range_params() + params.context = context + request('textDocument/codeAction', params) +end + +function M.execute_command(command) + validate { + command = { command.command, 's' }, + arguments = { command.arguments, 't', true } + } + request('workspace/executeCommand', command) +end + return M -- vim:sw=2 ts=2 et diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 09ca4b61e4..17f88a5181 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -3,6 +3,7 @@ local protocol = require 'vim.lsp.protocol' local util = require 'vim.lsp.util' local vim = vim local api = vim.api +local buf = require 'vim.lsp.buf' local M = {} @@ -11,6 +12,45 @@ local function err_message(...) api.nvim_command("redraw") end +M['workspace/executeCommand'] = function(err, _) + if err then + error("Could not execute code action: "..err.message) + end +end + +M['textDocument/codeAction'] = function(_, _, actions) + if vim.tbl_isempty(actions) then + print("No code actions available") + return + end + + local option_strings = {"Code Actions:"} + for i, action in ipairs(actions) do + local title = action.title:gsub('\r\n', '\\r\\n') + title = title:gsub('\n', '\\n') + table.insert(option_strings, string.format("%d. %s", i, title)) + end + + local choice = vim.fn.inputlist(option_strings) + if choice < 1 or choice > #actions then + return + end + local action_chosen = actions[choice] + -- textDocument/codeAction can return either Command[] or CodeAction[]. + -- If it is a CodeAction, it can have either an edit, a command or both. + -- Edits should be executed first + if action_chosen.edit or type(action_chosen.command) == "table" then + if action_chosen.edit then + util.apply_workspace_edit(action_chosen.edit) + end + if type(action_chosen.command) == "table" then + buf.execute_command(action_chosen.command) + end + else + buf.execute_command(action_chosen) + end +end + M['workspace/applyEdit'] = function(_, _, workspace_edit) if not workspace_edit then return end -- TODO(ashkan) Do something more with label? diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index ee6e29bac0..877d11411b 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -620,6 +620,15 @@ function protocol.make_client_capabilities() -- Send textDocument/didSave after saving (BufWritePost) didSave = true; }; + codeAction = { + dynamicRegistration = false; + + codeActionLiteralSupport = { + codeActionKind = { + valueSet = {}; + }; + }; + }; completion = { dynamicRegistration = false; completionItem = { diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 099a77099b..c92a317d0c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -720,19 +720,28 @@ do return severity_highlights[severity] end - function M.show_line_diagnostics() + function M.get_line_diagnostics() local bufnr = api.nvim_get_current_buf() - local line = api.nvim_win_get_cursor(0)[1] - 1 + local linenr = api.nvim_win_get_cursor(0)[1] - 1 + + local buffer_diagnostics = M.diagnostics_by_buf[bufnr] + + if not buffer_diagnostics then + return {} + end + + local diagnostics_by_line = M.diagnostics_group_by_line(buffer_diagnostics) + return diagnostics_by_line[linenr] or {} + end + + function M.show_line_diagnostics() -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {}) -- if #marks == 0 then -- return -- end local lines = {"Diagnostics:"} local highlights = {{0, "Bold"}} - - local buffer_diagnostics = M.diagnostics_by_buf[bufnr] - if not buffer_diagnostics then return end - local line_diagnostics = M.diagnostics_group_by_line(buffer_diagnostics)[line] + local line_diagnostics = M.get_line_diagnostics() if not line_diagnostics then return end for i, diagnostic in ipairs(line_diagnostics) do @@ -1044,14 +1053,26 @@ function M.try_trim_markdown_code_blocks(lines) end local str_utfindex = vim.str_utfindex -function M.make_position_params() +local function make_position_param() local row, col = unpack(api.nvim_win_get_cursor(0)) row = row - 1 local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] col = str_utfindex(line, col) + return { line = row; character = col; } +end + +function M.make_position_params() return { textDocument = M.make_text_document_params(); - position = { line = row; character = col; } + position = make_position_param() + } +end + +function M.make_range_params() + local position = make_position_param() + return { + textDocument = { uri = vim.uri_from_bufnr(0) }, + range = { start = position; ["end"] = position; } } end -- cgit From 986bed23294d000565ceb04274a6c645b7c987c9 Mon Sep 17 00:00:00 2001 From: Andy Lindeman Date: Fri, 15 May 2020 21:34:28 -0400 Subject: Check for nil before checking for empty table At least the `gopls` language server seems to return nil/null if no code actions are available. Currently this results in an error: > Error executing vim.schedule lua callback: shared.lua:199: Expected table, got nil --- runtime/lua/vim/lsp/callbacks.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 17f88a5181..37e9f1e5c1 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -19,7 +19,7 @@ M['workspace/executeCommand'] = function(err, _) end M['textDocument/codeAction'] = function(_, _, actions) - if vim.tbl_isempty(actions) then + if actions == nil or vim.tbl_isempty(actions) then print("No code actions available") return end -- cgit From d7d69fed18c2cd59d28ff12ef72f6fb2a98a7b66 Mon Sep 17 00:00:00 2001 From: Shougo Date: Sat, 16 May 2020 22:25:51 +0900 Subject: vim-patch:8.1.1084: cannot delete a match from another window (#12325) Problem: Cannot delete a match from another window. (Paul Jolly) Solution: Add window ID argument to matchdelete(), clearmatches(), getmatches() and setmatches(). (Andy Massimino, closes vim/vim#4178) https://github.com/vim/vim/commit/aff749145e23c0f20b5158d1d3a942948ed138e3 --- runtime/doc/eval.txt | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 6f13b34876..1992c34102 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2059,7 +2059,7 @@ chanclose({id}[, {stream}]) Number Closes a channel or one of its streams chansend({id}, {data}) Number Writes {data} to channel char2nr({expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} cindent({lnum}) Number C indent for line {lnum} -clearmatches() none clear all matches +clearmatches([{win}]) none clear all matches col({expr}) Number column nr of cursor or mark complete({startcol}, {matches}) none set Insert mode completion complete_add({expr}) Number add completion match @@ -2167,7 +2167,7 @@ getjumplist([{winnr} [, {tabnr}]]) getline({lnum}) String line {lnum} of current buffer getline({lnum}, {end}) List lines {lnum} to {end} of current buffer getloclist({nr} [, {what}]) List list of location list items -getmatches() List list of current matches +getmatches([{win}]) List list of current matches getpid() Number process ID of Vim getpos({expr}) List position of cursor, mark, etc. getqflist([{what}]) List list of quickfix items @@ -2259,7 +2259,7 @@ matchadd({group}, {pattern}[, {priority}[, {id}]]) matchaddpos({group}, {list}[, {priority}[, {id}]]) Number highlight positions with {group} matcharg({nr}) List arguments of |:match| -matchdelete({id}) Number delete match identified by {id} +matchdelete({id} [, {win}]) Number delete match identified by {id} matchend({expr}, {pat}[, {start}[, {count}]]) Number position where {pat} ends in {expr} matchlist({expr}, {pat}[, {start}[, {count}]]) @@ -2352,7 +2352,7 @@ setfperm({fname}, {mode} Number set {fname} file permissions to {mode} setline({lnum}, {line}) Number set line {lnum} to {line} setloclist({nr}, {list}[, {action}[, {what}]]) Number modify location list using {list} -setmatches({list}) Number restore a list of matches +setmatches({list} [, {win}]) Number restore a list of matches setpos({expr}, {list}) Number set the {expr} position to {list} setqflist({list}[, {action}[, {what}]] Number modify quickfix list using {list} @@ -3005,9 +3005,11 @@ cindent({lnum}) *cindent()* When {lnum} is invalid -1 is returned. See |C-indenting|. -clearmatches() *clearmatches()* - Clears all matches previously defined for the current window - by |matchadd()| and the |:match| commands. +clearmatches([{win}]) *clearmatches()* + Clears all matches previously defined for the current window + by |matchadd()| and the |:match| commands. + If {win} is specified, use the window with this number or + window ID instead of the current window. *col()* col({expr}) The result is a Number, which is the byte index of the column @@ -4602,7 +4604,7 @@ getloclist({nr},[, {what}]) *getloclist()* field is applicable only when called from a location list window. -getmatches() *getmatches()* +getmatches([{win}]) *getmatches()* Returns a |List| with all matches previously defined for the current window by |matchadd()| and the |:match| commands. |getmatches()| is useful in combination with |setmatches()|, @@ -5943,7 +5945,7 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]]) Defines a pattern to be highlighted in the current window (a "match"). It will be highlighted with {group}. Returns an identification number (ID), which can be used to delete the - match using |matchdelete()|. + match using |matchdelete()|. The ID is bound to the window. Matching is case sensitive and magic, unless case sensitivity or magicness are explicitly overridden in {pattern}. The 'magic', 'smartcase' and 'ignorecase' options are not used. @@ -6043,11 +6045,13 @@ matcharg({nr}) *matcharg()* Highlighting matches using the |:match| commands are limited to three matches. |matchadd()| does not have this limitation. -matchdelete({id}) *matchdelete()* *E802* *E803* +matchdelete({id} [, {win}) *matchdelete()* *E802* *E803* Deletes a match with ID {id} previously defined by |matchadd()| or one of the |:match| commands. Returns 0 if successful, otherwise -1. See example for |matchadd()|. All matches can be deleted in one operation by |clearmatches()|. + If {win} is specified, use the window with this number or + window ID instead of the current window. matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()* Same as |match()|, but return the index of first character @@ -7420,11 +7424,13 @@ setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* only the items listed in {what} are set. Refer to |setqflist()| for the list of supported keys in {what}. -setmatches({list}) *setmatches()* +setmatches({list} [, {win}]) *setmatches()* Restores a list of matches saved by |getmatches() for the current window|. Returns 0 if successful, otherwise -1. All current matches are cleared before the list is restored. See example for |getmatches()|. + If {win} is specified, use the window with this number or + window ID instead of the current window. *setpos()* setpos({expr}, {list}) -- cgit From ae5bd0454ee4ed3bdbf22e953a216449ca34dd46 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 18 May 2020 02:24:34 +0900 Subject: lua: add tbl_deep_extend (#11969) --- runtime/lua/vim/shared.lua | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index d18fcfaf95..2135bfc837 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -200,16 +200,7 @@ function vim.tbl_isempty(t) return next(t) == nil end ---- Merges two or more map-like tables. ---- ---@see |extend()| ---- ---@param behavior Decides what to do if a key is found in more than one map: ---- - "error": raise an error ---- - "keep": use value from the leftmost map ---- - "force": use value from the rightmost map ---@param ... Two or more map-like tables. -function vim.tbl_extend(behavior, ...) +local function tbl_extend(behavior, deep_extend, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) end @@ -228,7 +219,9 @@ function vim.tbl_extend(behavior, ...) vim.validate{["after the second argument"] = {tbl,'t'}} if tbl then for k, v in pairs(tbl) do - if behavior ~= 'force' and ret[k] ~= nil then + if type(v) == 'table' and deep_extend and not vim.tbl_islist(v) then + ret[k] = tbl_extend(behavior, true, ret[k] or vim.empty_dict(), v) + elseif behavior ~= 'force' and ret[k] ~= nil then if behavior == 'error' then error('key found in more than one map: '..k) end -- Else behavior is "keep". @@ -241,6 +234,32 @@ function vim.tbl_extend(behavior, ...) return ret end +--- Merges two or more map-like tables. +--- +--@see |extend()| +--- +--@param behavior Decides what to do if a key is found in more than one map: +--- - "error": raise an error +--- - "keep": use value from the leftmost map +--- - "force": use value from the rightmost map +--@param ... Two or more map-like tables. +function vim.tbl_extend(behavior, ...) + return tbl_extend(behavior, false, ...) +end + +--- Merges recursively two or more map-like tables. +--- +--@see |tbl_extend()| +--- +--@param behavior Decides what to do if a key is found in more than one map: +--- - "error": raise an error +--- - "keep": use value from the leftmost map +--- - "force": use value from the rightmost map +--@param ... Two or more map-like tables. +function vim.tbl_deep_extend(behavior, ...) + return tbl_extend(behavior, true, ...) +end + --- Deep compare values for equality function vim.deep_equal(a, b) if a == b then return true end -- cgit From 4fbbe1c957509393563be9492d03b9e95bb08e6a Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Sun, 17 May 2020 19:47:14 +0200 Subject: lsp: Handle end lines in apply_text_edits (#12314) If the LSP sends an end line that is larger than what nvim considers to be the last line, you get an Index out of bounds error when fetching the line from nvim, a change that was introduced in #12223. This change removes the strict indexing and checks the return value from nvim_buf_get_lines. --- runtime/lua/vim/lsp/util.lua | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index c92a317d0c..534c90f1fd 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -94,6 +94,19 @@ local edit_sort_key = sort_by_key(function(e) return {e.A[1], e.A[2], e.i} end) +local function get_line_byte_from_line_character(bufnr, lnum, cnum) + -- Skip check when the byte and character position is the same + if cnum > 0 then + local lines = api.nvim_buf_get_lines(bufnr, lnum, lnum+1, false) + + if #lines > 0 then + return vim.str_byteindex(lines[1], cnum) + end + end + + return cnum +end + function M.apply_text_edits(text_edits, bufnr) if not next(text_edits) then return end if not api.nvim_buf_is_loaded(bufnr) then @@ -104,13 +117,15 @@ function M.apply_text_edits(text_edits, bufnr) for i, e in ipairs(text_edits) do -- adjust start and end column for UTF-16 encoding of non-ASCII characters local start_row = e.range.start.line - local start_col = e.range.start.character - local start_bline = api.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1] - start_col = vim.str_byteindex(start_bline, start_col) + local start_col = get_line_byte_from_line_character( + bufnr, + start_row, + e.range.start.character) local end_row = e.range["end"].line - local end_col = e.range["end"].character - local end_bline = api.nvim_buf_get_lines(bufnr, end_row, end_row+1, true)[1] - end_col = vim.str_byteindex(end_bline, end_col) + local end_col = get_line_byte_from_line_character( + bufnr, + end_row, + e.range["end"].character) start_line = math.min(e.range.start.line, start_line) finish_line = math.max(e.range["end"].line, finish_line) -- TODO(ashkan) sanity check ranges for overlap. -- cgit From a91ce497b4f4d6c68e3009e5219d6b2ae0f63f7f Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Mon, 18 May 2020 09:12:22 +0900 Subject: lsp: Fix timezone format of LSP log (ISO 8601) (#12332) --- runtime/lua/vim/lsp/log.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index c0db5e5485..78aabf08ce 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -17,7 +17,7 @@ log.levels = { -- Default log level is warn. local current_log_level = log.levels.WARN -local log_date_format = "%FT%H:%M:%SZ%z" +local log_date_format = "%FT%H:%M:%S%z" do local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" -- cgit From a6be7a91809488adea23bf52bd77f0ed790bcbd3 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Sun, 17 May 2020 23:29:34 -0400 Subject: doc: Vim internal variables & options in lua (#12302) * doc: Add info about vim dicts in lua * doc: preamble and info * doc: remove weird spacing * fixup --- runtime/doc/lua.txt | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 7f376cbbf0..09034353a3 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -849,6 +849,66 @@ vim.types *vim.types* `vim.types.dictionary` will not change or that `vim.types` table will only contain values for these three types. +============================================================================== +Vim Internal Variables *lua-vim-internal-variables* + +Built-in Vim dictionaries can be accessed and set idiomatically in Lua by each +of the following tables. + +To set a value: > + + vim.g.my_global_variable = 5 +< + +To read a value: > + + print(vim.g.my_global_variable) +< + +To delete a value: > + + vim.g.my_global_variable = nil +< + +vim.g *vim.g* + Table with values from |g:| + Keys with no values set will result in `nil`. + +vim.b *vim.b* + Gets a buffer-scoped (b:) variable for the current buffer. + Keys with no values set will result in `nil`. + +vim.w *vim.w* + Gets a window-scoped (w:) variable for the current window. + Keys with no values set will result in `nil`. + +vim.t *vim.t* + Gets a tabpage-scoped (t:) variable for the current table. + Keys with no values set will result in `nil`. + +vim.v *vim.v* + Gets a v: variable. + Keys with no values set will result in `nil`. + + +Vim Internal Options *lua-vim-internal-options* + +Read, set and clear vim |options| in Lua by each of the following tables. + + +vim.o *vim.o* + Table with values from |options| + Invalid keys will result in an error. + +vim.bo *vim.bo* + Gets a buffer-scoped option for the current buffer. + Invalid keys will result in an error. + +vim.wo *vim.wo* + Gets a window-scoped option for the current window. + Invalid keys will result in an error. + + ============================================================================== Lua module: vim *lua-vim* -- cgit From f2894bffb024b712e69158d7914e9d9d3d495f72 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 18 May 2020 15:49:50 +0200 Subject: lua: Add highlight.on_yank (#12279) * add lua function to highlight yanked region * extract namespace, better naming, default values * add default for event argument * free timer * factor out mark to position calculation * d'oh * make sure timer stops before callback (cf. luv example) * factor out timer, more documentation * fixup * validate function argument for schedule * fix block selection past eol * correct handling of multibyte characters * move arguments around, some cleanup * move utility functions to vim.lua * use anonymous namespaces, avoid local api * rename function * add test for schedule_fn * fix indent * turn hl-yank into proper (hightlight) module * factor out position-to-region function mark extraction now part of highlight.on_yank * rename schedule_fn to defer_fn * add test for vim.region * todo: handle double-width characters * remove debug printout * do not shadow arguments * defer also callable table * whitespace change * move highlight to vim/highlight.lua * add documentation * add @return documentation * test: add check before vim.defer fires * doc: fixup --- runtime/doc/lua.txt | 29 +++++++++++++++++++++++++++++ runtime/doc/vim_diff.txt | 4 ++++ runtime/lua/vim/highlight.lua | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 runtime/lua/vim/highlight.lua (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 09034353a3..2b83c35c90 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -692,6 +692,27 @@ identical identifiers, highlighting both as |hl-WarningMsg|: > ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (eq? @WarningMsg.left @WarningMsg.right)) +------------------------------------------------------------------------------ +VIM.HIGHLIGHT *lua-highlight* + +Nvim includes a function for highlighting a selection on yank (see for example +https://github.com/machakann/vim-highlightedyank). To enable it, add +> + au TextYankPost * silent! lua require'vim.highlight'.on_yank() +< +to your `init.vim`. You can customize the highlight group and the duration of +the highlight via +> + au TextYankPost * silent! lua require'vim.highlight'.on_yank("IncSearch", 500) +< + +vim.highlight.on_yank([{higroup}, {timeout}, {event}]) + *vim.highlight.on_yank()* + Highlights the yanked text. Optional arguments are the highlight group + to use ({higroup}, default `"IncSearch"`), the duration of highlighting + in milliseconds ({timeout}, default `500`), and the event structure + that is fired ({event}, default `vim.v.event`). + ------------------------------------------------------------------------------ VIM.REGEX *lua-regex* @@ -758,6 +779,14 @@ vim.empty_dict() *vim.empty_dict()* Note: if numeric keys are added to the table, the metatable will be ignored and the dict converted to a list/array anyway. +vim.region({bufnr}, {pos1}, {pos2}, {type}, {inclusive}) *vim.region()* + Converts a selection specified by the buffer ({bufnr}), starting + position ({pos1}, a zero-indexed pair `{line1,column1}`), ending + position ({pos2}, same format as {pos1}), the type of the register + for the selection ({type}, see |regtype|), and a boolean indicating + whether the selection is inclusive or not, into a zero-indexed table + of linewise selections of the form `{linenr = {startcol, endcol}}` . + vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. If {channel} is 0, the event is broadcast to all channels. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 376375e4ef..24b562543e 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -116,6 +116,10 @@ backwards-compatibility cost. Some examples: - Directories for 'directory' and 'undodir' are auto-created. - Terminal features such as 'guicursor' are enabled where possible. +Some features are built in that otherwise required external plugins: + +- Highlighting the yanked region, see |lua-highlight|. + ARCHITECTURE ~ External plugins run in separate processes. |remote-plugin| This improves diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua new file mode 100644 index 0000000000..5c98c626a4 --- /dev/null +++ b/runtime/lua/vim/highlight.lua @@ -0,0 +1,41 @@ +local api = vim.api + +local highlight = {} + +--- Highlight the yanked region +-- +--- use from init.vim via +--- au TextYankPost * lua require'vim.highlight'.on_yank() +--- customize highlight group and timeout via +--- au TextYankPost * lua require'vim.highlight'.on_yank("IncSearch", 500) +-- @param higroup highlight group for yanked region +-- @param timeout time in ms before highlight is cleared +-- @param event event structure +function highlight.on_yank(higroup, timeout, event) + event = event or vim.v.event + if event.operator ~= 'y' or event.regtype == '' then return end + higroup = higroup or "IncSearch" + timeout = timeout or 500 + + local bufnr = api.nvim_get_current_buf() + local yank_ns = api.nvim_create_namespace('') + api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) + + local pos1 = vim.fn.getpos("'[") + local pos2 = vim.fn.getpos("']") + + pos1 = {pos1[2] - 1, pos1[3] - 1 + pos1[4]} + pos2 = {pos2[2] - 1, pos2[3] - 1 + pos2[4]} + + local region = vim.region(bufnr, pos1, pos2, event.regtype, event.inclusive) + for linenr, cols in pairs(region) do + api.nvim_buf_add_highlight(bufnr, yank_ns, higroup, linenr, cols[1], cols[2]) + end + + vim.defer_fn( + function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end, + timeout + ) +end + +return highlight -- cgit From 5126546ed77ca87eb7a5fd402ad0acaeae1d110d Mon Sep 17 00:00:00 2001 From: Mike Hartington Date: Mon, 18 May 2020 21:06:30 -0400 Subject: lsp: fix get diagnositcs --- runtime/lua/vim/lsp/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 534c90f1fd..5c6d183ac1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -757,7 +757,7 @@ do local lines = {"Diagnostics:"} local highlights = {{0, "Bold"}} local line_diagnostics = M.get_line_diagnostics() - if not line_diagnostics then return end + if vim.tbl_isempty(line_diagnostics) then return end for i, diagnostic in ipairs(line_diagnostics) do -- for i, mark in ipairs(marks) do -- cgit From 131063e08fef32ee34732ba1530676bdf408d924 Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Tue, 19 May 2020 08:49:13 +0200 Subject: Refactor fetching the line byte Takes the entire LSP position instead of line/col --- runtime/lua/vim/lsp/util.lua | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 534c90f1fd..8a536f72f4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -94,17 +94,23 @@ local edit_sort_key = sort_by_key(function(e) return {e.A[1], e.A[2], e.i} end) -local function get_line_byte_from_line_character(bufnr, lnum, cnum) - -- Skip check when the byte and character position is the same - if cnum > 0 then - local lines = api.nvim_buf_get_lines(bufnr, lnum, lnum+1, false) - +--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position +-- Returns a zero-indexed column, since set_lines() does the conversion to +-- 1-indexed +local function get_line_byte_from_position(bufnr, position) + -- LSP's line and characters are 0-indexed + -- Vim's line and columns are 1-indexed + local col = position.character + -- When on the first character, we can ignore the difference between byte and + -- character + if col > 0 then + local line = position.line + local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false) if #lines > 0 then - return vim.str_byteindex(lines[1], cnum) + return vim.str_byteindex(lines[1], col) end end - - return cnum + return col end function M.apply_text_edits(text_edits, bufnr) @@ -117,15 +123,9 @@ function M.apply_text_edits(text_edits, bufnr) for i, e in ipairs(text_edits) do -- adjust start and end column for UTF-16 encoding of non-ASCII characters local start_row = e.range.start.line - local start_col = get_line_byte_from_line_character( - bufnr, - start_row, - e.range.start.character) + local start_col = get_line_byte_from_position(bufnr, e.range.start) local end_row = e.range["end"].line - local end_col = get_line_byte_from_line_character( - bufnr, - end_row, - e.range["end"].character) + local end_col = get_line_byte_from_position(bufnr, e.range['end']) start_line = math.min(e.range.start.line, start_line) finish_line = math.max(e.range["end"].line, finish_line) -- TODO(ashkan) sanity check ranges for overlap. -- cgit From 0aca34e0a98ab47a8ae907031b25f687760a9264 Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Tue, 19 May 2020 08:50:31 +0200 Subject: Use get_line_byte_from_position in jump_to_location --- runtime/lua/vim/lsp/util.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 8a536f72f4..1b099fb3f4 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -474,9 +474,7 @@ function M.jump_to_location(location) api.nvim_buf_set_option(0, 'buflisted', true) local range = location.range or location.targetSelectionRange local row = range.start.line - local col = range.start.character - local line = api.nvim_buf_get_lines(0, row, row+1, true)[1] - col = vim.str_byteindex(line, col) + local col = get_line_byte_from_position(0, range.start) api.nvim_win_set_cursor(0, {row + 1, col}) return true end -- cgit From 7116105d661011cc4bdd5e01e1321d1e500b6b5b Mon Sep 17 00:00:00 2001 From: Faris A Chugthai <20028782+farisachugthai@users.noreply.github.com> Date: Wed, 20 May 2020 07:57:46 -0400 Subject: provider: Add python3.9 to autoload/provider/pythonx.vim (#12344) 3.9's scheduled for beta release today. https://www.python.org/dev/peps/pep-0596/ --- runtime/autoload/provider/pythonx.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 23e7ff8f64..ffb9bf3021 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -29,7 +29,7 @@ endfunction function! s:get_python_candidates(major_version) abort return { \ 2: ['python2', 'python2.7', 'python2.6', 'python'], - \ 3: ['python3', 'python3.8', 'python3.7', 'python3.6', 'python3.5', + \ 3: ['python3', 'python3.9', 'python3.8', 'python3.7', 'python3.6', 'python3.5', \ 'python3.4', 'python3.3', 'python'] \ }[a:major_version] endfunction -- cgit From 044eb56ed2f44b545e7488990ecf195a930174aa Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 21 May 2020 03:18:35 +0200 Subject: LSP: Don't swallow bufnr argument from callbacks (#12350) The callbacks for `textDocument/documentSymbol` and `workspace/symbol` never received the `bufnr` argument because the logic that adds error validation and logging swallowed the argument. --- runtime/lua/vim/lsp/callbacks.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 37e9f1e5c1..7c51fc2cc2 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -242,12 +242,12 @@ end -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do - M[k] = function(err, method, params, client_id) - local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err }) + M[k] = function(err, method, params, client_id, bufnr) + log.debug('default_callback', method, { params = params, client_id = client_id, err = err, bufnr = bufnr }) if err then error(tostring(err)) end - return fn(err, method, params, client_id) + return fn(err, method, params, client_id, bufnr) end end -- cgit From 04a0486c66e2ae6d67cad990f95283863dbe28fd Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Thu, 21 May 2020 18:17:21 +0200 Subject: Change uri_to_fname to not convert non-file URIs (#12351) * Change uri_to_fname to not convert non-file URIs A URI with a scheme other than file doesn't have a local file path. * fixup! Change uri_to_fname to not convert non-file URIs * fixup! fixup! Change uri_to_fname to not convert non-file URIs --- runtime/lua/vim/uri.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index e28cc9e20f..9c3535c676 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -65,9 +65,11 @@ local function uri_from_fname(path) return table.concat(uri_parts) end +local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*)://.*' + local function uri_from_bufnr(bufnr) local fname = vim.api.nvim_buf_get_name(bufnr) - local scheme = fname:match("^([a-z]+)://.*") + local scheme = fname:match(URI_SCHEME_PATTERN) if scheme then return fname else @@ -76,6 +78,10 @@ local function uri_from_bufnr(bufnr) end local function uri_to_fname(uri) + local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) + if scheme ~= 'file' then + return uri + end uri = uri_decode(uri) -- TODO improve this. if is_windows_file_uri(uri) then @@ -89,7 +95,7 @@ end -- Return or create a buffer for a uri. local function uri_to_bufnr(uri) - local scheme = assert(uri:match("^([a-z]+)://.*"), 'Uri must contain a scheme: ' .. uri) + local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) if scheme == 'file' then return vim.fn.bufadd(uri_to_fname(uri)) else -- cgit From ef8f965c64c157de728124a84281630cfb234002 Mon Sep 17 00:00:00 2001 From: Austin Traver <25112463+austintraver@users.noreply.github.com> Date: Fri, 22 May 2020 18:35:40 -0700 Subject: doc: Add optional d for `:lcd` and `:tcd` (#12359) The `:lcd -` command, and `:tcd -` which returns to a previous directory, can be done with `:lc -` and `:tc -` respectively. Basically, the d is optional, so I updated the documentation to indicate this in the traditional format. --- runtime/doc/editing.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 23a65f16e4..ac398ec494 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1265,7 +1265,7 @@ exist, the next-higher scope in the hierarchy applies. other tabs and windows is not changed. *:tcd-* -:tcd[!] - Change to the previous current directory (before the +:tc[d][!] - Change to the previous current directory (before the previous ":tcd {path}" command). *:tch* *:tchdir* @@ -1280,7 +1280,7 @@ exist, the next-higher scope in the hierarchy applies. :lch[dir][!] Same as |:lcd|. *:lcd-* -:lcd[!] - Change to the previous current directory (before the +:lc[d][!] - Change to the previous current directory (before the previous ":lcd {path}" command). *:pw* *:pwd* *E187* -- cgit From e0e5ada2bfa4c179062257857829bc27dbdeb580 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 23 May 2020 15:01:08 +0900 Subject: lsp: change log name to "lsp.log" from "vim-lsp.log" It's confusing because vim-lsp already has the same name as the plugin name that predates this built-in lsp. Also, since "vim.fn.stdpath" is used, adding the prefix "nvim-" is redundant, so just "lsp.log" will suffice. --- runtime/lua/vim/lsp/log.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 78aabf08ce..696ce43a59 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -24,7 +24,7 @@ do local function path_join(...) return table.concat(vim.tbl_flatten{...}, path_sep) end - local logfilename = path_join(vim.fn.stdpath('data'), 'vim-lsp.log') + local logfilename = path_join(vim.fn.stdpath('data'), 'lsp.log') --- Return the log filename. function log.get_filename() -- cgit From e89462d9855eef7718d482df7f92da4279a1c5c3 Mon Sep 17 00:00:00 2001 From: kuuote <36663503+kuuote@users.noreply.github.com> Date: Mon, 25 May 2020 03:45:25 +0900 Subject: vim-patch:8.1.2233: cannot get the Vim command line arguments (#12117) Problem: Cannot get the Vim command line arguments. Solution: Add v:argv. (Dmitri Vereshchagin, closes vim/vim#1322) https://github.com/vim/vim/commit/69bf634858a2a75f2984e42b1e4017bc529a040a --- runtime/doc/eval.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1992c34102..214d815006 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1423,6 +1423,10 @@ PREDEFINED VIM VARIABLES *vim-variable* *v:var* *v:* *E963* Some variables can be set by the user, but the type cannot be changed. + *v:argv* *argv-variable* +v:argv The command line arguments Vim was invoked with. This is a + list of strings. The first item is the Vim command. + *v:beval_col* *beval_col-variable* v:beval_col The number of the column, over which the mouse pointer is. This is the byte index in the |v:beval_lnum| line. @@ -2600,6 +2604,7 @@ argv([{nr} [, {winid}]) the whole |arglist| is returned. The {winid} argument specifies the window ID, see |argc()|. + For the Vim command line arguments see |v:argv|. assert_beeps({cmd}) *assert_beeps()* Run {cmd} and add an error message to |v:errors| if it does -- cgit From 37ee95504e43d246f4fa971fb7a38c2a1bc6b16f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 24 May 2020 14:46:41 -0400 Subject: vim-patch:8.0.1668: terminal debugger: can't re-open source code window (#12329) Problem: Terminal debugger: can't re-open source code window. Solution: Add the :Source command. Also create the window if needed when gdb stops at a source line. https://github.com/vim/vim/commit/c4b533e1e93151658cb170c6796d327a8c0f8612 --- .../pack/dist/opt/termdebug/plugin/termdebug.vim | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index aa2b69ad97..28dc3256c7 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -645,7 +645,7 @@ func s:InstallCommands() command Gdb call win_gotoid(s:gdbwin) command Program call win_gotoid(s:ptywin) command Source call s:GotoSourcewinOrCreateIt() - " command Winbar call s:InstallWinbar() + command Winbar call s:InstallWinbar() " TODO: can the K mapping be restored? nnoremap K :Evaluate @@ -655,6 +655,19 @@ endfunc " let s:winbar_winids = [] +" Install the window toolbar in the current window. +func s:InstallWinbar() + " if has('menu') && &mouse != '' + " nnoremenu WinBar.Step :Step + " nnoremenu WinBar.Next :Over + " nnoremenu WinBar.Finish :Finish + " nnoremenu WinBar.Cont :Continue + " nnoremenu WinBar.Stop :Stop + " nnoremenu WinBar.Eval :Evaluate + " call add(s:winbar_winids, win_getid(winnr())) + " endif +endfunc + " Delete installed debugger commands in the current window. func s:DeleteCommands() delcommand Break @@ -670,7 +683,7 @@ func s:DeleteCommands() delcommand Gdb delcommand Program delcommand Source - " delcommand Winbar + delcommand Winbar nunmap K @@ -940,7 +953,7 @@ func s:GotoSourcewinOrCreateIt() if !win_gotoid(s:sourcewin) new let s:sourcewin = win_getid(winnr()) - " call s:InstallWinbar() + call s:InstallWinbar() endif endfunc @@ -971,7 +984,7 @@ func s:HandleCursor(msg) " TODO: find existing window exe 'split ' . fnameescape(fname) let s:sourcewin = win_getid(winnr()) - " call s:InstallWinbar() + call s:InstallWinbar() else exe 'edit ' . fnameescape(fname) endif -- cgit From 15b762761ad966f91d452fdd28c718f2fd3e45be Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 26 May 2020 21:55:45 +0900 Subject: lsp: make the command error message more detailed (#11633) * lsp.lua: make the error message more detailed * test: add lsp._cmd_part test --- runtime/lua/vim/lsp.lua | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 61da2130c8..7135d2c5b6 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -122,19 +122,19 @@ local function validate_encoding(encoding) end function lsp._cmd_parts(input) - local cmd, cmd_args - if vim.tbl_islist(input) then - cmd = input[1] - cmd_args = {} - -- Don't mutate our input. - for i, v in ipairs(input) do - assert(type(v) == 'string', "input arguments must be strings") - if i > 1 then - table.insert(cmd_args, v) - end + vim.validate{cmd={ + input, + function() return vim.tbl_islist(input) end, + "list"}} + + local cmd = input[1] + local cmd_args = {} + -- Don't mutate our input. + for i, v in ipairs(input) do + vim.validate{["cmd argument"]={v, "s"}} + if i > 1 then + table.insert(cmd_args, v) end - else - error("cmd type must be list.") end return cmd, cmd_args end @@ -524,7 +524,7 @@ function lsp.start_client(config) function client.request(method, params, callback, bufnr) if not callback then callback = resolve_callback(method) - or error("not found: request callback for client "..client.name) + or error(string.format("not found: %q request callback for client %q.", method, client.name)) end local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback, bufnr) -- TODO keep these checks or just let it go anyway? -- cgit From 2ca8f02a6461fd4710c4ecc555fbe7ee9f75a70a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 26 May 2020 15:07:10 +0200 Subject: lsp: add preview_location util function (#12368) * add preview_location * add doc stub * doc style; return bufnr&winnr of preview * doc: function may return nil Co-authored-by: Hirokazu Hata * doc: fixup Co-authored-by: Hirokazu Hata --- runtime/doc/lsp.txt | 3 +++ runtime/lua/vim/lsp/util.lua | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 9460e600e3..0c510c3bec 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1053,6 +1053,9 @@ highlight_region({ft}, {start}, {finish}) jump_to_location({location}) *vim.lsp.util.jump_to_location()* TODO: Documentation +preview_location({location}) *vim.lsp.util.preview_location()* + TODO: Documentation + locations_to_items({locations}) *vim.lsp.util.locations_to_items()* TODO: Documentation diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 5c6d183ac1..b6eaae1fef 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -481,6 +481,28 @@ function M.jump_to_location(location) return true end +--- Preview a location in a floating windows +--- +--- behavior depends on type of location: +--- - for Location, range is shown (e.g., function definition) +--- - for LocationLink, targetRange is shown (e.g., body of function definition) +--- +--@param location a single Location or LocationLink +--@return bufnr,winnr buffer and window number of floating window or nil +function M.preview_location(location) + -- location may be LocationLink or Location (more useful for the former) + local uri = location.targetUri or location.uri + if uri == nil then return end + local bufnr = vim.uri_to_bufnr(uri) + if not api.nvim_buf_is_loaded(bufnr) then + vim.fn.bufload(bufnr) + end + local range = location.targetRange or location.range + local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line+1, false) + local filetype = api.nvim_buf_get_option(bufnr, 'filetype') + return M.open_floating_preview(contents, filetype) +end + local function find_window_by_var(name, value) for _, win in ipairs(api.nvim_list_wins()) do if npcall(api.nvim_win_get_var, win, name) == value then -- cgit From 5a9226c800d3075821203952da7c38626180680d Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Thu, 28 May 2020 14:31:56 +0200 Subject: lua: simple snippet support in the completion items (#12118) Old behavior is: foo(${placeholder: bar, ...) with lots of random garbage you'd never want inserted. New behavior is: foo(bar, baz) (which maybe is good, maybe is bad [depends on user], but definitely better than it was). ----- * Implement rudimentary snippet parsing Add support for parsing and discarding snippet tokens from the completion items. Fixes #11982 * Enable snippet support * Functional tests for snippet parsing Add simplified real-world snippet text examples to the completion items test * Add a test for nested snippet tokens * Remove TODO comment * Return the unmodified item if the format is plain text * Add a plain text completion item --- runtime/lua/vim/lsp/protocol.lua | 3 +- runtime/lua/vim/lsp/util.lua | 72 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 4 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 877d11411b..7d5f8f5ef1 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -633,8 +633,7 @@ function protocol.make_client_capabilities() dynamicRegistration = false; completionItem = { - -- TODO(tjdevries): Is it possible to implement this in plain lua? - snippetSupport = false; + snippetSupport = true; commitCharactersSupport = false; preselectSupport = false; deprecatedSupport = false; diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b6eaae1fef..752d4ff439 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -199,6 +199,66 @@ function M.get_current_line_to_cursor() return line:sub(pos[2]+1) end +local function parse_snippet_rec(input, inner) + local res = "" + + local close, closeend = nil, nil + if inner then + close, closeend = input:find("}", 1, true) + while close ~= nil and input:sub(close-1,close-1) == "\\" do + close, closeend = input:find("}", closeend+1, true) + end + end + + local didx = input:find('$', 1, true) + if didx == nil and close == nil then + return input, "" + elseif close ~=nil and (didx == nil or close < didx) then + -- No inner placeholders + return input:sub(0, close-1), input:sub(closeend+1) + end + + res = res .. input:sub(0, didx-1) + input = input:sub(didx+1) + + local tabstop, tabstopend = input:find('^%d+') + local placeholder, placeholderend = input:find('^{%d+:') + local choice, choiceend = input:find('^{%d+|') + + if tabstop then + input = input:sub(tabstopend+1) + elseif choice then + input = input:sub(choiceend+1) + close, closeend = input:find("|}", 1, true) + + res = res .. input:sub(0, close-1) + input = input:sub(closeend+1) + elseif placeholder then + -- TODO: add support for variables + input = input:sub(placeholderend+1) + + -- placeholders and variables are recursive + while input ~= "" do + local r, tail = parse_snippet_rec(input, true) + r = r:gsub("\\}", "}") + + res = res .. r + input = tail + end + else + res = res .. "$" + end + + return res, input +end + +-- Parse completion entries, consuming snippet tokens +function M.parse_snippet(input) + local res, _ = parse_snippet_rec(input, false) + + return res +end + -- Sort by CompletionItem.sortText -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion local function sort_completion_items(items) @@ -213,9 +273,17 @@ end -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion local function get_completion_word(item) if item.textEdit ~= nil and item.textEdit.newText ~= nil then - return item.textEdit.newText + if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then + return item.textEdit.newText + else + return M.parse_snippet(item.textEdit.newText) + end elseif item.insertText ~= nil then - return item.insertText + if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then + return item.insertText + else + return M.parse_snippet(item.insertText) + end end return item.label end -- cgit From 7d8dc4c331fee2527dfe499066035fa7470ad5b3 Mon Sep 17 00:00:00 2001 From: BusyBruce <4861164+BusyBruce@users.noreply.github.com> Date: Sat, 30 May 2020 20:29:24 +0800 Subject: provider: Fix ruby checkhealth error for Windows (#12400) Plaform: Windows 10 run `cmd /c gem list -ra ^^neovim$` *** REMOTE GEMS *** minitest-neovim (0.1.0) neovim (0.7.1, 0.7.0, 0.6.2, 0.6.1, 0.6.0, 0.5.1, 0.5.0, 0.4.0, 0.3.3, 0.3.2, 0.3.1, 0.3.0, 0.2.5, 0.2.4, 0.2.3, 0.2.2, 0.2.1, 0.2.0, 0.1.0, 0.0.6, 0.0.5, 0.0.4, 0.0.3, 0.0.2, 0.0.1) run `cmd /c gem list -ra "^^neovim$"` *** REMOTE GEMS *** neovim (0.7.1, 0.7.0, 0.6.2, 0.6.1, 0.6.0, 0.5.1, 0.5.0, 0.4.0, 0.3.3, 0.3.2, 0.3.1, 0.3.0, 0.2.5, 0.2.4, 0.2.3, 0.2.2, 0.2.1, 0.2.0, 0.1.0, 0.0.6, 0.0.5, 0.0.4, 0.0.3, 0.0.2, 0.0.1) --- runtime/autoload/health/provider.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 6d481e9f49..4975dc66b8 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -581,7 +581,7 @@ function! s:check_ruby() abort endif call health#report_info('Host: '. host) - let latest_gem_cmd = has('win32') ? 'cmd /c gem list -ra ^^neovim$' : 'gem list -ra ^neovim$' + let latest_gem_cmd = has('win32') ? 'cmd /c gem list -ra "^^neovim$"' : 'gem list -ra ^neovim$' let latest_gem = s:system(split(latest_gem_cmd)) if s:shell_error || empty(latest_gem) call health#report_error('Failed to run: '. latest_gem_cmd, -- cgit From be662fe5c7d06ee63377dd7defb72aea88134305 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Wed, 20 May 2020 11:08:19 -0400 Subject: lua: vim.wait implementation --- runtime/doc/lua.txt | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ runtime/lua/vim/lsp.lua | 37 +++++++------------------------- 2 files changed, 63 insertions(+), 30 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 2b83c35c90..19579a2a3c 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -827,6 +827,62 @@ vim.schedule({callback}) *vim.schedule()* Schedules {callback} to be invoked soon by the main event-loop. Useful to avoid |textlock| or other temporary restrictions. + +vim.defer_fn({fn}, {timeout}) *vim.defer_fn* + Defers calling {fn} until {timeout} ms passes. Use to do a one-shot timer + that calls {fn}. + + Parameters: ~ + {fn} Callback to call once {timeout} expires + {timeout} Time in ms to wait before calling {fn} + + Returns: ~ + |vim.loop|.new_timer() object + +vim.wait({time}, {callback} [, {interval}]) *vim.wait()* + Wait for {time} in milliseconds until {callback} returns `true`. + + Executes {callback} immediately and at approximately {interval} + milliseconds (default 200). Nvim still processes other events during + this time. + + + Returns: ~ + If {callback} returns `true` during the {time}: + `true, nil` + + If {callback} never returns `true` during the {time}: + `false, -1` + + If {callback} is interrupted during the {time}: + `false, -2` + + If {callback} errors, the error is raised. + + Examples: > + + --- + -- Wait for 100 ms, allowing other events to process + vim.wait(100, function() end) + + --- + -- Wait for 100 ms or until global variable set. + vim.wait(100, function() return vim.g.waiting_for_var end) + + --- + -- Wait for 1 second or until global variable set, checking every ~500 ms + vim.wait(1000, function() return vim.g.waiting_for_var end, 500) + + --- + -- Schedule a function to set a value in 100ms + vim.defer_fn(function() vim.g.timer_result = true end, 100) + + -- Would wait ten seconds if results blocked. Actually only waits 100 ms + if vim.wait(10000, function() return vim.g.timer_result end) then + print('Only waiting a little bit of time!') + end +< + vim.fn.{func}({...}) *vim.fn* Invokes |vim-function| or |user-function| {func} with arguments {...}. To call autoload functions, use the syntax: > diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 7135d2c5b6..84812b8c64 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -46,31 +46,6 @@ local function is_dir(filename) return stat and stat.type == 'directory' or false end --- TODO Use vim.wait when that is available, but provide an alternative for now. -local wait = vim.wait or function(timeout_ms, condition, interval) - validate { - timeout_ms = { timeout_ms, 'n' }; - condition = { condition, 'f' }; - interval = { interval, 'n', true }; - } - assert(timeout_ms > 0, "timeout_ms must be > 0") - local _ = log.debug() and log.debug("wait.fallback", timeout_ms) - interval = interval or 200 - local interval_cmd = "sleep "..interval.."m" - local timeout = timeout_ms + uv.now() - -- TODO is there a better way to sync this? - while true do - uv.update_time() - if condition() then - return 0 - end - if uv.now() >= timeout then - return -1 - end - nvim_command(interval_cmd) - -- vim.loop.sleep(10) - end -end local wait_result_reason = { [-1] = "timeout"; [-2] = "interrupted"; [-3] = "error" } local valid_encodings = { @@ -810,8 +785,8 @@ function lsp._vim_exit_handler() for _, client in pairs(active_clients) do client.stop() end - local wait_result = wait(500, function() return tbl_isempty(active_clients) end, 50) - if wait_result ~= 0 then + + if not vim.wait(500, function() return tbl_isempty(active_clients) end, 50) then for _, client in pairs(active_clients) do client.stop(true) end @@ -889,12 +864,14 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) for _ in pairs(client_request_ids) do expected_result_count = expected_result_count + 1 end - local wait_result = wait(timeout_ms or 100, function() + + local wait_result, reason = vim.wait(timeout_ms or 100, function() return result_count >= expected_result_count end, 10) - if wait_result ~= 0 then + + if not wait_result then cancel() - return nil, wait_result_reason[wait_result] + return nil, wait_result_reason[reason] end return request_results end -- cgit From 4b87248285e7d954e496f84a7ece58b4f1a830ca Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 30 May 2020 18:06:23 -0400 Subject: vim-patch:8.2.0843: filetype elm not detected (#12403) Problem: Filetype elm not detected. Solution: Recognize *.elm files. (closes vim/vim#6157) https://github.com/vim/vim/commit/0016fd2e29b7d4d4eef89a0516d14888a8651c61 --- runtime/filetype.vim | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 0b5003dc44..b29168984c 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -543,6 +543,9 @@ au BufNewFile,BufRead */etc/elinks.conf,*/.elinks/elinks.conf setf elinks " ERicsson LANGuage; Yaws is erlang too au BufNewFile,BufRead *.erl,*.hrl,*.yaws setf erlang +" Elm +au BufNewFile,BufRead *.elm setf elm + " Elm Filter Rules file au BufNewFile,BufRead filter-rules setf elmfilt -- cgit From 7124c0e5acde38957af7621afd1d92fb2b6d1d58 Mon Sep 17 00:00:00 2001 From: kuuote Date: Sun, 31 May 2020 20:21:58 +0900 Subject: runtime: fix remote plugin command fails at some case fixes #12410 --- runtime/autoload/remote/define.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/autoload/remote/define.vim b/runtime/autoload/remote/define.vim index 2688a62a82..2aec96e365 100644 --- a/runtime/autoload/remote/define.vim +++ b/runtime/autoload/remote/define.vim @@ -24,7 +24,7 @@ function! remote#define#CommandOnHost(host, method, sync, name, opts) endif if has_key(a:opts, 'nargs') - call add(forward_args, ' ') + call add(forward_args, ' " . . "') endif exe s:GetCommandPrefix(a:name, a:opts) -- cgit From 91e41c857622b61adcf84f6a6af87a3f5a4d65f5 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 31 May 2020 20:56:00 +0200 Subject: lua: add vim.highlight.range (#12401) --- runtime/doc/lsp.txt | 8 -------- runtime/doc/lua.txt | 10 ++++++++++ runtime/lua/vim/highlight.lua | 29 ++++++++++++++++++++++++----- runtime/lua/vim/lsp/util.lua | 25 ++++++------------------- 4 files changed, 40 insertions(+), 32 deletions(-) (limited to 'runtime') diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 0c510c3bec..9deaf26983 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1040,14 +1040,6 @@ get_current_line_to_cursor() *vim.lsp.util.get_severity_highlight_name()* get_severity_highlight_name({severity}) - TODO: Documentation - - *vim.lsp.util.highlight_range()* -highlight_range({bufnr}, {ns}, {hiname}, {start}, {finish}) - TODO: Documentation - - *vim.lsp.util.highlight_region()* -highlight_region({ft}, {start}, {finish}) TODO: Documentation jump_to_location({location}) *vim.lsp.util.jump_to_location()* diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 19579a2a3c..7c1b0ee73b 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -713,6 +713,16 @@ vim.highlight.on_yank([{higroup}, {timeout}, {event}]) in milliseconds ({timeout}, default `500`), and the event structure that is fired ({event}, default `vim.v.event`). + +vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {rtype}, {inclusive}) + *vim.highlight.range()* + Highlights the range between {start} and {finish} (tuples of {line,col}) + in buffer {bufnr} with the highlight group {higroup} using the namespace + {ns}. Optional arguments are the type of range (characterwise, linewise, + or blockwise, see |setreg|; default to characterwise) and whether the + range is inclusive (default false). + + ------------------------------------------------------------------------------ VIM.REGEX *lua-regex* diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 5c98c626a4..69c3c8a4dc 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -2,12 +2,34 @@ local api = vim.api local highlight = {} +--- Highlight range between two positions +--- +--@param bufnr number of buffer to apply highlighting to +--@param ns namespace to add highlight to +--@param higroup highlight group to use for highlighting +--@param rtype type of range (:help setreg, default charwise) +--@param inclusive boolean indicating whether the range is end-inclusive (default false) +function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive) + rtype = rtype or 'v' + inclusive = inclusive or false + + -- sanity check + if start[2] < 0 or finish[2] < start[2] then return end + + local region = vim.region(bufnr, start, finish, rtype, inclusive) + for linenr, cols in pairs(region) do + api.nvim_buf_add_highlight(bufnr, ns, higroup, linenr, cols[1], cols[2]) + end + +end + --- Highlight the yanked region --- +--- --- use from init.vim via --- au TextYankPost * lua require'vim.highlight'.on_yank() --- customize highlight group and timeout via --- au TextYankPost * lua require'vim.highlight'.on_yank("IncSearch", 500) +--- -- @param higroup highlight group for yanked region -- @param timeout time in ms before highlight is cleared -- @param event event structure @@ -27,10 +49,7 @@ function highlight.on_yank(higroup, timeout, event) pos1 = {pos1[2] - 1, pos1[3] - 1 + pos1[4]} pos2 = {pos2[2] - 1, pos2[3] - 1 + pos2[4]} - local region = vim.region(bufnr, pos1, pos2, event.regtype, event.inclusive) - for linenr, cols in pairs(region) do - api.nvim_buf_add_highlight(bufnr, yank_ns, higroup, linenr, cols[1], cols[2]) - end + highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive) vim.defer_fn( function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end, diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 752d4ff439..79d428d12d 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -3,6 +3,7 @@ local vim = vim local validate = vim.validate local api = vim.api local list_extend = vim.list_extend +local highlight = require 'vim.highlight' local M = {} @@ -691,7 +692,7 @@ function M.fancy_floating_markdown(contents, opts) vim.cmd("ownsyntax markdown") local idx = 1 - local function highlight_region(ft, start, finish) + local function apply_syntax_to_region(ft, start, finish) if ft == '' then return end local name = ft..idx idx = idx + 1 @@ -707,8 +708,8 @@ function M.fancy_floating_markdown(contents, opts) -- make sure that regions between code blocks are definitely markdown. -- local ph = {start = 0; finish = 1;} for _, h in ipairs(highlights) do - -- highlight_region('markdown', ph.finish, h.start) - highlight_region(h.ft, h.start, h.finish) + -- apply_syntax_to_region('markdown', ph.finish, h.start) + apply_syntax_to_region(h.ft, h.start, h.finish) -- ph = h end @@ -762,19 +763,6 @@ function M.open_floating_preview(contents, filetype, opts) return floating_bufnr, floating_winnr end -local function highlight_range(bufnr, ns, hiname, start, finish) - if start[1] == finish[1] then - -- TODO care about encoding here since this is in byte index? - api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], finish[2]) - else - api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], -1) - for line = start[1] + 1, finish[1] - 1 do - api.nvim_buf_add_highlight(bufnr, ns, hiname, line, 0, -1) - end - api.nvim_buf_add_highlight(bufnr, ns, hiname, finish[1], 0, finish[2]) - end -end - do local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics") local reference_ns = api.nvim_create_namespace("vim_lsp_references") @@ -908,8 +896,7 @@ do [protocol.DiagnosticSeverity.Hint]='Hint', } - -- TODO care about encoding here since this is in byte index? - highlight_range(bufnr, diagnostic_ns, + highlight.range(bufnr, diagnostic_ns, underline_highlight_name..hlmap[diagnostic.severity], {start.line, start.character}, {finish.line, finish.character} @@ -933,7 +920,7 @@ do [protocol.DocumentHighlightKind.Write] = "LspReferenceWrite"; } local kind = reference["kind"] or protocol.DocumentHighlightKind.Text - highlight_range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos) + highlight.range(bufnr, reference_ns, document_highlight_kind[kind], start_pos, end_pos) end end -- cgit From 60c581b35db439dd6b32cdc2ebe1a5aed933b44c Mon Sep 17 00:00:00 2001 From: notomo Date: Wed, 3 Jun 2020 08:31:43 +0900 Subject: lua: fix infinite loop for vim.split on empty string (#12420) --- runtime/lua/vim/shared.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 2135bfc837..5dc8d6dc10 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -79,7 +79,7 @@ function vim.gsplit(s, sep, plain) end return function() - if done then + if done or s == '' then return end if sep == '' then -- cgit From 8a1276005a1099187710bdcc19284de51a0aa89a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 3 Jun 2020 16:51:25 +0200 Subject: Add v:event.visual during `TextYankPost` (#12382) * propagate visual selection to textyankpost event * adapt tests * add docs * also adapt oldtest --- runtime/doc/autocmd.txt | 1 + runtime/doc/eval.txt | 2 ++ runtime/doc/lua.txt | 4 ++++ 3 files changed, 7 insertions(+) (limited to 'runtime') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 64ca7b6a45..f1753b75cc 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -844,6 +844,7 @@ TextYankPost Just after a |yank| or |deleting| command, but not regcontents regname regtype + visual The `inclusive` flag combined with the |'[| and |']| marks can be used to calculate the precise region of the operation. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 214d815006..7f50769023 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1591,6 +1591,8 @@ v:event Dictionary of event data for the current |autocommand|. Valid operation. regtype Type of register as returned by |getregtype()|. + visual Selection is visual (as opposed to, + e.g., via motion). completed_item Current selected complete item on |CompleteChanged|, Is `{}` when no complete item selected. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 7c1b0ee73b..5a49d36503 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -705,6 +705,10 @@ the highlight via > au TextYankPost * silent! lua require'vim.highlight'.on_yank("IncSearch", 500) < +If you want to exclude visual selections from highlighting on yank, use +> +au TextYankPost * silent! lua return (not vim.v.event.visual) and require'vim.highlight'.on_yank() +< vim.highlight.on_yank([{higroup}, {timeout}, {event}]) *vim.highlight.on_yank()* -- cgit From 6a93077475d298f46ac19c8030ed5b4a723685dc Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Wed, 3 Jun 2020 19:58:02 +0200 Subject: treesitter: fix tests --- runtime/lua/vim/treesitter.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index d3b78a7f73..c502e45bd0 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -162,16 +162,17 @@ function Query:match_preds(match, pattern, bufnr) -- Here we only want to return if a predicate DOES NOT match, and -- continue on the other case. This way unknown predicates will not be considered, -- which allows some testing and easier user extensibility (#12173). + -- Also, tree-sitter strips the leading # from predicates for us. if pred[1] == "eq?" then local node = match[pred[2]] local node_text = get_node_text(node, bufnr) local str if type(pred[3]) == "string" then - -- (eq? @aa "foo") + -- (#eq? @aa "foo") str = pred[3] else - -- (eq? @aa @bb) + -- (#eq? @aa @bb) str = get_node_text(match[pred[3]], bufnr) end -- cgit From ac5a3f2c56775040a1b7de438cfd592c0dc26400 Mon Sep 17 00:00:00 2001 From: notomo Date: Thu, 4 Jun 2020 21:48:48 +0900 Subject: lua: fix behavior when split empty string (#12429) * lua: fix behavior when split empty string * test: lsp.util.apply_text_edits with an empty edit --- runtime/lua/vim/shared.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime') diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 5dc8d6dc10..384d22cb89 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -79,7 +79,7 @@ function vim.gsplit(s, sep, plain) end return function() - if done or s == '' then + if done or (s == '' and sep == '') then return end if sep == '' then -- cgit From 6f4f38cd54693ca99c887756e6179cc0775377d0 Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy Date: Thu, 4 Jun 2020 06:52:44 -0600 Subject: lsp: Add check for `declaration` and `typeDefinition` support in vim lsp server before making `request` (#12421) * Add check for typeDefinition support in vim lsp server * Check for typeDefinitionProvider in server * Check for declarationProvider in server * Add check for client support * Fix typo --- runtime/lua/vim/lsp.lua | 2 ++ runtime/lua/vim/lsp/protocol.lua | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 84812b8c64..2fbc51481f 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -507,6 +507,8 @@ function lsp.start_client(config) or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp') or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition') or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation') + or (not client.resolved_capabilities.declaration and method == 'textDocument/declaration') + or (not client.resolved_capabilities.type_definition and method == 'textDocument/typeDefinition') or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol') or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol') then diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 7d5f8f5ef1..64911fe7bb 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -923,6 +923,28 @@ function protocol.resolve_capabilities(server_capabilities) error("The server sent invalid codeActionProvider") end + if server_capabilities.declarationProvider == nil then + general_properties.declaration = false + elseif type(server_capabilities.declarationProvider) == 'boolean' then + general_properties.declaration = server_capabilities.declarationProvider + elseif type(server_capabilities.declarationProvider) == 'table' then + -- TODO: support more detailed declarationProvider options. + general_properties.declaration = false + else + error("The server sent invalid declarationProvider") + end + + if server_capabilities.typeDefinitionProvider == nil then + general_properties.type_definition = false + elseif type(server_capabilities.typeDefinitionProvider) == 'boolean' then + general_properties.type_definition = server_capabilities.typeDefinitionProvider + elseif type(server_capabilities.typeDefinitionProvider) == 'table' then + -- TODO: support more detailed typeDefinitionProvider options. + general_properties.type_definition = false + else + error("The server sent invalid typeDefinitionProvider") + end + if server_capabilities.implementationProvider == nil then general_properties.implementation = false elseif type(server_capabilities.implementationProvider) == 'boolean' then -- cgit From b7f3f11049c6847a2b0c4bbd89e8339036e00da6 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 4 Jun 2020 20:23:03 +0200 Subject: lsp: compute height of floating preview correctly for wrapped lines (#12380) * take wrapping into account when computing float height * factor out size calculation * add test * accept and pass through opts.wrap_at in floating_preview * make padding configurable * slightly refactor fancy_floating_markdown to make use of make_position * padding using string.format * move trim and pad to separate function * nit Co-authored-by: Hirokazu Hata * remove mention of backward compat * make lint happy Co-authored-by: Hirokazu Hata --- runtime/lua/vim/lsp/util.lua | 152 +++++++++++++++++++++++++++++++++---------- 1 file changed, 118 insertions(+), 34 deletions(-) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 02d233fb7b..49e2557c16 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -289,7 +289,7 @@ local function get_completion_word(item) return item.label end --- Some lanuguage servers return complementary candidates whose prefixes do not match are also returned. +-- Some language servers return complementary candidates whose prefixes do not match are also returned. -- So we exclude completion candidates whose prefix does not match. local function remove_unmatch_completion_items(items, prefix) return vim.tbl_filter(function(item) @@ -614,13 +614,53 @@ function M.focusable_preview(unique_name, fn) end) end --- Convert markdown into syntax highlighted regions by stripping the code --- blocks and converting them into highlighted code. --- This will by default insert a blank line separator after those code block --- regions to improve readability. +--- Trim empty lines from input and pad left and right with spaces +--- +--@param contents table of lines to trim and pad +--@param opts dictionary with optional fields +-- - pad_left amount of columns to pad contents at left (default 1) +-- - pad_right amount of columns to pad contents at right (default 1) +--@return contents table of trimmed and padded lines +function M._trim_and_pad(contents, opts) + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + local left_padding = (" "):rep(opts.pad_left or 1) + local right_padding = (" "):rep(opts.pad_right or 1) + contents = M.trim_empty_lines(contents) + for i, line in ipairs(contents) do + contents[i] = string.format('%s%s%s', left_padding, line:gsub("\r", ""), right_padding) + end + return contents +end + + + +--- Convert markdown into syntax highlighted regions by stripping the code +--- blocks and converting them into highlighted code. +--- This will by default insert a blank line separator after those code block +--- regions to improve readability. +--- The result is shown in a floating preview +--- TODO: refactor to separate stripping/converting and make use of open_floating_preview +--- +--@param contents table of lines to show in window +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - pad_left amount of columns to pad contents at left +-- - pad_right amount of columns to pad contents at right +-- - separator insert separator after code block +--@return width,height size of float function M.fancy_floating_markdown(contents, opts) - local pad_left = opts and opts.pad_left - local pad_right = opts and opts.pad_right + validate { + contents = { contents, 't' }; + opts = { opts, 't', true }; + } + opts = opts or {} + local stripped = {} local highlights = {} do @@ -654,31 +694,27 @@ function M.fancy_floating_markdown(contents, opts) end end end - local width = 0 - for i, v in ipairs(stripped) do - v = v:gsub("\r", "") - if pad_left then v = (" "):rep(pad_left)..v end - if pad_right then v = v..(" "):rep(pad_right) end - stripped[i] = v - width = math.max(width, #v) - end - if opts and opts.max_width then - width = math.min(opts.max_width, width) - end - -- TODO(ashkan): decide how to make this customizable. - local insert_separator = true + -- Clean up and add padding + stripped = M._trim_and_pad(stripped, opts) + + -- Compute size of float needed to show (wrapped) lines + opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + local width, height = M._make_floating_popup_size(stripped, opts) + + -- Insert blank line separator after code block + local insert_separator = opts.separator or true if insert_separator then for i, h in ipairs(highlights) do h.start = h.start + i - 1 h.finish = h.finish + i - 1 if h.finish + 1 <= #stripped then table.insert(stripped, h.finish + 1, string.rep("─", width)) + height = height + 1 end end end -- Make the floating window. - local height = #stripped local bufnr = api.nvim_create_buf(false, true) local winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts)) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) @@ -719,33 +755,81 @@ function M.close_preview_autocmd(events, winnr) api.nvim_command("autocmd "..table.concat(events, ',').." ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") end -function M.open_floating_preview(contents, filetype, opts) +--- Compute size of float needed to show contents (with optional wrapping) +--- +--@param contents table of lines to show in window +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +--@return width,height size of float +function M._make_floating_popup_size(contents, opts) validate { contents = { contents, 't' }; - filetype = { filetype, 's', true }; opts = { opts, 't', true }; } opts = opts or {} - -- Trim empty lines from the end. - contents = M.trim_empty_lines(contents) - local width = opts.width - local height = opts.height or #contents + local height = opts.height + local line_widths = {} + if not width then width = 0 for i, line in ipairs(contents) do - -- Clean up the input and add left pad. - line = " "..line:gsub("\r", "") -- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced. - local line_width = vim.fn.strdisplaywidth(line) - width = math.max(line_width, width) - contents[i] = line + line_widths[i] = vim.fn.strdisplaywidth(line) + width = math.max(line_widths[i], width) end - -- Add right padding of 1 each. - width = width + 1 end + if not height then + height = #contents + local wrap_at = opts.wrap_at + if wrap_at and width > wrap_at then + height = 0 + if vim.tbl_isempty(line_widths) then + for _, line in ipairs(contents) do + local line_width = vim.fn.strdisplaywidth(line) + height = height + math.ceil(line_width/wrap_at) + end + else + for i = 1, #contents do + height = height + math.ceil(line_widths[i]/wrap_at) + end + end + end + end + + return width, height +end + +--- Show contents in a floating window +--- +--@param contents table of lines to show in window +--@param filetype string of filetype to set for opened buffer +--@param opts dictionary with optional fields +-- - height of floating window +-- - width of floating window +-- - wrap_at character to wrap at for computing height +-- - pad_left amount of columns to pad contents at left +-- - pad_right amount of columns to pad contents at right +--@return bufnr,winnr buffer and window number of floating window or nil +function M.open_floating_preview(contents, filetype, opts) + validate { + contents = { contents, 't' }; + filetype = { filetype, 's', true }; + opts = { opts, 't', true }; + } + opts = opts or {} + + -- Clean up input: trim empty lines from the end, pad + contents = M._trim_and_pad(contents, opts) + + -- Compute size of float needed to show (wrapped) lines + opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) + local width, height = M._make_floating_popup_size(contents, opts) + local floating_bufnr = api.nvim_create_buf(false, true) if filetype then api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype) -- cgit From dd4018947c9f9b39b4e473c21ebf0e27f1e7ddc5 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Thu, 4 Jun 2020 19:37:38 -0400 Subject: lsp: do not process diagnostics for unloaded buffers (#12440) --- runtime/lua/vim/lsp/callbacks.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'runtime') diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua index 7c51fc2cc2..4b14f0132d 100644 --- a/runtime/lua/vim/lsp/callbacks.lua +++ b/runtime/lua/vim/lsp/callbacks.lua @@ -72,6 +72,17 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri) return end + + -- Unloaded buffers should not handle diagnostics. + -- When the buffer is loaded, we'll call on_attach, which sends textDocument/didOpen. + -- This should trigger another publish of the diagnostics. + -- + -- In particular, this stops a ton of spam when first starting a server for current + -- unloaded buffers. + if not api.nvim_buf_is_loaded(bufnr) then + return + end + util.buf_clear_diagnostics(bufnr) -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic -- cgit From f85bf6ec374edbfbee35be2112840e0c6d011ca2 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 2 Jun 2020 23:52:41 -0400 Subject: vim-patch:8.2.0873: a .jl file can be sawfish (lisp) or Julia Problem: A .jl file can be sawfish (lisp) or Julia. Solution: Do not recognize *.jl as lisp, since it might be Julia. (closes vim/vim#6178) https://github.com/vim/vim/commit/2891459b81a21dbd2802ef1ae106c6680f085899 --- runtime/filetype.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime') diff --git a/runtime/filetype.vim b/runtime/filetype.vim index b29168984c..383a45b9d3 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -883,11 +883,12 @@ au BufNewFile,BufRead *.ll setf lifelines " Lilo: Linux loader au BufNewFile,BufRead lilo.conf setf lilo -" Lisp (*.el = ELisp, *.cl = Common Lisp, *.jl = librep Lisp) +" Lisp (*.el = ELisp, *.cl = Common Lisp) +" *.jl was removed, it's also used for Julia, better skip than guess wrong. if has("fname_case") - au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.jl,*.L,.emacs,.sawfishrc setf lisp + au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.L,.emacs,.sawfishrc setf lisp else - au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,*.jl,.emacs,.sawfishrc setf lisp + au BufNewFile,BufRead *.lsp,*.lisp,*.el,*.cl,.emacs,.sawfishrc setf lisp endif " SBCL implementation of Common Lisp -- cgit From 909af2f3f10b49faf5f56ca866d316dbb0a94384 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Wed, 3 Jun 2020 01:36:26 -0400 Subject: vim-patch:8.2.0491: cannot recognize a