aboutsummaryrefslogtreecommitdiff
path: root/gopls/internal/lsp/template/parse_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'gopls/internal/lsp/template/parse_test.go')
-rw-r--r--gopls/internal/lsp/template/parse_test.go238
1 files changed, 238 insertions, 0 deletions
diff --git a/gopls/internal/lsp/template/parse_test.go b/gopls/internal/lsp/template/parse_test.go
new file mode 100644
index 000000000..345f52347
--- /dev/null
+++ b/gopls/internal/lsp/template/parse_test.go
@@ -0,0 +1,238 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "strings"
+ "testing"
+)
+
+type datum struct {
+ buf string
+ cnt int
+ syms []string // the symbols in the parse of buf
+}
+
+var tmpl = []datum{{`
+{{if (foo .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}}
+{{$A.X 12}}
+{{foo (.X.Y) 23 ($A.Zü)}}
+{{end}}`, 1, []string{"{7,3,foo,Function,false}", "{12,1,X,Method,false}",
+ "{14,1,Y,Method,false}", "{21,2,$A,Variable,true}", "{26,2,,String,false}",
+ "{35,1,Z,Method,false}", "{38,2,$A,Variable,false}",
+ "{53,2,$A,Variable,false}", "{56,1,X,Method,false}", "{57,2,,Number,false}",
+ "{64,3,foo,Function,false}", "{70,1,X,Method,false}",
+ "{72,1,Y,Method,false}", "{75,2,,Number,false}", "{80,2,$A,Variable,false}",
+ "{83,2,Zü,Method,false}", "{94,3,,Constant,false}"}},
+
+ {`{{define "zzz"}}{{.}}{{end}}
+{{template "zzz"}}`, 2, []string{"{10,3,zzz,Namespace,true}", "{18,1,dot,Variable,false}",
+ "{41,3,zzz,Package,false}"}},
+
+ {`{{block "aaa" foo}}b{{end}}`, 2, []string{"{9,3,aaa,Namespace,true}",
+ "{9,3,aaa,Package,false}", "{14,3,foo,Function,false}", "{19,1,,Constant,false}"}},
+ {"", 0, nil},
+}
+
+func TestSymbols(t *testing.T) {
+ for i, x := range tmpl {
+ got := parseBuffer([]byte(x.buf))
+ if got.ParseErr != nil {
+ t.Errorf("error:%v", got.ParseErr)
+ continue
+ }
+ if len(got.named) != x.cnt {
+ t.Errorf("%d: got %d, expected %d", i, len(got.named), x.cnt)
+ }
+ for n, s := range got.symbols {
+ if s.String() != x.syms[n] {
+ t.Errorf("%d: got %s, expected %s", i, s.String(), x.syms[n])
+ }
+ }
+ }
+}
+
+func TestWordAt(t *testing.T) {
+ want := []string{"", "", "$A", "$A", "", "", "", "", "", "",
+ "", "", "", "if", "if", "", "$A", "$A", "", "",
+ "B", "", "", "end", "end", "end", "", "", ""}
+ p := parseBuffer([]byte("{{$A := .}}{{if $A}}B{{end}}"))
+ for i := 0; i < len(p.buf); i++ {
+ got := findWordAt(p, i)
+ if got != want[i] {
+ t.Errorf("for %d, got %q, wanted %q", i, got, want[i])
+ }
+ }
+}
+
+func TestNLS(t *testing.T) {
+ buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}}
+ {{$A.X 12}}
+ {{foo (.X.Y) 23 ($A.Z)}}
+ {{end}}
+ `
+ p := parseBuffer([]byte(buf))
+ if p.ParseErr != nil {
+ t.Fatal(p.ParseErr)
+ }
+ // line 0 doesn't have a \n in front of it
+ for i := 1; i < len(p.nls)-1; i++ {
+ if buf[p.nls[i]] != '\n' {
+ t.Errorf("line %d got %c", i, buf[p.nls[i]])
+ }
+ }
+ // fake line at end of file
+ if p.nls[len(p.nls)-1] != len(buf) {
+ t.Errorf("got %d expected %d", p.nls[len(p.nls)-1], len(buf))
+ }
+}
+
+func TestLineCol(t *testing.T) {
+ buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}}
+ {{$A.X 12}}
+ {{foo (.X.Y) 23 ($A.Z)}}
+ {{end}}`
+ if false {
+ t.Error(buf)
+ }
+ for n, cx := range tmpl {
+ buf := cx.buf
+ p := parseBuffer([]byte(buf))
+ if p.ParseErr != nil {
+ t.Fatal(p.ParseErr)
+ }
+ type loc struct {
+ offset int
+ l, c uint32
+ }
+ saved := []loc{}
+ // forwards
+ var lastl, lastc uint32
+ for offset := range buf {
+ l, c := p.LineCol(offset)
+ saved = append(saved, loc{offset, l, c})
+ if l > lastl {
+ lastl = l
+ if c != 0 {
+ t.Errorf("line %d, got %d instead of 0", l, c)
+ }
+ }
+ if c > lastc {
+ lastc = c
+ }
+ }
+ lines := strings.Split(buf, "\n")
+ mxlen := -1
+ for _, l := range lines {
+ if len(l) > mxlen {
+ mxlen = len(l)
+ }
+ }
+ if int(lastl) != len(lines)-1 && int(lastc) != mxlen {
+ // lastl is 0 if there is only 1 line(?)
+ t.Errorf("expected %d, %d, got %d, %d for case %d", len(lines)-1, mxlen, lastl, lastc, n)
+ }
+ // backwards
+ for j := len(saved) - 1; j >= 0; j-- {
+ s := saved[j]
+ xl, xc := p.LineCol(s.offset)
+ if xl != s.l || xc != s.c {
+ t.Errorf("at offset %d(%d), got (%d,%d), expected (%d,%d)", s.offset, j, xl, xc, s.l, s.c)
+ }
+ }
+ }
+}
+
+func TestLineColNL(t *testing.T) {
+ buf := "\n\n\n\n\n"
+ p := parseBuffer([]byte(buf))
+ if p.ParseErr != nil {
+ t.Fatal(p.ParseErr)
+ }
+ for i := 0; i < len(buf); i++ {
+ l, c := p.LineCol(i)
+ if c != 0 || int(l) != i+1 {
+ t.Errorf("got (%d,%d), expected (%d,0)", l, c, i)
+ }
+ }
+}
+
+func TestPos(t *testing.T) {
+ buf := `
+ {{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}}
+ {{$A.X 12}}
+ {{foo (.X.Y) 23 ($A.Z)}}
+ {{end}}`
+ p := parseBuffer([]byte(buf))
+ if p.ParseErr != nil {
+ t.Fatal(p.ParseErr)
+ }
+ for pos, r := range buf {
+ if r == '\n' {
+ continue
+ }
+ x := p.Position(pos)
+ n := p.FromPosition(x)
+ if n != pos {
+ // once it's wrong, it will be wrong forever
+ t.Fatalf("at pos %d (rune %c) got %d {%#v]", pos, r, n, x)
+ }
+
+ }
+}
+func TestLen(t *testing.T) {
+ data := []struct {
+ cnt int
+ v string
+ }{{1, "a"}, {1, "膈"}, {4, "😆🥸"}, {7, "3😀4567"}}
+ p := &Parsed{nonASCII: true}
+ for _, d := range data {
+ got := p.utf16len([]byte(d.v))
+ if got != d.cnt {
+ t.Errorf("%v, got %d wanted %d", d, got, d.cnt)
+ }
+ }
+}
+
+func TestUtf16(t *testing.T) {
+ buf := `
+ {{if (foÜx .X.Y)}}😀{{$A := "hi"}}{{.Z $A}}{{else}}
+ {{$A.X 12}}
+ {{foo (.X.Y) 23 ($A.Z)}}
+ {{end}}`
+ p := parseBuffer([]byte(buf))
+ if p.nonASCII == false {
+ t.Error("expected nonASCII to be true")
+ }
+}
+
+type ttest struct {
+ tmpl string
+ tokCnt int
+ elidedCnt int8
+}
+
+func TestQuotes(t *testing.T) {
+ tsts := []ttest{
+ {"{{- /*comment*/ -}}", 1, 0},
+ {"{{/*`\ncomment\n`*/}}", 1, 0},
+ //{"{{foo\nbar}}\n", 1, 0}, // this action spanning lines parses in 1.16
+ {"{{\"{{foo}}{{\"}}", 1, 0},
+ {"{{\n{{- when}}", 1, 1}, // corrected
+ {"{{{{if .}}xx{{\n{{end}}", 2, 2}, // corrected
+ }
+ for _, s := range tsts {
+ p := parseBuffer([]byte(s.tmpl))
+ if len(p.tokens) != s.tokCnt {
+ t.Errorf("%q: got %d tokens, expected %d", s, len(p.tokens), s.tokCnt)
+ }
+ if p.ParseErr != nil {
+ t.Errorf("%q: %v", string(p.buf), p.ParseErr)
+ }
+ if len(p.elided) != int(s.elidedCnt) {
+ t.Errorf("%q: elided %d, expected %d", s, len(p.elided), s.elidedCnt)
+ }
+ }
+}