patch 9.2.0573: Vim9: missing EX_WHOLE on some block keywords

Problem:  Several Vim9 keywords lack EX_WHOLE and can be shortened in
          Vim9 script, inconsistent with endif/enddef/endfor/endwhile/
          endtry which already have it.  The error from :endd in a
          nested function also hardcodes "enddef" instead of reporting
          what the user typed.  fullcommand("ho") returns "horizontal"
          even though :ho is below the documented 3-char minimum.
Solution: Add EX_WHOLE to :class, :def, :endclass, :endinterface,
          :endenum, :public and :static.  In get_function_body() pass
          the user-typed command to the error message.  Force :ho to
          CMD_SIZE in find_ex_command() so fullcommand() reflects the
          modifier minimum.  Extend tests and documentation accordingly
          (Peter Kenny).

fixes:  #20032
closes: #20191

Signed-off-by: Peter Kenny <github.com@k1w1.cyou>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Peter Kenny
2026-05-31 19:14:21 +00:00
committed by Christian Brabandt
parent 905312e0e6
commit 38d9a16eba
18 changed files with 360 additions and 150 deletions
+16 -8
View File
@@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.2. Last change: 2026 May 21
*builtin.txt* For Vim version 9.2. Last change: 2026 May 31
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -3672,14 +3672,22 @@ fullcommand({name} [, {vim9}]) *fullcommand()*
ambiguous (for user-defined commands) or cannot be shortened
this way. |vim9-no-shorten|
Without the {vim9} argument uses the current script version.
If {vim9} is present and FALSE then legacy script rules are
used. When {vim9} is present and TRUE then Vim9 rules are
used, e.g. "en" is not a short form of "endif".
For example `fullcommand('s')`, `fullcommand('sub')`,
`fullcommand(':%substitute')` all return "substitute".
Without the {vim9} argument, the current script version is
used. When {vim9} is present and FALSE, legacy script rules
are used. When {vim9} is present and TRUE, Vim9 rules are
used (e.g., "en" is not a short form of "endif").
Note: Command validation is not performed. Results depend on
Vim's internal command-specific identification rules.
Examples:
>vim
echo [fullcommand('s')] |" ['substitute']
echo [fullcommand('sub')] |" ['substitute']
echo [fullcommand(': mark word')] |" ['mark']
echo [fullcommand(': markword')] |" ['']
echo [fullcommand('en')] |" ['endif']
echo [fullcommand('en', v:true)] |" ['']
<
Can also be used as a |method|: >
GetName()->fullcommand()
<
+18 -14
View File
@@ -1,4 +1,4 @@
*change.txt* For Vim version 9.2. Last change: 2026 Mar 31
*change.txt* For Vim version 9.2. Last change: 2026 May 31
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -75,18 +75,21 @@ For inserting text see |insert.txt|.
*:d* *:de* *:del* *:delete* *:dl* *:dp*
:[range]d[elete] [x] Delete [range] lines (default: current line) [into
register x].
Note these weird abbreviations:
:dl delete and list
:dell idem
:delel idem
:deletl idem
:deletel idem
:dp delete and print
:dep idem
:delp idem
:delep idem
:deletp idem
:deletep idem
Note these weird abbreviations applicable only to
legacy Vim script:
:dl delete and list
:dell idem
:delel idem
:deletl idem
:deletel idem
:dp delete and print
:dep idem
:delp idem
:delep idem
:deletp idem
:deletep idem
Warning: These give |E492| in |Vim9| script and `:dl`
executes as `:dlist`.
:[range]d[elete] [x] {count}
Delete {count} lines, starting with [range]
@@ -798,7 +801,8 @@ out then. Example: >
:%s/TESTING
This deletes "TESTING" from all lines, but only one per line.
*E1270*
For compatibility with Vi these two exceptions are allowed in legacy script:
For compatibility with Vi these two exceptions are allowed in legacy Vim
script:
"\/{string}/" and "\?{string}?" do the same as "//{string}/r".
"\&{string}&" does the same as "//{string}/".
*pattern-delimiter* *E146* *E1241* *E1242*
+3 -3
View File
@@ -1,4 +1,4 @@
*eval.txt* For Vim version 9.2. Last change: 2026 May 21
*eval.txt* For Vim version 9.2. Last change: 2026 May 31
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -3384,7 +3384,7 @@ text...
s: script-local variables
l: local function variables
v: Vim variables.
This does not work in Vim9 script. |vim9-declaration|
This does not work in Vim9 script. |vim9-declaration|
:let List the values of all variables. The type of the
variable is indicated before the value:
@@ -3660,7 +3660,7 @@ text...
all nested `:try`s inside the loop. The outermost
`:endtry` then jumps back to the start of the loop.
In |Vim9| script `:cont` is the shortest form, to
In |Vim9| script `:continue` cannot be shortened, to
improve script readability.
*:break* *:brea* *E587*
:brea[k] When used inside a `:while` or `:for` loop, skips to
+4 -1
View File
@@ -1,4 +1,4 @@
*repeat.txt* For Vim version 9.2. Last change: 2026 Feb 14
*repeat.txt* For Vim version 9.2. Last change: 2026 May 31
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -454,6 +454,9 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
nested ":try"s in the script. The outermost ":endtry"
then stops sourcing the script.
In |Vim9| script `:finish` cannot be shortened, to
improve script readability.
All commands and command sequences can be repeated by putting them in a named
register and then executing it. There are two ways to get the commands in the
register:
+4 -3
View File
@@ -1,4 +1,4 @@
*tagsrch.txt* For Vim version 9.2. Last change: 2026 May 17
*tagsrch.txt* For Vim version 9.2. Last change: 2026 May 31
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -833,8 +833,9 @@ CTRL-W i Open a new window, with the cursor on the first line
Like `[D` and `]D`, but search in [range] lines
(default: whole file).
See |:search-args| for [/] and [!].
Note that `:dl` works like `:delete` with the "l"
flag, not `:dlist`.
Note: In legacy Vim script, `:dl` works like
`:delete` with the "l" flag, not `:dlist`, whereas in
|Vim9| script `:dl` does work like `:dlist`.
*[_CTRL-D*
[ CTRL-D Jump to the first macro definition that contains the
+16 -6
View File
@@ -1,4 +1,4 @@
*userfunc.txt* For Vim version 9.2. Last change: 2026 Feb 14
*userfunc.txt* For Vim version 9.2. Last change: 2026 May 31
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -27,13 +27,13 @@ make them script-local. If you do use a global function then avoid obvious,
short names. A good habit is to start the function name with the name of the
script, e.g., "HTMLcolor()".
In legacy script it is also possible to use curly braces, see
In legacy Vim script it is also possible to use curly braces, see
|curly-braces-names|.
The |autoload| facility is useful to define a function only when it's called.
*local-function*
A function local to a legacy script must start with "s:". A local script
A function local to a legacy Vim script must start with "s:". A local script
function can only be called from within the script and from functions, user
commands and autocommands defined in the script. It is also possible to call
the function from a mapping defined in the script, but then |<SID>| must be
@@ -195,9 +195,19 @@ See |:verbose-cmd| for more information.
When a function ends without an explicit ":return",
the number 0 is returned.
In a :def function *E1095* is given if unreachable
code follows after the `:return`.
In legacy script there is no check for unreachable
In |Vim9| script:
- `:return` cannot be shortened, and
- *E1095* is given if unreachable code follows after
the `:return`. For example:
>vim9
vim9script
var L: func = (): bool => {
return false
echo 'no' # E1095: Unreachable code after :return
}
echo L()
<
In legacy Vim script there is no check for unreachable
lines, thus there is no warning if commands follow
`:return`. Also, there is no check if the following
line contains a valid command. Forgetting the line
+12 -5
View File
@@ -1,9 +1,7 @@
*usr_20.txt* For Vim version 9.2. Last change: 2026 Feb 14
*usr_20.txt* For Vim version 9.2. Last change: 2026 May 31
VIM USER MANUAL by Bram Moolenaar
Typing command-line commands quickly
@@ -116,9 +114,18 @@ command. It's like deleting the ":" or "/" that the line starts with.
*20.2* Command line abbreviations
Some of the ":" commands are really long. We already mentioned that
":substitute" can be abbreviated to ":s". This is a generic mechanism, all
":" commands can be abbreviated.
":substitute" can be abbreviated to ":s". This is a generic mechanism, and
most ":" commands can be abbreviated. However, in Vim9 script some commands
cannot be shortened to improve readability - see |vim9-no-shorten|.
The builtin function |fullcommand()| can be used to return an abbreviated
command's full name. For example, the following commands echo "edit", "echo",
and "echowindow":
>vim
:echo fullcommand('e')
:echo fullcommand('ec')
:echo fullcommand('echow')
<
How short can a command get? There are 26 letters, and many more commands.
For example, ":set" also starts with ":s", but ":s" doesn't start a ":set"
command. Instead ":set" can be abbreviated to ":se".
+7 -7
View File
@@ -362,7 +362,7 @@ EXCMD(CMD_clast, "clast", ex_cc,
EX_RANGE|EX_COUNT|EX_TRLBAR|EX_BANG,
ADDR_UNSIGNED),
EXCMD(CMD_class, "class", ex_class,
EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE|EX_EXPORT,
ADDR_NONE),
EXCMD(CMD_close, "close", ex_close,
EX_BANG|EX_RANGE|EX_COUNT|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
@@ -470,7 +470,7 @@ EXCMD(CMD_debuggreedy, "debuggreedy", ex_debuggreedy,
EX_RANGE|EX_ZEROR|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
ADDR_OTHER),
EXCMD(CMD_def, "def", ex_function,
EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE|EX_EXPORT,
ADDR_NONE),
EXCMD(CMD_defcompile, "defcompile", ex_defcompile,
EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_TRLBAR|EX_EXTRA,
@@ -575,16 +575,16 @@ EXCMD(CMD_endif, "endif", ex_endif,
EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_endinterface, "endinterface", ex_wrongmodifier,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_endclass, "endclass", ex_wrongmodifier,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_enddef, "enddef", ex_endfunction,
EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_endenum, "endenum", ex_wrongmodifier,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_endfunction, "endfunction", ex_endfunction,
EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
@@ -1229,7 +1229,7 @@ EXCMD(CMD_put, "put", ex_put,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_REGSTR|EX_TRLBAR|EX_ZEROR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
ADDR_LINES),
EXCMD(CMD_public, "public", ex_wrongmodifier,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_pwd, "pwd", ex_pwd,
EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
@@ -1508,7 +1508,7 @@ EXCMD(CMD_startreplace, "startreplace", ex_startinsert,
EX_BANG|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_static, "static", ex_wrongmodifier,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE,
ADDR_NONE),
EXCMD(CMD_stopinsert, "stopinsert", ex_stopinsert,
EX_BANG|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
+7
View File
@@ -4025,6 +4025,13 @@ find_ex_command(
if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9)
eap->cmdidx = CMD_finally;
// Force ":ho" to be unresolved. Without this, find_ex_command()
// matches it to CMD_horizontal (the only "ho*" entry), which makes
// fullcommand("ho") return "horizontal" even though ":ho" cannot be
// used as the modifier (cmdmods[] requires 3 chars, "hor").
if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2)
eap->cmdidx = CMD_SIZE;
#ifdef FEAT_EVAL
if (eap->cmdidx < CMD_SIZE
&& vim9
+15 -6
View File
@@ -50,12 +50,18 @@ def Test_cmdmods_array()
enddef
def Test_keep_cmdmods_names()
# :k only available in legacy script
legacy call assert_equal('k', fullcommand(':k'))
legacy call assert_equal('k', fullcommand(':ke'))
# single character commands not supported in Vim9
assert_equal('', fullcommand(':k'))
assert_equal('keepmarks', fullcommand(':ke'))
# :k is only available in legacy Vim script
assert_equal('k', fullcommand(':k', false))
# many single character commands are not supported in Vim9 script, incl. :k
assert_equal('', fullcommand(':k', true))
# :k{a-zA-Z'} in legacy Vim script
assert_equal('k', fullcommand(':ka', false))
assert_equal('', fullcommand(':ka', true))
# :ke is an exception - it is 'keepmarks', not 'k', in Vim9 script
assert_equal('k', fullcommand(':ke', false))
assert_equal('keepmarks', fullcommand(':ke', true))
# :kee* shortenings
assert_equal('keepmarks', fullcommand(':kee', false))
assert_equal('keepmarks', fullcommand(':kee'))
assert_equal('keepmarks', fullcommand(':keep'))
assert_equal('keepmarks', fullcommand(':keepm'))
@@ -63,14 +69,17 @@ def Test_keep_cmdmods_names()
assert_equal('keepmarks', fullcommand(':keepmar'))
assert_equal('keepmarks', fullcommand(':keepmark'))
assert_equal('keepmarks', fullcommand(':keepmarks'))
assert_equal('keepalt', fullcommand(':keepa', false))
assert_equal('keepalt', fullcommand(':keepa'))
assert_equal('keepalt', fullcommand(':keepal'))
assert_equal('keepalt', fullcommand(':keepalt'))
assert_equal('keepjumps', fullcommand(':keepj', false))
assert_equal('keepjumps', fullcommand(':keepj'))
assert_equal('keepjumps', fullcommand(':keepju'))
assert_equal('keepjumps', fullcommand(':keepjum'))
assert_equal('keepjumps', fullcommand(':keepjump'))
assert_equal('keepjumps', fullcommand(':keepjumps'))
assert_equal('keeppatterns', fullcommand(':keepp', false))
assert_equal('keeppatterns', fullcommand(':keepp'))
assert_equal('keeppatterns', fullcommand(':keeppa'))
assert_equal('keeppatterns', fullcommand(':keeppat'))
+7
View File
@@ -254,7 +254,14 @@ func Test_marks_k_cmd()
call setline(1, ['foo', 'bar', 'baz', 'qux'])
1,3kr
call assert_equal([0, 3, 1, 0], getpos("'r"))
" whitespace before mark
4k f
call assert_equal([0, 4, 1, 0], getpos("'f"))
:2 k g
call assert_equal([0, 2, 1, 0], getpos("'g"))
bw!
call assert_fails(':kz7', 'E488: Trailing characters: z7')
call assert_fails(':execute ":k^"', 'E191: Argument must be a letter or forward/backward quote')
endfunc
" Test for file marks (A-Z)
+10 -15
View File
@@ -42,7 +42,8 @@ def Test_class_basic()
END
v9.CheckSourceFailure(lines, 'E475: Invalid argument: classy Something', 2)
# The complete "endclass" should be specified.
# Test for "endclass" cannot be shortened. Test_shortened_invalid_vim9() in
# test_vim9_script.vim has complete coverage (:endc to :endclas)
lines =<< trim END
vim9script
class Something
@@ -50,7 +51,7 @@ def Test_class_basic()
END
v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endcl', 3)
# "endclass" cannot be shortened (variant incl. whitespace and colon)
# "endclass" cannot be shortened (variant incl. colon-whitespace)
lines =<< trim END
vim9script
class Something
@@ -1361,13 +1362,14 @@ def Test_instance_variable_access()
echo Foo.new()
.Add(1).Add(2).x
echo Foo.new()
.Add(1)
.Add(1)
.Add(2)
.x
END
v9.CheckSourceSuccess(lines)
# Test for "public" cannot be abbreviated
# Test for "public" cannot be shortened. Test_shortened_invalid_vim9() in
# test_vim9_script.vim has complete coverage (:pub to :publi)
lines =<< trim END
vim9script
class Something
@@ -1460,7 +1462,8 @@ enddef
" Test for class variable access
def Test_class_variable_access()
# Test for "static" cannot be abbreviated
# Test for "static" cannot be shortened. Test_shortened_invalid_vim9() in
# test_vim9_script.vim has complete coverage (:stat and :stati)
var lines =<< trim END
vim9script
class Something
@@ -2951,7 +2954,8 @@ def Test_abstract_class()
END
v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1)
# Test for "abstract" cannot be abbreviated
# Test for "abstract" cannot be shortened. Test_shortened_invalid_vim9() in
# test_vim9_script.vim has complete coverage (:abs to :abstrac)
lines =<< trim END
vim9script
abs class A
@@ -5580,15 +5584,6 @@ def Test_abstract_method()
END
v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3)
# Abbreviate the "abstract" keyword
lines =<< trim END
vim9script
class A
abs def Foo()
endclass
END
v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3)
# Use "abstract" with a member variable
lines =<< trim END
vim9script
+6
View File
@@ -389,6 +389,12 @@ def Test_endfunc_enddef()
enddef there
END
v9.CheckScriptFailure(lines, 'E1173: Text found after enddef: there', 6)
lines =<< trim END
def ShortEnddef()
endd
END
v9.CheckScriptFailure(lines, 'E1065: Command cannot be shortened: endd', 2)
enddef
def Test_missing_endfunc_enddef()
+1 -1
View File
@@ -86,7 +86,7 @@ def Test_interface_basics()
END
v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3)
# "endinterface" cannot be shortened (variant incl. whitespace and colon)
# "endinterface" cannot be shortened (variant incl. colon-whitespace)
lines =<< trim END
vim9script
interface Short
+204 -80
View File
@@ -3,6 +3,7 @@
import './util/vim9.vim' as v9
source util/screendump.vim
" Test for has('vim9script')
def Test_vim9script_feature()
# example from the help, here the feature is always present
var lines =<< trim END
@@ -1216,7 +1217,7 @@ def Test_error_in_catch()
v9.CheckDefExecFailure(lines, 'E684:', 4)
enddef
" :while at the very start of a function that :continue jumps to
" Test for :while at the very start of a function that :continue jumps to
def s:TryContinueFunc()
while g:Count < 2
g:sequence ..= 't'
@@ -1330,8 +1331,8 @@ def Test_nocatch_throw_silenced()
source XthrowSilenced
enddef
" g:DeletedFunc() is found when compiling Test_try_catch_throw() and then
" deleted, this should give a runtime error.
" Test for g:DeletedFunc() is found when compiling Test_try_catch_throw() and
" then deleted, this should give a runtime error.
def DeletedFunc(): list<any>
return ['delete me']
enddef
@@ -1522,7 +1523,7 @@ def Try_catch_skipped()
endif
enddef
" The skipped try/endtry was updating the wrong instruction.
" Test for when the skipped try/endtry was updating the wrong instruction.
def Test_try_catch_skipped()
var instr = execute('disassemble Try_catch_skipped')
assert_match("NEWLIST size 0\n", instr)
@@ -4376,62 +4377,144 @@ def Run_test_reject_declaration()
g:StopVimInTerminal(buf)
enddef
def Test_minimal_command_name_length()
var names = [
'cons',
'brea',
'cat',
'catc',
'con',
'cont',
'conti',
'contin',
'continu',
'el',
'els',
'elsei',
'endfo',
'en',
'end',
'endi',
'endw',
'endt',
'endtr',
'exp',
'expo',
'expor',
'fina',
'finall',
'fini',
'finis',
'imp',
'impo',
'impor',
'retu',
'retur',
'th',
'thr',
'thro',
'wh',
'whi',
'whil',
]
for name in names
v9.CheckDefAndScriptFailure([name .. ' '], 'E1065:')
" Test shortened commands that are invalid in Vim9 script
def Test_shortened_invalid_vim9()
# Many Vim9 script commands cannot be shortened/abbreviated.
# SHORTENED is a list of dicts, each with a single key (the exact shortened
# command) and a list value with four items:
# [0] list<string> Lines passed to the check function (without 'vim9script'
# for SourceFailure lines)
# [1] number Line number where the error is expected ('vimscript',
# which is not in the list, is line 1 in the
# 'SourceFailure' and 'DefFailure lines, so needs to be
# included in the count)
# [2] string 'DefAndScriptFailure', 'SourceFailure', or 'DefFailure'
# specifying the applicable 'Check' function to call
const SHORTENED: list<dict<list<any>>> = [
# abstract
{abs: [['abs class A'], 1, 'DefAndScriptFailure']},
{abst: [['abst class A'], 1, 'DefAndScriptFailure']},
{abstr: [['abstr class A'], 1, 'DefAndScriptFailure']},
{abstra: [['abstra class A'], 1, 'DefAndScriptFailure']},
{abstrac: [['abstrac class A'], 1, 'DefAndScriptFailure']},
# break
{brea: [['for k in range(0, 2)', 'brea', 'endfor'], 2, 'DefAndScriptFailure']},
# catch
{cat: [['try', 'echo 0', 'cat'], 3, 'DefAndScriptFailure']},
{catc: [['try', 'echo 0', 'catc'], 3, 'DefAndScriptFailure']},
# class - n/a because :clas is :clast
# const
{cons: [['cons C = 0'], 1, 'DefAndScriptFailure']},
# continue
{con: [['var n: number', 'while n < 9', '++n', 'con'], 4, 'DefAndScriptFailure']},
{cont: [['var n: number', 'while n < 9', '++n', 'cont'], 4, 'DefAndScriptFailure']},
{conti: [['var n: number', 'while n < 9', '++n', 'conti'], 4, 'DefAndScriptFailure']},
{contin: [['var n: number', 'while n < 9', '++n', 'contin'], 4, 'DefAndScriptFailure']},
{continu: [['var n: number', 'while n < 9', '++n', 'continu'], 4, 'DefAndScriptFailure']},
# def has no applicable shortened form (:de is :delete)
# else
{els: [['if true', 'els'], 2, 'DefAndScriptFailure']},
# elseif
{elsei: [['if true', 'elsei false'], 2, 'DefAndScriptFailure']},
# endclass
{endc: [['class C', 'endc'], 3, 'SourceFailure']},
{endcl: [['class C', 'endcl'], 3, 'SourceFailure']},
{endcla: [['class C', 'endcla'], 3, 'SourceFailure']},
{endclas: [['class C', 'endclas'], 3, 'SourceFailure']},
# enddef
# (NB: The separate DefFailure check tests them nested -
# DefAndScriptFailure cannot be used for testing :endd[e])
{endd: [['def D()', 'endd'], 3, 'SourceFailure']},
{endde: [['def D()', 'endde'], 3, 'SourceFailure']},
{endd: [['var R: func = (): bool => {', 'def D()', 'endd', '}'], 4, 'DefFailure']},
{endde: [['var R: func = (): bool => {', 'def D()', 'endde', '}'], 4, 'DefFailure']},
# endenum
{ende: [['enum E', 'ende'], 3, 'SourceFailure']},
{enden: [['enum E', 'enden'], 3, 'SourceFailure']},
{endenu: [['enum E', 'endenu'], 3, 'SourceFailure']},
# endfor
{endfo: [['for n in range(0, 2)', 'endfo'], 2, 'DefAndScriptFailure']},
# endif
{en: [['if true', 'en'], 2, 'DefAndScriptFailure']},
{end: [['if true', 'end'], 2, 'DefAndScriptFailure']},
{endi: [['if true', 'endi'], 2, 'DefAndScriptFailure']},
# endinterface
{endin: [['interface I', 'endin'], 3, 'SourceFailure']},
{endint: [['interface I', 'endint'], 3, 'SourceFailure']},
{endinte: [['interface I', 'endinte'], 3, 'SourceFailure']},
{endinter: [['interface I', 'endinter'], 3, 'SourceFailure']},
{endinterf: [['interface I', 'endinterf'], 3, 'SourceFailure']},
{endinterfa: [['interface I', 'endinterfa'], 3, 'SourceFailure']},
{endinterfac: [['interface I', 'endinterfac'], 3, 'SourceFailure']},
# endtry
{endt: [['try', 'echo 0', 'endt'], 3, 'DefAndScriptFailure']},
{endtr: [['try', 'echo 0', 'endtr'], 3, 'DefAndScriptFailure']},
# endwhile
{endw: [['var n = 9', 'while n > 0', '--n', 'endw'], 4, 'DefAndScriptFailure']},
{endwh: [['var n = 9', 'while n > 0', '--n', 'endwh'], 4, 'DefAndScriptFailure']},
{endwhi: [['var n = 9', 'while n > 0', '--n', 'endwhi'], 4, 'DefAndScriptFailure']},
{endwhil: [['var n = 9', 'while n > 0', '--n', 'endwhil'], 4, 'DefAndScriptFailure']},
# enum
{enu: [['enu E', 'endenum'], 2, 'SourceFailure']},
# export
{exp: [['exp var b: bool'], 2, 'SourceFailure']},
{expo: [['expo var b: bool'], 2, 'SourceFailure']},
{expor: [['expor var b: bool'], 2, 'SourceFailure']},
# final has no applicable shortened form (because :fina is short for :finally)
# finally
{fina: [['try', '# Do something', 'fina'], 3, 'DefAndScriptFailure']},
# finish
{fini: [['fini'], 1, 'DefAndScriptFailure']},
# import
{imp: [['imp $"{$VIMRUNTIME}/autoload/ccomplete.vim"'], 2, 'SourceFailure']},
{impo: [['impo $"{$VIMRUNTIME}/autoload/ccomplete.vim"'], 2, 'SourceFailure']},
{impor: [['impor $"{$VIMRUNTIME}/autoload/ccomplete.vim"'], 2, 'SourceFailure']},
# interface
{inte: [['inte I', 'endinterface'], 2, 'SourceFailure']},
{inter: [['inter I', 'endinterface'], 2, 'SourceFailure']},
{interf: [['interf I', 'endinterface'], 2, 'SourceFailure']},
{interfa: [['interfa I', 'endinterface'], 2, 'SourceFailure']},
{interfac: [['interfac I', 'endinterface'], 2, 'SourceFailure']},
# public
{pub: [['class P', 'pub var b: bool', 'endclass'], 3, 'SourceFailure']},
{publ: [['class P', 'publ var b: bool', 'endclass'], 3, 'SourceFailure']},
{publi: [['class P', 'publi var b: bool', 'endclass'], 3, 'SourceFailure']},
# return (NB: line is 0 - for CheckDefAndScriptFailure the first line of the Vim9 script lambda function is considered 0)
{retu: [['var R: func = (): bool => {', 'retu false', '}'], 0, 'DefAndScriptFailure']},
{retur: [['var R: func = (): bool => {', 'retur false', '}'], 0, 'DefAndScriptFailure']},
# static
{stat: [['class S', 'stat var b: bool', 'endclass'], 3, 'SourceFailure']},
{stati: [['class S', 'stati var b: bool', 'endclass'], 3, 'SourceFailure']},
# this
{thi: [['thi'], 1, 'DefAndScriptFailure']},
# throw
{th: [['try', 'th 9', 'catch 9', 'echo "Should give E1065"', 'thr'], 2, 'DefAndScriptFailure']},
{thr: [['try', 'thr 9', 'catch 9', 'echo "Should give E1065"', 'thr'], 2, 'DefAndScriptFailure']},
{thro: [['try', 'thro 9', 'catch 9', 'echo "Should give E1065"', 'thro'], 2, 'DefAndScriptFailure']},
# type
{ty: [['ty ListOfBools = list<bool>'], 1, 'DefAndScriptFailure']},
{typ: [['typ ListOfBools = list<bool>'], 1, 'DefAndScriptFailure']},
# var
{va: [['va b: bool'], 1, 'DefAndScriptFailure']},
# while
{wh: [['var n = 9', 'wh n > 0', '--n', 'endwhile'], 2, 'DefAndScriptFailure']},
{whi: [['var n = 9', 'whi n > 0', '--n', 'endwhile'], 2, 'DefAndScriptFailure']},
{whil: [['var n = 9', 'whil n > 0', '--n', 'endwhile'], 2, 'DefAndScriptFailure']},
]
for short in SHORTENED
const CMD: string = short->keys()[0]
const LINES: list<string> = short[CMD][0]
const E1065: string = "E1065: Command cannot be shortened: " .. CMD
const LNUM: number = short[CMD][1]
const CHECK: string = short[CMD][2]
if CHECK == 'SourceFailure'
v9.CheckSourceFailure(['vim9script', LINES]->flattennew(), E1065, LNUM)
elseif CHECK == 'DefAndScriptFailure'
v9.CheckDefAndScriptFailure(LINES, E1065, LNUM)
elseif CHECK == 'DefFailure'
v9.CheckDefFailure(LINES, E1065, LNUM)
endif
endfor
var lines =<< trim END
vim9script
def SomeFunc()
endd
END
v9.CheckScriptFailure(lines, 'E1065:')
lines =<< trim END
vim9script
def SomeFunc()
endde
END
v9.CheckScriptFailure(lines, 'E1065:')
enddef
def Test_unset_any_variable()
@@ -5792,27 +5875,6 @@ def Test_type_func_with_void()
v9.CheckSourceFailure(lines, 'E1031: Cannot use void value', 4)
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new
setline(1, 'something')
:substitute(some(other(
assert_equal('otherthing', getline(1))
bwipe!
# also when the context is Vim9 script
var lines =<< trim END
vim9script
new
setline(1, 'something')
:substitute(some(other(
assert_equal('otherthing', getline(1))
bwipe!
END
writefile(lines, 'Xvim9lines', 'D')
source Xvim9lines
enddef
def Test_call_stack_string()
CheckScreendump
var lines =<< trim END
@@ -5994,4 +6056,66 @@ def Test_if_false_elseif_true_still_takes_elseif()
v9.CheckScriptSuccess(lines)
enddef
" Test for correct fullcommand() outputs: return the correct command (or '')
def Test_builtin_fullcommand()
# :hor is the minimum abbreviation of :horizontal; :ho is invalid
assert_equal('', fullcommand('ho', true))
assert_equal('horizontal', fullcommand('hor', true))
# :k is an invalid one-letter command in Vim9 script
assert_equal('', fullcommand('k', true))
assert_equal('', fullcommand(':k', true))
assert_equal('', fullcommand('karrrrrgh!', true))
assert_equal('k', fullcommand('k', false))
assert_equal('k', fullcommand(':k', false))
assert_equal('k', fullcommand('karrrrrgh!', false))
# :dl is "delete and list" in legacy Vim script but, because :dl itself is
# invalid in Vim9 script, :dl is 'dlist' in Vim9 script
assert_equal('delete', fullcommand('dl', v:false))
assert_equal('dlist', fullcommand('dl', v:true))
# Substitute :s two and three letter commands in legacy Vim script are
# invalid in Vim9 script
assert_equal('', fullcommand('sIr', true))
assert_equal('', fullcommand('sIrarrrrrgh!', true))
assert_equal('substitute', fullcommand('sIr', false))
assert_equal('substitute', fullcommand('sIrarrrrrgh!', false))
# Three :s? commands are exceptionss, returning different commands depending
# on whether the scope is legacy Vim script or Vim9 script
assert_equal('scriptnames', fullcommand('sc', true))
assert_equal('simalt', fullcommand('si', true))
assert_equal('srewind', fullcommand('sr', true))
assert_equal('substitute', fullcommand('sc', false))
assert_equal('substitute', fullcommand('si', false))
assert_equal('substitute', fullcommand('sr', false))
# :finally cannot be shortened in Vim9 script but :final should return 'final'
assert_equal('', fullcommand('fina', true))
assert_equal('final', fullcommand('final', true))
assert_equal('', fullcommand('finall', true))
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new
setline(1, 'something')
:substitute(some(other(
assert_equal('otherthing', getline(1))
bwipe!
# also when the context is Vim9 script
var lines =<< trim END
vim9script
new
setline(1, 'something')
:substitute(some(other(
assert_equal('otherthing', getline(1))
bwipe!
END
writefile(lines, 'Xvim9lines', 'D')
source Xvim9lines
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+27
View File
@@ -7702,6 +7702,33 @@ func Test_function_long_generic_name()
delfunc TestFunc
endfunc
" Test using fullcommand() {{{1
func Test_builtin_fullcommand()
" :hor is the minimum abbreviation of :horizontal; :ho is invalid
call assert_equal('', fullcommand('ho'))
call assert_equal('horizontal', fullcommand('hor'))
" :k takes one {a-zA-Z'} mark argument and optional whitespace
call assert_equal('k', fullcommand('k'))
call assert_equal('k', fullcommand(':k'))
call assert_equal('k', fullcommand('karrrrrgh!'))
" :dl is "delete and list" in a legacy Vim script scope
call assert_equal('delete', fullcommand('dl'))
" :s two and three letter commands
call assert_equal('substitute', fullcommand('sIr'))
call assert_equal('substitute', fullcommand('sIrarrrrrgh!'))
" :finally
call assert_equal('finally', fullcommand('fina'))
" 'final' - returns 'final', a Vim9 script-exclusive keyword
" - is a valid shortening of :finally in legacy Vim script
call assert_equal('final', fullcommand('final'))
call assert_equal('finally', fullcommand('finall'))
endfunc
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+1 -1
View File
@@ -1132,7 +1132,7 @@ get_function_body(
{
if (!nesting_inline[nesting] && nesting_def[nesting]
&& p < cmd + 6)
semsg(_(e_command_cannot_be_shortened_str), "enddef");
semsg(_(e_command_cannot_be_shortened_str), cmd);
if (nesting-- == 0)
{
char_u *nextcmd = NULL;
+2
View File
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
573,
/**/
572,
/**/