Skip to content

Complete vim9 support - #206

Open
jclsn wants to merge 21 commits into
vim-jp:masterfrom
jclsn:feat/complete-vim9-support
Open

jclsn wants to merge 21 commits into
vim-jp:masterfrom
jclsn:feat/complete-vim9-support

Conversation

@jclsn

@jclsn jclsn commented Oct 20, 2026

Copy link
Copy Markdown

I made some good use of my tokens.

This is complete vim9scipt support. Including tests.

jclsn added 20 commits October 19, 2026 21:02
Use Vim9 syntax inside :def functions even in a legacy script, and legacy
syntax inside :function bodies in a Vim9 script, instead of a single
file-wide flag.  In Vim9 syntax a double quote no longer starts a comment.

Also parse "vim9script noclear" and treat :final as :finally in legacy
script.
In Vim9 syntax:
- a colon is only part of a name after a one letter scope (g:name)
- there are no curly-braces names
- {key: val} uses literal keys, [expr] gives an expression key
- "." is a member access, never a concatenation
- an expression continues on the next line inside brackets, after an
  operator, or when the next line starts with an operator; comments and
  empty lines in between are skipped
Add the Vim9 lambda syntax "(args) => expr" with optional argument and
return types, and lambdas with a block of statements "(args) => {" ... "}".
Add tuples "(a, b)" (NODE_TUPLE) and type casts "<type>expr" (NODE_CAST).
In Vim9 syntax recognize, like find_ex_command() in Vim:
- assignments without :let (NODE_ASSIGN), including "++var" and "--var"
- function calls without :call and expressions without :eval
- generic function calls "Func<type>(args)"
- "{" ... "}" blocks (NODE_BLOCK)

A range must be preceded by a colon.  "name = expr" is an assignment when
the name was declared or is not an Ex command.
Share one parser for :var, :final and Vim9 :const that handles a missing
initializer, unpacking with optional types "var [a: number, _; rest]"
and heredocs.  Also parse typed :for loop variables and the "func: type"
type.
Parse the arguments with Vim9 syntax also in a legacy script, allow them
to span lines with comments, and accept generic type parameters
"def Func<T>(", constructor arguments "this.name" and repeated "_".
Parse the file name as an expression and store whether it is an autoload
import (.autoload) and the name after "as" (.alias).  .str keeps the text
of the arguments.
Add NODE_CLASS, NODE_INTERFACE, NODE_ENUM with NODE_ENUMVALUE for the enum
values, and NODE_TYPE for :type.  :public, :static and :abstract wrap the
following command like :export.  Methods in an interface and abstract
methods have no body.
Add interpolated strings $"text {expr}" (NODE_INTERPOLATION), the falsy
operator "??" (NODE_FALSY) and "0o" octal numbers, which are available in
legacy script too.  In Vim9 script allow quotes between digits.
:vim9cmd and :legacy are command modifiers that select the syntax for the
command.  :defer (NODE_DEFER) and :echowindow (NODE_ECHOWINDOW) take
expressions.
"command Name {" and "autocmd Event pat {" start a block of commands in
Vim9 syntax up to "}".  The block is stored in .block of the EXCMD node.
Like Vim, accept a double quote comment after a command also in Vim9
script, e.g. "endif \"}}}", and skip the colon before a range after a
command modifier: "silent! :%delete _".
Add :clipreset, :iput, :pbuffer, :redrawtabpanel, :this, :uniq and
:wlrestore.
Like Vim, ignore lines starting with "\ (legacy) or #\ (Vim9) in between
lines continued with a backslash.
Like Vim, accept "call Func()->Method()": the expression must start with a
function call, which may be followed by method calls.
"^\+\+" in a JavaScript string is "^++", which is not a valid regular
expression, so parsing an invalid ++opt argument like "x ++" threw a
SyntaxError instead of E474.
Like Vim, :enddef at the start of a line ends the function and is not a
continuation line, so "var x =" before it is an incomplete line instead
of taking "enddef" as the value.
a:pos and a local "pos" are different variables in Vim script, but both
become "pos" in Python and JavaScript.  Rename the arguments that have
the name of a local variable: is_vim9_command() checked the command at the
wrong position, so "foo = 1" was an Ex command in Python and JavaScript,
and tuples got the position of the wrong token.
A variable alone on a line has no effect, unless an operator on the next
line continues the expression.  So "new" is the :new command, also when a
variable "new" was declared somewhere in the script.
@jclsn

jclsn commented Oct 20, 2026

Copy link
Copy Markdown
Author

@mattn Care to have a look?

Like Vim, give an error for :append, :change, :insert, :k, :mode, :open,
:t, :xit and :Print in Vim9 script, instead of parsing them as commands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant