golang.org/x/tools/gopls@v0.15.3/internal/template/parse.go (about)

     1  // Copyright 2021 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 contains code for dealing with templates
     6  package template
     7  
     8  // template files are small enough that the code reprocesses them each time
     9  // this may be a bad choice for projects with lots of template files.
    10  
    11  import (
    12  	"bytes"
    13  	"context"
    14  	"fmt"
    15  	"io"
    16  	"log"
    17  	"regexp"
    18  	"runtime"
    19  	"sort"
    20  	"text/template"
    21  	"text/template/parse"
    22  	"unicode/utf8"
    23  
    24  	"golang.org/x/tools/gopls/internal/file"
    25  	"golang.org/x/tools/gopls/internal/protocol"
    26  	"golang.org/x/tools/internal/event"
    27  )
    28  
    29  var (
    30  	Left  = []byte("{{")
    31  	Right = []byte("}}")
    32  )
    33  
    34  type Parsed struct {
    35  	buf    []byte   //contents
    36  	lines  [][]byte // needed?, other than for debugging?
    37  	elided []int    // offsets where Left was replaced by blanks
    38  
    39  	// tokens are matched Left-Right pairs, computed before trying to parse
    40  	tokens []Token
    41  
    42  	// result of parsing
    43  	named    []*template.Template // the template and embedded templates
    44  	ParseErr error
    45  	symbols  []symbol
    46  	stack    []parse.Node // used while computing symbols
    47  
    48  	// for mapping from offsets in buf to LSP coordinates
    49  	// See FromPosition() and LineCol()
    50  	nls      []int // offset of newlines before each line (nls[0]==-1)
    51  	lastnl   int   // last line seen
    52  	check    int   // used to decide whether to use lastnl or search through nls
    53  	nonASCII bool  // are there any non-ascii runes in buf?
    54  }
    55  
    56  // Token is a single {{...}}. More precisely, Left...Right
    57  type Token struct {
    58  	Start, End int // offset from start of template
    59  	Multiline  bool
    60  }
    61  
    62  // All contains the Parse of all the template files
    63  type All struct {
    64  	files map[protocol.DocumentURI]*Parsed
    65  }
    66  
    67  // New returns the Parses of the snapshot's tmpl files
    68  // (maybe cache these, but then avoiding import cycles needs code rearrangements)
    69  func New(tmpls map[protocol.DocumentURI]file.Handle) *All {
    70  	all := make(map[protocol.DocumentURI]*Parsed)
    71  	for k, v := range tmpls {
    72  		buf, err := v.Content()
    73  		if err != nil { // PJW: decide what to do with these errors
    74  			log.Printf("failed to read %s (%v)", v.URI().Path(), err)
    75  			continue
    76  		}
    77  		all[k] = parseBuffer(buf)
    78  	}
    79  	return &All{files: all}
    80  }
    81  
    82  func parseBuffer(buf []byte) *Parsed {
    83  	ans := &Parsed{
    84  		buf:   buf,
    85  		check: -1,
    86  		nls:   []int{-1},
    87  	}
    88  	if len(buf) == 0 {
    89  		return ans
    90  	}
    91  	// how to compute allAscii...
    92  	for _, b := range buf {
    93  		if b >= utf8.RuneSelf {
    94  			ans.nonASCII = true
    95  			break
    96  		}
    97  	}
    98  	if buf[len(buf)-1] != '\n' {
    99  		ans.buf = append(buf, '\n')
   100  	}
   101  	for i, p := range ans.buf {
   102  		if p == '\n' {
   103  			ans.nls = append(ans.nls, i)
   104  		}
   105  	}
   106  	ans.setTokens() // ans.buf may be a new []byte
   107  	ans.lines = bytes.Split(ans.buf, []byte{'\n'})
   108  	t, err := template.New("").Parse(string(ans.buf))
   109  	if err != nil {
   110  		funcs := make(template.FuncMap)
   111  		for t == nil && ans.ParseErr == nil {
   112  			// in 1.17 it may be possible to avoid getting this error
   113  			//  template: :2: function "foo" not defined
   114  			matches := parseErrR.FindStringSubmatch(err.Error())
   115  			if len(matches) == 2 {
   116  				// suppress the error by giving it a function with the right name
   117  				funcs[matches[1]] = func() interface{} { return nil }
   118  				t, err = template.New("").Funcs(funcs).Parse(string(ans.buf))
   119  				continue
   120  			}
   121  			ans.ParseErr = err // unfixed error
   122  			return ans
   123  		}
   124  	}
   125  	ans.named = t.Templates()
   126  	// set the symbols
   127  	for _, t := range ans.named {
   128  		ans.stack = append(ans.stack, t.Root)
   129  		ans.findSymbols()
   130  		if t.Name() != "" {
   131  			// defining a template. The pos is just after {{define...}} (or {{block...}}?)
   132  			at, sz := ans.FindLiteralBefore(int(t.Root.Pos))
   133  			s := symbol{start: at, length: sz, name: t.Name(), kind: protocol.Namespace, vardef: true}
   134  			ans.symbols = append(ans.symbols, s)
   135  		}
   136  	}
   137  
   138  	sort.Slice(ans.symbols, func(i, j int) bool {
   139  		left, right := ans.symbols[i], ans.symbols[j]
   140  		if left.start != right.start {
   141  			return left.start < right.start
   142  		}
   143  		if left.vardef != right.vardef {
   144  			return left.vardef
   145  		}
   146  		return left.kind < right.kind
   147  	})
   148  	return ans
   149  }
   150  
   151  // FindLiteralBefore locates the first preceding string literal
   152  // returning its position and length in buf
   153  // or returns -1 if there is none.
   154  // Assume double-quoted string rather than backquoted string for now.
   155  func (p *Parsed) FindLiteralBefore(pos int) (int, int) {
   156  	left, right := -1, -1
   157  	for i := pos - 1; i >= 0; i-- {
   158  		if p.buf[i] != '"' {
   159  			continue
   160  		}
   161  		if right == -1 {
   162  			right = i
   163  			continue
   164  		}
   165  		left = i
   166  		break
   167  	}
   168  	if left == -1 {
   169  		return -1, 0
   170  	}
   171  	return left + 1, right - left - 1
   172  }
   173  
   174  var (
   175  	parseErrR = regexp.MustCompile(`template:.*function "([^"]+)" not defined`)
   176  )
   177  
   178  func (p *Parsed) setTokens() {
   179  	const (
   180  		// InRaw and InString only occur inside an action (SeenLeft)
   181  		Start = iota
   182  		InRaw
   183  		InString
   184  		SeenLeft
   185  	)
   186  	state := Start
   187  	var left, oldState int
   188  	for n := 0; n < len(p.buf); n++ {
   189  		c := p.buf[n]
   190  		switch state {
   191  		case InRaw:
   192  			if c == '`' {
   193  				state = oldState
   194  			}
   195  		case InString:
   196  			if c == '"' && !isEscaped(p.buf[:n]) {
   197  				state = oldState
   198  			}
   199  		case SeenLeft:
   200  			if c == '`' {
   201  				oldState = state // it's SeenLeft, but a little clearer this way
   202  				state = InRaw
   203  				continue
   204  			}
   205  			if c == '"' {
   206  				oldState = state
   207  				state = InString
   208  				continue
   209  			}
   210  			if bytes.HasPrefix(p.buf[n:], Right) {
   211  				right := n + len(Right)
   212  				tok := Token{Start: left,
   213  					End:       right,
   214  					Multiline: bytes.Contains(p.buf[left:right], []byte{'\n'}),
   215  				}
   216  				p.tokens = append(p.tokens, tok)
   217  				state = Start
   218  			}
   219  			// If we see (unquoted) Left then the original left is probably the user
   220  			// typing. Suppress the original left
   221  			if bytes.HasPrefix(p.buf[n:], Left) {
   222  				p.elideAt(left)
   223  				left = n
   224  				n += len(Left) - 1 // skip the rest
   225  			}
   226  		case Start:
   227  			if bytes.HasPrefix(p.buf[n:], Left) {
   228  				left = n
   229  				state = SeenLeft
   230  				n += len(Left) - 1 // skip the rest (avoids {{{ bug)
   231  			}
   232  		}
   233  	}
   234  	// this error occurs after typing {{ at the end of the file
   235  	if state != Start {
   236  		// Unclosed Left. remove the Left at left
   237  		p.elideAt(left)
   238  	}
   239  }
   240  
   241  func (p *Parsed) elideAt(left int) {
   242  	if p.elided == nil {
   243  		// p.buf is the same buffer that v.Read() returns, so copy it.
   244  		// (otherwise the next time it's parsed, elided information is lost)
   245  		b := make([]byte, len(p.buf))
   246  		copy(b, p.buf)
   247  		p.buf = b
   248  	}
   249  	for i := 0; i < len(Left); i++ {
   250  		p.buf[left+i] = ' '
   251  	}
   252  	p.elided = append(p.elided, left)
   253  }
   254  
   255  // isEscaped reports whether the byte after buf is escaped
   256  func isEscaped(buf []byte) bool {
   257  	backSlashes := 0
   258  	for j := len(buf) - 1; j >= 0 && buf[j] == '\\'; j-- {
   259  		backSlashes++
   260  	}
   261  	return backSlashes%2 == 1
   262  }
   263  
   264  func (p *Parsed) Tokens() []Token {
   265  	return p.tokens
   266  }
   267  
   268  // TODO(adonovan): the next 100 lines could perhaps replaced by use of protocol.Mapper.
   269  
   270  func (p *Parsed) utf16len(buf []byte) int {
   271  	cnt := 0
   272  	if !p.nonASCII {
   273  		return len(buf)
   274  	}
   275  	// we need a utf16len(rune), but we don't have it
   276  	for _, r := range string(buf) {
   277  		cnt++
   278  		if r >= 1<<16 {
   279  			cnt++
   280  		}
   281  	}
   282  	return cnt
   283  }
   284  
   285  func (p *Parsed) TokenSize(t Token) (int, error) {
   286  	if t.Multiline {
   287  		return -1, fmt.Errorf("TokenSize called with Multiline token %#v", t)
   288  	}
   289  	ans := p.utf16len(p.buf[t.Start:t.End])
   290  	return ans, nil
   291  }
   292  
   293  // RuneCount counts runes in line l, from col s to e
   294  // (e==0 for end of line. called only for multiline tokens)
   295  func (p *Parsed) RuneCount(l, s, e uint32) uint32 {
   296  	start := p.nls[l] + 1 + int(s)
   297  	end := p.nls[l] + 1 + int(e)
   298  	if e == 0 || end > p.nls[l+1] {
   299  		end = p.nls[l+1]
   300  	}
   301  	return uint32(utf8.RuneCount(p.buf[start:end]))
   302  }
   303  
   304  // LineCol converts from a 0-based byte offset to 0-based line, col. col in runes
   305  func (p *Parsed) LineCol(x int) (uint32, uint32) {
   306  	if x < p.check {
   307  		p.lastnl = 0
   308  	}
   309  	p.check = x
   310  	for i := p.lastnl; i < len(p.nls); i++ {
   311  		if p.nls[i] <= x {
   312  			continue
   313  		}
   314  		p.lastnl = i
   315  		var count int
   316  		if i > 0 && x == p.nls[i-1] { // \n
   317  			count = 0
   318  		} else {
   319  			count = p.utf16len(p.buf[p.nls[i-1]+1 : x])
   320  		}
   321  		return uint32(i - 1), uint32(count)
   322  	}
   323  	if x == len(p.buf)-1 { // trailing \n
   324  		return uint32(len(p.nls) - 1), 0
   325  	}
   326  	// shouldn't happen
   327  	for i := 1; i < 4; i++ {
   328  		_, f, l, ok := runtime.Caller(i)
   329  		if !ok {
   330  			break
   331  		}
   332  		log.Printf("%d: %s:%d", i, f, l)
   333  	}
   334  
   335  	msg := fmt.Errorf("LineCol off the end, %d of %d, nls=%v, %q", x, len(p.buf), p.nls, p.buf[x:])
   336  	event.Error(context.Background(), "internal error", msg)
   337  	return 0, 0
   338  }
   339  
   340  // Position produces a protocol.Position from an offset in the template
   341  func (p *Parsed) Position(pos int) protocol.Position {
   342  	line, col := p.LineCol(pos)
   343  	return protocol.Position{Line: line, Character: col}
   344  }
   345  
   346  func (p *Parsed) Range(x, length int) protocol.Range {
   347  	line, col := p.LineCol(x)
   348  	ans := protocol.Range{
   349  		Start: protocol.Position{Line: line, Character: col},
   350  		End:   protocol.Position{Line: line, Character: col + uint32(length)},
   351  	}
   352  	return ans
   353  }
   354  
   355  // FromPosition translates a protocol.Position into an offset into the template
   356  func (p *Parsed) FromPosition(x protocol.Position) int {
   357  	l, c := int(x.Line), int(x.Character)
   358  	if l >= len(p.nls) || p.nls[l]+1 >= len(p.buf) {
   359  		// paranoia to avoid panic. return the largest offset
   360  		return len(p.buf)
   361  	}
   362  	line := p.buf[p.nls[l]+1:]
   363  	cnt := 0
   364  	for w := range string(line) {
   365  		if cnt >= c {
   366  			return w + p.nls[l] + 1
   367  		}
   368  		cnt++
   369  	}
   370  	// do we get here? NO
   371  	pos := int(x.Character) + p.nls[int(x.Line)] + 1
   372  	event.Error(context.Background(), "internal error", fmt.Errorf("surprise %#v", x))
   373  	return pos
   374  }
   375  
   376  func symAtPosition(fh file.Handle, loc protocol.Position) (*symbol, *Parsed, error) {
   377  	buf, err := fh.Content()
   378  	if err != nil {
   379  		return nil, nil, err
   380  	}
   381  	p := parseBuffer(buf)
   382  	pos := p.FromPosition(loc)
   383  	syms := p.SymsAtPos(pos)
   384  	if len(syms) == 0 {
   385  		return nil, p, fmt.Errorf("no symbol found")
   386  	}
   387  	if len(syms) > 1 {
   388  		log.Printf("Hover: %d syms, not 1 %v", len(syms), syms)
   389  	}
   390  	sym := syms[0]
   391  	return &sym, p, nil
   392  }
   393  
   394  func (p *Parsed) SymsAtPos(pos int) []symbol {
   395  	ans := []symbol{}
   396  	for _, s := range p.symbols {
   397  		if s.start <= pos && pos < s.start+s.length {
   398  			ans = append(ans, s)
   399  		}
   400  	}
   401  	return ans
   402  }
   403  
   404  type wrNode struct {
   405  	p *Parsed
   406  	w io.Writer
   407  }
   408  
   409  // WriteNode is for debugging
   410  func (p *Parsed) WriteNode(w io.Writer, n parse.Node) {
   411  	wr := wrNode{p: p, w: w}
   412  	wr.writeNode(n, "")
   413  }
   414  
   415  func (wr wrNode) writeNode(n parse.Node, indent string) {
   416  	if n == nil {
   417  		return
   418  	}
   419  	at := func(pos parse.Pos) string {
   420  		line, col := wr.p.LineCol(int(pos))
   421  		return fmt.Sprintf("(%d)%v:%v", pos, line, col)
   422  	}
   423  	switch x := n.(type) {
   424  	case *parse.ActionNode:
   425  		fmt.Fprintf(wr.w, "%sActionNode at %s\n", indent, at(x.Pos))
   426  		wr.writeNode(x.Pipe, indent+". ")
   427  	case *parse.BoolNode:
   428  		fmt.Fprintf(wr.w, "%sBoolNode at %s, %v\n", indent, at(x.Pos), x.True)
   429  	case *parse.BranchNode:
   430  		fmt.Fprintf(wr.w, "%sBranchNode at %s\n", indent, at(x.Pos))
   431  		wr.writeNode(x.Pipe, indent+"Pipe. ")
   432  		wr.writeNode(x.List, indent+"List. ")
   433  		wr.writeNode(x.ElseList, indent+"Else. ")
   434  	case *parse.ChainNode:
   435  		fmt.Fprintf(wr.w, "%sChainNode at %s, %v\n", indent, at(x.Pos), x.Field)
   436  	case *parse.CommandNode:
   437  		fmt.Fprintf(wr.w, "%sCommandNode at %s, %d children\n", indent, at(x.Pos), len(x.Args))
   438  		for _, a := range x.Args {
   439  			wr.writeNode(a, indent+". ")
   440  		}
   441  	//case *parse.CommentNode: // 1.16
   442  	case *parse.DotNode:
   443  		fmt.Fprintf(wr.w, "%sDotNode at %s\n", indent, at(x.Pos))
   444  	case *parse.FieldNode:
   445  		fmt.Fprintf(wr.w, "%sFieldNode at %s, %v\n", indent, at(x.Pos), x.Ident)
   446  	case *parse.IdentifierNode:
   447  		fmt.Fprintf(wr.w, "%sIdentifierNode at %s, %v\n", indent, at(x.Pos), x.Ident)
   448  	case *parse.IfNode:
   449  		fmt.Fprintf(wr.w, "%sIfNode at %s\n", indent, at(x.Pos))
   450  		wr.writeNode(&x.BranchNode, indent+". ")
   451  	case *parse.ListNode:
   452  		if x == nil {
   453  			return // nil BranchNode.ElseList
   454  		}
   455  		fmt.Fprintf(wr.w, "%sListNode at %s, %d children\n", indent, at(x.Pos), len(x.Nodes))
   456  		for _, n := range x.Nodes {
   457  			wr.writeNode(n, indent+". ")
   458  		}
   459  	case *parse.NilNode:
   460  		fmt.Fprintf(wr.w, "%sNilNode at %s\n", indent, at(x.Pos))
   461  	case *parse.NumberNode:
   462  		fmt.Fprintf(wr.w, "%sNumberNode at %s, %s\n", indent, at(x.Pos), x.Text)
   463  	case *parse.PipeNode:
   464  		if x == nil {
   465  			return // {{template "xxx"}}
   466  		}
   467  		fmt.Fprintf(wr.w, "%sPipeNode at %s, %d vars, %d cmds, IsAssign:%v\n",
   468  			indent, at(x.Pos), len(x.Decl), len(x.Cmds), x.IsAssign)
   469  		for _, d := range x.Decl {
   470  			wr.writeNode(d, indent+"Decl. ")
   471  		}
   472  		for _, c := range x.Cmds {
   473  			wr.writeNode(c, indent+"Cmd. ")
   474  		}
   475  	case *parse.RangeNode:
   476  		fmt.Fprintf(wr.w, "%sRangeNode at %s\n", indent, at(x.Pos))
   477  		wr.writeNode(&x.BranchNode, indent+". ")
   478  	case *parse.StringNode:
   479  		fmt.Fprintf(wr.w, "%sStringNode at %s, %s\n", indent, at(x.Pos), x.Quoted)
   480  	case *parse.TemplateNode:
   481  		fmt.Fprintf(wr.w, "%sTemplateNode at %s, %s\n", indent, at(x.Pos), x.Name)
   482  		wr.writeNode(x.Pipe, indent+". ")
   483  	case *parse.TextNode:
   484  		fmt.Fprintf(wr.w, "%sTextNode at %s, len %d\n", indent, at(x.Pos), len(x.Text))
   485  	case *parse.VariableNode:
   486  		fmt.Fprintf(wr.w, "%sVariableNode at %s, %v\n", indent, at(x.Pos), x.Ident)
   487  	case *parse.WithNode:
   488  		fmt.Fprintf(wr.w, "%sWithNode at %s\n", indent, at(x.Pos))
   489  		wr.writeNode(&x.BranchNode, indent+". ")
   490  	}
   491  }
   492  
   493  var kindNames = []string{"", "File", "Module", "Namespace", "Package", "Class", "Method", "Property",
   494  	"Field", "Constructor", "Enum", "Interface", "Function", "Variable", "Constant", "String",
   495  	"Number", "Boolean", "Array", "Object", "Key", "Null", "EnumMember", "Struct", "Event",
   496  	"Operator", "TypeParameter"}
   497  
   498  func kindStr(k protocol.SymbolKind) string {
   499  	n := int(k)
   500  	if n < 1 || n >= len(kindNames) {
   501  		return fmt.Sprintf("?SymbolKind %d?", n)
   502  	}
   503  	return kindNames[n]
   504  }