golang.org/x/tools/gopls@v0.15.3/internal/test/integration/template/template_test.go (about) 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/gopls/internal/protocol" 13 . "golang.org/x/tools/gopls/internal/test/integration" 14 "golang.org/x/tools/gopls/internal/util/bug" 15 ) 16 17 func TestMain(m *testing.M) { 18 bug.PanicOnBugs = true 19 Main(m, hooks.Options) 20 } 21 22 func TestMultilineTokens(t *testing.T) { 23 // 51731: panic: runtime error: slice bounds out of range [38:3] 24 const files = ` 25 -- go.mod -- 26 module mod.com 27 28 go 1.17 29 -- hi.tmpl -- 30 {{if (foÜx .X.Y)}}😀{{$A := 31 "hi" 32 }}{{.Z $A}}{{else}} 33 {{$A.X 12}} 34 {{foo (.X.Y) 23 ($A.Z)}} 35 {{end}} 36 ` 37 WithOptions( 38 Settings{ 39 "templateExtensions": []string{"tmpl"}, 40 "semanticTokens": true, 41 }, 42 ).Run(t, files, func(t *testing.T, env *Env) { 43 var p protocol.SemanticTokensParams 44 p.TextDocument.URI = env.Sandbox.Workdir.URI("hi.tmpl") 45 toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p) 46 if err != nil { 47 t.Errorf("semantic token failed: %v", err) 48 } 49 if toks == nil || len(toks.Data) == 0 { 50 t.Errorf("got no semantic tokens") 51 } 52 }) 53 } 54 55 func TestTemplatesFromExtensions(t *testing.T) { 56 const files = ` 57 -- go.mod -- 58 module mod.com 59 60 go 1.12 61 -- hello.tmpl -- 62 {{range .Planets}} 63 Hello {{}} <-- missing body 64 {{end}} 65 ` 66 WithOptions( 67 Settings{ 68 "templateExtensions": []string{"tmpl"}, 69 "semanticTokens": true, 70 }, 71 ).Run(t, files, func(t *testing.T, env *Env) { 72 // TODO: can we move this diagnostic onto {{}}? 73 var diags protocol.PublishDiagnosticsParams 74 env.OnceMet( 75 InitialWorkspaceLoad, 76 Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")), 77 ReadDiagnostics("hello.tmpl", &diags), 78 ) 79 d := diags.Diagnostics // issue 50786: check for Source 80 if len(d) != 1 { 81 t.Errorf("expected 1 diagnostic, got %d", len(d)) 82 return 83 } 84 if d[0].Source != "template" { 85 t.Errorf("expected Source 'template', got %q", d[0].Source) 86 } 87 // issue 50801 (even broken templates could return some semantic tokens) 88 var p protocol.SemanticTokensParams 89 p.TextDocument.URI = env.Sandbox.Workdir.URI("hello.tmpl") 90 toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p) 91 if err != nil { 92 t.Errorf("semantic token failed: %v", err) 93 } 94 if toks == nil || len(toks.Data) == 0 { 95 t.Errorf("got no semantic tokens") 96 } 97 98 env.WriteWorkspaceFile("hello.tmpl", "{{range .Planets}}\nHello {{.}}\n{{end}}") 99 env.AfterChange(NoDiagnostics(ForFile("hello.tmpl"))) 100 }) 101 } 102 103 func TestTemplatesObserveDirectoryFilters(t *testing.T) { 104 const files = ` 105 -- go.mod -- 106 module mod.com 107 108 go 1.12 109 -- a/a.tmpl -- 110 A {{}} <-- missing body 111 -- b/b.tmpl -- 112 B {{}} <-- missing body 113 ` 114 115 WithOptions( 116 Settings{ 117 "directoryFilters": []string{"-b"}, 118 "templateExtensions": []string{"tmpl"}, 119 }, 120 ).Run(t, files, func(t *testing.T, env *Env) { 121 env.OnceMet( 122 InitialWorkspaceLoad, 123 Diagnostics(env.AtRegexp("a/a.tmpl", "()A")), 124 NoDiagnostics(ForFile("b/b.tmpl")), 125 ) 126 }) 127 } 128 129 func TestTemplatesFromLangID(t *testing.T) { 130 const files = ` 131 -- go.mod -- 132 module mod.com 133 134 go 1.12 135 ` 136 137 Run(t, files, func(t *testing.T, env *Env) { 138 env.CreateBuffer("hello.tmpl", "") 139 env.AfterChange( 140 NoDiagnostics(ForFile("hello.tmpl")), // Don't get spurious errors for empty templates. 141 ) 142 env.SetBufferContent("hello.tmpl", "{{range .Planets}}\nHello {{}}\n{{end}}") 143 env.Await(Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}"))) 144 env.RegexpReplace("hello.tmpl", "{{}}", "{{.}}") 145 env.Await(NoDiagnostics(ForFile("hello.tmpl"))) 146 }) 147 } 148 149 func TestClosingTemplatesMakesDiagnosticsDisappear(t *testing.T) { 150 const files = ` 151 -- go.mod -- 152 module mod.com 153 154 go 1.12 155 -- hello.tmpl -- 156 {{range .Planets}} 157 Hello {{}} <-- missing body 158 {{end}} 159 ` 160 161 Run(t, files, func(t *testing.T, env *Env) { 162 env.OpenFile("hello.tmpl") 163 env.AfterChange( 164 Diagnostics(env.AtRegexp("hello.tmpl", "()Hello {{}}")), 165 ) 166 // Since we don't have templateExtensions configured, closing hello.tmpl 167 // should make its diagnostics disappear. 168 env.CloseBuffer("hello.tmpl") 169 env.AfterChange( 170 NoDiagnostics(ForFile("hello.tmpl")), 171 ) 172 }) 173 } 174 175 func TestMultipleSuffixes(t *testing.T) { 176 const files = ` 177 -- go.mod -- 178 module mod.com 179 180 go 1.12 181 -- b.gotmpl -- 182 {{define "A"}}goo{{end}} 183 -- a.tmpl -- 184 {{template "A"}} 185 ` 186 187 WithOptions( 188 Settings{ 189 "templateExtensions": []string{"tmpl", "gotmpl"}, 190 }, 191 ).Run(t, files, func(t *testing.T, env *Env) { 192 env.OpenFile("a.tmpl") 193 x := env.RegexpSearch("a.tmpl", `A`) 194 loc := env.GoToDefinition(x) 195 refs := env.References(loc) 196 if len(refs) != 2 { 197 t.Fatalf("got %v reference(s), want 2", len(refs)) 198 } 199 // make sure we got one from b.gotmpl 200 want := env.Sandbox.Workdir.URI("b.gotmpl") 201 if refs[0].URI != want && refs[1].URI != want { 202 t.Errorf("failed to find reference to %s", shorten(want)) 203 for i, r := range refs { 204 t.Logf("%d: URI:%s %v", i, shorten(r.URI), r.Range) 205 } 206 } 207 208 content, nloc := env.Hover(loc) 209 if loc != nloc { 210 t.Errorf("loc? got %v, wanted %v", nloc, loc) 211 } 212 if content.Value != "template A defined" { 213 t.Errorf("got %s, wanted 'template A defined", content.Value) 214 } 215 }) 216 } 217 218 // shorten long URIs 219 func shorten(fn protocol.DocumentURI) string { 220 if len(fn) <= 20 { 221 return string(fn) 222 } 223 pieces := strings.Split(string(fn), "/") 224 if len(pieces) < 2 { 225 return string(fn) 226 } 227 j := len(pieces) 228 return pieces[j-2] + "/" + pieces[j-1] 229 } 230 231 // Hover needs tests