Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2022-04-01 10:19:32 +09:00
78 changed files with 2124 additions and 463 deletions
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -2,7 +2,7 @@
" Maintainer: <vacancy>
" Previous Maintainer: Aaron Griffin <aaronmgriffin@gmail.com>
" Version: 0.9
" Last Updated: 2020 Oct 9
" Last Updated: 2022 Mar 30
"
" Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim
"
@@ -91,6 +91,9 @@ endfunction
function! s:DefPython()
py3 << PYTHONEOF
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
import sys, tokenize, io, types
from token import NAME, DEDENT, NEWLINE, STRING
+7 -1
View File
@@ -1,5 +1,5 @@
" Maintainer: Drew Vogel <dvogel@sidejump.org>
" Last Change: 2021 Jul 25
" Last Change: 2022 Mar 20
"
" Replaced rgb.txt as the source of de facto standard color names. This is
" sourced each time the colorscheme command is run. It is also sourced each
@@ -430,6 +430,8 @@ call extend(v:colornames, {
\ 'yellow2': '#eeee00',
\ 'yellow3': '#cdcd00',
\ 'yellow4': '#8b8b00',
\ 'dark yellow': '#8b8b00',
\ 'darkyellow': '#8b8b00',
\ 'gold1': '#ffd700',
\ 'gold2': '#eec900',
\ 'gold3': '#cdad00',
@@ -506,6 +508,8 @@ call extend(v:colornames, {
\ 'orangered2': '#ee4000',
\ 'orangered3': '#cd3700',
\ 'orangered4': '#8b2500',
\ 'light red': '#ff8b8b',
\ 'lightred': '#ff8b8b',
\ 'red1': '#ff0000',
\ 'red2': '#ee0000',
\ 'red3': '#cd0000',
@@ -538,6 +542,8 @@ call extend(v:colornames, {
\ 'violetred2': '#ee3a8c',
\ 'violetred3': '#cd3278',
\ 'violetred4': '#8b2252',
\ 'light magenta': '#ff8bff',
\ 'lightmagenta': '#ff8bff',
\ 'magenta1': '#ff00ff',
\ 'magenta2': '#ee00ee',
\ 'magenta3': '#cd00cd',
+4 -4
View File
@@ -1,4 +1,4 @@
*autocmd.txt* For Vim version 8.2. Last change: 2022 Mar 04
*autocmd.txt* For Vim version 8.2. Last change: 2022 Mar 27
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -692,9 +692,9 @@ CursorHoldI Just like CursorHold, but in Insert mode.
CursorMoved After the cursor was moved in Normal or Visual
mode. Also when the text of the cursor line
has been changed, e.g., with "x", "rx" or "p".
Not triggered when there is typeahead, while
executing commands in a script file, when
an operator is pending or when moving to
Not always triggered when there is typeahead,
while executing commands in a script file,
when an operator is pending or when moving to
another window while remaining at the same
cursor position.
For an example see |match-parens|.
+10 -9
View File
@@ -1,4 +1,4 @@
*builtin.txt* For Vim version 8.2. Last change: 2022 Mar 08
*builtin.txt* For Vim version 8.2. Last change: 2022 Mar 26
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1562,14 +1562,15 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
or another valid interrupt key, confirm() returns 0.
An example: >
:let choice = confirm("What do you want?", "&Apples\n&Oranges\n&Bananas", 2)
:if choice == 0
: echo "make up your mind!"
:elseif choice == 3
: echo "tasteful"
:else
: echo "I prefer bananas myself."
:endif
let choice = confirm("What do you want?",
\ "&Apples\n&Oranges\n&Bananas", 2)
if choice == 0
echo "make up your mind!"
elseif choice == 3
echo "tasteful"
else
echo "I prefer bananas myself."
endif
< In a GUI dialog, buttons are used. The layout of the buttons
depends on the 'v' flag in 'guioptions'. If it is included,
the buttons are always put vertically. Otherwise, confirm()
+93 -6
View File
@@ -1,4 +1,4 @@
*channel.txt* For Vim version 8.2. Last change: 2022 Feb 27
*channel.txt* For Vim version 8.2. Last change: 2022 Mar 26
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -53,6 +53,7 @@ RAW nothing known, Vim cannot tell where a message ends
NL every message ends in a NL (newline) character
JSON JSON encoding |json_encode()|
JS JavaScript style JSON-like encoding |js_encode()|
LSP Language Server Protocol encoding |language-server-protocol|
Common combination are:
- Using a job connected through pipes in NL mode. E.g., to run a style
@@ -130,6 +131,7 @@ When using an IPv6 address, enclose it within square brackets. E.g.,
"js" - Use JS (JavaScript) encoding, more efficient than JSON.
"nl" - Use messages that end in a NL character
"raw" - Use raw messages
"lsp" - Use language server protocol encoding
*channel-callback* *E921*
"callback" A function that is called when a message is received that is
not handled otherwise (e.g. a JSON message with ID zero). It
@@ -140,8 +142,8 @@ When using an IPv6 address, enclose it within square brackets. E.g.,
endfunc
let channel = ch_open("localhost:8765", {"callback": "Handle"})
<
When "mode" is "json" or "js" the "msg" argument is the body
of the received message, converted to Vim types.
When "mode" is "json" or "js" or "lsp" the "msg" argument is
the body of the received message, converted to Vim types.
When "mode" is "nl" the "msg" argument is one message,
excluding the NL.
When "mode" is "raw" the "msg" argument is the whole message
@@ -165,7 +167,19 @@ When using an IPv6 address, enclose it within square brackets. E.g.,
to check for messages, the close_cb may be invoked while still
in the callback. The plugin must handle this somehow, it can
be useful to know that no more data is coming.
*channel-drop*
If it is not known if there is a message to be read, use a
try/catch block: >
try
let msg = ch_readraw(a:channel)
catch
let msg = 'no message'
endtry
try
let err = ch_readraw(a:channel, #{part: 'err'})
catch
let err = 'no error'
endtry
< *channel-drop*
"drop" Specifies when to drop messages:
"auto" When there is no callback to handle a message.
The "close_cb" is also considered for this.
@@ -443,7 +457,7 @@ to check if there is something to read.
Note that when there is no callback, messages are dropped. To avoid that add
a close callback to the channel.
To read all output from a RAW channel that is available: >
To read all normal output from a RAW channel that is available: >
let output = ch_readraw(channel)
To read the error output: >
let output = ch_readraw(channel, {"part": "err"})
@@ -503,6 +517,7 @@ ch_evalexpr({handle}, {expr} [, {options}]) *ch_evalexpr()*
according to the type of channel. The function cannot be used
with a raw channel. See |channel-use|.
{handle} can be a Channel or a Job that has a Channel.
When using the "lsp" channel mode, {expr} must be a |Dict|.
*E917*
{options} must be a Dictionary. It must not have a "callback"
entry. It can have a "timeout" entry to specify the timeout
@@ -578,7 +593,7 @@ ch_info({handle}) *ch_info()*
"err_io" "out", "null", "pipe", "file" or "buffer"
"err_timeout" timeout in msec
"in_status" "open" or "closed"
"in_mode" "NL", "RAW", "JSON" or "JS"
"in_mode" "NL", "RAW", "JSON", "JS" or "LSP"
"in_io" "null", "pipe", "file" or "buffer"
"in_timeout" timeout in msec
@@ -674,6 +689,7 @@ ch_sendexpr({handle}, {expr} [, {options}]) *ch_sendexpr()*
with a raw channel.
See |channel-use|. *E912*
{handle} can be a Channel or a Job that has a Channel.
When using the "lsp" channel mode, {expr} must be a |Dict|.
Can also be used as a |method|: >
GetChannel()->ch_sendexpr(expr)
@@ -1361,5 +1377,76 @@ The same in |Vim9| script: >
# start accepting shell commands
startinsert
==============================================================================
14. Language Server Protocol *language-server-protocol*
The language server protocol specification is available at:
https://microsoft.github.io/language-server-protocol/specification
Each LSP protocol message starts with a simple HTTP header followed by the
payload encoded in JSON-RPC format. This is described in:
https://www.jsonrpc.org/specification
For messages received on a channel with mode set to "lsp", Vim will process
the HTTP header and decode the payload into a Vim |Dict| type and call the
channel callback or the specified callback function. When sending messages on
a channel using |ch_evalexpr()| or |ch_sendexpr()|, Vim will add the HTTP
header and encode the Vim expression into JSON-RPC.
To open a channel using the 'lsp' mode, set the 'mode' item in the |ch_open()|
{options} argument to 'lsp'. Example: >
let ch = ch_open(..., #{mode: 'lsp'})
To open a channel using the 'lsp' mode with a job, set the 'in_mode' and
'out_mode' items in the |job_start()| {options} argument to 'lsp'. Example: >
let job = job_start(...., #{in_mode: 'lsp', out_mode: 'lsp'})
To synchronously send a JSON-RPC request to the server, use the |ch_evalexpr()|
function. This function will return the response from the server. You can use
the 'timeout' field in the {options} argument to control the response wait
time. Example: >
let req = {}
let req.method = 'textDocument/definition'
let req.params = {}
let req.params.textDocument = #{uri: 'a.c'}
let req.params.position = #{line: 10, character: 3}
let resp = ch_evalexpr(ch, req, #{timeout: 100})
Note that in the request message the 'id' field should not be specified. If it
is specified, then Vim will overwrite the value with an internally generated
identifier. Vim currently supports only a number type for the 'id' field.
To send a JSON-RPC request to the server and asynchronously process the
response, use the |ch_sendexpr()| function and supply a callback function.
Example: >
let req = {}
let req.method = 'textDocument/hover'
let req.params = {}
let req.params.textDocument = #{uri: 'a.c'}
let req.params.position = #{line: 10, character: 3}
let resp = ch_sendexpr(ch, req, #{callback: 'MyFn'})
To send a JSON-RPC notification message to the server, use the |ch_sendexpr()|
function. Example: >
call ch_sendexpr(ch, #{method: 'initialized'})
To respond to a JSON-RPC request message from the server, use the
|ch_sendexpr()| function. In the response message, copy the 'id' field value
from the server request message. Example: >
let resp = {}
let resp.id = req.id
let resp.result = 1
call ch_sendexpr(ch, resp)
The JSON-RPC notification messages from the server are delivered through the
|channel-callback| function.
vim:tw=78:ts=8:noet:ft=help:norl:
+2 -2
View File
@@ -1,4 +1,4 @@
*insert.txt* For Vim version 8.2. Last change: 2022 Mar 13
*insert.txt* For Vim version 8.2. Last change: 2022 Mar 28
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -800,7 +800,7 @@ If the previous expansion was split, because it got longer than 'textwidth',
then just the text in the current line will be used.
If the match found is at the end of a line, then the first word in the next
line will be inserted and the message "word from next line" displayed, if
line will be inserted and the message "Word from other line" displayed, if
this word is accepted the next CTRL-X CTRL-P or CTRL-X CTRL-N will search
for those lines starting with this word.
+3 -2
View File
@@ -1,4 +1,4 @@
*options.txt* For Vim version 8.2. Last change: 2022 Mar 13
*options.txt* For Vim version 8.2. Last change: 2022 Mar 29
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -984,7 +984,8 @@ A jump table for the options with a short description can be found at |Q_op|.
nostop like start, except CTRL-W and CTRL-U do not stop at the start of
insert.
When the value is empty, Vi compatible backspacing is used.
When the value is empty, Vi compatible backspacing is used, none of
the ways mentioned for the items above are possible.
For backwards compatibility with version 5.4 and earlier:
value effect ~
+10 -6
View File
@@ -1,4 +1,4 @@
*repeat.txt* For Vim version 8.2. Last change: 2022 Jan 21
*repeat.txt* For Vim version 8.2. Last change: 2022 Mar 30
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -210,22 +210,26 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
To source a range of lines that doesn't start with the
|:vim9script| command in Vim9 script context, the
|:vim9cmd| modifier can be used.
|:vim9cmd| modifier can be used. If you use a Visual
selection and type ":", the range in the form "'<,'>"
can come before it: >
:'<,'>vim9cmd source
< Otherwise the range goes after the modifier and must
have a colon prefixed, like all Vim9 ranges: >
:vim9cmd :5,9source
When a range of lines in a buffer is sourced in the
< When a range of lines in a buffer is sourced in the
Vim9 script context, the previously defined
script-local variables and functions are not cleared.
This works like the range started with the
":vim9script noclear" command. The "++clear" argument
can be used to clear the script-local variables and
functions before sourcing the script. This works like
the range started with the |:vimscript| command
the range started with the `:vim9script` command
without the "noclear" argument. See |vim9-reload| for
more information.
Examples: >
:4,5source
:vim9cmd :'<,'>source
:10,18source ++clear
*:source!*
+1
View File
@@ -8094,6 +8094,7 @@ lace.vim syntax.txt /*lace.vim*
lambda eval.txt /*lambda*
lang-variable eval.txt /*lang-variable*
language-mapping map.txt /*language-mapping*
language-server-protocol channel.txt /*language-server-protocol*
last-pattern pattern.txt /*last-pattern*
last-position-jump usr_05.txt /*last-position-jump*
last_buffer_nr() builtin.txt /*last_buffer_nr()*
+5 -17
View File
@@ -1,4 +1,4 @@
*todo.txt* For Vim version 8.2. Last change: 2022 Mar 18
*todo.txt* For Vim version 8.2. Last change: 2022 Mar 30
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -38,6 +38,9 @@ browser use: https://github.com/vim/vim/issues/1234
*known-bugs*
-------------------- Known bugs and current work -----------------------
Allow for: "import autoload '../lib/script.vim'"
avoids going through 'runtimepath' and avoids name collisions.
Really drop the Athena and NeXtaw GUI? Decide end of March.
Once Vim9 is stable:
@@ -48,6 +51,7 @@ Once Vim9 is stable:
vim9instr.c
vim9script.c
vim9type.c
- Adjust intro message to say "help version9".
Further Vim9 improvements, possibly after launch:
- Check performance with callgrind and kcachegrind.
@@ -417,13 +421,6 @@ register, then "" doesn't contain anything. Make it still follow "+.
File marks merging has duplicates since 7.4.1925. (Ingo Karkat, #5733)
"make test_gui" crashed in submenu_change(). Fix and remove workaround in
add_pixmap_args().
Athena is OK.
Motif: Build on Ubuntu can't enter any text in dialog text fields.
Running test_gui and test_gui_init with Motif sometimes kills the window
manager. Problem with Motif?
When editing a file with ":edit" the output of :swapname is relative, while
editing it with "vim file" it is absolute. (#355)
Which one should it be?
@@ -478,11 +475,6 @@ Test loose_clipboard() by selecting text before suspending.
Undo puts cursor in wrong line after "cG<Esc>" undo.
Implement completion for "breakadd". Should expand the second argument, e.g.
"func", and then function names after ":breakadd func". Including
script-local functions.
Also for ":profile".
:unmap <c-n> gives error but does remove the mapping. (Antony Scriven, 2019
Dec 19)
@@ -2501,10 +2493,6 @@ Works OK when 'cmdheight' is 2.
8 Use a mechanism similar to omni completion to figure out the kind of tab
for CTRL-] and jump to the appropriate matching tag (if there are
several).
Alternative: be able to define a function that takes the tag name and uses
taglist() to find the right location. With indication of using CTRL-] so
that the context can be taken into account. (Robert Webb)
Patch by Christian Brabandt, 2013 May 31.
The utf class table is missing some entries:
0x2212, minus sign
+16 -6
View File
@@ -1,4 +1,4 @@
*vim9.txt* For Vim version 8.2. Last change: 2022 Mar 18
*vim9.txt* For Vim version 8.2. Last change: 2022 Mar 28
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -184,8 +184,8 @@ For now you will need to pass the dictionary explicitly: >
def DictFunc(d: dict<any>, arg: string)
echo d[arg]
enddef
var d = {item: 'value', func: DictFunc}
d.func(d, 'item')
var ad = {item: 'value', func: DictFunc}
ad.func(d, 'item')
You can call a legacy dict function though: >
func Legacy() dict
@@ -376,13 +376,23 @@ And with autocommands: >
}
Although using a :def function probably works better.
*E1022* *E1103* *E1130* *E1131* *E1133*
*E1134* *E1235*
Declaring a variable with a type but without an initializer will initialize to
false (for bool), empty (for string, list, dict, etc.) or zero (for number,
any, etc.). This matters especially when using the "any" type, the value will
default to the number zero.
*E1016* *E1052* *E1066*
default to the number zero. For example, when declaring a list, items can be
added: >
var myList: list<number>
myList->add(7)
Initializing a variable to a null value, e.g. `null_list`, differs from not
initializing the variable. This throws an error: >
var myList = null_list
myList->add(7) # E1130: Cannot add to null list
< *E1016* *E1052* *E1066*
In Vim9 script `:let` cannot be used. An existing variable is assigned to
without any command. The same for global, window, tab, buffer and Vim
variables, because they are not really declared. Those can also be deleted
@@ -1243,7 +1253,7 @@ Closures defined in a loop will share the same context. For example: >
A closure must be compiled in the context that it is defined in, so that
variables in that context can be found. This mostly happens correctly, except
when a function is marked for debugging with `breakadd` after it was compiled.
Make sure the define the breakpoint before compiling the outerh function.
Make sure to define the breakpoint before compiling the outer function.
The "inloop" variable will exist only once, all closures put in the list refer
to the same instance, which in the end will have the value 4. This is
+4 -1
View File
@@ -1277,6 +1277,9 @@ au BufNewFile,BufRead *.[Oo][Pp][Ll] setf opl
" Oracle config file
au BufNewFile,BufRead *.ora setf ora
" Org
au BufNewFile,BufRead *.org,*.org_archive setf org
" Packet filter conf
au BufNewFile,BufRead pf.conf setf pf
@@ -1732,7 +1735,7 @@ au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh
au BufNewFile,BufRead *.zsh setf zsh
" Scheme
au BufNewFile,BufRead *.scm,*.ss,*.sld,*.rkt,*.rktd,*.rktl setf scheme
au BufNewFile,BufRead *.scm,*.ss,*.sld,*.rkt,*.rktd,*.rktl setf scheme
" Screen RC
au BufNewFile,BufRead .screenrc,screenrc setf screen
+6 -6
View File
@@ -5,7 +5,7 @@
" Meikel Brandmeyer <mb@kotka.de>
" URL: https://github.com/clojure-vim/clojure.vim
" License: Vim (see :h license)
" Last Change: 2021-10-26
" Last Change: 2022-03-24
if exists("b:did_ftplugin")
finish
@@ -43,7 +43,7 @@ setlocal commentstring=;\ %s
" specially and hence are not indented specially.
"
" -*- LISPWORDS -*-
" Generated from https://github.com/clojure-vim/clojure.vim/blob/62b215f079ce0f3834fd295c7a7f6bd8cc54bcc3/clj/src/vim_clojure_static/generate.clj
" Generated from https://github.com/clojure-vim/clojure.vim/blob/fd280e33e84c88e97860930557dba3ff80b1a82d/clj/src/vim_clojure_static/generate.clj
setlocal lispwords=as->,binding,bound-fn,case,catch,cond->,cond->>,condp,def,definline,definterface,defmacro,defmethod,defmulti,defn,defn-,defonce,defprotocol,defrecord,defstruct,deftest,deftest-,deftype,doseq,dotimes,doto,extend,extend-protocol,extend-type,fn,for,if,if-let,if-not,if-some,let,letfn,locking,loop,ns,proxy,reify,set-test,testing,when,when-first,when-let,when-not,when-some,while,with-bindings,with-in-str,with-local-vars,with-open,with-precision,with-redefs,with-redefs-fn,with-test
" Provide insert mode completions for special forms and clojure.core. As
@@ -66,10 +66,10 @@ endif
" Filter files in the browse dialog
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
let b:browsefilter = "Clojure Source Files (*.clj)\t*.clj\n" .
\ "ClojureScript Source Files (*.cljs)\t*.cljs\n" .
\ "Java Source Files (*.java)\t*.java\n" .
\ "All Files (*.*)\t*.*\n"
let b:browsefilter = "All Files\t*\n" .
\ "Clojure Files\t*.clj;*.cljc;*.cljs;*.cljx\n" .
\ "EDN Files\t*.edn\n" .
\ "Java Files\t*.java\n"
let b:undo_ftplugin .= ' | unlet! b:browsefilter'
endif
+25 -17
View File
@@ -3,7 +3,7 @@
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
" Last Change: 2020 Feb 13
" Last Change: 2022 Mar 21
if (exists("b:did_ftplugin"))
finish
@@ -53,7 +53,7 @@ endif
" TODO:
"setlocal define=^\\s*def
setlocal comments=:#
setlocal comments=b:#
setlocal commentstring=#\ %s
if !exists('g:ruby_version_paths')
@@ -87,8 +87,14 @@ endfunction
function! s:build_path(path) abort
let path = join(map(copy(a:path), 'v:val ==# "." ? "" : v:val'), ',')
if &g:path !~# '\v^%(\.,)=%(/%(usr|emx)/include,)=,$'
let path = substitute(&g:path,',,$',',','') . ',' . path
if &g:path =~# '\v^%(\.,)=%(/%(usr|emx)/include,)=,$'
let path = path . ',.,,'
elseif &g:path =~# ',\.,,$'
let path = &g:path[0:-4] . path . ',.,,'
elseif &g:path =~# ',,$'
let path = &g:path[0:-2] . path . ',,'
else
let path = substitute(&g:path, '[^,]\zs$', ',', '') . path
endif
return path
endfunction
@@ -164,6 +170,8 @@ let b:undo_ftplugin .= "| sil! cunmap <buffer> <Plug><ctag>| sil! cunmap <buffer
if !exists("g:no_plugin_maps") && !exists("g:no_ruby_maps")
nmap <buffer><script> <SID>: :<C-U>
nmap <buffer><script> <SID>c: :<C-U><C-R>=v:count ? v:count : ''<CR>
cmap <buffer> <SID><cfile> <Plug><cfile>
cmap <buffer> <SID><ctag> <Plug><ctag>
nnoremap <silent> <buffer> [m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'b','n')<CR>
nnoremap <silent> <buffer> ]m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'','n')<CR>
@@ -210,20 +218,20 @@ if !exists("g:no_plugin_maps") && !exists("g:no_ruby_maps")
call s:map('c', '', '<C-R><C-F> <Plug><cfile>')
cmap <buffer><script><expr> <SID>tagzv &foldopen =~# 'tag' ? '<Bar>norm! zv' : ''
call s:map('n', '<silent>', '<C-]> <SID>:exe v:count1."tag <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', 'g<C-]> <SID>:exe "tjump <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', 'g] <SID>:exe "tselect <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', '<C-W>] <SID>:exe v:count1."stag <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', '<C-W><C-]> <SID>:exe v:count1."stag <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', '<C-W>g<C-]> <SID>:exe "stjump <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', '<C-W>g] <SID>:exe "stselect <Plug><ctag>"<SID>tagzv<CR>')
call s:map('n', '<silent>', '<C-W>} <SID>:exe v:count1."ptag <Plug><ctag>"<CR>')
call s:map('n', '<silent>', '<C-W>g} <SID>:exe "ptjump <Plug><ctag>"<CR>')
call s:map('n', '<script><silent>', '<C-]> <SID>:exe v:count1."tag <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', 'g<C-]> <SID>:exe "tjump <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', 'g] <SID>:exe "tselect <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', '<C-W>] <SID>:exe v:count1."stag <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', '<C-W><C-]> <SID>:exe v:count1."stag <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', '<C-W>g<C-]> <SID>:exe "stjump <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', '<C-W>g] <SID>:exe "stselect <SID><ctag>"<SID>tagzv<CR>')
call s:map('n', '<script><silent>', '<C-W>} <SID>:exe v:count1."ptag <SID><ctag>"<CR>')
call s:map('n', '<script><silent>', '<C-W>g} <SID>:exe "ptjump <SID><ctag>"<CR>')
call s:map('n', '<silent>', 'gf <SID>c:find <Plug><cfile><CR>')
call s:map('n', '<silent>', '<C-W>f <SID>c:sfind <Plug><cfile><CR>')
call s:map('n', '<silent>', '<C-W><C-F> <SID>c:sfind <Plug><cfile><CR>')
call s:map('n', '<silent>', '<C-W>gf <SID>c:tabfind <Plug><cfile><CR>')
call s:map('n', '<script><silent>', 'gf <SID>c:find <SID><cfile><CR>')
call s:map('n', '<script><silent>', '<C-W>f <SID>c:sfind <SID><cfile><CR>')
call s:map('n', '<script><silent>', '<C-W><C-F> <SID>c:sfind <SID><cfile><CR>')
call s:map('n', '<script><silent>', '<C-W>gf <SID>c:tabfind <SID><cfile><CR>')
endif
let &cpo = s:cpo_save
+1 -1
View File
@@ -5,7 +5,7 @@
" Meikel Brandmeyer <mb@kotka.de>
" URL: https://github.com/clojure-vim/clojure.vim
" License: Vim (see :h license)
" Last Change: 2021-10-26
" Last Change: 2022-03-24
if exists("b:did_indent")
finish
+37 -11
View File
@@ -4,7 +4,7 @@
" Previous Maintainer: Nikolai Weibull <now at bitwi.se>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
" Last Change: 2021 Feb 03
" Last Change: 2022 Mar 22
" 0. Initialization {{{1
" =================
@@ -40,9 +40,11 @@ setlocal nosmartindent
" Now, set up our indentation expression and keys that trigger it.
setlocal indentexpr=GetRubyIndent(v:lnum)
setlocal indentkeys=0{,0},0),0],!^F,o,O,e,:,.
setlocal indentkeys+==end,=else,=elsif,=when,=ensure,=rescue,==begin,==end
setlocal indentkeys+==end,=else,=elsif,=when,=in\ ,=ensure,=rescue,==begin,==end
setlocal indentkeys+==private,=protected,=public
let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<"
" Only define the function once.
if exists("*GetRubyIndent")
finish
@@ -85,14 +87,17 @@ let s:skip_expr =
" Regex used for words that, at the start of a line, add a level of indent.
let s:ruby_indent_keywords =
\ '^\s*\zs\<\%(module\|class\|if\|for' .
\ '\|while\|until\|else\|elsif\|case\|when\|unless\|begin\|ensure\|rescue' .
\ '\|while\|until\|else\|elsif\|case\|when\|in\|unless\|begin\|ensure\|rescue' .
\ '\|\%(\K\k*[!?]\?\s\+\)\=def\):\@!\>' .
\ '\|\%([=,*/%+-]\|<<\|>>\|:\s\)\s*\zs' .
\ '\<\%(if\|for\|while\|until\|case\|unless\|begin\):\@!\>'
" Def without an end clause: def method_call(...) = <expression>
let s:ruby_endless_def = '\<def\s\+\k\+[!?]\=\%((.*)\|\s\)\s*='
" Regex used for words that, at the start of a line, remove a level of indent.
let s:ruby_deindent_keywords =
\ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|end\):\@!\>'
\ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|in\|end\):\@!\>'
" Regex that defines the start-match for the 'end' keyword.
"let s:end_start_regex = '\%(^\|[^.]\)\<\%(module\|class\|def\|if\|for\|while\|until\|case\|unless\|begin\|do\)\>'
@@ -104,15 +109,31 @@ let s:end_start_regex =
\ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>'
" Regex that defines the middle-match for the 'end' keyword.
let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|elsif\):\@!\>'
let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|\%(\%(^\|;\)\s*\)\@<=\<in\|elsif\):\@!\>'
" Regex that defines the end-match for the 'end' keyword.
let s:end_end_regex = '\%(^\|[^.:@$]\)\@<=\<end:\@!\>'
" Expression used for searchpair() call for finding match for 'end' keyword.
let s:end_skip_expr = s:skip_expr .
\ ' || (expand("<cword>") == "do"' .
\ ' && getline(".") =~ "^\\s*\\<\\(while\\|until\\|for\\):\\@!\\>")'
" Expression used for searchpair() call for finding a match for an 'end' keyword.
function! s:EndSkipExpr()
if eval(s:skip_expr)
return 1
elseif expand('<cword>') == 'do'
\ && getline(".") =~ '^\s*\<\(while\|until\|for\):\@!\>'
return 1
elseif getline('.') =~ s:ruby_endless_def
return 1
elseif getline('.') =~ '\<def\s\+\k\+[!?]\=([^)]*$'
" Then it's a `def method(` with a possible `) =` later
call search('\<def\s\+\k\+\zs(', 'W', line('.'))
normal! %
return getline('.') =~ ')\s*='
else
return 0
endif
endfunction
let s:end_skip_expr = function('s:EndSkipExpr')
" Regex that defines continuation lines, not including (, {, or [.
let s:non_bracket_continuation_regex =
@@ -572,6 +593,11 @@ function! s:AfterUnbalancedBracket(pline_info) abort
call cursor(info.plnum, closing.pos + 1)
normal! %
if strpart(info.pline, closing.pos) =~ '^)\s*='
" special case: the closing `) =` of an endless def
return indent(s:GetMSL(line('.')))
endif
if s:Match(line('.'), s:ruby_indent_keywords)
return indent('.') + info.sw
else
@@ -610,7 +636,7 @@ function! s:AfterIndentKeyword(pline_info) abort
let info = a:pline_info
let col = s:Match(info.plnum, s:ruby_indent_keywords)
if col > 0
if col > 0 && s:Match(info.plnum, s:ruby_endless_def) <= 0
call cursor(info.plnum, col)
let ind = virtcol('.') - 1 + info.sw
" TODO: make this better (we need to count them) (or, if a searchpair
@@ -657,7 +683,7 @@ function! s:IndentingKeywordInMSL(msl_info) abort
" TODO: this does not take into account contrived things such as
" module Foo; class Bar; end
let col = s:Match(info.plnum_msl, s:ruby_indent_keywords)
if col > 0
if col > 0 && s:Match(info.plnum_msl, s:ruby_endless_def) <= 0
let ind = indent(info.plnum_msl) + info.sw
if s:Match(info.plnum_msl, s:end_end_regex)
let ind = ind - info.sw
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -3,7 +3,7 @@
" Maintainer: Debian Vim Maintainers
" Former Maintainers: Gerfried Fuchs <alfie@ist.org>
" Wichert Akkerman <wakkerma@debian.org>
" Last Change: 2021 Oct 19
" Last Change: 2022 Mar 28
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debchangelog.vim
" Standard syntax initialization
@@ -24,7 +24,7 @@ let s:supported = [
\ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm',
\ 'trixie', 'sid', 'rc-buggy',
\
\ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy',
\ 'trusty', 'xenial', 'bionic', 'focal', 'impish', 'jammy',
\ 'devel'
\ ]
let s:unsupported = [
@@ -35,7 +35,7 @@ let s:unsupported = [
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
\ 'utopic', 'vivid', 'wily', 'yakkety', 'zesty', 'artful', 'cosmic',
\ 'disco', 'eoan', 'groovy'
\ 'disco', 'eoan', 'hirsute', 'groovy'
\ ]
let &cpo=s:cpo
+3 -3
View File
@@ -2,7 +2,7 @@
" Language: Debian sources.list
" Maintainer: Debian Vim Maintainers
" Former Maintainer: Matthijs Mohlmann <matthijs@cacholong.nl>
" Last Change: 2021 Oct 19
" Last Change: 2022 Mar 28
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debsources.vim
" Standard syntax initialization
@@ -26,7 +26,7 @@ let s:supported = [
\ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm',
\ 'trixie', 'sid', 'rc-buggy',
\
\ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy',
\ 'trusty', 'xenial', 'bionic', 'focal', 'impish', 'jammy',
\ 'devel'
\ ]
let s:unsupported = [
@@ -37,7 +37,7 @@ let s:unsupported = [
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
\ 'utopic', 'vivid', 'wily', 'yakkety', 'zesty', 'artful', 'cosmic',
\ 'disco', 'eoan', 'groovy'
\ 'disco', 'eoan', 'hirsute', 'groovy'
\ ]
let &cpo=s:cpo
+5 -7
View File
@@ -3,9 +3,9 @@
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
" Last Change: 2018 Jul 04
" Last Change: 2022 Mar 18
if &syntax !~# '\<eruby\>' || get(b:, 'current_syntax') =~# '\<eruby\>'
if exists("b:current_syntax")
finish
endif
@@ -19,8 +19,6 @@ endif
if &filetype =~ '^eruby\.'
let b:eruby_subtype = matchstr(&filetype,'^eruby\.\zs\w\+')
elseif &filetype =~ '^.*\.eruby\>'
let b:eruby_subtype = matchstr(&filetype,'^.\{-\}\ze\.eruby\>')
elseif !exists("b:eruby_subtype") && main_syntax == 'eruby'
let s:lines = getline(1)."\n".getline(2)."\n".getline(3)."\n".getline(4)."\n".getline(5)."\n".getline("$")
let b:eruby_subtype = matchstr(s:lines,'eruby_subtype=\zs\w\+')
@@ -54,10 +52,10 @@ if !b:eruby_nest_level
let b:eruby_nest_level = 1
endif
if get(b:, 'eruby_subtype', '') !~# '^\%(eruby\)\=$' && &syntax =~# '^eruby\>'
if exists("b:eruby_subtype") && b:eruby_subtype != '' && b:eruby_subtype !=? 'eruby'
exe "runtime! syntax/".b:eruby_subtype.".vim"
unlet! b:current_syntax
endif
unlet! b:current_syntax
syn include @rubyTop syntax/ruby.vim
syn cluster erubyRegions contains=erubyOneLiner,erubyBlock,erubyExpression,erubyComment
@@ -72,7 +70,7 @@ exe 'syn region erubyComment matchgroup=erubyDelimiter start="<%\{1,'.b:erub
hi def link erubyDelimiter PreProc
hi def link erubyComment Comment
let b:current_syntax = matchstr(&syntax, '^.*\<eruby\>')
let b:current_syntax = 'eruby'
if main_syntax == 'eruby'
unlet main_syntax
+13 -3
View File
@@ -3,7 +3,7 @@
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
" Last Change: 2021 Jun 06
" Last Change: 2021 Nov 03
" ----------------------------------------------------------------------------
"
" Previous Maintainer: Mirko Nasato
@@ -66,7 +66,7 @@ endfunction
com! -nargs=* SynFold call s:run_syntax_fold(<q-args>)
" Not-Top Cluster {{{1
syn cluster rubyNotTop contains=@rubyCommentNotTop,@rubyStringNotTop,@rubyRegexpSpecial,@rubyDeclaration,@rubyExceptionHandler,@rubyClassOperator,rubyConditional,rubyModuleName,rubyClassName,rubySymbolDelimiter,rubyParentheses,@Spell
syn cluster rubyNotTop contains=@rubyCommentNotTop,@rubyStringNotTop,@rubyRegexpSpecial,@rubyDeclaration,@rubyExceptionHandler,@rubyClassOperator,rubyConditional,rubyModuleName,rubyClassName,rubySymbolDelimiter,rubyDoubleQuoteSymbolDelimiter,rubySingleQuoteSymbolDelimiter,rubyParentheses,@Spell
" Whitespace Errors {{{1
if exists("ruby_space_errors")
@@ -364,6 +364,9 @@ if !exists("b:ruby_no_expensive") && !exists("ruby_no_expensive")
SynFold 'class' syn region rubyClassBlock start="\<class\>" matchgroup=rubyClass skip="\<end:" end="\<end\>" contains=ALLBUT,@rubyNotTop
SynFold 'module' syn region rubyModuleBlock start="\<module\>" matchgroup=rubyModule skip="\<end:" end="\<end\>" contains=ALLBUT,@rubyNotTop
" endless def
syn match rubyDefine "\<def\s\+\ze[^[:space:];#(]\+\%(\s\+\|\s*(.*)\s*\)=" nextgroup=rubyMethodDeclaration skipwhite
" modifiers
syn match rubyLineContinuation "\\$" nextgroup=@rubyModifier skipwhite skipnl
syn match rubyConditionalModifier "\<\%(if\|unless\)\>"
@@ -430,9 +433,10 @@ endif
" Comments and Documentation {{{1
syn match rubySharpBang "\%^#!.*" display
syn keyword rubyTodo FIXME NOTE TODO OPTIMIZE HACK REVIEW XXX todo contained
syn match rubyEncoding "[[:alnum:]-]\+" contained display
syn match rubyEncoding "[[:alnum:]-_]\+" contained display
syn match rubyMagicComment "\c\%<3l#\s*\zs\%(coding\|encoding\):" contained nextgroup=rubyEncoding skipwhite
syn match rubyMagicComment "\c\%<10l#\s*\zs\%(frozen_string_literal\|warn_indent\|warn_past_scope\):" contained nextgroup=rubyBoolean skipwhite
syn match rubyMagicComment "\c\%<10l#\s*\zs\%(shareable_constant_value\):" contained nextgroup=rubyEncoding skipwhite
syn match rubyComment "#.*" contains=@rubyCommentSpecial,rubySpaceError,@Spell
syn cluster rubyCommentSpecial contains=rubySharpBang,rubyTodo,rubyMagicComment
@@ -465,6 +469,10 @@ syn match rubyDefinedOperator "\%#=1\<defined?" display
syn match rubySymbol "\%(\w\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[?!]\=::\@!"he=e-1 contained containedin=rubyBlockParameterList,rubyCurlyBlock
syn match rubySymbol "[]})\"':]\@1<!\<\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],;]\@="he=e-1
syn match rubySymbol "[[:space:],{(]\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],;]\@="hs=s+1,he=e-1
syn match rubySingleQuoteSymbolDelimiter "'" contained
syn match rubySymbol "'\%(\\.\|[^']\)*'::\@!"he=e-1 contains=rubyQuoteEscape,rubyBackslashEscape,rubySingleQuoteSymbolDelimiter
syn match rubyDoubleQuoteSymbolDelimiter "\"" contained
syn match rubySymbol "\"\%(\\.\|[^\"]\)*\"::\@!"he=e-1 contains=@rubyStringSpecial,rubyDoubleQuoteSymbolDelimiter
" __END__ Directive {{{1
SynFold '__END__' syn region rubyData matchgroup=rubyDataDirective start="^__END__$" end="\%$"
@@ -565,6 +573,8 @@ hi def link rubyHeredocDelimiter rubyStringDelimiter
hi def link rubyPercentRegexpDelimiter rubyRegexpDelimiter
hi def link rubyPercentStringDelimiter rubyStringDelimiter
hi def link rubyPercentSymbolDelimiter rubySymbolDelimiter
hi def link rubyDoubleQuoteSymbolDelimiter rubySymbolDelimiter
hi def link rubySingleQuoteSymbolDelimiter rubySymbolDelimiter
hi def link rubyRegexpDelimiter rubyStringDelimiter
hi def link rubySymbolDelimiter rubySymbol
hi def link rubyString String
+79 -48
View File
@@ -1,5 +1,5 @@
" Language: tmux(1) configuration file
" Version: 3.2a (git-44ada9cd)
" Version: 3.3-rc (git-964deae4)
" URL: https://github.com/ericpruitt/tmux.vim/
" Maintainer: Eric Pruitt <eric.pruitt@gmail.com>
" License: 2-Clause BSD (http://opensource.org/licenses/BSD-2-Clause)
@@ -18,40 +18,49 @@ syntax iskeyword @,48-57,_,192-255,-
syntax case match
syn keyword tmuxAction none any current other
syn keyword tmuxBoolean off on
syn keyword tmuxBoolean off on yes no
syn keyword tmuxTodo FIXME NOTE TODO XXX contained
syn match tmuxColour /\<colour[0-9]\+/ display
syn match tmuxColour /\<colou\?r[0-9]\+\>/ display
syn match tmuxKey /\(C-\|M-\|\^\)\+\S\+/ display
syn match tmuxNumber /\<\d\+\>/ display
syn match tmuxFlags /\s-\a\+/ display
syn match tmuxVariable /\w\+=/ display
syn match tmuxVariableExpansion /\${\=\w\+}\=/ display
syn match tmuxControl /%\(if\|elif\|else\|endif\)/
syn match tmuxVariableExpansion /\$\({[A-Za-z_]\w*}\|[A-Za-z_]\w*\)/ display
syn match tmuxControl /^\s*%\(if\|elif\|else\|endif\)\>/
syn match tmuxEscape /\\\(u\x\{4\}\|U\x\{8\}\|\o\{3\}\|[\\ernt$]\)/ display
syn region tmuxComment start=/#/ skip=/\\\@<!\\$/ end=/$/ contains=tmuxTodo,@Spell
syn region tmuxString start=+"+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=tmuxFormatString,@Spell
syn region tmuxString start=+'+ skip=+\\\\\|\\'\|\\$+ excludenl end=+'+ end='$' contains=tmuxFormatString,@Spell
syn region tmuxString start=+"+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=tmuxFormatString,tmuxEscape,tmuxVariableExpansion,@Spell
syn region tmuxUninterpolatedString start=+'+ skip=+\\$+ excludenl end=+'+ end='$' contains=tmuxFormatString,@Spell
" TODO: Figure out how escaping works inside of #(...) and #{...} blocks.
syn region tmuxFormatString start=/#[#DFhHIPSTW]/ end=// contained keepend
syn region tmuxFormatString start=/#{/ skip=/#{.\{-}}/ end=/}/ keepend
syn region tmuxFormatString start=/#(/ skip=/#(.\{-})/ end=/)/ contained keepend
" At the time of this writing, the latest tmux release will parse a line
" reading "abc=xyz set-option ..." as an assignment followed by a command
" hence the presence of "\s" in the "end" argument.
syn region tmuxAssignment matchgroup=tmuxVariable start=/^\s*[A-Za-z_]\w*=\@=/ skip=/\\$\|\\\s/ end=/\s\|$/ contains=tmuxString,tmuxUninterpolatedString,tmuxVariableExpansion,tmuxControl,tmuxEscape
hi def link tmuxFormatString Identifier
hi def link tmuxAction Boolean
hi def link tmuxBoolean Boolean
hi def link tmuxCommands Keyword
hi def link tmuxControl Keyword
hi def link tmuxControl PreCondit
hi def link tmuxComment Comment
hi def link tmuxEscape Special
hi def link tmuxEscapeUnquoted Special
hi def link tmuxKey Special
hi def link tmuxNumber Number
hi def link tmuxFlags Identifier
hi def link tmuxOptions Function
hi def link tmuxString String
hi def link tmuxTodo Todo
hi def link tmuxUninterpolatedString
\ String
hi def link tmuxVariable Identifier
hi def link tmuxVariableExpansion Identifier
@@ -61,63 +70,85 @@ hi def link tmuxVariableExpansion Identifier
if get(g:, "tmux_syntax_colors", 1)
for s:i in range(0, 255)
let s:bg = (!s:i || s:i == 16 || (s:i > 231 && s:i < 235)) ? 15 : "none"
exec "syn match tmuxColour" . s:i . " /\\<colour" . s:i . "\\>/ display"
exec "syn match tmuxColour" . s:i . " /\\<colou\\?r" . s:i . "\\>/ display"
\ " | highlight tmuxColour" . s:i . " ctermfg=" . s:i . " ctermbg=" . s:bg
endfor
endif
syn keyword tmuxOptions
\ backspace buffer-limit command-alias copy-command default-terminal editor
\ escape-time exit-empty activity-action assume-paste-time base-index
\ bell-action default-command default-shell default-size destroy-unattached
\ activity-action after-bind-key after-capture-pane after-copy-mode
\ after-display-message after-display-panes after-kill-pane
\ after-list-buffers after-list-clients after-list-keys after-list-panes
\ after-list-sessions after-list-windows after-load-buffer after-lock-server
\ after-new-session after-new-window after-paste-buffer after-pipe-pane
\ after-queue after-refresh-client after-rename-session after-rename-window
\ after-resize-pane after-resize-window after-save-buffer
\ after-select-layout after-select-pane after-select-window after-send-keys
\ after-set-buffer after-set-environment after-set-hook after-set-option
\ after-show-environment after-show-messages after-show-options
\ after-split-window after-unbind-key aggressive-resize alert-activity
\ alert-bell alert-silence allow-passthrough allow-rename alternate-screen
\ assume-paste-time automatic-rename automatic-rename-format backspace
\ base-index bell-action buffer-limit client-active client-attached
\ client-detached client-focus-in client-focus-out client-resized
\ client-session-changed clock-mode-colour clock-mode-style command-alias
\ copy-command copy-mode-current-match-style copy-mode-mark-style
\ copy-mode-match-style cursor-colour cursor-style default-command
\ default-shell default-size default-terminal destroy-unattached
\ detach-on-destroy display-panes-active-colour display-panes-colour
\ display-panes-time display-time exit-unattached extended-keys focus-events
\ history-file history-limit key-table lock-after-time lock-command
\ message-command-style message-limit message-style aggressive-resize
\ allow-rename alternate-screen automatic-rename automatic-rename-format
\ clock-mode-colour clock-mode-style copy-mode-current-match-style
\ copy-mode-mark-style copy-mode-match-style main-pane-height
\ main-pane-width mode-keys mode-style monitor-activity monitor-bell
\ monitor-silence mouse other-pane-height other-pane-width
\ pane-active-border-style pane-base-index pane-border-format
\ pane-border-lines pane-border-status pane-border-style pane-colours prefix
\ prefix2 prompt-history-limit remain-on-exit renumber-windows repeat-time
\ set-clipboard set-titles set-titles-string silence-action status status-bg
\ status-fg status-format status-interval status-justify status-keys
\ status-left status-left-length status-left-style status-position
\ status-right status-right-length status-right-style status-style
\ synchronize-panes terminal-features terminal-overrides update-environment
\ user-keys visual-activity visual-bell visual-silence window-active-style
\ display-panes-time display-time editor escape-time exit-empty
\ exit-unattached extended-keys fill-character focus-events history-file
\ history-limit key-table lock-after-time lock-command main-pane-height
\ main-pane-width message-command-style message-limit message-style
\ mode-keys mode-style monitor-activity monitor-bell monitor-silence mouse
\ other-pane-height other-pane-width pane-active-border-style
\ pane-base-index pane-border-format pane-border-indicators
\ pane-border-lines pane-border-status pane-border-style pane-colours
\ pane-died pane-exited pane-focus-in pane-focus-out pane-mode-changed
\ pane-set-clipboard pane-title-changed popup-border-lines
\ popup-border-style popup-style prefix prefix2 prompt-history-limit
\ remain-on-exit remain-on-exit-format renumber-windows repeat-time
\ scroll-on-clear session-closed session-created session-renamed
\ session-window-changed set-clipboard set-titles set-titles-string
\ silence-action status status-bg status-fg status-format status-interval
\ status-justify status-keys status-left status-left-length
\ status-left-style status-position status-right status-right-length
\ status-right-style status-style synchronize-panes terminal-features
\ terminal-overrides update-environment user-keys visual-activity
\ visual-bell visual-silence window-active-style window-layout-changed
\ window-linked window-pane-changed window-renamed window-resized
\ window-size window-status-activity-style window-status-bell-style
\ window-status-current-format window-status-current-style
\ window-status-format window-status-last-style window-status-separator
\ window-status-style window-style word-separators wrap-search
\ window-status-style window-style window-unlinked word-separators
\ wrap-search xterm-keys
syn keyword tmuxCommands
\ attach attach-session bind bind-key break-pane breakp capture-pane
\ capturep choose-buffer choose-client choose-tree clear-history clearhist
\ capturep choose-buffer choose-client choose-session choose-tree
\ choose-window clear-history clear-prompt-history clearhist clearphist
\ clock-mode command-prompt confirm confirm-before copy-mode customize-mode
\ detach detach-client display display-menu display-message display-panes
\ display-popup displayp find-window findw if if-shell join-pane joinp
\ kill-pane kill-server kill-session kill-window killp has has-session killw
\ delete-buffer deleteb detach detach-client display display-menu
\ display-message display-panes display-popup displayp find-window findw has
\ has-session if if-shell info join-pane joinp kill-pane kill-server
\ kill-session kill-window killp killw last last-pane last-window lastp
\ link-window linkw list-buffers list-clients list-commands list-keys
\ list-panes list-sessions list-windows load-buffer loadb lock lock-client
\ lock-server lock-session lockc last-pane lastp locks ls last last-window
\ lsb delete-buffer deleteb lsc lscm lsk lsp lsw menu move-pane move-window
\ clear-prompt-history clearphist movep movew new new-session new-window
\ neww next next-layout next-window nextl paste-buffer pasteb pipe-pane
\ pipep popup prev previous-layout previous-window prevl refresh
\ refresh-client rename rename-session rename-window renamew resize-pane
\ resize-window resizep resizew respawn-pane respawn-window respawnp
\ respawnw rotate-window rotatew run run-shell save-buffer saveb
\ select-layout select-pane select-window selectl selectp selectw send
\ send-keys send-prefix set set-buffer set-environment set-hook set-option
\ lock-server lock-session lockc locks ls lsb lsc lscm lsk lsp lsw menu
\ move-pane move-window movep movew new new-session new-window neww next
\ next-layout next-window nextl paste-buffer pasteb pipe-pane pipep popup
\ prev previous-layout previous-window prevl refresh refresh-client rename
\ rename-session rename-window renamew resize-pane resize-window resizep
\ resizew respawn-pane respawn-window respawnp respawnw rotate-window
\ rotatew run run-shell save-buffer saveb select-layout select-pane
\ select-window selectl selectp selectw send send-keys send-prefix
\ server-info set set-buffer set-environment set-hook set-option
\ set-window-option setb setenv setw show show-buffer show-environment
\ show-hooks show-messages show-options show-prompt-history
\ show-window-options showb showenv showmsgs showphist showw source
\ source-file split-window splitw start start-server suspend-client suspendc
\ swap-pane swap-window swapp swapw switch-client switchc unbind unbind-key
\ unlink-window unlinkw wait wait-for
\ source-file split-pane split-window splitp splitw start start-server
\ suspend-client suspendc swap-pane swap-window swapp swapw switch-client
\ switchc unbind unbind-key unlink-window unlinkw wait wait-for
let &cpo = s:original_cpo
unlet! s:original_cpo s:bg s:i
+1 -1
View File
@@ -641,7 +641,7 @@ changed_common(
set_topline(wp, wp->w_topline);
#endif
// Relative numbering may require updating more.
if (wp->w_p_rnu)
if (wp->w_p_rnu && xtra != 0)
redraw_win_later(wp, SOME_VALID);
#ifdef FEAT_SYN_HL
// Cursor line highlighting probably need to be updated with
+253 -45
View File
@@ -2137,6 +2137,83 @@ channel_fill(js_read_T *reader)
return TRUE;
}
/*
* Process the HTTP header in a Language Server Protocol (LSP) message.
*
* The message format is described in the LSP specification:
* https://microsoft.github.io/language-server-protocol/specification
*
* It has the following two fields:
*
* Content-Length: ...
* Content-Type: application/vscode-jsonrpc; charset=utf-8
*
* Each field ends with "\r\n". The header ends with an additional "\r\n".
*
* Returns OK if a valid header is received and FAIL if some fields in the
* header are not correct. Returns MAYBE if a partial header is received and
* need to wait for more data to arrive.
*/
static int
channel_process_lsp_http_hdr(js_read_T *reader)
{
char_u *line_start;
char_u *p;
int_u hdr_len;
int payload_len = -1;
int_u jsbuf_len;
// We find the end once, to avoid calling strlen() many times.
jsbuf_len = (int_u)STRLEN(reader->js_buf);
reader->js_end = reader->js_buf + jsbuf_len;
p = reader->js_buf;
// Process each line in the header till an empty line is read (header
// separator).
while (TRUE)
{
line_start = p;
while (*p != NUL && *p != '\n')
p++;
if (*p == NUL) // partial header
return MAYBE;
p++;
// process the content length field (if present)
if ((p - line_start > 16)
&& STRNICMP(line_start, "Content-Length: ", 16) == 0)
{
errno = 0;
payload_len = strtol((char *)line_start + 16, NULL, 10);
if (errno == ERANGE || payload_len < 0)
// invalid length, discard the payload
return FAIL;
}
if ((p - line_start) == 2 && line_start[0] == '\r' &&
line_start[1] == '\n')
// reached the empty line
break;
}
if (payload_len == -1)
// Content-Length field is not present in the header
return FAIL;
hdr_len = p - reader->js_buf;
// if the entire payload is not received, wait for more data to arrive
if (jsbuf_len < hdr_len + payload_len)
return MAYBE;
reader->js_used += hdr_len;
// recalculate the end based on the length read from the header.
reader->js_end = reader->js_buf + hdr_len + payload_len;
return OK;
}
/*
* Use the read buffer of "channel"/"part" and parse a JSON message that is
* complete. The messages are added to the queue.
@@ -2150,7 +2227,7 @@ channel_parse_json(channel_T *channel, ch_part_T part)
jsonq_T *item;
chanpart_T *chanpart = &channel->ch_part[part];
jsonq_T *head = &chanpart->ch_json_head;
int status;
int status = OK;
int ret;
if (channel_peek(channel, part) == NULL)
@@ -2162,19 +2239,31 @@ channel_parse_json(channel_T *channel, ch_part_T part)
reader.js_cookie = channel;
reader.js_cookie_arg = part;
if (chanpart->ch_mode == MODE_LSP)
status = channel_process_lsp_http_hdr(&reader);
// When a message is incomplete we wait for a short while for more to
// arrive. After the delay drop the input, otherwise a truncated string
// or list will make us hang.
// Do not generate error messages, they will be written in a channel log.
++emsg_silent;
status = json_decode(&reader, &listtv,
chanpart->ch_mode == MODE_JS ? JSON_JS : 0);
--emsg_silent;
if (status == OK)
{
++emsg_silent;
status = json_decode(&reader, &listtv,
chanpart->ch_mode == MODE_JS ? JSON_JS : 0);
--emsg_silent;
}
if (status == OK)
{
// Only accept the response when it is a list with at least two
// items.
if (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2)
if (chanpart->ch_mode == MODE_LSP && listtv.v_type != VAR_DICT)
{
ch_error(channel, "Did not receive a LSP dict, discarding");
clear_tv(&listtv);
}
else if (chanpart->ch_mode != MODE_LSP &&
(listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2))
{
if (listtv.v_type != VAR_LIST)
ch_error(channel, "Did not receive a list, discarding");
@@ -2401,11 +2490,38 @@ channel_get_json(
while (item != NULL)
{
list_T *l = item->jq_value->vval.v_list;
list_T *l;
typval_T *tv;
CHECK_LIST_MATERIALIZE(l);
tv = &l->lv_first->li_tv;
if (channel->ch_part[part].ch_mode != MODE_LSP)
{
l = item->jq_value->vval.v_list;
CHECK_LIST_MATERIALIZE(l);
tv = &l->lv_first->li_tv;
}
else
{
dict_T *d;
dictitem_T *di;
// LSP message payload is a JSON-RPC dict.
// For RPC requests and responses, the 'id' item will be present.
// For notifications, it will not be present.
if (id > 0)
{
if (item->jq_value->v_type != VAR_DICT)
goto nextitem;
d = item->jq_value->vval.v_dict;
if (d == NULL)
goto nextitem;
di = dict_find(d, (char_u *)"id", -1);
if (di == NULL)
goto nextitem;
tv = &di->di_tv;
}
else
tv = item->jq_value;
}
if ((without_callback || !item->jq_no_callback)
&& ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id)
@@ -2421,6 +2537,7 @@ channel_get_json(
remove_json_node(head, item);
return OK;
}
nextitem:
item = item->jq_next;
}
return FAIL;
@@ -2788,6 +2905,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
callback_T *callback = NULL;
buf_T *buffer = NULL;
char_u *p;
int called_otc; // one time callbackup
if (channel->ch_nb_close_cb != NULL)
// this channel is handled elsewhere (netbeans)
@@ -2814,7 +2932,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
buffer = NULL;
}
if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
if (ch_mode == MODE_JSON || ch_mode == MODE_JS || ch_mode == MODE_LSP)
{
listitem_T *item;
int argc = 0;
@@ -2828,29 +2946,47 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
return FALSE;
}
for (item = listtv->vval.v_list->lv_first;
item != NULL && argc < CH_JSON_MAX_ARGS;
item = item->li_next)
argv[argc++] = item->li_tv;
while (argc < CH_JSON_MAX_ARGS)
argv[argc++].v_type = VAR_UNKNOWN;
if (argv[0].v_type == VAR_STRING)
if (ch_mode == MODE_LSP)
{
// ["cmd", arg] or ["cmd", arg, arg] or ["cmd", arg, arg, arg]
channel_exe_cmd(channel, part, argv);
free_tv(listtv);
return TRUE;
}
dict_T *d = listtv->vval.v_dict;
dictitem_T *di;
if (argv[0].v_type != VAR_NUMBER)
{
ch_error(channel,
"Dropping message with invalid sequence number type");
free_tv(listtv);
return FALSE;
seq_nr = 0;
if (d != NULL)
{
di = dict_find(d, (char_u *)"id", -1);
if (di != NULL && di->di_tv.v_type == VAR_NUMBER)
seq_nr = di->di_tv.vval.v_number;
}
argv[1] = *listtv;
}
else
{
for (item = listtv->vval.v_list->lv_first;
item != NULL && argc < CH_JSON_MAX_ARGS;
item = item->li_next)
argv[argc++] = item->li_tv;
while (argc < CH_JSON_MAX_ARGS)
argv[argc++].v_type = VAR_UNKNOWN;
if (argv[0].v_type == VAR_STRING)
{
// ["cmd", arg] or ["cmd", arg, arg] or ["cmd", arg, arg, arg]
channel_exe_cmd(channel, part, argv);
free_tv(listtv);
return TRUE;
}
if (argv[0].v_type != VAR_NUMBER)
{
ch_error(channel,
"Dropping message with invalid sequence number type");
free_tv(listtv);
return FALSE;
}
seq_nr = argv[0].vval.v_number;
}
seq_nr = argv[0].vval.v_number;
}
else if (channel_peek(channel, part) == NULL)
{
@@ -2932,24 +3068,35 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
argv[1].vval.v_string = msg;
}
called_otc = FALSE;
if (seq_nr > 0)
{
int done = FALSE;
// JSON or JS mode: invoke the one-time callback with the matching nr
// JSON or JS or LSP mode: invoke the one-time callback with the
// matching nr
for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next)
if (cbitem->cq_seq_nr == seq_nr)
{
invoke_one_time_callback(channel, cbhead, cbitem, argv);
done = TRUE;
called_otc = TRUE;
break;
}
if (!done)
}
if (seq_nr > 0 && (ch_mode != MODE_LSP || called_otc))
{
if (!called_otc)
{
// If the 'drop' channel attribute is set to 'never' or if
// ch_evalexpr() is waiting for this response message, then don't
// drop this message.
if (channel->ch_drop_never)
{
// message must be read with ch_read()
channel_push_json(channel, part, listtv);
// Change the type to avoid the value being freed.
listtv->v_type = VAR_NUMBER;
free_tv(listtv);
listtv = NULL;
}
else
@@ -3032,7 +3179,7 @@ channel_has_readahead(channel_T *channel, ch_part_T part)
{
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
if (ch_mode == MODE_JSON || ch_mode == MODE_JS || ch_mode == MODE_LSP)
{
jsonq_T *head = &channel->ch_part[part].ch_json_head;
@@ -3118,6 +3265,7 @@ channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part)
case MODE_RAW: s = "RAW"; break;
case MODE_JSON: s = "JSON"; break;
case MODE_JS: s = "JS"; break;
case MODE_LSP: s = "LSP"; break;
}
dict_add_string(dict, namebuf, (char_u *)s);
@@ -4354,9 +4502,59 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
return;
}
id = ++channel->ch_last_msg_id;
text = json_encode_nr_expr(id, &argvars[1],
(ch_mode == MODE_JS ? JSON_JS : 0) | JSON_NL);
if (ch_mode == MODE_LSP)
{
dict_T *d;
dictitem_T *di;
int callback_present = FALSE;
if (argvars[1].v_type != VAR_DICT)
{
semsg(_(e_dict_required_for_argument_nr), 2);
return;
}
d = argvars[1].vval.v_dict;
di = dict_find(d, (char_u *)"id", -1);
if (di != NULL && di->di_tv.v_type != VAR_NUMBER)
{
// only number type is supported for the 'id' item
semsg(_(e_invalid_value_for_argument_str), "id");
return;
}
if (argvars[2].v_type == VAR_DICT)
if (dict_find(argvars[2].vval.v_dict, (char_u *)"callback", -1)
!= NULL)
callback_present = TRUE;
if (eval || callback_present)
{
// When evaluating an expression or sending an expression with a
// callback, always assign a generated ID
id = ++channel->ch_last_msg_id;
if (di == NULL)
dict_add_number(d, "id", id);
else
di->di_tv.vval.v_number = id;
}
else
{
// When sending an expression, if the message has an 'id' item,
// then use it.
id = 0;
if (di != NULL)
id = di->di_tv.vval.v_number;
}
if (dict_find(d, (char_u *)"jsonrpc", -1) == NULL)
dict_add_string(d, "jsonrpc", (char_u *)"2.0");
text = json_encode_lsp_msg(&argvars[1]);
}
else
{
id = ++channel->ch_last_msg_id;
text = json_encode_nr_expr(id, &argvars[1],
(ch_mode == MODE_JS ? JSON_JS : 0) | JSON_NL);
}
if (text == NULL)
return;
@@ -4372,13 +4570,23 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
if (channel_read_json_block(channel, part_read, timeout, id, &listtv)
== OK)
{
list_T *list = listtv->vval.v_list;
if (ch_mode == MODE_LSP)
{
*rettv = *listtv;
// Change the type to avoid the value being freed.
listtv->v_type = VAR_NUMBER;
free_tv(listtv);
}
else
{
list_T *list = listtv->vval.v_list;
// Move the item from the list and then change the type to
// avoid the value being freed.
*rettv = list->lv_u.mat.lv_last->li_tv;
list->lv_u.mat.lv_last->li_tv.v_type = VAR_NUMBER;
free_tv(listtv);
// Move the item from the list and then change the type to
// avoid the value being freed.
*rettv = list->lv_u.mat.lv_last->li_tv;
list->lv_u.mat.lv_last->li_tv.v_type = VAR_NUMBER;
free_tv(listtv);
}
}
}
free_job_options(&opt);
+4 -3
View File
@@ -2507,11 +2507,11 @@ win_update(win_T *wp)
}
else
{
if (wp->w_p_rnu)
if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)
{
#ifdef FEAT_FOLDING
// 'relativenumber' set: The text doesn't need to be drawn, but
// the number column nearly always does.
// 'relativenumber' set and the cursor moved vertically: The
// text doesn't need to be drawn, but the number column does.
fold_count = foldedCount(wp, lnum, &win_foldinfo);
if (fold_count != 0)
fold_line(wp, fold_count, &win_foldinfo, lnum, row);
@@ -2553,6 +2553,7 @@ win_update(win_T *wp)
// update w_last_cursorline.
wp->w_last_cursorline = wp->w_p_cul ? wp->w_cursor.lnum : 0;
#endif
wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
#ifdef FEAT_VTP
// Rewrite the character at the end of the screen line.
+3 -2
View File
@@ -30,8 +30,9 @@ EXTERN char e_invalid_expression_str[]
#endif
EXTERN char e_invalid_range[]
INIT(= N_("E16: Invalid range"));
#if defined(UNIX) || defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
EXTERN char e_src_is_directory[]
#if defined(UNIX) || defined(FEAT_SYN_HL) \
|| defined(FEAT_SPELL) || defined(FEAT_EVAL)
EXTERN char e_str_is_directory[]
INIT(= N_("E17: \"%s\" is a directory"));
#endif
#ifdef FEAT_EVAL
+22 -8
View File
@@ -5296,15 +5296,29 @@ echo_string_core(
break;
case VAR_FUNC:
if (echo_style)
{
*tofree = NULL;
r = tv->vval.v_string;
}
else
{
*tofree = string_quote(tv->vval.v_string, TRUE);
r = *tofree;
char_u buf[MAX_FUNC_NAME_LEN];
if (echo_style)
{
r = make_ufunc_name_readable(tv->vval.v_string,
buf, MAX_FUNC_NAME_LEN);
if (r == buf)
{
r = vim_strsave(buf);
*tofree = r;
}
else
*tofree = NULL;
}
else
{
*tofree = string_quote(tv->vval.v_string == NULL ? NULL
: make_ufunc_name_readable(
tv->vval.v_string, buf, MAX_FUNC_NAME_LEN),
TRUE);
r = *tofree;
}
}
break;
+9 -1
View File
@@ -2825,7 +2825,7 @@ eval_variable(
{
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
&& ((type != NULL && type != &t_dict_empty)
|| !in_vim9script()))
|| !in_vim9script()))
{
tv->vval.v_dict = dict_alloc();
if (tv->vval.v_dict != NULL)
@@ -2845,6 +2845,14 @@ eval_variable(
tv->vval.v_list->lv_type = alloc_type(type);
}
}
else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
&& ((type != NULL && type != &t_blob_null)
|| !in_vim9script()))
{
tv->vval.v_blob = blob_alloc();
if (tv->vval.v_blob != NULL)
++tv->vval.v_blob->bv_refcount;
}
}
copy_tv(tv, rettv);
}
+1 -1
View File
@@ -2116,7 +2116,7 @@ check_overwrite(
// with UNIX it is possible to open a directory
if (mch_isdir(ffname))
{
semsg(_(e_src_is_directory), ffname);
semsg(_(e_str_is_directory), ffname);
return FAIL;
}
#endif
+1 -1
View File
@@ -8402,7 +8402,7 @@ open_exfile(
// with Unix it is possible to open a directory
if (mch_isdir(fname))
{
semsg(_(e_src_is_directory), fname);
semsg(_(e_str_is_directory), fname);
return NULL;
}
#endif
+22 -20
View File
@@ -892,33 +892,35 @@ f_exepath(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = p;
}
/*
* Return TRUE if "fname" is a readable file.
*/
int
file_is_readable(char_u *fname)
{
int fd;
#ifndef O_NONBLOCK
# define O_NONBLOCK 0
#endif
if (*fname && !mch_isdir(fname)
&& (fd = mch_open((char *)fname, O_RDONLY | O_NONBLOCK, 0)) >= 0)
{
close(fd);
return TRUE;
}
return FALSE;
}
/*
* "filereadable()" function
*/
void
f_filereadable(typval_T *argvars, typval_T *rettv)
{
int fd;
char_u *p;
int n;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
#ifndef O_NONBLOCK
# define O_NONBLOCK 0
#endif
p = tv_get_string(&argvars[0]);
if (*p && !mch_isdir(p) && (fd = mch_open((char *)p,
O_RDONLY | O_NONBLOCK, 0)) >= 0)
{
n = TRUE;
close(fd);
}
else
n = FALSE;
rettv->vval.v_number = n;
rettv->vval.v_number = file_is_readable(tv_get_string(&argvars[0]));
}
/*
@@ -1761,7 +1763,7 @@ read_file_or_blob(typval_T *argvars, typval_T *rettv, int always_blob)
if (mch_isdir(fname))
{
semsg(_(e_src_is_directory), fname);
semsg(_(e_str_is_directory), fname);
return;
}
if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL)
+1
View File
@@ -405,6 +405,7 @@ EXTERN type_T t_number_bool INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_BOOL_OK
EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_blob_null INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, &t_void, NULL);
EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL);
+1 -1
View File
@@ -1191,7 +1191,7 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
#endif
menu->parent = parent;
menu->submenu_id = NULL;
menu->submenu_id = (Widget)0;
if (!XtIsManaged(toolBar)
&& vim_strchr(p_go, GO_TOOLBAR) != NULL)
gui_mch_show_toolbar(TRUE);
+12 -15
View File
@@ -944,13 +944,21 @@ gui_mch_add_menu(vimmenu_T *menu, int idx)
&& tearoff_val == (int)XmTEAR_OFF_ENABLED ? 1 : 0),
#endif
NULL);
gui_motif_menu_colors(menu->id);
gui_motif_menu_fontlist(menu->id);
XmStringFree(label);
if (menu->id == (Widget)0) // failed
return;
// The "Help" menu is a special case, and should be placed at the far
// right hand side of the menu-bar. It's recognized by its high priority.
if (parent == NULL && menu->priority >= 9999)
XtVaSetValues(menuBar,
XmNmenuHelpWidget, menu->id,
NULL);
gui_motif_menu_colors(menu->id);
gui_motif_menu_fontlist(menu->id);
// add accelerator text
gui_motif_add_actext(menu);
@@ -978,19 +986,8 @@ gui_mch_add_menu(vimmenu_T *menu, int idx)
XmNsubMenuId, menu->submenu_id,
NULL);
/*
* The "Help" menu is a special case, and should be placed at the far
* right hand side of the menu-bar. It's recognized by its high priority.
*/
if (parent == NULL && menu->priority >= 9999)
XtVaSetValues(menuBar,
XmNmenuHelpWidget, menu->id,
NULL);
/*
* When we add a top-level item to the menu bar, we can figure out how
* high the menu bar should be.
*/
// When we add a top-level item to the menu bar, we can figure out how
// high the menu bar should be.
if (parent == NULL)
gui_mch_compute_menu_height(menu->id);
}
+2
View File
@@ -31,6 +31,8 @@ handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo)
*modep = MODE_JS;
else if (STRCMP(val, "json") == 0)
*modep = MODE_JSON;
else if (STRCMP(val, "lsp") == 0)
*modep = MODE_LSP;
else
{
semsg(_(e_invalid_argument_str), val);
+26
View File
@@ -86,6 +86,32 @@ json_encode_nr_expr(int nr, typval_T *val, int options)
ga_append(&ga, NUL);
return ga.ga_data;
}
/*
* Encode "val" into a JSON format string prefixed by the LSP HTTP header.
* Returns NULL when out of memory.
*/
char_u *
json_encode_lsp_msg(typval_T *val)
{
garray_T ga;
garray_T lspga;
ga_init2(&ga, 1, 4000);
if (json_encode_gap(&ga, val, 0) == FAIL)
return NULL;
ga_append(&ga, NUL);
ga_init2(&lspga, 1, 4000);
vim_snprintf((char *)IObuff, IOSIZE,
"Content-Length: %u\r\n"
"Content-Type: application/vim-jsonrpc; charset=utf-8\r\n\r\n",
ga.ga_len - 1);
ga_concat(&lspga, IObuff);
ga_concat_len(&lspga, ga.ga_data, ga.ga_len);
ga_clear(&ga);
return lspga.ga_data;
}
#endif
static void
+9 -9
View File
@@ -1002,21 +1002,21 @@ mark_adjust(
void
mark_adjust_nofold(
linenr_T line1,
linenr_T line2,
long amount,
long amount_after)
linenr_T line1,
linenr_T line2,
long amount,
long amount_after)
{
mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
}
static void
mark_adjust_internal(
linenr_T line1,
linenr_T line2,
long amount,
long amount_after,
int adjust_folds UNUSED)
linenr_T line1,
linenr_T line2,
long amount,
long amount_after,
int adjust_folds UNUSED)
{
int i;
int fnum = curbuf->b_fnum;
+2 -2
View File
@@ -147,10 +147,10 @@ redraw_for_cursorcolumn(win_T *wp)
{
// When 'cursorcolumn' is set need to redraw with SOME_VALID.
if (wp->w_p_cuc)
redraw_later(SOME_VALID);
redraw_win_later(wp, SOME_VALID);
// When 'cursorlineopt' contains "screenline" need to redraw with VALID.
else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE))
redraw_later(VALID);
redraw_win_later(wp, VALID);
}
}
#endif
+3 -3
View File
@@ -570,7 +570,7 @@ EXTERN char_u *p_fenc; // 'fileencoding'
EXTERN char_u *p_fencs; // 'fileencodings'
EXTERN char_u *p_ff; // 'fileformat'
EXTERN char_u *p_ffs; // 'fileformats'
EXTERN long p_fic; // 'fileignorecase'
EXTERN int p_fic; // 'fileignorecase'
EXTERN char_u *p_ft; // 'filetype'
EXTERN char_u *p_fcs; // 'fillchar'
EXTERN int p_fixeol; // 'fixendofline'
@@ -767,7 +767,7 @@ EXTERN long p_mis; // 'menuitems'
EXTERN char_u *p_msm; // 'mkspellmem'
#endif
EXTERN int p_ml; // 'modeline'
EXTERN long p_mle; // 'modelineexpr'
EXTERN int p_mle; // 'modelineexpr'
EXTERN long p_mls; // 'modelines'
EXTERN int p_ma; // 'modifiable'
#ifdef FEAT_GUI_MACVIM
@@ -1104,7 +1104,7 @@ EXTERN int p_wiv; // 'weirdinvert'
EXTERN char_u *p_ww; // 'whichwrap'
EXTERN long p_wc; // 'wildchar'
EXTERN long p_wcm; // 'wildcharm'
EXTERN long p_wic; // 'wildignorecase'
EXTERN int p_wic; // 'wildignorecase'
EXTERN char_u *p_wim; // 'wildmode'
#ifdef FEAT_WILDMENU
EXTERN int p_wmnu; // 'wildmenu'
+1 -1
View File
@@ -3768,7 +3768,7 @@ get_tty_info(int fd, ttyinfo_T *info)
static int mouse_ison = FALSE;
/*
* Set mouse clicks on or off.
* Set mouse clicks on or off and possible enable mouse movement events.
*/
void
mch_setmouse(int on)
+39 -26
View File
@@ -100,6 +100,9 @@ pum_display(
#if defined(FEAT_QUICKFIX)
win_T *pvwin;
#endif
#ifdef FEAT_RIGHTLEFT
int right_left = State == CMDLINE ? FALSE : curwin->w_p_rl;
#endif
do
{
@@ -156,11 +159,17 @@ pum_display(
{
// pum above "pum_win_row"
// Leave two lines of context if possible
if (curwin->w_wrow - curwin->w_cline_row >= 2)
context_lines = 2;
if (State == CMDLINE)
// for cmdline pum, no need for context lines
context_lines = 0;
else
context_lines = curwin->w_wrow - curwin->w_cline_row;
{
// Leave two lines of context if possible
if (curwin->w_wrow - curwin->w_cline_row >= 2)
context_lines = 2;
else
context_lines = curwin->w_wrow - curwin->w_cline_row;
}
if (pum_win_row >= size + context_lines)
{
@@ -182,14 +191,20 @@ pum_display(
{
// pum below "pum_win_row"
// Leave two lines of context if possible
validate_cheight();
if (curwin->w_cline_row
+ curwin->w_cline_height - curwin->w_wrow >= 3)
context_lines = 3;
if (State == CMDLINE)
// for cmdline pum, no need for context lines
context_lines = 0;
else
context_lines = curwin->w_cline_row
+ curwin->w_cline_height - curwin->w_wrow;
{
// Leave two lines of context if possible
validate_cheight();
if (curwin->w_cline_row
+ curwin->w_cline_height - curwin->w_wrow >= 3)
context_lines = 3;
else
context_lines = curwin->w_cline_row
+ curwin->w_cline_height - curwin->w_wrow;
}
pum_row = pum_win_row + context_lines;
if (size > below_row - pum_row)
@@ -226,7 +241,7 @@ pum_display(
else
#endif
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl)
if (right_left)
cursor_col = curwin->w_wincol + curwin->w_width
- curwin->w_wcol - 1;
else
@@ -245,12 +260,10 @@ pum_display(
if (def_width < max_width)
def_width = max_width;
if (((cursor_col < Columns - p_pw
|| cursor_col < Columns - max_width)
if (((cursor_col < Columns - p_pw || cursor_col < Columns - max_width)
#ifdef FEAT_RIGHTLEFT
&& !curwin->w_p_rl)
|| (curwin->w_p_rl
&& (cursor_col > p_pw || cursor_col > max_width)
&& !right_left)
|| (right_left && (cursor_col > p_pw || cursor_col > max_width)
#endif
))
{
@@ -259,7 +272,7 @@ pum_display(
// start with the maximum space available
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl)
if (right_left)
pum_width = pum_col - pum_scrollbar + 1;
else
#endif
@@ -276,22 +289,22 @@ pum_display(
}
else if (((cursor_col > p_pw || cursor_col > max_width)
#ifdef FEAT_RIGHTLEFT
&& !curwin->w_p_rl)
|| (curwin->w_p_rl && (cursor_col < Columns - p_pw
&& !right_left)
|| (right_left && (cursor_col < Columns - p_pw
|| cursor_col < Columns - max_width)
#endif
))
{
// align pum edge with "cursor_col"
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl
if (right_left
&& W_ENDCOL(curwin) < max_width + pum_scrollbar + 1)
{
pum_col = cursor_col + max_width + pum_scrollbar + 1;
if (pum_col >= Columns)
pum_col = Columns - 1;
}
else if (!curwin->w_p_rl)
else if (!right_left)
#endif
{
if (curwin->w_wincol > Columns - max_width - pum_scrollbar
@@ -305,7 +318,7 @@ pum_display(
}
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl)
if (right_left)
pum_width = pum_col - pum_scrollbar + 1;
else
#endif
@@ -315,7 +328,7 @@ pum_display(
{
pum_width = p_pw;
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl)
if (right_left)
{
if (pum_width > pum_col)
pum_width = pum_col;
@@ -343,7 +356,7 @@ pum_display(
{
// not enough room, will use what we have
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl)
if (right_left)
pum_col = Columns - 1;
else
#endif
@@ -355,7 +368,7 @@ pum_display(
if (max_width > p_pw)
max_width = p_pw; // truncate
#ifdef FEAT_RIGHTLEFT
if (curwin->w_p_rl)
if (right_left)
pum_col = max_width - 1;
else
#endif
+1
View File
@@ -5,6 +5,7 @@ void f_chdir(typval_T *argvars, typval_T *rettv);
void f_delete(typval_T *argvars, typval_T *rettv);
void f_executable(typval_T *argvars, typval_T *rettv);
void f_exepath(typval_T *argvars, typval_T *rettv);
int file_is_readable(char_u *fname);
void f_filereadable(typval_T *argvars, typval_T *rettv);
void f_filewritable(typval_T *argvars, typval_T *rettv);
void f_finddir(typval_T *argvars, typval_T *rettv);
+1
View File
@@ -1,6 +1,7 @@
/* json.c */
char_u *json_encode(typval_T *val, int options);
char_u *json_encode_nr_expr(int nr, typval_T *val, int options);
char_u *json_encode_lsp_msg(typval_T *val);
int json_decode(js_read_T *reader, typval_T *res, int options);
int json_find_end(js_read_T *reader, int options);
void f_js_decode(typval_T *argvars, typval_T *rettv);
+2
View File
@@ -6,6 +6,8 @@ int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
estack_T *estack_pop(void);
char_u *estack_sfile(estack_arg_T which);
void ex_runtime(exarg_T *eap);
int find_script_by_name(char_u *name);
int get_new_scriptitem_for_fname(int *error, char_u *fname);
int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
int source_runtime(char_u *name, int flags);
+2
View File
@@ -1,6 +1,7 @@
/* userfunc.c */
void func_init(void);
hashtab_T *func_tbl_get(void);
char_u *make_ufunc_name_readable(char_u *name, char_u *buf, size_t bufsize);
char_u *get_lambda_name(void);
char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
@@ -8,6 +9,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **
void emsg_funcname(char *ermsg, char_u *name);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
void func_name_with_sid(char_u *name, int sid, char_u *buffer);
ufunc_T *find_func_even_dead(char_u *name, int flags);
ufunc_T *find_func(char_u *name, int is_global);
int func_is_global(ufunc_T *ufunc);
+1
View File
@@ -54,6 +54,7 @@ int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int a
int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len);
int generate_ECHO(cctx_T *cctx, int with_white, int count);
int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count);
int generate_SOURCE(cctx_T *cctx, int sid);
int generate_PUT(cctx_T *cctx, int regname, linenr_T lnum);
int generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line);
int generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str);
+1
View File
@@ -3194,6 +3194,7 @@ qf_jump_edit_buffer(
if (qfl_type == QFLT_LOCATION)
{
win_T *wp = win_id2wp(prev_winid);
if (wp == NULL && curwin->w_llist != qi)
{
emsg(_(e_current_window_was_closed));
+9
View File
@@ -3360,8 +3360,17 @@ regmatch(
int mark = OPERAND(scan)[0];
int cmp = OPERAND(scan)[1];
pos_T *pos;
size_t col = REG_MULTI ? rex.input - rex.line : 0;
pos = getmark_buf(rex.reg_buf, mark, FALSE);
// Line may have been freed, get it again.
if (REG_MULTI)
{
rex.line = reg_getline(rex.lnum);
rex.input = rex.line + col;
}
if (pos == NULL // mark doesn't exist
|| pos->lnum <= 0) // mark isn't set in reg_buf
{
+4 -2
View File
@@ -6764,8 +6764,10 @@ nfa_regmatch(
case NFA_MARK_GT:
case NFA_MARK_LT:
{
size_t col = rex.input - rex.line;
pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, FALSE);
pos_T *pos;
size_t col = REG_MULTI ? rex.input - rex.line : 0;
pos = getmark_buf(rex.reg_buf, t->state->val, FALSE);
// Line may have been freed, get it again.
if (REG_MULTI)
+2 -2
View File
@@ -2399,8 +2399,8 @@ ex_display(exarg_T *eap)
msg_puts_attr("^J", attr);
n -= 2;
}
for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0;
++p)
for (p = yb->y_array[j];
*p != NUL && (n -= ptr2cells(p)) >= 0; ++p)
{
clen = (*mb_ptr2len)(p);
msg_outtrans_len(p, clen);
+20 -11
View File
@@ -251,7 +251,7 @@ source_callback(char_u *fname, void *cookie)
* Find an already loaded script "name".
* If found returns its script ID. If not found returns -1.
*/
static int
int
find_script_by_name(char_u *name)
{
int sid;
@@ -320,6 +320,21 @@ get_new_scriptitem(int *error)
return sid;
}
int
get_new_scriptitem_for_fname(int *error, char_u *fname)
{
int sid = get_new_scriptitem(error);
if (*error == OK)
{
scriptitem_T *si = SCRIPT_ITEM(sid);
si->sn_name = vim_strsave(fname);
si->sn_state = SN_STATE_NOT_LOADED;
}
return sid;
}
static void
find_script_callback(char_u *fname, void *cookie)
{
@@ -329,17 +344,8 @@ find_script_callback(char_u *fname, void *cookie)
sid = find_script_by_name(fname);
if (sid < 0)
{
// script does not exist yet, create a new scriptitem
sid = get_new_scriptitem(&error);
if (error == OK)
{
scriptitem_T *si = SCRIPT_ITEM(sid);
si->sn_name = vim_strsave(fname);
si->sn_state = SN_STATE_NOT_LOADED;
}
}
sid = get_new_scriptitem_for_fname(&error, fname);
*ret_sid = sid;
}
#endif
@@ -1918,7 +1924,10 @@ get_one_sourceline(source_cookie_T *sp)
break; // all the lines are processed
ga_concat(&ga, ((char_u **)sp->buflines.ga_data)[sp->buf_lnum]);
sp->buf_lnum++;
if (ga_grow(&ga, 1) == FAIL)
break;
buf = (char_u *)ga.ga_data;
buf[ga.ga_len++] = NUL;
}
else
{
+21 -3
View File
@@ -686,6 +686,11 @@ makeopens(
if (put_line(fd, "endif") == FAIL)
goto fail;
// save 'shortmess' if not storing options
if ((ssop_flags & SSOP_OPTIONS) == 0
&& put_line(fd, "let s:shortmess_save = &shortmess") == FAIL)
goto fail;
// Now save the current files, current buffer first.
if (put_line(fd, "set shortmess=aoO") == FAIL)
goto fail;
@@ -964,10 +969,23 @@ makeopens(
if (put_line(fd, "unlet! s:wipebuf") == FAIL)
goto fail;
// Re-apply 'winheight', 'winwidth' and 'shortmess'.
if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s",
p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL)
// Re-apply 'winheight' and 'winwidth'.
if (fprintf(fd, "set winheight=%ld winwidth=%ld",
p_wh, p_wiw) < 0 || put_eol(fd) == FAIL)
goto fail;
// Restore 'shortmess'.
if (ssop_flags & SSOP_OPTIONS)
{
if (fprintf(fd, "set shortmess=%s", p_shm) < 0 || put_eol(fd) == FAIL)
goto fail;
}
else
{
if (put_line(fd, "let &shortmess = s:shortmess_save") == FAIL)
goto fail;
}
if (tab_firstwin->w_next != NULL)
{
// Restore 'winminheight' and 'winminwidth'.
+1 -1
View File
@@ -5976,7 +5976,7 @@ mkspell(
}
if (mch_isdir(wfname))
{
semsg(_(e_src_is_directory), wfname);
semsg(_(e_str_is_directory), wfname);
goto theend;
}
+10 -2
View File
@@ -1833,7 +1833,7 @@ typedef struct {
*/
typedef struct
{
char_u *sn_name;
char_u *sn_name; // full path of script file
int sn_script_seq; // latest sctx_T sc_seq value
// "sn_vars" stores the s: variables currently valid. When leaving a block
@@ -1864,9 +1864,13 @@ typedef struct
char_u *sn_save_cpo; // 'cpo' value when :vim9script found
char sn_is_vimrc; // .vimrc file, do not restore 'cpo'
// for "vim9script autoload" this is "dir#scriptname#"
// for a Vim9 script under "rtp/autoload/" this is "dir#scriptname#"
char_u *sn_autoload_prefix;
// TRUE for a script used with "import autoload './dirname/script.vim'"
// For "../autoload/script.vim" sn_autoload_prefix is also set.
int sn_import_autoload;
# ifdef FEAT_PROFILE
int sn_prof_on; // TRUE when script is/was profiled
int sn_pr_force; // forceit: profile functions in this script
@@ -2193,6 +2197,7 @@ typedef enum
MODE_RAW,
MODE_JSON,
MODE_JS,
MODE_LSP // Language Server Protocol (http + json)
} ch_mode_T;
typedef enum {
@@ -3477,6 +3482,9 @@ struct window_S
colnr_T w_old_visual_col; // last known start of visual part
colnr_T w_old_curswant; // last known value of Curswant
linenr_T w_last_cursor_lnum_rnu; // cursor lnum when 'rnu' was last
// redrawn
lcs_chars_T w_lcs_chars; // 'listchars' characters
/*
@@ -0,0 +1,10 @@
| +0&#ffffff0@74
@75
@75
@5| +0#0000001#e0e0e08|d|e|f|i|n|e| @8| +0#0000000#ffffff0@53
|<+2#ffffff16#00e0003|o|r|t| | +0#0000001#ffd7ff255|j|u|m|p| @10|w+2#ffffff16#00e0003|r|i|t|e|(|s|y|s|.|s|t|d|i|n|.|r|e|a|d|(|)@1|"| |[|r|u|n@1|i|n|g|]| @1|0|,|0|-|1| @9|A|l@1
| +0#0000000#ffffff0@4| +0#0000001#ffd7ff255|l|i|s|t| @10| +0#0000000#ffffff0@53
|~+0#4040ff13&| @3| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53
|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53
|[+1#0000000&|N|o| |N| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +1#0000000#ffffff0@35|0|,|0|-|1| @9|A|l@1
|:+0&&|s|i|g|n| |d|e|f|i|n|e> @62
+209
View File
@@ -2378,5 +2378,214 @@ func Test_job_start_with_invalid_argument()
call assert_fails('call job_start([0zff])', 'E976:')
endfunc
" Test for the 'lsp' channel mode
func LspCb(chan, msg)
call add(g:lspNotif, a:msg)
endfunc
func LspOtCb(chan, msg)
call add(g:lspOtMsgs, a:msg)
endfunc
func LspTests(port)
" call ch_logfile('Xlsprpc.log', 'w')
let ch = ch_open(s:localhost .. a:port, #{mode: 'lsp', callback: 'LspCb'})
if ch_status(ch) == "fail"
call assert_report("Can't open the lsp channel")
return
endif
" check for channel information
let info = ch_info(ch)
call assert_equal('LSP', info.sock_mode)
" Evaluate an expression
let resp = ch_evalexpr(ch, #{method: 'simple-rpc', params: [10, 20]})
call assert_false(empty(resp))
call assert_equal(#{id: 1, jsonrpc: '2.0', result: 'simple-rpc'}, resp)
" Evaluate an expression. While waiting for the response, a notification
" message is delivered.
let g:lspNotif = []
let resp = ch_evalexpr(ch, #{method: 'rpc-with-notif', params: {'v': 10}})
call assert_false(empty(resp))
call assert_equal(#{id: 2, jsonrpc: '2.0', result: 'rpc-with-notif-resp'},
\ resp)
call assert_equal([#{jsonrpc: '2.0', result: 'rpc-with-notif-notif'}],
\ g:lspNotif)
" Wrong payload notification test
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'wrong-payload', params: {}})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', result: 'wrong-payload'}], g:lspNotif)
" Test for receiving a response with incorrect 'id' and additional
" notification messages while evaluating an expression.
let g:lspNotif = []
let resp = ch_evalexpr(ch, #{method: 'rpc-resp-incorrect-id',
\ params: {'a': [1, 2]}})
call assert_false(empty(resp))
call assert_equal(#{id: 4, jsonrpc: '2.0',
\ result: 'rpc-resp-incorrect-id-4'}, resp)
call assert_equal([#{jsonrpc: '2.0', result: 'rpc-resp-incorrect-id-1'},
\ #{jsonrpc: '2.0', result: 'rpc-resp-incorrect-id-2'},
\ #{jsonrpc: '2.0', id: 1, result: 'rpc-resp-incorrect-id-3'}],
\ g:lspNotif)
" simple notification test
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'simple-notif', params: [#{a: 10, b: []}]})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', result: 'simple-notif'}], g:lspNotif)
" multiple notifications test
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'multi-notif', params: [#{a: {}, b: {}}]})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', result: 'multi-notif1'},
\ #{jsonrpc: '2.0', result: 'multi-notif2'}], g:lspNotif)
" Test for sending a message with an identifier.
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'msg-with-id', id: 93, params: #{s: 'str'}})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', id: 93, result: 'msg-with-id'}],
\ g:lspNotif)
" Test for setting the 'id' value in a request message
let resp = ch_evalexpr(ch, #{method: 'ping', id: 1, params: {}})
call assert_equal(#{id: 8, jsonrpc: '2.0', result: 'alive'}, resp)
" Test for using a one time callback function to process a response
let g:lspOtMsgs = []
call ch_sendexpr(ch, #{method: 'msg-specifc-cb', params: {}},
\ #{callback: 'LspOtCb'})
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{id: 9, jsonrpc: '2.0', result: 'msg-specifc-cb'}],
\ g:lspOtMsgs)
" Test for generating a request message from the other end (server)
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'server-req', params: #{}})
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([{'id': 201, 'jsonrpc': '2.0',
\ 'result': {'method': 'checkhealth', 'params': {'a': 20}}}],
\ g:lspNotif)
" Test for sending a message without an id
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'echo', params: #{s: 'msg-without-id'}})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', result:
\ #{method: 'echo', jsonrpc: '2.0', params: #{s: 'msg-without-id'}}}],
\ g:lspNotif)
" Test for sending a notification message with an id
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'echo', id: 110, params: #{s: 'msg-with-id'}})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', result:
\ #{method: 'echo', jsonrpc: '2.0', id: 110,
\ params: #{s: 'msg-with-id'}}}], g:lspNotif)
" Test for processing the extra fields in the HTTP header
let resp = ch_evalexpr(ch, #{method: 'extra-hdr-fields', params: {}})
call assert_equal({'id': 14, 'jsonrpc': '2.0', 'result': 'extra-hdr-fields'},
\ resp)
" Test for processing a HTTP header without the Content-Length field
let resp = ch_evalexpr(ch, #{method: 'hdr-without-len', params: {}},
\ #{timeout: 200})
call assert_equal('', resp)
" send a ping to make sure communication still works
let resp = ch_evalexpr(ch, #{method: 'ping'})
call assert_equal({'id': 16, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
" Test for processing a HTTP header with wrong length
let resp = ch_evalexpr(ch, #{method: 'hdr-with-wrong-len', params: {}},
\ #{timeout: 200})
call assert_equal('', resp)
" send a ping to make sure communication still works
let resp = ch_evalexpr(ch, #{method: 'ping'})
call assert_equal({'id': 18, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
" Test for processing a HTTP header with negative length
let resp = ch_evalexpr(ch, #{method: 'hdr-with-negative-len', params: {}},
\ #{timeout: 200})
call assert_equal('', resp)
" send a ping to make sure communication still works
let resp = ch_evalexpr(ch, #{method: 'ping'})
call assert_equal({'id': 20, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
" Test for an empty header
let resp = ch_evalexpr(ch, #{method: 'empty-header', params: {}},
\ #{timeout: 200})
call assert_equal('', resp)
" send a ping to make sure communication still works
let resp = ch_evalexpr(ch, #{method: 'ping'})
call assert_equal({'id': 22, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
" Test for an empty payload
let resp = ch_evalexpr(ch, #{method: 'empty-payload', params: {}},
\ #{timeout: 200})
call assert_equal('', resp)
" send a ping to make sure communication still works
let resp = ch_evalexpr(ch, #{method: 'ping'})
call assert_equal({'id': 24, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
" Test for invoking an unsupported method
let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200})
call assert_equal('', resp)
" Test for sending a message without a callback function. Notification
" message should be dropped but RPC response should not be dropped.
call ch_setoptions(ch, #{callback: ''})
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'echo', params: #{s: 'no-callback'}})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([], g:lspNotif)
" Restore the callback function
call ch_setoptions(ch, #{callback: 'LspCb'})
let g:lspNotif = []
call ch_sendexpr(ch, #{method: 'echo', params: #{s: 'no-callback'}})
" Send a ping to wait for all the notification messages to arrive
call ch_evalexpr(ch, #{method: 'ping'})
call assert_equal([#{jsonrpc: '2.0', result:
\ #{method: 'echo', jsonrpc: '2.0', params: #{s: 'no-callback'}}}],
\ g:lspNotif)
" " Test for sending a raw message
" let g:lspNotif = []
" let s = "Content-Length: 62\r\n"
" let s ..= "Content-Type: application/vim-jsonrpc; charset=utf-8\r\n"
" let s ..= "\r\n"
" let s ..= '{"method":"echo","jsonrpc":"2.0","params":{"m":"raw-message"}}'
" call ch_sendraw(ch, s)
" call ch_evalexpr(ch, #{method: 'ping'})
" call assert_equal([{'jsonrpc': '2.0',
" \ 'result': {'method': 'echo', 'jsonrpc': '2.0',
" \ 'params': {'m': 'raw-message'}}}], g:lspNotif)
" Invalid arguments to ch_evalexpr() and ch_sendexpr()
call assert_fails('call ch_sendexpr(ch, #{method: "cookie", id: "cookie"})',
\ 'E475:')
call assert_fails('call ch_evalexpr(ch, #{method: "ping", id: [{}]})', 'E475:')
call assert_fails('call ch_evalexpr(ch, [1, 2, 3])', 'E1206:')
call assert_fails('call ch_sendexpr(ch, "abc")', 'E1206:')
call assert_fails('call ch_evalexpr(ch, #{method: "ping"}, #{callback: "LspOtCb"})', 'E917:')
" call ch_logfile('', 'w')
endfunc
func Test_channel_lsp_mode()
call RunServer('test_channel_lsp.py', 'LspTests', [])
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+299
View File
@@ -0,0 +1,299 @@
#!/usr/bin/env python
#
# Server that will accept connections from a Vim channel.
# Used by test_channel.vim to test LSP functionality.
#
# This requires Python 2.6 or later.
from __future__ import print_function
import json
import socket
import sys
import time
import threading
try:
# Python 3
import socketserver
except ImportError:
# Python 2
import SocketServer as socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def setup(self):
self.request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
def send_lsp_msg(self, msgid, resp_dict):
v = {'jsonrpc': '2.0', 'result': resp_dict}
if msgid != -1:
v['id'] = msgid
s = json.dumps(v)
resp = "Content-Length: " + str(len(s)) + "\r\n"
resp += "Content-Type: application/vim-jsonrpc; charset=utf-8\r\n"
resp += "\r\n"
resp += s
if self.debug:
with open("Xlspdebug.log", "a") as myfile:
myfile.write("\n=> send\n" + resp)
self.request.sendall(resp.encode('utf-8'))
def send_wrong_payload(self):
v = 'wrong-payload'
s = json.dumps(v)
resp = "Content-Length: " + str(len(s)) + "\r\n"
resp += "Content-Type: application/vim-jsonrpc; charset=utf-8\r\n"
resp += "\r\n"
resp += s
self.request.sendall(resp.encode('utf-8'))
def send_empty_header(self, msgid, resp_dict):
v = {'jsonrpc': '2.0', 'id': msgid, 'result': resp_dict}
s = json.dumps(v)
resp = "\r\n"
resp += s
self.request.sendall(resp.encode('utf-8'))
def send_empty_payload(self):
resp = "Content-Length: 0\r\n"
resp += "Content-Type: application/vim-jsonrpc; charset=utf-8\r\n"
resp += "\r\n"
self.request.sendall(resp.encode('utf-8'))
def send_extra_hdr_fields(self, msgid, resp_dict):
# test for sending extra fields in the http header
v = {'jsonrpc': '2.0', 'id': msgid, 'result': resp_dict}
s = json.dumps(v)
resp = "Host: abc.vim.org\r\n"
resp += "User-Agent: Python\r\n"
resp += "Accept-Language: en-US,en\r\n"
resp += "Content-Type: application/vim-jsonrpc; charset=utf-8\r\n"
resp += "Content-Length: " + str(len(s)) + "\r\n"
resp += "\r\n"
resp += s
self.request.sendall(resp.encode('utf-8'))
def send_hdr_without_len(self, msgid, resp_dict):
# test for sending the http header without length
v = {'jsonrpc': '2.0', 'id': msgid, 'result': resp_dict}
s = json.dumps(v)
resp = "Content-Type: application/vim-jsonrpc; charset=utf-8\r\n"
resp += "\r\n"
resp += s
self.request.sendall(resp.encode('utf-8'))
def send_hdr_with_wrong_len(self, msgid, resp_dict):
# test for sending the http header with wrong length
v = {'jsonrpc': '2.0', 'id': msgid, 'result': resp_dict}
s = json.dumps(v)
resp = "Content-Length: 1000\r\n"
resp += "\r\n"
resp += s
self.request.sendall(resp.encode('utf-8'))
def send_hdr_with_negative_len(self, msgid, resp_dict):
# test for sending the http header with negative length
v = {'jsonrpc': '2.0', 'id': msgid, 'result': resp_dict}
s = json.dumps(v)
resp = "Content-Length: -1\r\n"
resp += "\r\n"
resp += s
self.request.sendall(resp.encode('utf-8'))
def do_ping(self, payload):
time.sleep(0.2)
self.send_lsp_msg(payload['id'], 'alive')
def do_echo(self, payload):
self.send_lsp_msg(-1, payload)
def do_simple_rpc(self, payload):
# test for a simple RPC request
self.send_lsp_msg(payload['id'], 'simple-rpc')
def do_rpc_with_notif(self, payload):
# test for sending a notification before replying to a request message
self.send_lsp_msg(-1, 'rpc-with-notif-notif')
# sleep for some time to make sure the notification is delivered
time.sleep(0.2)
self.send_lsp_msg(payload['id'], 'rpc-with-notif-resp')
def do_wrong_payload(self, payload):
# test for sending a non dict payload
self.send_wrong_payload()
time.sleep(0.2)
self.send_lsp_msg(-1, 'wrong-payload')
def do_rpc_resp_incorrect_id(self, payload):
self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-1')
self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-2')
self.send_lsp_msg(1, 'rpc-resp-incorrect-id-3')
time.sleep(0.2)
self.send_lsp_msg(payload['id'], 'rpc-resp-incorrect-id-4')
def do_simple_notif(self, payload):
# notification message test
self.send_lsp_msg(-1, 'simple-notif')
def do_multi_notif(self, payload):
# send multiple notifications
self.send_lsp_msg(-1, 'multi-notif1')
self.send_lsp_msg(-1, 'multi-notif2')
def do_msg_with_id(self, payload):
self.send_lsp_msg(payload['id'], 'msg-with-id')
def do_msg_specific_cb(self, payload):
self.send_lsp_msg(payload['id'], 'msg-specifc-cb')
def do_server_req(self, payload):
self.send_lsp_msg(201, {'method': 'checkhealth', 'params': {'a': 20}})
def do_extra_hdr_fields(self, payload):
self.send_extra_hdr_fields(payload['id'], 'extra-hdr-fields')
def do_hdr_without_len(self, payload):
self.send_hdr_without_len(payload['id'], 'hdr-without-len')
def do_hdr_with_wrong_len(self, payload):
self.send_hdr_with_wrong_len(payload['id'], 'hdr-with-wrong-len')
def do_hdr_with_negative_len(self, payload):
self.send_hdr_with_negative_len(payload['id'], 'hdr-with-negative-len')
def do_empty_header(self, payload):
self.send_empty_header(payload['id'], 'empty-header')
def do_empty_payload(self, payload):
self.send_empty_payload()
def process_msg(self, msg):
try:
decoded = json.loads(msg)
print("Decoded:")
print(str(decoded))
if 'method' in decoded:
test_map = {
'ping': self.do_ping,
'echo': self.do_echo,
'simple-rpc': self.do_simple_rpc,
'rpc-with-notif': self.do_rpc_with_notif,
'wrong-payload': self.do_wrong_payload,
'rpc-resp-incorrect-id': self.do_rpc_resp_incorrect_id,
'simple-notif': self.do_simple_notif,
'multi-notif': self.do_multi_notif,
'msg-with-id': self.do_msg_with_id,
'msg-specifc-cb': self.do_msg_specific_cb,
'server-req': self.do_server_req,
'extra-hdr-fields': self.do_extra_hdr_fields,
'hdr-without-len': self.do_hdr_without_len,
'hdr-with-wrong-len': self.do_hdr_with_wrong_len,
'hdr-with-negative-len': self.do_hdr_with_negative_len,
'empty-header': self.do_empty_header,
'empty-payload': self.do_empty_payload
}
if decoded['method'] in test_map:
test_map[decoded['method']](decoded)
else:
print("Error: Unsupported method: " + decoded['method'])
else:
print("Error: 'method' field is not found")
except ValueError:
print("json decoding failed")
def process_msgs(self, msgbuf):
while True:
sidx = msgbuf.find('Content-Length: ')
if sidx == -1:
return msgbuf
sidx += 16
eidx = msgbuf.find('\r\n')
if eidx == -1:
return msgbuf
msglen = int(msgbuf[sidx:eidx])
hdrend = msgbuf.find('\r\n\r\n')
if hdrend == -1:
return msgbuf
# Remove the header
msgbuf = msgbuf[hdrend + 4:]
payload = msgbuf[:msglen]
self.process_msg(payload)
# Remove the processed message
msgbuf = msgbuf[msglen:]
def handle(self):
print("=== socket opened ===")
self.debug = False
msgbuf = ''
while True:
try:
received = self.request.recv(4096).decode('utf-8')
except socket.error:
print("=== socket error ===")
break
except IOError:
print("=== socket closed ===")
break
if received == '':
print("=== socket closed ===")
break
print("\nReceived:\n{0}".format(received))
# Write the received lines into the file for debugging
if self.debug:
with open("Xlspdebug.log", "a") as myfile:
myfile.write("\n<= recv\n" + received)
# Can receive more than one line in a response or a partial line.
# Accumulate all the received characters and process one line at
# a time.
msgbuf += received
msgbuf = self.process_msgs(msgbuf)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def writePortInFile(port):
# Write the port number in Xportnr, so that the test knows it.
f = open("Xportnr", "w")
f.write("{0}".format(port))
f.close()
def main(host, port, server_class=ThreadedTCPServer):
# Wait half a second before opening the port to test waittime in ch_open().
# We do want to get the port number, get that first. We cannot open the
# socket, guess a port is free.
if len(sys.argv) >= 2 and sys.argv[1] == 'delay':
port = 13684
writePortInFile(port)
print("Wait for it...")
time.sleep(0.5)
server = server_class((host, port), ThreadedTCPRequestHandler)
ip, port = server.server_address[0:2]
# Start a thread with the server. That thread will then start a new thread
# for each connection.
server_thread = threading.Thread(target=server.serve_forever)
server_thread.start()
writePortInFile(port)
print("Listening on port {0}".format(port))
# Main thread terminates, but the server continues running
# until server.shutdown() is called.
try:
while server_thread.is_alive():
server_thread.join(1)
except (KeyboardInterrupt, SystemExit):
server.shutdown()
if __name__ == "__main__":
main("localhost", 0)
+24
View File
@@ -2510,6 +2510,30 @@ func Test_wildmenumode_with_pum()
cunmap <F2>
endfunc
" Test for opening the cmdline completion popup menu from the terminal window.
" The popup menu should be positioned correctly over the status line of the
" bottom-most window.
func Test_wildmenu_pum_from_terminal()
CheckRunVimInTerminal
let python = PythonProg()
call CheckPython(python)
%bw!
let cmds = ['set wildmenu wildoptions=pum']
let pcmd = python .. ' -c "import sys; sys.stdout.write(sys.stdin.read())"'
call add(cmds, "call term_start('" .. pcmd .. "')")
call writefile(cmds, 'Xtest')
let buf = RunVimInTerminal('-S Xtest', #{rows: 10})
call term_sendkeys(buf, "\r\r\r")
call term_wait(buf)
call term_sendkeys(buf, "\<C-W>:sign \<Tab>")
call term_wait(buf)
call VerifyScreenDump(buf, 'Test_wildmenu_pum_term_01', {})
call term_wait(buf)
call StopVimInTerminal(buf)
call delete('Xtest')
endfunc
" Test for completion after a :substitute command followed by a pipe (|)
" character
func Test_cmdline_complete_substitute()
+1 -2
View File
@@ -139,8 +139,7 @@ endfunc
func Test_FileChangedShell_edit_dialog()
CheckNotGui
" FIXME: why does this not work on MS-Windows?
CheckUnix
CheckUnix " Using low level feedkeys() does not work on MS-Windows.
new Xchanged_r
call setline(1, 'reload this')
+1
View File
@@ -382,6 +382,7 @@ let s:filename_checks = {
\ 'opam': ['opam', 'file.opam', 'file.opam.template'],
\ 'openroad': ['file.or'],
\ 'ora': ['file.ora'],
\ 'org': ['file.org', 'file.org_archive'],
\ 'pamconf': ['/etc/pam.conf', '/etc/pam.d/file', 'any/etc/pam.conf', 'any/etc/pam.d/file'],
\ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment', '.pam_environment', 'pam_env.conf'],
\ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'],
+43
View File
@@ -1007,6 +1007,49 @@ func Test_mksession_winminheight()
set sessionoptions&
endfunc
" Test for mksession with and without options restores shortmess
func Test_mksession_shortmess()
" Without options
set sessionoptions-=options
split
mksession! Xtest_mks.out
let found_save = 0
let found_restore = 0
let lines = readfile('Xtest_mks.out')
for line in lines
let line = trim(line)
if line ==# 'let s:shortmess_save = &shortmess'
let found_save += 1
endif
if found_save !=# 0 && line ==# 'let &shortmess = s:shortmess_save'
let found_restore += 1
endif
endfor
call assert_equal(1, found_save)
call assert_equal(1, found_restore)
call delete('Xtest_mks.out')
close
set sessionoptions&
" With options
set sessionoptions+=options
split
mksession! Xtest_mks.out
let found_restore = 0
let lines = readfile('Xtest_mks.out')
for line in lines
if line =~# 's:shortmess_save'
let found_restore += 1
endif
endfor
call assert_equal(0, found_restore)
call delete('Xtest_mks.out')
close
set sessionoptions&
endfunc
" Test for mksession with 'compatible' option
func Test_mksession_compatible()
mksession! Xtest_mks1.out
+7
View File
@@ -1042,10 +1042,17 @@ endfunc
func Test_using_mark_position()
" this was using freed memory
" new engine
new
norm O0
call assert_fails("s/\\%')", 'E486:')
bwipe!
" old engine
new
norm O0
call assert_fails("s/\\%#=1\\%')", 'E486:')
bwipe!
endfunc
func Test_using_visual_position()
+9
View File
@@ -646,4 +646,13 @@ func Test_source_buffer_vim9()
%bw!
endfunc
func Test_source_buffer_long_line()
" This was reading past the end of the line.
new
norm300gr0
so
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+1 -1
View File
@@ -935,7 +935,7 @@ func TerminalTmap(remap)
tunmap 123
tunmap 456
call assert_equal('', maparg('123', 't'))
close
exe buf . 'bwipe'
unlet g:job
endfunc
+7
View File
@@ -371,10 +371,17 @@ def Test_bufname()
assert_fails('bufname([])', 'E1220:')
enddef
let s:bufnr_res = 0
def Test_bufnr()
var buf = bufnr()
bufnr('%')->assert_equal(buf)
# check the lock is not taken over through the stack
const nr = 10
bufnr_res = bufnr()
bufnr_res = 12345
buf = bufnr('Xdummy', true)
buf->assert_notequal(-1)
exe 'bwipe! ' .. buf
+40
View File
@@ -318,6 +318,46 @@ def Test_disassemble_push()
&rtp = save_rtp
enddef
def Test_disassemble_import_autoload()
writefile(['vim9script'], 'XimportAL.vim')
var lines =<< trim END
vim9script
import autoload './XimportAL.vim'
def AutoloadFunc()
echo XimportAL.SomeFunc()
echo XimportAL.someVar
XimportAL.someVar = "yes"
enddef
var res = execute('disass AutoloadFunc')
assert_match('<SNR>\d*_AutoloadFunc.*' ..
'echo XimportAL.SomeFunc()\_s*' ..
'\d SOURCE .*/testdir/XimportAL.vim\_s*' ..
'\d PUSHFUNC "<80><fd>R\d\+_SomeFunc"\_s*' ..
'\d PCALL top (argc 0)\_s*' ..
'\d PCALL end\_s*' ..
'\d ECHO 1\_s*' ..
'echo XimportAL.someVar\_s*' ..
'\d SOURCE .*/testdir/XimportAL.vim\_s*' ..
'\d LOADEXPORT s:someVar from .*/testdir/XimportAL.vim\_s*' ..
'\d ECHO 1\_s*' ..
'XimportAL.someVar = "yes"\_s*' ..
'\d\+ PUSHS "yes"\_s*' ..
'\d\+ SOURCE .*/testdir/XimportAL.vim\_s*' ..
'\d\+ STOREEXPORT someVar in .*/testdir/XimportAL.vim\_s*' ..
'\d\+ RETURN void',
res)
END
v9.CheckScriptSuccess(lines)
delete('XimportAL.vim')
enddef
def s:ScriptFuncStore()
var localnr = 1
localnr = 2
+88 -1
View File
@@ -890,6 +890,93 @@ def Test_expr4_compare_null()
unlet g:null_dict
unlet g:not_null_list
# variables declared at script level used in a :def function
lines =<< trim END
vim9script
var l_decl: list<number>
var l_empty = []
var l_null = null_list
def TestList()
assert_false(l_decl == null)
assert_false(l_decl is null_list)
assert_false(l_empty == null)
assert_false(l_empty is null_list)
assert_true(l_null == null)
assert_true(l_null is null_list)
assert_true(l_null == null_list)
add(l_decl, 6)
assert_equal([6], l_decl)
add(l_empty, 7)
assert_equal([7], l_empty)
var caught = false
try
add(l_null, 9)
catch /E1130:/
caught = true
endtry
assert_true(caught)
enddef
TestList()
var b_decl: blob
var b_empty = 0z
var b_null = null_blob
def TestBlob()
assert_false(b_decl == null)
assert_false(b_decl is null_blob)
assert_false(b_empty == null)
assert_false(b_empty is null_blob)
assert_true(b_null == null)
assert_true(b_null is null_blob)
assert_true(b_null == null_blob)
add(b_decl, 6)
assert_equal(0z06, b_decl)
add(b_empty, 7)
assert_equal(0z07, b_empty)
var caught = false
try
add(b_null, 9)
catch /E1131:/
caught = true
endtry
assert_true(caught)
enddef
TestBlob()
var d_decl: dict<number>
var d_empty = {}
var d_null = null_dict
def TestDict()
assert_false(d_decl == null)
assert_false(d_decl is null_dict)
assert_false(d_empty == null)
assert_false(d_empty is null_dict)
assert_true(d_null == null)
assert_true(d_null is null_dict)
assert_true(d_null == null_dict)
d_decl['a'] = 6
assert_equal({a: 6}, d_decl)
d_empty['b'] = 7
assert_equal({b: 7}, d_empty)
var caught = false
try
d_null['c'] = 9
catch /E1103:/
caught = true
endtry
assert_true(caught)
enddef
TestDict()
END
v9.CheckScriptSuccess(lines)
lines =<< trim END
var d: dict<func> = {f: null_function}
assert_equal(null_function, d.f)
@@ -3923,7 +4010,7 @@ func Test_expr_fails()
call v9.CheckDefFailure(["echo len('asdf'"], 'E110:', 2)
call v9.CheckScriptFailure(['vim9script', "echo len('asdf'"], 'E116:', 2)
call v9.CheckDefAndScriptFailure(["echo Func0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789()"], ['E1011:', 'E117:'], 1)
call v9.CheckDefAndScriptFailure(["echo Func01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789()"], ['E1011:', 'E117:'], 1)
call v9.CheckDefAndScriptFailure(["echo doesnotexist()"], 'E117:', 1)
endfunc
+162 -2
View File
@@ -840,6 +840,160 @@ def Test_use_autoload_import_in_fold_expression()
&rtp = save_rtp
enddef
def Test_autoload_import_relative()
var lines =<< trim END
vim9script
g:loaded = 'yes'
export def RelFunc(): string
return 'relfunc'
enddef
def NotExported()
echo 'not'
enddef
export var someText = 'some text'
var notexp = 'bad'
END
writefile(lines, 'XimportRel.vim')
writefile(lines, 'XimportRel2.vim')
writefile(lines, 'XimportRel3.vim')
lines =<< trim END
vim9script
g:loaded = 'no'
import autoload './XimportRel.vim'
assert_equal('no', g:loaded)
def AFunc(): string
var res = ''
res ..= XimportRel.RelFunc()
res ..= '/'
res ..= XimportRel.someText
XimportRel.someText = 'from AFunc'
return res
enddef
# script not loaded when compiling
defcompile
assert_equal('no', g:loaded)
assert_equal('relfunc/some text', AFunc())
assert_equal('yes', g:loaded)
unlet g:loaded
assert_equal('from AFunc', XimportRel.someText)
XimportRel.someText = 'from script'
assert_equal('from script', XimportRel.someText)
END
v9.CheckScriptSuccess(lines)
lines =<< trim END
vim9script
import autoload './XimportRel.vim'
echo XimportRel.NotExported()
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 3)
lines =<< trim END
vim9script
import autoload './XimportRel.vim'
echo XimportRel.notexp
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3)
lines =<< trim END
vim9script
import autoload './XimportRel.vim'
XimportRel.notexp = 'bad'
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 3)
lines =<< trim END
vim9script
import autoload './XimportRel.vim'
def Func()
echo XimportRel.NotExported()
enddef
Func()
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExported', 1)
lines =<< trim END
vim9script
import autoload './XimportRel.vim'
def Func()
echo XimportRel.notexp
enddef
Func()
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)
lines =<< trim END
vim9script
import autoload './XimportRel.vim'
def Func()
XimportRel.notexp = 'bad'
enddef
Func()
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)
# does not fail if the script wasn't loaded yet
g:loaded = 'no'
lines =<< trim END
vim9script
import autoload './XimportRel2.vim'
def Func()
echo XimportRel2.notexp
enddef
defcompile
END
v9.CheckScriptSuccess(lines)
assert_equal('no', g:loaded)
# fails with a not loaded import
lines =<< trim END
vim9script
import autoload './XimportRel3.vim'
def Func()
XimportRel3.notexp = 'bad'
enddef
Func()
END
v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 1)
assert_equal('yes', g:loaded)
unlet g:loaded
delete('XimportRel.vim')
delete('XimportRel2.vim')
delete('XimportRel3.vim')
enddef
def Test_autoload_import_relative_autoload_dir()
mkdir('autoload', 'p')
var lines =<< trim END
vim9script
export def Bar()
g:called_bar = 'yes'
enddef
END
writefile(lines, 'autoload/script.vim')
lines =<< trim END
vim9script
import autoload './autoload/script.vim'
def Foo()
script.Bar()
enddef
Foo()
assert_equal('yes', g:called_bar)
END
v9.CheckScriptSuccess(lines)
unlet g:called_bar
delete('autoload', 'rf')
enddef
func Test_import_in_diffexpr()
CheckExecutable diff
@@ -2379,13 +2533,19 @@ def Test_import_autoload_fails()
vim9script
import autoload './doesNotExist.vim'
END
v9.CheckScriptFailure(lines, 'E1264:')
v9.CheckScriptFailure(lines, 'E282:', 2)
lines =<< trim END
vim9script
import autoload '/dir/doesNotExist.vim'
END
v9.CheckScriptFailure(lines, 'E1264:')
v9.CheckScriptFailure(lines, 'E282:', 2)
lines =<< trim END
vim9script
import autoload '../testdir'
END
v9.CheckScriptFailure(lines, 'E17:', 2)
lines =<< trim END
vim9script
+43 -15
View File
@@ -526,6 +526,28 @@ set_ufunc_name(ufunc_T *fp, char_u *name)
}
}
/*
* If "name" starts with K_SPECIAL and "buf[bufsize]" is big enough
* return "buf" filled with a readable function name.
* Otherwise just return "name", thus the return value can always be used.
* "name" and "buf" may be equal.
*/
char_u *
make_ufunc_name_readable(char_u *name, char_u *buf, size_t bufsize)
{
size_t len;
if (name[0] != K_SPECIAL)
return name;
len = STRLEN(name);
if (len + 3 > bufsize)
return name;
mch_memmove(buf + 5, name + 3, len + 1);
mch_memmove(buf, "<SNR>", 5);
return buf;
}
/*
* Get a name for a lambda. Returned in static memory.
*/
@@ -1883,6 +1905,21 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error)
return fname;
}
/*
* Concatenate the script ID and function name into "<SNR>99_name".
* "buffer" must have size MAX_FUNC_NAME_LEN.
*/
void
func_name_with_sid(char_u *name, int sid, char_u *buffer)
{
// A script-local function is stored as "<SNR>99_name".
buffer[0] = K_SPECIAL;
buffer[1] = KS_EXTRA;
buffer[2] = (int)KE_SNR;
vim_snprintf((char *)buffer + 3, MAX_FUNC_NAME_LEN - 3, "%ld_%s",
(long)sid, name);
}
/*
* Find a function "name" in script "sid".
*/
@@ -1890,17 +1927,12 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error)
find_func_with_sid(char_u *name, int sid)
{
hashitem_T *hi;
char_u buffer[200];
char_u buffer[MAX_FUNC_NAME_LEN];
if (!SCRIPT_ID_VALID(sid))
return NULL; // not in a script
// A script-local function is stored as "<SNR>99_name".
buffer[0] = K_SPECIAL;
buffer[1] = KS_EXTRA;
buffer[2] = (int)KE_SNR;
vim_snprintf((char *)buffer + 3, sizeof(buffer) - 3, "%ld_%s",
(long)sid, name);
func_name_with_sid(name, sid, buffer);
hi = hash_find(&func_hashtab, buffer);
if (!HASHITEM_EMPTY(hi))
return HI2UF(hi);
@@ -1914,7 +1946,7 @@ find_func_with_sid(char_u *name, int sid)
find_func_with_prefix(char_u *name, int sid)
{
hashitem_T *hi;
char_u buffer[200];
char_u buffer[MAX_FUNC_NAME_LEN];
scriptitem_T *si;
if (vim_strchr(name, AUTOLOAD_CHAR) != NULL)
@@ -3344,13 +3376,12 @@ user_func_error(int error, char_u *name, funcexe_T *funcexe)
{
case FCERR_UNKNOWN:
if (funcexe->fe_found_var)
semsg(_(e_not_callable_type_str), name);
emsg_funcname(e_not_callable_type_str, name);
else
emsg_funcname(e_unknown_function_str, name);
break;
case FCERR_NOTMETHOD:
emsg_funcname(
N_(e_cannot_use_function_as_method_str), name);
emsg_funcname(e_cannot_use_function_as_method_str, name);
break;
case FCERR_DELETED:
emsg_funcname(e_function_was_deleted_str, name);
@@ -3362,8 +3393,7 @@ user_func_error(int error, char_u *name, funcexe_T *funcexe)
emsg_funcname(e_not_enough_arguments_for_function_str, name);
break;
case FCERR_SCRIPT:
emsg_funcname(
e_using_sid_not_in_script_context_str, name);
emsg_funcname(e_using_sid_not_in_script_context_str, name);
break;
case FCERR_DICT:
emsg_funcname(e_calling_dict_function_without_dictionary_str,
@@ -3603,9 +3633,7 @@ theend:
* cancelled due to an aborting error, an interrupt, or an exception.
*/
if (!aborting())
{
user_func_error(error, (name != NULL) ? name : funcname, funcexe);
}
// clear the copies made from the partial
while (argv_clear > 0)
+38
View File
@@ -765,6 +765,44 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4658,
/**/
4657,
/**/
4656,
/**/
4655,
/**/
4654,
/**/
4653,
/**/
4652,
/**/
4651,
/**/
4650,
/**/
4649,
/**/
4648,
/**/
4647,
/**/
4646,
/**/
4645,
/**/
4644,
/**/
4643,
/**/
4642,
/**/
4641,
/**/
4640,
/**/
4639,
/**/
+4
View File
@@ -1580,6 +1580,9 @@ typedef UINT32_TYPEDEF UINT32_T;
*/
#define MAXMAPLEN 50
// maximum length of a function name, including SID and NUL
#define MAX_FUNC_NAME_LEN 200
// Size in bytes of the hash used in the undo file.
#define UNDO_HASH_SIZE 32
@@ -2240,6 +2243,7 @@ typedef enum {
#define ASSIGN_UNPACK 0x10 // using [a, b] = list
#define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type
#define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable
#define ASSIGN_INIT 0x80 // not assigning a value, just a declaration
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff
+4
View File
@@ -28,6 +28,8 @@ typedef enum {
ISN_ECHOERR, // :echoerr with isn_arg.number items on top of stack
ISN_RANGE, // compute range from isn_arg.string, push to stack
ISN_SUBSTITUTE, // :s command with expression
ISN_SOURCE, // source autoload script, isn_arg.number is the script ID
ISN_INSTR, // instructions compiled from expression
// get and set variables
@@ -43,6 +45,7 @@ typedef enum {
ISN_LOADWDICT, // push w: dict
ISN_LOADTDICT, // push t: dict
ISN_LOADS, // push s: variable isn_arg.loadstore
ISN_LOADEXPORT, // push exported variable isn_arg.loadstore
ISN_LOADOUTER, // push variable from outer scope isn_arg.outer
ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
ISN_LOADOPT, // push option isn_arg.string
@@ -57,6 +60,7 @@ typedef enum {
ISN_STOREW, // pop into window-local variable isn_arg.string
ISN_STORET, // pop into tab-local variable isn_arg.string
ISN_STORES, // pop into script variable isn_arg.loadstore
ISN_STOREEXPORT, // pop into exported script variable isn_arg.loadstore
ISN_STOREOUTER, // pop variable into outer scope isn_arg.outer
ISN_STORESCRIPT, // pop into script variable isn_arg.script
ISN_STOREOPT, // pop into option isn_arg.storeopt
+112 -31
View File
@@ -937,6 +937,7 @@ call_prepare(int argcount, typval_T *argvars, ectx_T *ectx)
tv = STACK_TV_BOT(-1);
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
tv->v_lock = 0;
return OK;
}
@@ -1011,10 +1012,11 @@ call_ufunc(
if (error != FCERR_UNKNOWN)
{
if (error == FCERR_TOOMANY)
semsg(_(e_too_many_arguments_for_function_str), ufunc->uf_name);
semsg(_(e_too_many_arguments_for_function_str),
printable_func_name(ufunc));
else
semsg(_(e_not_enough_arguments_for_function_str),
ufunc->uf_name);
printable_func_name(ufunc));
return FAIL;
}
@@ -1046,7 +1048,7 @@ call_ufunc(
if (error != FCERR_NONE)
{
user_func_error(error, ufunc->uf_name, &funcexe);
user_func_error(error, printable_func_name(ufunc), &funcexe);
return FAIL;
}
if (did_emsg > did_emsg_before)
@@ -1210,7 +1212,7 @@ call_partial(
if (res == FAIL)
{
if (called_emsg == called_emsg_before)
semsg(_(e_unknown_function_str),
emsg_funcname(e_unknown_function_str,
name == NULL ? (char_u *)"[unknown]" : name);
return FAIL;
}
@@ -1336,20 +1338,22 @@ do_2string(typval_T *tv, int is_2string_any, int tolerant)
* When the value of "sv" is a null list of dict, allocate it.
*/
static void
allocate_if_null(typval_T *tv)
allocate_if_null(svar_T *sv)
{
typval_T *tv = sv->sv_tv;
switch (tv->v_type)
{
case VAR_LIST:
if (tv->vval.v_list == NULL)
if (tv->vval.v_list == NULL && sv->sv_type != &t_list_empty)
(void)rettv_list_alloc(tv);
break;
case VAR_DICT:
if (tv->vval.v_dict == NULL)
if (tv->vval.v_dict == NULL && sv->sv_type != &t_dict_empty)
(void)rettv_dict_alloc(tv);
break;
case VAR_BLOB:
if (tv->vval.v_blob == NULL)
if (tv->vval.v_blob == NULL && sv->sv_type != &t_blob_null)
(void)rettv_blob_alloc(tv);
break;
default:
@@ -1507,6 +1511,13 @@ get_script_svar(scriptref_T *sref, int dfunc_idx)
emsg(_(e_script_variable_type_changed));
return NULL;
}
if (!sv->sv_export && sref->sref_sid != current_sctx.sc_sid)
{
if (dfunc != NULL)
semsg(_(e_item_not_exported_in_script_str), sv->sv_name);
return NULL;
}
return sv;
}
@@ -1560,14 +1571,10 @@ call_eval_func(
dictitem_T *v;
v = find_var(name, NULL, FALSE);
if (v == NULL)
if (v == NULL || (v->di_tv.v_type != VAR_PARTIAL
&& v->di_tv.v_type != VAR_FUNC))
{
semsg(_(e_unknown_function_str), name);
return FAIL;
}
if (v->di_tv.v_type != VAR_PARTIAL && v->di_tv.v_type != VAR_FUNC)
{
semsg(_(e_unknown_function_str), name);
emsg_funcname(e_unknown_function_str, name);
return FAIL;
}
return call_partial(&v->di_tv, argcount, ectx);
@@ -2620,6 +2627,20 @@ exec_instructions(ectx_T *ectx)
}
break;
case ISN_SOURCE:
{
scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number);
if (si->sn_state == SN_STATE_NOT_LOADED)
{
SOURCING_LNUM = iptr->isn_lnum;
if (do_source(si->sn_name, FALSE, DOSO_NONE, NULL)
== FAIL)
goto on_error;
}
}
break;
// execute :substitute with an expression
case ISN_SUBSTITUTE:
{
@@ -2891,7 +2912,7 @@ exec_instructions(ectx_T *ectx)
sv = get_script_svar(sref, ectx->ec_dfunc_idx);
if (sv == NULL)
goto theend;
allocate_if_null(sv->sv_tv);
allocate_if_null(sv);
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
goto theend;
copy_tv(sv->sv_tv, STACK_TV_BOT(0));
@@ -2899,11 +2920,12 @@ exec_instructions(ectx_T *ectx)
}
break;
// load s: variable in old script
// load s: variable in old script or autoload import
case ISN_LOADS:
case ISN_LOADEXPORT:
{
hashtab_T *ht = &SCRIPT_VARS(
iptr->isn_arg.loadstore.ls_sid);
int sid = iptr->isn_arg.loadstore.ls_sid;
hashtab_T *ht = &SCRIPT_VARS(sid);
char_u *name = iptr->isn_arg.loadstore.ls_name;
dictitem_T *di = find_var_in_ht(ht, 0, name, TRUE);
@@ -2915,6 +2937,25 @@ exec_instructions(ectx_T *ectx)
}
else
{
if (iptr->isn_type == ISN_LOADEXPORT)
{
int idx = get_script_item_idx(sid, name, 0,
NULL, NULL);
svar_T *sv;
if (idx >= 0)
{
sv = ((svar_T *)SCRIPT_ITEM(sid)
->sn_var_vals.ga_data) + idx;
if (!sv->sv_export)
{
SOURCING_LNUM = iptr->isn_lnum;
semsg(_(e_item_not_exported_in_script_str),
name);
goto on_error;
}
}
}
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
goto theend;
copy_tv(&di->di_tv, STACK_TV_BOT(0));
@@ -3036,20 +3077,50 @@ exec_instructions(ectx_T *ectx)
*tv = *STACK_TV_BOT(0);
break;
// store s: variable in old script
// store s: variable in old script or autoload import
case ISN_STORES:
case ISN_STOREEXPORT:
{
hashtab_T *ht = &SCRIPT_VARS(
iptr->isn_arg.loadstore.ls_sid);
int sid = iptr->isn_arg.loadstore.ls_sid;
hashtab_T *ht = &SCRIPT_VARS(sid);
char_u *name = iptr->isn_arg.loadstore.ls_name;
dictitem_T *di = find_var_in_ht(ht, 0, name + 2, TRUE);
dictitem_T *di = find_var_in_ht(ht, 0,
iptr->isn_type == ISN_STORES
? name + 2 : name, TRUE);
--ectx->ec_stack.ga_len;
SOURCING_LNUM = iptr->isn_lnum;
if (di == NULL)
{
if (iptr->isn_type == ISN_STOREEXPORT)
{
semsg(_(e_undefined_variable_str), name);
clear_tv(STACK_TV_BOT(0));
goto on_error;
}
store_var(name, STACK_TV_BOT(0));
}
else
{
SOURCING_LNUM = iptr->isn_lnum;
if (iptr->isn_type == ISN_STOREEXPORT)
{
int idx = get_script_item_idx(sid, name, 0,
NULL, NULL);
if (idx >= 0)
{
svar_T *sv = ((svar_T *)SCRIPT_ITEM(sid)
->sn_var_vals.ga_data) + idx;
if (!sv->sv_export)
{
semsg(_(e_item_not_exported_in_script_str),
name);
clear_tv(STACK_TV_BOT(0));
goto on_error;
}
}
}
if (var_check_permission(di, name) == FAIL)
{
clear_tv(STACK_TV_BOT(0));
@@ -5406,11 +5477,15 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
#endif
break;
case ISN_INSTR:
smsg("%s%4d INSTR", pfx, current);
list_instructions(" ", iptr->isn_arg.instr, INT_MAX, NULL);
msg(" -------------");
break;
case ISN_SOURCE:
{
smsg("%s%4d INSTR", pfx, current);
list_instructions(" ", iptr->isn_arg.instr,
INT_MAX, NULL);
msg(" -------------");
scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number);
smsg("%s%4d SOURCE %s", pfx, current, si->sn_name);
}
break;
case ISN_SUBSTITUTE:
@@ -5497,12 +5572,15 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
}
break;
case ISN_LOADS:
case ISN_LOADEXPORT:
{
scriptitem_T *si = SCRIPT_ITEM(
iptr->isn_arg.loadstore.ls_sid);
smsg("%s%4d LOADS s:%s from %s", pfx, current,
iptr->isn_arg.loadstore.ls_name, si->sn_name);
smsg("%s%4d %s s:%s from %s", pfx, current,
iptr->isn_type == ISN_LOADS ? "LOADS"
: "LOADEXPORT",
iptr->isn_arg.loadstore.ls_name, si->sn_name);
}
break;
case ISN_LOADAUTO:
@@ -5583,11 +5661,14 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
smsg("%s%4d STORET %s", pfx, current, iptr->isn_arg.string);
break;
case ISN_STORES:
case ISN_STOREEXPORT:
{
scriptitem_T *si = SCRIPT_ITEM(
iptr->isn_arg.loadstore.ls_sid);
smsg("%s%4d STORES %s in %s", pfx, current,
smsg("%s%4d %s %s in %s", pfx, current,
iptr->isn_type == ISN_STORES
? "STORES" : "STOREEXPORT",
iptr->isn_arg.loadstore.ls_name, si->sn_name);
}
break;
+48 -21
View File
@@ -298,26 +298,53 @@ compile_load_scriptvar(
*p = NUL;
si = SCRIPT_ITEM(import->imp_sid);
if (si->sn_autoload_prefix != NULL
&& si->sn_state == SN_STATE_NOT_LOADED)
{
char_u *auto_name = concat_str(si->sn_autoload_prefix, exp_name);
if (si->sn_import_autoload && si->sn_state == SN_STATE_NOT_LOADED)
// "import autoload './dir/script.vim'" or
// "import autoload './autoload/script.vim'" - load script first
res = generate_SOURCE(cctx, import->imp_sid);
// autoload script must be loaded later, access by the autoload
// name. If a '(' follows it must be a function. Otherwise we
// don't know, it can be "script.Func".
if (cc == '(' || paren_follows_after_expr)
res = generate_PUSHFUNC(cctx, auto_name, &t_func_any);
else
res = generate_AUTOLOAD(cctx, auto_name, &t_any);
vim_free(auto_name);
done = TRUE;
}
else
if (res == OK)
{
idx = find_exported(import->imp_sid, exp_name, &ufunc, &type,
cctx, NULL, TRUE);
if (si->sn_autoload_prefix != NULL
&& si->sn_state == SN_STATE_NOT_LOADED)
{
char_u *auto_name =
concat_str(si->sn_autoload_prefix, exp_name);
// autoload script must be loaded later, access by the autoload
// name. If a '(' follows it must be a function. Otherwise we
// don't know, it can be "script.Func".
if (cc == '(' || paren_follows_after_expr)
res = generate_PUSHFUNC(cctx, auto_name, &t_func_any);
else
res = generate_AUTOLOAD(cctx, auto_name, &t_any);
vim_free(auto_name);
done = TRUE;
}
else if (si->sn_import_autoload
&& si->sn_state == SN_STATE_NOT_LOADED)
{
// If a '(' follows it must be a function. Otherwise we don't
// know, it can be "script.Func".
if (cc == '(' || paren_follows_after_expr)
{
char_u sid_name[MAX_FUNC_NAME_LEN];
func_name_with_sid(exp_name, import->imp_sid, sid_name);
res = generate_PUSHFUNC(cctx, sid_name, &t_func_any);
}
else
res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name,
import->imp_sid, &t_any);
done = TRUE;
}
else
{
idx = find_exported(import->imp_sid, exp_name, &ufunc, &type,
cctx, NULL, TRUE);
}
}
*p = cc;
*end = p;
if (done)
@@ -671,7 +698,7 @@ compile_call(
char_u *name = *arg;
char_u *p;
int argcount = argcount_init;
char_u namebuf[100];
char_u namebuf[MAX_FUNC_NAME_LEN];
char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL;
int error = FCERR_NONE;
@@ -791,7 +818,7 @@ compile_call(
res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
}
else
semsg(_(e_unknown_function_str), namebuf);
emsg_funcname(e_unknown_function_str, namebuf);
goto theend;
}
@@ -816,7 +843,7 @@ compile_call(
&& vim_strchr(ufunc->uf_name, AUTOLOAD_CHAR) == NULL)
{
// A function name without g: prefix must be found locally.
semsg(_(e_unknown_function_str), namebuf);
emsg_funcname(e_unknown_function_str, namebuf);
goto theend;
}
}
@@ -847,7 +874,7 @@ compile_call(
if (has_g_namespace || is_autoload)
res = generate_UCALL(cctx, name, argcount);
else
semsg(_(e_unknown_function_str), namebuf);
emsg_funcname(e_unknown_function_str, namebuf);
theend:
vim_free(tofree);
+58 -25
View File
@@ -1066,7 +1066,7 @@ generate_OLDSCRIPT(
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if (isn_type == ISN_LOADS)
if (isn_type == ISN_LOADS || isn_type == ISN_LOADEXPORT)
isn = generate_instr_type(cctx, isn_type, type);
else
isn = generate_instr_drop(cctx, isn_type, 1);
@@ -1517,7 +1517,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
}
if (ufunc->uf_def_status == UF_COMPILE_ERROR)
{
emsg_funcname(_(e_call_to_function_that_failed_to_compile_str),
emsg_funcname(e_call_to_function_that_failed_to_compile_str,
ufunc->uf_name);
return FAIL;
}
@@ -1594,12 +1594,12 @@ generate_PCALL(
if (argcount < type->tt_min_argcount - varargs)
{
semsg(_(e_not_enough_arguments_for_function_str), name);
emsg_funcname(e_not_enough_arguments_for_function_str, name);
return FAIL;
}
if (!varargs && argcount > type->tt_argcount)
{
semsg(_(e_too_many_arguments_for_function_str), name);
emsg_funcname(e_too_many_arguments_for_function_str, name);
return FAIL;
}
if (type->tt_args != NULL)
@@ -1727,6 +1727,21 @@ generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count)
return OK;
}
/*
* Generate an ISN_SOURCE instruction.
*/
int
generate_SOURCE(cctx_T *cctx, int sid)
{
isn_T *isn;
if ((isn = generate_instr(cctx, ISN_SOURCE)) == NULL)
return FAIL;
isn->isn_arg.number = sid;
return OK;
}
/*
* Generate an ISN_PUT instruction.
*/
@@ -1913,9 +1928,24 @@ generate_store_var(
return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
case dest_script:
if (scriptvar_idx < 0)
{
isntype_T isn_type = ISN_STORES;
if (SCRIPT_ID_VALID(scriptvar_sid)
&& SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
&& SCRIPT_ITEM(scriptvar_sid)->sn_autoload_prefix
== NULL)
{
// "import autoload './dir/script.vim'" - load script first
if (generate_SOURCE(cctx, scriptvar_sid) == FAIL)
return FAIL;
isn_type = ISN_STOREEXPORT;
}
// "s:" may be included in the name.
return generate_OLDSCRIPT(cctx, ISN_STORES, name,
scriptvar_sid, type);
return generate_OLDSCRIPT(cctx, isn_type, name,
scriptvar_sid, type);
}
return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
scriptvar_sid, scriptvar_idx, type);
case dest_local:
@@ -2062,7 +2092,9 @@ delete_instr(isn_T *isn)
break;
case ISN_LOADS:
case ISN_LOADEXPORT:
case ISN_STORES:
case ISN_STOREEXPORT:
vim_free(isn->isn_arg.loadstore.ls_name);
break;
@@ -2089,7 +2121,7 @@ delete_instr(isn_T *isn)
if (isn->isn_arg.funcref.fr_func_name == NULL)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ isn->isn_arg.funcref.fr_dfunc_idx;
+ isn->isn_arg.funcref.fr_dfunc_idx;
ufunc_T *ufunc = dfunc->df_ufunc;
if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
@@ -2109,10 +2141,10 @@ delete_instr(isn_T *isn)
case ISN_DCALL:
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ isn->isn_arg.dfunc.cdf_idx;
+ isn->isn_arg.dfunc.cdf_idx;
if (dfunc->df_ufunc != NULL
&& func_name_refcount(dfunc->df_ufunc->uf_name))
&& func_name_refcount(dfunc->df_ufunc->uf_name))
func_ptr_unref(dfunc->df_ufunc);
}
break;
@@ -2140,7 +2172,7 @@ delete_instr(isn_T *isn)
case ISN_CMDMOD:
vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod
->cmod_filter_regmatch.regprog);
->cmod_filter_regmatch.regprog);
vim_free(isn->isn_arg.cmdmod.cf_cmdmod);
break;
@@ -2243,21 +2275,22 @@ delete_instr(isn_T *isn)
case ISN_STORE:
case ISN_STOREINDEX:
case ISN_STORENR:
case ISN_STOREOUTER:
case ISN_STORERANGE:
case ISN_STOREREG:
case ISN_STOREV:
case ISN_STRINDEX:
case ISN_STRSLICE:
case ISN_THROW:
case ISN_TRYCONT:
case ISN_UNLETINDEX:
case ISN_UNLETRANGE:
case ISN_UNPACK:
case ISN_USEDICT:
// nothing allocated
break;
}
case ISN_SOURCE:
case ISN_STOREOUTER:
case ISN_STORERANGE:
case ISN_STOREREG:
case ISN_STOREV:
case ISN_STRINDEX:
case ISN_STRSLICE:
case ISN_THROW:
case ISN_TRYCONT:
case ISN_UNLETINDEX:
case ISN_UNLETRANGE:
case ISN_UNPACK:
case ISN_USEDICT:
// nothing allocated
break;
}
}
void
+68 -23
View File
@@ -383,6 +383,48 @@ mark_imports_for_reload(int sid)
}
}
/*
* Part of "import" that handles a relative or absolute file name/
* Returns OK or FAIL.
*/
static int
handle_import_fname(char_u *fname, int is_autoload, int *sid)
{
if (is_autoload)
{
scriptitem_T *si;
*sid = find_script_by_name(fname);
if (*sid < 0)
{
int error = OK;
// Script does not exist yet, check name and create a new
// scriptitem.
if (!file_is_readable(fname))
{
semsg(_(mch_isdir(fname) ? e_str_is_directory
: e_cannot_read_from_str_2), fname);
return FAIL;
}
*sid = get_new_scriptitem_for_fname(&error, fname);
if (error == FAIL)
return FAIL;
}
si = SCRIPT_ITEM(*sid);
si->sn_import_autoload = TRUE;
if (si->sn_autoload_prefix == NULL)
si->sn_autoload_prefix = get_autoload_prefix(si);
// with testing override: load autoload script right away
if (!override_autoload || si->sn_state != SN_STATE_NOT_LOADED)
return OK;
}
return do_source(fname, FALSE, DOSO_NONE, sid);
}
/*
* Handle an ":import" command and add the resulting imported_T to "gap", when
* not NULL, or script "import_sid" sn_imports.
@@ -442,25 +484,18 @@ handle_import(
char_u *tail = gettail(si->sn_name);
char_u *from_name;
if (is_autoload)
res = FAIL;
else
{
// Relative to current script: "./name.vim", "../../name.vim".
len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
from_name = alloc((int)len);
if (from_name == NULL)
goto erret;
vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
add_pathsep(from_name);
STRCAT(from_name, tv.vval.v_string);
simplify_filename(from_name);
// Relative to current script: "./name.vim", "../../name.vim".
len = STRLEN(si->sn_name) - STRLEN(tail)
+ STRLEN(tv.vval.v_string) + 2;
from_name = alloc((int)len);
if (from_name == NULL)
goto erret;
vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
add_pathsep(from_name);
STRCAT(from_name, tv.vval.v_string);
simplify_filename(from_name);
res = do_source(from_name, FALSE, DOSO_NONE, &sid);
vim_free(from_name);
}
res = handle_import_fname(from_name, is_autoload, &sid);
vim_free(from_name);
}
else if (mch_isFullName(tv.vval.v_string)
#ifdef BACKSLASH_IN_FILENAME
@@ -471,10 +506,7 @@ handle_import(
)
{
// Absolute path: "/tmp/name.vim"
if (is_autoload)
res = FAIL;
else
res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
res = handle_import_fname(tv.vval.v_string, is_autoload, &sid);
}
else if (is_autoload)
{
@@ -677,6 +709,12 @@ find_exported(
svar_T *sv;
scriptitem_T *script = SCRIPT_ITEM(sid);
if (script->sn_import_autoload && script->sn_state == SN_STATE_NOT_LOADED)
{
if (do_source(script->sn_name, FALSE, DOSO_NONE, NULL) == FAIL)
return -1;
}
// Find name in "script".
idx = get_script_item_idx(sid, name, 0, cctx, cstack);
if (idx >= 0)
@@ -822,7 +860,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
init_tv.v_type = VAR_NUMBER;
else
init_tv.v_type = type->tt_type;
set_var_const(name, 0, type, &init_tv, FALSE, 0, 0);
set_var_const(name, 0, type, &init_tv, FALSE, ASSIGN_INIT, 0);
vim_free(name);
return p;
@@ -925,6 +963,13 @@ update_vim9_script_var(
if (*type == NULL)
*type = typval2type(tv, get_copyID(), &si->sn_type_list,
do_member ? TVTT_DO_MEMBER : 0);
else if ((flags & ASSIGN_INIT) == 0
&& (*type)->tt_type == VAR_BLOB && tv->v_type == VAR_BLOB
&& tv->vval.v_blob == NULL)
{
// "var b: blob = null_blob" has a different type.
*type = &t_blob_null;
}
if (sv->sv_type_allocated)
free_type(sv->sv_type);
if (*type != NULL && ((*type)->tt_type == VAR_FUNC
+6 -2
View File
@@ -337,7 +337,11 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int flags)
if (tv->v_type == VAR_STRING)
return &t_string;
if (tv->v_type == VAR_BLOB)
{
if (tv->vval.v_blob == NULL)
return &t_blob_null;
return &t_blob;
}
if (tv->v_type == VAR_LIST)
{
@@ -776,12 +780,12 @@ check_argument_types(
return OK; // just in case
if (totcount < type->tt_min_argcount - varargs)
{
semsg(_(e_not_enough_arguments_for_function_str), name);
emsg_funcname(e_not_enough_arguments_for_function_str, name);
return FAIL;
}
if (!varargs && type->tt_argcount >= 0 && totcount > type->tt_argcount)
{
semsg(_(e_too_many_arguments_for_function_str), name);
emsg_funcname(e_too_many_arguments_for_function_str, name);
return FAIL;
}
if (type->tt_args == NULL)