Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / stack / parse.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/internal/stack/parse.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/internal/stack/parse.go
new file mode 100644 (file)
index 0000000..e01da8f
--- /dev/null
@@ -0,0 +1,175 @@
+// Copyright 2020 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 stack
+
+import (
+       "bufio"
+       "errors"
+       "io"
+       "regexp"
+       "strconv"
+)
+
+var (
+       reBlank     = regexp.MustCompile(`^\s*$`)
+       reGoroutine = regexp.MustCompile(`^\s*goroutine (\d+) \[([^\]]*)\]:\s*$`)
+       reCall      = regexp.MustCompile(`^\s*` +
+               `(created by )?` + //marker
+               `(([\w/.]+/)?[\w]+)\.` + //package
+               `(\(([^:.)]*)\)\.)?` + //optional type
+               `([\w\.]+)` + //function
+               `(\(.*\))?` + // args
+               `\s*$`)
+       rePos = regexp.MustCompile(`^\s*(.*):(\d+)( .*)?$`)
+
+       errBreakParse = errors.New("break parse")
+)
+
+// Scanner splits an input stream into lines in a way that is consumable by
+// the parser.
+type Scanner struct {
+       lines *bufio.Scanner
+       done  bool
+}
+
+// NewScanner creates a scanner on top of a reader.
+func NewScanner(r io.Reader) *Scanner {
+       s := &Scanner{
+               lines: bufio.NewScanner(r),
+       }
+       s.Skip() // prefill
+       return s
+}
+
+// Peek returns the next line without consuming it.
+func (s *Scanner) Peek() string {
+       if s.done {
+               return ""
+       }
+       return s.lines.Text()
+}
+
+// Skip consumes the next line without looking at it.
+// Normally used after it has already been looked at using Peek.
+func (s *Scanner) Skip() {
+       if !s.lines.Scan() {
+               s.done = true
+       }
+}
+
+// Next consumes and returns the next line.
+func (s *Scanner) Next() string {
+       line := s.Peek()
+       s.Skip()
+       return line
+}
+
+// Done returns true if the scanner has reached the end of the underlying
+// stream.
+func (s *Scanner) Done() bool {
+       return s.done
+}
+
+// Err returns true if the scanner has reached the end of the underlying
+// stream.
+func (s *Scanner) Err() error {
+       return s.lines.Err()
+}
+
+// Match returns the submatchs of the regular expression against the next line.
+// If it matched the line is also consumed.
+func (s *Scanner) Match(re *regexp.Regexp) []string {
+       if s.done {
+               return nil
+       }
+       match := re.FindStringSubmatch(s.Peek())
+       if match != nil {
+               s.Skip()
+       }
+       return match
+}
+
+// SkipBlank skips any number of pure whitespace lines.
+func (s *Scanner) SkipBlank() {
+       for !s.done {
+               line := s.Peek()
+               if len(line) != 0 && !reBlank.MatchString(line) {
+                       return
+               }
+               s.Skip()
+       }
+}
+
+// Parse the current contiguous block of goroutine stack traces until the
+// scanned content no longer matches.
+func Parse(scanner *Scanner) (Dump, error) {
+       dump := Dump{}
+       for {
+               gr, ok := parseGoroutine(scanner)
+               if !ok {
+                       return dump, nil
+               }
+               dump = append(dump, gr)
+       }
+}
+
+func parseGoroutine(scanner *Scanner) (Goroutine, bool) {
+       match := scanner.Match(reGoroutine)
+       if match == nil {
+               return Goroutine{}, false
+       }
+       id, _ := strconv.ParseInt(match[1], 0, 32)
+       gr := Goroutine{
+               ID:    int(id),
+               State: match[2],
+       }
+       for {
+               frame, ok := parseFrame(scanner)
+               if !ok {
+                       scanner.SkipBlank()
+                       return gr, true
+               }
+               if frame.Position.Filename != "" {
+                       gr.Stack = append(gr.Stack, frame)
+               }
+       }
+}
+
+func parseFrame(scanner *Scanner) (Frame, bool) {
+       fun, ok := parseFunction(scanner)
+       if !ok {
+               return Frame{}, false
+       }
+       frame := Frame{
+               Function: fun,
+       }
+       frame.Position, ok = parsePosition(scanner)
+       // if ok is false, then this is a broken state.
+       // we got the func but not the file that must follow
+       // the consumed line can be recovered from the frame
+       //TODO: push back the fun raw
+       return frame, ok
+}
+
+func parseFunction(scanner *Scanner) (Function, bool) {
+       match := scanner.Match(reCall)
+       if match == nil {
+               return Function{}, false
+       }
+       return Function{
+               Package: match[2],
+               Type:    match[5],
+               Name:    match[6],
+       }, true
+}
+
+func parsePosition(scanner *Scanner) (Position, bool) {
+       match := scanner.Match(rePos)
+       if match == nil {
+               return Position{}, false
+       }
+       line, _ := strconv.ParseInt(match[2], 0, 32)
+       return Position{Filename: match[1], Line: int(line)}, true
+}