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 / godoc / format.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/godoc/format.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/godoc/format.go
new file mode 100644 (file)
index 0000000..3e8c867
--- /dev/null
@@ -0,0 +1,371 @@
+// Copyright 2011 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.
+
+// This file implements FormatSelections and FormatText.
+// FormatText is used to HTML-format Go and non-Go source
+// text with line numbers and highlighted sections. It is
+// built on top of FormatSelections, a generic formatter
+// for "selected" text.
+
+package godoc
+
+import (
+       "fmt"
+       "go/scanner"
+       "go/token"
+       "io"
+       "regexp"
+       "strconv"
+       "text/template"
+)
+
+// ----------------------------------------------------------------------------
+// Implementation of FormatSelections
+
+// A Segment describes a text segment [start, end).
+// The zero value of a Segment is a ready-to-use empty segment.
+//
+type Segment struct {
+       start, end int
+}
+
+func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }
+
+// A Selection is an "iterator" function returning a text segment.
+// Repeated calls to a selection return consecutive, non-overlapping,
+// non-empty segments, followed by an infinite sequence of empty
+// segments. The first empty segment marks the end of the selection.
+//
+type Selection func() Segment
+
+// A LinkWriter writes some start or end "tag" to w for the text offset offs.
+// It is called by FormatSelections at the start or end of each link segment.
+//
+type LinkWriter func(w io.Writer, offs int, start bool)
+
+// A SegmentWriter formats a text according to selections and writes it to w.
+// The selections parameter is a bit set indicating which selections provided
+// to FormatSelections overlap with the text segment: If the n'th bit is set
+// in selections, the n'th selection provided to FormatSelections is overlapping
+// with the text.
+//
+type SegmentWriter func(w io.Writer, text []byte, selections int)
+
+// FormatSelections takes a text and writes it to w using link and segment
+// writers lw and sw as follows: lw is invoked for consecutive segment starts
+// and ends as specified through the links selection, and sw is invoked for
+// consecutive segments of text overlapped by the same selections as specified
+// by selections. The link writer lw may be nil, in which case the links
+// Selection is ignored.
+//
+func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
+       // If we have a link writer, make the links
+       // selection the last entry in selections
+       if lw != nil {
+               selections = append(selections, links)
+       }
+
+       // compute the sequence of consecutive segment changes
+       changes := newMerger(selections)
+
+       // The i'th bit in bitset indicates that the text
+       // at the current offset is covered by selections[i].
+       bitset := 0
+       lastOffs := 0
+
+       // Text segments are written in a delayed fashion
+       // such that consecutive segments belonging to the
+       // same selection can be combined (peephole optimization).
+       // last describes the last segment which has not yet been written.
+       var last struct {
+               begin, end int // valid if begin < end
+               bitset     int
+       }
+
+       // flush writes the last delayed text segment
+       flush := func() {
+               if last.begin < last.end {
+                       sw(w, text[last.begin:last.end], last.bitset)
+               }
+               last.begin = last.end // invalidate last
+       }
+
+       // segment runs the segment [lastOffs, end) with the selection
+       // indicated by bitset through the segment peephole optimizer.
+       segment := func(end int) {
+               if lastOffs < end { // ignore empty segments
+                       if last.end != lastOffs || last.bitset != bitset {
+                               // the last segment is not adjacent to or
+                               // differs from the new one
+                               flush()
+                               // start a new segment
+                               last.begin = lastOffs
+                       }
+                       last.end = end
+                       last.bitset = bitset
+               }
+       }
+
+       for {
+               // get the next segment change
+               index, offs, start := changes.next()
+               if index < 0 || offs > len(text) {
+                       // no more segment changes or the next change
+                       // is past the end of the text - we're done
+                       break
+               }
+               // determine the kind of segment change
+               if lw != nil && index == len(selections)-1 {
+                       // we have a link segment change (see start of this function):
+                       // format the previous selection segment, write the
+                       // link tag and start a new selection segment
+                       segment(offs)
+                       flush()
+                       lastOffs = offs
+                       lw(w, offs, start)
+               } else {
+                       // we have a selection change:
+                       // format the previous selection segment, determine
+                       // the new selection bitset and start a new segment
+                       segment(offs)
+                       lastOffs = offs
+                       mask := 1 << uint(index)
+                       if start {
+                               bitset |= mask
+                       } else {
+                               bitset &^= mask
+                       }
+               }
+       }
+       segment(len(text))
+       flush()
+}
+
+// A merger merges a slice of Selections and produces a sequence of
+// consecutive segment change events through repeated next() calls.
+//
+type merger struct {
+       selections []Selection
+       segments   []Segment // segments[i] is the next segment of selections[i]
+}
+
+const infinity int = 2e9
+
+func newMerger(selections []Selection) *merger {
+       segments := make([]Segment, len(selections))
+       for i, sel := range selections {
+               segments[i] = Segment{infinity, infinity}
+               if sel != nil {
+                       if seg := sel(); !seg.isEmpty() {
+                               segments[i] = seg
+                       }
+               }
+       }
+       return &merger{selections, segments}
+}
+
+// next returns the next segment change: index specifies the Selection
+// to which the segment belongs, offs is the segment start or end offset
+// as determined by the start value. If there are no more segment changes,
+// next returns an index value < 0.
+//
+func (m *merger) next() (index, offs int, start bool) {
+       // find the next smallest offset where a segment starts or ends
+       offs = infinity
+       index = -1
+       for i, seg := range m.segments {
+               switch {
+               case seg.start < offs:
+                       offs = seg.start
+                       index = i
+                       start = true
+               case seg.end < offs:
+                       offs = seg.end
+                       index = i
+                       start = false
+               }
+       }
+       if index < 0 {
+               // no offset found => all selections merged
+               return
+       }
+       // offset found - it's either the start or end offset but
+       // either way it is ok to consume the start offset: set it
+       // to infinity so it won't be considered in the following
+       // next call
+       m.segments[index].start = infinity
+       if start {
+               return
+       }
+       // end offset found - consume it
+       m.segments[index].end = infinity
+       // advance to the next segment for that selection
+       seg := m.selections[index]()
+       if !seg.isEmpty() {
+               m.segments[index] = seg
+       }
+       return
+}
+
+// ----------------------------------------------------------------------------
+// Implementation of FormatText
+
+// lineSelection returns the line segments for text as a Selection.
+func lineSelection(text []byte) Selection {
+       i, j := 0, 0
+       return func() (seg Segment) {
+               // find next newline, if any
+               for j < len(text) {
+                       j++
+                       if text[j-1] == '\n' {
+                               break
+                       }
+               }
+               if i < j {
+                       // text[i:j] constitutes a line
+                       seg = Segment{i, j}
+                       i = j
+               }
+               return
+       }
+}
+
+// tokenSelection returns, as a selection, the sequence of
+// consecutive occurrences of token sel in the Go src text.
+//
+func tokenSelection(src []byte, sel token.Token) Selection {
+       var s scanner.Scanner
+       fset := token.NewFileSet()
+       file := fset.AddFile("", fset.Base(), len(src))
+       s.Init(file, src, nil, scanner.ScanComments)
+       return func() (seg Segment) {
+               for {
+                       pos, tok, lit := s.Scan()
+                       if tok == token.EOF {
+                               break
+                       }
+                       offs := file.Offset(pos)
+                       if tok == sel {
+                               seg = Segment{offs, offs + len(lit)}
+                               break
+                       }
+               }
+               return
+       }
+}
+
+// makeSelection is a helper function to make a Selection from a slice of pairs.
+// Pairs describing empty segments are ignored.
+//
+func makeSelection(matches [][]int) Selection {
+       i := 0
+       return func() Segment {
+               for i < len(matches) {
+                       m := matches[i]
+                       i++
+                       if m[0] < m[1] {
+                               // non-empty segment
+                               return Segment{m[0], m[1]}
+                       }
+               }
+               return Segment{}
+       }
+}
+
+// regexpSelection computes the Selection for the regular expression expr in text.
+func regexpSelection(text []byte, expr string) Selection {
+       var matches [][]int
+       if rx, err := regexp.Compile(expr); err == nil {
+               matches = rx.FindAllIndex(text, -1)
+       }
+       return makeSelection(matches)
+}
+
+var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)
+
+// RangeSelection computes the Selection for a text range described
+// by the argument str; the range description must match the selRx
+// regular expression.
+func RangeSelection(str string) Selection {
+       m := selRx.FindStringSubmatch(str)
+       if len(m) >= 2 {
+               from, _ := strconv.Atoi(m[1])
+               to, _ := strconv.Atoi(m[2])
+               if from < to {
+                       return makeSelection([][]int{{from, to}})
+               }
+       }
+       return nil
+}
+
+// Span tags for all the possible selection combinations that may
+// be generated by FormatText. Selections are indicated by a bitset,
+// and the value of the bitset specifies the tag to be used.
+//
+// bit 0: comments
+// bit 1: highlights
+// bit 2: selections
+//
+var startTags = [][]byte{
+       /* 000 */ []byte(``),
+       /* 001 */ []byte(`<span class="comment">`),
+       /* 010 */ []byte(`<span class="highlight">`),
+       /* 011 */ []byte(`<span class="highlight-comment">`),
+       /* 100 */ []byte(`<span class="selection">`),
+       /* 101 */ []byte(`<span class="selection-comment">`),
+       /* 110 */ []byte(`<span class="selection-highlight">`),
+       /* 111 */ []byte(`<span class="selection-highlight-comment">`),
+}
+
+var endTag = []byte(`</span>`)
+
+func selectionTag(w io.Writer, text []byte, selections int) {
+       if selections < len(startTags) {
+               if tag := startTags[selections]; len(tag) > 0 {
+                       w.Write(tag)
+                       template.HTMLEscape(w, text)
+                       w.Write(endTag)
+                       return
+               }
+       }
+       template.HTMLEscape(w, text)
+}
+
+// FormatText HTML-escapes text and writes it to w.
+// Consecutive text segments are wrapped in HTML spans (with tags as
+// defined by startTags and endTag) as follows:
+//
+//     - if line >= 0, line number (ln) spans are inserted before each line,
+//       starting with the value of line
+//     - if the text is Go source, comments get the "comment" span class
+//     - each occurrence of the regular expression pattern gets the "highlight"
+//       span class
+//     - text segments covered by selection get the "selection" span class
+//
+// Comments, highlights, and selections may overlap arbitrarily; the respective
+// HTML span classes are specified in the startTags variable.
+//
+func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
+       var comments, highlights Selection
+       if goSource {
+               comments = tokenSelection(text, token.COMMENT)
+       }
+       if pattern != "" {
+               highlights = regexpSelection(text, pattern)
+       }
+       if line >= 0 || comments != nil || highlights != nil || selection != nil {
+               var lineTag LinkWriter
+               if line >= 0 {
+                       lineTag = func(w io.Writer, _ int, start bool) {
+                               if start {
+                                       fmt.Fprintf(w, "<span id=\"L%d\" class=\"ln\">%6d</span>", line, line)
+                                       line++
+                               }
+                       }
+               }
+               FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
+       } else {
+               template.HTMLEscape(w, text)
+       }
+}