Skip to content

Commit 26f4ff5

Browse files
Add syntax support for toolchain directive
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
1 parent 5bed70d commit 26f4ff5

File tree

2 files changed

+141
-12
lines changed

2 files changed

+141
-12
lines changed

autoload/go/highlight_test.vim

+114
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,120 @@ function! s:functionCallHighlightGroup(testname, value)
671671
endtry
672672
endfunc
673673

674+
function! Test_gomodToolchainVersion_highlight() abort
675+
try
676+
syntax on
677+
678+
let g:go_gopls_enabled = 0
679+
let l:wd = getcwd()
680+
let l:dir = gotest#write_file('gomodtest/go.mod', [
681+
\ 'module github.com/fatih/vim-go',
682+
\ '',
683+
\ 'toolchain default',
684+
\ 'toolchain go1',
685+
\ 'toolchain go1',
686+
\ 'toolchain go1.21',
687+
\ 'toolchain go1.21rc3',
688+
\ 'toolchain go1.21.3-somesuffix',
689+
\ 'toolchain go1.21rc2-somesuffix',
690+
\ 'toolchain go1.21 some-suffix',
691+
\ ''])
692+
693+
let l:lineno = 3
694+
let l:lineclose = line('$')
695+
while l:lineno < l:lineclose
696+
let l:line = getline(l:lineno)
697+
let l:split_idx = stridx(l:line, ' ')
698+
let l:idx = 0
699+
let l:col = 1
700+
701+
while l:idx < len(l:line) - 1
702+
call cursor(l:lineno, l:col)
703+
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
704+
let l:errlen = len(v:errors)
705+
706+
if l:idx < l:split_idx
707+
call assert_equal('gomodToolchain', l:synname, 'toolchain on line ' . l:lineno . ' and col ' . l:col)
708+
elseif l:idx > l:split_idx
709+
call assert_equal('gomodToolchainVersion', l:synname, 'version on line ' . l:lineno . ' and col ' . l:col)
710+
endif
711+
712+
if l:errlen < len(v:errors)
713+
break
714+
endif
715+
716+
let l:col += 1
717+
let l:idx += 1
718+
endwhile
719+
let l:lineno += 1
720+
endwhile
721+
722+
finally
723+
call go#util#Chdir(l:wd)
724+
call delete(l:dir, 'rf')
725+
endtry
726+
endfunc
727+
728+
function! Test_gomodToolchainVersion_invalid_highlight() abort
729+
try
730+
syntax on
731+
let g:go_gopls_enabled = 0
732+
let l:wd = getcwd()
733+
734+
" 1. No release candidate for patch versions
735+
" 2+3. Release version can only be followed by 'rcN' or a valid suffix
736+
" 4+5. toolchain version must start with 'go'
737+
let l:dir = gotest#write_file('gomodtest/go.mod', [
738+
\ 'module github.com/fatih/vim-go',
739+
\ '',
740+
\ 'toolchain go2',
741+
\ 'toolchain go1.21.1.4',
742+
\ 'toolchain go1.21.1blah',
743+
\ 'toolchain go1.21!some-suffix',
744+
\ 'toolchain something-else',
745+
\ ''])
746+
747+
let l:lineno = 3
748+
let l:lineclose = line('$')
749+
while l:lineno < l:lineclose
750+
let l:line = getline(l:lineno)
751+
let l:col = col([l:lineno, '$']) - 1
752+
let l:idx = len(l:line) - 1
753+
" e.g. go1.21.1rc2 is valid until 'rc2'
754+
" each 'go*' test above has last version number '1'
755+
let l:valid_version_start_idx = strridx(l:line, '1')
756+
757+
if l:valid_version_start_idx != -1
758+
let l:end_idx = l:valid_version_start_idx
759+
else
760+
" the whole version is invalid
761+
let l:end_idx = stridx(l:line, ' ') + 1
762+
endif
763+
764+
while l:idx > l:end_idx
765+
call cursor(l:lineno, l:col)
766+
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
767+
let l:errlen = len(v:errors)
768+
769+
call assert_notequal('gomodToolchainVersion', l:synname, 'version on line ' . l:lineno . ' and col ' . l:col)
770+
771+
if l:errlen < len(v:errors)
772+
break
773+
endif
774+
775+
let l:col -= 1
776+
let l:idx -= 1
777+
endwhile
778+
let l:lineno += 1
779+
endwhile
780+
781+
finally
782+
call go#util#Chdir(l:wd)
783+
call delete(l:dir, 'rf')
784+
endtry
785+
endfunc
786+
787+
674788
" restore Vi compatibility settings
675789
let &cpo = s:cpo_save
676790
unlet s:cpo_save

syntax/gomod.vim

+27-12
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,30 @@ syntax case match
1111
" https://golang.org/ref/mod#go-mod-file-grammar
1212

1313
" match keywords
14-
syntax keyword gomodModule module
15-
syntax keyword gomodGo go contained
16-
syntax keyword gomodRequire require
17-
syntax keyword gomodExclude exclude
18-
syntax keyword gomodReplace replace
19-
syntax keyword gomodRetract retract
14+
syntax keyword gomodModule module
15+
syntax keyword gomodGo go contained
16+
syntax keyword gomodToolchain toolchain contained
17+
syntax keyword gomodRequire require
18+
syntax keyword gomodExclude exclude
19+
syntax keyword gomodReplace replace
20+
syntax keyword gomodRetract retract
2021

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

2830
" set highlights
29-
highlight default link gomodModule Keyword
30-
highlight default link gomodGo Keyword
31-
highlight default link gomodRequire Keyword
32-
highlight default link gomodExclude Keyword
33-
highlight default link gomodReplace Keyword
34-
highlight default link gomodRetract Keyword
31+
highlight default link gomodModule Keyword
32+
highlight default link gomodGo Keyword
33+
highlight default link gomodToolchain Keyword
34+
highlight default link gomodRequire Keyword
35+
highlight default link gomodExclude Keyword
36+
highlight default link gomodReplace Keyword
37+
highlight default link gomodRetract Keyword
3538

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

51+
" match toolchain versions, based on https://go.dev/doc/toolchain#version,
52+
" * default
53+
" * go1
54+
" * go1.X.Y
55+
" * go1.X
56+
" * go1.XrcN
57+
" * go1.XrcN-somesuffix
58+
" * go1.X.Y-somesuffix
59+
syntax match gomodToolchainVersion "default$" contained
60+
syntax match gomodToolchainVersion "go1\(.\d\+\)\{,2\}\(rc\d\+\)\?\([ \t-].*\)\?" contained
61+
highlight default link gomodToolchainVersion Identifier
62+
4863
" match go versions
4964
syntax match gomodGoVersion "1\.\d\+" contained
5065
highlight default link gomodGoVersion Identifier

0 commit comments

Comments
 (0)