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 }