Commit 17808905 authored by Robert Griesemer's avatar Robert Griesemer

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
parent 22ec5399
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
{.section Funcs} {.section Funcs}
<hr /> <hr />
{.repeated section @} {.repeated section @}
<h2>func {Name|html}</h2> <h2>func <a href="{Decl|link}">{Name|html}</a></h2>
<p><code>{Decl|html}</code></p> <p><code>{Decl|html}</code></p>
{Doc|html-comment} {Doc|html-comment}
{.end} {.end}
...@@ -45,16 +45,16 @@ ...@@ -45,16 +45,16 @@
{.section Types} {.section Types}
{.repeated section @} {.repeated section @}
<hr /> <hr />
<h2>type {.section Type}{Name|html}{.end}</h2> <h2>type <a href="{Decl|link}">{Type.Name|html}</a></h2>
{Doc|html-comment} {Doc|html-comment}
<p><pre>{Decl|html}</pre></p> <p><pre>{Decl|html}</pre></p>
{.repeated section Factories} {.repeated section Factories}
<h3>func {Name|html}</h3> <h3>func <a href="{Decl|link}">{Name|html}</a></h3>
<p><code>{Decl|html}</code></p> <p><code>{Decl|html}</code></p>
{Doc|html-comment} {Doc|html-comment}
{.end} {.end}
{.repeated section Methods} {.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> <p><code>{Decl|html}</code></p>
{Doc|html-comment} {Doc|html-comment}
{.end} {.end}
......
...@@ -258,10 +258,25 @@ func textFmt(w io.Writer, x interface{}, format string) { ...@@ -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{ var fmap = template.FormatterMap{
"": textFmt, "": textFmt,
"html": htmlFmt, "html": htmlFmt,
"html-comment": htmlCommentFmt, "html-comment": htmlCommentFmt,
"link": linkFmt,
} }
...@@ -312,7 +327,9 @@ func servePage(c *http.Conn, title, content interface{}) { ...@@ -312,7 +327,9 @@ func servePage(c *http.Conn, title, content interface{}) {
d.header = title; d.header = title;
d.timestamp = time.SecondsToLocalTime(syncTime.get()).String(); d.timestamp = time.SecondsToLocalTime(syncTime.get()).String();
d.content = content; 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) { ...@@ -328,7 +345,9 @@ func serveText(c *http.Conn, text []byte) {
func serveParseErrors(c *http.Conn, errors *parseErrors) { func serveParseErrors(c *http.Conn, errors *parseErrors) {
// format errors // format errors
var buf bytes.Buffer; 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()); servePage(c, errors.filename + " - Parse Errors", buf.Data());
} }
...@@ -473,16 +492,14 @@ func servePkg(c *http.Conn, r *http.Request) { ...@@ -473,16 +492,14 @@ func servePkg(c *http.Conn, r *http.Request) {
var buf bytes.Buffer; var buf bytes.Buffer;
if false { // TODO req.Params["format"] == "text" if false { // TODO req.Params["format"] == "text"
err := packageText.Execute(info, &buf); if err := packageText.Execute(info, &buf); err != nil {
if err != nil {
log.Stderrf("packageText.Execute: %s", err); log.Stderrf("packageText.Execute: %s", err);
} }
serveText(c, buf.Data()); serveText(c, buf.Data());
return; return;
} }
err := packageHtml.Execute(info, &buf); if err := packageHtml.Execute(info, &buf); err != nil {
if err != nil {
log.Stderrf("packageHtml.Execute: %s", err); log.Stderrf("packageHtml.Execute: %s", err);
} }
...@@ -648,5 +665,7 @@ func main() { ...@@ -648,5 +665,7 @@ func main() {
info.PDoc.Filter(args[1 : len(args)]); 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);
}
} }
...@@ -54,6 +54,19 @@ var ( ...@@ -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 { type printer struct {
// configuration (does not change after initialization) // configuration (does not change after initialization)
output io.Writer; output io.Writer;
...@@ -64,7 +77,10 @@ type printer struct { ...@@ -64,7 +77,10 @@ type printer struct {
written int; // number of bytes written written int; // number of bytes written
level int; // function nesting level; 0 = package scope, 1 = top-level function scope, etc. level int; // function nesting level; 0 = package scope, 1 = top-level function scope, etc.
indent int; // current indentation 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 // buffered whitespace
buffer [8]whiteSpace; // whitespace sequences are short (1 or 2); 8 entries is plenty buffer [8]whiteSpace; // whitespace sequences are short (1 or 2); 8 entries is plenty
...@@ -169,21 +185,38 @@ func (p *printer) writeNewlines(n int) { ...@@ -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; p.pos = pos;
if debug { if debug {
// do not update p.pos - use write0 // do not update p.pos - use write0
p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column))); p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)));
} }
// TODO(gri) Enable once links are generated.
/*
if p.mode & GenHTML != 0 { if p.mode & GenHTML != 0 {
// do not HTML-escape or update p.pos - use write0 // no html-escaping and no p.pos update for tags - use write0
p.write0(strings.Bytes(fmt.Sprintf("<a id=%x></a>", pos.Offset))); 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); p.write(data);
p.prev = p.pos; // 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.last = p.pos;
} }
...@@ -205,14 +238,14 @@ func untabify(src []byte) []byte { ...@@ -205,14 +238,14 @@ func untabify(src []byte) []byte {
func (p *printer) writeComment(comment *ast.Comment) { func (p *printer) writeComment(comment *ast.Comment) {
// separation from previous item // separation from last item
if p.prev.IsValid() { if p.last.IsValid() {
// there was a preceding item (otherwise, the comment is the // there was a preceding item (otherwise, the comment is the
// first item to be printed - in that case do not apply extra // first item to be printed - in that case do not apply extra
// spacing) // spacing)
n := comment.Pos().Line - p.prev.Line; n := comment.Pos().Line - p.last.Line;
if n == 0 { 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]); p.write(tabs[0 : 1]);
} else { } else {
// comment on a different line; separate with newlines // comment on a different line; separate with newlines
...@@ -221,7 +254,7 @@ func (p *printer) writeComment(comment *ast.Comment) { ...@@ -221,7 +254,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
} }
// write 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) { ...@@ -253,8 +286,8 @@ func (p *printer) intersperseComments(next token.Position) {
// are not present in the original source. This makes sure // are not present in the original source. This makes sure
// that comments that need to be adjacent to a declaration // that comments that need to be adjacent to a declaration
// remain adjacent. // remain adjacent.
if p.prev.IsValid() { if p.last.IsValid() {
n := next.Line - p.prev.Line; n := next.Line - p.last.Line;
if n < p.buflen { if n < p.buflen {
p.buflen = n; p.buflen = n;
} }
...@@ -271,7 +304,7 @@ func (p *printer) intersperseComments(next token.Position) { ...@@ -271,7 +304,7 @@ func (p *printer) intersperseComments(next token.Position) {
p.buflen = 1; p.buflen = 1;
ch = newline; // original ch was a lie 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 ch = formfeed; // comments span at least 2 lines
} }
p.buffer[0] = ch; p.buffer[0] = ch;
...@@ -305,6 +338,7 @@ func (p *printer) writeWhitespace() { ...@@ -305,6 +338,7 @@ func (p *printer) writeWhitespace() {
// printed, followed by the actual token. // printed, followed by the actual token.
// //
func (p *printer) print(args ...) { func (p *printer) print(args ...) {
setLineTag := false;
v := reflect.NewValue(args).(*reflect.StructValue); v := reflect.NewValue(args).(*reflect.StructValue);
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
f := v.Field(i); f := v.Field(i);
...@@ -334,7 +368,17 @@ func (p *printer) print(args ...) { ...@@ -334,7 +368,17 @@ func (p *printer) print(args ...) {
case token.Token: case token.Token:
data = strings.Bytes(x.String()); data = strings.Bytes(x.String());
case token.Position: case token.Position:
if x.IsValid() {
next = x; // accurate position of next item 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: default:
panicln("print: unsupported argument type", f.Type().String()); panicln("print: unsupported argument type", f.Type().String());
} }
...@@ -351,7 +395,8 @@ func (p *printer) print(args ...) { ...@@ -351,7 +395,8 @@ func (p *printer) print(args ...) {
// intersperse extra newlines if present in the source // intersperse extra newlines if present in the source
p.writeNewlines(next.Line - p.pos.Line); 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) { ...@@ -475,8 +520,8 @@ func (p *printer) signature(params, result []*ast.Field) {
// Returns true if the field list ends in a closing brace. // 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 { func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isInterface bool) bool {
if !lbrace.IsValid() { if list == nil {
// forward declaration without {}'s // forward declaration
return false; // no {}'s return false; // no {}'s
} }
...@@ -487,8 +532,8 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok ...@@ -487,8 +532,8 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
p.print(blank, lbrace, token.LBRACE, +1, newline); p.print(blank, lbrace, token.LBRACE, +1, newline);
var lastWasAnon bool; // true if the previous line was an anonymous field var lastWasAnon bool; // true if the last line was an anonymous field
var lastComment *ast.CommentGroup; // the comment from the previous line var lastComment *ast.CommentGroup; // the comment from the last line
for i, f := range list { for i, f := range list {
// at least one visible identifier or anonymous field // at least one visible identifier or anonymous field
isAnon := len(f.Names) == 0; isAnon := len(f.Names) == 0;
...@@ -496,11 +541,11 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok ...@@ -496,11 +541,11 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
p.lineComment(lastComment); p.lineComment(lastComment);
if lastWasAnon == isAnon { if lastWasAnon == isAnon {
// previous and current line have same structure; // last and current line have same structure;
// continue with existing columns // continue with existing columns
p.print(newline); p.print(newline);
} else { } else {
// previous and current line have different structure; // last and current line have different structure;
// flush tabwriter and start new columns (the "type // flush tabwriter and start new columns (the "type
// column" on a line with named fields may line up // column" on a line with named fields may line up
// with the "line comment column" on a line with // 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) ...@@ -1018,7 +1063,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool)
case *ast.GenDecl: case *ast.GenDecl:
p.leadComment(d.Doc); p.leadComment(d.Doc);
p.print(d.Pos(), d.Tok, blank); p.print(lineTag(d.Pos()), d.Tok, blank);
if d.Lparen.IsValid() { if d.Lparen.IsValid() {
// group of parenthesized declarations // group of parenthesized declarations
...@@ -1050,7 +1095,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool) ...@@ -1050,7 +1095,7 @@ func (p *printer) decl(decl ast.Decl) (comment *ast.CommentGroup, optSemi bool)
case *ast.FuncDecl: case *ast.FuncDecl:
p.leadComment(d.Doc); 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 { if recv := d.Recv; recv != nil {
// method: print receiver // method: print receiver
p.print(token.LPAREN); p.print(token.LPAREN);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment