]> Cypherpunks repositories - gostls13.git/commitdiff
go/printer:
authorRobert Griesemer <gri@golang.org>
Thu, 22 Oct 2009 16:41:38 +0000 (09:41 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 22 Oct 2009 16:41:38 +0000 (09:41 -0700)
- handle HTML tagging via (client-installable) Stylers

go/doc:
- basic styler support
- some factoring
- ready to contain the search code (but for now excluded)

doc/style.css:
- updated

doc/go_spec.css:
- cleanup: replace deprecated uses of <font> tag with <span> tag

R=rsc
DELTA=302  (160 added, 62 deleted, 80 changed)
OCL=35973
CL=35996

doc/go_spec.html
doc/style.css
src/cmd/cgo/out.go
src/cmd/godoc/godoc.go
src/cmd/gofmt/gofmt.go
src/pkg/go/printer/printer.go
src/pkg/go/printer/printer_test.go

index 40c65dfdbd52128d4f39e85af1c5413b5fefd9d6..c743f6d04d4809508c580d3b092c47c02f5ee77a 100644 (file)
@@ -1868,7 +1868,7 @@ math.Sin
 </pre>
 
 <p>
-<font color=red>TODO: Unify this section with Selectors - it's the same syntax.</font>
+<span class="alert">TODO: Unify this section with Selectors - it's the same syntax.</span>
 </p>
 
 <h3 id="Composite_literals">Composite literals</h3>
@@ -2219,9 +2219,9 @@ p.M0        // ((*p).T0).M0
 </pre>
 
 
-<font color=red>
+<span class="alert">
 TODO: Specify what happens to receivers.
-</font>
+</span>
 
 
 <h3 id="Indexes">Indexes</h3>
@@ -2779,7 +2779,7 @@ that is, either a variable, pointer indirection, array or slice indexing
 operation,
 or a field selector of an addressable struct operand.
 A function result variable is not addressable.
-(<font color=red>TODO: remove this restriction.</font>)
+(<span class="alert">TODO: remove this restriction.</span>)
 Given an operand of pointer type, the pointer indirection
 operator <code>*</code> retrieves the value pointed
 to by the operand.
@@ -2881,8 +2881,8 @@ zero value for its type (§<a href="#The_zero_value">The zero value</a>).
 </p>
 
 <p>
-<font color=red>TODO: Probably in a separate section, communication semantics
-need to be presented regarding send, receive, select, and goroutines.</font>
+<span class="alert">TODO: Probably in a separate section, communication semantics
+need to be presented regarding send, receive, select, and goroutines.</span>
 </p>
 
 <h3 id="Method_expressions">Method expressions</h3>
@@ -3161,11 +3161,11 @@ int8(^1)    // same as int8(-2)
 </pre>
 
 <p>
-<font color=red>
+<span class="alert">
 TODO: perhaps ^ should be disallowed on non-uints instead of assuming twos complement.
 Also it may be possible to make typed constants more like variables, at the cost of fewer
 overflow etc. errors being caught.
-</font>
+</span>
 </p>
 
 
@@ -3900,11 +3900,11 @@ func complex_f3() (re float, im float) {
 </ol>
 
 <p>
-<font color=red>
+<span class="alert">
 TODO: Define when return is required.<br />
 TODO: Language about result parameters needs to go into a section on
       function/method invocation<br />
-</font>
+</span>
 </p>
 
 <h3 id="Break_statements">Break statements</h3>
@@ -3977,7 +3977,7 @@ L:
 <p>
 is erroneous because the jump to label <code>L</code> skips
 the creation of <code>v</code>.
-(<font color=red>TODO: Eliminate in favor of used and not set errors?</font>)
+(<span class="alert">TODO: Eliminate in favor of used and not set errors?</span>)
 </p>
 
 <h3 id="Fallthrough_statements">Fallthrough statements</h3>
@@ -4578,9 +4578,9 @@ The following minimal alignment properties are guaranteed:
    <code>unsafe.Alignof(x[0])</code>, but at least 1.
 </ol>
 
-<h2 id="Implementation_differences"><font color=red>Implementation differences - TODO</font></h2>
+<h2 id="Implementation_differences"><span class="alert">Implementation differences - TODO</span></h2>
 <ul>
-       <li><font color=red>Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</font></li>
-       <li><font color=red>Gccgo does not implement the blank identifier.</font></li>
-       <li><font color=red>Method expressions are not implemented.</font></li>
+       <li><span class="alert">Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</span></li>
+       <li><span class="alert">Gccgo does not implement the blank identifier.</span></li>
+       <li><span class="alert">Method expressions are not implemented.</span></li>
 </ul>
index fcbc0b3bd5b446e672506dd7fcce222eaa35ad44..cd344df9cf5289839ec95a027132dfe76141cbc5 100644 (file)
@@ -1,3 +1,12 @@
+/*
+   Copyright 2009 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.
+*/
+
+/* ------------------------------------------------------------------------- */
+/* Styles meant to help page authors achieve beauty. */
+
 code, .code {
   font-size: 100%;
   font-family: monospace;
@@ -19,11 +28,15 @@ pre.grammar {
 }
 
 p.rule {
-  font-style: italic
+  font-style: italic;
 }
 
 span.event {
-  font-style: italic
+  font-style: italic;
+}
+
+span.alert {
+  color: #ff0000;
 }
 
 body {
@@ -41,7 +54,7 @@ h2 {
   border-top:1px solid #36C;
 }
 
-pre{
+pre {
   font-size: 9pt;
   background-color: #f8f8ff;
   margin: 1em 0 0 0;
@@ -59,8 +72,9 @@ li pre {
   margin: 0.5em 0px 1em 0px;
 }
 
-/* Above this comment, styles meant to help page authors achieve beauty. */
-/* Below this comment, styles used in the boilerplate-ish parts of pages. */
+
+/* ------------------------------------------------------------------------- */
+/* Styles used in the boilerplate-ish parts of pages. */
 
 div#content {
   margin-left: 20%;
@@ -133,3 +147,14 @@ div#linkList li.navhead {
   }
 }
 
+
+/* ------------------------------------------------------------------------- */
+/* Styles used by go/printer Styler implementations. */
+
+span.comment {
+  color: #0000a0;
+}
+
+span.highlight {
+  background-color: #00ff00;
+}
index 524786d548ce09e496382f594920d82bda3beac3..5f55a6ba757c23c135f2ee686583100f2a7a3a24 100644 (file)
@@ -37,7 +37,7 @@ func (p *Prog) writeOutput(srcfile string) {
        // Write Go output: Go input with rewrites of C.xxx to _C_xxx.
        fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n");
        fmt.Fprintf(fgo1, "//line %s:1\n", srcfile);
-       printer.Fprint(fgo1, p.AST, 0, 8);
+       printer.Fprint(fgo1, p.AST, 0, 8, nil);
 
        // Write second Go output: definitions of _C_xxx.
        // In a separate file so that the import of "unsafe" does not
@@ -48,7 +48,7 @@ func (p *Prog) writeOutput(srcfile string) {
 
        for name, def := range p.Typedef {
                fmt.Fprintf(fgo2, "type %s ", name);
-               printer.Fprint(fgo2, def, 0, 8);
+               printer.Fprint(fgo2, def, 0, 8, nil);
                fmt.Fprintf(fgo2, "\n");
        }
        fmt.Fprintf(fgo2, "type _C_void [0]byte\n");
@@ -63,7 +63,7 @@ func (p *Prog) writeOutput(srcfile string) {
        for name, def := range p.Vardef {
                fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s/%s_%s.so\"\n", p.Package, name, name, pkgroot, p.PackagePath, base);
                fmt.Fprintf(fgo2, "var _C_%s ", name);
-               printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}, 0, 8);
+               printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}, 0, 8, nil);
                fmt.Fprintf(fgo2, "\n");
        }
        fmt.Fprintf(fc, "\n");
@@ -74,7 +74,7 @@ func (p *Prog) writeOutput(srcfile string) {
                        Name: &ast.Ident{Value: "_C_"+name},
                        Type: def.Go,
                };
-               printer.Fprint(fgo2, d, 0, 8);
+               printer.Fprint(fgo2, d, 0, 8, nil);
                fmt.Fprintf(fgo2, "\n");
 
                if name == "CString" || name == "GoString" {
index 0916d2ec87dde7896f6c3ed17cf37dbac6da58c0..652d8f1265660a6d26c8d1da0495f257c52587d5 100644 (file)
@@ -53,53 +53,49 @@ import (
 const Pkg = "/pkg/"    // name for auto-generated package documentation tree
 
 
-type delayTime struct {
-       mutex   sync.RWMutex;
-       minutes int;
-}
-
+// ----------------------------------------------------------------------------
+// Support types
 
-func (dt *delayTime) set(minutes int) {
-       dt.mutex.Lock();
-       dt.minutes = minutes;
-       dt.mutex.Unlock();
+// An RWValue wraps a value and permits mutually exclusive
+// access to it and records the time the value was last set.
+type RWValue struct {
+       mutex   sync.RWMutex;
+       value   interface{};
+       timestamp       int64;  // time of last set(), in seconds since epoch
 }
 
 
-func (dt *delayTime) backoff(max int) {
-       dt.mutex.Lock();
-       dt.minutes *= 2;
-       if dt.minutes > max {
-               dt.minutes = max;
-       }
-       dt.mutex.Unlock();
+func (v *RWValue) set(value interface{}) {
+       v.mutex.Lock();
+       v.value = value;
+       v.timestamp = time.Seconds();
+       v.mutex.Unlock();
 }
 
 
-func (dt *delayTime) get() int {
-       dt.mutex.RLock();
-       defer dt.mutex.RUnlock();
-       return dt.minutes;
+func (v *RWValue) get() (interface{}, int64) {
+       v.mutex.RLock();
+       defer v.mutex.RUnlock();
+       return v.value, v.timestamp;
 }
 
 
-type timeStamp struct {
-       mutex   sync.RWMutex;
-       seconds int64;
-}
-
+// ----------------------------------------------------------------------------
+// Globals
 
-func (ts *timeStamp) set() {
-       ts.mutex.Lock();
-       ts.seconds = time.Seconds();
-       ts.mutex.Unlock();
+type delayTime struct {
+       RWValue;
 }
 
 
-func (ts *timeStamp) get() int64 {
-       ts.mutex.RLock();
-       defer ts.mutex.RUnlock();
-       return ts.seconds;
+func (dt *delayTime) backoff(max int) {
+       dt.mutex.Lock();
+       v := dt.value.(int) * 2;
+       if v > max {
+               v = max;
+       }
+       dt.value = v;
+       dt.mutex.Unlock();
 }
 
 
@@ -115,7 +111,7 @@ var (
        syncCmd                         = flag.String("sync", "", "sync command; disabled if empty");
        syncMin                         = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0");
        syncDelay       delayTime;      // actual sync delay in minutes; usually syncDelay == syncMin, but delay may back off exponentially
-       syncTime        timeStamp;      // time of last p4 sync
+       syncTime        RWValue;        // time of last p4 sync
 
        // layout control
        tabwidth        = flag.Int("tabwidth", 4, "tab width");
@@ -132,7 +128,7 @@ func init() {
                goroot = "/home/r/go-release/go";
        }
        flag.StringVar(&goroot, "goroot", goroot, "Go root directory");
-       syncTime.set(); // have a reasonable initial value
+       syncTime.set(nil);      // have a reasonable initial value (time is shown on web page)
 }
 
 
@@ -220,16 +216,62 @@ func parse(path string, mode uint) (*ast.File, *parseErrors) {
 }
 
 
+// ----------------------------------------------------------------------------
+// HTML formatting support
+
+// Styler implements a printer.Styler.
+type Styler struct {
+       highlight string;
+}
+
+
+func (s *Styler) LineTag(line int) (text []byte, tag printer.HtmlTag) {
+       tag = printer.HtmlTag{fmt.Sprintf(`<a id="L%d">`, line), "</a>"};
+       return;
+}
+
+
+func (s *Styler) Comment(c *ast.Comment, line []byte)  (text []byte, tag printer.HtmlTag) {
+       text = line;
+       // minimal syntax-coloring of comments for now - people will want more
+       // (don't do anything more until there's a button to turn it on/off)
+       tag = printer.HtmlTag{`<span class="comment">`, "</span>"};
+       return;
+}
+
+
+func (s *Styler) BasicLit(x *ast.BasicLit)  (text []byte, tag printer.HtmlTag) {
+       text = x.Value;
+       return;
+}
+
+
+func (s *Styler) Ident(id *ast.Ident)  (text []byte, tag printer.HtmlTag) {
+       text = strings.Bytes(id.Value);
+       if s.highlight == id.Value {
+               tag = printer.HtmlTag{"<span class=highlight>", "</span>"};
+       }
+       return;
+}
+
+
+func (s *Styler) Token(tok token.Token)  (text []byte, tag printer.HtmlTag) {
+       text = strings.Bytes(tok.String());
+       return;
+}
+
+
+
 // ----------------------------------------------------------------------------
 // Templates
 
 // Write an AST-node to w; optionally html-escaped.
-func writeNode(w io.Writer, node interface{}, html bool) {
+func writeNode(w io.Writer, node interface{}, html bool, style printer.Styler) {
        mode := printer.UseSpaces;
        if html {
                mode |= printer.GenHTML;
        }
-       printer.Fprint(w, node, mode, *tabwidth);
+       printer.Fprint(w, node, mode, *tabwidth, style);
 }
 
 
@@ -251,9 +293,9 @@ func writeAny(w io.Writer, x interface{}, html bool) {
        case string:
                writeText(w, strings.Bytes(v), html);
        case ast.Decl:
-               writeNode(w, v, html);
+               writeNode(w, v, html, nil);
        case ast.Expr:
-               writeNode(w, v, html);
+               writeNode(w, v, html, nil);
        default:
                if html {
                        var buf bytes.Buffer;
@@ -324,11 +366,13 @@ func readTemplate(name string) *template.Template {
 }
 
 
-var godocHtml *template.Template
-var packageHtml *template.Template
-var packageText *template.Template
-var parseerrorHtml *template.Template
-var parseerrorText *template.Template
+var (
+       godocHtml,
+       packageHtml,
+       packageText,
+       parseerrorHtml,
+       parseerrorText *template.Template;
+)
 
 func readTemplates() {
        // have to delay until after flags processing,
@@ -351,9 +395,10 @@ func servePage(c *http.Conn, title, content interface{}) {
                content         interface{};
        }
 
+       _, ts := syncTime.get();
        d := Data{
                title: title,
-               timestamp: time.SecondsToLocalTime(syncTime.get()).String(),
+               timestamp: time.SecondsToLocalTime(ts).String(),
                content: content,
        };
 
@@ -420,7 +465,7 @@ func serveParseErrors(c *http.Conn, errors *parseErrors) {
 }
 
 
-func serveGoSource(c *http.Conn, filename string) {
+func serveGoSource(c *http.Conn, filename string, style printer.Styler) {
        path := pathutil.Join(goroot, filename);
        prog, errors := parse(path, parser.ParseComments);
        if errors != nil {
@@ -430,7 +475,7 @@ func serveGoSource(c *http.Conn, filename string) {
 
        var buf bytes.Buffer;
        fmt.Fprintln(&buf, "<pre>");
-       writeNode(&buf, prog, true);
+       writeNode(&buf, prog, true, style);
        fmt.Fprintln(&buf, "</pre>");
 
        servePage(c, "Source file " + filename, buf.Bytes());
@@ -455,7 +500,7 @@ func serveFile(c *http.Conn, r *http.Request) {
                serveHtmlDoc(c, r, path);
 
        case ext == ".go":
-               serveGoSource(c, path);
+               serveGoSource(c, path, &Styler{highlight: r.FormValue("h")});
 
        default:
                // TODO:
@@ -654,7 +699,7 @@ func dosync(c *http.Conn, r *http.Request) {
        args := []string{"/bin/sh", "-c", *syncCmd};
        if exec(c, args) {
                // sync succeeded
-               syncTime.set();
+               syncTime.set(nil);
                syncDelay.set(*syncMin);        //  revert to regular sync schedule
        } else {
                // sync failed - back off exponentially, but try at least once a day
@@ -722,14 +767,16 @@ func main() {
                        go func() {
                                for {
                                        dosync(nil, nil);
+                                       _, delay := syncDelay.get();
                                        if *verbose {
-                                               log.Stderrf("next sync in %dmin", syncDelay.get());
+                                               log.Stderrf("next sync in %dmin", delay);
                                        }
-                                       time.Sleep(int64(syncDelay.get())*(60*1e9));
+                                       time.Sleep(int64(delay)*60e9);
                                }
                        }();
                }
 
+               // Start http server.
                if err := http.ListenAndServe(*httpaddr, handler); err != nil {
                        log.Exitf("ListenAndServe %s: %v", *httpaddr, err);
                }
index 3c9131de7b58a92bce84232ae7f0dd021b080832..e4b0762d17d7916a687c9afee01aa7cae3c2a9df 100644 (file)
@@ -91,7 +91,7 @@ func processFile(filename string) os.Error {
        }
 
        var res bytes.Buffer;
-       _, err = printer.Fprint(&res, file, printerMode(), *tabwidth);
+       _, err = printer.Fprint(&res, file, printerMode(), *tabwidth, nil);
        if err != nil {
                return err;
        }
index 0e799eceb9ae24d3ae1a92e6debaf3e875567ee0..07cfee364a00dd739651934b77f6b4efd727e386 100644 (file)
@@ -64,16 +64,20 @@ var (
 var noPos token.Position
 
 
-// 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
+// An HtmlTag specifies a start and end tag.
+type HtmlTag struct {
+       Start, End string;  // empty if tags are absent
+}
 
 
-// A htmlTag specifies a start and end tag.
-type htmlTag struct {
-       start, end string;  // empty if tags are absent
+// A Styler specifies the formatting line tags and elementary Go words.
+// A format consists of text and a (possibly empty) surrounding HTML tag.
+type Styler interface {
+       LineTag(line int) ([]byte, HtmlTag);
+       Comment(c *ast.Comment, line []byte)  ([]byte, HtmlTag);
+       BasicLit(x *ast.BasicLit)  ([]byte, HtmlTag);
+       Ident(id *ast.Ident)  ([]byte, HtmlTag);
+       Token(tok token.Token)  ([]byte, HtmlTag);
 }
 
 
@@ -82,6 +86,7 @@ type printer struct {
        output io.Writer;
        mode uint;
        tabwidth int;
+       style Styler;
        errors chan os.Error;
 
        // Current state
@@ -103,7 +108,6 @@ type printer struct {
        last token.Position;
 
        // HTML support
-       tag htmlTag;  // tag to be used around next item
        lastTaggedLine int;  // last line for which a line tag was written
 
        // The list of comments; or nil.
@@ -111,10 +115,11 @@ type printer struct {
 }
 
 
-func (p *printer) init(output io.Writer, mode uint, tabwidth int) {
+func (p *printer) init(output io.Writer, mode uint, tabwidth int, style Styler) {
        p.output = output;
        p.mode = mode;
        p.tabwidth = tabwidth;
+       p.style = style;
        p.errors = make(chan os.Error);
        p.buffer = make([]whiteSpace, 0, 16);  // whitespace sequences are short
 }
@@ -216,41 +221,42 @@ func (p *printer) writeNewlines(n int) {
 }
 
 
+func (p *printer) writeTaggedItem(data []byte, tag HtmlTag) {
+       // write start tag, if any
+       // (no html-escaping and no p.pos update for tags - use write0)
+       if tag.Start != "" {
+               p.write0(strings.Bytes(tag.Start));
+       }
+       p.write(data);
+       // write end tag, if any
+       if tag.End != "" {
+               p.write0(strings.Bytes(tag.End));
+       }
+}
+
+
 // writeItem writes data at position pos. data is the text corresponding to
 // a single lexical token, but may also be comment text. pos is the actual
 // (or at least very accurately estimated) position of the data in the original
-// source text. The data may be tagged, depending on p.mode and the setLineTag
-// parameter. writeItem updates p.last to the position immediately following
-// the data.
+// source text. If tags are present and GenHTML is set, the tags are written
+// before and after the data. writeItem updates p.last to the position
+// immediately following the data.
 //
-func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) {
+func (p *printer) writeItem(pos token.Position, data []byte, tag HtmlTag) {
        p.pos = pos;
        if debug {
                // do not update p.pos - use write0
                p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)));
        }
        if p.mode & GenHTML != 0 {
-               // 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);
+               // write line tag if on a new line
+               // TODO(gri): should write line tags on each line at the start
+               //            will be more useful (e.g. to show line numbers)
+               if p.style != nil && pos.Line > p.lastTaggedLine {
+                       p.writeTaggedItem(p.style.LineTag(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
-               }
+               p.writeTaggedItem(data, tag);
        } else {
                p.write(data);
        }
@@ -357,7 +363,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
        }
 
        // write comment
-       p.writeItem(comment.Pos(), text, false);
+       var tag HtmlTag;
+       if p.style != nil {
+               text, tag = p.style.Comment(comment, text);
+       }
+       p.writeItem(comment.Pos(), text, tag);
 }
 
 
@@ -475,13 +485,13 @@ func (p *printer) writeWhitespace(n int) {
 // 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);
 
                next := p.pos;  // estimated position of next item
                var data []byte;
+               var tag HtmlTag;
                switch x := f.Interface().(type) {
                case whiteSpace:
                        if x == ignore {
@@ -501,23 +511,40 @@ func (p *printer) print(args ...) {
                        p.buffer = p.buffer[0 : i+1];
                        p.buffer[i] = x;
                case []byte:
+                       // TODO(gri): remove this case once commentList
+                       //            handles comments correctly
                        data = x;
                case string:
+                       // TODO(gri): remove this case once fieldList
+                       //            handles comments correctly
                        data = strings.Bytes(x);
+               case *ast.Ident:
+                       if p.style != nil {
+                               data, tag = p.style.Ident(x);
+                       } else {
+                               data = strings.Bytes(x.Value);
+                       }
+               case *ast.BasicLit:
+                       if p.style != nil {
+                               data, tag = p.style.BasicLit(x);
+                       } else {
+                               data = x.Value;
+                       }
+                       // escape all literals so they pass through unchanged
+                       // (note that valid Go programs cannot contain esc ('\xff')
+                       // bytes since they do not appear in legal UTF-8 sequences)
+                       // TODO(gri): this this more efficiently.
+                       data = strings.Bytes("\xff" + string(data) + "\xff");
                case token.Token:
-                       data = strings.Bytes(x.String());
+                       if p.style != nil {
+                               data, tag = p.style.Token(x);
+                       } else {
+                               data = strings.Bytes(x.String());
+                       }
                case token.Position:
                        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());
                }
@@ -531,8 +558,7 @@ func (p *printer) print(args ...) {
                        // at the end of a file)
                        p.writeNewlines(next.Line - p.pos.Line);
 
-                       p.writeItem(next, data, setLineTag);
-                       setLineTag = false;
+                       p.writeItem(next, data, tag);
                }
        }
 }
@@ -600,6 +626,7 @@ func (p *printer) linebreak(line, min, max int, ws whiteSpace, newSection bool)
 func (p *printer) commentList(list []*ast.Comment) {
        for i, c := range list {
                t := c.Text;
+               // TODO(gri): this needs to be styled like normal comments
                p.print(c.Pos(), t);
                if t[1] == '/' && i+1 < len(list) {
                        //-style comment which is not at the end; print a newline
@@ -823,6 +850,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
                        }
                }
                if isIncomplete {
+                       // TODO(gri): this needs to be styled like normal comments
                        p.print("// contains unexported fields");
                }
 
@@ -845,6 +873,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
                        }
                }
                if isIncomplete {
+                       // TODO(gri): this needs to be styled like normal comments
                        p.print("// contains unexported methods");
                }
 
@@ -960,7 +989,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
                p.print("BadExpr");
 
        case *ast.Ident:
-               p.print(x.Value);
+               p.print(x);
 
        case *ast.BinaryExpr:
                p.binaryExpr(x, prec1);
@@ -991,10 +1020,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
                }
 
        case *ast.BasicLit:
-               // escape all literals so they pass through unchanged
-               // (note that valid Go programs cannot contain esc ('\xff')
-               // bytes since they do not appear in legal UTF-8 sequences)
-               p.print(esc, x.Value, esc);
+               p.print(x);
 
        case *ast.StringList:
                p.stringList(x.Strings);
@@ -1451,7 +1477,7 @@ func (p *printer) spec(spec ast.Spec, n int, context declContext) {
 
 func (p *printer) genDecl(d *ast.GenDecl, context declContext) {
        p.leadComment(d.Doc);
-       p.print(lineTag(d.Pos()), d.Tok, blank);
+       p.print(d.Pos(), d.Tok, blank);
 
        if d.Lparen.IsValid() {
                // group of parenthesized declarations
@@ -1483,7 +1509,7 @@ func (p *printer) isOneLiner(b *ast.BlockStmt) bool {
 
        // test-print the statement and see if it would fit
        var buf bytes.Buffer;
-       _, err := Fprint(&buf, b.List[0], p.mode, p.tabwidth);
+       _, err := Fprint(&buf, b.List[0], p.mode, p.tabwidth, p.style);
        if err != nil {
                return false;  // don't try
        }
@@ -1526,7 +1552,7 @@ func (p *printer) funcBody(b *ast.BlockStmt, isLit bool) {
 
 func (p *printer) funcDecl(d *ast.FuncDecl) {
        p.leadComment(d.Doc);
-       p.print(lineTag(d.Pos()), token.FUNC, blank);
+       p.print(d.Pos(), token.FUNC, blank);
        if recv := d.Recv; recv != nil {
                // method: print receiver
                p.print(token.LPAREN);
@@ -1697,7 +1723,7 @@ var inf = token.Position{Offset: 1<<30, Line: 1<<30}
 // or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing
 // is controlled by the mode and tabwidth parameters.
 //
-func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int) (int, os.Error) {
+func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style Styler) (int, os.Error) {
        // redirect output through a trimmer to eliminate trailing whitespace
        // (Input to a tabwriter must be untrimmed since trailing tabs provide
        // formatting information. The tabwriter could provide trimming
@@ -1721,7 +1747,7 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int) (int, o
 
        // setup printer and print node
        var p printer;
-       p.init(output, mode, tabwidth);
+       p.init(output, mode, tabwidth, style);
        go func() {
                switch n := node.(type) {
                case ast.Expr:
index 9fbc1ad2330bc39ef3cafed5f6d99914d3d0ec5c..1b39b4f44279a71f244f919be96a075424ced6ee 100644 (file)
@@ -62,7 +62,7 @@ func check(t *testing.T, source, golden string, mode checkMode) {
 
        // format source
        var buf bytes.Buffer;
-       if _, err := Fprint(&buf, prog, pmode, tabwidth); err != nil {
+       if _, err := Fprint(&buf, prog, pmode, tabwidth, nil); err != nil {
                t.Error(err);
        }
        res := buf.Bytes();