mirror of
https://github.com/golang/go
synced 2024-11-25 04:57:56 -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:
parent
3040f067c3
commit
90cc4a5949
@ -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>
|
||||
|
@ -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 {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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" {
|
||||
|
@ -53,56 +53,52 @@ import (
|
||||
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;
|
||||
minutes int;
|
||||
value interface{};
|
||||
timestamp int64; // time of last set(), in seconds since epoch
|
||||
}
|
||||
|
||||
|
||||
func (dt *delayTime) set(minutes int) {
|
||||
dt.mutex.Lock();
|
||||
dt.minutes = minutes;
|
||||
dt.mutex.Unlock();
|
||||
func (v *RWValue) set(value interface{}) {
|
||||
v.mutex.Lock();
|
||||
v.value = value;
|
||||
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) {
|
||||
dt.mutex.Lock();
|
||||
dt.minutes *= 2;
|
||||
if dt.minutes > max {
|
||||
dt.minutes = max;
|
||||
v := dt.value.(int) * 2;
|
||||
if v > max {
|
||||
v = max;
|
||||
}
|
||||
dt.value = v;
|
||||
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 (
|
||||
verbose = flag.Bool("v", false, "verbose mode");
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
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:
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user