Skip to content

Commit a222cdb

Browse files
committed
gopls/internal/regtest: add regression tests for template diagnostics
Add some additional regressions tests for our loading of templates based on language ID and/or the configured templateExtensions. Move these tests to a new regtest package "templates", so that they may be easily run together. Fixes golang/vscode-go#1957 Change-Id: Ic83454725e9aec41b3c1f5202bb68d97cc73c839 Reviewed-on: https://go-review.googlesource.com/c/tools/+/378394 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Suzy Mueller <[email protected]>
1 parent c4cfc42 commit a222cdb

File tree

3 files changed

+189
-91
lines changed

3 files changed

+189
-91
lines changed

gopls/internal/regtest/misc/template_test.go

Lines changed: 0 additions & 75 deletions
This file was deleted.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package template
6+
7+
import (
8+
"strings"
9+
"testing"
10+
11+
"golang.org/x/tools/gopls/internal/hooks"
12+
"golang.org/x/tools/internal/lsp/protocol"
13+
. "golang.org/x/tools/internal/lsp/regtest"
14+
)
15+
16+
func TestMain(m *testing.M) {
17+
Main(m, hooks.Options)
18+
}
19+
20+
func TestTemplatesFromExtensions(t *testing.T) {
21+
const files = `
22+
-- go.mod --
23+
module mod.com
24+
25+
go 1.12
26+
-- hello.tmpl --
27+
{{range .Planets}}
28+
Hello {{}} <-- missing body
29+
{{end}}
30+
`
31+
32+
WithOptions(
33+
EditorConfig{
34+
Settings: map[string]interface{}{
35+
"templateExtensions": []string{"tmpl"},
36+
},
37+
},
38+
).Run(t, files, func(t *testing.T, env *Env) {
39+
// TODO: can we move this diagnostic onto {{}}?
40+
env.Await(env.DiagnosticAtRegexp("hello.tmpl", "()Hello {{}}"))
41+
env.WriteWorkspaceFile("hello.tmpl", "{{range .Planets}}\nHello {{.}}\n{{end}}")
42+
env.Await(EmptyDiagnostics("hello.tmpl"))
43+
})
44+
}
45+
46+
func TestTemplatesFromLangID(t *testing.T) {
47+
const files = `
48+
-- go.mod --
49+
module mod.com
50+
51+
go 1.12
52+
`
53+
54+
Run(t, files, func(t *testing.T, env *Env) {
55+
env.CreateBuffer("hello.tmpl", "")
56+
env.Await(
57+
OnceMet(
58+
env.DoneWithOpen(),
59+
NoDiagnostics("hello.tmpl"), // Don't get spurious errors for empty templates.
60+
),
61+
)
62+
env.SetBufferContent("hello.tmpl", "{{range .Planets}}\nHello {{}}\n{{end}}")
63+
env.Await(env.DiagnosticAtRegexp("hello.tmpl", "()Hello {{}}"))
64+
env.RegexpReplace("hello.tmpl", "{{}}", "{{.}}")
65+
env.Await(EmptyOrNoDiagnostics("hello.tmpl"))
66+
})
67+
}
68+
69+
func TestClosingTemplatesMakesDiagnosticsDisappear(t *testing.T) {
70+
const files = `
71+
-- go.mod --
72+
module mod.com
73+
74+
go 1.12
75+
-- hello.tmpl --
76+
{{range .Planets}}
77+
Hello {{}} <-- missing body
78+
{{end}}
79+
`
80+
81+
Run(t, files, func(t *testing.T, env *Env) {
82+
env.OpenFile("hello.tmpl")
83+
env.Await(env.DiagnosticAtRegexp("hello.tmpl", "()Hello {{}}"))
84+
// Since we don't have templateExtensions configured, closing hello.tmpl
85+
// should make its diagnostics disappear.
86+
env.CloseBuffer("hello.tmpl")
87+
env.Await(EmptyDiagnostics("hello.tmpl"))
88+
})
89+
}
90+
91+
func TestMultipleSuffixes(t *testing.T) {
92+
const files = `
93+
-- go.mod --
94+
module mod.com
95+
96+
go 1.12
97+
-- b.gotmpl --
98+
{{define "A"}}goo{{end}}
99+
-- a.tmpl --
100+
{{template "A"}}
101+
`
102+
103+
WithOptions(
104+
EditorConfig{
105+
Settings: map[string]interface{}{
106+
"templateExtensions": []string{"tmpl", "gotmpl"},
107+
},
108+
},
109+
).Run(t, files, func(t *testing.T, env *Env) {
110+
env.OpenFile("a.tmpl")
111+
x := env.RegexpSearch("a.tmpl", `A`)
112+
file, pos := env.GoToDefinition("a.tmpl", x)
113+
refs := env.References(file, pos)
114+
if len(refs) != 2 {
115+
t.Fatalf("got %v reference(s), want 2", len(refs))
116+
}
117+
// make sure we got one from b.gotmpl
118+
want := env.Sandbox.Workdir.URI("b.gotmpl")
119+
if refs[0].URI != want && refs[1].URI != want {
120+
t.Errorf("failed to find reference to %s", shorten(want))
121+
for i, r := range refs {
122+
t.Logf("%d: URI:%s %v", i, shorten(r.URI), r.Range)
123+
}
124+
}
125+
126+
content, npos := env.Hover(file, pos)
127+
if pos != npos {
128+
t.Errorf("pos? got %v, wanted %v", npos, pos)
129+
}
130+
if content.Value != "template A defined" {
131+
t.Errorf("got %s, wanted 'template A defined", content.Value)
132+
}
133+
})
134+
}
135+
136+
// shorten long URIs
137+
func shorten(fn protocol.DocumentURI) string {
138+
if len(fn) <= 20 {
139+
return string(fn)
140+
}
141+
pieces := strings.Split(string(fn), "/")
142+
if len(pieces) < 2 {
143+
return string(fn)
144+
}
145+
j := len(pieces)
146+
return pieces[j-2] + "/" + pieces[j-1]
147+
}
148+
149+
// Hover, SemTok, Diagnose with errors
150+
// and better coverage

internal/lsp/fake/editor.go

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"context"
1010
"fmt"
1111
"os"
12+
"path"
1213
"path/filepath"
1314
"regexp"
1415
"strings"
@@ -114,6 +115,14 @@ type EditorConfig struct {
114115
// Whether to edit files with windows line endings.
115116
WindowsLineEndings bool
116117

118+
// Map of language ID -> regexp to match, used to set the file type of new
119+
// buffers. Applied as an overlay on top of the following defaults:
120+
// "go" -> ".*\.go"
121+
// "go.mod" -> "go\.mod"
122+
// "go.sum" -> "go\.sum"
123+
// "gotmpl" -> ".*tmpl"
124+
FileAssociations map[string]string
125+
117126
// Settings holds arbitrary additional settings to apply to the gopls config.
118127
// TODO(rfindley): replace existing EditorConfig fields with Settings.
119128
Settings map[string]interface{}
@@ -378,21 +387,6 @@ func (e *Editor) OpenFile(ctx context.Context, path string) error {
378387
return e.createBuffer(ctx, path, false, content)
379388
}
380389

381-
func textDocumentItem(wd *Workdir, buf buffer) protocol.TextDocumentItem {
382-
uri := wd.URI(buf.path)
383-
languageID := ""
384-
if strings.HasSuffix(buf.path, ".go") {
385-
// TODO: what about go.mod files? What is their language ID?
386-
languageID = "go"
387-
}
388-
return protocol.TextDocumentItem{
389-
URI: uri,
390-
LanguageID: languageID,
391-
Version: int32(buf.version),
392-
Text: buf.text(),
393-
}
394-
}
395-
396390
// CreateBuffer creates a new unsaved buffer corresponding to the workdir path,
397391
// containing the given textual content.
398392
func (e *Editor) CreateBuffer(ctx context.Context, path, content string) error {
@@ -410,7 +404,13 @@ func (e *Editor) createBuffer(ctx context.Context, path string, dirty bool, cont
410404
e.mu.Lock()
411405
defer e.mu.Unlock()
412406
e.buffers[path] = buf
413-
item := textDocumentItem(e.sandbox.Workdir, buf)
407+
408+
item := protocol.TextDocumentItem{
409+
URI: e.sandbox.Workdir.URI(buf.path),
410+
LanguageID: e.languageID(buf.path),
411+
Version: int32(buf.version),
412+
Text: buf.text(),
413+
}
414414

415415
if e.Server != nil {
416416
if err := e.Server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
@@ -425,6 +425,29 @@ func (e *Editor) createBuffer(ctx context.Context, path string, dirty bool, cont
425425
return nil
426426
}
427427

428+
var defaultFileAssociations = map[string]*regexp.Regexp{
429+
"go": regexp.MustCompile(`^.*\.go$`), // '$' is important: don't match .gotmpl!
430+
"go.mod": regexp.MustCompile(`^go\.mod$`),
431+
"go.sum": regexp.MustCompile(`^go\.sum$`),
432+
"gotmpl": regexp.MustCompile(`^.*tmpl$`),
433+
}
434+
435+
func (e *Editor) languageID(p string) string {
436+
base := path.Base(p)
437+
for lang, re := range e.Config.FileAssociations {
438+
re := regexp.MustCompile(re)
439+
if re.MatchString(base) {
440+
return lang
441+
}
442+
}
443+
for lang, re := range defaultFileAssociations {
444+
if re.MatchString(base) {
445+
return lang
446+
}
447+
}
448+
return ""
449+
}
450+
428451
// lines returns line-ending agnostic line representation of content.
429452
func lines(content string) []string {
430453
lines := strings.Split(content, "\n")

0 commit comments

Comments
 (0)