summaryrefslogtreecommitdiff
path: root/src/cmd/godoc/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/godoc/format.go')
-rw-r--r--src/cmd/godoc/format.go372
1 files changed, 0 insertions, 372 deletions
diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go
deleted file mode 100644
index 59a89c5bf..000000000
--- a/src/cmd/godoc/format.go
+++ /dev/null
@@ -1,372 +0,0 @@
-// 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 main
-
-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, "<a id=\"L%d\"></a><span class=\"ln\">%6d</span>\t", line, line)
- line++
- }
- }
- }
- FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
- } else {
- template.HTMLEscape(w, text)
- }
-}