diff --git a/gopls/internal/regtest/template/template_test.go b/gopls/internal/regtest/template/template_test.go index 1846d4ba41..b0acdfeb85 100644 --- a/gopls/internal/regtest/template/template_test.go +++ b/gopls/internal/regtest/template/template_test.go @@ -17,6 +17,41 @@ func TestMain(m *testing.M) { Main(m, hooks.Options) } +func TestMultilineTokens(t *testing.T) { + // 51731: panic: runtime error: slice bounds out of range [38:3] + const files = ` +-- go.mod -- +module mod.com + +go 1.17 +-- hi.tmpl -- +{{if (foÜx .X.Y)}}😀{{$A := + "hi" + }}{{.Z $A}}{{else}} +{{$A.X 12}} +{{foo (.X.Y) 23 ($A.Z)}} +{{end}} +` + WithOptions( + EditorConfig{ + Settings: map[string]interface{}{ + "templateExtensions": []string{"tmpl"}, + "semanticTokens": true, + }, + }, + ).Run(t, files, func(t *testing.T, env *Env) { + var p protocol.SemanticTokensParams + p.TextDocument.URI = env.Sandbox.Workdir.URI("hi.tmpl") + toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p) + if err != nil { + t.Errorf("semantic token failed: %v", err) + } + if toks == nil || len(toks.Data) == 0 { + t.Errorf("got no semantic tokens") + } + }) +} + func TestTemplatesFromExtensions(t *testing.T) { const files = ` -- go.mod -- @@ -28,7 +63,6 @@ go 1.12 Hello {{}} <-- missing body {{end}} ` - WithOptions( EditorConfig{ Settings: map[string]interface{}{ @@ -193,5 +227,4 @@ func shorten(fn protocol.DocumentURI) string { return pieces[j-2] + "/" + pieces[j-1] } -// Hover, SemTok, Diagnose with errors -// and better coverage +// Hover needs tests diff --git a/internal/lsp/template/parse.go b/internal/lsp/template/parse.go index bf4e1b4bc4..194eeb3f5e 100644 --- a/internal/lsp/template/parse.go +++ b/internal/lsp/template/parse.go @@ -291,11 +291,12 @@ func (p *Parsed) TokenSize(t Token) (int, error) { return ans, nil } -// RuneCount counts runes in a line +// RuneCount counts runes in line l, from col s to e +// (e==0 for end of line. called only for multiline tokens) func (p *Parsed) RuneCount(l, s, e uint32) uint32 { start := p.nls[l] + 1 + int(s) - end := int(e) - if e == 0 || int(e) >= p.nls[l+1] { + end := p.nls[l] + 1 + int(e) + if e == 0 || end > p.nls[l+1] { end = p.nls[l+1] } return uint32(utf8.RuneCount(p.buf[start:end]))