Skip to content

Commit

Permalink
Add syntax support for toolchain directive
Browse files Browse the repository at this point in the history
This directive was added to `go.mod` with Go 1.21[1] So that this
directive can be highlighted in `go.mod`.

It's not entirely clear exactly what the fully supported syntax is. The
docs[1] suggests any Go release version, e.g.

* `go1.20`
* `go1.21.1`
* `go1.21.1rc1`

`golang.org/x/mod` gives a much more relaxed definition, requiring just
that things match against the regex `^default$|^go1($|\.)`[2]

Finally there's `FromToolchain` from the stdlib's internals for
processing versions[3] which is broader than that from[1] but more
limited than that from[2], supporting arbitrary suffixes (after any of
`" \t-"`) appended to the version, e.g.

* go1.21.3-somesuffix
* go1.21rc2-somesuffix
* go1.21 some-suffix

The approach taken for the syntax matching here is closest to this final
condition, and will not include some toolchain verison's that would be
supported by the `modfile` regex, e.g.

* go1.21.1blah
* go1.21!some-suffix

Since these would be rejected by the `go` tool itself with an error like

> go: invalid toolchain "go1.21.1blah" in go.mod
  • Loading branch information
matthewhughes934 committed Jan 21, 2024
1 parent 5bed70d commit 26f4ff5
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 12 deletions.
114 changes: 114 additions & 0 deletions autoload/go/highlight_test.vim
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,120 @@ function! s:functionCallHighlightGroup(testname, value)
endtry
endfunc

function! Test_gomodToolchainVersion_highlight() abort
try
syntax on

let g:go_gopls_enabled = 0
let l:wd = getcwd()
let l:dir = gotest#write_file('gomodtest/go.mod', [
\ 'module github.com/fatih/vim-go',
\ '',
\ 'toolchain default',
\ 'toolchain go1',
\ 'toolchain go1',
\ 'toolchain go1.21',
\ 'toolchain go1.21rc3',
\ 'toolchain go1.21.3-somesuffix',
\ 'toolchain go1.21rc2-somesuffix',
\ 'toolchain go1.21 some-suffix',
\ ''])

let l:lineno = 3
let l:lineclose = line('$')
while l:lineno < l:lineclose
let l:line = getline(l:lineno)
let l:split_idx = stridx(l:line, ' ')
let l:idx = 0
let l:col = 1

while l:idx < len(l:line) - 1
call cursor(l:lineno, l:col)
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
let l:errlen = len(v:errors)

if l:idx < l:split_idx
call assert_equal('gomodToolchain', l:synname, 'toolchain on line ' . l:lineno . ' and col ' . l:col)
elseif l:idx > l:split_idx
call assert_equal('gomodToolchainVersion', l:synname, 'version on line ' . l:lineno . ' and col ' . l:col)
endif

if l:errlen < len(v:errors)
break
endif

let l:col += 1
let l:idx += 1
endwhile
let l:lineno += 1
endwhile

finally
call go#util#Chdir(l:wd)
call delete(l:dir, 'rf')
endtry
endfunc

function! Test_gomodToolchainVersion_invalid_highlight() abort
try
syntax on
let g:go_gopls_enabled = 0
let l:wd = getcwd()

" 1. No release candidate for patch versions
" 2+3. Release version can only be followed by 'rcN' or a valid suffix
" 4+5. toolchain version must start with 'go'
let l:dir = gotest#write_file('gomodtest/go.mod', [
\ 'module github.com/fatih/vim-go',
\ '',
\ 'toolchain go2',
\ 'toolchain go1.21.1.4',
\ 'toolchain go1.21.1blah',
\ 'toolchain go1.21!some-suffix',
\ 'toolchain something-else',
\ ''])

let l:lineno = 3
let l:lineclose = line('$')
while l:lineno < l:lineclose
let l:line = getline(l:lineno)
let l:col = col([l:lineno, '$']) - 1
let l:idx = len(l:line) - 1
" e.g. go1.21.1rc2 is valid until 'rc2'
" each 'go*' test above has last version number '1'
let l:valid_version_start_idx = strridx(l:line, '1')

if l:valid_version_start_idx != -1
let l:end_idx = l:valid_version_start_idx
else
" the whole version is invalid
let l:end_idx = stridx(l:line, ' ') + 1
endif

while l:idx > l:end_idx
call cursor(l:lineno, l:col)
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
let l:errlen = len(v:errors)

call assert_notequal('gomodToolchainVersion', l:synname, 'version on line ' . l:lineno . ' and col ' . l:col)

if l:errlen < len(v:errors)
break
endif

let l:col -= 1
let l:idx -= 1
endwhile
let l:lineno += 1
endwhile

finally
call go#util#Chdir(l:wd)
call delete(l:dir, 'rf')
endtry
endfunc


" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
Expand Down
39 changes: 27 additions & 12 deletions syntax/gomod.vim
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,30 @@ syntax case match
" https://golang.org/ref/mod#go-mod-file-grammar

" match keywords
syntax keyword gomodModule module
syntax keyword gomodGo go contained
syntax keyword gomodRequire require
syntax keyword gomodExclude exclude
syntax keyword gomodReplace replace
syntax keyword gomodRetract retract
syntax keyword gomodModule module
syntax keyword gomodGo go contained
syntax keyword gomodToolchain toolchain contained
syntax keyword gomodRequire require
syntax keyword gomodExclude exclude
syntax keyword gomodReplace replace
syntax keyword gomodRetract retract

" require, exclude, replace, and go can be also grouped into block
syntax region gomodRequire start='require (' end=')' transparent contains=gomodRequire,gomodVersion
syntax region gomodExclude start='exclude (' end=')' transparent contains=gomodExclude,gomodVersion
syntax region gomodReplace start='replace (' end=')' transparent contains=gomodReplace,gomodVersion
syntax region gomodRetract start='retract (' end=')' transparent contains=gomodVersionRange,gomodVersion
syntax match gomodGo '^go .*$' transparent contains=gomodGo,gomodGoVersion
syntax match gomodToolchain '^toolchain .*$' transparent contains=gomodToolchain,gomodToolchainVersion

" set highlights
highlight default link gomodModule Keyword
highlight default link gomodGo Keyword
highlight default link gomodRequire Keyword
highlight default link gomodExclude Keyword
highlight default link gomodReplace Keyword
highlight default link gomodRetract Keyword
highlight default link gomodModule Keyword
highlight default link gomodGo Keyword
highlight default link gomodToolchain Keyword
highlight default link gomodRequire Keyword
highlight default link gomodExclude Keyword
highlight default link gomodReplace Keyword
highlight default link gomodRetract Keyword

" comments are always in form of // ...
syntax region gomodComment start="//" end="$" contains=@Spell
Expand All @@ -45,6 +48,18 @@ highlight default link gomodString String
syntax match gomodReplaceOperator "\v\=\>"
highlight default link gomodReplaceOperator Operator

" match toolchain versions, based on https://go.dev/doc/toolchain#version,
" * default
" * go1
" * go1.X.Y
" * go1.X
" * go1.XrcN
" * go1.XrcN-somesuffix
" * go1.X.Y-somesuffix
syntax match gomodToolchainVersion "default$" contained
syntax match gomodToolchainVersion "go1\(.\d\+\)\{,2\}\(rc\d\+\)\?\([ \t-].*\)\?" contained
highlight default link gomodToolchainVersion Identifier

" match go versions
syntax match gomodGoVersion "1\.\d\+" contained
highlight default link gomodGoVersion Identifier
Expand Down

0 comments on commit 26f4ff5

Please sign in to comment.