diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2c4f9fe37d..dfa242237d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2653,6 +2653,7 @@ matcharg({nr}) List arguments of |:match| matchdelete({id} [, {win}]) Number delete match identified by {id} matchend({expr}, {pat} [, {start} [, {count}]]) Number position where {pat} ends in {expr} +matchfuzzy({list}, {str}) List fuzzy match {str} in {list} matchlist({expr}, {pat} [, {start} [, {count}]]) List match and submatches of {pat} in {expr} matchstr({expr}, {pat} [, {start} [, {count}]]) @@ -7319,6 +7320,29 @@ matchend({expr}, {pat} [, {start} [, {count}]]) *matchend()* Can also be used as a |method|: > GetText()->matchend('word') + +matchfuzzy({list}, {str}) *matchfuzzy()* + Returns a list with all the strings in {list} that fuzzy + match {str}. The strings in the returned list are sorted + based on the matching score. {str} is treated as a literal + string and regular expression matching is NOT supported. + The maximum supported {str} length is 256. + + If there are no matching strings or there is an error, then an + empty list is returned. If length of {str} is greater than + 256, then returns an empty list. + + Example: > + :echo matchfuzzy(["clay", "crow"], "cay") +< results in ["clay"]. > + :echo getbufinfo()->map({_, v -> v.name})->matchfuzzy("ndl") +< results in a list of buffer names fuzzy matching "ndl". > + :echo v:oldfiles->matchfuzzy("test") +< results in a list of file names fuzzy matching "test". > + :let l = readfile("buffer.c")->matchfuzzy("str") +< results in a list of lines in "buffer.c" fuzzy matching "str". + + matchlist({expr}, {pat} [, {start} [, {count}]]) *matchlist()* Same as |match()|, but return a |List|. The first item in the list is the matched string, same as what matchstr() would @@ -12357,7 +12381,9 @@ text... < is equivalent to: > :let x = 1 :lockvar! x -< This is useful if you want to make sure the variable +< NOTE: in Vim9 script `:const` works differently, see + |vim9-const| + This is useful if you want to make sure the variable is not modified. If the value is a List or Dictionary literal then the items also cannot be changed: > const ll = [1, 2, 3] @@ -12397,6 +12423,8 @@ text... [depth] is relevant when locking a |List| or |Dictionary|. It specifies how deep the locking goes: + 0 Lock the variable {name} but not its + value. 1 Lock the |List| or |Dictionary| itself, cannot add or remove items, but can still change their values. @@ -12410,7 +12438,14 @@ text... |Dictionary|, one level deeper. The default [depth] is 2, thus when {name} is a |List| or |Dictionary| the values cannot be changed. - *E743* + + Example with [depth] 0: > + let mylist = [1, 2, 3] + lockvar 0 mylist + let mylist[0] = 77 " OK + call add(mylist, 4] " OK + let mylist = [7, 8, 9] " Error! +< *E743* For unlimited depth use [!] and omit [depth]. However, there is a maximum depth of 100 to catch loops. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 1dbfba128b..1e5915f41b 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -603,6 +603,7 @@ String manipulation: *string-functions* charclass() class of a character match() position where a pattern matches in a string matchend() position where a pattern match ends in a string + matchfuzzy() fuzzy matches a string in a list of strings matchstr() match of a pattern in a string matchstrpos() match and positions of a pattern in a string matchlist() like matchstr() and also return submatches diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index a32ce183c3..71c454ae94 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 07 +*vim9.txt* For Vim version 8.2. Last change: 2020 Sep 13 VIM REFERENCE MANUAL by Bram Moolenaar @@ -192,6 +192,9 @@ To intentionally avoid a variable being available later, a block can be used: } echo temp # Error! +Declaring a variable with a type but without an initializer will initialize to +zero, false or empty. + An existing variable cannot be assigned to with `:let`, since that implies a declaration. Global, window, tab, buffer and Vim variables can only be used without `:let`, because they are not really declared, they can also be deleted @@ -210,6 +213,40 @@ at the script level. > Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be used to repeat a `:substitute` command. + *vim9-const* +In legacy Vim script "const list = []" would make the variable "list" +immutable and also the value. Thus you cannot add items to the list. This +differs from what many languages do. Vim9 script does it like TypeScript: only +"list" is immutable, the value can be changed. + +One can use `:const!` to make both the variable and the value immutable. Use +this for composite structures that you want to make sure will not be modified. + +How this works: > + vim9script + const list = [1, 2] + list = [3, 4] # Error! + list[0] = 2 # OK + + const! LIST = [1, 2] + LIST = [3, 4] # Error! + LIST[0] = 2 # Error! +It is common to write constants as ALL_CAPS, but you don't have to. + +The constant only applies to the value itself, not what it refers to. > + cont females = ["Mary"] + const! NAMES = [["John", "Peter"], females] + NAMES[0] = ["Jack"] # Error! + NAMES[0][0] = ["Jack"] # Error! + NAMES[1] = ["Emma"] # Error! + Names[1][0] = "Emma" # OK, now females[0] == "Emma" + +Rationale: TypeScript has no way to make the value immutable. One can use +immutable types, but that quickly gets complicated for nested values. And +with a type cast the value can be made mutable again, which means there is no +guarantee the value won't change. Vim supports immutable values, in legacy +script this was done with `:lockvar`. But that is an extra statement and also +applies to nested values. Therefore the solution to use `:const!`. *E1092* Declaring more than one variable at a time, using the unpack notation, is @@ -408,7 +445,7 @@ for using a list or job. This is very much like JavaScript, but there are a few exceptions. type TRUE when ~ - bool v:true + bool v:true or 1 number non-zero float non-zero string non-empty @@ -946,26 +983,41 @@ declarations: > Expression evaluation was already close to what JavaScript and other languages are doing. Some details are unexpected and can be fixed. For example how the || and && operators work. Legacy Vim script: > - let result = 44 + let value = 44 ... - return result || 0 # returns 1 + let result = value || 0 # result == 1 Vim9 script works like JavaScript/TypeScript, keep the value: > - let result = 44 + let value = 44 ... - return result || 0 # returns 44 - -On the other hand, overloading "+" to use both for addition and string -concatenation goes against legacy Vim script and often leads to mistakes. -For that reason we will keep using ".." for string concatenation. Lua also -uses ".." this way. + let result = value || 0 # result == 44 There is no intention to completely match TypeScript syntax and semantics. We just want to take those parts that we can use for Vim and we expect Vim users -are happy with. TypeScript is a complex language with its own advantages and -disadvantages. People used to other languages (Java, Python, etc.) will also -find things in TypeScript that they do not like or do not understand. We'll -try to avoid those things. +will be happy with. TypeScript is a complex language with its own advantages +and disadvantages. To get an idea of the disadvantages read the book: +"JavaScript: The Good Parts". Or find the article "TypeScript: the good +parts" and read the "Things to avoid" section. + +People used to other languages (Java, Python, etc.) will also find things in +TypeScript that they do not like or do not understand. We'll try to avoid +those things. + +Specific items from TypeScript we avoid: +- Overloading "+", using it both for addition and string concatenation. This + goes against legacy Vim script and often leads to mistakes. For that reason + we will keep using ".." for string concatenation. Lua also uses ".." this + way. And it allows for conversion to string for more values. +- TypeScript can use an expression like "99 || 'yes'" in a condition, but + cannot assign the value to a boolean. That is inconsistent and can be + annoying. Vim recognizes an expression with && or || and allows using the + result as a bool. +- TypeScript considers an empty string as Falsy, but an empty list or dict as + Truthy. That is inconsistent. In Vim an empty list and dict are also + Falsy. +- TypeScript has various "Readonly" types, which have limited usefulness, + since a type cast can remove the immutable nature. Vim locks the value, + which is more flexible, but is only checked at runtime. Import and Export ~ diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 498fbef537..a9bca7a8cd 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: Bram Moolenaar -" Last Change: 2020 aug 30 +" Last Change: 2020 Sep 10 " If there already is an option window, jump to that one. let buf = bufnr('option-window') @@ -20,7 +20,7 @@ let s:cpo_save = &cpo set cpo&vim " function to be called when is hit in the option-window -fun! CR() +func CR() " If on a continued comment line, go back to the first comment line let lnum = search("^[^\t]", 'bWcn') @@ -47,10 +47,10 @@ fun! CR() elseif match(line, '^ \=[0-9]') >= 0 exe "norm! /" . line . "\zt" endif -endfun +endfunc " function to be called when is hit in the option-window -fun! Space() +func Space() let lnum = line(".") let line = getline(lnum) @@ -67,14 +67,14 @@ fun! Space() endif endif -endfun +endfunc let s:local_to_window = gettext('(local to window)') let s:local_to_buffer = gettext('(local to buffer)') " find the window in which the option applies " returns 0 for global option, 1 for local option, -1 for error -fun! Find(lnum) +func Find(lnum) let line = getline(a:lnum - 1) if line =~ s:local_to_window || line =~ s:local_to_buffer let local = 1 @@ -95,10 +95,10 @@ fun! Find(lnum) let local = -1 endif return local -endfun +endfunc " Update a "set" line in the option window -fun! Update(lnum, line, local, thiswin) +func Update(lnum, line, local, thiswin) " get the new value of the option and update the option window line if match(a:line, "=") >= 0 let name = substitute(a:line, '^ \tset \([^=]*\)=.*', '\1', "") @@ -123,7 +123,7 @@ fun! Update(lnum, line, local, thiswin) endif endif set nomodified -endfun +endfunc " Reset 'title' and 'icon' to make it work faster. " Reset 'undolevels' to avoid undo'ing until the buffer is empty. @@ -159,35 +159,45 @@ call append(6, gettext('" Hit on a "set" line to refresh it.')) " These functions are called often below. Keep them fast! +" Add an option name and explanation. The text can contain "\n" characters +" where a line break is to be inserted. +func AddOption(name, text) + let lines = split(a:text, "\n") + call append("$", a:name .. "\t" .. lines[0]) + for line in lines[1:] + call append("$", "\t" .. line) + endfor +endfunc + " Init a local binary option -fun! BinOptionL(name) +func BinOptionL(name) let val = getwinvar(winnr('#'), '&' . a:name) call append("$", substitute(substitute(" \tset " . val . a:name . "\t" . \!val . a:name, "0", "no", ""), "1", "", "")) -endfun +endfunc " Init a global binary option -fun! BinOptionG(name, val) +func BinOptionG(name, val) call append("$", substitute(substitute(" \tset " . a:val . a:name . "\t" . \!a:val . a:name, "0", "no", ""), "1", "", "")) -endfun +endfunc " Init a local string option -fun! OptionL(name) +func OptionL(name) let val = escape(getwinvar(winnr('#'), '&' . a:name), " \t\\\"|") call append("$", " \tset " . a:name . "=" . val) -endfun +endfunc " Init a global string option -fun! OptionG(name, val) +func OptionG(name, val) call append("$", " \tset " . a:name . "=" . escape(a:val, " \t\\\"|")) -endfun +endfunc let s:idx = 1 let s:lnum = line("$") call append("$", "") -fun! Header(text) +func Header(text) let line = s:idx . " " . a:text if s:idx < 10 let line = " " . line @@ -198,15 +208,15 @@ fun! Header(text) call append(s:lnum, line) let s:idx = s:idx + 1 let s:lnum = s:lnum + 1 -endfun +endfunc " Get the value of 'pastetoggle'. It could be a special key. -fun! PTvalue() +func PTvalue() redir @a silent set pt redir END return substitute(@a, '[^=]*=\(.*\)', '\1', "") -endfun +endfunc " Restore the previous value of 'cpoptions' here, it's used below. let &cpo = s:cpo_save @@ -214,480 +224,474 @@ let &cpo = s:cpo_save " List of all options, organized by function. " The text should be sufficient to know what the option is used for. -call Header("important") -call append("$", "compatible\tbehave very Vi compatible (not advisable)") +call Header(gettext("important")) +call AddOption("compatible", gettext("behave very Vi compatible (not advisable)")) call BinOptionG("cp", &cp) -call append("$", "cpoptions\tlist of flags to specify Vi compatibility") +call AddOption("cpoptions", gettext("list of flags to specify Vi compatibility")) call OptionG("cpo", &cpo) -call append("$", "insertmode\tuse Insert mode as the default mode") +call AddOption("insertmode", gettext("use Insert mode as the default mode")) call BinOptionG("im", &im) -call append("$", "paste\tpaste mode, insert typed text literally") +call AddOption("paste", gettext("paste mode, insert typed text literally")) call BinOptionG("paste", &paste) -call append("$", "pastetoggle\tkey sequence to toggle paste mode") +call AddOption("pastetoggle", gettext("key sequence to toggle paste mode")) if &pt =~ "\x80" call append("$", " \tset pt=" . PTvalue()) else call OptionG("pt", &pt) endif -call append("$", "runtimepath\tlist of directories used for runtime files and plugins") +call AddOption("runtimepath", gettext("list of directories used for runtime files and plugins")) call OptionG("rtp", &rtp) -call append("$", "packpath\tlist of directories used for plugin packages") +call AddOption("packpath", gettext("list of directories used for plugin packages")) call OptionG("pp", &pp) -call append("$", "helpfile\tname of the main help file") +call AddOption("helpfile", gettext("name of the main help file")) call OptionG("hf", &hf) -call Header("moving around, searching and patterns") -call append("$", "whichwrap\tlist of flags specifying which commands wrap to another line") +call Header(gettext("moving around, searching and patterns")) +call AddOption("whichwrap", gettext("list of flags specifying which commands wrap to another line")) call OptionG("ww", &ww) -call append("$", "startofline\tmany jump commands move the cursor to the first non-blank") -call append("$", "\tcharacter of a line") +call AddOption("startofline", gettext("many jump commands move the cursor to the first non-blank\ncharacter of a line")) call BinOptionG("sol", &sol) -call append("$", "paragraphs\tnroff macro names that separate paragraphs") +call AddOption("paragraphs", gettext("nroff macro names that separate paragraphs")) call OptionG("para", ¶) -call append("$", "sections\tnroff macro names that separate sections") +call AddOption("sections", gettext("nroff macro names that separate sections")) call OptionG("sect", §) -call append("$", "path\tlist of directory names used for file searching") -call append("$", "\t(global or local to buffer)") +call AddOption("path", gettext("list of directory names used for file searching")) +call append("$", gettext("\t(global or local to buffer)")) call OptionG("pa", &pa) -call append("$", "cdpath\tlist of directory names used for :cd") +call AddOption("cdpath", gettext("list of directory names used for :cd")) call OptionG("cd", &cd) if exists("+autochdir") - call append("$", "autochdir\tchange to directory of file in buffer") + call AddOption("autochdir", gettext("change to directory of file in buffer")) call BinOptionG("acd", &acd) endif -call append("$", "wrapscan\tsearch commands wrap around the end of the buffer") +call AddOption("wrapscan", gettext("search commands wrap around the end of the buffer")) call BinOptionG("ws", &ws) -call append("$", "incsearch\tshow match for partly typed search command") +call AddOption("incsearch", gettext("show match for partly typed search command")) call BinOptionG("is", &is) -call append("$", "magic\tchange the way backslashes are used in search patterns") +call AddOption("magic", gettext("change the way backslashes are used in search patterns")) call BinOptionG("magic", &magic) -call append("$", "regexpengine\tselect the default regexp engine used") +call AddOption("regexpengine", gettext("select the default regexp engine used")) call OptionG("re", &re) -call append("$", "ignorecase\tignore case when using a search pattern") +call AddOption("ignorecase", gettext("ignore case when using a search pattern")) call BinOptionG("ic", &ic) -call append("$", "smartcase\toverride 'ignorecase' when pattern has upper case characters") +call AddOption("smartcase", gettext("override 'ignorecase' when pattern has upper case characters")) call BinOptionG("scs", &scs) -call append("$", "casemap\twhat method to use for changing case of letters") +call AddOption("casemap", gettext("what method to use for changing case of letters")) call OptionG("cmp", &cmp) -call append("$", "maxmempattern\tmaximum amount of memory in Kbyte used for pattern matching") +call AddOption("maxmempattern", gettext("maximum amount of memory in Kbyte used for pattern matching")) call append("$", " \tset mmp=" . &mmp) -call append("$", "define\tpattern for a macro definition line") -call append("$", "\t(global or local to buffer)") +call AddOption("define", gettext("pattern for a macro definition line")) +call append("$", gettext("\t(global or local to buffer)")) call OptionG("def", &def) if has("find_in_path") - call append("$", "include\tpattern for an include-file line") + call AddOption("include", gettext("pattern for an include-file line")) call append("$", "\t" .. s:local_to_buffer) call OptionL("inc") - call append("$", "includeexpr\texpression used to transform an include line to a file name") + call AddOption("includeexpr", gettext("expression used to transform an include line to a file name")) call append("$", "\t" .. s:local_to_buffer) call OptionL("inex") endif -call Header("tags") -call append("$", "tagbsearch\tuse binary searching in tags files") +call Header(gettext("tags")) +call AddOption("tagbsearch", gettext("use binary searching in tags files")) call BinOptionG("tbs", &tbs) -call append("$", "taglength\tnumber of significant characters in a tag name or zero") +call AddOption("taglength", gettext("number of significant characters in a tag name or zero")) call append("$", " \tset tl=" . &tl) -call append("$", "tags\tlist of file names to search for tags") -call append("$", "\t(global or local to buffer)") +call AddOption("tags", gettext("list of file names to search for tags")) +call append("$", gettext("\t(global or local to buffer)")) call OptionG("tag", &tag) -call append("$", "tagcase\thow to handle case when searching in tags files:") -call append("$", "\t\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"") +call AddOption("tagcase", gettext("how to handle case when searching in tags files:\n\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"")) call append("$", "\t(global or local to buffer)") call OptionG("tc", &tc) -call append("$", "tagrelative\tfile names in a tags file are relative to the tags file") +call AddOption("tagrelative", gettext("file names in a tags file are relative to the tags file")) call BinOptionG("tr", &tr) -call append("$", "tagstack\ta :tag command will use the tagstack") +call AddOption("tagstack", gettext("a :tag command will use the tagstack")) call BinOptionG("tgst", &tgst) -call append("$", "showfulltag\twhen completing tags in Insert mode show more info") +call AddOption("showfulltag", gettext("when completing tags in Insert mode show more info")) call BinOptionG("sft", &sft) if has("eval") - call append("$", "tagfunc\ta function to be used to perform tag searches") + call AddOption("tagfunc", gettext("a function to be used to perform tag searches")) call append("$", "\t" .. s:local_to_buffer) call OptionL("tfu") endif if has("cscope") - call append("$", "cscopeprg\tcommand for executing cscope") + call AddOption("cscopeprg", gettext("command for executing cscope")) call OptionG("csprg", &csprg) - call append("$", "cscopetag\tuse cscope for tag commands") + call AddOption("cscopetag", gettext("use cscope for tag commands")) call BinOptionG("cst", &cst) - call append("$", "cscopetagorder\t0 or 1; the order in which \":cstag\" performs a search") + call AddOption("cscopetagorder", gettext("0 or 1; the order in which \":cstag\" performs a search")) call append("$", " \tset csto=" . &csto) - call append("$", "cscopeverbose\tgive messages when adding a cscope database") + call AddOption("cscopeverbose", gettext("give messages when adding a cscope database")) call BinOptionG("csverb", &csverb) - call append("$", "cscopepathcomp\thow many components of the path to show") + call AddOption("cscopepathcomp", gettext("how many components of the path to show")) call append("$", " \tset cspc=" . &cspc) - call append("$", "cscopequickfix\twhen to open a quickfix window for cscope") + call AddOption("cscopequickfix", gettext("when to open a quickfix window for cscope")) call OptionG("csqf", &csqf) - call append("$", "cscoperelative\tfile names in a cscope file are relative to that file") + call AddOption("cscoperelative", gettext("file names in a cscope file are relative to that file")) call BinOptionG("csre", &csre) endif -call Header("displaying text") -call append("$", "scroll\tnumber of lines to scroll for CTRL-U and CTRL-D") +call Header(gettext("displaying text")) +call AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D")) call append("$", "\t" .. s:local_to_window) call OptionL("scr") -call append("$", "scrolloff\tnumber of screen lines to show around the cursor") +call AddOption("scrolloff", gettext("number of screen lines to show around the cursor")) call append("$", " \tset so=" . &so) -call append("$", "wrap\tlong lines wrap") +call AddOption("wrap", gettext("long lines wrap")) call append("$", "\t" .. s:local_to_window) call BinOptionL("wrap") -call append("$", "linebreak\twrap long lines at a character in 'breakat'") +call AddOption("linebreak", gettext("wrap long lines at a character in 'breakat'")) call append("$", "\t" .. s:local_to_window) call BinOptionL("lbr") -call append("$", "breakindent\tpreserve indentation in wrapped text") +call AddOption("breakindent", gettext("preserve indentation in wrapped text")) call append("$", "\t" .. s:local_to_window) call BinOptionL("bri") -call append("$", "breakindentopt\tadjust breakindent behaviour") +call AddOption("breakindentopt", gettext("adjust breakindent behaviour")) call append("$", "\t" .. s:local_to_window) call OptionL("briopt") -call append("$", "breakat\twhich characters might cause a line break") +call AddOption("breakat", gettext("which characters might cause a line break")) call OptionG("brk", &brk) -call append("$", "showbreak\tstring to put before wrapped screen lines") +call AddOption("showbreak", gettext("string to put before wrapped screen lines")) call OptionG("sbr", &sbr) -call append("$", "sidescroll\tminimal number of columns to scroll horizontally") +call AddOption("sidescroll", gettext("minimal number of columns to scroll horizontally")) call append("$", " \tset ss=" . &ss) -call append("$", "sidescrolloff\tminimal number of columns to keep left and right of the cursor") +call AddOption("sidescrolloff", gettext("minimal number of columns to keep left and right of the cursor")) call append("$", " \tset siso=" . &siso) -call append("$", "display\tinclude \"lastline\" to show the last line even if it doesn't fit") -call append("$", "\tinclude \"uhex\" to show unprintable characters as a hex number") +call AddOption("display", gettext("include \"lastline\" to show the last line even if it doesn't fit\ninclude \"uhex\" to show unprintable characters as a hex number")) call OptionG("dy", &dy) -call append("$", "fillchars\tcharacters to use for the status line, folds and filler lines") +call AddOption("fillchars", gettext("characters to use for the status line, folds and filler lines")) call OptionG("fcs", &fcs) -call append("$", "cmdheight\tnumber of lines used for the command-line") +call AddOption("cmdheight", gettext("number of lines used for the command-line")) call append("$", " \tset ch=" . &ch) -call append("$", "columns\twidth of the display") +call AddOption("columns", gettext("width of the display")) call append("$", " \tset co=" . &co) -call append("$", "lines\tnumber of lines in the display") +call AddOption("lines", gettext("number of lines in the display")) call append("$", " \tset lines=" . &lines) -call append("$", "window\tnumber of lines to scroll for CTRL-F and CTRL-B") +call AddOption("window", gettext("number of lines to scroll for CTRL-F and CTRL-B")) call append("$", " \tset window=" . &window) -call append("$", "lazyredraw\tdon't redraw while executing macros") +call AddOption("lazyredraw", gettext("don't redraw while executing macros")) call BinOptionG("lz", &lz) if has("reltime") - call append("$", "redrawtime\ttimeout for 'hlsearch' and :match highlighting in msec") + call AddOption("redrawtime", gettext("timeout for 'hlsearch' and :match highlighting in msec")) call append("$", " \tset rdt=" . &rdt) endif -call append("$", "writedelay\tdelay in msec for each char written to the display") -call append("$", "\t(for debugging)") +call AddOption("writedelay", gettext("delay in msec for each char written to the display\n(for debugging)")) call append("$", " \tset wd=" . &wd) -call append("$", "list\tshow as ^I and end-of-line as $") +call AddOption("list", gettext("show as ^I and end-of-line as $")) call append("$", "\t" .. s:local_to_window) call BinOptionL("list") -call append("$", "listchars\tlist of strings used for list mode") +call AddOption("listchars", gettext("list of strings used for list mode")) call OptionG("lcs", &lcs) -call append("$", "number\tshow the line number for each line") +call AddOption("number", gettext("show the line number for each line")) call append("$", "\t" .. s:local_to_window) call BinOptionL("nu") -call append("$", "relativenumber\tshow the relative line number for each line") +call AddOption("relativenumber", gettext("show the relative line number for each line")) call append("$", "\t" .. s:local_to_window) call BinOptionL("rnu") if has("linebreak") - call append("$", "numberwidth\tnumber of columns to use for the line number") + call AddOption("numberwidth", gettext("number of columns to use for the line number")) call append("$", "\t" .. s:local_to_window) call OptionL("nuw") endif if has("conceal") - call append("$", "conceallevel\tcontrols whether concealable text is hidden") + call AddOption("conceallevel", gettext("controls whether concealable text is hidden")) call append("$", "\t" .. s:local_to_window) call OptionL("cole") - call append("$", "concealcursor\tmodes in which text in the cursor line can be concealed") + call AddOption("concealcursor", gettext("modes in which text in the cursor line can be concealed")) call append("$", "\t" .. s:local_to_window) call OptionL("cocu") endif -call Header("syntax, highlighting and spelling") -call append("$", "background\t\"dark\" or \"light\"; the background color brightness") +call Header(gettext("syntax, highlighting and spelling")) +call AddOption("background", gettext("\"dark\" or \"light\"; the background color brightness")) call OptionG("bg", &bg) -call append("$", "filetype\ttype of file; triggers the FileType event when set") +call AddOption("filetype", gettext("type of file; triggers the FileType event when set")) call append("$", "\t" .. s:local_to_buffer) call OptionL("ft") if has("syntax") - call append("$", "syntax\tname of syntax highlighting used") + call AddOption("syntax", gettext("name of syntax highlighting used")) call append("$", "\t" .. s:local_to_buffer) call OptionL("syn") - call append("$", "synmaxcol\tmaximum column to look for syntax items") + call AddOption("synmaxcol", gettext("maximum column to look for syntax items")) call append("$", "\t" .. s:local_to_buffer) call OptionL("smc") endif -call append("$", "highlight\twhich highlighting to use for various occasions") +call AddOption("highlight", gettext("which highlighting to use for various occasions")) call OptionG("hl", &hl) -call append("$", "hlsearch\thighlight all matches for the last used search pattern") +call AddOption("hlsearch", gettext("highlight all matches for the last used search pattern")) call BinOptionG("hls", &hls) -call append("$", "wincolor\thighlight group to use for the window") +call AddOption("wincolor", gettext("highlight group to use for the window")) call append("$", "\t" .. s:local_to_window) call OptionL("wcr") if has("termguicolors") - call append("$", "termguicolors\tuse GUI colors for the terminal") + call AddOption("termguicolors", gettext("use GUI colors for the terminal")) call BinOptionG("tgc", &tgc) endif if has("syntax") - call append("$", "cursorcolumn\thighlight the screen column of the cursor") + call AddOption("cursorcolumn", gettext("highlight the screen column of the cursor")) call append("$", "\t" .. s:local_to_window) call BinOptionL("cuc") - call append("$", "cursorline\thighlight the screen line of the cursor") + call AddOption("cursorline", gettext("highlight the screen line of the cursor")) call append("$", "\t" .. s:local_to_window) call BinOptionL("cul") - call append("$", "cursorlineopt\tspecifies which area 'cursorline' highlights") + call AddOption("cursorlineopt", gettext("specifies which area 'cursorline' highlights")) call append("$", "\t" .. s:local_to_window) call OptionL("culopt") - call append("$", "colorcolumn\tcolumns to highlight") + call AddOption("colorcolumn", gettext("columns to highlight")) call append("$", "\t" .. s:local_to_window) call OptionL("cc") - call append("$", "spell\thighlight spelling mistakes") + call AddOption("spell", gettext("highlight spelling mistakes")) call append("$", "\t" .. s:local_to_window) call BinOptionL("spell") - call append("$", "spelllang\tlist of accepted languages") + call AddOption("spelllang", gettext("list of accepted languages")) call append("$", "\t" .. s:local_to_buffer) call OptionL("spl") - call append("$", "spellfile\tfile that \"zg\" adds good words to") + call AddOption("spellfile", gettext("file that \"zg\" adds good words to")) call append("$", "\t" .. s:local_to_buffer) call OptionL("spf") - call append("$", "spellcapcheck\tpattern to locate the end of a sentence") + call AddOption("spellcapcheck", gettext("pattern to locate the end of a sentence")) call append("$", "\t" .. s:local_to_buffer) call OptionL("spc") - call append("$", "spelloptions\tflags to change how spell checking works") + call AddOption("spelloptions", gettext("flags to change how spell checking works")) call append("$", "\t" .. s:local_to_buffer) call OptionL("spo") - call append("$", "spellsuggest\tmethods used to suggest corrections") + call AddOption("spellsuggest", gettext("methods used to suggest corrections")) call OptionG("sps", &sps) - call append("$", "mkspellmem\tamount of memory used by :mkspell before compressing") + call AddOption("mkspellmem", gettext("amount of memory used by :mkspell before compressing")) call OptionG("msm", &msm) endif -call Header("multiple windows") -call append("$", "laststatus\t0, 1 or 2; when to use a status line for the last window") +call Header(gettext("multiple windows")) +call AddOption("laststatus", gettext("0, 1 or 2; when to use a status line for the last window")) call append("$", " \tset ls=" . &ls) if has("statusline") - call append("$", "statusline\talternate format to be used for a status line") + call AddOption("statusline", gettext("alternate format to be used for a status line")) call OptionG("stl", &stl) endif -call append("$", "equalalways\tmake all windows the same size when adding/removing windows") +call AddOption("equalalways", gettext("make all windows the same size when adding/removing windows")) call BinOptionG("ea", &ea) -call append("$", "eadirection\tin which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"") +call AddOption("eadirection", gettext("in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"")) call OptionG("ead", &ead) -call append("$", "winheight\tminimal number of lines used for the current window") +call AddOption("winheight", gettext("minimal number of lines used for the current window")) call append("$", " \tset wh=" . &wh) -call append("$", "winminheight\tminimal number of lines used for any window") +call AddOption("winminheight", gettext("minimal number of lines used for any window")) call append("$", " \tset wmh=" . &wmh) -call append("$", "winfixheight\tkeep the height of the window") +call AddOption("winfixheight", gettext("keep the height of the window")) call append("$", "\t" .. s:local_to_window) call BinOptionL("wfh") -call append("$", "winfixwidth\tkeep the width of the window") +call AddOption("winfixwidth", gettext("keep the width of the window")) call append("$", "\t" .. s:local_to_window) call BinOptionL("wfw") -call append("$", "winwidth\tminimal number of columns used for the current window") +call AddOption("winwidth", gettext("minimal number of columns used for the current window")) call append("$", " \tset wiw=" . &wiw) -call append("$", "winminwidth\tminimal number of columns used for any window") +call AddOption("winminwidth", gettext("minimal number of columns used for any window")) call append("$", " \tset wmw=" . &wmw) -call append("$", "helpheight\tinitial height of the help window") +call AddOption("helpheight", gettext("initial height of the help window")) call append("$", " \tset hh=" . &hh) if has("quickfix") - call append("$", "previewpopup\tuse a popup window for preview") + call AddOption("previewpopup", gettext("use a popup window for preview")) call append("$", " \tset pvp=" . &pvp) - call append("$", "previewheight\tdefault height for the preview window") + call AddOption("previewheight", gettext("default height for the preview window")) call append("$", " \tset pvh=" . &pvh) - call append("$", "previewwindow\tidentifies the preview window") + call AddOption("previewwindow", gettext("identifies the preview window")) call append("$", "\t" .. s:local_to_window) call BinOptionL("pvw") endif -call append("$", "hidden\tdon't unload a buffer when no longer shown in a window") +call AddOption("hidden", gettext("don't unload a buffer when no longer shown in a window")) call BinOptionG("hid", &hid) -call append("$", "switchbuf\t\"useopen\" and/or \"split\"; which window to use when jumping") -call append("$", "\tto a buffer") +call AddOption("switchbuf", gettext("\"useopen\" and/or \"split\"; which window to use when jumping\nto a buffer")) call OptionG("swb", &swb) -call append("$", "splitbelow\ta new window is put below the current one") +call AddOption("splitbelow", gettext("a new window is put below the current one")) call BinOptionG("sb", &sb) -call append("$", "splitright\ta new window is put right of the current one") +call AddOption("splitright", gettext("a new window is put right of the current one")) call BinOptionG("spr", &spr) -call append("$", "scrollbind\tthis window scrolls together with other bound windows") +call AddOption("scrollbind", gettext("this window scrolls together with other bound windows")) call append("$", "\t" .. s:local_to_window) call BinOptionL("scb") -call append("$", "scrollopt\t\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'") +call AddOption("scrollopt", gettext("\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'")) call OptionG("sbo", &sbo) -call append("$", "cursorbind\tthis window's cursor moves together with other bound windows") +call AddOption("cursorbind", gettext("this window's cursor moves together with other bound windows")) call append("$", "\t" .. s:local_to_window) call BinOptionL("crb") if has("terminal") - call append("$", "termwinsize\tsize of a terminal window") + call AddOption("termwinsize", gettext("size of a terminal window")) call append("$", "\t" .. s:local_to_window) call OptionL("tws") - call append("$", "termwinkey\tkey that precedes Vim commands in a terminal window") + call AddOption("termwinkey", gettext("key that precedes Vim commands in a terminal window")) call append("$", "\t" .. s:local_to_window) call OptionL("twk") - call append("$", "termwinscroll\tmax number of lines to keep for scrollback in a terminal window") + call AddOption("termwinscroll", gettext("max number of lines to keep for scrollback in a terminal window")) call append("$", "\t" .. s:local_to_window) + call OptionL("twsl") if has('win32') - call append("$", "termwintype\ttype of pty to use for a terminal window") + call AddOption("termwintype", gettext("type of pty to use for a terminal window")) call OptionG("twt", &twt) endif - call OptionL("twsl") if exists("&winptydll") - call append("$", "winptydll\tname of the winpty dynamic library") + call AddOption("winptydll", gettext("name of the winpty dynamic library")) call OptionG("winptydll", &winptydll) endif endif -call Header("multiple tab pages") -call append("$", "showtabline\t0, 1 or 2; when to use a tab pages line") +call Header(gettext("multiple tab pages")) +call AddOption("showtabline", gettext("0, 1 or 2; when to use a tab pages line")) call append("$", " \tset stal=" . &stal) -call append("$", "tabpagemax\tmaximum number of tab pages to open for -p and \"tab all\"") +call AddOption("tabpagemax", gettext("maximum number of tab pages to open for -p and \"tab all\"")) call append("$", " \tset tpm=" . &tpm) -call append("$", "tabline\tcustom tab pages line") +call AddOption("tabline", gettext("custom tab pages line")) call OptionG("tal", &tal) if has("gui") - call append("$", "guitablabel\tcustom tab page label for the GUI") + call AddOption("guitablabel", gettext("custom tab page label for the GUI")) call OptionG("gtl", >l) - call append("$", "guitabtooltip\tcustom tab page tooltip for the GUI") + call AddOption("guitabtooltip", gettext("custom tab page tooltip for the GUI")) call OptionG("gtt", >t) endif -call Header("terminal") -call append("$", "term\tname of the used terminal") +call Header(gettext("terminal")) +call AddOption("term", gettext("name of the used terminal")) call OptionG("term", &term) -call append("$", "ttytype\talias for 'term'") +call AddOption("ttytype", gettext("alias for 'term'")) call OptionG("tty", &tty) -call append("$", "ttybuiltin\tcheck built-in termcaps first") +call AddOption("ttybuiltin", gettext("check built-in termcaps first")) call BinOptionG("tbi", &tbi) -call append("$", "ttyfast\tterminal connection is fast") +call AddOption("ttyfast", gettext("terminal connection is fast")) call BinOptionG("tf", &tf) -call append("$", "weirdinvert\tterminal that requires extra redrawing") +call AddOption("weirdinvert", gettext("terminal that requires extra redrawing")) call BinOptionG("wiv", &wiv) -call append("$", "esckeys\trecognize keys that start with in Insert mode") +call AddOption("esckeys", gettext("recognize keys that start with in Insert mode")) call BinOptionG("ek", &ek) -call append("$", "scrolljump\tminimal number of lines to scroll at a time") +call AddOption("scrolljump", gettext("minimal number of lines to scroll at a time")) call append("$", " \tset sj=" . &sj) -call append("$", "ttyscroll\tmaximum number of lines to use scrolling instead of redrawing") +call AddOption("ttyscroll", gettext("maximum number of lines to use scrolling instead of redrawing")) call append("$", " \tset tsl=" . &tsl) if has("gui") || has("win32") - call append("$", "guicursor\tspecifies what the cursor looks like in different modes") + call AddOption("guicursor", gettext("specifies what the cursor looks like in different modes")) call OptionG("gcr", &gcr) endif if has("title") let &title = s:old_title - call append("$", "title\tshow info in the window title") + call AddOption("title", gettext("show info in the window title")) call BinOptionG("title", &title) set notitle - call append("$", "titlelen\tpercentage of 'columns' used for the window title") + call AddOption("titlelen", gettext("percentage of 'columns' used for the window title")) call append("$", " \tset titlelen=" . &titlelen) - call append("$", "titlestring\twhen not empty, string to be used for the window title") + call AddOption("titlestring", gettext("when not empty, string to be used for the window title")) call OptionG("titlestring", &titlestring) - call append("$", "titleold\tstring to restore the title to when exiting Vim") + call AddOption("titleold", gettext("string to restore the title to when exiting Vim")) call OptionG("titleold", &titleold) let &icon = s:old_icon - call append("$", "icon\tset the text of the icon for this window") + call AddOption("icon", gettext("set the text of the icon for this window")) call BinOptionG("icon", &icon) set noicon - call append("$", "iconstring\twhen not empty, text for the icon of this window") + call AddOption("iconstring", gettext("when not empty, text for the icon of this window")) call OptionG("iconstring", &iconstring) endif if has("win32") - call append("$", "restorescreen\trestore the screen contents when exiting Vim") + call AddOption("restorescreen", gettext("restore the screen contents when exiting Vim")) call BinOptionG("rs", &rs) endif -call Header("using the mouse") -call append("$", "mouse\tlist of flags for using the mouse") +call Header(gettext("using the mouse")) +call AddOption("mouse", gettext("list of flags for using the mouse")) call OptionG("mouse", &mouse) if has("gui") - call append("$", "mousefocus\tthe window with the mouse pointer becomes the current one") + call AddOption("mousefocus", gettext("the window with the mouse pointer becomes the current one")) call BinOptionG("mousef", &mousef) endif -call append("$", "scrollfocus\tthe window with the mouse pointer scrolls with the mouse wheel") +call AddOption("scrollfocus", gettext("the window with the mouse pointer scrolls with the mouse wheel")) call BinOptionG("scf", &scf) if has("gui") - call append("$", "mousehide\thide the mouse pointer while typing") + call AddOption("mousehide", gettext("hide the mouse pointer while typing")) call BinOptionG("mh", &mh) endif -call append("$", "mousemodel\t\"extend\", \"popup\" or \"popup_setpos\"; what the right") -call append("$", "\tmouse button is used for") +call AddOption("mousemodel", gettext("\"extend\", \"popup\" or \"popup_setpos\"; what the right\nmouse button is used for")) call OptionG("mousem", &mousem) -call append("$", "mousetime\tmaximum time in msec to recognize a double-click") +call AddOption("mousetime", gettext("maximum time in msec to recognize a double-click")) call append("$", " \tset mouset=" . &mouset) -call append("$", "ttymouse\t\"xterm\", \"xterm2\", \"dec\" or \"netterm\"; type of mouse") +call AddOption("ttymouse", gettext("\"xterm\", \"xterm2\", \"dec\" or \"netterm\"; type of mouse")) call OptionG("ttym", &ttym) if has("mouseshape") - call append("$", "mouseshape\twhat the mouse pointer looks like in different modes") + call AddOption("mouseshape", gettext("what the mouse pointer looks like in different modes")) call OptionG("mouses", &mouses) endif if has("gui") - call Header("GUI") - call append("$", "guifont\tlist of font names to be used in the GUI") + call Header(gettext("GUI")) + call AddOption("guifont", gettext("list of font names to be used in the GUI")) call OptionG("gfn", &gfn) if has("xfontset") - call append("$", "guifontset\tpair of fonts to be used, for multibyte editing") + call AddOption("guifontset", gettext("pair of fonts to be used, for multibyte editing")) call OptionG("gfs", &gfs) endif - call append("$", "guifontwide\tlist of font names to be used for double-wide characters") + call AddOption("guifontwide", gettext("list of font names to be used for double-wide characters")) call OptionG("gfw", &gfw) if has("mac") - call append("$", "antialias\tuse smooth, antialiased fonts") + call AddOption("antialias", gettext("use smooth, antialiased fonts")) call BinOptionG("anti", &anti) endif - call append("$", "guioptions\tlist of flags that specify how the GUI works") + call AddOption("guioptions", gettext("list of flags that specify how the GUI works")) call OptionG("go", &go) if has("gui_gtk") - call append("$", "toolbar\t\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar") + call AddOption("toolbar", gettext("\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar")) call OptionG("tb", &tb) if has("gui_gtk2") - call append("$", "toolbariconsize\tsize of toolbar icons") + call AddOption("toolbariconsize", gettext("size of toolbar icons")) call OptionG("tbis", &tbis) endif - call append("$", "guiheadroom\troom (in pixels) left above/below the window") + call AddOption("guiheadroom", gettext("room (in pixels) left above/below the window")) call append("$", " \tset ghr=" . &ghr) endif if has("directx") - call append("$", "renderoptions\toptions for text rendering") + call AddOption("renderoptions", gettext("options for text rendering")) call OptionG("rop", &rop) endif - call append("$", "guipty\tuse a pseudo-tty for I/O to external commands") + call AddOption("guipty", gettext("use a pseudo-tty for I/O to external commands")) call BinOptionG("guipty", &guipty) if has("browse") - call append("$", "browsedir\t\"last\", \"buffer\" or \"current\": which directory used for the file browser") + call AddOption("browsedir", gettext("\"last\", \"buffer\" or \"current\": which directory used for the file browser")) call OptionG("bsdir", &bsdir) endif if has("multi_lang") - call append("$", "langmenu\tlanguage to be used for the menus") + call AddOption("langmenu", gettext("language to be used for the menus")) call OptionG("langmenu", &lm) endif - call append("$", "menuitems\tmaximum number of items in one menu") + call AddOption("menuitems", gettext("maximum number of items in one menu")) call append("$", " \tset mis=" . &mis) if has("winaltkeys") - call append("$", "winaltkeys\t\"no\", \"yes\" or \"menu\"; how to use the ALT key") + call AddOption("winaltkeys", gettext("\"no\", \"yes\" or \"menu\"; how to use the ALT key")) call OptionG("wak", &wak) endif - call append("$", "linespace\tnumber of pixel lines to use between characters") + call AddOption("linespace", gettext("number of pixel lines to use between characters")) call append("$", " \tset lsp=" . &lsp) call append("$", "columnspace\tnumber of pixel columns to use between characters") call append("$", " \tset csp=" . &csp) if has("balloon_eval") || has("balloon_eval_term") - call append("$", "balloondelay\tdelay in milliseconds before a balloon may pop up") + call AddOption("balloondelay", gettext("delay in milliseconds before a balloon may pop up")) call append("$", " \tset bdlay=" . &bdlay) if has("balloon_eval") - call append("$", "ballooneval\tuse balloon evaluation in the GUI") + call AddOption("ballooneval", gettext("use balloon evaluation in the GUI")) call BinOptionG("beval", &beval) endif if has("balloon_eval_term") - call append("$", "balloonevalterm\tuse balloon evaluation in the terminal") + call AddOption("balloonevalterm", gettext("use balloon evaluation in the terminal")) call BinOptionG("bevalterm", &beval) endif if has("eval") - call append("$", "balloonexpr\texpression to show in balloon eval") + call AddOption("balloonexpr", gettext("expression to show in balloon eval")) call append("$", " \tset bexpr=" . &bexpr) endif endif if exists("+macatsui") - call append("$", "macatsui\tuse ATSUI text drawing; disable to avoid display problems") + call AddOption("macatsui", gettext("use ATSUI text drawing; disable to avoid display problems")) call OptionG("macatsui", &macatsui) endif if has("gui_macvim") @@ -710,722 +714,718 @@ if has("gui") endif if has("printer") - call Header("printing") - call append("$", "printoptions\tlist of items that control the format of :hardcopy output") + call Header(gettext("printing")) + call AddOption("printoptions", gettext("list of items that control the format of :hardcopy output")) call OptionG("popt", &popt) - call append("$", "printdevice\tname of the printer to be used for :hardcopy") + call AddOption("printdevice", gettext("name of the printer to be used for :hardcopy")) call OptionG("pdev", &pdev) if has("postscript") - call append("$", "printexpr\texpression used to print the PostScript file for :hardcopy") + call AddOption("printexpr", gettext("expression used to print the PostScript file for :hardcopy")) call OptionG("pexpr", &pexpr) endif - call append("$", "printfont\tname of the font to be used for :hardcopy") + call AddOption("printfont", gettext("name of the font to be used for :hardcopy")) call OptionG("pfn", &pfn) - call append("$", "printheader\tformat of the header used for :hardcopy") + call AddOption("printheader", gettext("format of the header used for :hardcopy")) call OptionG("pheader", &pheader) if has("postscript") - call append("$", "printencoding\tencoding used to print the PostScript file for :hardcopy") + call AddOption("printencoding", gettext("encoding used to print the PostScript file for :hardcopy")) call OptionG("penc", &penc) endif - call append("$", "printmbcharset\tthe CJK character set to be used for CJK output from :hardcopy") + call AddOption("printmbcharset", gettext("the CJK character set to be used for CJK output from :hardcopy")) call OptionG("pmbcs", &pmbcs) - call append("$", "printmbfont\tlist of font names to be used for CJK output from :hardcopy") + call AddOption("printmbfont", gettext("list of font names to be used for CJK output from :hardcopy")) call OptionG("pmbfn", &pmbfn) endif -call Header("messages and info") -call append("$", "terse\tadd 's' flag in 'shortmess' (don't show search message)") +call Header(gettext("messages and info")) +call AddOption("terse", gettext("add 's' flag in 'shortmess' (don't show search message)")) call BinOptionG("terse", &terse) -call append("$", "shortmess\tlist of flags to make messages shorter") +call AddOption("shortmess", gettext("list of flags to make messages shorter")) call OptionG("shm", &shm) -call append("$", "showcmd\tshow (partial) command keys in the status line") +call AddOption("showcmd", gettext("show (partial) command keys in the status line")) let &sc = s:old_sc call BinOptionG("sc", &sc) set nosc -call append("$", "showmode\tdisplay the current mode in the status line") +call AddOption("showmode", gettext("display the current mode in the status line")) call BinOptionG("smd", &smd) -call append("$", "ruler\tshow cursor position below each window") +call AddOption("ruler", gettext("show cursor position below each window")) let &ru = s:old_ru call BinOptionG("ru", &ru) set noru if has("statusline") - call append("$", "rulerformat\talternate format to be used for the ruler") + call AddOption("rulerformat", gettext("alternate format to be used for the ruler")) call OptionG("ruf", &ruf) endif -call append("$", "report\tthreshold for reporting number of changed lines") +call AddOption("report", gettext("threshold for reporting number of changed lines")) call append("$", " \tset report=" . &report) -call append("$", "verbose\tthe higher the more messages are given") +call AddOption("verbose", gettext("the higher the more messages are given")) call append("$", " \tset vbs=" . &vbs) -call append("$", "verbosefile\tfile to write messages in") +call AddOption("verbosefile", gettext("file to write messages in")) call OptionG("vfile", &vfile) -call append("$", "more\tpause listings when the screen is full") +call AddOption("more", gettext("pause listings when the screen is full")) call BinOptionG("more", &more) if has("dialog_con") || has("dialog_gui") - call append("$", "confirm\tstart a dialog when a command fails") + call AddOption("confirm", gettext("start a dialog when a command fails")) call BinOptionG("cf", &cf) endif -call append("$", "errorbells\tring the bell for error messages") +call AddOption("errorbells", gettext("ring the bell for error messages")) call BinOptionG("eb", &eb) -call append("$", "visualbell\tuse a visual bell instead of beeping") +call AddOption("visualbell", gettext("use a visual bell instead of beeping")) call BinOptionG("vb", &vb) -call append("$", "belloff\tdo not ring the bell for these reasons") +call AddOption("belloff", gettext("do not ring the bell for these reasons")) call OptionG("belloff", &belloff) if has("multi_lang") - call append("$", "helplang\tlist of preferred languages for finding help") + call AddOption("helplang", gettext("list of preferred languages for finding help")) call OptionG("hlg", &hlg) endif -call Header("selecting text") -call append("$", "selection\t\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves") +call Header(gettext("selecting text")) +call AddOption("selection", gettext("\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves")) call OptionG("sel", &sel) -call append("$", "selectmode\t\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode") -call append("$", "\tinstead of Visual mode") +call AddOption("selectmode", gettext("\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode\ninstead of Visual mode")) call OptionG("slm", &slm) if has("clipboard") - call append("$", "clipboard\t\"unnamed\" to use the * register like unnamed register") - call append("$", "\t\"autoselect\" to always put selected text on the clipboard") + call AddOption("clipboard", gettext("\"unnamed\" to use the * register like unnamed register\n\"autoselect\" to always put selected text on the clipboard")) call OptionG("cb", &cb) endif -call append("$", "keymodel\t\"startsel\" and/or \"stopsel\"; what special keys can do") +call AddOption("keymodel", gettext("\"startsel\" and/or \"stopsel\"; what special keys can do")) call OptionG("km", &km) -call Header("editing text") -call append("$", "undolevels\tmaximum number of changes that can be undone") -call append("$", "\t(global or local to buffer)") +call Header(gettext("editing text")) +call AddOption("undolevels", gettext("maximum number of changes that can be undone")) +call append("$", gettext("\t(global or local to buffer)")) call append("$", " \tset ul=" . s:old_ul) -call append("$", "undofile\tautomatically save and restore undo history") +call AddOption("undofile", gettext("automatically save and restore undo history")) call BinOptionG("udf", &udf) -call append("$", "undodir\tlist of directories for undo files") +call AddOption("undodir", gettext("list of directories for undo files")) call OptionG("udir", &udir) -call append("$", "undoreload\tmaximum number lines to save for undo on a buffer reload") +call AddOption("undoreload", gettext("maximum number lines to save for undo on a buffer reload")) call append("$", " \tset ur=" . &ur) -call append("$", "modified\tchanges have been made and not written to a file") +call AddOption("modified", gettext("changes have been made and not written to a file")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("mod") -call append("$", "readonly\tbuffer is not to be written") +call AddOption("readonly", gettext("buffer is not to be written")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("ro") -call append("$", "modifiable\tchanges to the text are not possible") +call AddOption("modifiable", gettext("changes to the text are not possible")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("ma") -call append("$", "textwidth\tline length above which to break a line") +call AddOption("textwidth", gettext("line length above which to break a line")) call append("$", "\t" .. s:local_to_buffer) call OptionL("tw") -call append("$", "wrapmargin\tmargin from the right in which to break a line") +call AddOption("wrapmargin", gettext("margin from the right in which to break a line")) call append("$", "\t" .. s:local_to_buffer) call OptionL("wm") -call append("$", "backspace\tspecifies what , CTRL-W, etc. can do in Insert mode") +call AddOption("backspace", gettext("specifies what , CTRL-W, etc. can do in Insert mode")) call append("$", " \tset bs=" . &bs) -call append("$", "comments\tdefinition of what comment lines look like") +call AddOption("comments", gettext("definition of what comment lines look like")) call append("$", "\t" .. s:local_to_buffer) call OptionL("com") -call append("$", "formatoptions\tlist of flags that tell how automatic formatting works") +call AddOption("formatoptions", gettext("list of flags that tell how automatic formatting works")) call append("$", "\t" .. s:local_to_buffer) call OptionL("fo") -call append("$", "formatlistpat\tpattern to recognize a numbered list") +call AddOption("formatlistpat", gettext("pattern to recognize a numbered list")) call append("$", "\t" .. s:local_to_buffer) call OptionL("flp") if has("eval") - call append("$", "formatexpr\texpression used for \"gq\" to format lines") + call AddOption("formatexpr", gettext("expression used for \"gq\" to format lines")) call append("$", "\t" .. s:local_to_buffer) call OptionL("fex") endif if has("insert_expand") - call append("$", "complete\tspecifies how Insert mode completion works for CTRL-N and CTRL-P") + call AddOption("complete", gettext("specifies how Insert mode completion works for CTRL-N and CTRL-P")) call append("$", "\t" .. s:local_to_buffer) call OptionL("cpt") - call append("$", "completeopt\twhether to use a popup menu for Insert mode completion") + call AddOption("completeopt", gettext("whether to use a popup menu for Insert mode completion")) call OptionG("cot", &cot) if exists("+completepopup") - call append("$", "completepopup\toptions for the Insert mode completion info popup") + call AddOption("completepopup", gettext("options for the Insert mode completion info popup")) call OptionG("cpp", &cpp) endif - call append("$", "pumheight\tmaximum height of the popup menu") + call AddOption("pumheight", gettext("maximum height of the popup menu")) call OptionG("ph", &ph) - call append("$", "pumwidth\tminimum width of the popup menu") + call AddOption("pumwidth", gettext("minimum width of the popup menu")) call OptionG("pw", &pw) - call append("$", "completefunc\tuser defined function for Insert mode completion") + call AddOption("completefunc", gettext("user defined function for Insert mode completion")) call append("$", "\t" .. s:local_to_buffer) call OptionL("cfu") - call append("$", "omnifunc\tfunction for filetype-specific Insert mode completion") + call AddOption("omnifunc", gettext("function for filetype-specific Insert mode completion")) call append("$", "\t" .. s:local_to_buffer) call OptionL("ofu") - call append("$", "dictionary\tlist of dictionary files for keyword completion") - call append("$", "\t(global or local to buffer)") + call AddOption("dictionary", gettext("list of dictionary files for keyword completion")) + call append("$", gettext("\t(global or local to buffer)")) call OptionG("dict", &dict) - call append("$", "thesaurus\tlist of thesaurus files for keyword completion") - call append("$", "\t(global or local to buffer)") + call AddOption("thesaurus", gettext("list of thesaurus files for keyword completion")) + call append("$", gettext("\t(global or local to buffer)")) call OptionG("tsr", &tsr) endif -call append("$", "infercase\tadjust case of a keyword completion match") +call AddOption("infercase", gettext("adjust case of a keyword completion match")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("inf") if has("digraphs") - call append("$", "digraph\tenable entering digraphs with c1 c2") + call AddOption("digraph", gettext("enable entering digraphs with c1 c2")) call BinOptionG("dg", &dg) endif -call append("$", "tildeop\tthe \"~\" command behaves like an operator") +call AddOption("tildeop", gettext("the \"~\" command behaves like an operator")) call BinOptionG("top", &top) -call append("$", "operatorfunc\tfunction called for the\"g@\" operator") +call AddOption("operatorfunc", gettext("function called for the\"g@\" operator")) call OptionG("opfunc", &opfunc) -call append("$", "showmatch\twhen inserting a bracket, briefly jump to its match") +call AddOption("showmatch", gettext("when inserting a bracket, briefly jump to its match")) call BinOptionG("sm", &sm) -call append("$", "matchtime\ttenth of a second to show a match for 'showmatch'") +call AddOption("matchtime", gettext("tenth of a second to show a match for 'showmatch'")) call append("$", " \tset mat=" . &mat) -call append("$", "matchpairs\tlist of pairs that match for the \"%\" command") +call AddOption("matchpairs", gettext("list of pairs that match for the \"%\" command")) call append("$", "\t" .. s:local_to_buffer) call OptionL("mps") -call append("$", "joinspaces\tuse two spaces after '.' when joining a line") +call AddOption("joinspaces", gettext("use two spaces after '.' when joining a line")) call BinOptionG("js", &js) -call append("$", "nrformats\t\"alpha\", \"octal\" and/or \"hex\"; number formats recognized for") -call append("$", "\tCTRL-A and CTRL-X commands") +call AddOption("nrformats", gettext("\"alpha\", \"octal\" and/or \"hex\"; number formats recognized for\nCTRL-A and CTRL-X commands")) call append("$", "\t" .. s:local_to_buffer) call OptionL("nf") -call Header("tabs and indenting") -call append("$", "tabstop\tnumber of spaces a in the text stands for") +call Header(gettext("tabs and indenting")) +call AddOption("tabstop", gettext("number of spaces a in the text stands for")) call append("$", "\t" .. s:local_to_buffer) call OptionL("ts") -call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent") +call AddOption("shiftwidth", gettext("number of spaces used for each step of (auto)indent")) call append("$", "\t" .. s:local_to_buffer) call OptionL("sw") if has("vartabs") - call append("$", "vartabstop\tlist of number of spaces a tab counts for") + call AddOption("vartabstop", gettext("list of number of spaces a tab counts for")) call append("$", "\t" .. s:local_to_buffer) call OptionL("vts") - call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for") + call AddOption("varsofttabstop", gettext("list of number of spaces a soft tabsstop counts for")) call append("$", "\t" .. s:local_to_buffer) call OptionL("vsts") endif -call append("$", "smarttab\ta in an indent inserts 'shiftwidth' spaces") +call AddOption("smarttab", gettext("a in an indent inserts 'shiftwidth' spaces")) call BinOptionG("sta", &sta) -call append("$", "softtabstop\tif non-zero, number of spaces to insert for a ") +call AddOption("softtabstop", gettext("if non-zero, number of spaces to insert for a ")) call append("$", "\t" .. s:local_to_buffer) call OptionL("sts") -call append("$", "shiftround\tround to 'shiftwidth' for \"<<\" and \">>\"") +call AddOption("shiftround", gettext("round to 'shiftwidth' for \"<<\" and \">>\"")) call BinOptionG("sr", &sr) -call append("$", "expandtab\texpand to spaces in Insert mode") +call AddOption("expandtab", gettext("expand to spaces in Insert mode")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("et") -call append("$", "autoindent\tautomatically set the indent of a new line") +call AddOption("autoindent", gettext("automatically set the indent of a new line")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("ai") if has("smartindent") - call append("$", "smartindent\tdo clever autoindenting") + call AddOption("smartindent", gettext("do clever autoindenting")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("si") endif if has("cindent") - call append("$", "cindent\tenable specific indenting for C code") + call AddOption("cindent", gettext("enable specific indenting for C code")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("cin") - call append("$", "cinoptions\toptions for C-indenting") + call AddOption("cinoptions", gettext("options for C-indenting")) call append("$", "\t" .. s:local_to_buffer) call OptionL("cino") - call append("$", "cinkeys\tkeys that trigger C-indenting in Insert mode") + call AddOption("cinkeys", gettext("keys that trigger C-indenting in Insert mode")) call append("$", "\t" .. s:local_to_buffer) call OptionL("cink") - call append("$", "cinwords\tlist of words that cause more C-indent") + call AddOption("cinwords", gettext("list of words that cause more C-indent")) call append("$", "\t" .. s:local_to_buffer) call OptionL("cinw") - call append("$", "indentexpr\texpression used to obtain the indent of a line") + call AddOption("indentexpr", gettext("expression used to obtain the indent of a line")) call append("$", "\t" .. s:local_to_buffer) call OptionL("inde") - call append("$", "indentkeys\tkeys that trigger indenting with 'indentexpr' in Insert mode") + call AddOption("indentkeys", gettext("keys that trigger indenting with 'indentexpr' in Insert mode")) call append("$", "\t" .. s:local_to_buffer) call OptionL("indk") endif -call append("$", "copyindent\tcopy whitespace for indenting from previous line") +call AddOption("copyindent", gettext("copy whitespace for indenting from previous line")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("ci") -call append("$", "preserveindent\tpreserve kind of whitespace when changing indent") +call AddOption("preserveindent", gettext("preserve kind of whitespace when changing indent")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("pi") if has("lispindent") - call append("$", "lisp\tenable lisp mode") + call AddOption("lisp", gettext("enable lisp mode")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("lisp") - call append("$", "lispwords\twords that change how lisp indenting works") + call AddOption("lispwords", gettext("words that change how lisp indenting works")) call OptionL("lw") endif if has("folding") - call Header("folding") - call append("$", "foldenable\tset to display all folds open") + call Header(gettext("folding")) + call AddOption("foldenable", gettext("set to display all folds open")) call append("$", "\t" .. s:local_to_window) call BinOptionL("fen") - call append("$", "foldlevel\tfolds with a level higher than this number will be closed") + call AddOption("foldlevel", gettext("folds with a level higher than this number will be closed")) call append("$", "\t" .. s:local_to_window) call OptionL("fdl") - call append("$", "foldlevelstart\tvalue for 'foldlevel' when starting to edit a file") + call AddOption("foldlevelstart", gettext("value for 'foldlevel' when starting to edit a file")) call append("$", " \tset fdls=" . &fdls) - call append("$", "foldcolumn\twidth of the column used to indicate folds") + call AddOption("foldcolumn", gettext("width of the column used to indicate folds")) call append("$", "\t" .. s:local_to_window) call OptionL("fdc") - call append("$", "foldtext\texpression used to display the text of a closed fold") + call AddOption("foldtext", gettext("expression used to display the text of a closed fold")) call append("$", "\t" .. s:local_to_window) call OptionL("fdt") - call append("$", "foldclose\tset to \"all\" to close a fold when the cursor leaves it") + call AddOption("foldclose", gettext("set to \"all\" to close a fold when the cursor leaves it")) call OptionG("fcl", &fcl) - call append("$", "foldopen\tspecifies for which commands a fold will be opened") + call AddOption("foldopen", gettext("specifies for which commands a fold will be opened")) call OptionG("fdo", &fdo) - call append("$", "foldminlines\tminimum number of screen lines for a fold to be closed") + call AddOption("foldminlines", gettext("minimum number of screen lines for a fold to be closed")) call append("$", "\t" .. s:local_to_window) call OptionL("fml") - call append("$", "commentstring\ttemplate for comments; used to put the marker in") + call AddOption("commentstring", gettext("template for comments; used to put the marker in")) call OptionL("cms") - call append("$", "foldmethod\tfolding type: \"manual\", \"indent\", \"expr\", \"marker\" or \"syntax\"") + call AddOption("foldmethod", gettext("folding type: \"manual\", \"indent\", \"expr\", \"marker\" or \"syntax\"")) call append("$", "\t" .. s:local_to_window) call OptionL("fdm") - call append("$", "foldexpr\texpression used when 'foldmethod' is \"expr\"") + call AddOption("foldexpr", gettext("expression used when 'foldmethod' is \"expr\"")) call append("$", "\t" .. s:local_to_window) call OptionL("fde") - call append("$", "foldignore\tused to ignore lines when 'foldmethod' is \"indent\"") + call AddOption("foldignore", gettext("used to ignore lines when 'foldmethod' is \"indent\"")) call append("$", "\t" .. s:local_to_window) call OptionL("fdi") - call append("$", "foldmarker\tmarkers used when 'foldmethod' is \"marker\"") + call AddOption("foldmarker", gettext("markers used when 'foldmethod' is \"marker\"")) call append("$", "\t" .. s:local_to_window) call OptionL("fmr") - call append("$", "foldnestmax\tmaximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"") + call AddOption("foldnestmax", gettext("maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"")) call append("$", "\t" .. s:local_to_window) call OptionL("fdn") endif if has("diff") - call Header("diff mode") - call append("$", "diff\tuse diff mode for the current window") + call Header(gettext("diff mode")) + call AddOption("diff", gettext("use diff mode for the current window")) call append("$", "\t" .. s:local_to_window) call BinOptionL("diff") - call append("$", "diffopt\toptions for using diff mode") + call AddOption("diffopt", gettext("options for using diff mode")) call OptionG("dip", &dip) - call append("$", "diffexpr\texpression used to obtain a diff file") + call AddOption("diffexpr", gettext("expression used to obtain a diff file")) call OptionG("dex", &dex) - call append("$", "patchexpr\texpression used to patch a file") + call AddOption("patchexpr", gettext("expression used to patch a file")) call OptionG("pex", &pex) endif -call Header("mapping") -call append("$", "maxmapdepth\tmaximum depth of mapping") +call Header(gettext("mapping")) +call AddOption("maxmapdepth", gettext("maximum depth of mapping")) call append("$", " \tset mmd=" . &mmd) -call append("$", "remap\trecognize mappings in mapped keys") +call AddOption("remap", gettext("recognize mappings in mapped keys")) call BinOptionG("remap", &remap) -call append("$", "timeout\tallow timing out halfway into a mapping") +call AddOption("timeout", gettext("allow timing out halfway into a mapping")) call BinOptionG("to", &to) -call append("$", "ttimeout\tallow timing out halfway into a key code") +call AddOption("ttimeout", gettext("allow timing out halfway into a key code")) call BinOptionG("ttimeout", &ttimeout) -call append("$", "timeoutlen\ttime in msec for 'timeout'") +call AddOption("timeoutlen", gettext("time in msec for 'timeout'")) call append("$", " \tset tm=" . &tm) -call append("$", "ttimeoutlen\ttime in msec for 'ttimeout'") +call AddOption("ttimeoutlen", gettext("time in msec for 'ttimeout'")) call append("$", " \tset ttm=" . &ttm) -call Header("reading and writing files") -call append("$", "modeline\tenable using settings from modelines when reading a file") +call Header(gettext("reading and writing files")) +call AddOption("modeline", gettext("enable using settings from modelines when reading a file")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("ml") -call append("$", "modelineexpr\tallow setting expression options from a modeline") +call AddOption("modelineexpr", gettext("allow setting expression options from a modeline")) call BinOptionG("mle", &mle) -call append("$", "modelines\tnumber of lines to check for modelines") +call AddOption("modelines", gettext("number of lines to check for modelines")) call append("$", " \tset mls=" . &mls) -call append("$", "binary\tbinary file editing") +call AddOption("binary", gettext("binary file editing")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("bin") -call append("$", "endofline\tlast line in the file has an end-of-line") +call AddOption("endofline", gettext("last line in the file has an end-of-line")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("eol") -call append("$", "fixendofline\tfixes missing end-of-line at end of text file") +call AddOption("fixendofline", gettext("fixes missing end-of-line at end of text file")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("fixeol") -call append("$", "bomb\tprepend a Byte Order Mark to the file") +call AddOption("bomb", gettext("prepend a Byte Order Mark to the file")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("bomb") -call append("$", "fileformat\tend-of-line format: \"dos\", \"unix\" or \"mac\"") +call AddOption("fileformat", gettext("end-of-line format: \"dos\", \"unix\" or \"mac\"")) call append("$", "\t" .. s:local_to_buffer) call OptionL("ff") -call append("$", "fileformats\tlist of file formats to look for when editing a file") +call AddOption("fileformats", gettext("list of file formats to look for when editing a file")) call OptionG("ffs", &ffs) -call append("$", "textmode\tobsolete, use 'fileformat'") +call AddOption("textmode", gettext("obsolete, use 'fileformat'")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("tx") -call append("$", "textauto\tobsolete, use 'fileformats'") +call AddOption("textauto", gettext("obsolete, use 'fileformats'")) call BinOptionG("ta", &ta) -call append("$", "write\twriting files is allowed") +call AddOption("write", gettext("writing files is allowed")) call BinOptionG("write", &write) -call append("$", "writebackup\twrite a backup file before overwriting a file") +call AddOption("writebackup", gettext("write a backup file before overwriting a file")) call BinOptionG("wb", &wb) -call append("$", "backup\tkeep a backup after overwriting a file") +call AddOption("backup", gettext("keep a backup after overwriting a file")) call BinOptionG("bk", &bk) -call append("$", "backupskip\tpatterns that specify for which files a backup is not made") +call AddOption("backupskip", gettext("patterns that specify for which files a backup is not made")) call append("$", " \tset bsk=" . &bsk) -call append("$", "backupcopy\twhether to make the backup as a copy or rename the existing file") -call append("$", "\t(global or local to buffer)") +call AddOption("backupcopy", gettext("whether to make the backup as a copy or rename the existing file")) +call append("$", gettext("\t(global or local to buffer)")) call append("$", " \tset bkc=" . &bkc) -call append("$", "backupdir\tlist of directories to put backup files in") +call AddOption("backupdir", gettext("list of directories to put backup files in")) call OptionG("bdir", &bdir) -call append("$", "backupext\tfile name extension for the backup file") +call AddOption("backupext", gettext("file name extension for the backup file")) call OptionG("bex", &bex) -call append("$", "autowrite\tautomatically write a file when leaving a modified buffer") +call AddOption("autowrite", gettext("automatically write a file when leaving a modified buffer")) call BinOptionG("aw", &aw) -call append("$", "autowriteall\tas 'autowrite', but works with more commands") +call AddOption("autowriteall", gettext("as 'autowrite', but works with more commands")) call BinOptionG("awa", &awa) -call append("$", "writeany\talways write without asking for confirmation") +call AddOption("writeany", gettext("always write without asking for confirmation")) call BinOptionG("wa", &wa) -call append("$", "autoread\tautomatically read a file when it was modified outside of Vim") -call append("$", "\t(global or local to buffer)") +call AddOption("autoread", gettext("automatically read a file when it was modified outside of Vim")) +call append("$", gettext("\t(global or local to buffer)")) call BinOptionG("ar", &ar) -call append("$", "patchmode\tkeep oldest version of a file; specifies file name extension") +call AddOption("patchmode", gettext("keep oldest version of a file; specifies file name extension")) call OptionG("pm", &pm) -call append("$", "fsync\tforcibly sync the file to disk after writing it") +call AddOption("fsync", gettext("forcibly sync the file to disk after writing it")) call BinOptionG("fs", &fs) -call append("$", "shortname\tuse 8.3 file names") +call AddOption("shortname", gettext("use 8.3 file names")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("sn") -call append("$", "cryptmethod\tencryption method for file writing: zip or blowfish") +call AddOption("cryptmethod", gettext("encryption method for file writing: zip or blowfish")) call append("$", "\t" .. s:local_to_buffer) call OptionL("cm") -call Header("the swap file") -call append("$", "directory\tlist of directories for the swap file") +call Header(gettext("the swap file")) +call AddOption("directory", gettext("list of directories for the swap file")) call OptionG("dir", &dir) -call append("$", "swapfile\tuse a swap file for this buffer") +call AddOption("swapfile", gettext("use a swap file for this buffer")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("swf") -call append("$", "swapsync\t\"sync\", \"fsync\" or empty; how to flush a swap file to disk") +call AddOption("swapsync", gettext("\"sync\", \"fsync\" or empty; how to flush a swap file to disk")) call OptionG("sws", &sws) -call append("$", "updatecount\tnumber of characters typed to cause a swap file update") +call AddOption("updatecount", gettext("number of characters typed to cause a swap file update")) call append("$", " \tset uc=" . &uc) -call append("$", "updatetime\ttime in msec after which the swap file will be updated") +call AddOption("updatetime", gettext("time in msec after which the swap file will be updated")) call append("$", " \tset ut=" . &ut) -call append("$", "maxmem\tmaximum amount of memory in Kbyte used for one buffer") +call AddOption("maxmem", gettext("maximum amount of memory in Kbyte used for one buffer")) call append("$", " \tset mm=" . &mm) -call append("$", "maxmemtot\tmaximum amount of memory in Kbyte used for all buffers") +call AddOption("maxmemtot", gettext("maximum amount of memory in Kbyte used for all buffers")) call append("$", " \tset mmt=" . &mmt) -call Header("command line editing") -call append("$", "history\thow many command lines are remembered ") +call Header(gettext("command line editing")) +call AddOption("history", gettext("how many command lines are remembered ")) call append("$", " \tset hi=" . &hi) -call append("$", "wildchar\tkey that triggers command-line expansion") +call AddOption("wildchar", gettext("key that triggers command-line expansion")) call append("$", " \tset wc=" . &wc) -call append("$", "wildcharm\tlike 'wildchar' but can also be used in a mapping") +call AddOption("wildcharm", gettext("like 'wildchar' but can also be used in a mapping")) call append("$", " \tset wcm=" . &wcm) -call append("$", "wildmode\tspecifies how command line completion works") +call AddOption("wildmode", gettext("specifies how command line completion works")) call OptionG("wim", &wim) if has("wildoptions") - call append("$", "wildoptions\tempty or \"tagfile\" to list file name of matching tags") + call AddOption("wildoptions", gettext("empty or \"tagfile\" to list file name of matching tags")) call OptionG("wop", &wop) endif -call append("$", "suffixes\tlist of file name extensions that have a lower priority") +call AddOption("suffixes", gettext("list of file name extensions that have a lower priority")) call OptionG("su", &su) if has("file_in_path") - call append("$", "suffixesadd\tlist of file name extensions added when searching for a file") + call AddOption("suffixesadd", gettext("list of file name extensions added when searching for a file")) call append("$", "\t" .. s:local_to_buffer) call OptionL("sua") endif if has("wildignore") - call append("$", "wildignore\tlist of patterns to ignore files for file name completion") + call AddOption("wildignore", gettext("list of patterns to ignore files for file name completion")) call OptionG("wig", &wig) endif -call append("$", "fileignorecase\tignore case when using file names") +call AddOption("fileignorecase", gettext("ignore case when using file names")) call BinOptionG("fic", &fic) -call append("$", "wildignorecase\tignore case when completing file names") +call AddOption("wildignorecase", gettext("ignore case when completing file names")) call BinOptionG("wic", &wic) if has("wildmenu") - call append("$", "wildmenu\tcommand-line completion shows a list of matches") + call AddOption("wildmenu", gettext("command-line completion shows a list of matches")) call BinOptionG("wmnu", &wmnu) endif -call append("$", "cedit\tkey used to open the command-line window") +call AddOption("cedit", gettext("key used to open the command-line window")) call OptionG("cedit", &cedit) -call append("$", "cmdwinheight\theight of the command-line window") +call AddOption("cmdwinheight", gettext("height of the command-line window")) call OptionG("cwh", &cwh) -call Header("executing external commands") -call append("$", "shell\tname of the shell program used for external commands") +call Header(gettext("executing external commands")) +call AddOption("shell", gettext("name of the shell program used for external commands")) call OptionG("sh", &sh) if has("amiga") - call append("$", "shelltype\twhen to use the shell or directly execute a command") + call AddOption("shelltype", gettext("when to use the shell or directly execute a command")) call append("$", " \tset st=" . &st) endif -call append("$", "shellquote\tcharacter(s) to enclose a shell command in") +call AddOption("shellquote", gettext("character(s) to enclose a shell command in")) call OptionG("shq", &shq) -call append("$", "shellxquote\tlike 'shellquote' but include the redirection") +call AddOption("shellxquote", gettext("like 'shellquote' but include the redirection")) call OptionG("sxq", &sxq) -call append("$", "shellxescape\tcharacters to escape when 'shellxquote' is (") +call AddOption("shellxescape", gettext("characters to escape when 'shellxquote' is (")) call OptionG("sxe", &sxe) -call append("$", "shellcmdflag\targument for 'shell' to execute a command") +call AddOption("shellcmdflag", gettext("argument for 'shell' to execute a command")) call OptionG("shcf", &shcf) -call append("$", "shellredir\tused to redirect command output to a file") +call AddOption("shellredir", gettext("used to redirect command output to a file")) call OptionG("srr", &srr) -call append("$", "shelltemp\tuse a temp file for shell commands instead of using a pipe") +call AddOption("shelltemp", gettext("use a temp file for shell commands instead of using a pipe")) call BinOptionG("stmp", &stmp) -call append("$", "equalprg\tprogram used for \"=\" command") -call append("$", "\t(global or local to buffer)") +call AddOption("equalprg", gettext("program used for \"=\" command")) +call append("$", gettext("\t(global or local to buffer)")) call OptionG("ep", &ep) -call append("$", "formatprg\tprogram used to format lines with \"gq\" command") +call AddOption("formatprg", gettext("program used to format lines with \"gq\" command")) call OptionG("fp", &fp) -call append("$", "keywordprg\tprogram used for the \"K\" command") +call AddOption("keywordprg", gettext("program used for the \"K\" command")) call OptionG("kp", &kp) -call append("$", "warn\twarn when using a shell command and a buffer has changes") +call AddOption("warn", gettext("warn when using a shell command and a buffer has changes")) call BinOptionG("warn", &warn) if has("quickfix") - call Header("running make and jumping to errors (quickfix)") - call append("$", "errorfile\tname of the file that contains error messages") + call Header(gettext("running make and jumping to errors (quickfix)")) + call AddOption("errorfile", gettext("name of the file that contains error messages")) call OptionG("ef", &ef) - call append("$", "errorformat\tlist of formats for error messages") - call append("$", "\t(global or local to buffer)") + call AddOption("errorformat", gettext("list of formats for error messages")) + call append("$", gettext("\t(global or local to buffer)")) call OptionG("efm", &efm) - call append("$", "makeprg\tprogram used for the \":make\" command") - call append("$", "\t(global or local to buffer)") + call AddOption("makeprg", gettext("program used for the \":make\" command")) + call append("$", gettext("\t(global or local to buffer)")) call OptionG("mp", &mp) - call append("$", "shellpipe\tstring used to put the output of \":make\" in the error file") + call AddOption("shellpipe", gettext("string used to put the output of \":make\" in the error file")) call OptionG("sp", &sp) - call append("$", "makeef\tname of the errorfile for the 'makeprg' command") + call AddOption("makeef", gettext("name of the errorfile for the 'makeprg' command")) call OptionG("mef", &mef) - call append("$", "grepprg\tprogram used for the \":grep\" command") - call append("$", "\t(global or local to buffer)") + call AddOption("grepprg", gettext("program used for the \":grep\" command")) + call append("$", gettext("\t(global or local to buffer)")) call OptionG("gp", &gp) - call append("$", "grepformat\tlist of formats for output of 'grepprg'") + call AddOption("grepformat", gettext("list of formats for output of 'grepprg'")) call OptionG("gfm", &gfm) - call append("$", "makeencoding\tencoding of the \":make\" and \":grep\" output") - call append("$", "\t(global or local to buffer)") + call AddOption("makeencoding", gettext("encoding of the \":make\" and \":grep\" output")) + call append("$", gettext("\t(global or local to buffer)")) call OptionG("menc", &menc) - call append("$", "quickfixtextfunc\tfunction to display text in the quickfix window") + call AddOption("quickfixtextfunc", gettext("function to display text in the quickfix window")) call OptionG("qftf", &qftf) endif if has("win32") || has("osfiletype") - call Header("system specific") + call Header(gettext("system specific")) if has("osfiletype") - call append("$", "osfiletype\tOS-specific information about the type of file") + call AddOption("osfiletype", gettext("OS-specific information about the type of file")) call append("$", "\t" .. s:local_to_buffer) call OptionL("oft") endif if has("win32") - call append("$", "shellslash\tuse forward slashes in file names; for Unix-like shells") + call AddOption("shellslash", gettext("use forward slashes in file names; for Unix-like shells")) call BinOptionG("ssl", &ssl) - call append("$", "completeslash\tspecifies slash/backslash used for completion") + call AddOption("completeslash", gettext("specifies slash/backslash used for completion")) call OptionG("csl", &csl) endif endif -call Header("language specific") -call append("$", "isfname\tspecifies the characters in a file name") +call Header(gettext("language specific")) +call AddOption("isfname", gettext("specifies the characters in a file name")) call OptionG("isf", &isf) -call append("$", "isident\tspecifies the characters in an identifier") +call AddOption("isident", gettext("specifies the characters in an identifier")) call OptionG("isi", &isi) -call append("$", "iskeyword\tspecifies the characters in a keyword") +call AddOption("iskeyword", gettext("specifies the characters in a keyword")) call append("$", "\t" .. s:local_to_buffer) call OptionL("isk") -call append("$", "isprint\tspecifies printable characters") +call AddOption("isprint", gettext("specifies printable characters")) call OptionG("isp", &isp) if has("textobjects") - call append("$", "quoteescape\tspecifies escape characters in a string") + call AddOption("quoteescape", gettext("specifies escape characters in a string")) call append("$", "\t" .. s:local_to_buffer) call OptionL("qe") endif if has("rightleft") - call append("$", "rightleft\tdisplay the buffer right-to-left") + call AddOption("rightleft", gettext("display the buffer right-to-left")) call append("$", "\t" .. s:local_to_window) call BinOptionL("rl") - call append("$", "rightleftcmd\twhen to edit the command-line right-to-left") + call AddOption("rightleftcmd", gettext("when to edit the command-line right-to-left")) call append("$", "\t" .. s:local_to_window) call OptionL("rlc") - call append("$", "revins\tinsert characters backwards") + call AddOption("revins", gettext("insert characters backwards")) call BinOptionG("ri", &ri) - call append("$", "allowrevins\tallow CTRL-_ in Insert and Command-line mode to toggle 'revins'") + call AddOption("allowrevins", gettext("allow CTRL-_ in Insert and Command-line mode to toggle 'revins'")) call BinOptionG("ari", &ari) - call append("$", "aleph\tthe ASCII code for the first letter of the Hebrew alphabet") + call AddOption("aleph", gettext("the ASCII code for the first letter of the Hebrew alphabet")) call append("$", " \tset al=" . &al) - call append("$", "hkmap\tuse Hebrew keyboard mapping") + call AddOption("hkmap", gettext("use Hebrew keyboard mapping")) call BinOptionG("hk", &hk) - call append("$", "hkmapp\tuse phonetic Hebrew keyboard mapping") + call AddOption("hkmapp", gettext("use phonetic Hebrew keyboard mapping")) call BinOptionG("hkp", &hkp) endif if has("farsi") - call append("$", "altkeymap\tuse Farsi as the second language when 'revins' is set") + call AddOption("altkeymap", gettext("use Farsi as the second language when 'revins' is set")) call BinOptionG("akm", &akm) - call append("$", "fkmap\tuse Farsi keyboard mapping") + call AddOption("fkmap", gettext("use Farsi keyboard mapping")) call BinOptionG("fk", &fk) endif if has("arabic") - call append("$", "arabic\tprepare for editing Arabic text") + call AddOption("arabic", gettext("prepare for editing Arabic text")) call append("$", "\t" .. s:local_to_window) call BinOptionL("arab") - call append("$", "arabicshape\tperform shaping of Arabic characters") + call AddOption("arabicshape", gettext("perform shaping of Arabic characters")) call BinOptionG("arshape", &arshape) - call append("$", "termbidi\tterminal will perform bidi handling") + call AddOption("termbidi", gettext("terminal will perform bidi handling")) call BinOptionG("tbidi", &tbidi) endif if has("keymap") - call append("$", "keymap\tname of a keyboard mapping") + call AddOption("keymap", gettext("name of a keyboard mapping")) call OptionL("kmp") endif if has("langmap") - call append("$", "langmap\tlist of characters that are translated in Normal mode") + call AddOption("langmap", gettext("list of characters that are translated in Normal mode")) call OptionG("lmap", &lmap) - call append("$", "langremap\tapply 'langmap' to mapped characters") + call AddOption("langremap", gettext("apply 'langmap' to mapped characters")) call BinOptionG("lrm", &lrm) endif if has("xim") - call append("$", "imdisable\twhen set never use IM; overrules following IM options") + call AddOption("imdisable", gettext("when set never use IM; overrules following IM options")) call BinOptionG("imd", &imd) endif -call append("$", "iminsert\tin Insert mode: 1: use :lmap; 2: use IM; 0: neither") +call AddOption("iminsert", gettext("in Insert mode: 1: use :lmap; 2: use IM; 0: neither")) call append("$", "\t" .. s:local_to_window) call OptionL("imi") -call append("$", "imstyle\tinput method style, 0: on-the-spot, 1: over-the-spot") +call AddOption("imstyle", gettext("input method style, 0: on-the-spot, 1: over-the-spot")) call OptionG("imst", &imst) -call append("$", "imsearch\tentering a search pattern: 1: use :lmap; 2: use IM; 0: neither") +call AddOption("imsearch", gettext("entering a search pattern: 1: use :lmap; 2: use IM; 0: neither")) call append("$", "\t" .. s:local_to_window) call OptionL("ims") if has("xim") - call append("$", "imcmdline\twhen set always use IM when starting to edit a command line") + call AddOption("imcmdline", gettext("when set always use IM when starting to edit a command line")) call BinOptionG("imc", &imc) - call append("$", "imstatusfunc\tfunction to obtain IME status") + call AddOption("imstatusfunc", gettext("function to obtain IME status")) call OptionG("imsf", &imsf) - call append("$", "imactivatefunc\tfunction to enable/disable IME") + call AddOption("imactivatefunc", gettext("function to enable/disable IME")) call OptionG("imaf", &imaf) endif -call Header("multi-byte characters") -call append("$", "encoding\tcharacter encoding used in Vim: \"latin1\", \"utf-8\"") -call append("$", "\t\"euc-jp\", \"big5\", etc.") +call Header(gettext("multi-byte characters")) +call AddOption("encoding", gettext("character encoding used in Vim: \"latin1\", \"utf-8\"\n\"euc-jp\", \"big5\", etc.")) call OptionG("enc", &enc) -call append("$", "fileencoding\tcharacter encoding for the current file") +call AddOption("fileencoding", gettext("character encoding for the current file")) call append("$", "\t" .. s:local_to_buffer) call OptionL("fenc") -call append("$", "fileencodings\tautomatically detected character encodings") +call AddOption("fileencodings", gettext("automatically detected character encodings")) call OptionG("fencs", &fencs) -call append("$", "termencoding\tcharacter encoding used by the terminal") +call AddOption("termencoding", gettext("character encoding used by the terminal")) call OptionG("tenc", &tenc) -call append("$", "charconvert\texpression used for character encoding conversion") +call AddOption("charconvert", gettext("expression used for character encoding conversion")) call OptionG("ccv", &ccv) -call append("$", "delcombine\tdelete combining (composing) characters on their own") +call AddOption("delcombine", gettext("delete combining (composing) characters on their own")) call BinOptionG("deco", &deco) -call append("$", "maxcombine\tmaximum number of combining (composing) characters displayed") +call AddOption("maxcombine", gettext("maximum number of combining (composing) characters displayed")) call OptionG("mco", &mco) if has("xim") && has("gui_gtk") - call append("$", "imactivatekey\tkey that activates the X input method") + call AddOption("imactivatekey", gettext("key that activates the X input method")) call OptionG("imak", &imak) endif -call append("$", "ambiwidth\twidth of ambiguous width characters") +call AddOption("ambiwidth", gettext("width of ambiguous width characters")) call OptionG("ambw", &ambw) -call append("$", "emoji\temoji characters are full width") +call AddOption("emoji", gettext("emoji characters are full width")) call BinOptionG("emo", &emo) -call Header("various") -call append("$", "virtualedit\twhen to use virtual editing: \"block\", \"insert\" and/or \"all\"") +call Header(gettext("various")) +call AddOption("virtualedit", gettext("when to use virtual editing: \"block\", \"insert\" and/or \"all\"")) call OptionG("ve", &ve) -call append("$", "eventignore\tlist of autocommand events which are to be ignored") +call AddOption("eventignore", gettext("list of autocommand events which are to be ignored")) call OptionG("ei", &ei) -call append("$", "loadplugins\tload plugin scripts when starting up") +call AddOption("loadplugins", gettext("load plugin scripts when starting up")) call BinOptionG("lpl", &lpl) -call append("$", "exrc\tenable reading .vimrc/.exrc/.gvimrc in the current directory") +call AddOption("exrc", gettext("enable reading .vimrc/.exrc/.gvimrc in the current directory")) call BinOptionG("ex", &ex) -call append("$", "secure\tsafer working with script files in the current directory") +call AddOption("secure", gettext("safer working with script files in the current directory")) call BinOptionG("secure", &secure) -call append("$", "gdefault\tuse the 'g' flag for \":substitute\"") +call AddOption("gdefault", gettext("use the 'g' flag for \":substitute\"")) call BinOptionG("gd", &gd) -call append("$", "edcompatible\t'g' and 'c' flags of \":substitute\" toggle") +call AddOption("edcompatible", gettext("'g' and 'c' flags of \":substitute\" toggle")) call BinOptionG("ed", &ed) if exists("+opendevice") - call append("$", "opendevice\tallow reading/writing devices") + call AddOption("opendevice", gettext("allow reading/writing devices")) call BinOptionG("odev", &odev) endif if exists("+maxfuncdepth") - call append("$", "maxfuncdepth\tmaximum depth of function calls") + call AddOption("maxfuncdepth", gettext("maximum depth of function calls")) call append("$", " \tset mfd=" . &mfd) endif if has("mksession") - call append("$", "sessionoptions\tlist of words that specifies what to put in a session file") + call AddOption("sessionoptions", gettext("list of words that specifies what to put in a session file")) call OptionG("ssop", &ssop) - call append("$", "viewoptions\tlist of words that specifies what to save for :mkview") + call AddOption("viewoptions", gettext("list of words that specifies what to save for :mkview")) call OptionG("vop", &vop) - call append("$", "viewdir\tdirectory where to store files with :mkview") + call AddOption("viewdir", gettext("directory where to store files with :mkview")) call OptionG("vdir", &vdir) endif if has("viminfo") - call append("$", "viminfo\tlist that specifies what to write in the viminfo file") + call AddOption("viminfo", gettext("list that specifies what to write in the viminfo file")) call OptionG("vi", &vi) - call append("$", "viminfofile\tfile name used for the viminfo file") + call AddOption("viminfofile", gettext("file name used for the viminfo file")) call OptionG("vif", &vif) endif if has("quickfix") - call append("$", "bufhidden\twhat happens with a buffer when it's no longer in a window") + call AddOption("bufhidden", gettext("what happens with a buffer when it's no longer in a window")) call append("$", "\t" .. s:local_to_buffer) call OptionL("bh") - call append("$", "buftype\t\"\", \"nofile\", \"nowrite\" or \"quickfix\": type of buffer") + call AddOption("buftype", gettext("\"\", \"nofile\", \"nowrite\" or \"quickfix\": type of buffer")) call append("$", "\t" .. s:local_to_buffer) call OptionL("bt") endif -call append("$", "buflisted\twhether the buffer shows up in the buffer list") +call AddOption("buflisted", gettext("whether the buffer shows up in the buffer list")) call append("$", "\t" .. s:local_to_buffer) call BinOptionL("bl") -call append("$", "debug\tset to \"msg\" to see all error messages") +call AddOption("debug", gettext("set to \"msg\" to see all error messages")) call append("$", " \tset debug=" . &debug) if has("signs") - call append("$", "signcolumn\twhether to show the signcolumn") + call AddOption("signcolumn", gettext("whether to show the signcolumn")) call append("$", "\t" .. s:local_to_window) call OptionL("scl") endif if has("mzscheme") - call append("$", "mzquantum\tinterval in milliseconds between polls for MzScheme threads") + call AddOption("mzquantum", gettext("interval in milliseconds between polls for MzScheme threads")) call append("$", " \tset mzq=" . &mzq) endif if exists("&luadll") - call append("$", "luadll\tname of the Lua dynamic library") + call AddOption("luadll", gettext("name of the Lua dynamic library")) call OptionG("luadll", &luadll) endif if exists("&perldll") - call append("$", "perldll\tname of the Perl dynamic library") + call AddOption("perldll", gettext("name of the Perl dynamic library")) call OptionG("perldll", &perldll) endif if has('pythonx') - call append("$", "pyxversion\twhether to use Python 2 or 3") + call AddOption("pyxversion", gettext("whether to use Python 2 or 3")) call append("$", " \tset pyx=" . &wd) endif if exists("&pythondll") - call append("$", "pythondll\tname of the Python 2 dynamic library") + call AddOption("pythondll", gettext("name of the Python 2 dynamic library")) call OptionG("pythondll", &pythondll) endif if exists("&pythonhome") - call append("$", "pythonhome\tname of the Python 2 home directory") + call AddOption("pythonhome", gettext("name of the Python 2 home directory")) call OptionG("pythonhome", &pythonhome) endif if exists("&pythonthreedll") - call append("$", "pythonthreedll\tname of the Python 3 dynamic library") + call AddOption("pythonthreedll", gettext("name of the Python 3 dynamic library")) call OptionG("pythonthreedll", &pythonthreedll) endif if exists("&pythonthreehome") - call append("$", "pythonthreehome\tname of the Python 3 home directory") + call AddOption("pythonthreehome", gettext("name of the Python 3 home directory")) call OptionG("pythonthreehome", &pythonthreehome) endif if exists("&rubydll") - call append("$", "rubydll\tname of the Ruby dynamic library") + call AddOption("rubydll", gettext("name of the Ruby dynamic library")) call OptionG("rubydll", &rubydll) endif if exists("&tcldll") - call append("$", "tcldll\tname of the Tcl dynamic library") + call AddOption("tcldll", gettext("name of the Tcl dynamic library")) call OptionG("tcldll", &tcldll) endif if exists("&mzschemedll") - call append("$", "mzschemedll\tname of the Tcl dynamic library") + call AddOption("mzschemedll", gettext("name of the Tcl dynamic library")) call OptionG("mzschemedll", &mzschemedll) - call append("$", "mzschemegcdll\tname of the Tcl GC dynamic library") + call AddOption("mzschemegcdll", gettext("name of the Tcl GC dynamic library")) call OptionG("mzschemegcdll", &mzschemegcdll) endif @@ -1465,7 +1465,7 @@ augroup optwin \ call unload() | delfun unload augroup END -fun! unload() +func unload() delfun CR delfun Space delfun Find @@ -1476,7 +1476,7 @@ fun! unload() delfun BinOptionG delfun Header au! optwin -endfun +endfunc " Restore the previous value of 'title' and 'icon'. let &title = s:old_title diff --git a/src/change.c b/src/change.c index f7e8f35ad8..9fc5b06ea5 100644 --- a/src/change.c +++ b/src/change.c @@ -693,7 +693,7 @@ changed_bytes(linenr_T lnum, colnr_T col) * Like changed_bytes() but also adjust text properties for "added" bytes. * When "added" is negative text was deleted. */ - static void + void inserted_bytes(linenr_T lnum, colnr_T col, int added UNUSED) { #ifdef FEAT_PROP_POPUP diff --git a/src/channel.c b/src/channel.c index 2d6f2f2523..eeb8632a08 100644 --- a/src/channel.c +++ b/src/channel.c @@ -996,8 +996,8 @@ channel_open( CLEAR_FIELD(hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; -# ifdef AI_ADDRCONFIG - hints.ai_flags = AI_ADDRCONFIG; +# if defined(AI_ADDRCONFIG) && defined(AI_V4MAPPED) + hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; # endif // Set port number manually in order to prevent name resolution services // from being invoked in the environment where AI_NUMERICSERV is not @@ -2645,9 +2645,7 @@ invoke_one_time_callback( static void append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part) { - bufref_T save_curbuf = {NULL, 0, 0}; - win_T *save_curwin = NULL; - tabpage_T *save_curtab = NULL; + aco_save_T aco; linenr_T lnum = buffer->b_ml.ml_line_count; int save_write_to = buffer->b_write_to_channel; chanpart_T *ch_part = &channel->ch_part[part]; @@ -2673,12 +2671,13 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part) } // Append to the buffer - ch_log(channel, "appending line %d to buffer", (int)lnum + 1 - empty); + ch_log(channel, "appending line %d to buffer %s", + (int)lnum + 1 - empty, buffer->b_fname); buffer->b_p_ma = TRUE; - // Save curbuf/curwin/curtab and make "buffer" the current buffer. - switch_to_win_for_buf(buffer, &save_curwin, &save_curtab, &save_curbuf); + // set curbuf to be our buf, temporarily + aucmd_prepbuf(&aco, buffer); u_sync(TRUE); // ignore undo failure, undo is not very useful here @@ -2694,8 +2693,8 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part) ml_append(lnum, msg, 0, FALSE); appended_lines_mark(lnum, 1L); - // Restore curbuf/curwin/curtab - restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); + // reset notion of buffer + aucmd_restbuf(&aco); if (ch_part->ch_nomodifiable) buffer->b_p_ma = FALSE; @@ -2719,9 +2718,10 @@ append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, ch_part_T part) // down. If the topline is outdated update it now. if (move_cursor || wp->w_topline > buffer->b_ml.ml_line_count) { + win_T *save_curwin = curwin; + if (move_cursor) ++wp->w_cursor.lnum; - save_curwin = curwin; curwin = wp; curbuf = curwin->w_buffer; scroll_cursor_bot(0, FALSE); diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 46e383cea6..37bd39fe44 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -992,7 +992,7 @@ set_one_cmd_context( } // 3. Skip over the range to find the command. - cmd = skip_range(cmd, &xp->xp_context); + cmd = skip_range(cmd, TRUE, &xp->xp_context); xp->xp_pattern = cmd; if (*cmd == NUL) return NULL; diff --git a/src/debugger.c b/src/debugger.c index 00fb9c8f8b..f745761f10 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -105,7 +105,7 @@ do_debug(char_u *cmd) vim_free(debug_newval); debug_newval = NULL; } - sname = estack_sfile(FALSE); + sname = estack_sfile(ESTACK_NONE); if (sname != NULL) msg((char *)sname); vim_free(sname); @@ -344,7 +344,7 @@ do_checkbacktracelevel(void) } else { - char_u *sname = estack_sfile(FALSE); + char_u *sname = estack_sfile(ESTACK_NONE); int max = get_maxbacktrace_level(sname); if (debug_backtrace_level > max) @@ -365,7 +365,7 @@ do_showbacktrace(char_u *cmd) int i = 0; int max; - sname = estack_sfile(FALSE); + sname = estack_sfile(ESTACK_NONE); max = get_maxbacktrace_level(sname); if (sname != NULL) { diff --git a/src/dict.c b/src/dict.c index 73190c4514..05281cb70d 100644 --- a/src/dict.c +++ b/src/dict.c @@ -1009,7 +1009,7 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action) } else if (*action == 'f' && HI2DI(hi2) != di1) { - if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) + if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) break; clear_tv(&di1->di_tv); @@ -1227,7 +1227,7 @@ dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) if (argvars[2].v_type != VAR_UNKNOWN) semsg(_(e_toomanyarg), "remove()"); else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) + && !value_check_lock(d->dv_lock, arg_errmsg, TRUE)) { key = tv_get_string_chk(&argvars[1]); if (key != NULL) diff --git a/src/drawline.c b/src/drawline.c index d2c9b24b6d..e3fce040bd 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -2779,8 +2779,12 @@ win_line( // highlight the cursor position itself. // Also highlight the 'colorcolumn' if it is different than // 'cursorcolumn' + // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak' + // options are set vcol_save_attr = -1; - if (draw_state == WL_LINE && !lnum_in_visual_area + if ((draw_state == WL_LINE || + draw_state == WL_BRI || + draw_state == WL_SBR) && !lnum_in_visual_area && search_attr == 0 && area_attr == 0) { if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol diff --git a/src/errors.h b/src/errors.h index 659619944d..d12e97f6f3 100644 --- a/src/errors.h +++ b/src/errors.h @@ -22,27 +22,27 @@ EXTERN char e_invalid_command[] EXTERN char e_invalid_command_str[] INIT(= N_("E476: Invalid command: %s")); EXTERN char e_cannot_slice_dictionary[] - INIT(= N_("E719: cannot slice a Dictionary")); + INIT(= N_("E719: Cannot slice a Dictionary")); EXTERN char e_assert_fails_second_arg[] - INIT(= N_("E856: assert_fails() second argument must be a string or a list with one or two strings")); + INIT(= N_("E856: \"assert_fails()\" second argument must be a string or a list with one or two strings")); EXTERN char e_cannot_index_special_variable[] INIT(= N_("E909: Cannot index a special variable")); EXTERN char e_missing_let_str[] INIT(= N_("E1100: Missing :let: %s")); EXTERN char e_variable_not_found_str[] - INIT(= N_("E1001: variable not found: %s")); + INIT(= N_("E1001: Variable not found: %s")); EXTERN char e_syntax_error_at_str[] INIT(= N_("E1002: Syntax error at %s")); EXTERN char e_missing_return_value[] INIT(= N_("E1003: Missing return value")); EXTERN char e_white_space_required_before_and_after_str[] - INIT(= N_("E1004: white space required before and after '%s'")); + INIT(= N_("E1004: White space required before and after '%s'")); EXTERN char e_too_many_argument_types[] INIT(= N_("E1005: Too many argument types")); EXTERN char e_str_is_used_as_argument[] INIT(= N_("E1006: %s is used as an argument")); EXTERN char e_mandatory_argument_after_optional_argument[] - INIT(= N_("E1007: mandatory argument after optional argument")); + INIT(= N_("E1007: Mandatory argument after optional argument")); EXTERN char e_missing_type[] INIT(= N_("E1008: Missing ")); EXTERN char e_missing_gt_after_type[] @@ -50,11 +50,11 @@ EXTERN char e_missing_gt_after_type[] EXTERN char e_type_not_recognized_str[] INIT(= N_("E1010: Type not recognized: %s")); EXTERN char e_name_too_long_str[] - INIT(= N_("E1011: name too long: %s")); + INIT(= N_("E1011: Name too long: %s")); EXTERN char e_type_mismatch_expected_str_but_got_str[] - INIT(= N_("E1012: type mismatch, expected %s but got %s")); + INIT(= N_("E1012: Type mismatch; expected %s but got %s")); EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[] - INIT(= N_("E1013: argument %d: type mismatch, expected %s but got %s")); + INIT(= N_("E1013: Argument %d: type mismatch, expected %s but got %s")); EXTERN char e_invalid_key_str[] INIT(= N_("E1014: Invalid key: %s")); EXTERN char e_name_expected[] @@ -70,23 +70,23 @@ EXTERN char e_cannot_assign_to_constant[] EXTERN char e_can_only_concatenate_to_string[] INIT(= N_("E1019: Can only concatenate to string")); EXTERN char e_cannot_use_operator_on_new_variable[] - INIT(= N_("E1020: cannot use an operator on a new variable: %s")); + INIT(= N_("E1020: Cannot use an operator on a new variable: %s")); EXTERN char e_const_requires_a_value[] - INIT(= N_("E1021: const requires a value")); + INIT(= N_("E1021: Const requires a value")); EXTERN char e_type_or_initialization_required[] - INIT(= N_("E1022: type or initialization required")); + INIT(= N_("E1022: Type or initialization required")); EXTERN char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d")); EXTERN char e_using_number_as_string[] INIT(= N_("E1024: Using a Number as a String")); EXTERN char e_using_rcurly_outside_if_block_scope[] - INIT(= N_("E1025: using } outside of a block scope")); + INIT(= N_("E1025: Using } outside of a block scope")); EXTERN char e_missing_rcurly[] INIT(= N_("E1026: Missing }")); EXTERN char e_missing_return_statement[] INIT(= N_("E1027: Missing return statement")); EXTERN char e_compile_def_function_failed[] - INIT(= N_("E1028: compile_def_function failed")); + INIT(= N_("E1028: Compiling :def function failed")); EXTERN char e_expected_str_but_got_str[] INIT(= N_("E1029: Expected %s but got %s")); EXTERN char e_using_string_as_number[] @@ -94,9 +94,9 @@ EXTERN char e_using_string_as_number[] EXTERN char e_cannot_use_void_value[] INIT(= N_("E1031: Cannot use void value")); EXTERN char e_missing_catch_or_finally[] - INIT(= N_("E1032: missing :catch or :finally")); + INIT(= N_("E1032: Missing :catch or :finally")); EXTERN char e_catch_unreachable_after_catch_all[] - INIT(= N_("E1033: catch unreachable after catch-all")); + INIT(= N_("E1033: Catch unreachable after catch-all")); EXTERN char e_cannot_use_reserved_name[] INIT(= N_("E1034: Cannot use reserved name %s")); EXTERN char e_percent_requires_number_arguments[] @@ -106,25 +106,25 @@ EXTERN char e_char_requires_number_or_float_arguments[] EXTERN char e_cannot_use_str_with_str[] INIT(= N_("E1037: Cannot use \"%s\" with %s")); EXTERN char e_vim9script_can_only_be_used_in_script[] - INIT(= N_("E1038: vim9script can only be used in a script")); + INIT(= N_("E1038: \"vim9script\" can only be used in a script")); EXTERN char e_vim9script_must_be_first_command_in_script[] - INIT(= N_("E1039: vim9script must be the first command in a script")); + INIT(= N_("E1039: \"vim9script\" must be the first command in a script")); EXTERN char e_cannot_use_scriptversion_after_vim9script[] INIT(= N_("E1040: Cannot use :scriptversion after :vim9script")); EXTERN char e_redefining_script_item_str[] INIT(= N_("E1041: Redefining script item %s")); EXTERN char e_export_can_only_be_used_in_vim9script[] - INIT(= N_("E1042: export can only be used in vim9script")); + INIT(= N_("E1042: Export can only be used in vim9script")); EXTERN char e_invalid_command_after_export[] INIT(= N_("E1043: Invalid command after :export")); EXTERN char e_export_with_invalid_argument[] - INIT(= N_("E1044: export with invalid argument")); + INIT(= N_("E1044: Export with invalid argument")); EXTERN char e_missing_as_after_star[] INIT(= N_("E1045: Missing \"as\" after *")); EXTERN char e_missing_comma_in_import[] INIT(= N_("E1046: Missing comma in import")); EXTERN char e_syntax_error_in_import[] - INIT(= N_("E1047: syntax error in import")); + INIT(= N_("E1047: Syntax error in import")); EXTERN char e_item_not_found_in_script_str[] INIT(= N_("E1048: Item not found in script: %s")); EXTERN char e_item_not_exported_in_script_str[] @@ -132,7 +132,7 @@ EXTERN char e_item_not_exported_in_script_str[] EXTERN char e_colon_required_before_a_range[] INIT(= N_("E1050: Colon required before a range")); EXTERN char e_wrong_argument_type_for_plus[] - INIT(= N_("E1051: wrong argument type for +")); + INIT(= N_("E1051: Wrong argument type for +")); EXTERN char e_cannot_declare_an_option[] INIT(= N_("E1052: Cannot declare an option: %s")); EXTERN char e_could_not_import_str[] @@ -142,21 +142,21 @@ EXTERN char e_variable_already_declared_in_script[] EXTERN char e_missing_name_after_dots[] INIT(= N_("E1055: Missing name after ...")); EXTERN char e_expected_type_str[] - INIT(= N_("E1056: expected a type: %s")); + INIT(= N_("E1056: Expected a type: %s")); EXTERN char e_missing_enddef[] INIT(= N_("E1057: Missing :enddef")); EXTERN char e_function_nesting_too_deep[] - INIT(= N_("E1058: function nesting too deep")); + INIT(= N_("E1058: Function nesting too deep")); EXTERN char e_no_white_space_allowed_before_colon_str[] INIT(= N_("E1059: No white space allowed before colon: %s")); EXTERN char e_expected_dot_after_name_str[] - INIT(= N_("E1060: expected dot after name: %s")); + INIT(= N_("E1060: Expected dot after name: %s")); EXTERN char e_cannot_find_function_str[] INIT(= N_("E1061: Cannot find function %s")); EXTERN char e_cannot_index_number[] INIT(= N_("E1062: Cannot index a Number")); EXTERN char e_type_mismatch_for_v_variable[] - INIT(= N_("E1063: type mismatch for v: variable")); + INIT(= N_("E1063: Type mismatch for v: variable")); // E1064 unused // E1065 unused EXTERN char e_cannot_declare_a_register_str[] @@ -166,7 +166,7 @@ EXTERN char e_separator_mismatch_str[] EXTERN char e_no_white_space_allowed_before_str[] INIT(= N_("E1068: No white space allowed before '%s'")); EXTERN char e_white_space_required_after_str[] - INIT(= N_("E1069: white space required after '%s'")); + INIT(= N_("E1069: White space required after '%s'")); EXTERN char e_missing_from[] INIT(= N_("E1070: Missing \"from\"")); EXTERN char e_invalid_string_after_from[] @@ -174,9 +174,9 @@ EXTERN char e_invalid_string_after_from[] EXTERN char e_cannot_compare_str_with_str[] INIT(= N_("E1072: Cannot compare %s with %s")); EXTERN char e_name_already_defined_str[] - INIT(= N_("E1073: name already defined: %s")); + INIT(= N_("E1073: Name already defined: %s")); EXTERN char e_no_white_space_allowed_after_dot[] - INIT(= N_("E1074: no white space allowed after dot")); + INIT(= N_("E1074: No white space allowed after dot")); EXTERN char e_namespace_not_supported_str[] INIT(= N_("E1075: Namespace not supported: %s")); EXTERN char e_this_vim_is_not_compiled_with_float_support[] @@ -191,7 +191,7 @@ EXTERN char e_cannot_unlet_str[] EXTERN char e_cannot_use_namespaced_variable[] INIT(= N_("E1082: Cannot use a namespaced variable: %s")); EXTERN char e_missing_backtick[] - INIT(= N_("E1083: missing backtick")); + INIT(= N_("E1083: Missing backtick")); EXTERN char e_cannot_delete_vim9_script_function_str[] INIT(= N_("E1084: Cannot delete Vim9 script function %s")); EXTERN char e_not_callable_type_str[] @@ -199,10 +199,10 @@ EXTERN char e_not_callable_type_str[] EXTERN char e_cannot_use_function_inside_def[] INIT(= N_("E1086: Cannot use :function inside :def")); EXTERN char e_cannot_use_index_when_declaring_variable[] - INIT(= N_("E1087: cannot use an index when declaring a variable")); + INIT(= N_("E1087: Cannot use an index when declaring a variable")); // E1088 unused EXTERN char e_unknown_variable_str[] - INIT(= N_("E1089: unknown variable: %s")); + INIT(= N_("E1089: Unknown variable: %s")); EXTERN char e_cannot_assign_to_argument[] INIT(= N_("E1090: Cannot assign to argument %s")); EXTERN char e_function_is_not_compiled_str[] @@ -212,20 +212,20 @@ EXTERN char e_cannot_use_list_for_declaration[] EXTERN char e_expected_nr_items_but_got_nr[] INIT(= N_("E1093: Expected %d items but got %d")); EXTERN char e_import_can_only_be_used_in_script[] - INIT(= N_("E1094: import can only be used in a script")); + INIT(= N_("E1094: Import can only be used in a script")); EXTERN char e_unreachable_code_after_return[] INIT(= N_("E1095: Unreachable code after :return")); EXTERN char e_returning_value_in_function_without_return_type[] INIT(= N_("E1096: Returning a value in a function without a return type")); EXTERN char e_line_incomplete[] - INIT(= N_("E1097: line incomplete")); + INIT(= N_("E1097: Line incomplete")); // E1098 unused EXTERN char e_unknown_error_while_executing_str[] INIT(= N_("E1099: Unknown error while executing %s")); EXTERN char e_cannot_declare_script_variable_in_function[] INIT(= N_("E1101: Cannot declare a script variable in a function: %s")); EXTERN char e_lambda_function_not_found_str[] - INIT(= N_("E1102: lambda function not found: %s")); + INIT(= N_("E1102: Lambda function not found: %s")); EXTERN char e_dictionary_not_set[] INIT(= N_("E1103: Dictionary not set")); EXTERN char e_missing_gt[] @@ -233,7 +233,7 @@ EXTERN char e_missing_gt[] EXTERN char e_cannot_convert_str_to_string[] INIT(= N_("E1105: Cannot convert %s to string")); EXTERN char e_one_argument_too_many[] - INIT(= N_("E1106: one argument too many")); + INIT(= N_("E1106: One argument too many")); EXTERN char e_nr_arguments_too_many[] INIT(= N_("E1106: %d arguments too many")); EXTERN char e_string_list_dict_or_blob_required[] @@ -253,7 +253,19 @@ EXTERN char e_overlapping_ranges_for_nr[] EXTERN char e_only_values_of_0x100_and_higher_supported[] INIT(= N_("E1114: Only values of 0x100 and higher supported")); EXTERN char e_assert_fails_fourth_argument[] - INIT(= N_("E1115: assert_fails() fourth argument must be a number")); + INIT(= N_("E1115: \"assert_fails()\" fourth argument must be a number")); EXTERN char e_assert_fails_fifth_argument[] - INIT(= N_("E1116: assert_fails() fifth argument must be a string")); + INIT(= N_("E1116: \"assert_fails()\" fifth argument must be a string")); +EXTERN char e_cannot_use_bang_with_nested_def[] + INIT(= N_("E1117: Cannot use ! with nested :def")); +EXTERN char e_cannot_change_list[] + INIT(= N_("E1118: Cannot change list")); +EXTERN char e_cannot_change_list_item[] + INIT(= N_("E1119: Cannot change list item")); +EXTERN char e_cannot_change_dict[] + INIT(= N_("E1120: Cannot change dict")); +EXTERN char e_cannot_change_dict_item[] + INIT(= N_("E1121: Cannot change dict item")); +EXTERN char e_variable_is_locked_str[] + INIT(= N_("E1122: Variable is locked: %s")); #endif diff --git a/src/eval.c b/src/eval.c index 6283fdf140..9b9b7f47ca 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1055,7 +1055,8 @@ get_lval( } // existing variable, need to check if it can be changed else if ((flags & GLV_READ_ONLY) == 0 - && var_check_ro(lp->ll_di->di_flags, name, FALSE)) + && (var_check_ro(lp->ll_di->di_flags, name, FALSE) + || var_check_lock(lp->ll_di->di_flags, name, FALSE))) { clear_tv(&var1); return NULL; @@ -1200,7 +1201,7 @@ set_var_lval( char_u *endp, typval_T *rettv, int copy, - int flags, // LET_IS_CONST and/or LET_NO_COMMAND + int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND char_u *op) { int cc; @@ -1220,7 +1221,7 @@ set_var_lval( semsg(_(e_letwrong), op); return; } - if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE)) + if (value_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE)) return; if (lp->ll_range && rettv->v_type == VAR_BLOB) @@ -1297,7 +1298,7 @@ set_var_lval( } *endp = cc; } - else if (var_check_lock(lp->ll_newkey == NULL + else if (value_check_lock(lp->ll_newkey == NULL ? lp->ll_tv->v_lock : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name, FALSE)) ; @@ -1317,7 +1318,7 @@ set_var_lval( */ for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { - if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return; ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) @@ -2103,6 +2104,8 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) char_u *p; int getnext; + CLEAR_POINTER(rettv); + /* * Get the first variable. */ @@ -3586,7 +3589,7 @@ eval_index( ; if (keylen == 0) return FAIL; - *arg = skipwhite(key + keylen); + *arg = key + keylen; } else { diff --git a/src/evalfunc.c b/src/evalfunc.c index 3ac23dded1..bcdf946aab 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -386,11 +386,14 @@ ret_argv(int argcount, type_T **argtypes UNUSED) static type_T * ret_remove(int argcount UNUSED, type_T **argtypes) { - if (argtypes[0]->tt_type == VAR_LIST - || argtypes[0]->tt_type == VAR_DICT) - return argtypes[0]->tt_member; - if (argtypes[0]->tt_type == VAR_BLOB) - return &t_number; + if (argtypes != NULL) + { + if (argtypes[0]->tt_type == VAR_LIST + || argtypes[0]->tt_type == VAR_DICT) + return argtypes[0]->tt_member; + if (argtypes[0]->tt_type == VAR_BLOB) + return &t_number; + } return &t_any; } @@ -750,6 +753,7 @@ static funcentry_T global_functions[] = {"matcharg", 1, 1, FEARG_1, ret_list_string, f_matcharg}, {"matchdelete", 1, 2, FEARG_1, ret_number, f_matchdelete}, {"matchend", 2, 4, FEARG_1, ret_number, f_matchend}, + {"matchfuzzy", 2, 2, FEARG_1, ret_list_string, f_matchfuzzy}, {"matchlist", 2, 4, FEARG_1, ret_list_string, f_matchlist}, {"matchstr", 2, 4, FEARG_1, ret_string, f_matchstr}, {"matchstrpos", 2, 4, FEARG_1, ret_list_any, f_matchstrpos}, @@ -2914,7 +2918,7 @@ ret_f_function(int argcount, type_T **argtypes) { if (argcount == 1 && argtypes[0]->tt_type == VAR_STRING) return &t_func_any; - return &t_func_void; + return &t_func_unknown; } /* diff --git a/src/evalvars.c b/src/evalvars.c index 255cb6292e..c7c2ed12fa 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -175,7 +175,6 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first); static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op); static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie); static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie); -static void item_lock(typval_T *tv, int deep, int lock, int check_refcount); static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); @@ -711,6 +710,8 @@ ex_let(exarg_T *eap) // detect Vim9 assignment without ":let" or ":const" if (eap->arg == eap->cmd) flags |= LET_NO_COMMAND; + if (eap->forceit) + flags |= LET_FORCEIT; argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE); if (argend == NULL) @@ -861,7 +862,7 @@ ex_let_vars( int copy, // copy values from "tv", don't move int semicolon, // from skip_var_list() int var_count, // from skip_var_list() - int flags, // LET_IS_CONST and/or LET_NO_COMMAND + int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND char_u *op) { char_u *arg = arg_start; @@ -1216,7 +1217,7 @@ ex_let_one( char_u *arg, // points to variable name typval_T *tv, // value to assign to variable int copy, // copy value from "tv" - int flags, // LET_IS_CONST and/or LET_NO_COMMAND + int flags, // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND char_u *endchars, // valid chars after variable name or NULL char_u *op) // "+", "-", "." or NULL { @@ -1561,9 +1562,9 @@ do_unlet_var( *name_end = cc; } else if ((lp->ll_list != NULL - && var_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) + && value_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) || (lp->ll_dict != NULL - && var_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) + && value_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) return FAIL; else if (lp->ll_range) { @@ -1574,7 +1575,7 @@ do_unlet_var( while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; - if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return FAIL; ll_li = li; ++ll_n1; @@ -1647,7 +1648,7 @@ do_unlet(char_u *name, int forceit) di = HI2DI(hi); if (var_check_fixed(di->di_flags, name, FALSE) || var_check_ro(di->di_flags, name, FALSE) - || var_check_lock(d->dv_lock, name, FALSE)) + || value_check_lock(d->dv_lock, name, FALSE)) return FAIL; delete_var(ht, hi); @@ -1678,9 +1679,6 @@ do_lock_var( int cc; dictitem_T *di; - if (deep == 0) // nothing to do - return OK; - if (lp->ll_tv == NULL) { cc = *name_end; @@ -1711,11 +1709,16 @@ do_lock_var( di->di_flags |= DI_FLAGS_LOCK; else di->di_flags &= ~DI_FLAGS_LOCK; - item_lock(&di->di_tv, deep, lock, FALSE); + if (deep != 0) + item_lock(&di->di_tv, deep, lock, FALSE); } } *name_end = cc; } + else if (deep == 0) + { + // nothing to do + } else if (lp->ll_range) { listitem_T *li = lp->ll_li; @@ -1743,7 +1746,7 @@ do_lock_var( * When "check_refcount" is TRUE do not lock a list or dict with a reference * count larger than 1. */ - static void + void item_lock(typval_T *tv, int deep, int lock, int check_refcount) { static int recurse = 0; @@ -2939,7 +2942,7 @@ set_var_const( type_T *type, typval_T *tv_arg, int copy, // make copy of value in "tv" - int flags) // LET_IS_CONST and/or LET_NO_COMMAND + int flags) // LET_IS_CONST, LET_FORCEIT, LET_NO_COMMAND { typval_T *tv = tv_arg; typval_T bool_tv; @@ -2947,22 +2950,23 @@ set_var_const( char_u *varname; hashtab_T *ht; int is_script_local; + int vim9script = in_vim9script(); ht = find_var_ht(name, &varname); if (ht == NULL || *varname == NUL) { semsg(_(e_illvar), name); - return; + goto failed; } is_script_local = ht == get_script_local_ht(); - if (in_vim9script() + if (vim9script && !is_script_local && (flags & LET_NO_COMMAND) == 0 && name[1] == ':') { vim9_declare_error(name); - return; + goto failed; } di = find_var_in_ht(ht, 0, varname, TRUE); @@ -2973,7 +2977,7 @@ set_var_const( if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) && var_wrong_func_name(name, di == NULL)) - return; + goto failed; if (need_convert_to_bool(type, tv)) { @@ -2991,25 +2995,30 @@ set_var_const( if (flags & LET_IS_CONST) { emsg(_(e_cannot_mod)); - return; + goto failed; } - if (is_script_local && in_vim9script()) + if (is_script_local && vim9script) { if ((flags & LET_NO_COMMAND) == 0) { semsg(_(e_redefining_script_item_str), name); - return; + goto failed; } // check the type and adjust to bool if needed if (check_script_var_type(&di->di_tv, tv, name) == FAIL) - return; + goto failed; } + // Check in this order for backwards compatibility: + // - Whether the variable is read-only + // - Whether the variable value is locked + // - Whether the variable is locked if (var_check_ro(di->di_flags, name, FALSE) - || var_check_lock(di->di_tv.v_lock, name, FALSE)) - return; + || value_check_lock(di->di_tv.v_lock, name, FALSE) + || var_check_lock(di->di_flags, name, FALSE)) + goto failed; } else // can only redefine once @@ -3039,7 +3048,7 @@ set_var_const( di->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; } - return; + goto failed; } else if (di->di_tv.v_type == VAR_NUMBER) { @@ -3053,12 +3062,12 @@ set_var_const( redraw_all_later(SOME_VALID); } #endif - return; + goto failed; } else if (di->di_tv.v_type != tv->v_type) { semsg(_("E963: setting %s to value with wrong type"), name); - return; + goto failed; } } @@ -3070,27 +3079,27 @@ set_var_const( if (ht == &vimvarht || ht == get_funccal_args_ht()) { semsg(_(e_illvar), name); - return; + goto failed; } // Make sure the variable name is valid. if (!valid_varname(varname)) - return; + goto failed; di = alloc(sizeof(dictitem_T) + STRLEN(varname)); if (di == NULL) - return; + goto failed; STRCPY(di->di_key, varname); if (hash_add(ht, DI2HIKEY(di)) == FAIL) { vim_free(di); - return; + goto failed; } di->di_flags = DI_FLAGS_ALLOC; if (flags & LET_IS_CONST) di->di_flags |= DI_FLAGS_LOCK; - if (is_script_local && in_vim9script()) + if (is_script_local && vim9script) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); @@ -3125,11 +3134,16 @@ set_var_const( init_tv(tv); } - if (flags & LET_IS_CONST) + // ":const var = val" locks the value; in Vim9 script only with ":const!" + if ((flags & LET_IS_CONST) && (!vim9script || (flags & LET_FORCEIT))) // Like :lockvar! name: lock the value and what it contains, but only // if the reference count is up to one. That locks only literal // values. item_lock(&di->di_tv, DICT_MAXNEST, TRUE, TRUE); + return; +failed: + if (!copy) + clear_tv(tv_arg); } /* @@ -3152,6 +3166,22 @@ var_check_ro(int flags, char_u *name, int use_gettext) return FALSE; } +/* + * Return TRUE if di_flags "flags" indicates variable "name" is locked. + * Also give an error message. + */ + int +var_check_lock(int flags, char_u *name, int use_gettext) +{ + if (flags & DI_FLAGS_LOCK) + { + semsg(_(e_variable_is_locked_str), + use_gettext ? (char_u *)_(name) : name); + return TRUE; + } + return FALSE; +} + /* * Return TRUE if di_flags "flags" indicates variable "name" is fixed. * Also give an error message. @@ -3199,12 +3229,12 @@ var_wrong_func_name( } /* - * Return TRUE if "flags" indicates variable "name" is locked (immutable). - * Also give an error message, using "name" or _("name") when use_gettext is - * TRUE. + * Return TRUE if "flags" indicates variable "name" has a locked (immutable) + * value. Also give an error message, using "name" or _("name") when + * "use_gettext" is TRUE. */ int -var_check_lock(int lock, char_u *name, int use_gettext) +value_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) { diff --git a/src/ex_cmds.h b/src/ex_cmds.h index c0e0d877c8..4d600e435c 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -398,7 +398,7 @@ EXCMD(CMD_confirm, "confirm", ex_wrongmodifier, EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_const, "const", ex_let, - EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_BANG|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_copen, "copen", ex_copen, EX_RANGE|EX_COUNT|EX_TRLBAR, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7c5e56bbe4..a5fa43c596 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -1785,9 +1785,7 @@ do_one_cmd( may_have_range = !vim9script || starts_with_colon; if (may_have_range) #endif - ea.cmd = skip_range(ea.cmd, NULL); - if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) - ea.cmd = skipwhite(ea.cmd + 1); + ea.cmd = skip_range(ea.cmd, TRUE, NULL); #ifdef FEAT_EVAL if (vim9script && !starts_with_colon) @@ -2689,7 +2687,7 @@ parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only) return FAIL; } - p = skip_range(eap->cmd, NULL); + p = skip_range(eap->cmd, TRUE, NULL); switch (*p) { // When adding an entry, also modify cmd_exists(). @@ -3232,19 +3230,33 @@ find_ex_command( // "g:varname" is an expression. || eap->cmd[1] == ':' ) - : ( - // "varname[]" is an expression. - *p == '[' // "varname->func()" is an expression. - || (*p == '-' && p[1] == '>') - // "varname.expr" is an expression. - || (*p == '.' && ASCII_ISALPHA(p[1])) - ))) + : (*p == '-' && p[1] == '>'))) { eap->cmdidx = CMD_eval; return eap->cmd; } + if (p != eap->cmd && ( + // "varname[]" is an expression. + *p == '[' + // "varname.key" is an expression. + || (*p == '.' && ASCII_ISALPHA(p[1])))) + { + char_u *after = p; + + // When followed by "=" or "+=" then it is an assignment. + ++emsg_silent; + if (skip_expr(&after) == OK + && (*after == '=' + || (*after != NUL && after[1] == '='))) + eap->cmdidx = CMD_let; + else + eap->cmdidx = CMD_eval; + --emsg_silent; + return eap->cmd; + } + // "[...]->Method()" is a list expression, but "[a, b] = Func()" is // an assignment. // If there is no line break inside the "[...]" then "p" is @@ -3540,7 +3552,8 @@ excmd_get_argt(cmdidx_T idx) char_u * skip_range( char_u *cmd, - int *ctx) // pointer to xp_context or NULL + int skip_star, // skip "*" used for Visual range + int *ctx) // pointer to xp_context or NULL { unsigned delim; @@ -3575,6 +3588,10 @@ skip_range( while (*cmd == ':') cmd = skipwhite(cmd + 1); + // Skip "*" used for Visual range. + if (skip_star && *cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) + cmd = skipwhite(cmd + 1); + return cmd; } @@ -8405,6 +8422,7 @@ find_cmdline_var(char_u *src, int *usedlen) * '' to C-expression under the cursor * '' to path name under the cursor * '' to sourced file name + * '' to call stack * '' to sourced file line number * '' to file name for autocommand * '' to buffer number for autocommand @@ -8622,7 +8640,8 @@ eval_vars( case SPEC_SFILE: // file name for ":so" command case SPEC_STACK: // call stack - result = estack_sfile(spec_idx == SPEC_SFILE); + result = estack_sfile(spec_idx == SPEC_SFILE + ? ESTACK_SFILE : ESTACK_STACK); if (result == NULL) { *errormsg = spec_idx == SPEC_SFILE diff --git a/src/ex_eval.c b/src/ex_eval.c index 58088a0706..cbdf82e893 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -290,7 +290,7 @@ cause_errthrow( // Get the source name and lnum now, it may change before // reaching do_errthrow(). - elem->sfile = estack_sfile(FALSE); + elem->sfile = estack_sfile(ESTACK_NONE); elem->slnum = SOURCING_LNUM; } } @@ -549,7 +549,7 @@ throw_exception(void *value, except_type_T type, char_u *cmdname) } else { - excp->throw_name = estack_sfile(FALSE); + excp->throw_name = estack_sfile(ESTACK_NONE); if (excp->throw_name == NULL) excp->throw_name = vim_strsave((char_u *)""); if (excp->throw_name == NULL) diff --git a/src/ex_getln.c b/src/ex_getln.c index c1e9188526..627d7aaf23 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -187,8 +187,12 @@ set_search_match(pos_T *t) * May change the last search pattern. */ static int -do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *is_state, - int *skiplen, int *patlen) +do_incsearch_highlighting( + int firstc, + int *search_delim, + incsearch_state_T *is_state, + int *skiplen, + int *patlen) { char_u *cmd; cmdmod_T save_cmdmod = cmdmod; @@ -230,7 +234,7 @@ do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *is_s parse_command_modifiers(&ea, &dummy, TRUE); cmdmod = save_cmdmod; - cmd = skip_range(ea.cmd, NULL); + cmd = skip_range(ea.cmd, TRUE, NULL); if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) goto theend; diff --git a/src/findfile.c b/src/findfile.c index 190fc69dc6..4f69c47caa 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -1934,6 +1934,13 @@ grab_file_name(long count, linenr_T *file_lnum) if (get_visual_text(NULL, &ptr, &len) == FAIL) return NULL; + // Only recognize ":123" here + if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) + { + char_u *p = ptr + len + 1; + + *file_lnum = getdigits(&p); + } return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname); } diff --git a/src/globals.h b/src/globals.h index 89b8054bad..7fe92d0226 100644 --- a/src/globals.h +++ b/src/globals.h @@ -391,42 +391,41 @@ EXTERN sctx_T current_sctx INIT4(0, 0, 0, 0); // Commonly used types. -EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL); -EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, 0, NULL, NULL); -EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL); -EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL); -EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL); -EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, 0, NULL, NULL); -EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, 0, NULL, NULL); -EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, 0, NULL, NULL); -EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, 0, NULL, NULL); -EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL); -EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL); +EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL); +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_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL); -EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, 0, &t_unknown, NULL); -EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL); -EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL); -EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL); -EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, 0, &t_string, NULL); -EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, 0, &t_void, NULL); -EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, 0, &t_any, NULL); -EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, 0, &t_number, NULL); -EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, 0, &t_string, NULL); +EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL); +EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL); +EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL); +EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL); +EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, NULL); +EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, NULL); +EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL); +EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, NULL); +EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_string, NULL); -EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL); -EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL); -EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_unknown, NULL); -EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_unknown, NULL); +EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_any, NULL); +EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_any, NULL); +EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_unknown, NULL); +EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, NULL); -EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL); -EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL); -EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, 0, &t_string, NULL); -EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, 0, &t_dict_any, NULL); - -EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, 0, &t_bool, NULL); -EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, 0, &t_number, NULL); -EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, 0, &t_string, NULL); +EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL); +EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, NULL); +EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL); +EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL); +EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL); +EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, NULL); +EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, NULL); #endif diff --git a/src/hashtab.c b/src/hashtab.c index f114b28808..dc0cbb6b86 100644 --- a/src/hashtab.c +++ b/src/hashtab.c @@ -81,7 +81,7 @@ hash_clear(hashtab_T *ht) vim_free(ht->ht_array); } -#if defined(FEAT_SPELL) || defined(PROTO) +#if defined(FEAT_SPELL) || defined(FEAT_TERMINAL) || defined(PROTO) /* * Free the array of a hash table and all the keys it contains. The keys must * have been allocated. "off" is the offset from the start of the allocate diff --git a/src/highlight.c b/src/highlight.c index 65b240205e..b6c5ed5662 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -729,7 +729,8 @@ do_highlight( if (!ends_excmd2(line, skipwhite(to_end))) { - semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start); + semsg(_("E413: Too many arguments: \":highlight link %s\""), + from_start); return; } @@ -1630,7 +1631,8 @@ restore_cterm_colors(void) static int hl_has_settings(int idx, int check_link) { - return ( HL_TABLE()[idx].sg_term_attr != 0 + return HL_TABLE()[idx].sg_cleared == 0 + && ( HL_TABLE()[idx].sg_term_attr != 0 || HL_TABLE()[idx].sg_cterm_attr != 0 || HL_TABLE()[idx].sg_cterm_fg != 0 || HL_TABLE()[idx].sg_cterm_bg != 0 diff --git a/src/if_perl.xs b/src/if_perl.xs index cad571c5c7..e54a79d2b3 100644 --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -241,6 +241,9 @@ typedef int perl_key; # else # define Perl_sv_2pv dll_Perl_sv_2pv # endif +# if (PERL_REVISION == 5) && (PERL_VERSION >= 32) +# define Perl_sv_2pvbyte_flags dll_Perl_sv_2pvbyte_flags +# endif # define Perl_sv_2pvbyte dll_Perl_sv_2pvbyte # define Perl_sv_bless dll_Perl_sv_bless # if (PERL_REVISION == 5) && (PERL_VERSION >= 8) @@ -397,6 +400,9 @@ static char* (*Perl_sv_2pv_nolen)(pTHX_ SV*); static char* (*Perl_sv_2pv)(pTHX_ SV*, STRLEN*); # endif static char* (*Perl_sv_2pvbyte)(pTHX_ SV*, STRLEN*); +# if (PERL_REVISION == 5) && (PERL_VERSION >= 32) +static char* (*Perl_sv_2pvbyte_flags)(pTHX_ SV*, STRLEN*, I32); +# endif static SV* (*Perl_sv_bless)(pTHX_ SV*, HV*); # if (PERL_REVISION == 5) && (PERL_VERSION >= 8) static void (*Perl_sv_catpvn_flags)(pTHX_ SV* , const char*, STRLEN, I32); @@ -553,6 +559,9 @@ static struct { {"Perl_sv_2pv", (PERL_PROC*)&Perl_sv_2pv}, # endif {"Perl_sv_2pvbyte", (PERL_PROC*)&Perl_sv_2pvbyte}, +# if (PERL_REVISION == 5) && (PERL_VERSION >= 32) + {"Perl_sv_2pvbyte_flags", (PERL_PROC*)&Perl_sv_2pvbyte_flags}, +# endif # ifdef PERL589_OR_LATER {"Perl_sv_2iv_flags", (PERL_PROC*)&Perl_sv_2iv_flags}, {"Perl_newXS_flags", (PERL_PROC*)&Perl_newXS_flags}, diff --git a/src/insexpand.c b/src/insexpand.c index 252a003422..11ee3ea369 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -4014,6 +4014,7 @@ ins_complete(int c, int enable_pum) { edit_submode_extra = (char_u *)_("The only match"); edit_submode_highl = HLF_COUNT; + compl_curr_match->cp_number = 0; } else { diff --git a/src/list.c b/src/list.c index fa9843ac88..e86ec8686f 100644 --- a/src/list.c +++ b/src/list.c @@ -817,7 +817,7 @@ f_flatten(typval_T *argvars, typval_T *rettv) } l = argvars[0].vval.v_list; - if (l != NULL && !var_check_lock(l->lv_lock, + if (l != NULL && !value_check_lock(l->lv_lock, (char_u *)N_("flatten() argument"), TRUE) && list_flatten(l, maxdepth) == OK) copy_tv(&argvars[0], rettv); @@ -1439,7 +1439,7 @@ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) int idx; if ((l = argvars[0].vval.v_list) == NULL - || var_check_lock(l->lv_lock, arg_errmsg, TRUE)) + || value_check_lock(l->lv_lock, arg_errmsg, TRUE)) return; idx = (long)tv_get_number_chk(&argvars[1], &error); @@ -1687,7 +1687,7 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) else { l = argvars[0].vval.v_list; - if (l == NULL || var_check_lock(l->lv_lock, + if (l == NULL || value_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) goto theend; @@ -1717,18 +1717,25 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) else { int error = FALSE; + int nr = 0; - i = (long)tv_get_number_chk(&argvars[1], &error); - if (error) - goto theend; // type error; errmsg already given - if (i == 1) - info.item_compare_ic = TRUE; - else if (argvars[1].v_type != VAR_NUMBER) - info.item_compare_func = tv_get_string(&argvars[1]); - else if (i != 0) + if (argvars[1].v_type == VAR_NUMBER) { - emsg(_(e_invarg)); - goto theend; + nr = tv_get_number_chk(&argvars[1], &error); + if (error) + goto theend; // type error; errmsg already given + if (nr == 1) + info.item_compare_ic = TRUE; + } + if (nr != 1) + { + if (argvars[1].v_type != VAR_NUMBER) + info.item_compare_func = tv_get_string(&argvars[1]); + else if (nr != 0) + { + emsg(_(e_invarg)); + goto theend; + } } if (info.item_compare_func != NULL) { @@ -1955,13 +1962,13 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE))) + || (!map && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) return; } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE))) + || (!map && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) return; } else @@ -2004,7 +2011,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) --todo; di = HI2DI(hi); - if (map && (var_check_lock(di->di_tv.v_lock, + if (map && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) || var_check_ro(di->di_flags, arg_errmsg, TRUE))) @@ -2077,7 +2084,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) l->lv_lock = VAR_LOCKED; for (li = l->lv_first; li != NULL; li = nli) { - if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) + if (map && value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) break; nli = li->li_next; set_vim_var_nr(VV_KEY, idx); @@ -2131,7 +2138,7 @@ f_add(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, + && !value_check_lock(l->lv_lock, (char_u *)N_("add() argument"), TRUE) && list_append_tv(l, &argvars[1]) == OK) copy_tv(&argvars[0], rettv); @@ -2139,7 +2146,7 @@ f_add(typval_T *argvars, typval_T *rettv) else if (argvars[0].v_type == VAR_BLOB) { if ((b = argvars[0].vval.v_blob) != NULL - && !var_check_lock(b->bv_lock, + && !value_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE)) { int error = FALSE; @@ -2282,7 +2289,7 @@ f_extend(typval_T *argvars, typval_T *rettv) l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; - if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE) + if (l1 != NULL && !value_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) @@ -2318,7 +2325,7 @@ f_extend(typval_T *argvars, typval_T *rettv) d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE) + if (d1 != NULL && !value_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL) { // Check the third argument. @@ -2402,7 +2409,7 @@ f_insert(typval_T *argvars, typval_T *rettv) else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "insert()"); else if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, + && !value_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE)) { if (argvars[2].v_type != VAR_UNKNOWN) @@ -2475,7 +2482,7 @@ f_reverse(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "reverse()"); else if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, + && !value_check_lock(l->lv_lock, (char_u *)N_("reverse() argument"), TRUE)) { if (l->lv_first == &range_list_item) diff --git a/src/memline.c b/src/memline.c index 1c594d453a..54f8c19b0f 100644 --- a/src/memline.c +++ b/src/memline.c @@ -2634,7 +2634,7 @@ add_text_props_for_append( int n; char_u *props; int new_len = 0; // init for gcc - char_u *new_line; + char_u *new_line = NULL; textprop_T prop; // Make two rounds: diff --git a/src/message.c b/src/message.c index eb19e7e007..b528178e0a 100644 --- a/src/message.c +++ b/src/message.c @@ -461,7 +461,7 @@ get_emsg_source(void) if (SOURCING_NAME != NULL && other_sourcing_name()) { - char_u *sname = estack_sfile(FALSE); + char_u *sname = estack_sfile(ESTACK_NONE); char_u *tofree = sname; if (sname == NULL) diff --git a/src/ops.c b/src/ops.c index a681df0dc3..99388a60b8 100644 --- a/src/ops.c +++ b/src/ops.c @@ -481,6 +481,7 @@ block_insert( int count = 0; // extra spaces to replace a cut TAB int spaces = 0; // non-zero if cutting a TAB colnr_T offset; // pointer along new line + colnr_T startcol; // column where insert starts unsigned s_len; // STRLEN(s) char_u *newp, *oldp; // new, old lines linenr_T lnum; // loop var @@ -553,9 +554,10 @@ block_insert( // insert pre-padding vim_memset(newp + offset, ' ', (size_t)spaces); + startcol = offset + spaces; // copy the new text - mch_memmove(newp + offset + spaces, s, (size_t)s_len); + mch_memmove(newp + startcol, s, (size_t)s_len); offset += s_len; if (spaces && !bdp->is_short) @@ -574,6 +576,10 @@ block_insert( ml_replace(lnum, newp, FALSE); + if (b_insert) + // correct any text properties + inserted_bytes(lnum, startcol, s_len); + if (lnum == oap->end.lnum) { // Set "']" mark to the end of the block instead of the end of @@ -2636,6 +2642,9 @@ do_addsub( } else { + pos_T save_pos; + int i; + if (col > 0 && ptr[col - 1] == '-' && (!has_mbyte || !(*mb_head_off)(ptr, ptr + col - 1)) @@ -2734,7 +2743,9 @@ do_addsub( */ if (c == '-') --length; - while (todel-- > 0) + + save_pos = curwin->w_cursor; + for (i = 0; i < todel; ++i) { if (c < 0x100 && isalpha(c)) { @@ -2743,10 +2754,10 @@ do_addsub( else hexupper = FALSE; } - // del_char() will mark line needing displaying - (void)del_char(FALSE); + inc_cursor(); c = gchar_cursor(); } + curwin->w_cursor = save_pos; /* * Prepare the leading characters in buf1[]. @@ -2776,7 +2787,6 @@ do_addsub( */ if (pre == 'b' || pre == 'B') { - int i; int bit = 0; int bits = sizeof(uvarnumber_T) * 8; @@ -2809,9 +2819,34 @@ do_addsub( while (length-- > 0) *ptr++ = '0'; *ptr = NUL; + STRCAT(buf1, buf2); + + // Insert just after the first character to be removed, so that any + // text properties will be adjusted. Then delete the old number + // afterwards. + save_pos = curwin->w_cursor; + if (todel > 0) + inc_cursor(); ins_str(buf1); // insert the new number vim_free(buf1); + + // del_char() will also mark line needing displaying + if (todel > 0) + { + int bytes_after = (int)STRLEN(ml_get_curline()) + - curwin->w_cursor.col; + + // Delete the one character before the insert. + curwin->w_cursor = save_pos; + (void)del_char(FALSE); + curwin->w_cursor.col = (colnr_T)(STRLEN(ml_get_curline()) + - bytes_after); + --todel; + } + while (todel-- > 0) + (void)del_char(FALSE); + endpos = curwin->w_cursor; if (did_change && curwin->w_cursor.col) --curwin->w_cursor.col; diff --git a/src/option.c b/src/option.c index 608f4b4836..c5b991ccf5 100644 --- a/src/option.c +++ b/src/option.c @@ -37,6 +37,7 @@ static void set_options_default(int opt_flags); static void set_string_default_esc(char *name, char_u *val, int escape); +static char_u *find_dup_item(char_u *origval, char_u *newval, long_u flags); static char_u *option_expand(int opt_idx, char_u *val); static void didset_options(void); static void didset_options2(void); @@ -142,6 +143,9 @@ set_init_1(int clean_arg) int len; garray_T ga; int mustfree; + char_u *item; + + opt_idx = findoption((char_u *)"backupskip"); ga_init2(&ga, 1, 100); for (n = 0; n < (long)(sizeof(names) / sizeof(char *)); ++n) @@ -161,15 +165,20 @@ set_init_1(int clean_arg) { // First time count the NUL, otherwise count the ','. len = (int)STRLEN(p) + 3; - if (ga_grow(&ga, len) == OK) + item = alloc(len); + STRCPY(item, p); + add_pathsep(item); + STRCAT(item, "*"); + if (find_dup_item(ga.ga_data, item, options[opt_idx].flags) + == NULL + && ga_grow(&ga, len) == OK) { if (ga.ga_len > 0) STRCAT(ga.ga_data, ","); - STRCAT(ga.ga_data, p); - add_pathsep(ga.ga_data); - STRCAT(ga.ga_data, "*"); + STRCAT(ga.ga_data, item); ga.ga_len += len; } + vim_free(item); } if (mustfree) vim_free(p); @@ -685,6 +694,46 @@ set_string_default(char *name, char_u *val) set_string_default_esc(name, val, FALSE); } +/* + * For an option value that contains comma separated items, find "newval" in + * "origval". Return NULL if not found. + */ + static char_u * +find_dup_item(char_u *origval, char_u *newval, long_u flags) +{ + int bs = 0; + size_t newlen; + char_u *s; + + if (origval == NULL) + return NULL; + + newlen = STRLEN(newval); + for (s = origval; *s != NUL; ++s) + { + if ((!(flags & P_COMMA) + || s == origval + || (s[-1] == ',' && !(bs & 1))) + && STRNCMP(s, newval, newlen) == 0 + && (!(flags & P_COMMA) + || s[newlen] == ',' + || s[newlen] == NUL)) + return s; + // Count backslashes. Only a comma with an even number of backslashes + // or a single backslash preceded by a comma before it is recognized as + // a separator. + if ((s > origval + 1 + && s[-1] == '\\' + && s[-2] != ',') + || (s == origval + 1 + && s[-1] == '\\')) + ++bs; + else + bs = 0; + } + return NULL; +} + /* * Set the Vi-default value of a number option. * Used for 'lines' and 'columns'. @@ -1590,7 +1639,6 @@ do_set( #endif unsigned newlen; int comma; - int bs; int new_value_alloced; // new string option // was allocated @@ -1829,39 +1877,20 @@ do_set( if (removing || (flags & P_NODUP)) { i = (int)STRLEN(newval); - bs = 0; - for (s = origval; *s; ++s) - { - if ((!(flags & P_COMMA) - || s == origval - || (s[-1] == ',' && !(bs & 1))) - && STRNCMP(s, newval, i) == 0 - && (!(flags & P_COMMA) - || s[i] == ',' - || s[i] == NUL)) - break; - // Count backslashes. Only a comma with an - // even number of backslashes or a single - // backslash preceded by a comma before it - // is recognized as a separator - if ((s > origval + 1 - && s[-1] == '\\' - && s[-2] != ',') - || (s == origval + 1 - && s[-1] == '\\')) - - ++bs; - else - bs = 0; - } + s = find_dup_item(origval, newval, flags); // do not add if already there - if ((adding || prepending) && *s) + if ((adding || prepending) && s != NULL) { prepending = FALSE; adding = FALSE; STRCPY(newval, origval); } + + // if no duplicate, move pointer to end of + // original value + if (s == NULL) + s = origval + (int)STRLEN(origval); } // concatenate the two strings; add a ',' if diff --git a/src/os_unix.c b/src/os_unix.c index 75df0caa77..ed0d5e3d64 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -7456,7 +7456,7 @@ mch_libcall( ))) { if (string_result == NULL) - retval_int = ((STRPROCINT)ProcAdd)(argstring); + retval_int = ((STRPROCINT)(void *)ProcAdd)(argstring); else retval_str = (ProcAdd)(argstring); } @@ -7478,7 +7478,7 @@ mch_libcall( ))) { if (string_result == NULL) - retval_int = ((INTPROCINT)ProcAddI)(argint); + retval_int = ((INTPROCINT)(void *)ProcAddI)(argint); else retval_str = (ProcAddI)(argint); } diff --git a/src/proto/change.pro b/src/proto/change.pro index badb19dbdf..69ba2a6b08 100644 --- a/src/proto/change.pro +++ b/src/proto/change.pro @@ -9,6 +9,7 @@ void may_invoke_listeners(buf_T *buf, linenr_T lnum, linenr_T lnume, int added); void invoke_listeners(buf_T *buf); void remove_listeners(buf_T *buf); void changed_bytes(linenr_T lnum, colnr_T col); +void inserted_bytes(linenr_T lnum, colnr_T col, int added); void appended_lines(linenr_T lnum, long count); void appended_lines_mark(linenr_T lnum, long count); void deleted_lines(linenr_T lnum, long count); diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro index f304872230..569a73ae24 100644 --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -23,6 +23,7 @@ void ex_unlet(exarg_T *eap); void ex_lockvar(exarg_T *eap); void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie); int do_unlet(char_u *name, int forceit); +void item_lock(typval_T *tv, int deep, int lock, int check_refcount); void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); char *get_var_special_name(int nr); @@ -65,11 +66,12 @@ void unref_var_dict(dict_T *dict); void vars_clear(hashtab_T *ht); void vars_clear_ext(hashtab_T *ht, int free_val); void set_var(char_u *name, typval_T *tv, int copy); -void set_var_const(char_u *name, type_T *type, typval_T *tv, int copy, int flags); +void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags); int var_check_ro(int flags, char_u *name, int use_gettext); +int var_check_lock(int flags, char_u *name, int use_gettext); int var_check_fixed(int flags, char_u *name, int use_gettext); int var_wrong_func_name(char_u *name, int new_var); -int var_check_lock(int lock, char_u *name, int use_gettext); +int value_check_lock(int lock, char_u *name, int use_gettext); int valid_varname(char_u *varname); void reset_v_option_vars(void); void assert_error(garray_T *gap); diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 05c1a85b59..39c4cfd601 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -16,7 +16,7 @@ int modifier_len(char_u *cmd); int cmd_exists(char_u *name); cmdidx_T excmd_get_cmdidx(char_u *cmd, int len); long excmd_get_argt(cmdidx_T idx); -char_u *skip_range(char_u *cmd, int *ctx); +char_u *skip_range(char_u *cmd, int skip_star, int *ctx); void ex_ni(exarg_T *eap); int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp); void separate_nextcmd(exarg_T *eap); diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro index 52df2ca82e..cbb9253c52 100644 --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -4,7 +4,7 @@ estack_T *estack_push(etype_T type, char_u *name, long lnum); estack_T *estack_push_ufunc(ufunc_T *ufunc, long lnum); int estack_top_is_ufunc(ufunc_T *ufunc, long lnum); estack_T *estack_pop(void); -char_u *estack_sfile(int is_sfile); +char_u *estack_sfile(estack_arg_T which); void ex_runtime(exarg_T *eap); 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); diff --git a/src/proto/search.pro b/src/proto/search.pro index ccad0fb8eb..1b62254199 100644 --- a/src/proto/search.pro +++ b/src/proto/search.pro @@ -36,4 +36,5 @@ void find_pattern_in_path(char_u *ptr, int dir, int len, int whole, int skip_com spat_T *get_spat(int idx); int get_spat_last_idx(void); void f_searchcount(typval_T *argvars, typval_T *rettv); +void f_matchfuzzy(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index e8760d14a7..f3bd5a31c7 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -2,7 +2,7 @@ void init_job_options(jobopt_T *opt); buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags); void ex_terminal(exarg_T *eap); -int term_write_session(FILE *fd, win_T *wp); +int term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs); int term_should_restore(buf_T *buf); void free_terminal(buf_T *buf); void free_unused_terminals(void); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index e6acc18864..4e3f2dbf4f 100644 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -11,6 +11,7 @@ int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error); ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx); ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx); +int func_is_global(ufunc_T *ufunc); void copy_func(char_u *lambda, char_u *global); int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); void save_funccal(funccal_entry_T *entry); diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro index 1492488002..e80f303a4d 100644 --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -1,6 +1,8 @@ /* vim9type.c */ -type_T *alloc_type(garray_T *type_gap); +type_T *get_type_ptr(garray_T *type_gap); void clear_type_list(garray_T *gap); +type_T *alloc_type(type_T *type); +void free_type(type_T *type); type_T *get_list_type(type_T *member_type, garray_T *type_gap); type_T *get_dict_type(type_T *member_type, garray_T *type_gap); type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap); diff --git a/src/quickfix.c b/src/quickfix.c index c8858b456e..43d2d3fbd5 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -216,7 +216,9 @@ static char_u *e_no_more_items = (char_u *)N_("E553: No more items"); static char_u *qf_last_bufname = NULL; static bufref_T qf_last_bufref = {NULL, 0, 0}; -static char *e_loc_list_changed = +static char *e_current_quickfix_list_was_changed = + N_("E925: Current quickfix list was changed"); +static char *e_current_location_list_was_changed = N_("E926: Current location list was changed"); /* @@ -3108,6 +3110,7 @@ qf_jump_edit_buffer( int *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); + int old_changedtick = qfl->qf_changedtick; qfltype_T qfl_type = qfl->qfl_type; int retval = OK; int old_qf_curlist = qi->qf_curlist; @@ -3146,17 +3149,20 @@ qf_jump_edit_buffer( if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid)) { - emsg(_("E925: Current quickfix was changed")); + emsg(_(e_current_quickfix_list_was_changed)); return NOTDONE; } + // Check if the list was changed. The pointers may happen to be identical, + // thus also check qf_changedtick. if (old_qf_curlist != qi->qf_curlist + || old_changedtick != qfl->qf_changedtick || !is_qf_entry_present(qfl, qf_ptr)) { if (qfl_type == QFLT_QUICKFIX) - emsg(_("E925: Current quickfix was changed")); + emsg(_(e_current_quickfix_list_was_changed)); else - emsg(_(e_loc_list_changed)); + emsg(_(e_current_location_list_was_changed)); return NOTDONE; } @@ -3264,10 +3270,25 @@ qf_jump_open_window( int newwin, int *opened_window) { + qf_list_T *qfl = qf_get_curlist(qi); + int old_changedtick = qfl->qf_changedtick; + int old_qf_curlist = qi->qf_curlist; + qfltype_T qfl_type = qfl->qfl_type; + // For ":helpgrep" find a help window or open one. if (qf_ptr->qf_type == 1 && (!bt_help(curwin->w_buffer) || cmdmod.tab != 0)) if (jump_to_help_window(qi, newwin, opened_window) == FAIL) return FAIL; + if (old_qf_curlist != qi->qf_curlist + || old_changedtick != qfl->qf_changedtick + || !is_qf_entry_present(qfl, qf_ptr)) + { + if (qfl_type == QFLT_QUICKFIX) + emsg(_(e_current_quickfix_list_was_changed)); + else + emsg(_(e_current_location_list_was_changed)); + return FAIL; + } // If currently in the quickfix window, find another window to show the // file in. @@ -3282,6 +3303,16 @@ qf_jump_open_window( opened_window) == FAIL) return FAIL; } + if (old_qf_curlist != qi->qf_curlist + || old_changedtick != qfl->qf_changedtick + || !is_qf_entry_present(qfl, qf_ptr)) + { + if (qfl_type == QFLT_QUICKFIX) + emsg(_(e_current_quickfix_list_was_changed)); + else + emsg(_(e_current_location_list_was_changed)); + return FAIL; + } return OK; } @@ -5834,7 +5865,7 @@ vgr_qflist_valid( if (wp != NULL) { // An autocmd has freed the location list. - emsg(_(e_loc_list_changed)); + emsg(_(e_current_location_list_was_changed)); return FALSE; } else diff --git a/src/scriptfile.c b/src/scriptfile.c index e075236953..a8e67b98ad 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -111,10 +111,10 @@ estack_pop(void) /* * Get the current value for in allocated memory. - * "is_sfile" is TRUE for itself. + * "which" is ESTACK_SFILE for and ESTACK_STACK for . */ char_u * -estack_sfile(int is_sfile UNUSED) +estack_sfile(estack_arg_T which UNUSED) { estack_T *entry; #ifdef FEAT_EVAL @@ -127,7 +127,7 @@ estack_sfile(int is_sfile UNUSED) entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1; #ifdef FEAT_EVAL - if (is_sfile && entry->es_type != ETYPE_UFUNC) + if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC) #endif { if (entry->es_name == NULL) @@ -144,6 +144,9 @@ estack_sfile(int is_sfile UNUSED) entry = ((estack_T *)exestack.ga_data) + idx; if (entry->es_name != NULL) { + long lnum = 0; + char *dots; + len = STRLEN(entry->es_name) + 15; type_name = ""; if (entry->es_type != last_type) @@ -159,15 +162,20 @@ estack_sfile(int is_sfile UNUSED) len += STRLEN(type_name); if (ga_grow(&ga, (int)len) == FAIL) break; - if (idx == exestack.ga_len - 1 || entry->es_lnum == 0) - // For the bottom entry: do not add the line number, it is used - // in . Also leave it out when the number is not set. - vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s", - type_name, entry->es_name, - idx == exestack.ga_len - 1 ? "" : ".."); + if (idx == exestack.ga_len - 1) + lnum = which == ESTACK_STACK ? SOURCING_LNUM : 0; else - vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%ld]..", - type_name, entry->es_name, entry->es_lnum); + lnum = entry->es_lnum; + dots = idx == exestack.ga_len - 1 ? "" : ".."; + if (lnum == 0) + // For the bottom entry of : do not add the line number, + // it is used in . Also leave it out when the number is + // not set. + vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s", + type_name, entry->es_name, dots); + else + vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%ld]%s", + type_name, entry->es_name, lnum, dots); ga.ga_len += (int)STRLEN((char *)ga.ga_data + ga.ga_len); } } diff --git a/src/search.c b/src/search.c index 6ce8a10233..5b5996674b 100644 --- a/src/search.c +++ b/src/search.c @@ -4180,4 +4180,344 @@ f_searchcount(typval_T *argvars, typval_T *rettv) the_end: restore_last_search_pattern(); } + +/* + * Fuzzy string matching + * + * Ported from the lib_fts library authored by Forrest Smith. + * https://github.com/forrestthewoods/lib_fts/tree/master/code + * + * Blog describing the algorithm: + * https://www.forrestthewoods.com/blog/reverse_engineering_sublime_texts_fuzzy_match/ + * + * Each matching string is assigned a score. The following factors are checked: + * Matched letter + * Unmatched letter + * Consecutively matched letters + * Proximity to start + * Letter following a separator (space, underscore) + * Uppercase letter following lowercase (aka CamelCase) + * + * Matched letters are good. Unmatched letters are bad. Matching near the start + * is good. Matching the first letter in the middle of a phrase is good. + * Matching the uppercase letters in camel case entries is good. + * + * The score assigned for each factor is explained below. + * File paths are different from file names. File extensions may be ignorable. + * Single words care about consecutive matches but not separators or camel + * case. + * Score starts at 0 + * Matched letter: +0 points + * Unmatched letter: -1 point + * Consecutive match bonus: +5 points + * Separator bonus: +10 points + * Camel case bonus: +10 points + * Unmatched leading letter: -3 points (max: -9) + * + * There is some nuance to this. Scores don’t have an intrinsic meaning. The + * score range isn’t 0 to 100. It’s roughly [-50, 50]. Longer words have a + * lower minimum score due to unmatched letter penalty. Longer search patterns + * have a higher maximum score due to match bonuses. + * + * Separator and camel case bonus is worth a LOT. Consecutive matches are worth + * quite a bit. + * + * There is a penalty if you DON’T match the first three letters. Which + * effectively rewards matching near the start. However there’s no difference + * in matching between the middle and end. + * + * There is not an explicit bonus for an exact match. Unmatched letters receive + * a penalty. So shorter strings and closer matches are worth more. + */ +typedef struct +{ + listitem_T *item; + int score; +} fuzzyItem_T; + + static int +fuzzy_match_recursive( + char_u *fuzpat, + char_u *str, + int *outScore, + char_u *strBegin, + char_u *srcMatches, + char_u *matches, + int maxMatches, + int nextMatch, + int *recursionCount, + int recursionLimit) +{ + // Recursion params + int recursiveMatch = FALSE; + char_u bestRecursiveMatches[256]; + int bestRecursiveScore = 0; + int first_match; + int matched; + + // Count recursions + ++*recursionCount; + if (*recursionCount >= recursionLimit) + return FALSE; + + // Detect end of strings + if (*fuzpat == '\0' || *str == '\0') + return FALSE; + + // Loop through fuzpat and str looking for a match + first_match = TRUE; + while (*fuzpat != '\0' && *str != '\0') + { + // Found match + if (vim_tolower(*fuzpat) == vim_tolower(*str)) + { + char_u recursiveMatches[256]; + int recursiveScore = 0; + + // Supplied matches buffer was too short + if (nextMatch >= maxMatches) + return FALSE; + + // "Copy-on-Write" srcMatches into matches + if (first_match && srcMatches) + { + memcpy(matches, srcMatches, nextMatch); + first_match = FALSE; + } + + // Recursive call that "skips" this match + if (fuzzy_match_recursive(fuzpat, str + 1, &recursiveScore, + strBegin, matches, recursiveMatches, + sizeof(recursiveMatches), nextMatch, recursionCount, + recursionLimit)) + { + // Pick best recursive score + if (!recursiveMatch || recursiveScore > bestRecursiveScore) + { + memcpy(bestRecursiveMatches, recursiveMatches, 256); + bestRecursiveScore = recursiveScore; + } + recursiveMatch = TRUE; + } + + // Advance + matches[nextMatch++] = (char_u)(str - strBegin); + ++fuzpat; + } + ++str; + } + + // Determine if full fuzpat was matched + matched = *fuzpat == '\0' ? TRUE : FALSE; + + // Calculate score + if (matched) + { + // bonus for adjacent matches + int sequential_bonus = 15; + // bonus if match occurs after a separator + int separator_bonus = 30; + // bonus if match is uppercase and prev is lower + int camel_bonus = 30; + // bonus if the first letter is matched + int first_letter_bonus = 15; + // penalty applied for every letter in str before the first match + int leading_letter_penalty = -5; + // maximum penalty for leading letters + int max_leading_letter_penalty = -15; + // penalty for every letter that doesn't matter + int unmatched_letter_penalty = -1; + int penalty; + int unmatched; + int i; + + // Iterate str to end + while (*str != '\0') + ++str; + + // Initialize score + *outScore = 100; + + // Apply leading letter penalty + penalty = leading_letter_penalty * matches[0]; + if (penalty < max_leading_letter_penalty) + penalty = max_leading_letter_penalty; + *outScore += penalty; + + // Apply unmatched penalty + unmatched = (int)(str - strBegin) - nextMatch; + *outScore += unmatched_letter_penalty * unmatched; + + // Apply ordering bonuses + for (i = 0; i < nextMatch; ++i) + { + char_u currIdx = matches[i]; + + if (i > 0) + { + char_u prevIdx = matches[i - 1]; + + // Sequential + if (currIdx == (prevIdx + 1)) + *outScore += sequential_bonus; + } + + // Check for bonuses based on neighbor character value + if (currIdx > 0) + { + // Camel case + char_u neighbor = strBegin[currIdx - 1]; + char_u curr = strBegin[currIdx]; + int neighborSeparator; + + if (islower(neighbor) && isupper(curr)) + *outScore += camel_bonus; + + // Separator + neighborSeparator = neighbor == '_' || neighbor == ' '; + if (neighborSeparator) + *outScore += separator_bonus; + } + else + { + // First letter + *outScore += first_letter_bonus; + } + } + } + + // Return best result + if (recursiveMatch && (!matched || bestRecursiveScore > *outScore)) + { + // Recursive score is better than "this" + memcpy(matches, bestRecursiveMatches, maxMatches); + *outScore = bestRecursiveScore; + return TRUE; + } + else if (matched) + return TRUE; // "this" score is better than recursive + + return FALSE; // no match +} + +/* + * fuzzy_match() + * + * Performs exhaustive search via recursion to find all possible matches and + * match with highest score. + * Scores values have no intrinsic meaning. Possible score range is not + * normalized and varies with pattern. + * Recursion is limited internally (default=10) to prevent degenerate cases + * (fuzpat="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"). + * Uses char_u for match indices. Therefore patterns are limited to 256 + * characters. + * + * Returns TRUE if fuzpat is found AND calculates a score. + */ + static int +fuzzy_match(char_u *str, char_u *fuzpat, int *outScore) +{ + char_u matches[256]; + int recursionCount = 0; + int recursionLimit = 10; + + *outScore = 0; + + return fuzzy_match_recursive(fuzpat, str, outScore, str, NULL, matches, + sizeof(matches), 0, &recursionCount, recursionLimit); +} + +/* + * Sort the fuzzy matches in the descending order of the match score. + */ + static int +fuzzy_item_compare(const void *s1, const void *s2) +{ + int v1 = ((fuzzyItem_T *)s1)->score; + int v2 = ((fuzzyItem_T *)s2)->score; + + return v1 == v2 ? 0 : v1 > v2 ? -1 : 1; +} + +/* + * Fuzzy search the string 'str' in 'strlist' and return the matching strings + * in 'fmatchlist'. + */ + static void +match_fuzzy(list_T *strlist, char_u *str, list_T *fmatchlist) +{ + long len; + fuzzyItem_T *ptrs; + listitem_T *li; + long i = 0; + int found_match = FALSE; + + len = list_len(strlist); + if (len == 0) + return; + + ptrs = ALLOC_MULT(fuzzyItem_T, len); + if (ptrs == NULL) + return; + + // For all the string items in strlist, get the fuzzy matching score + FOR_ALL_LIST_ITEMS(strlist, li) + { + int score; + + ptrs[i].item = li; + ptrs[i].score = -9999; + // ignore non-string items in the list + if (li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL) + if (fuzzy_match(li->li_tv.vval.v_string, str, &score)) + { + ptrs[i].score = score; + found_match = TRUE; + } + ++i; + } + + if (found_match) + { + // Sort the list by the descending order of the match score + qsort((void *)ptrs, (size_t)len, sizeof(fuzzyItem_T), + fuzzy_item_compare); + + // Copy the matching strings with 'score != -9999' to the return list + for (i = 0; i < len; i++) + { + if (ptrs[i].score == -9999) + break; + list_append_string(fmatchlist, ptrs[i].item->li_tv.vval.v_string, + -1); + } + } + + vim_free(ptrs); +} + +/* + * "matchfuzzy()" function + */ + void +f_matchfuzzy(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_listreq)); + return; + } + if (argvars[0].vval.v_list == NULL) + return; + if (argvars[1].v_type != VAR_STRING + || argvars[1].vval.v_string == NULL) + { + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + if (rettv_list_alloc(rettv) == OK) + match_fuzzy(argvars[0].vval.v_list, tv_get_string(&argvars[1]), + rettv->vval.v_list); +} + #endif diff --git a/src/session.c b/src/session.c index 0f42e04369..44cd2732af 100644 --- a/src/session.c +++ b/src/session.c @@ -303,10 +303,12 @@ put_view_curpos(FILE *fd, win_T *wp, char *spaces) put_view( FILE *fd, win_T *wp, - int add_edit, // add ":edit" command to view - unsigned *flagp, // vop_flags or ssop_flags - int current_arg_idx) // current argument index of the window, use - // -1 if unknown + int add_edit, // add ":edit" command to view + unsigned *flagp, // vop_flags or ssop_flags + int current_arg_idx, // current argument index of the window, + // use -1 if unknown + hashtab_T *terminal_bufs UNUSED) // already encountered terminal buffers, + // can be NULL { win_T *save_curwin; int f; @@ -349,7 +351,7 @@ put_view( # ifdef FEAT_TERMINAL if (bt_terminal(wp->w_buffer)) { - if (term_write_session(fd, wp) == FAIL) + if (term_write_session(fd, wp, terminal_bufs) == FAIL) return FAIL; } else @@ -588,6 +590,12 @@ makeopens( frame_T *tab_topframe; int cur_arg_idx = 0; int next_arg_idx = 0; + int ret = FAIL; +#ifdef FEAT_TERMINAL + hashtab_T terminal_bufs; + + hash_init(&terminal_bufs); +#endif if (ssop_flags & SSOP_BUFFERS) only_save_windows = FALSE; // Save ALL buffers @@ -596,25 +604,25 @@ makeopens( // sessionable variables. #ifdef FEAT_EVAL if (put_line(fd, "let v:this_session=expand(\":p\")") == FAIL) - return FAIL; + goto fail; if (ssop_flags & SSOP_GLOBALS) if (store_session_globals(fd) == FAIL) - return FAIL; + goto fail; #endif // Close all windows and tabs but one. if (put_line(fd, "silent only") == FAIL) - return FAIL; + goto fail; if ((ssop_flags & SSOP_TABPAGES) && put_line(fd, "silent tabonly") == FAIL) - return FAIL; + goto fail; // Now a :cd command to the session directory or the current directory if (ssop_flags & SSOP_SESDIR) { if (put_line(fd, "exe \"cd \" . escape(expand(\":p:h\"), ' ')") == FAIL) - return FAIL; + goto fail; } else if (ssop_flags & SSOP_CURDIR) { @@ -625,7 +633,7 @@ makeopens( || put_eol(fd) == FAIL) { vim_free(sname); - return FAIL; + goto fail; } vim_free(sname); } @@ -633,33 +641,33 @@ makeopens( // If there is an empty, unnamed buffer we will wipe it out later. // Remember the buffer number. if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL) - return FAIL; + goto fail; if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL) - return FAIL; + goto fail; if (put_line(fd, "endif") == FAIL) - return FAIL; + goto fail; // Now save the current files, current buffer first. if (put_line(fd, "set shortmess=aoO") == FAIL) - return FAIL; + goto fail; // the global argument list if (ses_arglist(fd, "argglobal", &global_alist.al_ga, !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) - return FAIL; + goto fail; if (ssop_flags & SSOP_RESIZE) { // Note: after the restore we still check it worked! if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0 || put_eol(fd) == FAIL) - return FAIL; + goto fail; #ifdef FEAT_FULLSCREEN // fullscreen needs to be set after lines and columns if (p_fullscreen) { if (fprintf(fd, "set fullscreen") < 0 || put_eol(fd) == FAIL) - return FAIL; + goto fail; } #endif } @@ -673,7 +681,7 @@ makeopens( { // Note: after the restore we still check it worked! if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL) - return FAIL; + goto fail; } } #endif @@ -685,7 +693,7 @@ makeopens( if (p_stal == 1 && first_tabpage->tp_next != NULL) { if (put_line(fd, "set stal=2") == FAIL) - return FAIL; + goto fail; restore_stal = TRUE; } @@ -703,9 +711,9 @@ makeopens( // later local options won't be copied to the new tabs. FOR_ALL_TABPAGES(tp) if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) - return FAIL; + goto fail; if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL) - return FAIL; + goto fail; } for (tabnr = 1; ; ++tabnr) { @@ -747,13 +755,13 @@ makeopens( ) { if (need_tabnext && put_line(fd, "tabnext") == FAIL) - return FAIL; + goto fail; need_tabnext = FALSE; if (fputs("edit ", fd) < 0 || ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE) == FAIL) - return FAIL; + goto fail; if (!wp->w_arg_idx_invalid) edited_win = wp; break; @@ -762,17 +770,17 @@ makeopens( // If no file got edited create an empty tab page. if (need_tabnext && put_line(fd, "tabnext") == FAIL) - return FAIL; + goto fail; // Save current window layout. if (put_line(fd, "set splitbelow splitright") == FAIL) - return FAIL; + goto fail; if (ses_win_rec(fd, tab_topframe) == FAIL) - return FAIL; + goto fail; if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL) - return FAIL; + goto fail; if (!p_spr && put_line(fd, "set nosplitright") == FAIL) - return FAIL; + goto fail; // Check if window sizes can be restored (no windows omitted). // Remember the window number of the current window after restoring. @@ -789,7 +797,7 @@ makeopens( // Go to the first window. if (put_line(fd, "wincmd t") == FAIL) - return FAIL; + goto fail; // If more than one window, see if sizes can be restored. // First set 'winheight' and 'winwidth' to 1 to avoid the windows being @@ -802,9 +810,9 @@ makeopens( || put_line(fd, "set winheight=1") == FAIL || put_line(fd, "set winminwidth=0") == FAIL || put_line(fd, "set winwidth=1") == FAIL) - return FAIL; + goto fail; if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) - return FAIL; + goto fail; // Restore the tab-local working directory if specified // Do this before the windows, so that the window-local directory can @@ -814,7 +822,7 @@ makeopens( if (fputs("tcd ", fd) < 0 || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL || put_eol(fd) == FAIL) - return FAIL; + goto fail; did_lcd = TRUE; } @@ -823,11 +831,16 @@ makeopens( { if (!ses_do_win(wp)) continue; - if (put_view(fd, wp, wp != edited_win, &ssop_flags, - cur_arg_idx) == FAIL) - return FAIL; + if (put_view(fd, wp, wp != edited_win, &ssop_flags, cur_arg_idx, +#ifdef FEAT_TERMINAL + &terminal_bufs +#else + NULL +#endif + ) == FAIL) + goto fail; if (nr > 1 && put_line(fd, "wincmd w") == FAIL) - return FAIL; + goto fail; next_arg_idx = wp->w_arg_idx; } @@ -839,12 +852,12 @@ makeopens( // Restore cursor to the current window if it's not the first one. if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0 || put_eol(fd) == FAIL)) - return FAIL; + goto fail; // Restore window sizes again after jumping around in windows, because // the current window has a minimum size while others may not. if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) - return FAIL; + goto fail; // Don't continue in another tab page when doing only the current one // or when at the last tab page. @@ -856,10 +869,10 @@ makeopens( { if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0 || put_eol(fd) == FAIL) - return FAIL; + goto fail; } if (restore_stal && put_line(fd, "set stal=1") == FAIL) - return FAIL; + goto fail; // Now put the remaining buffers into the buffer list. // This is near the end, so that when 'hidden' is set we don't create extra @@ -880,38 +893,43 @@ makeopens( if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L : buf->b_wininfo->wi_fpos.lnum) < 0 || ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL) - return FAIL; + goto fail; } } // Wipe out an empty unnamed buffer we started in. if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0") == FAIL) - return FAIL; + goto fail; if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) - return FAIL; + goto fail; if (put_line(fd, "endif") == FAIL) - return FAIL; + goto fail; if (put_line(fd, "unlet! s:wipebuf") == FAIL) - return 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) - return FAIL; + goto fail; // Re-apply 'winminheight' and 'winminwidth'. if (fprintf(fd, "set winminheight=%ld winminwidth=%ld", p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL) - return FAIL; + goto fail; // Lastly, execute the x.vim file if it exists. if (put_line(fd, "let s:sx = expand(\":p:r\").\"x.vim\"") == FAIL || put_line(fd, "if filereadable(s:sx)") == FAIL || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL || put_line(fd, "endif") == FAIL) - return FAIL; + goto fail; - return OK; + ret = OK; +fail: +#ifdef FEAT_TERMINAL + hash_clear_all(&terminal_bufs, 0); +#endif + return ret; } /* @@ -1248,8 +1266,8 @@ ex_mkrc(exarg_T *eap) } else { - failed |= (put_view(fd, curwin, !using_vdir, flagp, - -1) == FAIL); + failed |= (put_view(fd, curwin, !using_vdir, flagp, -1, NULL) + == FAIL); } if (put_line(fd, "let &so = s:so_save | let &siso = s:siso_save") == FAIL) diff --git a/src/spell.c b/src/spell.c index b3d9b0a3cd..6b431fa5bf 100644 --- a/src/spell.c +++ b/src/spell.c @@ -66,10 +66,6 @@ #define REGION_ALL 0xff // word valid in all regions -#define VIMSUGMAGIC "VIMsug" // string at start of Vim .sug file -#define VIMSUGMAGICL 6 -#define VIMSUGVERSION 1 - // Result values. Lower number is accepted over higher one. #define SP_BANNED -1 #define SP_OK 0 diff --git a/src/structs.h b/src/structs.h index 22427a691b..d92ad4902d 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1374,6 +1374,7 @@ struct type_S { #define TTFLAG_VARARGS 1 // func args ends with "..." #define TTFLAG_OPTARG 2 // func arg type with "?" #define TTFLAG_BOOL_OK 4 // can be converted to bool +#define TTFLAG_STATIC 8 // one of the static types, e.g. t_any /* * Structure to hold an internal variable without a name. diff --git a/src/syntax.c b/src/syntax.c index ccdb2030cc..6025889b20 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -6316,9 +6316,11 @@ ex_ownsyntax(exarg_T *eap) #ifdef FEAT_SPELL // TODO: keep the spell checking as it was. curwin->w_p_spell = FALSE; // No spell checking + // make sure option values are "empty_option" instead of NULL clear_string_option(&curwin->w_s->b_p_spc); clear_string_option(&curwin->w_s->b_p_spf); clear_string_option(&curwin->w_s->b_p_spl); + clear_string_option(&curwin->w_s->b_p_spo); #endif clear_string_option(&curwin->w_s->b_syn_isk); } diff --git a/src/terminal.c b/src/terminal.c index de1a20488f..3c6a51e279 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -935,9 +935,30 @@ theend: * Return FAIL if writing fails. */ int -term_write_session(FILE *fd, win_T *wp) +term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs) { - term_T *term = wp->w_buffer->b_term; + const int bufnr = wp->w_buffer->b_fnum; + term_T *term = wp->w_buffer->b_term; + + if (terminal_bufs != NULL && wp->w_buffer->b_nwindows > 1) + { + // There are multiple views into this terminal buffer. We don't want to + // create the terminal multiple times. If it's the first time, create, + // otherwise link to the first buffer. + char id_as_str[NUMBUFLEN]; + hashitem_T *entry; + + vim_snprintf(id_as_str, sizeof(id_as_str), "%d", bufnr); + + entry = hash_find(terminal_bufs, (char_u *)id_as_str); + if (!HASHITEM_EMPTY(entry)) + { + // we've already opened this terminal buffer + if (fprintf(fd, "execute 'buffer ' . s:term_buf_%d", bufnr) < 0) + return FAIL; + return put_eol(fd); + } + } // Create the terminal and run the command. This is not without // risk, but let's assume the user only creates a session when this @@ -951,6 +972,19 @@ term_write_session(FILE *fd, win_T *wp) #endif if (term->tl_command != NULL && fputs((char *)term->tl_command, fd) < 0) return FAIL; + if (put_eol(fd) != OK) + return FAIL; + + if (fprintf(fd, "let s:term_buf_%d = bufnr()", bufnr) < 0) + return FAIL; + + if (terminal_bufs != NULL && wp->w_buffer->b_nwindows > 1) + { + char *hash_key = alloc(NUMBUFLEN); + + vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr); + hash_add(terminal_bufs, (char_u *)hash_key); + } return put_eol(fd); } diff --git a/src/testdir/Make_ming.mak b/src/testdir/Make_ming.mak index f9dcfc86e0..5e17963c5e 100644 --- a/src/testdir/Make_ming.mak +++ b/src/testdir/Make_ming.mak @@ -95,7 +95,7 @@ tinytests: $(SCRIPTS_TINY_OUT) # Copy the input files to dostmp, changing the fileformat to dos. $(DOSTMP)/%.in : %.in if not exist $(DOSTMP)\nul mkdir $(DOSTMP) - if not exist $@ $(DEL) $@ + if exist $(DOSTMP)\$< $(DEL) $(DOSTMP)\$< $(VIMPROG) -u dos.vim $(NO_INITS) "+set ff=dos|f $@|wq" $< # For each input file dostmp/test99.in run the tests. diff --git a/src/testdir/dumps/Test_colorcolumn_2.dump b/src/testdir/dumps/Test_colorcolumn_2.dump new file mode 100644 index 0000000000..44e0e7a2da --- /dev/null +++ b/src/testdir/dumps/Test_colorcolumn_2.dump @@ -0,0 +1,10 @@ +>T+0&#ffffff0|h|e| |q|u|i|c|k| |b|r|o|w|n| |f|o|x| |j|u|m|p|e|d| |o|v|e|r| |t|h|e| @3| +0&#ffd7d7255 +@1| +0&#ffffff0|l+0&#ffd7d7255|a+0&#ffffff0|z|y| |d|o|g|s| @28 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +| +0#0000000&@21|1|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_colorcolumn_3.dump b/src/testdir/dumps/Test_colorcolumn_3.dump new file mode 100644 index 0000000000..6cb0c241cf --- /dev/null +++ b/src/testdir/dumps/Test_colorcolumn_3.dump @@ -0,0 +1,10 @@ +>T+0&#ffffff0|h|e| |q|u|i|c|k| |b|r|o|w|n| |f|o|x| |j|u|m|p|e|d| |o|v|e|r| |t|h|e| |l|a|z|y+0&#ffd7d7255 +|++0#4040ff13&|++0&#ffffff0|++0&#ffd7d7255|>+0&#ffffff0| | +0#0000000&|d|o|g|s| @29 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +| +0#0000000&@21|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_assert.vim b/src/testdir/test_assert.vim index 47dcc1f1e8..04fde2d655 100644 --- a/src/testdir/test_assert.vim +++ b/src/testdir/test_assert.vim @@ -254,35 +254,35 @@ func Test_assert_fail_fails() catch let exp = v:exception endtry - call assert_match("E856: assert_fails() second argument", exp) + call assert_match("E856: \"assert_fails()\" second argument", exp) try call assert_equal(1, assert_fails('xxx', ['1', '2', '3'])) catch let exp = v:exception endtry - call assert_match("E856: assert_fails() second argument", exp) + call assert_match("E856: \"assert_fails()\" second argument", exp) try call assert_equal(1, assert_fails('xxx', #{one: 1})) catch let exp = v:exception endtry - call assert_match("E856: assert_fails() second argument", exp) + call assert_match("E856: \"assert_fails()\" second argument", exp) try call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp')) catch let exp = v:exception endtry - call assert_match("E1115: assert_fails() fourth argument must be a number", exp) + call assert_match("E1115: \"assert_fails()\" fourth argument must be a number", exp) try call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123)) catch let exp = v:exception endtry - call assert_match("E1116: assert_fails() fifth argument must be a string", exp) + call assert_match("E1116: \"assert_fails()\" fifth argument must be a string", exp) endfunc func Test_assert_fails_in_try_block() diff --git a/src/testdir/test_const.vim b/src/testdir/test_const.vim index 5eb2f69949..c88efa632b 100644 --- a/src/testdir/test_const.vim +++ b/src/testdir/test_const.vim @@ -215,6 +215,14 @@ func Test_lockvar() if 0 | lockvar x | endif let x = 'again' + + let val = [1, 2, 3] + lockvar 0 val + let val[0] = 9 + call assert_equal([9, 2, 3], val) + call add(val, 4) + call assert_equal([9, 2, 3, 4], val) + call assert_fails('let val = [4, 5, 6]', 'E1122:') endfunc diff --git a/src/testdir/test_expand_func.vim b/src/testdir/test_expand_func.vim index 42aa8e01bc..3c430e1c61 100644 --- a/src/testdir/test_expand_func.vim +++ b/src/testdir/test_expand_func.vim @@ -39,9 +39,9 @@ endfunc func Test_expand_sfile_and_stack() call assert_match('test_expand_func\.vim$', s:sfile) - let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$' - call assert_match(expected , expand('')) - call assert_match(expected , expand('')) + let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack' + call assert_match(expected .. '$', expand('')) + call assert_match(expected .. '\[4\]' , expand('')) " Call in script-local function call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.\d\+_expand_sfile$', s:expand_sfile()) @@ -53,11 +53,12 @@ func Test_expand_sfile_and_stack() " Use from sourced script. let lines =<< trim END + " comment here let g:stack_value = expand('') END call writefile(lines, 'Xstack') source Xstack - call assert_match('\map({_, v -> bufadd(v) + bufload(v)}) + let l = getbufinfo()->map({_, v -> v.name})->matchfuzzy('ndl') + call assert_equal(1, len(l)) + call assert_match('needle', l[0]) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_gf.vim b/src/testdir/test_gf.vim index eaa8c0fc95..deede22f88 100644 --- a/src/testdir/test_gf.vim +++ b/src/testdir/test_gf.vim @@ -124,7 +124,7 @@ func Test_gf() endfunc func Test_gf_visual() - call writefile([], "Xtest_gf_visual") + call writefile(['one', 'two', 'three', 'four'], "Xtest_gf_visual") new call setline(1, 'XXXtest_gf_visualXXX') set hidden @@ -138,6 +138,25 @@ func Test_gf_visual() normal VGgf call assert_equal('Xtest_gf_visual', @%) + " following line number is used for gF + bwipe! + new + call setline(1, 'XXXtest_gf_visual:3XXX') + norm! 0ttvt:gF + call assert_equal('Xtest_gf_visual', bufname('%')) + call assert_equal(3, getcurpos()[1]) + + " line number in visual area is used for file name + if has('unix') + bwipe! + call writefile([], "Xtest_gf_visual:3") + new + call setline(1, 'XXXtest_gf_visual:3XXX') + norm! 0ttvtXgF + call assert_equal('Xtest_gf_visual:3', bufname('%')) + call delete('Xtest_gf_visual:3') + endif + bwipe! call delete('Xtest_gf_visual') set hidden& diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index b5e382f1a5..5db2c57cb8 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -662,6 +662,42 @@ func Test_colorcolumn() call delete('Xtest_colorcolumn') endfunc +func Test_colorcolumn_bri() + CheckScreendump + + " check 'colorcolumn' when 'breakindent' is set + let lines =<< trim END + call setline(1, 'The quick brown fox jumped over the lazy dogs') + END + call writefile(lines, 'Xtest_colorcolumn_bri') + let buf = RunVimInTerminal('-S Xtest_colorcolumn_bri', {'rows': 10,'columns': 40}) + call term_sendkeys(buf, ":set co=40 linebreak bri briopt=shift:2 cc=40,41,43\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_colorcolumn_2', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_colorcolumn_bri') +endfunc + +func Test_colorcolumn_sbr() + CheckScreendump + + " check 'colorcolumn' when 'showbreak' is set + let lines =<< trim END + call setline(1, 'The quick brown fox jumped over the lazy dogs') + END + call writefile(lines, 'Xtest_colorcolumn_srb') + let buf = RunVimInTerminal('-S Xtest_colorcolumn_srb', {'rows': 10,'columns': 40}) + call term_sendkeys(buf, ":set co=40 showbreak=+++>\\ cc=40,41,43\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_colorcolumn_3', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_colorcolumn_srb') +endfunc + " This test must come before the Test_cursorline test, as it appears this " defines the Normal highlighting group anyway. func Test_1_highlight_Normalgroup_exists() @@ -796,4 +832,36 @@ func Test_highlight_term_attr() hi clear endfunc +" Test default highlighting is restored +func Test_highlight_restore_defaults() + hi! link TestLink Identifier + hi! TestHi ctermbg=red + + let hlTestLinkPre = HighlightArgs('TestLink') + let hlTestHiPre = HighlightArgs('TestHi') + + " Test colorscheme + hi clear + if exists('syntax_on') + syntax reset + endif + let g:colors_name = 'test' + hi! link TestLink ErrorMsg + hi! TestHi ctermbg=green + + " Restore default highlighting + colorscheme default + syntax on + " 'default' should work no matter if highlight group was cleared + hi def link TestLink Identifier + hi def TestHi ctermbg=red + + let hlTestLinkPost = HighlightArgs('TestLink') + let hlTestHiPost = HighlightArgs('TestHi') + + call assert_equal(hlTestLinkPre, hlTestLinkPost) + call assert_equal(hlTestHiPre, hlTestHiPost) + hi clear +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index 41d6b7f50e..992fefab43 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -313,6 +313,24 @@ func Test_CompleteDone_undo() au! CompleteDone endfunc +func CompleteTest(findstart, query) + if a:findstart + return col('.') + endif + return ['matched'] +endfunc + +func Test_completefunc_info() + new + set completeopt=menuone + set completefunc=CompleteTest + call feedkeys("i\\\\=string(complete_info())\\", "tx") + call assert_equal("matched{'pum_visible': 1, 'mode': 'function', 'selected': -1, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1)) + bwipe! + set completeopt& + set completefunc& +endfunc + " Check that when using feedkeys() typeahead does not interrupt searching for " completions. func Test_compl_feedkeys() diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index 694ba0a410..66b84060a1 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -354,7 +354,7 @@ endfunc " Locked variables func Test_list_locked_var() let expected = [ - \ [['0000-000', 'ppppppp'], + \ [['1000-000', 'ppppppF'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppppppF'], @@ -381,7 +381,7 @@ func Test_list_locked_var() exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") - call assert_equal(expected[depth][u][0], ps) + call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try let l[1][1][0] = 99 @@ -425,7 +425,7 @@ func Test_list_locked_var() catch let ps .= 'F' endtry - call assert_equal(expected[depth][u][1], ps) + call assert_equal(expected[depth][u][1], ps, 'depth: ' .. depth) endfor endfor call assert_fails("let x=islocked('a b')", 'E488:') @@ -438,7 +438,7 @@ endfunc " Unletting locked variables func Test_list_locked_var_unlet() let expected = [ - \ [['0000-000', 'ppppppp'], + \ [['1000-000', 'ppppppp'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppFppFp'], @@ -466,7 +466,7 @@ func Test_list_locked_var_unlet() exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") - call assert_equal(expected[depth][u][0], ps) + call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try unlet l[2]['6'][7] @@ -666,6 +666,9 @@ func Test_func_arg_list() call s:arg_list_test(1, 2, [3, 4], {5: 6}) endfunc +func Test_dict_item_locked() +endfunc + " Tests for reverse(), sort(), uniq() func Test_reverse_sort_uniq() let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] diff --git a/src/testdir/test_mksession.vim b/src/testdir/test_mksession.vim index 5693b97ac0..06be838296 100644 --- a/src/testdir/test_mksession.vim +++ b/src/testdir/test_mksession.vim @@ -351,9 +351,8 @@ func Test_mksession_blank_windows() call delete('Xtest_mks.out') endfunc -if has('terminal') - func Test_mksession_terminal_shell() + CheckFeature terminal CheckFeature quickfix terminal @@ -374,6 +373,8 @@ func Test_mksession_terminal_shell() endfunc func Test_mksession_terminal_no_restore_cmdarg() + CheckFeature terminal + terminal ++norestore mksession! Xtest_mks.out let lines = readfile('Xtest_mks.out') @@ -389,6 +390,8 @@ func Test_mksession_terminal_no_restore_cmdarg() endfunc func Test_mksession_terminal_no_restore_funcarg() + CheckFeature terminal + call term_start(&shell, {'norestore': 1}) mksession! Xtest_mks.out let lines = readfile('Xtest_mks.out') @@ -404,6 +407,8 @@ func Test_mksession_terminal_no_restore_funcarg() endfunc func Test_mksession_terminal_no_restore_func() + CheckFeature terminal + terminal call term_setrestore(bufnr('%'), 'NONE') mksession! Xtest_mks.out @@ -420,6 +425,8 @@ func Test_mksession_terminal_no_restore_func() endfunc func Test_mksession_terminal_no_ssop() + CheckFeature terminal + terminal set sessionoptions-=terminal mksession! Xtest_mks.out @@ -437,6 +444,7 @@ func Test_mksession_terminal_no_ssop() endfunc func Test_mksession_terminal_restore_other() + CheckFeature terminal CheckFeature quickfix terminal @@ -455,7 +463,46 @@ func Test_mksession_terminal_restore_other() call delete('Xtest_mks.out') endfunc -endif " has('terminal') +func Test_mksession_terminal_shared_windows() + CheckFeature terminal + + terminal + let term_buf = bufnr() + new + execute "buffer" term_buf + mksession! Xtest_mks.out + + let lines = readfile('Xtest_mks.out') + let found_creation = 0 + let found_use = 0 + + for line in lines + if line =~ '^terminal' + let found_creation = 1 + call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+', line) + elseif line =~ "^execute 'buffer ' . s:term_buf_" . term_buf . "$" + let found_use = 1 + endif + endfor + + call assert_true(found_creation && found_use) + + call StopShellInTerminal(term_buf) + call delete('Xtest_mks.out') +endfunc + +func Test_mkview_terminal_windows() + CheckFeature terminal + + " create two window on the same terminal to check this is handled OK + terminal + let term_buf = bufnr() + exe 'sbuf ' .. term_buf + mkview! Xtestview + + call StopShellInTerminal(term_buf) + call delete('Xtestview') +endfunc " Test :mkview with a file argument. func Test_mkview_file() diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index d943394f28..be23c03c92 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -1,5 +1,6 @@ " Test for options +source shared.vim source check.vim source view_util.vim @@ -587,6 +588,35 @@ func Test_backupskip() endif endfor + " Duplicates from environment variables should be filtered out (option has + " P_NODUP). Run this in a separate instance and write v:errors in a file, + " so that we see what happens on startup. + let after =<< trim [CODE] + let bsklist = split(&backupskip, ',') + call assert_equal(uniq(copy(bsklist)), bsklist) + call writefile(['errors:'] + v:errors, 'Xtestout') + qall + [CODE] + call writefile(after, 'Xafter') + let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + + let saveenv = {} + for var in ['TMPDIR', 'TMP', 'TEMP'] + let saveenv[var] = getenv(var) + call setenv(var, '/duplicate/path') + endfor + + exe 'silent !' . cmd + call assert_equal(['errors:'], readfile('Xtestout')) + + " restore environment variables + for var in ['TMPDIR', 'TMP', 'TEMP'] + call setenv(var, saveenv[var]) + endfor + + call delete('Xtestout') + call delete('Xafter') + " Duplicates should be filtered out (option has P_NODUP) let backupskip = &backupskip set backupskip= diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 1e753a467c..0d8e35538b 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -1430,6 +1430,30 @@ func Test_quickfix_was_changed_by_autocmd() call XquickfixChangedByAutocmd('l') endfunc +func Test_setloclist_in_autocommand() + call writefile(['test1', 'test2'], 'Xfile') + edit Xfile + let s:bufnr = bufnr() + call setloclist(1, + \ [{'bufnr' : s:bufnr, 'lnum' : 1, 'text' : 'test1'}, + \ {'bufnr' : s:bufnr, 'lnum' : 2, 'text' : 'test2'}]) + + augroup Test_LocList + au! + autocmd BufEnter * call setloclist(1, + \ [{'bufnr' : s:bufnr, 'lnum' : 1, 'text' : 'test1'}, + \ {'bufnr' : s:bufnr, 'lnum' : 2, 'text' : 'test2'}], 'r') + augroup END + + lopen + call assert_fails('exe "normal j\"', 'E926:') + + augroup Test_LocList + au! + augroup END + call delete('Xfile') +endfunc + func Test_caddbuffer_to_empty() helpgr quickfix call setqflist([], 'r') diff --git a/src/testdir/test_spell.vim b/src/testdir/test_spell.vim index c2de5ed0fb..4beff9a1a1 100644 --- a/src/testdir/test_spell.vim +++ b/src/testdir/test_spell.vim @@ -112,6 +112,7 @@ foobar/? set spelllang= call assert_fails("call spellbadword('maxch')", 'E756:') + call assert_fails("spelldump", 'E756:') call delete('Xwords.spl') call delete('Xwords') diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index 16a07b1caa..49e02326a7 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -307,6 +307,9 @@ func Test_spellfile_format_error() " SN_SOFO: empty sofofrom and sofoto call Spellfile_Test(0z06000000000400000000FF000000000000000000000000, '') + " SN_SOFO: multi-byte characters in sofofrom and sofoto + call Spellfile_Test(0z0600000000080002CF810002CF82FF000000000000000000000000, '') + " SN_COMPOUND: compmax is less than 2 call Spellfile_Test(0z08000000000101, 'E759:') @@ -550,8 +553,14 @@ endfunc " Test for the :mkspell command func Test_mkspell() call assert_fails('mkspell Xtest_us.spl', 'E751:') + call assert_fails('mkspell Xtest.spl abc', 'E484:') call assert_fails('mkspell a b c d e f g h i j k', 'E754:') + " create a .aff file but not the .dic file + call writefile([], 'Xtest.aff') + call assert_fails('mkspell Xtest.spl Xtest', 'E484:') + call delete('Xtest.aff') + call writefile([], 'Xtest.spl') call writefile([], 'Xtest.dic') call assert_fails('mkspell Xtest.spl Xtest.dic', 'E13:') @@ -772,6 +781,14 @@ func Test_aff_file_format_error() call assert_fails('mkspell! Xtest.spl Xtest', 'E761:') let &encoding = save_encoding + " missing UPP entry + call writefile(["FOL abc", "LOW abc"], 'Xtest.aff') + let save_encoding = &encoding + set encoding=cp949 + let output = execute('mkspell! Xtest.spl Xtest') + call assert_match('Missing FOL/LOW/UPP line in Xtest.aff', output) + let &encoding = save_encoding + " duplicate word in the .dic file call writefile(['2', 'good', 'good', 'good'], 'Xtest.dic') call writefile(['NAME vim'], 'Xtest.aff') @@ -779,6 +796,20 @@ func Test_aff_file_format_error() call assert_match('First duplicate word in Xtest.dic line 3: good', output) call assert_match('2 duplicate word(s) in Xtest.dic', output) + " use multiple .aff files with different values for COMPOUNDWORDMAX and + " MIDWORD (number and string) + call writefile(['1', 'world'], 'Xtest_US.dic') + call writefile(['1', 'world'], 'Xtest_CA.dic') + call writefile(["COMPOUNDWORDMAX 3", "MIDWORD '-"], 'Xtest_US.aff') + call writefile(["COMPOUNDWORDMAX 4", "MIDWORD '="], 'Xtest_CA.aff') + let output = execute('mkspell! Xtest.spl Xtest_US Xtest_CA') + call assert_match('COMPOUNDWORDMAX value differs from what is used in another .aff file', output) + call assert_match('MIDWORD value differs from what is used in another .aff file', output) + call delete('Xtest_US.dic') + call delete('Xtest_CA.dic') + call delete('Xtest_US.aff') + call delete('Xtest_CA.aff') + call delete('Xtest.dic') call delete('Xtest.aff') call delete('Xtest.spl') diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim index cd06892733..8a021851f3 100644 --- a/src/testdir/test_syntax.vim +++ b/src/testdir/test_syntax.vim @@ -428,7 +428,11 @@ func Test_ownsyntax() call setline(1, '#define FOO') syntax on set filetype=c + ownsyntax perl + " this should not crash + set + call assert_equal('perlComment', synIDattr(synID(line('.'), col('.'), 1), 'name')) call assert_equal('c', b:current_syntax) call assert_equal('perl', w:current_syntax) diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index ff7102f6ab..5bd7e95c3d 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -1273,7 +1273,7 @@ func Test_prop_func_invalid_args() call assert_fails("call prop_type_list([])", 'E715:') endfunc -func Test_split_join() +func Test_prop_split_join() new call prop_type_add('test', {'highlight': 'ErrorMsg'}) call setline(1, 'just some text') @@ -1294,4 +1294,55 @@ func Test_split_join() call prop_type_delete('test') endfunc +func Test_prop_increment_decrement() + new + call prop_type_add('test', {'highlight': 'ErrorMsg'}) + call setline(1, 'its 998 times') + call prop_add(1, 5, {'length': 3, 'type': 'test'}) + + exe "normal! 0f9\" + eval getline(1)->assert_equal('its 999 times') + eval prop_list(1)->assert_equal([ + \ #{id: 0, col: 5, end: 1, type: 'test', length: 3, start: 1}]) + + exe "normal! 0f9\" + eval getline(1)->assert_equal('its 1000 times') + eval prop_list(1)->assert_equal([ + \ #{id: 0, col: 5, end: 1, type: 'test', length: 4, start: 1}]) + + bwipe! + call prop_type_delete('test') +endfunc + +func Test_prop_block_insert() + new + call prop_type_add('test', {'highlight': 'ErrorMsg'}) + call setline(1, ['one ', 'two ']) + call prop_add(1, 1, {'length': 3, 'type': 'test'}) + call prop_add(2, 1, {'length': 3, 'type': 'test'}) + + " insert "xx" in the first column of both lines + exe "normal! gg0\jIxx\" + eval getline(1, 2)->assert_equal(['xxone ', 'xxtwo ']) + let expected = [#{id: 0, col: 3, end: 1, type: 'test', length: 3, start: 1}] + eval prop_list(1)->assert_equal(expected) + eval prop_list(2)->assert_equal(expected) + + " insert "yy" inside the text props to make them longer + exe "normal! gg03l\jIyy\" + eval getline(1, 2)->assert_equal(['xxoyyne ', 'xxtyywo ']) + let expected[0].length = 5 + eval prop_list(1)->assert_equal(expected) + eval prop_list(2)->assert_equal(expected) + + " insert "zz" after the text props, text props don't change + exe "normal! gg07l\jIzz\" + eval getline(1, 2)->assert_equal(['xxoyynezz ', 'xxtyywozz ']) + eval prop_list(1)->assert_equal(expected) + eval prop_list(2)->assert_equal(expected) + + bwipe! + call prop_type_delete('test') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 4b4e58e943..6b97372786 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -330,6 +330,17 @@ def Test_put_command() bwipe! enddef +def Test_command_star_range() + new + setline(1, ['xxx foo xxx', 'xxx bar xxx', 'xxx foo xx bar']) + setpos("'<", [0, 1, 0, 0]) + setpos("'>", [0, 3, 0, 0]) + :*s/\(foo\|bar\)/baz/g + getline(1, 3)->assert_equal(['xxx baz xxx', 'xxx baz xxx', 'xxx baz xx baz']) + + bwipe! +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 78d0a62aad..7fd8c062b1 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -258,7 +258,7 @@ def Test_disassemble_list_assign() '\d STORE $2\_s*' .. '\[x, y; l\] = g:stringlist\_s*' .. '\d LOADG g:stringlist\_s*' .. - '\d CHECKTYPE list stack\[-1\]\_s*' .. + '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d CHECKLEN >= 2\_s*' .. '\d\+ ITEM 0\_s*' .. '\d\+ CHECKTYPE string stack\[-1\]\_s*' .. @@ -829,7 +829,7 @@ def Test_disassemble_for_loop_eval() '\d STORE -1 in $1\_s*' .. '\d PUSHS "\["one", "two"\]"\_s*' .. '\d BCALL eval(argc 1)\_s*' .. - '\d CHECKTYPE list stack\[-1\]\_s*' .. + '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d FOR $1 -> \d\+\_s*' .. '\d STORE $2\_s*' .. 'res ..= str\_s*' .. @@ -1144,7 +1144,7 @@ def Test_disassemble_any_slice() '\d STORE $0\_s*' .. 'return res\_s*' .. '\d LOAD $0\_s*' .. - '\d CHECKTYPE list stack\[-1\]\_s*' .. + '\d CHECKTYPE list stack\[-1\]\_s*' .. '\d RETURN', instr) assert_equal([2, 3, 4], AnySlice()) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 8cf5bc0d60..d79e2cd6bc 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -143,12 +143,12 @@ enddef func Test_expr1_fails() call CheckDefFailure(["let x = 1 ? 'one'"], "Missing ':' after '?'", 1) - let msg = "white space required before and after '?'" + let msg = "White space required before and after '?'" call CheckDefFailure(["let x = 1? 'one' : 'two'"], msg, 1) call CheckDefFailure(["let x = 1 ?'one' : 'two'"], msg, 1) call CheckDefFailure(["let x = 1?'one' : 'two'"], msg, 1) - let msg = "white space required before and after ':'" + let msg = "White space required before and after ':'" call CheckDefFailure(["let x = 1 ? 'one': 'two'"], msg, 1) call CheckDefFailure(["let x = 1 ? 'one' :'two'"], msg, 1) call CheckDefFailure(["let x = 1 ? 'one':'two'"], msg, 1) @@ -276,7 +276,7 @@ def Test_expr2_vimscript() enddef func Test_expr2_fails() - let msg = "white space required before and after '||'" + let msg = "White space required before and after '||'" call CheckDefFailure(["let x = 1||2"], msg, 1) call CheckDefFailure(["let x = 1 ||2"], msg, 1) call CheckDefFailure(["let x = 1|| 2"], msg, 1) @@ -401,7 +401,7 @@ def Test_expr3_vimscript() enddef func Test_expr3_fails() - let msg = "white space required before and after '&&'" + let msg = "White space required before and after '&&'" call CheckDefFailure(["let x = 1&&2"], msg, 1) call CheckDefFailure(["let x = 1 &&2"], msg, 1) call CheckDefFailure(["let x = 1&& 2"], msg, 1) @@ -481,6 +481,7 @@ def Test_expr4_equal() set noignorecase CheckDefFailure(["let x = 'a' == xxx"], 'E1001:', 1) + CheckDefExecFailure(['let items: any', 'eval 1', 'eval 2', 'if items == []', 'endif'], 'E691:', 4) let bb = 0z3f assert_equal(true, 0z3f == bb) @@ -860,22 +861,22 @@ def Test_expr4_vim9script() enddef func Test_expr4_fails() - let msg = "white space required before and after '>'" + let msg = "White space required before and after '>'" call CheckDefFailure(["let x = 1>2"], msg, 1) call CheckDefFailure(["let x = 1 >2"], msg, 1) call CheckDefFailure(["let x = 1> 2"], msg, 1) - let msg = "white space required before and after '=='" + let msg = "White space required before and after '=='" call CheckDefFailure(["let x = 1==2"], msg, 1) call CheckDefFailure(["let x = 1 ==2"], msg, 1) call CheckDefFailure(["let x = 1== 2"], msg, 1) - let msg = "white space required before and after 'is'" + let msg = "White space required before and after 'is'" call CheckDefFailure(["let x = '1'is'2'"], msg, 1) call CheckDefFailure(["let x = '1' is'2'"], msg, 1) call CheckDefFailure(["let x = '1'is '2'"], msg, 1) - let msg = "white space required before and after 'isnot'" + let msg = "White space required before and after 'isnot'" call CheckDefFailure(["let x = '1'isnot'2'"], msg, 1) call CheckDefFailure(["let x = '1' isnot'2'"], msg, 1) call CheckDefFailure(["let x = '1'isnot '2'"], msg, 1) @@ -1150,17 +1151,17 @@ def Test_expr5_float() enddef func Test_expr5_fails() - let msg = "white space required before and after '+'" + let msg = "White space required before and after '+'" call CheckDefFailure(["let x = 1+2"], msg, 1) call CheckDefFailure(["let x = 1 +2"], msg, 1) call CheckDefFailure(["let x = 1+ 2"], msg, 1) - let msg = "white space required before and after '-'" + let msg = "White space required before and after '-'" call CheckDefFailure(["let x = 1-2"], msg, 1) call CheckDefFailure(["let x = 1 -2"], msg, 1) call CheckDefFailure(["let x = 1- 2"], msg, 1) - let msg = "white space required before and after '..'" + let msg = "White space required before and after '..'" call CheckDefFailure(["let x = '1'..'2'"], msg, 1) call CheckDefFailure(["let x = '1' ..'2'"], msg, 1) call CheckDefFailure(["let x = '1'.. '2'"], msg, 1) @@ -1305,17 +1306,17 @@ def Test_expr6_float() enddef func Test_expr6_fails() - let msg = "white space required before and after '*'" + let msg = "White space required before and after '*'" call CheckDefFailure(["let x = 1*2"], msg, 1) call CheckDefFailure(["let x = 1 *2"], msg, 1) call CheckDefFailure(["let x = 1* 2"], msg, 1) - let msg = "white space required before and after '/'" + let msg = "White space required before and after '/'" call CheckDefFailure(["let x = 1/2"], msg, 1) call CheckDefFailure(["let x = 1 /2"], msg, 1) call CheckDefFailure(["let x = 1/ 2"], msg, 1) - let msg = "white space required before and after '%'" + let msg = "White space required before and after '%'" call CheckDefFailure(["let x = 1%2"], msg, 1) call CheckDefFailure(["let x = 1 %2"], msg, 1) call CheckDefFailure(["let x = 1% 2"], msg, 1) @@ -1437,8 +1438,11 @@ def Test_expr7_vimvar() let old: list = v:oldfiles let compl: dict = v:completed_item - CheckDefFailure(["let old: list = v:oldfiles"], 'E1012: type mismatch, expected list but got list', 1) - CheckDefFailure(["let old: dict = v:completed_item"], 'E1012: type mismatch, expected dict but got dict', 1) + CheckDefFailure(["let old: list = v:oldfiles"], 'E1012: Type mismatch; expected list but got list', 1) + new + exec "normal! afoo fo\\" + CheckDefExecFailure(["let old: dict = v:completed_item"], 'E1012: Type mismatch; expected dict but got dict', 1) + bwipe! enddef def Test_expr7_special() @@ -1519,14 +1523,14 @@ def Test_expr7_list() CheckDefExecFailure(["echo 1", "let x = [][0]", "echo 3"], 'E684:', 2) - CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1029:', 1) + CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E1012:', 1) CheckDefFailure(["let x = g:list_mixed["], 'E1097:', 2) CheckDefFailure(["let x = g:list_mixed[0"], 'E1097:', 2) CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:', 1) - CheckDefFailure(["let l: list = [234, 'x']"], 'E1012:', 1) - CheckDefFailure(["let l: list = ['x', 234]"], 'E1012:', 1) - CheckDefFailure(["let l: list = [234, 'x']"], 'E1012:', 1) - CheckDefFailure(["let l: list = ['x', 123]"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = [234, 'x']"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = ['x', 234]"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = [234, 'x']"], 'E1012:', 1) + CheckDefExecFailure(["let l: list = ['x', 123]"], 'E1012:', 1) enddef def Test_expr7_list_vim9script() @@ -1654,7 +1658,7 @@ def Test_expr7_lambda() assert_equal('xxxyyy', 'xxx'->{a, b -> a .. b}('yyy')) CheckDefExecFailure(["let s = 'asdf'->{a -> a}('x')"], - 'E1106: one argument too many') + 'E1106: One argument too many') CheckDefExecFailure(["let s = 'asdf'->{a -> a}('x', 'y')"], 'E1106: 2 arguments too many') CheckDefFailure(["echo 'asdf'->{a -> a}(x)"], 'E1001:', 1) @@ -1730,10 +1734,10 @@ def Test_expr7_dict() CheckDefExecFailure(["let x = g:anint.member"], 'E715:', 1) CheckDefExecFailure(["let x = g:dict_empty.member"], 'E716:', 1) - CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) - CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) - CheckDefFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) - CheckDefFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: 234, b: "1"}'], 'E1012:', 1) + CheckDefExecFailure(['let x: dict = #{a: "x", b: 134}'], 'E1012:', 1) enddef def Test_expr7_dict_vim9script() @@ -1839,7 +1843,7 @@ def Test_expr_member() CheckDefFailure(["let x = g:dict_one.#$!"], 'E1002:', 1) CheckDefExecFailure(["let d: dict", "echo d['a']"], 'E716:', 2) - CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1029: Expected dict but got list', 2) + CheckDefExecFailure(["let d: dict", "d = g:list_empty"], 'E1012: Type mismatch; expected dict but got list', 2) enddef def Test_expr7_any_index_slice() @@ -2310,10 +2314,20 @@ def Test_expr7_list_subscript() CheckScriptSuccess(['vim9script'] + lines) lines = ['let l = [0, 1, 2]', 'echo l[g:astring : g:theone]'] - CheckDefExecFailure(lines, 'E1029:') + CheckDefExecFailure(lines, 'E1012:') CheckScriptFailure(['vim9script'] + lines, 'E1030:', 3) enddef +def Test_expr7_dict_subscript() + let lines =<< trim END + vim9script + let l = [#{lnum: 2}, #{lnum: 1}] + let res = l[0].lnum > l[1].lnum + assert_true(res) + END + CheckScriptSuccess(lines) +enddef + def Test_expr7_subscript_linebreak() let range = range( 3) @@ -2369,6 +2383,9 @@ def Test_expr7_method_call() type: '', module: ''} ], getloclist(0)) + + let result: bool = get(#{n: 0}, 'n', 0) + assert_equal(false, result) enddef func Test_expr7_trailing_fails() diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index ae8f329f97..f3aae7c6ca 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -10,7 +10,7 @@ func Test_def_basic() def SomeFunc(): string return 'yes' enddef - call assert_equal('yes', SomeFunc()) + call SomeFunc()->assert_equal('yes') endfunc def ReturnString(): string @@ -28,9 +28,9 @@ def ReturnGlobal(): number enddef def Test_return_something() - assert_equal('string', ReturnString()) - assert_equal(123, ReturnNumber()) - assert_fails('ReturnGlobal()', 'E1029: Expected number but got string', '', 1, 'ReturnGlobal') + ReturnString()->assert_equal('string') + ReturnNumber()->assert_equal(123) + assert_fails('ReturnGlobal()', 'E1012: Type mismatch; expected number but got string', '', 1, 'ReturnGlobal') enddef def Test_missing_return() @@ -69,7 +69,7 @@ enddef def Test_return_nothing() ReturnNothing() - assert_equal(1, s:nothing) + s:nothing->assert_equal(1) enddef func Increment() @@ -82,8 +82,8 @@ def Test_call_ufunc_count() Increment() Increment() # works with and without :call - assert_equal(4, g:counter) - call assert_equal(4, g:counter) + g:counter->assert_equal(4) + eval g:counter->assert_equal(4) unlet g:counter enddef @@ -96,9 +96,9 @@ def MyVarargs(arg: string, ...rest: list): string enddef def Test_call_varargs() - assert_equal('one', MyVarargs('one')) - assert_equal('one,two', MyVarargs('one', 'two')) - assert_equal('one,two,three', MyVarargs('one', 'two', 'three')) + MyVarargs('one')->assert_equal('one') + MyVarargs('one', 'two')->assert_equal('one,two') + MyVarargs('one', 'two', 'three')->assert_equal('one,two,three') enddef def MyDefaultArgs(name = 'string'): string @@ -110,23 +110,23 @@ def MyDefaultSecond(name: string, second: bool = true): string enddef def Test_call_default_args() - assert_equal('string', MyDefaultArgs()) - assert_equal('one', MyDefaultArgs('one')) + MyDefaultArgs()->assert_equal('string') + MyDefaultArgs('one')->assert_equal('one') assert_fails('MyDefaultArgs("one", "two")', 'E118:', '', 3, 'Test_call_default_args') - assert_equal('test', MyDefaultSecond('test')) - assert_equal('test', MyDefaultSecond('test', true)) - assert_equal('none', MyDefaultSecond('test', false)) + MyDefaultSecond('test')->assert_equal('test') + MyDefaultSecond('test', true)->assert_equal('test') + MyDefaultSecond('test', false)->assert_equal('none') CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef', 'defcompile'], 'E1001:') - CheckScriptFailure(['def Func(arg: number = "text")', 'enddef', 'defcompile'], 'E1013: argument 1: type mismatch, expected number but got string') + CheckScriptFailure(['def Func(arg: number = "text")', 'enddef', 'defcompile'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef def Test_nested_function() def Nested(arg: string): string return 'nested ' .. arg enddef - assert_equal('nested function', Nested('function')) + Nested('function')->assert_equal('nested function') CheckDefFailure(['def Nested()', 'enddef', 'Nested(66)'], 'E118:') CheckDefFailure(['def Nested(arg: string)', 'enddef', 'Nested()'], 'E119:') @@ -134,11 +134,28 @@ def Test_nested_function() CheckDefFailure(['func Nested()', 'endfunc'], 'E1086:') CheckDefFailure(['def s:Nested()', 'enddef'], 'E1075:') CheckDefFailure(['def b:Nested()', 'enddef'], 'E1075:') + + CheckDefFailure([ + 'def Outer()', + ' def Inner()', + ' # comment', + ' enddef', + ' def Inner()', + ' enddef', + 'enddef'], 'E1073:') + CheckDefFailure([ + 'def Outer()', + ' def Inner()', + ' # comment', + ' enddef', + ' def! Inner()', + ' enddef', + 'enddef'], 'E1117:') enddef func Test_call_default_args_from_func() - call assert_equal('string', MyDefaultArgs()) - call assert_equal('one', MyDefaultArgs('one')) + call MyDefaultArgs()->assert_equal('string') + call MyDefaultArgs('one')->assert_equal('one') call assert_fails('call MyDefaultArgs("one", "two")', 'E118:', '', 3, 'Test_call_default_args_from_func') endfunc @@ -152,13 +169,13 @@ def Test_nested_global_function() enddef defcompile Outer() - assert_equal('inner', g:Inner()) + g:Inner()->assert_equal('inner') delfunc g:Inner Outer() - assert_equal('inner', g:Inner()) + g:Inner()->assert_equal('inner') delfunc g:Inner Outer() - assert_equal('inner', g:Inner()) + g:Inner()->assert_equal('inner') delfunc g:Inner END CheckScriptSuccess(lines) @@ -200,8 +217,8 @@ def Test_global_local_function() def Func(): string return 'local' enddef - assert_equal('global', g:Func()) - assert_equal('local', Func()) + g:Func()->assert_equal('global') + Func()->assert_equal('local') END CheckScriptSuccess(lines) @@ -215,6 +232,36 @@ def Test_global_local_function() CheckScriptFailure(lines, 'E117:') enddef +def Test_local_function_shadows_global() + let lines =<< trim END + vim9script + def g:Gfunc(): string + return 'global' + enddef + def AnotherFunc(): number + let Gfunc = function('len') + return Gfunc('testing') + enddef + g:Gfunc()->assert_equal('global') + AnotherFunc()->assert_equal(7) + delfunc g:Gfunc + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + def g:Func(): string + return 'global' + enddef + def AnotherFunc() + g:Func = function('len') + enddef + AnotherFunc() + END + CheckScriptFailure(lines, 'E705:') + delfunc g:Func +enddef + func TakesOneArg(arg) echo a:arg endfunc @@ -232,7 +279,7 @@ def Test_call_wrong_args() enddef Func([]) END - call CheckScriptFailure(lines, 'E1013: argument 1: type mismatch, expected string but got list', 5) + CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got list', 5) enddef " Default arg and varargs @@ -246,13 +293,13 @@ enddef def Test_call_def_varargs() assert_fails('MyDefVarargs()', 'E119:', '', 1, 'Test_call_def_varargs') - assert_equal('one,foo', MyDefVarargs('one')) - assert_equal('one,two', MyDefVarargs('one', 'two')) - assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three')) + MyDefVarargs('one')->assert_equal('one,foo') + MyDefVarargs('one', 'two')->assert_equal('one,two') + MyDefVarargs('one', 'two', 'three')->assert_equal('one,two,three') CheckDefFailure(['MyDefVarargs("one", 22)'], - 'E1013: argument 2: type mismatch, expected string but got number') + 'E1013: Argument 2: type mismatch, expected string but got number') CheckDefFailure(['MyDefVarargs("one", "two", 123)'], - 'E1013: argument 3: type mismatch, expected string but got number') + 'E1013: Argument 3: type mismatch, expected string but got number') let lines =<< trim END vim9script @@ -272,6 +319,15 @@ def Test_call_def_varargs() END CheckScriptSuccess(lines) + lines =<< trim END + vim9script + def Func(...l: any) + echo l + enddef + Func(0) + END + CheckScriptSuccess(lines) + lines =<< trim END vim9script def Func(...l: list) @@ -279,7 +335,7 @@ def Test_call_def_varargs() enddef Func(1, 2, 3) END - CheckScriptFailure(lines, 'E1013: argument 1: type mismatch') + CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch') lines =<< trim END vim9script @@ -288,7 +344,7 @@ def Test_call_def_varargs() enddef Func('a', 9) END - CheckScriptFailure(lines, 'E1013: argument 2: type mismatch') + CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch') lines =<< trim END vim9script @@ -297,13 +353,13 @@ def Test_call_def_varargs() enddef Func(1, 'a') END - CheckScriptFailure(lines, 'E1013: argument 1: type mismatch') + CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch') enddef def Test_call_call() let l = [3, 2, 1] call('reverse', [l]) - assert_equal([1, 2, 3], l) + l->assert_equal([1, 2, 3]) enddef let s:value = '' @@ -324,24 +380,24 @@ def Test_func_type_varargs() let RefDefArg: func(?string) RefDefArg = FuncOneDefArg RefDefArg() - assert_equal('text', s:value) + s:value->assert_equal('text') RefDefArg('some') - assert_equal('some', s:value) + s:value->assert_equal('some') let RefDef2Arg: func(?number, ?string): string RefDef2Arg = FuncTwoDefArg - assert_equal('123text', RefDef2Arg()) - assert_equal('99text', RefDef2Arg(99)) - assert_equal('77some', RefDef2Arg(77, 'some')) + RefDef2Arg()->assert_equal('123text') + RefDef2Arg(99)->assert_equal('99text') + RefDef2Arg(77, 'some')->assert_equal('77some') CheckDefFailure(['let RefWrong: func(string?)'], 'E1010:') CheckDefFailure(['let RefWrong: func(?string, string)'], 'E1007:') let RefVarargs: func(...list): string RefVarargs = FuncVarargs - assert_equal('', RefVarargs()) - assert_equal('one', RefVarargs('one')) - assert_equal('one,two', RefVarargs('one', 'two')) + RefVarargs()->assert_equal('') + RefVarargs('one')->assert_equal('one') + RefVarargs('one', 'two')->assert_equal('one,two') CheckDefFailure(['let RefWrong: func(...list, string)'], 'E110:') CheckDefFailure(['let RefWrong: func(...list, ?string)'], 'E110:') @@ -353,11 +409,11 @@ def MyVarargsOnly(...args: list): string enddef def Test_call_varargs_only() - assert_equal('', MyVarargsOnly()) - assert_equal('one', MyVarargsOnly('one')) - assert_equal('one,two', MyVarargsOnly('one', 'two')) - CheckDefFailure(['MyVarargsOnly(1)'], 'E1013: argument 1: type mismatch, expected string but got number') - CheckDefFailure(['MyVarargsOnly("one", 2)'], 'E1013: argument 2: type mismatch, expected string but got number') + MyVarargsOnly()->assert_equal('') + MyVarargsOnly('one')->assert_equal('one') + MyVarargsOnly('one', 'two')->assert_equal('one,two') + CheckDefFailure(['MyVarargsOnly(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['MyVarargsOnly("one", 2)'], 'E1013: Argument 2: type mismatch, expected string but got number') enddef def Test_using_var_as_arg() @@ -378,16 +434,16 @@ def Test_assign_to_argument() # works for dict and list let d: dict = {} DictArg(d) - assert_equal('value', d['key']) + d['key']->assert_equal('value') let l: list = [] ListArg(l) - assert_equal('value', l[0]) + l[0]->assert_equal('value') CheckScriptFailure(['def Func(arg: number)', 'arg = 3', 'enddef', 'defcompile'], 'E1090:') enddef def Test_call_func_defined_later() - assert_equal('one', g:DefinedLater('one')) + g:DefinedLater('one')->assert_equal('one') assert_fails('NotDefined("one")', 'E117:', '', 2, 'Test_call_func_defined_later') enddef @@ -396,7 +452,7 @@ func DefinedLater(arg) endfunc def Test_call_funcref() - assert_equal(3, g:SomeFunc('abc')) + g:SomeFunc('abc')->assert_equal(3) assert_fails('NotAFunc()', 'E117:', '', 2, 'Test_call_funcref') # comment after call assert_fails('g:NotAFunc()', 'E117:', '', 3, 'Test_call_funcref') @@ -406,7 +462,7 @@ def Test_call_funcref() return 123 enddef let Funcref: func: number = function('RetNumber') - assert_equal(123, Funcref()) + Funcref()->assert_equal(123) END CheckScriptSuccess(lines) @@ -419,7 +475,7 @@ def Test_call_funcref() return F() enddef let Funcref = function('RetNumber') - assert_equal(123, Bar(Funcref)) + Bar(Funcref)->assert_equal(123) END CheckScriptSuccess(lines) @@ -440,7 +496,7 @@ def Test_call_funcref() enddef let Funcref: func(string) = function('UseNumber') END - CheckScriptFailure(lines, 'E1012: type mismatch, expected func(string) but got func(number)') + CheckScriptFailure(lines, 'E1012: Type mismatch; expected func(string) but got func(number)') lines =<< trim END vim9script @@ -449,9 +505,9 @@ def Test_call_funcref() enddef let Funcref: func(?number) = function('EchoNr') Funcref() - assert_equal(34, g:echo) + g:echo->assert_equal(34) Funcref(123) - assert_equal(123, g:echo) + g:echo->assert_equal(123) END CheckScriptSuccess(lines) @@ -462,9 +518,9 @@ def Test_call_funcref() enddef let Funcref: func(...list) = function('EchoList') Funcref() - assert_equal([], g:echo) + g:echo->assert_equal([]) Funcref(1, 2, 3) - assert_equal([1, 2, 3], g:echo) + g:echo->assert_equal([1, 2, 3]) END CheckScriptSuccess(lines) @@ -476,17 +532,17 @@ def Test_call_funcref() return nr enddef let Funcref: func(number, ?number, ...list): number = function('OptAndVar') - assert_equal(10, Funcref(10)) - assert_equal(12, g:optarg) - assert_equal([], g:listarg) + Funcref(10)->assert_equal(10) + g:optarg->assert_equal(12) + g:listarg->assert_equal([]) - assert_equal(11, Funcref(11, 22)) - assert_equal(22, g:optarg) - assert_equal([], g:listarg) + Funcref(11, 22)->assert_equal(11) + g:optarg->assert_equal(22) + g:listarg->assert_equal([]) - assert_equal(17, Funcref(17, 18, 1, 2, 3)) - assert_equal(18, g:optarg) - assert_equal([1, 2, 3], g:listarg) + Funcref(17, 18, 1, 2, 3)->assert_equal(17) + g:optarg->assert_equal(18) + g:listarg->assert_equal([1, 2, 3]) END CheckScriptSuccess(lines) enddef @@ -587,68 +643,68 @@ def Test_vim9script_call() var = arg enddef MyFunc('foobar') - assert_equal('foobar', var) + var->assert_equal('foobar') let str = 'barfoo' str->MyFunc() - assert_equal('barfoo', var) + var->assert_equal('barfoo') g:value = 'value' g:value->MyFunc() - assert_equal('value', var) + var->assert_equal('value') let listvar = [] def ListFunc(arg: list) listvar = arg enddef [1, 2, 3]->ListFunc() - assert_equal([1, 2, 3], listvar) + listvar->assert_equal([1, 2, 3]) let dictvar = {} def DictFunc(arg: dict) dictvar = arg enddef {'a': 1, 'b': 2}->DictFunc() - assert_equal(#{a: 1, b: 2}, dictvar) + dictvar->assert_equal(#{a: 1, b: 2}) def CompiledDict() {'a': 3, 'b': 4}->DictFunc() enddef CompiledDict() - assert_equal(#{a: 3, b: 4}, dictvar) + dictvar->assert_equal(#{a: 3, b: 4}) #{a: 3, b: 4}->DictFunc() - assert_equal(#{a: 3, b: 4}, dictvar) + dictvar->assert_equal(#{a: 3, b: 4}) ('text')->MyFunc() - assert_equal('text', var) + var->assert_equal('text') ("some")->MyFunc() - assert_equal('some', var) + var->assert_equal('some') # line starting with single quote is not a mark # line starting with double quote can be a method call 'asdfasdf'->MyFunc() - assert_equal('asdfasdf', var) + var->assert_equal('asdfasdf') "xyz"->MyFunc() - assert_equal('xyz', var) + var->assert_equal('xyz') def UseString() 'xyork'->MyFunc() enddef UseString() - assert_equal('xyork', var) + var->assert_equal('xyork') def UseString2() "knife"->MyFunc() enddef UseString2() - assert_equal('knife', var) + var->assert_equal('knife') # prepending a colon makes it a mark new setline(1, ['aaa', 'bbb', 'ccc']) normal! 3Gmt1G :'t - assert_equal(3, getcurpos()[1]) + getcurpos()[1]->assert_equal(3) bwipe! MyFunc( @@ -692,7 +748,7 @@ def Test_vim9script_call_fail_type() enddef MyFunc(1234) END - CheckScriptFailure(lines, 'E1013: argument 1: type mismatch, expected string but got number') + CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number') enddef def Test_vim9script_call_fail_const() @@ -753,9 +809,9 @@ def Test_redef_failure() so Xdef delete('Xdef') - assert_equal(0, g:Func0()) - assert_equal('Func1', g:Func1()) - assert_equal('Func2', g:Func2()) + g:Func0()->assert_equal(0) + g:Func1()->assert_equal('Func1') + g:Func2()->assert_equal('Func2') delfunc! Func0 delfunc! Func1 @@ -806,12 +862,12 @@ func Test_InternalFuncRetType() call writefile(lines, 'Xscript') source Xscript - call assert_equal(2.0, RetFloat()) - call assert_equal([['k', 'v']], RetListAny()) - call assert_equal(['a', 'b', 'c'], RetListString()) - call assert_notequal([], RetListDictAny()) - call assert_notequal({}, RetDictNumber()) - call assert_notequal({}, RetDictString()) + call RetFloat()->assert_equal(2.0) + call RetListAny()->assert_equal([['k', 'v']]) + call RetListString()->assert_equal(['a', 'b', 'c']) + call RetListDictAny()->assert_notequal([]) + call RetDictNumber()->assert_notequal({}) + call RetDictString()->assert_notequal({}) call delete('Xscript') endfunc @@ -878,28 +934,28 @@ def Test_func_type() s:funcResult = 0 Ref1 = FuncNoArgNoRet Ref1() - assert_equal(11, s:funcResult) + s:funcResult->assert_equal(11) let Ref2: func s:funcResult = 0 Ref2 = FuncNoArgNoRet Ref2() - assert_equal(11, s:funcResult) + s:funcResult->assert_equal(11) s:funcResult = 0 Ref2 = FuncOneArgNoRet Ref2(12) - assert_equal(12, s:funcResult) + s:funcResult->assert_equal(12) s:funcResult = 0 Ref2 = FuncNoArgRetNumber - assert_equal(1234, Ref2()) - assert_equal(22, s:funcResult) + Ref2()->assert_equal(1234) + s:funcResult->assert_equal(22) s:funcResult = 0 Ref2 = FuncOneArgRetNumber - assert_equal(13, Ref2(13)) - assert_equal(13, s:funcResult) + Ref2(13)->assert_equal(13) + s:funcResult->assert_equal(13) enddef def Test_repeat_return_type() @@ -907,13 +963,13 @@ def Test_repeat_return_type() for n in repeat([1], 3) res += n endfor - assert_equal(3, res) + res->assert_equal(3) res = 0 for n in add([1, 2], 3) res += n endfor - assert_equal(6, res) + res->assert_equal(6) enddef def Test_argv_return_type() @@ -922,44 +978,44 @@ def Test_argv_return_type() for name in argv() res ..= name endfor - assert_equal('fileonefiletwo', res) + res->assert_equal('fileonefiletwo') enddef def Test_func_type_part() let RefVoid: func: void RefVoid = FuncNoArgNoRet RefVoid = FuncOneArgNoRet - CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func() but got func(): number') - CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: type mismatch, expected func() but got func(): string') + CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...) but got func(): number') + CheckDefFailure(['let RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...) but got func(): string') let RefAny: func(): any RefAny = FuncNoArgRetNumber RefAny = FuncNoArgRetString - CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): any but got func()') - CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: type mismatch, expected func(): any but got func(number)') + CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func()') + CheckDefFailure(['let RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func(number)') let RefNr: func: number RefNr = FuncNoArgRetNumber RefNr = FuncOneArgRetNumber - CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): number but got func()') - CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: type mismatch, expected func(): number but got func(): string') + CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): number but got func()') + CheckDefFailure(['let RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...): number but got func(): string') let RefStr: func: string RefStr = FuncNoArgRetString RefStr = FuncOneArgRetString - CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: type mismatch, expected func(): string but got func()') - CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func(): string but got func(): number') + CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): string but got func()') + CheckDefFailure(['let RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...): string but got func(): number') enddef def Test_func_type_fails() CheckDefFailure(['let ref1: func()'], 'E704:') - CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: type mismatch, expected func() but got func(): number') - CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: type mismatch, expected func() but got func(number)') - CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: type mismatch, expected func() but got func(number): number') - CheckDefFailure(['let Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(bool) but got func(bool, number)') - CheckDefFailure(['let Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(?bool) but got func(bool, number)') - CheckDefFailure(['let Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: type mismatch, expected func(...bool) but got func(bool, number)') + CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(): number') + CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func() but got func(number)') + CheckDefFailure(['let Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(number): number') + CheckDefFailure(['let Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(bool) but got func(bool, number)') + CheckDefFailure(['let Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(?bool) but got func(bool, number)') + CheckDefFailure(['let Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(...bool) but got func(bool, number)') CheckDefFailure(['let RefWrong: func(string ,number)'], 'E1068:') CheckDefFailure(['let RefWrong: func(string,number)'], 'E1069:') @@ -970,16 +1026,16 @@ enddef def Test_func_return_type() let nr: number nr = FuncNoArgRetNumber() - assert_equal(1234, nr) + nr->assert_equal(1234) nr = FuncOneArgRetAny(122) - assert_equal(122, nr) + nr->assert_equal(122) let str: string str = FuncOneArgRetAny('yes') - assert_equal('yes', str) + str->assert_equal('yes') - CheckDefFailure(['let str: string', 'str = FuncNoArgRetNumber()'], 'E1012: type mismatch, expected string but got number') + CheckDefFailure(['let str: string', 'str = FuncNoArgRetNumber()'], 'E1012: Type mismatch; expected string but got number') enddef def MultiLine( @@ -999,17 +1055,17 @@ def MultiLineComment( enddef def Test_multiline() - assert_equal('text1234', MultiLine('text')) - assert_equal('text777', MultiLine('text', 777)) - assert_equal('text777one', MultiLine('text', 777, 'one')) - assert_equal('text777one-two', MultiLine('text', 777, 'one', 'two')) + MultiLine('text')->assert_equal('text1234') + MultiLine('text', 777)->assert_equal('text777') + MultiLine('text', 777, 'one')->assert_equal('text777one') + MultiLine('text', 777, 'one', 'two')->assert_equal('text777one-two') enddef func Test_multiline_not_vim9() - call assert_equal('text1234', MultiLine('text')) - call assert_equal('text777', MultiLine('text', 777)) - call assert_equal('text777one', MultiLine('text', 777, 'one')) - call assert_equal('text777one-two', MultiLine('text', 777, 'one', 'two')) + call MultiLine('text')->assert_equal('text1234') + call MultiLine('text', 777)->assert_equal('text777') + call MultiLine('text', 777, 'one')->assert_equal('text777one') + call MultiLine('text', 777, 'one', 'two')->assert_equal('text777one-two') endfunc @@ -1024,7 +1080,7 @@ func Test_E1056_1059() catch /E1056:/ let caught_1056 = 1 endtry - call assert_equal(1, caught_1056) + eval caught_1056->assert_equal(1) let caught_1059 = 0 try @@ -1034,7 +1090,7 @@ func Test_E1056_1059() catch /E1059:/ let caught_1059 = 1 endtry - call assert_equal(1, caught_1059) + eval caught_1059->assert_equal(1) endfunc func DelMe() @@ -1052,13 +1108,13 @@ def Test_error_reporting() enddef defcompile END - call writefile(lines, 'Xdef') + writefile(lines, 'Xdef') try source Xdef assert_report('should have failed') catch /E476:/ - assert_match('Invalid command: invalid', v:exception) - assert_match(', line 3$', v:throwpoint) + v:exception->assert_match('Invalid command: invalid') + v:throwpoint->assert_match(', line 3$') endtry # comment lines after the start of the function @@ -1072,13 +1128,13 @@ def Test_error_reporting() enddef defcompile END - call writefile(lines, 'Xdef') + writefile(lines, 'Xdef') try source Xdef assert_report('should have failed') catch /E476:/ - assert_match('Invalid command: invalid', v:exception) - assert_match(', line 4$', v:throwpoint) + v:exception->assert_match('Invalid command: invalid') + v:throwpoint->assert_match(', line 4$') endtry lines =<< trim END @@ -1091,15 +1147,15 @@ def Test_error_reporting() defcompile Func() END - call writefile(lines, 'Xdef') + writefile(lines, 'Xdef') try source Xdef assert_report('should have failed') catch /E716:/ - assert_match('_Func, line 3$', v:throwpoint) + v:throwpoint->assert_match('_Func, line 3$') endtry - call delete('Xdef') + delete('Xdef') enddef def Test_deleted_function() @@ -1121,7 +1177,7 @@ enddef def Test_closure_simple() let local = 'some ' - assert_equal('some more', RefFunc({s -> local .. s})) + RefFunc({s -> local .. s})->assert_equal('some more') enddef def MakeRef() @@ -1131,7 +1187,7 @@ enddef def Test_closure_ref_after_return() MakeRef() - assert_equal('some thing', g:Ref('thing')) + g:Ref('thing')->assert_equal('some thing') unlet g:Ref enddef @@ -1143,11 +1199,11 @@ enddef def Test_closure_two_refs() MakeTwoRefs() - assert_equal('some', join(g:Read(), ' ')) + join(g:Read(), ' ')->assert_equal('some') g:Extend('more') - assert_equal('some more', join(g:Read(), ' ')) + join(g:Read(), ' ')->assert_equal('some more') g:Extend('even') - assert_equal('some more even', join(g:Read(), ' ')) + join(g:Read(), ' ')->assert_equal('some more even') unlet g:Extend unlet g:Read @@ -1157,17 +1213,17 @@ def ReadRef(Ref: func(): list): string return join(Ref(), ' ') enddef -def ExtendRef(Ref: func(string), add: string) +def ExtendRef(Ref: func(string): list, add: string) Ref(add) enddef def Test_closure_two_indirect_refs() MakeTwoRefs() - assert_equal('some', ReadRef(g:Read)) + ReadRef(g:Read)->assert_equal('some') ExtendRef(g:Extend, 'more') - assert_equal('some more', ReadRef(g:Read)) + ReadRef(g:Read)->assert_equal('some more') ExtendRef(g:Extend, 'even') - assert_equal('some more even', ReadRef(g:Read)) + ReadRef(g:Read)->assert_equal('some more even') unlet g:Extend unlet g:Read @@ -1185,10 +1241,10 @@ enddef def Test_closure_using_argument() MakeArgRefs('arg_val') - assert_equal('arg_val/loc_val/call_val', g:UseArg('call_val')) + g:UseArg('call_val')->assert_equal('arg_val/loc_val/call_val') MakeArgRefsVarargs('arg_val', 'one', 'two') - assert_equal('arg_val/the_loc/call_val/one two', g:UseVararg('call_val')) + g:UseVararg('call_val')->assert_equal('arg_val/the_loc/call_val/one two') unlet g:UseArg unlet g:UseVararg @@ -1210,11 +1266,11 @@ enddef def Test_closure_append_get() MakeGetAndAppendRefs() - assert_equal('a', g:Get()) + g:Get()->assert_equal('a') g:Append('-b') - assert_equal('a-b', g:Get()) + g:Get()->assert_equal('a-b') g:Append('-c') - assert_equal('a-b-c', g:Get()) + g:Get()->assert_equal('a-b-c') unlet g:Append unlet g:Get @@ -1225,7 +1281,7 @@ def Test_nested_closure() def Closure(arg: string): string return local .. arg enddef - assert_equal('text!!!', Closure('!!!')) + Closure('!!!')->assert_equal('text!!!') enddef func GetResult(Ref) @@ -1235,7 +1291,7 @@ endfunc def Test_call_closure_not_compiled() let text = 'text' g:Ref = {s -> s .. text} - assert_equal('sometext', GetResult(g:Ref)) + GetResult(g:Ref)->assert_equal('sometext') enddef def Test_sort_return_type() @@ -1243,20 +1299,25 @@ def Test_sort_return_type() res = [1, 2, 3]->sort() enddef +def Test_sort_argument() + let res = ['b', 'a', 'c']->sort('i') + res->assert_equal(['a', 'b', 'c']) +enddef + def Test_getqflist_return_type() let l = getqflist() - assert_equal([], l) + l->assert_equal([]) let d = getqflist(#{items: 0}) - assert_equal(#{items: []}, d) + d->assert_equal(#{items: []}) enddef def Test_getloclist_return_type() let l = getloclist(1) - assert_equal([], l) + l->assert_equal([]) let d = getloclist(1, #{items: 0}) - assert_equal(#{items: []}, d) + d->assert_equal(#{items: []}) enddef def Test_copy_return_type() @@ -1265,14 +1326,14 @@ def Test_copy_return_type() for n in l res += n endfor - assert_equal(6, res) + res->assert_equal(6) let dl = deepcopy([1, 2, 3]) res = 0 for n in dl res += n endfor - assert_equal(6, res) + res->assert_equal(6) dl = deepcopy([1, 2, 3], true) enddef @@ -1283,7 +1344,7 @@ def Test_extend_return_type() for n in l res += n endfor - assert_equal(6, res) + res->assert_equal(6) enddef def Test_garbagecollect() @@ -1296,12 +1357,12 @@ def Test_insert_return_type() for n in l res += n endfor - assert_equal(6, res) + res->assert_equal(6) enddef def Test_keys_return_type() const var: list = #{a: 1, b: 2}->keys() - assert_equal(['a', 'b'], var) + var->assert_equal(['a', 'b']) enddef def Test_reverse_return_type() @@ -1310,7 +1371,7 @@ def Test_reverse_return_type() for n in l res += n endfor - assert_equal(6, res) + res->assert_equal(6) enddef def Test_remove_return_type() @@ -1319,7 +1380,7 @@ def Test_remove_return_type() for n in l res += n endfor - assert_equal(3, res) + res->assert_equal(3) enddef def Test_filter_return_type() @@ -1328,26 +1389,26 @@ def Test_filter_return_type() for n in l res += n endfor - assert_equal(6, res) + res->assert_equal(6) enddef def Test_bufnr() let buf = bufnr() - assert_equal(buf, bufnr('%')) + bufnr('%')->assert_equal(buf) buf = bufnr('Xdummy', true) - assert_notequal(-1, buf) + buf->assert_notequal(-1) exe 'bwipe! ' .. buf enddef def Test_col() new setline(1, 'asdf') - assert_equal(5, col([1, '$'])) + col([1, '$'])->assert_equal(5) enddef def Test_char2nr() - assert_equal(12354, char2nr('あ', true)) + char2nr('あ', true)->assert_equal(12354) enddef def Test_getreg_return_type() @@ -1361,7 +1422,7 @@ def Wrong_dict_key_type(items: list): list enddef def Test_wrong_dict_key_type() - assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1029:') + assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1012:') enddef def Line_continuation_in_def(dir: string = ''): string @@ -1372,10 +1433,10 @@ def Line_continuation_in_def(dir: string = ''): string enddef def Test_line_continuation_in_def() - assert_equal('full', Line_continuation_in_def('.')) + Line_continuation_in_def('.')->assert_equal('full') enddef -def Line_continuation_in_lambda(): list +def Line_continuation_in_lambda(): list let x = range(97, 100) ->map({_, v -> nr2char(v) ->toupper()}) @@ -1384,7 +1445,7 @@ def Line_continuation_in_lambda(): list enddef def Test_line_continuation_in_lambda() - assert_equal(['D', 'C', 'B', 'A'], Line_continuation_in_lambda()) + Line_continuation_in_lambda()->assert_equal(['D', 'C', 'B', 'A']) enddef func Test_silent_echo() @@ -1413,9 +1474,9 @@ endfunc def Test_bufname() split SomeFile - assert_equal('SomeFile', bufname('%')) + bufname('%')->assert_equal('SomeFile') edit OtherFile - assert_equal('SomeFile', bufname('#')) + bufname('#')->assert_equal('SomeFile') close enddef @@ -1425,7 +1486,7 @@ def Test_bufwinid() let SomeFileID = win_getid() below split OtherFile below split SomeFile - assert_equal(SomeFileID, bufwinid('SomeFile')) + bufwinid('SomeFile')->assert_equal(SomeFileID) win_gotoid(origwin) only @@ -1434,19 +1495,19 @@ def Test_bufwinid() enddef def Test_count() - assert_equal(3, count('ABC ABC ABC', 'b', true)) - assert_equal(0, count('ABC ABC ABC', 'b', false)) + count('ABC ABC ABC', 'b', true)->assert_equal(3) + count('ABC ABC ABC', 'b', false)->assert_equal(0) enddef def Test_expand() split SomeFile - assert_equal(['SomeFile'], expand('%', true, true)) + expand('%', true, true)->assert_equal(['SomeFile']) close enddef def Test_getbufinfo() let bufinfo = getbufinfo(bufnr()) - assert_equal(bufinfo, getbufinfo('%')) + getbufinfo('%')->assert_equal(bufinfo) edit Xtestfile1 hide edit Xtestfile2 @@ -1462,7 +1523,7 @@ def Test_getbufline() e # let lines = ['aaa', 'bbb', 'ccc'] setbufline(buf, 1, lines) - assert_equal(lines, getbufline('#', 1, '$')) + getbufline('#', 1, '$')->assert_equal(lines) bwipe! enddef @@ -1471,57 +1532,57 @@ def Test_getchangelist() new setline(1, 'some text') let changelist = bufnr()->getchangelist() - assert_equal(changelist, getchangelist('%')) + getchangelist('%')->assert_equal(changelist) bwipe! enddef def Test_getchar() while getchar(0) endwhile - assert_equal(0, getchar(true)) + getchar(true)->assert_equal(0) enddef def Test_getcompletion() set wildignore=*.vim,*~ let l = getcompletion('run', 'file', true) - assert_equal([], l) + l->assert_equal([]) set wildignore& enddef def Test_getreg() let lines = ['aaa', 'bbb', 'ccc'] setreg('a', lines) - assert_equal(lines, getreg('a', true, true)) + getreg('a', true, true)->assert_equal(lines) enddef def Test_glob() - assert_equal(['runtest.vim'], glob('runtest.vim', true, true, true)) + glob('runtest.vim', true, true, true)->assert_equal(['runtest.vim']) enddef def Test_globpath() - assert_equal(['./runtest.vim'], globpath('.', 'runtest.vim', true, true, true)) + globpath('.', 'runtest.vim', true, true, true)->assert_equal(['./runtest.vim']) enddef def Test_has() - assert_equal(1, has('eval', true)) + has('eval', true)->assert_equal(1) enddef def Test_hasmapto() - assert_equal(0, hasmapto('foobar', 'i', true)) + hasmapto('foobar', 'i', true)->assert_equal(0) iabbrev foo foobar - assert_equal(1, hasmapto('foobar', 'i', true)) + hasmapto('foobar', 'i', true)->assert_equal(1) iunabbrev foo enddef def Test_index() - assert_equal(3, index(['a', 'b', 'a', 'B'], 'b', 2, true)) + index(['a', 'b', 'a', 'B'], 'b', 2, true)->assert_equal(3) enddef def Test_list2str_str2list_utf8() let s = "\u3042\u3044" let l = [0x3042, 0x3044] - assert_equal(l, str2list(s, true)) - assert_equal(s, list2str(l, true)) + str2list(s, true)->assert_equal(l) + list2str(l, true)->assert_equal(s) enddef def SID(): number @@ -1533,7 +1594,7 @@ enddef def Test_maparg() let lnum = str2nr(expand('')) map foo bar - assert_equal(#{ + maparg('foo', '', false, true)->assert_equal(#{ lnum: lnum + 1, script: 0, mode: ' ', @@ -1545,19 +1606,18 @@ def Test_maparg() expr: 0, sid: SID(), rhs: 'bar', - buffer: 0}, - maparg('foo', '', false, true)) + buffer: 0}) unmap foo enddef def Test_mapcheck() iabbrev foo foobar - assert_equal('foobar', mapcheck('foo', 'i', true)) + mapcheck('foo', 'i', true)->assert_equal('foobar') iunabbrev foo enddef def Test_nr2char() - assert_equal('a', nr2char(97, true)) + nr2char(97, true)->assert_equal('a') enddef def Test_readdir() @@ -1570,14 +1630,14 @@ def Test_search() setline(1, ['foo', 'bar']) let val = 0 # skip expr returns boolean - assert_equal(2, search('bar', 'W', 0, 0, {-> val == 1})) + search('bar', 'W', 0, 0, {-> val == 1})->assert_equal(2) :1 - assert_equal(0, search('bar', 'W', 0, 0, {-> val == 0})) + search('bar', 'W', 0, 0, {-> val == 0})->assert_equal(0) # skip expr returns number, only 0 and 1 are accepted :1 - assert_equal(2, search('bar', 'W', 0, 0, {-> 0})) + search('bar', 'W', 0, 0, {-> 0})->assert_equal(2) :1 - assert_equal(0, search('bar', 'W', 0, 0, {-> 1})) + search('bar', 'W', 0, 0, {-> 1})->assert_equal(0) assert_fails("search('bar', '', 0, 0, {-> -1})", 'E1023:') assert_fails("search('bar', '', 0, 0, {-> -1})", 'E1023:') enddef @@ -1586,33 +1646,33 @@ def Test_searchcount() new setline(1, "foo bar") :/foo - assert_equal(#{ - exact_match: 1, - current: 1, - total: 1, - maxcount: 99, - incomplete: 0, - }, searchcount(#{recompute: true})) + searchcount(#{recompute: true}) + ->assert_equal(#{ + exact_match: 1, + current: 1, + total: 1, + maxcount: 99, + incomplete: 0}) bwipe! enddef def Test_searchdecl() - assert_equal(1, searchdecl('blah', true, true)) + searchdecl('blah', true, true)->assert_equal(1) enddef def Test_setbufvar() - setbufvar(bufnr('%'), '&syntax', 'vim') - assert_equal('vim', &syntax) - setbufvar(bufnr('%'), '&ts', 16) - assert_equal(16, &ts) - settabwinvar(1, 1, '&syntax', 'vam') - assert_equal('vam', &syntax) - settabwinvar(1, 1, '&ts', 15) - assert_equal(15, &ts) - setlocal ts=8 + setbufvar(bufnr('%'), '&syntax', 'vim') + &syntax->assert_equal('vim') + setbufvar(bufnr('%'), '&ts', 16) + &ts->assert_equal(16) + settabwinvar(1, 1, '&syntax', 'vam') + &syntax->assert_equal('vam') + settabwinvar(1, 1, '&ts', 15) + &ts->assert_equal(15) + setlocal ts=8 - setbufvar('%', 'myvar', 123) - assert_equal(123, getbufvar('%', 'myvar')) + setbufvar('%', 'myvar', 123) + getbufvar('%', 'myvar')->assert_equal(123) enddef def Test_setloclist() @@ -1626,7 +1686,7 @@ def Test_setreg() setreg('a', ['aaa', 'bbb', 'ccc']) let reginfo = getreginfo('a') setreg('a', reginfo) - assert_equal(reginfo, getreginfo('a')) + getreginfo('a')->assert_equal(reginfo) enddef def Test_spellsuggest() @@ -1654,13 +1714,13 @@ def Test_submatch() let Rep = {-> range(10)->map({_, v -> submatch(v, true)})->string()} let actual = substitute('A123456789', pat, Rep, '') let expected = "[['A123456789'], ['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7'], ['8'], ['9']]" - assert_equal(expected, actual) + actual->assert_equal(expected) enddef def Test_synID() new setline(1, "text") - assert_equal(0, synID(1, 1, true)) + synID(1, 1, true)->assert_equal(0) bwipe! enddef @@ -1669,7 +1729,7 @@ def Test_term_gettty() MissingFeature 'terminal' else let buf = Run_shell_in_terminal({}) - assert_notequal('', term_gettty(buf, true)) + term_gettty(buf, true)->assert_notequal('') StopShellInTerminal(buf) endif enddef @@ -1681,7 +1741,7 @@ def Test_term_start() botright new let winnr = winnr() term_start(&shell, #{curwin: true}) - assert_equal(winnr, winnr()) + winnr()->assert_equal(winnr) bwipe! endif enddef @@ -1690,7 +1750,7 @@ def Test_timer_paused() let id = timer_start(50, {-> 0}) timer_pause(id, true) let info = timer_info(id) - assert_equal(1, info[0]['paused']) + info[0]['paused']->assert_equal(1) timer_stop(id) enddef @@ -1711,7 +1771,7 @@ def Fibonacci(n: number): number enddef def Test_recursive_call() - assert_equal(6765, Fibonacci(20)) + Fibonacci(20)->assert_equal(6765) enddef def TreeWalk(dir: string): list @@ -1728,7 +1788,7 @@ def Test_closure_in_map() writefile(['222'], 'XclosureDir/file2') writefile(['333'], 'XclosureDir/tdir/file3') - assert_equal(['file1', 'file2', {'tdir': ['file3']}], TreeWalk('XclosureDir')) + TreeWalk('XclosureDir')->assert_equal(['file1', 'file2', {'tdir': ['file3']}]) delete('XclosureDir', 'rf') enddef @@ -1736,19 +1796,19 @@ enddef def Test_partial_call() let Xsetlist = function('setloclist', [0]) Xsetlist([], ' ', {'title': 'test'}) - assert_equal({'title': 'test'}, getloclist(0, {'title': 1})) + getloclist(0, {'title': 1})->assert_equal({'title': 'test'}) Xsetlist = function('setloclist', [0, [], ' ']) Xsetlist({'title': 'test'}) - assert_equal({'title': 'test'}, getloclist(0, {'title': 1})) + getloclist(0, {'title': 1})->assert_equal({'title': 'test'}) Xsetlist = function('setqflist') Xsetlist([], ' ', {'title': 'test'}) - assert_equal({'title': 'test'}, getqflist({'title': 1})) + getqflist({'title': 1})->assert_equal({'title': 'test'}) Xsetlist = function('setqflist', [[], ' ']) Xsetlist({'title': 'test'}) - assert_equal({'title': 'test'}, getqflist({'title': 1})) + getqflist({'title': 1})->assert_equal({'title': 'test'}) enddef def Test_cmd_modifier() @@ -1773,7 +1833,27 @@ def Test_restore_modifiers() Func() END CheckScriptSuccess(lines) - assert_equal('', g:ei_after) + g:ei_after->assert_equal('') +enddef + +def StackTop() + eval 1 + eval 2 + # call not on fourth line + StackBot() +enddef + +def StackBot() + # throw an error + eval [][0] +enddef + +def Test_callstack_def() + try + StackTop() + catch + v:throwpoint->assert_match('Test_callstack_def\[2\]..StackTop\[4\]..StackBot, line 2') + endtry enddef diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index bd0a5c8748..2f1e708f29 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -180,9 +180,9 @@ def Test_assignment() CheckDefFailure(['¬ex += 3'], 'E113:') CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') CheckDefFailure(['&ts = [7]'], 'E1012:') - CheckDefExecFailure(['&ts = g:alist'], 'E1029: Expected number but got list') + CheckDefExecFailure(['&ts = g:alist'], 'E1012: Type mismatch; expected number but got list') CheckDefFailure(['&ts = "xx"'], 'E1012:') - CheckDefExecFailure(['&ts = g:astring'], 'E1029: Expected number but got string') + CheckDefExecFailure(['&ts = g:astring'], 'E1012: Type mismatch; expected number but got string') CheckDefFailure(['&path += 3'], 'E1012:') CheckDefExecFailure(['&bs = "asdf"'], 'E474:') # test freeing ISN_STOREOPT @@ -319,7 +319,6 @@ def Test_assignment_list() list3 += ['end'] assert_equal(['sdf', 'asdf', 'end'], list3) - CheckDefExecFailure(['let ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:') CheckDefExecFailure(['let [v1, v2] = [1, 2]'], 'E1092:') @@ -367,7 +366,7 @@ def Test_assignment_dict() enddef assert_equal(#{a: 43}, FillDict()) END - call CheckScriptSuccess(lines) + CheckScriptSuccess(lines) lines =<< trim END vim9script @@ -378,7 +377,7 @@ def Test_assignment_dict() enddef FillDict() END - call CheckScriptFailure(lines, 'E1103:') + CheckScriptFailure(lines, 'E1103:') # assignment to global dict lines =<< trim END @@ -390,7 +389,7 @@ def Test_assignment_dict() enddef assert_equal(#{a: 43}, FillDict()) END - call CheckScriptSuccess(lines) + CheckScriptSuccess(lines) # assignment to buffer dict lines =<< trim END @@ -402,7 +401,7 @@ def Test_assignment_dict() enddef assert_equal(#{a: 43}, FillDict()) END - call CheckScriptSuccess(lines) + CheckScriptSuccess(lines) enddef def Test_assignment_local() @@ -440,7 +439,7 @@ def Test_assignment_local() enddef call Test_assignment_local_internal() END - call CheckScriptSuccess(script_lines) + CheckScriptSuccess(script_lines) enddef def Test_assignment_default() @@ -794,37 +793,102 @@ def Test_delfunction() CheckScriptSuccess(lines) enddef -func Test_wrong_type() - call CheckDefFailure(['let var: list'], 'E1010:') - call CheckDefFailure(['let var: list>'], 'E1010:') - call CheckDefFailure(['let var: dict'], 'E1010:') - call CheckDefFailure(['let var: dict>'], 'E1010:') +def Test_wrong_type() + CheckDefFailure(['let var: list'], 'E1010:') + CheckDefFailure(['let var: list>'], 'E1010:') + CheckDefFailure(['let var: dict'], 'E1010:') + CheckDefFailure(['let var: dict>'], 'E1010:') - call CheckDefFailure(['let var: dict'], 'E1009:') + CheckDefFailure(['let var: dict'], 'E1009:') - call CheckDefFailure(['let var: ally'], 'E1010:') - call CheckDefFailure(['let var: bram'], 'E1010:') - call CheckDefFailure(['let var: cathy'], 'E1010:') - call CheckDefFailure(['let var: dom'], 'E1010:') - call CheckDefFailure(['let var: freddy'], 'E1010:') - call CheckDefFailure(['let var: john'], 'E1010:') - call CheckDefFailure(['let var: larry'], 'E1010:') - call CheckDefFailure(['let var: ned'], 'E1010:') - call CheckDefFailure(['let var: pam'], 'E1010:') - call CheckDefFailure(['let var: sam'], 'E1010:') - call CheckDefFailure(['let var: vim'], 'E1010:') + CheckDefFailure(['let var: ally'], 'E1010:') + CheckDefFailure(['let var: bram'], 'E1010:') + CheckDefFailure(['let var: cathy'], 'E1010:') + CheckDefFailure(['let var: dom'], 'E1010:') + CheckDefFailure(['let var: freddy'], 'E1010:') + CheckDefFailure(['let var: john'], 'E1010:') + CheckDefFailure(['let var: larry'], 'E1010:') + CheckDefFailure(['let var: ned'], 'E1010:') + CheckDefFailure(['let var: pam'], 'E1010:') + CheckDefFailure(['let var: sam'], 'E1010:') + CheckDefFailure(['let var: vim'], 'E1010:') - call CheckDefFailure(['let Ref: number', 'Ref()'], 'E1085:') - call CheckDefFailure(['let Ref: string', 'let res = Ref()'], 'E1085:') -endfunc + CheckDefFailure(['let Ref: number', 'Ref()'], 'E1085:') + CheckDefFailure(['let Ref: string', 'let res = Ref()'], 'E1085:') +enddef -func Test_const() - call CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:') - call CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:') - call CheckDefFailure(['const two'], 'E1021:') - call CheckDefFailure(['const &option'], 'E996:') -endfunc +def Test_const() + CheckDefFailure(['const var = 234', 'var = 99'], 'E1018:') + CheckDefFailure(['const one = 234', 'let one = 99'], 'E1017:') + CheckDefFailure(['const list = [1, 2]', 'let list = [3, 4]'], 'E1017:') + CheckDefFailure(['const two'], 'E1021:') + CheckDefFailure(['const &option'], 'E996:') + + let lines =<< trim END + const list = [1, 2, 3] + list[0] = 4 + list->assert_equal([4, 2, 3]) + const! other = [5, 6, 7] + other->assert_equal([5, 6, 7]) + + let varlist = [7, 8] + const! constlist = [1, varlist, 3] + varlist[0] = 77 + # TODO: does not work yet + # constlist[1][1] = 88 + let cl = constlist[1] + cl[1] = 88 + constlist->assert_equal([1, [77, 88], 3]) + + let vardict = #{five: 5, six: 6} + const! constdict = #{one: 1, two: vardict, three: 3} + vardict['five'] = 55 + # TODO: does not work yet + # constdict['two']['six'] = 66 + let cd = constdict['two'] + cd['six'] = 66 + constdict->assert_equal(#{one: 1, two: #{five: 55, six: 66}, three: 3}) + END + CheckDefAndScriptSuccess(lines) +enddef + +def Test_const_bang() + let lines =<< trim END + const! var = 234 + var = 99 + END + CheckDefExecFailure(lines, 'E1018:', 2) + CheckScriptFailure(['vim9script'] + lines, 'E46:', 3) + + lines =<< trim END + const! ll = [2, 3, 4] + ll[0] = 99 + END + CheckDefExecFailure(lines, 'E1119:', 2) + CheckScriptFailure(['vim9script'] + lines, 'E741:', 3) + + lines =<< trim END + const! ll = [2, 3, 4] + ll[3] = 99 + END + CheckDefExecFailure(lines, 'E1118:', 2) + CheckScriptFailure(['vim9script'] + lines, 'E684:', 3) + + lines =<< trim END + const! dd = #{one: 1, two: 2} + dd["one"] = 99 + END + CheckDefExecFailure(lines, 'E1121:', 2) + CheckScriptFailure(['vim9script'] + lines, 'E741:', 3) + + lines =<< trim END + const! dd = #{one: 1, two: 2} + dd["three"] = 99 + END + CheckDefExecFailure(lines, 'E1120:') + CheckScriptFailure(['vim9script'] + lines, 'E741:', 3) +enddef def Test_range_no_colon() CheckDefFailure(['%s/a/b/'], 'E1050:') @@ -844,11 +908,11 @@ def Test_block() assert_equal(1, outer) enddef -func Test_block_failure() - call CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:') - call CheckDefFailure(['}'], 'E1025:') - call CheckDefFailure(['{', 'echo 1'], 'E1026:') -endfunc +def Test_block_failure() + CheckDefFailure(['{', 'let inner = 1', '}', 'echo inner'], 'E1001:') + CheckDefFailure(['}'], 'E1025:') + CheckDefFailure(['{', 'echo 1'], 'E1026:') +enddef func g:NoSuchFunc() echo 'none' @@ -894,14 +958,14 @@ def Test_try_catch() try # string slice returns a string, not a number n = g:astring[3] - catch /E1029:/ + catch /E1012:/ n = 77 endtry assert_equal(77, n) try n = l[g:astring] - catch /E1029:/ + catch /E1012:/ n = 88 endtry assert_equal(88, n) @@ -952,7 +1016,7 @@ def Test_try_catch() let nd: dict try nd = {g:anumber: 1} - catch /E1029:/ + catch /E1012:/ n = 266 endtry assert_equal(266, n) @@ -966,7 +1030,7 @@ def Test_try_catch() try &ts = g:astring - catch /E1029:/ + catch /E1012:/ n = 288 endtry assert_equal(288, n) @@ -1105,6 +1169,26 @@ def Test_try_catch_nested() assert_equal('finally', g:in_finally) enddef +def TryOne(): number + try + return 0 + catch + endtry + return 0 +enddef + +def TryTwo(n: number): string + try + let x = {} + catch + endtry + return 'text' +enddef + +def Test_try_catch_twice() + assert_equal('text', TryOne()->TryTwo()) +enddef + def Test_try_catch_match() let seq = 'a' try @@ -1910,7 +1994,7 @@ def Test_import_compile_error() source Ximport.vim catch /E1001/ # Error should be fore the Xexported.vim file. - assert_match('E1001: variable not found: notDefined', v:exception) + assert_match('E1001: Variable not found: notDefined', v:exception) assert_match('function \d\+_ImpFunc\[1\]..\d\+_ExpFunc, line 1', v:throwpoint) endtry @@ -3120,6 +3204,24 @@ def Test_let_type_check() CheckScriptSuccess(lines) enddef +let g:dict_number = #{one: 1, two: 2} + +def Test_let_list_dict_type() + let ll: list + ll = [1, 2, 2, 3, 3, 3]->uniq() + ll->assert_equal([1, 2, 3]) + + let dd: dict + dd = g:dict_number + dd->assert_equal(g:dict_number) + + let lines =<< trim END + let ll: list + ll = [1, 2, 3]->map('"one"') + END + CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list but got list') +enddef + def Test_forward_declaration() let lines =<< trim END vim9script @@ -3308,6 +3410,14 @@ def Test_invalid_sid() delete('Xdidit') enddef +def Test_unset_any_variable() + let lines =<< trim END + let var: any + assert_equal(0, var) + END + CheckDefAndScriptSuccess(lines) +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/testing.c b/src/testing.c index 8db7d62ed4..8115441033 100644 --- a/src/testing.c +++ b/src/testing.c @@ -22,7 +22,7 @@ prepare_assert_error(garray_T *gap) { char buf[NUMBUFLEN]; - char_u *sname = estack_sfile(FALSE); + char_u *sname = estack_sfile(ESTACK_NONE); ga_init2(gap, 1, 100); if (sname != NULL) diff --git a/src/typval.c b/src/typval.c index db52d59730..1db0d77c4e 100644 --- a/src/typval.c +++ b/src/typval.c @@ -512,8 +512,8 @@ tv_check_lock(typval_T *tv, char_u *name, int use_gettext) default: break; } - return var_check_lock(tv->v_lock, name, use_gettext) - || (lock != 0 && var_check_lock(lock, name, use_gettext)); + return value_check_lock(tv->v_lock, name, use_gettext) + || (lock != 0 && value_check_lock(lock, name, use_gettext)); } /* diff --git a/src/userfunc.c b/src/userfunc.c index d5bdadc188..017098d0fc 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -874,6 +874,15 @@ find_func(char_u *name, int is_global, cctx_T *cctx) return NULL; } +/* + * Return TRUE if "ufunc" is a global function. + */ + int +func_is_global(ufunc_T *ufunc) +{ + return ufunc->uf_name[0] != K_SPECIAL; +} + /* * Copy the function name of "fp" to buffer "buf". * "buf" must be able to hold the function name plus three bytes. @@ -882,7 +891,7 @@ find_func(char_u *name, int is_global, cctx_T *cctx) static void cat_func_name(char_u *buf, ufunc_T *fp) { - if (fp->uf_name[0] == K_SPECIAL) + if (!func_is_global(fp)) { STRCPY(buf, ""); STRCAT(buf, fp->uf_name + 3); @@ -3141,7 +3150,7 @@ def_function(exarg_T *eap, char_u *name_arg) } // Check for ":append", ":change", ":insert". Not for :def. - p = skip_range(p, NULL); + p = skip_range(p, FALSE, NULL); if (eap->cmdidx != CMD_def && ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) || (p[0] == 'c' @@ -3337,11 +3346,11 @@ def_function(exarg_T *eap, char_u *name_arg) if (fudi.fd_di == NULL) { // Can't add a function to a locked dictionary - if (var_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) + if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) goto erret; } // Can't change an existing function if it is locked - else if (var_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) + else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) goto erret; // Give the function a sequential number. Can only be used with a diff --git a/src/version.c b/src/version.c index 3943001241..87d6552c2d 100644 --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,106 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1701, +/**/ + 1700, +/**/ + 1699, +/**/ + 1698, +/**/ + 1697, +/**/ + 1696, +/**/ + 1695, +/**/ + 1694, +/**/ + 1693, +/**/ + 1692, +/**/ + 1691, +/**/ + 1690, +/**/ + 1689, +/**/ + 1688, +/**/ + 1687, +/**/ + 1686, +/**/ + 1685, +/**/ + 1684, +/**/ + 1683, +/**/ + 1682, +/**/ + 1681, +/**/ + 1680, +/**/ + 1679, +/**/ + 1678, +/**/ + 1677, +/**/ + 1676, +/**/ + 1675, +/**/ + 1674, +/**/ + 1673, +/**/ + 1672, +/**/ + 1671, +/**/ + 1670, +/**/ + 1669, +/**/ + 1668, +/**/ + 1667, +/**/ + 1666, +/**/ + 1665, +/**/ + 1664, +/**/ + 1663, +/**/ + 1662, +/**/ + 1661, +/**/ + 1660, +/**/ + 1659, +/**/ + 1658, +/**/ + 1657, +/**/ + 1656, +/**/ + 1655, +/**/ + 1654, +/**/ + 1653, +/**/ + 1652, /**/ 1651, /**/ diff --git a/src/vim.h b/src/vim.h index e260673552..bbc01eed1b 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2108,8 +2108,7 @@ typedef struct stat stat_T; # define USE_PRINTF_FORMAT_ATTRIBUTE #endif -typedef enum -{ +typedef enum { ASSERT_EQUAL, ASSERT_NOTEQUAL, ASSERT_MATCH, @@ -2139,9 +2138,17 @@ typedef enum { USEPOPUP_HIDDEN // use info popup initially hidden } use_popup_T; +// Argument for estack_sfile(). +typedef enum { + ESTACK_NONE, + ESTACK_SFILE, + ESTACK_STACK +} estack_arg_T; + // Flags for assignment functions. #define LET_IS_CONST 1 // ":const" -#define LET_NO_COMMAND 2 // "var = expr" without ":let" or ":const" +#define LET_FORCEIT 2 // ":const!" (LET_IS_CONST is also set) +#define LET_NO_COMMAND 4 // "var = expr" without ":let" or ":const" #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff diff --git a/src/vim9.h b/src/vim9.h index 367c05ce0b..b3d5bc226a 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -58,6 +58,8 @@ typedef enum { ISN_UNLET, // unlet variable isn_arg.unlet.ul_name ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name + ISN_LOCKCONST, // lock constant value + // constants ISN_PUSHNR, // push number isn_arg.number ISN_PUSHBOOL, // push bool value isn_arg.number @@ -205,7 +207,7 @@ typedef struct { // arguments to ISN_CHECKTYPE typedef struct { - vartype_T ct_type; + type_T *ct_type; int ct_off; // offset in stack, -1 is bottom } checktype_T; diff --git a/src/vim9compile.c b/src/vim9compile.c index 7199e85774..338fbc6391 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -292,12 +292,14 @@ lookup_script(char_u *name, size_t len, int vim9script) /* * Check if "p[len]" is already defined, either in script "import_sid" or in * compilation context "cctx". + * Does not check the global namespace. * Return FAIL and give an error if it defined. */ int check_defined(char_u *p, size_t len, cctx_T *cctx) { - int c = p[len]; + int c = p[len]; + ufunc_T *ufunc = NULL; p[len] = NUL; if (lookup_script(p, len, FALSE) == OK @@ -305,11 +307,16 @@ check_defined(char_u *p, size_t len, cctx_T *cctx) && (lookup_local(p, len, cctx) != NULL || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK)) || find_imported(p, len, cctx) != NULL - || find_func_even_dead(p, FALSE, cctx) != NULL) + || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) { - p[len] = c; - semsg(_(e_name_already_defined_str), p); - return FAIL; + // A local or script-local function can shadow a global function. + if (ufunc == NULL || !func_is_global(ufunc) + || (p[0] == 'g' && p[1] == ':')) + { + p[len] = c; + semsg(_(e_name_already_defined_str), p); + return FAIL; + } } p[len] = c; return OK; @@ -697,7 +704,10 @@ generate_2BOOL(cctx_T *cctx, int invert) } static int -generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset) +generate_TYPECHECK( + cctx_T *cctx, + type_T *expected, + int offset) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; @@ -705,19 +715,18 @@ generate_TYPECHECK(cctx_T *cctx, type_T *vartype, int offset) RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL) return FAIL; - // TODO: whole type, e.g. for a function also arg and return types - isn->isn_arg.type.ct_type = vartype->tt_type; + isn->isn_arg.type.ct_type = alloc_type(expected); isn->isn_arg.type.ct_off = offset; - // type becomes vartype - ((type_T **)stack->ga_data)[stack->ga_len + offset] = vartype; + // type becomes expected + ((type_T **)stack->ga_data)[stack->ga_len + offset] = expected; return OK; } /* * Check that - * - "actual" is "expected" type or + * - "actual" matches "expected" type or * - "actual" is a type that can be "expected" type: add a runtime check; or * - return FAIL. */ @@ -740,17 +749,29 @@ need_type( if (check_type(expected, actual, FALSE, 0) == OK) return OK; - if (actual->tt_type != VAR_ANY - && actual->tt_type != VAR_UNKNOWN - && !(actual->tt_type == VAR_FUNC - && (actual->tt_member == &t_any || actual->tt_argcount < 0))) + + // If the actual type can be the expected type add a runtime check. + // TODO: if it's a constant a runtime check makes no sense. + if (actual->tt_type == VAR_ANY + || actual->tt_type == VAR_UNKNOWN + || (actual->tt_type == VAR_FUNC + && (expected->tt_type == VAR_FUNC + || expected->tt_type == VAR_PARTIAL) + && (actual->tt_member == &t_any || actual->tt_argcount < 0)) + || (actual->tt_type == VAR_LIST + && expected->tt_type == VAR_LIST + && actual->tt_member == &t_any) + || (actual->tt_type == VAR_DICT + && expected->tt_type == VAR_DICT + && actual->tt_member == &t_any)) { - if (!silent) - type_mismatch(expected, actual); - return FAIL; + generate_TYPECHECK(cctx, expected, offset); + return OK; } - generate_TYPECHECK(cctx, expected, offset); - return OK; + + if (!silent) + type_mismatch(expected, actual); + return FAIL; } /* @@ -769,7 +790,7 @@ generate_PUSHNR(cctx_T *cctx, varnumber_T number) if (number == 0 || number == 1) { - type_T *type = alloc_type(cctx->ctx_type_list); + type_T *type = get_type_ptr(cctx->ctx_type_list); // A 0 or 1 number can also be used as a bool. if (type != NULL) @@ -1101,6 +1122,20 @@ generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit) return OK; } +/* + * Generate an ISN_LOCKCONST instruction. + */ + static int +generate_LOCKCONST(cctx_T *cctx) +{ + isn_T *isn; + + RETURN_OK_IF_SKIP(cctx); + if ((isn = generate_instr(cctx, ISN_LOCKCONST)) == NULL) + return FAIL; + return OK; +} + /* * Generate an ISN_LOADS instruction. */ @@ -1395,8 +1430,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount) continue; expected = ufunc->uf_arg_types[i]; } - else if (ufunc->uf_va_type == NULL) - // possibly a lambda + else if (ufunc->uf_va_type == NULL || ufunc->uf_va_type == &t_any) + // possibly a lambda or "...: any" expected = &t_any; else expected = ufunc->uf_va_type->tt_member; @@ -2114,10 +2149,16 @@ generate_funcref(cctx_T *cctx, char_u *name) /* * Compile a variable name into a load instruction. * "end" points to just after the name. + * "is_expr" is TRUE when evaluating an expression, might be a funcref. * When "error" is FALSE do not give an error when not found. */ static int -compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error) +compile_load( + char_u **arg, + char_u *end_arg, + cctx_T *cctx, + int is_expr, + int error) { type_T *type; char_u *name = NULL; @@ -2214,10 +2255,11 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error) || find_imported(name, 0, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); - // When the name starts with an uppercase letter or "x:" it - // can be a user defined function. + // When evaluating an expression and the name starts with an + // uppercase letter or "x:" it can be a user defined function. // TODO: this is just guessing - if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':')) + if (res == FAIL && is_expr + && (ASCII_ISUPPER(*name) || name[1] == ':')) res = generate_funcref(cctx, name); } } @@ -2368,8 +2410,9 @@ compile_call( } // If we can find the function by name generate the right call. + // Skip global functions here, a local funcref takes precedence. ufunc = find_func(name, FALSE, cctx); - if (ufunc != NULL) + if (ufunc != NULL && !func_is_global(ufunc)) { res = generate_CALL(cctx, ufunc, argcount); goto theend; @@ -2380,7 +2423,7 @@ compile_call( // Not for eome#Func(), it will be loaded later. p = namebuf; if (STRNCMP(namebuf, "g:", 2) != 0 && !is_autoload - && compile_load(&p, namebuf + varlen, cctx, FALSE) == OK) + && compile_load(&p, namebuf + varlen, cctx, FALSE, FALSE) == OK) { garray_T *stack = &cctx->ctx_type_stack; type_T *type; @@ -2390,6 +2433,13 @@ compile_call( goto theend; } + // If we can find a global function by name generate the right call. + if (ufunc != NULL) + { + res = generate_CALL(cctx, ufunc, argcount); + goto theend; + } + // A global function may be defined only later. Need to figure out at // runtime. Also handles a FuncRef at runtime. if (STRNCMP(namebuf, "g:", 2) == 0 || is_autoload) @@ -3548,7 +3598,7 @@ compile_expr7( { if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; - r = compile_load(arg, p, cctx, TRUE); + r = compile_load(arg, p, cctx, TRUE, TRUE); } if (r == FAIL) return FAIL; @@ -4001,7 +4051,7 @@ compile_and_or( typep = ((type_T **)stack->ga_data) + stack->ga_len - 1; if (*typep != &t_bool) { - type_T *type = alloc_type(cctx->ctx_type_list); + type_T *type = get_type_ptr(cctx->ctx_type_list); if (type != NULL) { @@ -4254,7 +4304,7 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx) } if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, cctx, FALSE) == FAIL) - return NULL; + return NULL; } } else @@ -4320,6 +4370,12 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx) ufunc_T *ufunc; int r; + if (eap->forceit) + { + emsg(_(e_cannot_use_bang_with_nested_def)); + return NULL; + } + // Only g:Func() can use a namespace. if (name_start[1] == ':' && !is_global) { @@ -4772,11 +4828,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) semsg(_(e_variable_already_declared), name); goto theend; } - else if (lvar->lv_const) - { - semsg(_(e_cannot_assign_to_constant), name); - goto theend; - } } else { @@ -4932,6 +4983,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) semsg(_(e_cannot_assign_to_argument), name); goto theend; } + if (!is_decl && lvar != NULL && lvar->lv_const && !has_index) + { + semsg(_(e_cannot_assign_to_constant), name); + goto theend; + } if (!heredoc) { @@ -4996,6 +5052,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) : ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (lvar != NULL && (is_decl || !has_type)) { + if ((stacktype->tt_type == VAR_FUNC + || stacktype->tt_type == VAR_PARTIAL) + && var_wrong_func_name(name, TRUE)) + goto theend; + if (new_local && !has_type) { if (stacktype->tt_type == VAR_VOID) @@ -5003,12 +5064,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) emsg(_(e_cannot_use_void_value)); goto theend; } - else if ((stacktype->tt_type == VAR_FUNC - || stacktype->tt_type == VAR_PARTIAL) - && var_wrong_func_name(name, TRUE)) - { - goto theend; - } else { // An empty list or dict has a &t_void member, @@ -5025,12 +5080,13 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) { type_T *use_type = lvar->lv_type; - // without operator type is here, otherwise below + // without operator check type here, otherwise below if (has_index) { use_type = use_type->tt_member; if (use_type == NULL) - use_type = &t_void; + // could be indexing "any" + use_type = &t_any; } if (need_type(stacktype, use_type, -1, cctx, FALSE) == FAIL) @@ -5205,6 +5261,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) } else { + if (is_decl && eap->forceit && cmdidx == CMD_const + && (dest == dest_script || dest == dest_local)) + // ":const! var": lock the value, but not referenced variables + generate_LOCKCONST(cctx); + switch (dest) { case dest_option: @@ -6335,13 +6396,8 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx) char_u *line = arg; linenr_T lnum; char *errormsg; - int above = FALSE; + int above = eap->forceit; - if (*arg == '!') - { - above = TRUE; - line = skipwhite(arg + 1); - } eap->regname = *line; if (eap->regname == '=') @@ -6384,7 +6440,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx) if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE) { - long argt = excmd_get_argt(eap->cmdidx); + long argt = eap->argt; int usefilter = FALSE; has_expr = argt & (EX_XFILE | EX_EXPAND); @@ -6803,7 +6859,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) cmd = ea.cmd; if (*cmd != '\'' || starts_with_colon) { - ea.cmd = skip_range(ea.cmd, NULL); + ea.cmd = skip_range(ea.cmd, TRUE, NULL); if (ea.cmd > cmd) { if (!starts_with_colon) @@ -6843,8 +6899,6 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) } } - p = skipwhite(p); - if (cctx.ctx_had_return && ea.cmdidx != CMD_elseif && ea.cmdidx != CMD_else @@ -6859,6 +6913,19 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) goto erret; } + p = skipwhite(p); + if (ea.cmdidx != CMD_SIZE + && ea.cmdidx != CMD_write && ea.cmdidx != CMD_read) + { + if (ea.cmdidx >= 0) + ea.argt = excmd_get_argt(ea.cmdidx); + if ((ea.argt & EX_BANG) && *p == '!') + { + ea.forceit = TRUE; + p = skipwhite(p + 1); + } + } + switch (ea.cmdidx) { case CMD_def: @@ -7237,6 +7304,10 @@ delete_instr(isn_T *isn) } break; + case ISN_CHECKTYPE: + free_type(isn->isn_arg.type.ct_type); + break; + case ISN_2BOOL: case ISN_2STRING: case ISN_2STRING_ANY: @@ -7248,7 +7319,6 @@ delete_instr(isn_T *isn) case ISN_CATCH: case ISN_CHECKLEN: case ISN_CHECKNR: - case ISN_CHECKTYPE: case ISN_COMPAREANY: case ISN_COMPAREBLOB: case ISN_COMPAREBOOL: @@ -7282,6 +7352,7 @@ delete_instr(isn_T *isn) case ISN_LOADTDICT: case ISN_LOADV: case ISN_LOADWDICT: + case ISN_LOCKCONST: case ISN_MEMBER: case ISN_NEGATENR: case ISN_NEWDICT: diff --git a/src/vim9execute.c b/src/vim9execute.c index 76cafb4c9b..40d6193ca5 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -677,6 +677,21 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx) return OK; } +/* + * Check if "lock" is VAR_LOCKED or VAR_FIXED. If so give an error and return + * TRUE. + */ + static int +error_if_locked(int lock, char *error) +{ + if (lock & (VAR_LOCKED | VAR_FIXED)) + { + emsg(_(error)); + return TRUE; + } + return FALSE; +} + /* * Store "tv" in variable "name". * This is for s: and g: variables. @@ -814,6 +829,7 @@ call_def_function( // Check the type of the list items. tv = STACK_TV_BOT(-1); if (ufunc->uf_va_type != NULL + && ufunc->uf_va_type != &t_any && ufunc->uf_va_type->tt_member != &t_any && tv->vval.v_list != NULL) { @@ -1455,12 +1471,12 @@ call_def_function( typval_T *tv_list = STACK_TV_BOT(-1); list_T *list = tv_list->vval.v_list; + SOURCING_LNUM = iptr->isn_lnum; if (lidx < 0 && list->lv_len + lidx >= 0) // negative index is relative to the end lidx = list->lv_len + lidx; if (lidx < 0 || lidx > list->lv_len) { - SOURCING_LNUM = iptr->isn_lnum; semsg(_(e_listidx), lidx); goto on_error; } @@ -1469,12 +1485,18 @@ call_def_function( { listitem_T *li = list_find(list, lidx); + if (error_if_locked(li->li_tv.v_lock, + e_cannot_change_list_item)) + goto failed; // overwrite existing list item clear_tv(&li->li_tv); li->li_tv = *tv; } else { + if (error_if_locked(list->lv_lock, + e_cannot_change_list)) + goto failed; // append to list, only fails when out of memory if (list_append_tv(list, tv) == FAIL) goto failed; @@ -1495,9 +1517,9 @@ call_def_function( dict_T *dict = tv_dict->vval.v_dict; dictitem_T *di; + SOURCING_LNUM = iptr->isn_lnum; if (dict == NULL) { - SOURCING_LNUM = iptr->isn_lnum; emsg(_(e_dictionary_not_set)); goto on_error; } @@ -1507,12 +1529,18 @@ call_def_function( di = dict_find(dict, key, -1); if (di != NULL) { + if (error_if_locked(di->di_tv.v_lock, + e_cannot_change_dict_item)) + goto failed; // overwrite existing value clear_tv(&di->di_tv); di->di_tv = *tv; } else { + if (error_if_locked(dict->dv_lock, + e_cannot_change_dict)) + goto failed; // add to dict, only fails when out of memory if (dict_add_tv(dict, (char *)key, tv) == FAIL) goto failed; @@ -1603,6 +1631,10 @@ call_def_function( vim_unsetenv(iptr->isn_arg.unlet.ul_name); break; + case ISN_LOCKCONST: + item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE); + break; + // create a list from items on the stack; uses a single allocation // for the list header and the items case ISN_NEWLIST: @@ -1665,6 +1697,7 @@ call_def_function( // call a :def function case ISN_DCALL: + SOURCING_LNUM = iptr->isn_lnum; if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, iptr->isn_arg.dfunc.cdf_argcount, &ectx) == FAIL) @@ -1889,6 +1922,7 @@ call_def_function( trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch; trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally; trycmd->tcd_caught = FALSE; + trycmd->tcd_return = FALSE; } break; @@ -2125,6 +2159,7 @@ call_def_function( exptype_T exptype = iptr->isn_arg.op.op_type; int ic = iptr->isn_arg.op.op_ic; + SOURCING_LNUM = iptr->isn_lnum; typval_compare(tv1, tv2, exptype, ic); clear_tv(tv2); --ectx.ec_stack.ga_len; @@ -2502,18 +2537,19 @@ call_def_function( checktype_T *ct = &iptr->isn_arg.type; tv = STACK_TV_BOT(ct->ct_off); - // TODO: better type comparison - if (tv->v_type != ct->ct_type - && !((tv->v_type == VAR_PARTIAL - && ct->ct_type == VAR_FUNC) - || (tv->v_type == VAR_FUNC - && ct->ct_type == VAR_PARTIAL))) - { - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_expected_str_but_got_str), - vartype_name(ct->ct_type), - vartype_name(tv->v_type)); + SOURCING_LNUM = iptr->isn_lnum; + if (check_typval_type(ct->ct_type, tv, 0) == FAIL) goto on_error; + + // number 0 is FALSE, number 1 is TRUE + if (tv->v_type == VAR_NUMBER + && ct->ct_type->tt_type == VAR_BOOL + && (tv->vval.v_number == 0 + || tv->vval.v_number == 1)) + { + tv->v_type = VAR_BOOL; + tv->vval.v_number = tv->vval.v_number + ? VVAL_TRUE : VVAL_FALSE; } } break; @@ -3011,6 +3047,9 @@ ex_disassemble(exarg_T *eap) iptr->isn_arg.unlet.ul_forceit ? "!" : "", iptr->isn_arg.unlet.ul_name); break; + case ISN_LOCKCONST: + smsg("%4d LOCKCONST", current); + break; case ISN_NEWLIST: smsg("%4d NEWLIST size %lld", current, (long long)(iptr->isn_arg.number)); @@ -3238,10 +3277,16 @@ ex_disassemble(exarg_T *eap) case ISN_NEGATENR: smsg("%4d NEGATENR", current); break; case ISN_CHECKNR: smsg("%4d CHECKNR", current); break; - case ISN_CHECKTYPE: smsg("%4d CHECKTYPE %s stack[%d]", current, - vartype_name(iptr->isn_arg.type.ct_type), - iptr->isn_arg.type.ct_off); - break; + case ISN_CHECKTYPE: + { + char *tofree; + + smsg("%4d CHECKTYPE %s stack[%d]", current, + type_name(iptr->isn_arg.type.ct_type, &tofree), + iptr->isn_arg.type.ct_off); + vim_free(tofree); + break; + } case ISN_CHECKLEN: smsg("%4d CHECKLEN %s%d", current, iptr->isn_arg.checklen.cl_more_OK ? ">= " : "", iptr->isn_arg.checklen.cl_min_len); diff --git a/src/vim9script.c b/src/vim9script.c index 48163150c6..74d0579f35 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -548,7 +548,11 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg) // Create the variable with 0/NULL value. CLEAR_FIELD(init_tv); - init_tv.v_type = type->tt_type; + if (type->tt_type == VAR_ANY) + // A variable of type "any" is not possible, just use zero instead + init_tv.v_type = VAR_NUMBER; + else + init_tv.v_type = type->tt_type; set_var_const(name, type, &init_tv, FALSE, 0); vim_free(name); diff --git a/src/vim9type.c b/src/vim9type.c index 637032d348..cc879d0e75 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -22,10 +22,10 @@ /* * Allocate memory for a type_T and add the pointer to type_gap, so that it can - * be freed later. + * be easily freed later. */ type_T * -alloc_type(garray_T *type_gap) +get_type_ptr(garray_T *type_gap) { type_T *type; @@ -48,6 +48,60 @@ clear_type_list(garray_T *gap) ga_clear(gap); } +/* + * Take a type that is using entries in a growarray and turn it into a type + * with allocated entries. + */ + type_T * +alloc_type(type_T *type) +{ + type_T *ret; + + if (type == NULL) + return NULL; + + // A fixed type never contains allocated types, return as-is. + if (type->tt_flags & TTFLAG_STATIC) + return type; + + ret = ALLOC_ONE(type_T); + *ret = *type; + + if (ret->tt_member != NULL) + ret->tt_member = alloc_type(ret->tt_member); + if (type->tt_args != NULL) + { + int i; + + ret->tt_args = ALLOC_MULT(type_T *, type->tt_argcount); + if (ret->tt_args != NULL) + for (i = 0; i < type->tt_argcount; ++i) + ret->tt_args[i] = alloc_type(type->tt_args[i]); + } + + return ret; +} + +/* + * Free a type that was created with alloc_type(). + */ + void +free_type(type_T *type) +{ + int i; + + if (type == NULL || (type->tt_flags & TTFLAG_STATIC)) + return; + if (type->tt_args != NULL) + { + for (i = 0; i < type->tt_argcount; ++i) + free_type(type->tt_args[i]); + vim_free(type->tt_args); + } + free_type(type->tt_member); + vim_free(type); +} + type_T * get_list_type(type_T *member_type, garray_T *type_gap) { @@ -67,7 +121,7 @@ get_list_type(type_T *member_type, garray_T *type_gap) return &t_list_string; // Not a common type, create a new entry. - type = alloc_type(type_gap); + type = get_type_ptr(type_gap); if (type == NULL) return &t_any; type->tt_type = VAR_LIST; @@ -96,7 +150,7 @@ get_dict_type(type_T *member_type, garray_T *type_gap) return &t_dict_string; // Not a common type, create a new entry. - type = alloc_type(type_gap); + type = get_type_ptr(type_gap); if (type == NULL) return &t_any; type->tt_type = VAR_DICT; @@ -112,7 +166,7 @@ get_dict_type(type_T *member_type, garray_T *type_gap) type_T * alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap) { - type_T *type = alloc_type(type_gap); + type_T *type = get_type_ptr(type_gap); if (type == NULL) return &t_any; @@ -197,13 +251,14 @@ func_type_add_arg_types( /* * Get a type_T for a typval_T. - * "type_list" is used to temporarily create types in. + * "type_gap" is used to temporarily create types in. */ static type_T * typval2type_int(typval_T *tv, garray_T *type_gap) { type_T *type; - type_T *member_type; + type_T *member_type = &t_any; + int argcount = 0; if (tv->v_type == VAR_NUMBER) return &t_number; @@ -262,8 +317,18 @@ typval2type_int(typval_T *tv, garray_T *type_gap) else name = tv->vval.v_string; if (name != NULL) - // TODO: how about a builtin function? - ufunc = find_func(name, FALSE, NULL); + { + int idx = find_internal_func(name); + + if (idx >= 0) + { + // TODO: get actual arg count and types + argcount = -1; + member_type = internal_func_ret_type(idx, 0, NULL); + } + else + ufunc = find_func(name, FALSE, NULL); + } if (ufunc != NULL) { // May need to get the argument types from default values by @@ -276,11 +341,12 @@ typval2type_int(typval_T *tv, garray_T *type_gap) } } - type = alloc_type(type_gap); + type = get_type_ptr(type_gap); if (type == NULL) return NULL; type->tt_type = tv->v_type; - type->tt_member = &t_any; + type->tt_argcount = argcount; + type->tt_member = member_type; return type; } @@ -311,7 +377,7 @@ typval2type(typval_T *tv, garray_T *type_gap) && (tv->vval.v_number == 0 || tv->vval.v_number == 1)) || (tv->v_lock & VAR_BOOL_OK))) { - type_T *newtype = alloc_type(type_gap); + type_T *newtype = get_type_ptr(type_gap); // Number 0 and 1 and expression with "&&" or "||" can also be used // for bool. @@ -420,6 +486,7 @@ check_type(type_T *expected, type_T *actual, int give_msg, int argidx) ret = check_type(expected->tt_member, actual->tt_member, FALSE, 0); if (ret == OK && expected->tt_argcount != -1 + && actual->tt_argcount != -1 && (actual->tt_argcount < expected->tt_min_argcount || actual->tt_argcount > expected->tt_argcount)) ret = FAIL; @@ -940,7 +1007,6 @@ type_name(type_T *type, char **tofree) ga_init2(&ga, 1, 100); if (ga_grow(&ga, 20) == FAIL) return "[unknown]"; - *tofree = ga.ga_data; STRCPY(ga.ga_data, "func("); ga.ga_len += 5; @@ -963,20 +1029,19 @@ type_name(type_T *type, char **tofree) if (ga_grow(&ga, len + 8) == FAIL) { vim_free(arg_free); + ga_clear(&ga); return "[unknown]"; } - *tofree = ga.ga_data; if (varargs && i == type->tt_argcount - 1) - { - STRCPY((char *)ga.ga_data + ga.ga_len, "..."); - ga.ga_len += 3; - } + ga_concat(&ga, (char_u *)"..."); else if (i >= type->tt_min_argcount) *((char *)ga.ga_data + ga.ga_len++) = '?'; - STRCPY((char *)ga.ga_data + ga.ga_len, arg_type); - ga.ga_len += len; + ga_concat(&ga, (char_u *)arg_type); vim_free(arg_free); } + if (type->tt_argcount < 0) + // any number of arguments + ga_concat(&ga, (char_u *)"..."); if (type->tt_member == &t_void) STRCPY((char *)ga.ga_data + ga.ga_len, ")"); @@ -990,18 +1055,18 @@ type_name(type_T *type, char **tofree) if (ga_grow(&ga, len) == FAIL) { vim_free(ret_free); + ga_clear(&ga); return "[unknown]"; } - *tofree = ga.ga_data; STRCPY((char *)ga.ga_data + ga.ga_len, "): "); STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name); vim_free(ret_free); } + *tofree = ga.ga_data; return ga.ga_data; } return name; } - #endif // FEAT_EVAL