1
0
mirror of https://github.com/golang/go synced 2024-11-22 01:44:40 -07:00

go/printer:

- 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
This commit is contained in:
Robert Griesemer 2009-10-22 09:41:38 -07:00
parent 3040f067c3
commit 90cc4a5949
7 changed files with 234 additions and 136 deletions

View File

@ -1868,7 +1868,7 @@ math.Sin
</pre> </pre>
<p> <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> </p>
<h3 id="Composite_literals">Composite literals</h3> <h3 id="Composite_literals">Composite literals</h3>
@ -2219,9 +2219,9 @@ p.M0 // ((*p).T0).M0
</pre> </pre>
<font color=red> <span class="alert">
TODO: Specify what happens to receivers. TODO: Specify what happens to receivers.
</font> </span>
<h3 id="Indexes">Indexes</h3> <h3 id="Indexes">Indexes</h3>
@ -2779,7 +2779,7 @@ that is, either a variable, pointer indirection, array or slice indexing
operation, operation,
or a field selector of an addressable struct operand. or a field selector of an addressable struct operand.
A function result variable is not addressable. 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 Given an operand of pointer type, the pointer indirection
operator <code>*</code> retrieves the value pointed operator <code>*</code> retrieves the value pointed
to by the operand. to by the operand.
@ -2881,8 +2881,8 @@ zero value for its type (§<a href="#The_zero_value">The zero value</a>).
</p> </p>
<p> <p>
<font color=red>TODO: Probably in a separate section, communication semantics <span class="alert">TODO: Probably in a separate section, communication semantics
need to be presented regarding send, receive, select, and goroutines.</font> need to be presented regarding send, receive, select, and goroutines.</span>
</p> </p>
<h3 id="Method_expressions">Method expressions</h3> <h3 id="Method_expressions">Method expressions</h3>
@ -3161,11 +3161,11 @@ int8(^1) // same as int8(-2)
</pre> </pre>
<p> <p>
<font color=red> <span class="alert">
TODO: perhaps ^ should be disallowed on non-uints instead of assuming twos complement. 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 Also it may be possible to make typed constants more like variables, at the cost of fewer
overflow etc. errors being caught. overflow etc. errors being caught.
</font> </span>
</p> </p>
@ -3900,11 +3900,11 @@ func complex_f3() (re float, im float) {
</ol> </ol>
<p> <p>
<font color=red> <span class="alert">
TODO: Define when return is required.<br /> TODO: Define when return is required.<br />
TODO: Language about result parameters needs to go into a section on TODO: Language about result parameters needs to go into a section on
function/method invocation<br /> function/method invocation<br />
</font> </span>
</p> </p>
<h3 id="Break_statements">Break statements</h3> <h3 id="Break_statements">Break statements</h3>
@ -3977,7 +3977,7 @@ L:
<p> <p>
is erroneous because the jump to label <code>L</code> skips is erroneous because the jump to label <code>L</code> skips
the creation of <code>v</code>. 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> </p>
<h3 id="Fallthrough_statements">Fallthrough statements</h3> <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. <code>unsafe.Alignof(x[0])</code>, but at least 1.
</ol> </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> <ul>
<li><font color=red>Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</font></li> <li><span class="alert">Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</span></li>
<li><font color=red>Gccgo does not implement the blank identifier.</font></li> <li><span class="alert">Gccgo does not implement the blank identifier.</span></li>
<li><font color=red>Method expressions are not implemented.</font></li> <li><span class="alert">Method expressions are not implemented.</span></li>
</ul> </ul>

View 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 { code, .code {
font-size: 100%; font-size: 100%;
font-family: monospace; font-family: monospace;
@ -19,11 +28,15 @@ pre.grammar {
} }
p.rule { p.rule {
font-style: italic font-style: italic;
} }
span.event { span.event {
font-style: italic font-style: italic;
}
span.alert {
color: #ff0000;
} }
body { body {
@ -41,7 +54,7 @@ h2 {
border-top:1px solid #36C; border-top:1px solid #36C;
} }
pre{ pre {
font-size: 9pt; font-size: 9pt;
background-color: #f8f8ff; background-color: #f8f8ff;
margin: 1em 0 0 0; margin: 1em 0 0 0;
@ -59,8 +72,9 @@ li pre {
margin: 0.5em 0px 1em 0px; 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 { div#content {
margin-left: 20%; 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;
}

View File

@ -37,7 +37,7 @@ func (p *Prog) writeOutput(srcfile string) {
// Write Go output: Go input with rewrites of C.xxx to _C_xxx. // 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, "// Created by cgo - DO NOT EDIT\n");
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile); 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. // Write second Go output: definitions of _C_xxx.
// In a separate file so that the import of "unsafe" does not // 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 { for name, def := range p.Typedef {
fmt.Fprintf(fgo2, "type %s ", name); 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, "\n");
} }
fmt.Fprintf(fgo2, "type _C_void [0]byte\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 { 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(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); 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(fgo2, "\n");
} }
fmt.Fprintf(fc, "\n"); fmt.Fprintf(fc, "\n");
@ -74,7 +74,7 @@ func (p *Prog) writeOutput(srcfile string) {
Name: &ast.Ident{Value: "_C_"+name}, Name: &ast.Ident{Value: "_C_"+name},
Type: def.Go, Type: def.Go,
}; };
printer.Fprint(fgo2, d, 0, 8); printer.Fprint(fgo2, d, 0, 8, nil);
fmt.Fprintf(fgo2, "\n"); fmt.Fprintf(fgo2, "\n");
if name == "CString" || name == "GoString" { if name == "CString" || name == "GoString" {

View File

@ -53,56 +53,52 @@ import (
const Pkg = "/pkg/" // name for auto-generated package documentation tree const Pkg = "/pkg/" // name for auto-generated package documentation tree
type delayTime struct { // ----------------------------------------------------------------------------
// Support types
// 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; mutex sync.RWMutex;
minutes int; value interface{};
timestamp int64; // time of last set(), in seconds since epoch
} }
func (dt *delayTime) set(minutes int) { func (v *RWValue) set(value interface{}) {
dt.mutex.Lock(); v.mutex.Lock();
dt.minutes = minutes; v.value = value;
dt.mutex.Unlock(); v.timestamp = time.Seconds();
v.mutex.Unlock();
}
func (v *RWValue) get() (interface{}, int64) {
v.mutex.RLock();
defer v.mutex.RUnlock();
return v.value, v.timestamp;
}
// ----------------------------------------------------------------------------
// Globals
type delayTime struct {
RWValue;
} }
func (dt *delayTime) backoff(max int) { func (dt *delayTime) backoff(max int) {
dt.mutex.Lock(); dt.mutex.Lock();
dt.minutes *= 2; v := dt.value.(int) * 2;
if dt.minutes > max { if v > max {
dt.minutes = max; v = max;
} }
dt.value = v;
dt.mutex.Unlock(); dt.mutex.Unlock();
} }
func (dt *delayTime) get() int {
dt.mutex.RLock();
defer dt.mutex.RUnlock();
return dt.minutes;
}
type timeStamp struct {
mutex sync.RWMutex;
seconds int64;
}
func (ts *timeStamp) set() {
ts.mutex.Lock();
ts.seconds = time.Seconds();
ts.mutex.Unlock();
}
func (ts *timeStamp) get() int64 {
ts.mutex.RLock();
defer ts.mutex.RUnlock();
return ts.seconds;
}
var ( var (
verbose = flag.Bool("v", false, "verbose mode"); verbose = flag.Bool("v", false, "verbose mode");
@ -115,7 +111,7 @@ var (
syncCmd = flag.String("sync", "", "sync command; disabled if empty"); syncCmd = flag.String("sync", "", "sync command; disabled if empty");
syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0"); 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 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 // layout control
tabwidth = flag.Int("tabwidth", 4, "tab width"); tabwidth = flag.Int("tabwidth", 4, "tab width");
@ -132,7 +128,7 @@ func init() {
goroot = "/home/r/go-release/go"; goroot = "/home/r/go-release/go";
} }
flag.StringVar(&goroot, "goroot", goroot, "Go root directory"); 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 // Templates
// Write an AST-node to w; optionally html-escaped. // 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; mode := printer.UseSpaces;
if html { if html {
mode |= printer.GenHTML; 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: case string:
writeText(w, strings.Bytes(v), html); writeText(w, strings.Bytes(v), html);
case ast.Decl: case ast.Decl:
writeNode(w, v, html); writeNode(w, v, html, nil);
case ast.Expr: case ast.Expr:
writeNode(w, v, html); writeNode(w, v, html, nil);
default: default:
if html { if html {
var buf bytes.Buffer; var buf bytes.Buffer;
@ -324,11 +366,13 @@ func readTemplate(name string) *template.Template {
} }
var godocHtml *template.Template var (
var packageHtml *template.Template godocHtml,
var packageText *template.Template packageHtml,
var parseerrorHtml *template.Template packageText,
var parseerrorText *template.Template parseerrorHtml,
parseerrorText *template.Template;
)
func readTemplates() { func readTemplates() {
// have to delay until after flags processing, // have to delay until after flags processing,
@ -351,9 +395,10 @@ func servePage(c *http.Conn, title, content interface{}) {
content interface{}; content interface{};
} }
_, ts := syncTime.get();
d := Data{ d := Data{
title: title, title: title,
timestamp: time.SecondsToLocalTime(syncTime.get()).String(), timestamp: time.SecondsToLocalTime(ts).String(),
content: content, 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); path := pathutil.Join(goroot, filename);
prog, errors := parse(path, parser.ParseComments); prog, errors := parse(path, parser.ParseComments);
if errors != nil { if errors != nil {
@ -430,7 +475,7 @@ func serveGoSource(c *http.Conn, filename string) {
var buf bytes.Buffer; var buf bytes.Buffer;
fmt.Fprintln(&buf, "<pre>"); fmt.Fprintln(&buf, "<pre>");
writeNode(&buf, prog, true); writeNode(&buf, prog, true, style);
fmt.Fprintln(&buf, "</pre>"); fmt.Fprintln(&buf, "</pre>");
servePage(c, "Source file " + filename, buf.Bytes()); servePage(c, "Source file " + filename, buf.Bytes());
@ -455,7 +500,7 @@ func serveFile(c *http.Conn, r *http.Request) {
serveHtmlDoc(c, r, path); serveHtmlDoc(c, r, path);
case ext == ".go": case ext == ".go":
serveGoSource(c, path); serveGoSource(c, path, &Styler{highlight: r.FormValue("h")});
default: default:
// TODO: // TODO:
@ -654,7 +699,7 @@ func dosync(c *http.Conn, r *http.Request) {
args := []string{"/bin/sh", "-c", *syncCmd}; args := []string{"/bin/sh", "-c", *syncCmd};
if exec(c, args) { if exec(c, args) {
// sync succeeded // sync succeeded
syncTime.set(); syncTime.set(nil);
syncDelay.set(*syncMin); // revert to regular sync schedule syncDelay.set(*syncMin); // revert to regular sync schedule
} else { } else {
// sync failed - back off exponentially, but try at least once a day // sync failed - back off exponentially, but try at least once a day
@ -722,14 +767,16 @@ func main() {
go func() { go func() {
for { for {
dosync(nil, nil); dosync(nil, nil);
_, delay := syncDelay.get();
if *verbose { 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 { if err := http.ListenAndServe(*httpaddr, handler); err != nil {
log.Exitf("ListenAndServe %s: %v", *httpaddr, err); log.Exitf("ListenAndServe %s: %v", *httpaddr, err);
} }

View File

@ -91,7 +91,7 @@ func processFile(filename string) os.Error {
} }
var res bytes.Buffer; var res bytes.Buffer;
_, err = printer.Fprint(&res, file, printerMode(), *tabwidth); _, err = printer.Fprint(&res, file, printerMode(), *tabwidth, nil);
if err != nil { if err != nil {
return err; return err;
} }

View File

@ -64,16 +64,20 @@ var (
var noPos token.Position var noPos token.Position
// A lineTag is a token.Position that is used to print // An HtmlTag specifies a start and end tag.
// line tag id's of the form "L%d" where %d stands for type HtmlTag struct {
// the line indicated by position. Start, End string; // empty if tags are absent
// }
type lineTag token.Position
// A htmlTag specifies a start and end tag. // A Styler specifies the formatting line tags and elementary Go words.
type htmlTag struct { // A format consists of text and a (possibly empty) surrounding HTML tag.
start, end string; // empty if tags are absent 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; output io.Writer;
mode uint; mode uint;
tabwidth int; tabwidth int;
style Styler;
errors chan os.Error; errors chan os.Error;
// Current state // Current state
@ -103,7 +108,6 @@ type printer struct {
last token.Position; last token.Position;
// HTML support // HTML support
tag htmlTag; // tag to be used around next item
lastTaggedLine int; // last line for which a line tag was written lastTaggedLine int; // last line for which a line tag was written
// The list of comments; or nil. // 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.output = output;
p.mode = mode; p.mode = mode;
p.tabwidth = tabwidth; p.tabwidth = tabwidth;
p.style = style;
p.errors = make(chan os.Error); p.errors = make(chan os.Error);
p.buffer = make([]whiteSpace, 0, 16); // whitespace sequences are short 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 // 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 // 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 // (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 // source text. If tags are present and GenHTML is set, the tags are written
// parameter. writeItem updates p.last to the position immediately following // before and after the data. writeItem updates p.last to the position
// the data. // 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; 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)));
} }
if p.mode & GenHTML != 0 { if p.mode & GenHTML != 0 {
// no html-escaping and no p.pos update for tags - use write0 // write line tag if on a new line
if setLineTag && pos.Line > p.lastTaggedLine { // TODO(gri): should write line tags on each line at the start
// id's must be unique within a document: set // will be more useful (e.g. to show line numbers)
// line tag only if line number has increased if p.style != nil && pos.Line > p.lastTaggedLine {
// (note: for now write complete start and end p.writeTaggedItem(p.style.LineTag(pos.Line));
// 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; p.lastTaggedLine = pos.Line;
} }
// write start tag, if any p.writeTaggedItem(data, tag);
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 { } else {
p.write(data); p.write(data);
} }
@ -357,7 +363,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
} }
// write 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. // 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);
next := p.pos; // estimated position of next item next := p.pos; // estimated position of next item
var data []byte; var data []byte;
var tag HtmlTag;
switch x := f.Interface().(type) { switch x := f.Interface().(type) {
case whiteSpace: case whiteSpace:
if x == ignore { if x == ignore {
@ -501,23 +511,40 @@ func (p *printer) print(args ...) {
p.buffer = p.buffer[0 : i+1]; p.buffer = p.buffer[0 : i+1];
p.buffer[i] = x; p.buffer[i] = x;
case []byte: case []byte:
// TODO(gri): remove this case once commentList
// handles comments correctly
data = x; data = x;
case string: case string:
// TODO(gri): remove this case once fieldList
// handles comments correctly
data = strings.Bytes(x); 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: 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: case token.Position:
if x.IsValid() { 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());
} }
@ -531,8 +558,7 @@ func (p *printer) print(args ...) {
// at the end of a file) // at the end of a file)
p.writeNewlines(next.Line - p.pos.Line); p.writeNewlines(next.Line - p.pos.Line);
p.writeItem(next, data, setLineTag); p.writeItem(next, data, tag);
setLineTag = false;
} }
} }
} }
@ -600,6 +626,7 @@ func (p *printer) linebreak(line, min, max int, ws whiteSpace, newSection bool)
func (p *printer) commentList(list []*ast.Comment) { func (p *printer) commentList(list []*ast.Comment) {
for i, c := range list { for i, c := range list {
t := c.Text; t := c.Text;
// TODO(gri): this needs to be styled like normal comments
p.print(c.Pos(), t); p.print(c.Pos(), t);
if t[1] == '/' && i+1 < len(list) { if t[1] == '/' && i+1 < len(list) {
//-style comment which is not at the end; print a newline //-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 { if isIncomplete {
// TODO(gri): this needs to be styled like normal comments
p.print("// contains unexported fields"); p.print("// contains unexported fields");
} }
@ -845,6 +873,7 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
} }
} }
if isIncomplete { if isIncomplete {
// TODO(gri): this needs to be styled like normal comments
p.print("// contains unexported methods"); p.print("// contains unexported methods");
} }
@ -960,7 +989,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.print("BadExpr"); p.print("BadExpr");
case *ast.Ident: case *ast.Ident:
p.print(x.Value); p.print(x);
case *ast.BinaryExpr: case *ast.BinaryExpr:
p.binaryExpr(x, prec1); p.binaryExpr(x, prec1);
@ -991,10 +1020,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
} }
case *ast.BasicLit: case *ast.BasicLit:
// escape all literals so they pass through unchanged p.print(x);
// (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);
case *ast.StringList: case *ast.StringList:
p.stringList(x.Strings); 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) { func (p *printer) genDecl(d *ast.GenDecl, context declContext) {
p.leadComment(d.Doc); p.leadComment(d.Doc);
p.print(lineTag(d.Pos()), d.Tok, blank); p.print(d.Pos(), d.Tok, blank);
if d.Lparen.IsValid() { if d.Lparen.IsValid() {
// group of parenthesized declarations // 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 // test-print the statement and see if it would fit
var buf bytes.Buffer; 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 { if err != nil {
return false; // don't try 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) { func (p *printer) funcDecl(d *ast.FuncDecl) {
p.leadComment(d.Doc); 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 { if recv := d.Recv; recv != nil {
// method: print receiver // method: print receiver
p.print(token.LPAREN); 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 // or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing
// is controlled by the mode and tabwidth parameters. // 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 // redirect output through a trimmer to eliminate trailing whitespace
// (Input to a tabwriter must be untrimmed since trailing tabs provide // (Input to a tabwriter must be untrimmed since trailing tabs provide
// formatting information. The tabwriter could provide trimming // 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 // setup printer and print node
var p printer; var p printer;
p.init(output, mode, tabwidth); p.init(output, mode, tabwidth, style);
go func() { go func() {
switch n := node.(type) { switch n := node.(type) {
case ast.Expr: case ast.Expr:

View File

@ -62,7 +62,7 @@ func check(t *testing.T, source, golden string, mode checkMode) {
// format source // format source
var buf bytes.Buffer; 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); t.Error(err);
} }
res := buf.Bytes(); res := buf.Bytes();