diff options
author | Robert Griesemer <gri@golang.org> | 2009-08-03 09:53:00 -0700 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2009-08-03 09:53:00 -0700 |
commit | e6baf6d7886440d371058c4c643730031fd3223c (patch) | |
tree | 87956d4bd710adfd2c4daa184cfccd56e115aac1 | |
parent | ef75aaa6310b19cf2dbc7bc5ad9cb7c186a81767 (diff) | |
download | golang-e6baf6d7886440d371058c4c643730031fd3223c.tar.gz |
printer.go:
- emit line tag id's in html mode
- support for general html tags
- better names for a few identifiers
godoc.go:
- emit links from exported names to source code
(actual placement needs fine-tuning)
R=rsc
DELTA=108 (68 added, 4 deleted, 36 changed)
OCL=32639
CL=32654
-rw-r--r-- | lib/godoc/package.html | 8 | ||||
-rw-r--r-- | src/cmd/godoc/godoc.go | 33 | ||||
-rw-r--r-- | src/pkg/go/printer/printer.go | 99 |
3 files changed, 102 insertions, 38 deletions
diff --git a/lib/godoc/package.html b/lib/godoc/package.html index 4931035df..c7ae7593d 100644 --- a/lib/godoc/package.html +++ b/lib/godoc/package.html @@ -37,7 +37,7 @@ {.section Funcs} <hr /> {.repeated section @} - <h2>func {Name|html}</h2> + <h2>func <a href="{Decl|link}">{Name|html}</a></h2> <p><code>{Decl|html}</code></p> {Doc|html-comment} {.end} @@ -45,16 +45,16 @@ {.section Types} {.repeated section @} <hr /> - <h2>type {.section Type}{Name|html}{.end}</h2> + <h2>type <a href="{Decl|link}">{Type.Name|html}</a></h2> {Doc|html-comment} <p><pre>{Decl|html}</pre></p> {.repeated section Factories} - <h3>func {Name|html}</h3> + <h3>func <a href="{Decl|link}">{Name|html}</a></h3> <p><code>{Decl|html}</code></p> {Doc|html-comment} {.end} {.repeated section Methods} - <h3>func ({Recv|html}) {Name|html}</h3> + <h3>func ({Recv|html}) <a href="{Decl|link}">{Name|html}</a></h3> <p><code>{Decl|html}</code></p> {Doc|html-comment} {.end} diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index f4875623f..f7ba3dbbe 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -258,10 +258,25 @@ func textFmt(w io.Writer, x interface{}, format string) { } +// Template formatter for "link" format. +func linkFmt(w io.Writer, x interface{}, format string) { + type Positioner interface { Pos() token.Position } + if node, ok := x.(Positioner); ok { + pos := node.Pos(); + if pos.IsValid() { + // line id's in html-printed source are of the + // form "L%d" where %d stands for the line number + fmt.Fprintf(w, "/%s#L%d", pos.Filename, pos.Line); + } + } +} + + var fmap = template.FormatterMap{ "": textFmt, "html": htmlFmt, "html-comment": htmlCommentFmt, + "link": linkFmt, } @@ -312,7 +327,9 @@ func servePage(c *http.Conn, title, content interface{}) { d.header = title; d.timestamp = time.SecondsToLocalTime(syncTime.get()).String(); d.content = content; - godocHtml.Execute(&d, c); + if err := godocHtml.Execute(&d, c); err != nil { + log.Stderrf("godocHtml.Execute: %s", err); + } } @@ -328,7 +345,9 @@ func serveText(c *http.Conn, text []byte) { func serveParseErrors(c *http.Conn, errors *parseErrors) { // format errors var buf bytes.Buffer; - parseerrorHtml.Execute(errors, &buf); + if err := parseerrorHtml.Execute(errors, &buf); err != nil { + log.Stderrf("parseerrorHtml.Execute: %s", err); + } servePage(c, errors.filename + " - Parse Errors", buf.Data()); } @@ -473,16 +492,14 @@ func servePkg(c *http.Conn, r *http.Request) { var buf bytes.Buffer; if false { // TODO req.Params["format"] == "text" - err := packageText.Execute(info, &buf); - if err != nil { + if err := packageText.Execute(info, &buf); err != nil { log.Stderrf("packageText.Execute: %s", err); } serveText(c, buf.Data()); return; } - err := packageHtml.Execute(info, &buf); - if err != nil { + if err := packageHtml.Execute(info, &buf); err != nil { log.Stderrf("packageHtml.Execute: %s", err); } @@ -648,5 +665,7 @@ func main() { info.PDoc.Filter(args[1 : len(args)]); } - packageText.Execute(info, os.Stdout); + if err := packageText.Execute(info, os.Stdout); err != nil { + log.Stderrf("packageText.Execute: %s", err); + } } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index 2522c69f5..b2d34ef5c 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -54,6 +54,19 @@ var ( ) +// A lineTag is a token.Position that is used to print +// line tag id's of the form "L%d" where %d stands for +// the line indicated by position. +// +type lineTag token.Position + + +// A htmlTag specifies a start and end tag. +type htmlTag struct { + start, end string; // empty if tags are absent +} + + type printer struct { // configuration (does not change after initialization) output io.Writer; @@ -64,7 +77,10 @@ type printer struct { written int; // number of bytes written level int; // function nesting level; 0 = package scope, 1 = top-level function scope, etc. indent int; // current indentation - prev, pos token.Position; + last token.Position; // (possibly estimated) position immediately after the last item; in AST space + pos token.Position; // (possibly estimated) position; in AST space + tag htmlTag; // tag to be used around next item + lastTaggedLine int; // last line for which a line tag was written // buffered whitespace buffer [8]whiteSpace; // whitespace sequences are short (1 or 2); 8 entries is plenty @@ -169,21 +185,38 @@ func (p *printer) writeNewlines(n int) { } -func (p *printer) writeItem(pos token.Position, data []byte) { +func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) { p.pos = pos; if debug { // do not update p.pos - use write0 p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column))); } - // TODO(gri) Enable once links are generated. - /* if p.mode & GenHTML != 0 { - // do not HTML-escape or update p.pos - use write0 - p.write0(strings.Bytes(fmt.Sprintf("<a id=%x></a>", pos.Offset))); + // no html-escaping and no p.pos update for tags - use write0 + if setLineTag && pos.Line > p.lastTaggedLine { + // id's must be unique within a document: set + // line tag only if line number has increased + // (note: for now write complete start and end + // tag - shorter versions seem to have issues + // with Safari) + p.tag.start = fmt.Sprintf(`<a id="L%d"></a>`, pos.Line); + p.lastTaggedLine = pos.Line; + } + // write start tag, if any + if p.tag.start != "" { + p.write0(strings.Bytes(p.tag.start)); + p.tag.start = ""; // tag consumed + } + p.write(data); + // write end tag, if any + if p.tag.end != "" { + p.write0(strings.Bytes(p.tag.end)); + p.tag.end = ""; // tag consumed + } + } else { + p.write(data); } - */ - p.write(data); - p.prev = p.pos; + p.last = p.pos; } @@ -205,14 +238,14 @@ func untabify(src []byte) []byte { func (p *printer) writeComment(comment *ast.Comment) { - // separation from previous item - if p.prev.IsValid() { + // separation from last item + if p.last.IsValid() { // there was a preceding item (otherwise, the comment is the // first item to be printed - in that case do not apply extra // spacing) - n := comment.Pos().Line - p.prev.Line; + n := comment.Pos().Line - p.last.Line; if n == 0 { - // comment on the same line as previous item; separate with tab + // comment on the same line as last item; separate with tab p.write(tabs[0 : 1]); } else { // comment on a different line; separate with newlines @@ -221,7 +254,7 @@ func (p *printer) writeComment(comment *ast.Comment) { } // write comment - p.writeItem(comment.Pos(), comment.Text); + p.writeItem(comment.Pos(), comment.Text, false); } @@ -253,8 +286,8 @@ func (p *printer) intersperseComments(next token.Position) { // are not present in the original source. This makes sure // that comments that need to be adjacent to a declaration // remain adjacent. - if p.prev.IsValid() { - n := next.Line - p.prev.Line; + if p.last.IsValid() { + n := next.Line - p.last.Line; if n < p.buflen { p.buflen = n; } @@ -271,7 +304,7 @@ func (p *printer) intersperseComments(next token.Position) { p.buflen = 1; ch = newline; // original ch was a lie } - if p.prev.Line > firstLine { + if p.last.Line > firstLine { ch = formfeed; // comments span at least 2 lines } p.buffer[0] = ch; @@ -305,6 +338,7 @@ func (p *printer) writeWhitespace() { // printed, followed by the actual token. // func (p *printer) print(args ...) { + setLineTag := false; v := reflect.NewValue(args).(*reflect.StructValue); for i := 0; i < v.NumField(); i++ { f := v.Field(i); @@ -334,7 +368,17 @@ func (p *printer) print(args ...) { case token.Token: data = strings.Bytes(x.String()); case token.Position: - next = x; // accurate position of next item + if x.IsValid() { + next = x; // accurate position of next item + } + case lineTag: + pos := token.Position(x); + if pos.IsValid() { + next = pos; // accurate position of next item + setLineTag = true; + } + case htmlTag: + p.tag = x; // tag surrounding next item default: panicln("print: unsupported argument type", f.Type().String()); } @@ -351,7 +395,8 @@ func (p *printer) print(args ...) { // intersperse extra newlines if present in the source p.writeNewlines(next.Line - p.pos.Line); - p.writeItem(next, data); + p.writeItem(next, data, setLineTag); + setLineTag = false; } } } @@ -475,8 +520,8 @@ func (p *printer) signature(params, result []*ast.Field) { // Returns true if the field list ends in a closing brace. func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isInterface bool) bool { - if !lbrace.IsValid() { - // forward declaration without {}'s + if list == nil { + // forward declaration return false; // no {}'s } @@ -487,8 +532,8 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok p.print(blank, lbrace, token.LBRACE, +1, newline); - var lastWasAnon bool; // true if the previous line was an anonymous field - var lastComment *ast.CommentGroup; // the comment from the previous line + var lastWasAnon bool; // true if the last line was an anonymous field + var lastComment *ast.CommentGroup; // the comment from the last line for i, f := range list { // at least one visible identifier or anonymous field isAnon := len(f.Names) == 0; @@ -496,11 +541,11 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok p.print(token.SEMICOLON); p.lineComment(lastComment); if lastWasAnon == isAnon { - // previous and current line have same structure; + // last and current line have same structure; // continue with existing columns p.print(newline); } else { - // previous and current line have different structure; + // last and current line have different structure; // flush tabwriter and start new columns (the "type // column" on a line with named fields may line up // with the "line comment column" on a line with @@ -1018,7 +1063,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool) case *ast.GenDecl: p.leadComment(d.Doc); - p.print(d.Pos(), d.Tok, blank); + p.print(lineTag(d.Pos()), d.Tok, blank); if d.Lparen.IsValid() { // group of parenthesized declarations @@ -1050,7 +1095,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool) case *ast.FuncDecl: p.leadComment(d.Doc); - p.print(d.Pos(), token.FUNC, blank); + p.print(lineTag(d.Pos()), token.FUNC, blank); if recv := d.Recv; recv != nil { // method: print receiver p.print(token.LPAREN); |