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