Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(541)

Delta Between Two Patch Sets: src/cmd/godoc/godoc.go

Issue 3699041: code review 3699041: godoc: support for regular expression full text search (Closed)
Left Patch Set: code review 3699041: godoc: support for regular expression full text search Created 14 years, 2 months ago
Right Patch Set: code review 3699041: godoc: support for regular expression full text search Created 14 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/godoc/format.go ('k') | src/cmd/godoc/index.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2009 The Go Authors. All rights reserved. 1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package main 5 package main
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "flag" 9 "flag"
10 "fmt" 10 "fmt"
11 "go/ast" 11 "go/ast"
12 "go/doc" 12 "go/doc"
13 "go/parser" 13 "go/parser"
14 "go/printer" 14 "go/printer"
15 "go/scanner"
16 "go/token" 15 "go/token"
17 "http" 16 "http"
18 "io" 17 "io"
19 "io/ioutil" 18 "io/ioutil"
20 "log" 19 "log"
21 "os" 20 "os"
22 pathutil "path" 21 pathutil "path"
23 "regexp" 22 "regexp"
24 "runtime" 23 "runtime"
25 "sort" 24 "sort"
26 "strconv"
27 "strings" 25 "strings"
28 "template" 26 "template"
29 "time" 27 "time"
30 "utf8" 28 "utf8"
31 ) 29 )
32 30
33 31
34 // ---------------------------------------------------------------------------- 32 // ----------------------------------------------------------------------------
35 // Globals 33 // Globals
36 34
(...skipping 22 matching lines...) Expand all
59 goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory ") 57 goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory ")
60 testDir = flag.String("testdir", "", "Go root subdirectory - for tes ting only (faster startups)") 58 testDir = flag.String("testdir", "", "Go root subdirectory - for tes ting only (faster startups)")
61 path = flag.String("path", "", "additional package directories (c olon-separated)") 59 path = flag.String("path", "", "additional package directories (c olon-separated)")
62 filter = flag.String("filter", "", "filter file containing permitte d package directory paths") 60 filter = flag.String("filter", "", "filter file containing permitte d package directory paths")
63 filterMin = flag.Int("filter_minutes", 0, "filter file update interval in minutes; disabled if <= 0") 61 filterMin = flag.Int("filter_minutes", 0, "filter file update interval in minutes; disabled if <= 0")
64 filterDelay delayTime // actual filter update interval in minutes; usual ly filterDelay == filterMin, but filterDelay may back off exponentially 62 filterDelay delayTime // actual filter update interval in minutes; usual ly filterDelay == filterMin, but filterDelay may back off exponentially
65 63
66 // layout control 64 // layout control
67 tabwidth = flag.Int("tabwidth", 4, "tab width") 65 tabwidth = flag.Int("tabwidth", 4, "tab width")
68 showTimestamps = flag.Bool("timestamps", true, "show timestamps with dir ectory listings") 66 showTimestamps = flag.Bool("timestamps", true, "show timestamps with dir ectory listings")
69 » fulltextIndex = flag.Bool("fulltext", false, "build full text index for search queries") 67 » fulltextIndex = flag.Bool("fulltext", false, "build full text index for regular expression queries")
70 » regexpQueries = flag.Bool("regexp", false, "interpret queries as regula r expressions for full text search")
71 68
72 // file system mapping 69 // file system mapping
73 fsMap Mapping // user-defined mapping 70 fsMap Mapping // user-defined mapping
74 fsTree RWValue // *Directory tree of packages, updated with each syn c 71 fsTree RWValue // *Directory tree of packages, updated with each syn c
75 pathFilter RWValue // filter used when building fsMap directory trees 72 pathFilter RWValue // filter used when building fsMap directory trees
76 fsModified RWValue // timestamp of last call to invalidateIndex 73 fsModified RWValue // timestamp of last call to invalidateIndex
77 74
78 // http handlers 75 // http handlers
79 fileServer http.Handler // default file server 76 fileServer http.Handler // default file server
80 cmdHandler httpHandler 77 cmdHandler httpHandler
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 // no user-defined mapping found; use default mapping 262 // no user-defined mapping found; use default mapping
266 relpath = path[len(prefix):] 263 relpath = path[len(prefix):]
267 } 264 }
268 } 265 }
269 // Only if path is an invalid absolute path is relpath == "" 266 // Only if path is an invalid absolute path is relpath == ""
270 // at this point. This should never happen since absolute paths 267 // at this point. This should never happen since absolute paths
271 // are only created via godoc for files that do exist. However, 268 // are only created via godoc for files that do exist. However,
272 // it is ok to return ""; it will simply provide a link to the 269 // it is ok to return ""; it will simply provide a link to the
273 // top of the pkg or src directories. 270 // top of the pkg or src directories.
274 return relpath 271 return relpath
275 }
276
277
278 // ----------------------------------------------------------------------------
279 // HTML formatting support
280
281 // aposescaper implements an io.Writer that escapes single quotes:
282 // ' is written as \' . It is used to escape text such that it can
283 // be used as the content of single-quoted string literals.
284 type aposescaper struct {
285 w io.Writer
286 }
287
288
289 func (e *aposescaper) Write(p []byte) (n int, err os.Error) {
290 backslash := []byte{'\\'}
291 var i, m int
292 for j, b := range p {
293 if b == '\'' {
294 m, err = e.w.Write(p[i:j])
295 n += m
296 if err != nil {
297 return
298 }
299 _, err = e.w.Write(backslash)
300 if err != nil {
301 return
302 }
303 i = j
304 }
305 }
306 m, err = e.w.Write(p[i:])
307 n += m
308 return
309 }
310
311
312 // Styler implements a printer.Styler.
313 type Styler struct {
314 linetags bool
315 highlight string
316 objmap map[*ast.Object]int
317 idcount int
318 }
319
320
321 func newStyler(highlight string) *Styler {
322 return &Styler{true, highlight, make(map[*ast.Object]int), 0}
323 }
324
325
326 // identId returns a number >= 0 identifying the *ast.Object
327 // denoted by name. If no object is denoted, the result is < 0.
328 //
329 // TODO(gri): Consider making this a mapping from popup info
330 // (for that name) to id, instead of *ast.Object
331 // to id. If a lot of the popup info is the same
332 // (e.g. type information), this will reduce the
333 // size of the html generated.
334 func (s *Styler) identId(name *ast.Ident) int {
335 obj := name.Obj
336 if obj == nil || s.objmap == nil {
337 return -1
338 }
339 id, found := s.objmap[obj]
340 if !found {
341 // first occurence
342 id = s.idcount
343 s.objmap[obj] = id
344 s.idcount++
345 }
346 return id
347 }
348
349
350 // writeObjInfo writes the popup info corresponding to obj to w.
351 // The text is HTML-escaped and does not contain single quotes.
352 func writeObjInfo(w io.Writer, fset *token.FileSet, obj *ast.Object) {
353 // for now, show object kind and name; eventually
354 // do something more interesting (show declaration,
355 // for instance)
356 if obj.Kind != ast.Bad {
357 fmt.Fprintf(w, "%s ", obj.Kind)
358 }
359 template.HTMLEscape(w, []byte(obj.Name))
360 // show type if we know it
361 if obj.Type != nil && obj.Type.Expr != nil {
362 fmt.Fprint(w, " ")
363 writeNode(&aposescaper{w}, fset, obj.Type.Expr, true, &defaultSt yler)
364 }
365 }
366
367
368 // idList returns a Javascript array (source) with identifier popup
369 // information: The i'th array entry is a single-quoted string with
370 // the popup information for an identifier x with s.identId(x) == i,
371 // for 0 <= i < s.idcount.
372 func (s *Styler) idList(fset *token.FileSet) []byte {
373 var buf bytes.Buffer
374 buf.WriteString("[\n")
375
376 if s.idcount > 0 {
377 // invert objmap: create an array [id]obj from map[obj]id
378 a := make([]*ast.Object, s.idcount)
379 for obj, id := range s.objmap {
380 a[id] = obj
381 }
382
383 // for each id, print object info as single-quoted Javascript st ring
384 for id, obj := range a {
385 printIndex := false // enable for debugging (but longer html)
386 if printIndex {
387 fmt.Fprintf(&buf, "/* %4d */ ", id)
388 }
389 fmt.Fprint(&buf, "'")
390 writeObjInfo(&buf, fset, obj)
391 fmt.Fprint(&buf, "',\n")
392 }
393 }
394
395 buf.WriteString("]\n")
396 return buf.Bytes()
397 }
398
399
400 // Use the defaultStyler when there is no specific styler.
401 // The defaultStyler does not emit line tags since they may
402 // interfere with tags emitted by templates.
403 // TODO(gri): Should emit line tags at the beginning of a line;
404 // never in the middle of code.
405 var defaultStyler Styler
406
407
408 func (s *Styler) LineTag(line int) (text []byte, tag printer.HTMLTag) {
409 if s.linetags {
410 tag = printer.HTMLTag{fmt.Sprintf(`<a id="L%d">`, line), "</a>"}
411 }
412 return
413 }
414
415
416 func (s *Styler) Comment(c *ast.Comment, line []byte) (text []byte, tag printer. HTMLTag) {
417 text = line
418 // minimal syntax-coloring of comments for now - people will want more
419 // (don't do anything more until there's a button to turn it on/off)
420 tag = printer.HTMLTag{`<span class="comment">`, "</span>"}
421 return
422 }
423
424
425 func (s *Styler) BasicLit(x *ast.BasicLit) (text []byte, tag printer.HTMLTag) {
426 text = x.Value
427 return
428 }
429
430
431 func (s *Styler) Ident(name *ast.Ident) (text []byte, tag printer.HTMLTag) {
432 text = []byte(name.Name)
433 var str string
434 if id := s.identId(name); id >= 0 {
435 str = fmt.Sprintf(` id="%d"`, id)
436 }
437 if s.highlight == name.Name {
438 str += ` class="highlight"`
439 }
440 if str != "" {
441 tag = printer.HTMLTag{"<span" + str + ">", "</span>"}
442 }
443 return
444 }
445
446
447 func (s *Styler) Token(tok token.Token) (text []byte, tag printer.HTMLTag) {
448 text = []byte(tok.String())
449 return
450 } 272 }
451 273
452 274
453 // ---------------------------------------------------------------------------- 275 // ----------------------------------------------------------------------------
454 // Tab conversion 276 // Tab conversion
455 277
456 var spaces = []byte(" ") // 16 spaces seems like a good number 278 var spaces = []byte(" ") // 16 spaces seems like a good number
457 279
458 const ( 280 const (
459 indenting = iota 281 indenting = iota
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
519 _, err = p.output.Write(data[pos:]) 341 _, err = p.output.Write(data[pos:])
520 } 342 }
521 return 343 return
522 } 344 }
523 345
524 346
525 // ---------------------------------------------------------------------------- 347 // ----------------------------------------------------------------------------
526 // Templates 348 // Templates
527 349
528 // Write an AST-node to w; optionally html-escaped. 350 // Write an AST-node to w; optionally html-escaped.
529 func writeNode(w io.Writer, fset *token.FileSet, node interface{}, html bool, st yler printer.Styler) { 351 func writeNode(w io.Writer, fset *token.FileSet, node interface{}, html bool) {
530 mode := printer.TabIndent | printer.UseSpaces 352 mode := printer.TabIndent | printer.UseSpaces
531 if html { 353 if html {
532 mode |= printer.GenHTML 354 mode |= printer.GenHTML
533 } 355 }
534 // convert trailing tabs into spaces using a tconv filter 356 // convert trailing tabs into spaces using a tconv filter
535 // to ensure a good outcome in most browsers (there may still 357 // to ensure a good outcome in most browsers (there may still
536 // be tabs in comments and strings, but converting those into 358 // be tabs in comments and strings, but converting those into
537 // the right number of spaces is much harder) 359 // the right number of spaces is much harder)
538 » (&printer.Config{mode, *tabwidth, styler}).Fprint(&tconv{output: w}, fse t, node) 360 » (&printer.Config{mode, *tabwidth, nil}).Fprint(&tconv{output: w}, fset, node)
539 } 361 }
540 362
541 363
542 // Write text to w; optionally html-escaped. 364 // Write text to w; optionally html-escaped.
543 func writeText(w io.Writer, text []byte, html bool) { 365 func writeText(w io.Writer, text []byte, html bool) {
544 if html { 366 if html {
545 template.HTMLEscape(w, text) 367 template.HTMLEscape(w, text)
546 return 368 return
547 } 369 }
548 w.Write(text) 370 w.Write(text)
549 } 371 }
550 372
551 373
552 // Write anything to w; optionally html-escaped. 374 // Write anything to w; optionally html-escaped.
553 func writeAny(w io.Writer, fset *token.FileSet, html bool, x interface{}) { 375 func writeAny(w io.Writer, fset *token.FileSet, html bool, x interface{}) {
554 switch v := x.(type) { 376 switch v := x.(type) {
555 case []byte: 377 case []byte:
556 writeText(w, v, html) 378 writeText(w, v, html)
557 case string: 379 case string:
558 writeText(w, []byte(v), html) 380 writeText(w, []byte(v), html)
559 case ast.Decl, ast.Expr, ast.Stmt, *ast.File: 381 case ast.Decl, ast.Expr, ast.Stmt, *ast.File:
560 » » writeNode(w, fset, x, html, &defaultStyler) 382 » » writeNode(w, fset, x, html)
561 default: 383 default:
562 if html { 384 if html {
563 var buf bytes.Buffer 385 var buf bytes.Buffer
564 fmt.Fprint(&buf, x) 386 fmt.Fprint(&buf, x)
565 writeText(w, buf.Bytes(), true) 387 writeText(w, buf.Bytes(), true)
566 } else { 388 } else {
567 fmt.Fprint(w, x) 389 fmt.Fprint(w, x)
568 } 390 }
569 } 391 }
570 } 392 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
615 var buf bytes.Buffer 437 var buf bytes.Buffer
616 writeAny(&buf, fileset(x), false, x[0]) 438 writeAny(&buf, fileset(x), false, x[0])
617 template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes())))) 439 template.HTMLEscape(w, []byte(http.URLEscape(string(buf.Bytes()))))
618 } 440 }
619 441
620 442
621 // Template formatter for the various "url-xxx" formats excluding url-esc. 443 // Template formatter for the various "url-xxx" formats excluding url-esc.
622 func urlFmt(w io.Writer, format string, x ...interface{}) { 444 func urlFmt(w io.Writer, format string, x ...interface{}) {
623 var path string 445 var path string
624 var line int 446 var line int
447 var low, high int // selection
625 448
626 // determine path and position info, if any 449 // determine path and position info, if any
627 type positioner interface { 450 type positioner interface {
628 Pos() token.Pos 451 Pos() token.Pos
452 End() token.Pos
629 } 453 }
630 switch t := x[0].(type) { 454 switch t := x[0].(type) {
631 case string: 455 case string:
632 path = t 456 path = t
633 case positioner: 457 case positioner:
634 » » pos := t.Pos() 458 » » fset := fileset(x)
635 » » if pos.IsValid() { 459 » » if p := t.Pos(); p.IsValid() {
636 » » » pos := fileset(x).Position(pos) 460 » » » pos := fset.Position(p)
637 path = pos.Filename 461 path = pos.Filename
638 line = pos.Line 462 line = pos.Line
463 low = pos.Offset
464 }
465 if p := t.End(); p.IsValid() {
466 high = fset.Position(p).Offset
639 } 467 }
640 default: 468 default:
641 // we should never reach here, but be resilient 469 // we should never reach here, but be resilient
642 // and assume the position is invalid (empty path, 470 // and assume the position is invalid (empty path,
643 // and line 0) 471 // and line 0)
644 log.Printf("INTERNAL ERROR: urlFmt(%s) without a string or posit ioner", format) 472 log.Printf("INTERNAL ERROR: urlFmt(%s) without a string or posit ioner", format)
645 } 473 }
646 474
647 // map path 475 // map path
648 relpath := relativePath(path) 476 relpath := relativePath(path)
649 477
650 // convert to relative URLs so that they can also 478 // convert to relative URLs so that they can also
651 // be used as relative file names in .txt templates 479 // be used as relative file names in .txt templates
652 switch format { 480 switch format {
653 default: 481 default:
654 // we should never reach here, but be resilient 482 // we should never reach here, but be resilient
655 // and assume the url-pkg format instead 483 // and assume the url-pkg format instead
656 log.Printf("INTERNAL ERROR: urlFmt(%s)", format) 484 log.Printf("INTERNAL ERROR: urlFmt(%s)", format)
657 fallthrough 485 fallthrough
658 case "url-pkg": 486 case "url-pkg":
659 // because of the irregular mapping under goroot 487 // because of the irregular mapping under goroot
660 // we need to correct certain relative paths 488 // we need to correct certain relative paths
661 if strings.HasPrefix(relpath, "src/pkg/") { 489 if strings.HasPrefix(relpath, "src/pkg/") {
662 relpath = relpath[len("src/pkg/"):] 490 relpath = relpath[len("src/pkg/"):]
663 } 491 }
664 template.HTMLEscape(w, []byte(pkgHandler.pattern[1:]+relpath)) / / remove trailing '/' for relative URL 492 template.HTMLEscape(w, []byte(pkgHandler.pattern[1:]+relpath)) / / remove trailing '/' for relative URL
665 case "url-src": 493 case "url-src":
666 template.HTMLEscape(w, []byte(relpath)) 494 template.HTMLEscape(w, []byte(relpath))
667 case "url-pos": 495 case "url-pos":
496 template.HTMLEscape(w, []byte(relpath))
497 // selection ranges are of form "s=low:high"
498 if low < high {
499 fmt.Fprintf(w, "?s=%d:%d", low, high)
500 // if we have a selection, position the page
501 // such that the selection is a bit below the top
502 line -= 10
503 if line < 1 {
504 line = 1
505 }
506 }
668 // line id's in html-printed source are of the 507 // line id's in html-printed source are of the
669 // form "L%d" where %d stands for the line number 508 // form "L%d" where %d stands for the line number
670 » » template.HTMLEscape(w, []byte(relpath)) 509 » » if line > 0 {
671 » » fmt.Fprintf(w, "#L%d", line) 510 » » » fmt.Fprintf(w, "#L%d", line)
511 » » }
672 } 512 }
673 } 513 }
674 514
675 515
676 // The strings in infoKinds must be properly html-escaped. 516 // The strings in infoKinds must be properly html-escaped.
677 var infoKinds = [nKinds]string{ 517 var infoKinds = [nKinds]string{
678 PackageClause: "package&nbsp;clause", 518 PackageClause: "package&nbsp;clause",
679 ImportDecl: "import&nbsp;decl", 519 ImportDecl: "import&nbsp;decl",
680 ConstDecl: "const&nbsp;decl", 520 ConstDecl: "const&nbsp;decl",
681 TypeDecl: "type&nbsp;decl", 521 TypeDecl: "type&nbsp;decl",
(...skipping 26 matching lines...) Expand all
708 line = 0 548 line = 0
709 } 549 }
710 } 550 }
711 fmt.Fprintf(w, "%d", line) 551 fmt.Fprintf(w, "%d", line)
712 } 552 }
713 553
714 554
715 // Template formatter for "infoSnippet" format. 555 // Template formatter for "infoSnippet" format.
716 func infoSnippetFmt(w io.Writer, format string, x ...interface{}) { 556 func infoSnippetFmt(w io.Writer, format string, x ...interface{}) {
717 info := x[0].(SpotInfo) 557 info := x[0].(SpotInfo)
718 » text := `<span class="alert">no snippet text available</span>` 558 » text := []byte(`<span class="alert">no snippet text available</span>`)
719 if info.IsIndex() { 559 if info.IsIndex() {
720 index, _ := searchIndex.get() 560 index, _ := searchIndex.get()
721 // no escaping of snippet text needed; 561 // no escaping of snippet text needed;
722 // snippet text is escaped when generated 562 // snippet text is escaped when generated
723 text = index.(*Index).Snippet(info.Lori()).Text 563 text = index.(*Index).Snippet(info.Lori()).Text
724 } 564 }
725 » fmt.Fprint(w, text) 565 » w.Write(text)
726 } 566 }
727 567
728 568
729 // Template formatter for "padding" format. 569 // Template formatter for "padding" format.
730 func paddingFmt(w io.Writer, format string, x ...interface{}) { 570 func paddingFmt(w io.Writer, format string, x ...interface{}) {
731 for i := x[0].(int); i > 0; i-- { 571 for i := x[0].(int); i > 0; i-- {
732 fmt.Fprint(w, `<td width="25"></td>`) 572 fmt.Fprint(w, `<td width="25"></td>`)
733 } 573 }
734 } 574 }
735 575
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
798 638
799 var ( 639 var (
800 codewalkHTML, 640 codewalkHTML,
801 codewalkdirHTML, 641 codewalkdirHTML,
802 dirlistHTML, 642 dirlistHTML,
803 errorHTML, 643 errorHTML,
804 godocHTML, 644 godocHTML,
805 packageHTML, 645 packageHTML,
806 packageText, 646 packageText,
807 searchHTML, 647 searchHTML,
808 » searchText, 648 » searchText *template.Template
809 » sourceHTML *template.Template
810 ) 649 )
811 650
812 func readTemplates() { 651 func readTemplates() {
813 // have to delay until after flags processing since paths depend on goro ot 652 // have to delay until after flags processing since paths depend on goro ot
814 codewalkHTML = readTemplate("codewalk.html") 653 codewalkHTML = readTemplate("codewalk.html")
815 codewalkdirHTML = readTemplate("codewalkdir.html") 654 codewalkdirHTML = readTemplate("codewalkdir.html")
816 dirlistHTML = readTemplate("dirlist.html") 655 dirlistHTML = readTemplate("dirlist.html")
817 errorHTML = readTemplate("error.html") 656 errorHTML = readTemplate("error.html")
818 godocHTML = readTemplate("godoc.html") 657 godocHTML = readTemplate("godoc.html")
819 packageHTML = readTemplate("package.html") 658 packageHTML = readTemplate("package.html")
820 packageText = readTemplate("package.txt") 659 packageText = readTemplate("package.txt")
821 searchHTML = readTemplate("search.html") 660 searchHTML = readTemplate("search.html")
822 searchText = readTemplate("search.txt") 661 searchText = readTemplate("search.txt")
823 sourceHTML = readTemplate("source.html")
824 } 662 }
825 663
826 664
827 // ---------------------------------------------------------------------------- 665 // ----------------------------------------------------------------------------
828 // Generic HTML wrapper 666 // Generic HTML wrapper
829 667
830 func servePage(w http.ResponseWriter, title, subtitle, query string, content []b yte) { 668 func servePage(w http.ResponseWriter, title, subtitle, query string, content []b yte) {
831 d := struct { 669 d := struct {
832 Title string 670 Title string
833 Subtitle string 671 Subtitle string
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
914 752
915 func applyTemplate(t *template.Template, name string, data interface{}) []byte { 753 func applyTemplate(t *template.Template, name string, data interface{}) []byte {
916 var buf bytes.Buffer 754 var buf bytes.Buffer
917 if err := t.Execute(data, &buf); err != nil { 755 if err := t.Execute(data, &buf); err != nil {
918 log.Printf("%s.Execute: %s", name, err) 756 log.Printf("%s.Execute: %s", name, err)
919 } 757 }
920 return buf.Bytes() 758 return buf.Bytes()
921 } 759 }
922 760
923 761
924 func serveGoSource(w http.ResponseWriter, r *http.Request, abspath, relpath stri ng) {
925 fset := token.NewFileSet()
926 file, err := parser.ParseFile(fset, abspath, nil, parser.ParseComments)
927 if err != nil {
928 log.Printf("parser.ParseFile: %s", err)
929 serveError(w, r, relpath, err)
930 return
931 }
932
933 // TODO(gri) enable once we are confident it works for all files
934 // augment AST with types; ignore errors (partial type information ok)
935 // typechecker.CheckFile(file, nil)
936
937 var buf bytes.Buffer
938 styler := newStyler(r.FormValue("h"))
939 writeNode(&buf, fset, file, true, styler)
940
941 type SourceInfo struct {
942 IdList []byte
943 Source []byte
944 }
945 info := &SourceInfo{styler.idList(fset), buf.Bytes()}
946
947 contents := applyTemplate(sourceHTML, "sourceHTML", info)
948 servePage(w, "Source file "+relpath, "", "", contents)
949 }
950
951
952 func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { 762 func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) {
953 if canonical := pathutil.Clean(r.URL.Path) + "/"; r.URL.Path != canonica l { 763 if canonical := pathutil.Clean(r.URL.Path) + "/"; r.URL.Path != canonica l {
954 http.Redirect(w, r, canonical, http.StatusMovedPermanently) 764 http.Redirect(w, r, canonical, http.StatusMovedPermanently)
955 redirected = true 765 redirected = true
956 } 766 }
957 return 767 return
958 } 768 }
959 769
960 770
961 // TODO(gri): Should have a mapping from extension to handler, eventually. 771 // TODO(gri): Should have a mapping from extension to handler, eventually.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
998 // decoding error or control character - not a text file 808 // decoding error or control character - not a text file
999 return false 809 return false
1000 } 810 }
1001 } 811 }
1002 812
1003 // likely a text file 813 // likely a text file
1004 return true 814 return true
1005 } 815 }
1006 816
1007 817
1008 // commentSelection computes the Selection for all Go comments in src. 818 func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit le string) {
1009 func commentSelection(src []byte) Selection {
1010 » var s scanner.Scanner
1011 » file := s.Init(token.NewFileSet(), "", src, nil, scanner.ScanComments+sc anner.InsertSemis)
1012 » return func() (seg []int) {
1013 » » for {
1014 » » » pos, tok, lit := s.Scan()
1015 » » » if tok == token.EOF {
1016 » » » » break
1017 » » » }
1018 » » » offs := file.Offset(pos)
1019 » » » if tok == token.COMMENT {
1020 » » » » seg = []int{offs, offs + len(lit)}
1021 » » » » break
1022 » » » }
1023 » » }
1024 » » return
1025 » }
1026 }
1027
1028
1029 func makeSelection(matches [][]int) Selection {
1030 » return func() (seg []int) {
1031 » » if len(matches) > 0 {
1032 » » » seg = matches[0]
1033 » » » matches = matches[1:]
1034 » » }
1035 » » return
1036 » }
1037 }
1038
1039
1040 // regexpSelection computes the Selection for the regular expression expr in tex t.
1041 func regexpSelection(text []byte, expr string) Selection {
1042 » var matches [][]int
1043 » if rx, err := regexp.Compile(expr); err == nil {
1044 » » matches = rx.FindAllIndex(text, -1)
1045 » }
1046 » return makeSelection(matches)
1047 }
1048
1049
1050 var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)
1051
1052 func rangeSelection(str string, max int) Selection {
1053 » m := selRx.FindStringSubmatch(str)
1054 » if len(m) >= 2 {
1055 » » from, _ := strconv.Atoi(m[1])
1056 » » to, _ := strconv.Atoi(m[2])
1057 » » if from < to {
1058 » » » return makeSelection([][]int{[]int{from, to}})
1059 » » }
1060 » }
1061 » return nil
1062 }
1063
1064
1065 // bit 0 (2^0 == 1): comments
1066 // bit 1 (2^1 == 2): highlights
1067 // bit 2 (2^2 == 4): selections
1068 //
1069 var startTags = []string{
1070 » /* 0 */ ``,
1071 » /* 1 */ `<span class ="comment">`,
1072 » /* 2 */ `<span class="highlight">`,
1073 » /* 3 */ `<span class="highlight-comment">`,
1074 » /* 4 */ `<span class="selection">`,
1075 » /* 5 */ `<span class="selection-comment">`,
1076 » /* 6 */ `<span class="selection-highlight">`,
1077 » /* 7 */ `<span class="selection-highlight-comment">`,
1078 }
1079
1080
1081 func formatText(text []byte, isGoFile bool, pattern, rangeStr string) []byte {
1082 » var buf bytes.Buffer
1083 » buf.WriteString("<pre>\n")
1084
1085 » var comments, highlights, ranges Selection
1086 » if isGoFile {
1087 » » comments = commentSelection(text)
1088 » }
1089 » if pattern != "" {
1090 » » highlights = regexpSelection(text, pattern)
1091 » }
1092 » if rangeStr != "" {
1093 » » ranges = rangeSelection(rangeStr, len(text))
1094 » }
1095 » if comments != nil || highlights != nil || ranges != nil {
1096 » » FormatSelections(&buf, text, startTags, comments, highlights, ra nges)
1097 » } else {
1098 » » template.HTMLEscape(&buf, text)
1099 » }
1100
1101 » buf.WriteString("</pre>\n")
1102 » return buf.Bytes()
1103 }
1104
1105
1106 func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath stri ng) {
1107 src, err := ioutil.ReadFile(abspath) 819 src, err := ioutil.ReadFile(abspath)
1108 if err != nil { 820 if err != nil {
1109 log.Printf("ioutil.ReadFile: %s", err) 821 log.Printf("ioutil.ReadFile: %s", err)
1110 serveError(w, r, relpath, err) 822 serveError(w, r, relpath, err)
1111 return 823 return
1112 } 824 }
1113 825
1114 » contents := formatText(src, pathutil.Ext(abspath) == ".go", r.FormValue( "g"), r.FormValue("s")) 826 » contents := FormatText(src, 1, pathutil.Ext(abspath) == ".go", r.FormVal ue("h"), rangeSelection(r.FormValue("s")))
1115 » servePage(w, "Text file "+relpath, "", "", contents) 827 » servePage(w, title+" "+relpath, "", "", contents)
1116 } 828 }
1117 829
1118 830
1119 func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str ing) { 831 func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str ing) {
1120 if redirect(w, r) { 832 if redirect(w, r) {
1121 return 833 return
1122 } 834 }
1123 835
1124 list, err := ioutil.ReadDir(abspath) 836 list, err := ioutil.ReadDir(abspath)
1125 if err != nil { 837 if err != nil {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1160 if strings.HasSuffix(abspath, "/index.html") { 872 if strings.HasSuffix(abspath, "/index.html") {
1161 // We'll show index.html for the directory. 873 // We'll show index.html for the directory.
1162 // Use the dir/ version as canonical instead of dir/inde x.html. 874 // Use the dir/ version as canonical instead of dir/inde x.html.
1163 http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("in dex.html")], http.StatusMovedPermanently) 875 http.Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len("in dex.html")], http.StatusMovedPermanently)
1164 return 876 return
1165 } 877 }
1166 serveHTMLDoc(w, r, abspath, relpath) 878 serveHTMLDoc(w, r, abspath, relpath)
1167 return 879 return
1168 880
1169 case ".go": 881 case ".go":
1170 » » if r.FormValue("g") != "" { 882 » » serveTextFile(w, r, abspath, relpath, "Source file")
1171 » » » serveTextFile(w, r, abspath, relpath)
1172 » » » return
1173 » » }
1174 » » serveGoSource(w, r, abspath, relpath)
1175 return 883 return
1176 } 884 }
1177 885
1178 dir, err := os.Lstat(abspath) 886 dir, err := os.Lstat(abspath)
1179 if err != nil { 887 if err != nil {
1180 log.Print(err) 888 log.Print(err)
1181 serveError(w, r, relpath, err) 889 serveError(w, r, relpath, err)
1182 return 890 return
1183 } 891 }
1184 892
1185 if dir != nil && dir.IsDirectory() { 893 if dir != nil && dir.IsDirectory() {
1186 if redirect(w, r) { 894 if redirect(w, r) {
1187 return 895 return
1188 } 896 }
1189 if index := abspath + "/index.html"; isTextFile(index) { 897 if index := abspath + "/index.html"; isTextFile(index) {
1190 serveHTMLDoc(w, r, index, relativePath(index)) 898 serveHTMLDoc(w, r, index, relativePath(index))
1191 return 899 return
1192 } 900 }
1193 serveDirectory(w, r, abspath, relpath) 901 serveDirectory(w, r, abspath, relpath)
1194 return 902 return
1195 } 903 }
1196 904
1197 if isTextFile(abspath) { 905 if isTextFile(abspath) {
1198 » » serveTextFile(w, r, abspath, relpath) 906 » » serveTextFile(w, r, abspath, relpath, "Text file")
1199 return 907 return
1200 } 908 }
1201 909
1202 fileServer.ServeHTTP(w, r) 910 fileServer.ServeHTTP(w, r)
1203 } 911 }
1204 912
1205 913
1206 // ---------------------------------------------------------------------------- 914 // ----------------------------------------------------------------------------
1207 // Packages 915 // Packages
1208 916
(...skipping 27 matching lines...) Expand all
1236 fsRoot string // file system root to which the pattern is mapped 944 fsRoot string // file system root to which the pattern is mapped
1237 isPkg bool // true if this handler serves real package documentation (as opposed to command documentation) 945 isPkg bool // true if this handler serves real package documentation (as opposed to command documentation)
1238 } 946 }
1239 947
1240 948
1241 // getPageInfo returns the PageInfo for a package directory abspath. If the 949 // getPageInfo returns the PageInfo for a package directory abspath. If the
1242 // parameter genAST is set, an AST containing only the package exports is 950 // parameter genAST is set, an AST containing only the package exports is
1243 // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) 951 // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
1244 // is extracted from the AST. If there is no corresponding package in the 952 // is extracted from the AST. If there is no corresponding package in the
1245 // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- 953 // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub-
1246 // directories, PageInfo.Dirs is nil. If a directory read error occured, 954 // directories, PageInfo.Dirs is nil. If a directory read error occurred,
1247 // PageInfo.Err is set to the respective error but the error is not logged. 955 // PageInfo.Err is set to the respective error but the error is not logged.
1248 // 956 //
1249 func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf oMode) PageInfo { 957 func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf oMode) PageInfo {
1250 // filter function to select the desired .go files 958 // filter function to select the desired .go files
1251 filter := func(d *os.FileInfo) bool { 959 filter := func(d *os.FileInfo) bool {
1252 // If we are looking at cmd documentation, only accept 960 // If we are looking at cmd documentation, only accept
1253 // the special fakePkgFile containing the documentation. 961 // the special fakePkgFile containing the documentation.
1254 return isPkgFile(d) && (h.isPkg || d.Name == fakePkgFile) 962 return isPkgFile(d) && (h.isPkg || d.Name == fakePkgFile)
1255 } 963 }
1256 964
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
1435 1143
1436 type SearchResult struct { 1144 type SearchResult struct {
1437 Query string 1145 Query string
1438 Alert string // error or warning message 1146 Alert string // error or warning message
1439 1147
1440 // identifier matches 1148 // identifier matches
1441 Hit *LookupResult // identifier matches of Query 1149 Hit *LookupResult // identifier matches of Query
1442 Alt *AltWords // alternative identifiers to look for 1150 Alt *AltWords // alternative identifiers to look for
1443 1151
1444 // textual matches 1152 // textual matches
1445 » Found int // number of textual occurences found 1153 » Found int // number of textual occurrences found
1446 Textual []FileLines // textual matches of Query 1154 Textual []FileLines // textual matches of Query
1447 » Complete bool // true if all textual occurences of Query are repo rted 1155 » Complete bool // true if all textual occurrences of Query are rep orted
1448 } 1156 }
1449 1157
1450 1158
1451 func lookup(query string) (result SearchResult) { 1159 func lookup(query string) (result SearchResult) {
1452 result.Query = query 1160 result.Query = query
1453
1454 // if regexp queries are disabled, quote the query string:
1455 // it will always compile and look like a literal
1456 if !*regexpQueries {
1457 query = regexp.QuoteMeta(query)
1458 }
1459 1161
1460 // determine identifier lookup string and full text regexp 1162 // determine identifier lookup string and full text regexp
1461 lookupStr := "" 1163 lookupStr := ""
1462 lookupRx, err := regexp.Compile(query) 1164 lookupRx, err := regexp.Compile(query)
1463 if err != nil { 1165 if err != nil {
1464 result.Alert = "Error in query regular expression: " + err.Strin g() 1166 result.Alert = "Error in query regular expression: " + err.Strin g()
1465 return 1167 return
1466 } 1168 }
1467 if prefix, complete := lookupRx.LiteralPrefix(); complete { 1169 if prefix, complete := lookupRx.LiteralPrefix(); complete {
1468 // otherwise we lookup "" (with no result) because 1170 // otherwise we lookup "" (with no result) because
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
1590 log.Printf("after GC: bytes = %d footprint = %d", runti me.MemStats.HeapAlloc, runtime.MemStats.Sys) 1292 log.Printf("after GC: bytes = %d footprint = %d", runti me.MemStats.HeapAlloc, runtime.MemStats.Sys)
1591 } 1293 }
1592 var delay int64 = 60 * 1e9 // by default, try every 60s 1294 var delay int64 = 60 * 1e9 // by default, try every 60s
1593 if *testDir != "" { 1295 if *testDir != "" {
1594 // in test mode, try once a second for fast startup 1296 // in test mode, try once a second for fast startup
1595 delay = 1 * 1e9 1297 delay = 1 * 1e9
1596 } 1298 }
1597 time.Sleep(delay) 1299 time.Sleep(delay)
1598 } 1300 }
1599 } 1301 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b