OLD | NEW |
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 "encoding/json" | 9 "encoding/json" |
10 "flag" | 10 "flag" |
(...skipping 792 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
803 func remoteSearchURL(query string, html bool) string { | 803 func remoteSearchURL(query string, html bool) string { |
804 s := "/search?m=text&q=" | 804 s := "/search?m=text&q=" |
805 if html { | 805 if html { |
806 s = "/search?q=" | 806 s = "/search?q=" |
807 } | 807 } |
808 return s + url.QueryEscape(query) | 808 return s + url.QueryEscape(query) |
809 } | 809 } |
810 | 810 |
811 type PageInfo struct { | 811 type PageInfo struct { |
812 Dirname string // directory containing the package | 812 Dirname string // directory containing the package |
813 PList []string // list of package names found | |
814 FSet *token.FileSet // corresponding file set | 813 FSet *token.FileSet // corresponding file set |
815 PAst *ast.File // nil if no single AST with package exports | 814 PAst *ast.File // nil if no single AST with package exports |
816 PDoc *doc.Package // nil if no single package documentation | 815 PDoc *doc.Package // nil if no single package documentation |
817 Examples []*doc.Example // nil if no example code | 816 Examples []*doc.Example // nil if no example code |
818 Dirs *DirList // nil if no directory information | 817 Dirs *DirList // nil if no directory information |
819 DirTime time.Time // directory time stamp | 818 DirTime time.Time // directory time stamp |
820 DirFlat bool // if set, show directory in a flat (non-indente
d) manner | 819 DirFlat bool // if set, show directory in a flat (non-indente
d) manner |
821 IsPkg bool // false if this is not documenting a real packa
ge | 820 IsPkg bool // false if this is not documenting a real packa
ge |
822 Err error // I/O error or nil | 821 Err error // I/O error or nil |
823 } | 822 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
869 } | 868 } |
870 | 869 |
871 // getPageInfo returns the PageInfo for a package directory abspath. If the | 870 // getPageInfo returns the PageInfo for a package directory abspath. If the |
872 // parameter genAST is set, an AST containing only the package exports is | 871 // parameter genAST is set, an AST containing only the package exports is |
873 // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) | 872 // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) |
874 // is extracted from the AST. If there is no corresponding package in the | 873 // is extracted from the AST. If there is no corresponding package in the |
875 // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- | 874 // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- |
876 // directories, PageInfo.Dirs is nil. If a directory read error occurred, | 875 // directories, PageInfo.Dirs is nil. If a directory read error occurred, |
877 // PageInfo.Err is set to the respective error but the error is not logged. | 876 // PageInfo.Err is set to the respective error but the error is not logged. |
878 // | 877 // |
879 func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoM
ode) PageInfo { | 878 func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) Page
Info { |
880 var pkgFiles []string | 879 var pkgFiles []string |
881 | 880 |
882 » // If we're showing the default package, restrict to the ones | 881 » // Restrict to the package files |
883 // that would be used when building the package on this | 882 // that would be used when building the package on this |
884 // system. This makes sure that if there are separate | 883 // system. This makes sure that if there are separate |
885 // implementations for, say, Windows vs Unix, we don't | 884 // implementations for, say, Windows vs Unix, we don't |
886 // jumble them all together. | 885 // jumble them all together. |
887 » if pkgname == "" { | 886 » // Note: Uses current binary's GOOS/GOARCH. |
888 » » // Note: Uses current binary's GOOS/GOARCH. | 887 » // To use different pair, such as if we allowed the user |
889 » » // To use different pair, such as if we allowed the user | 888 » // to choose, set ctxt.GOOS and ctxt.GOARCH before |
890 » » // to choose, set ctxt.GOOS and ctxt.GOARCH before | 889 » // calling ctxt.ScanDir. |
891 » » // calling ctxt.ScanDir. | 890 » ctxt := build.Default |
892 » » ctxt := build.Default | 891 » ctxt.IsAbsPath = pathpkg.IsAbs |
893 » » ctxt.IsAbsPath = pathpkg.IsAbs | 892 » ctxt.ReadDir = fsReadDir |
894 » » ctxt.ReadDir = fsReadDir | 893 » ctxt.OpenFile = fsOpenFile |
895 » » ctxt.OpenFile = fsOpenFile | 894 » if dir, err := ctxt.ImportDir(abspath, 0); err == nil { |
896 » » dir, err := ctxt.ImportDir(abspath, 0) | 895 » » pkgFiles = append(dir.GoFiles, dir.CgoFiles...) |
897 » » if err == nil { | |
898 » » » pkgFiles = append(dir.GoFiles, dir.CgoFiles...) | |
899 » » } | |
900 } | 896 } |
901 | 897 |
902 // filter function to select the desired .go files | 898 // filter function to select the desired .go files |
903 filter := func(d os.FileInfo) bool { | 899 filter := func(d os.FileInfo) bool { |
904 // Only Go files. | 900 // Only Go files. |
905 if !isPkgFile(d) { | 901 if !isPkgFile(d) { |
906 return false | 902 return false |
907 } | 903 } |
908 // If we are looking at cmd documentation, only accept | 904 // If we are looking at cmd documentation, only accept |
909 // the special fakePkgFile containing the documentation. | 905 // the special fakePkgFile containing the documentation. |
910 if !h.isPkg { | 906 if !h.isPkg { |
911 return d.Name() == fakePkgFile | 907 return d.Name() == fakePkgFile |
912 } | 908 } |
913 // Also restrict file list to pkgFiles. | 909 // Also restrict file list to pkgFiles. |
914 return pkgFiles == nil || inList(d.Name(), pkgFiles) | 910 return pkgFiles == nil || inList(d.Name(), pkgFiles) |
915 } | 911 } |
916 | 912 |
917 // get package ASTs | 913 // get package ASTs |
918 fset := token.NewFileSet() | 914 fset := token.NewFileSet() |
919 pkgs, err := parseDir(fset, abspath, filter) | 915 pkgs, err := parseDir(fset, abspath, filter) |
920 » if err != nil && pkgs == nil { | 916 » if err != nil { |
921 » » // only report directory read errors, ignore parse errors | |
922 » » // (may be able to extract partial package information) | |
923 return PageInfo{Dirname: abspath, Err: err} | 917 return PageInfo{Dirname: abspath, Err: err} |
924 } | 918 } |
925 | 919 |
926 // select package | 920 // select package |
927 var pkg *ast.Package // selected package | 921 var pkg *ast.Package // selected package |
928 var plist []string // list of other package (names), if any | |
929 if len(pkgs) == 1 { | 922 if len(pkgs) == 1 { |
930 // Exactly one package - select it. | 923 // Exactly one package - select it. |
931 for _, p := range pkgs { | 924 for _, p := range pkgs { |
932 pkg = p | 925 pkg = p |
933 } | 926 } |
934 | 927 |
935 } else if len(pkgs) > 1 { | 928 } else if len(pkgs) > 1 { |
936 » » // Multiple packages - select the best matching package: The | 929 » » // More than one package - report an error. |
937 » » // 1st choice is the package with pkgname, the 2nd choice is | 930 » » var buf bytes.Buffer |
938 » » // the package with dirname, and the 3rd choice is a package | 931 » » for _, p := range pkgs { |
939 » » // that is not called "main" if there is exactly one such | 932 » » » if buf.Len() > 0 { |
940 » » // package. Otherwise, don't select a package. | 933 » » » » fmt.Fprintf(&buf, ", ") |
941 » » dirpath, dirname := pathpkg.Split(abspath) | 934 » » » } |
942 | 935 » » » fmt.Fprintf(&buf, p.Name) |
943 » » // If the dirname is "go" we might be in a sub-directory for | |
944 » » // .go files - use the outer directory name instead for better | |
945 » » // results. | |
946 » » if dirname == "go" { | |
947 » » » _, dirname = pathpkg.Split(pathpkg.Clean(dirpath)) | |
948 } | 936 } |
949 | 937 » » return PageInfo{ |
950 » » var choice3 *ast.Package | 938 » » » Dirname: abspath, |
951 » loop: | 939 » » » Err: fmt.Errorf("%s contains more than one package:
%s", abspath, buf.Bytes()), |
952 » » for _, p := range pkgs { | |
953 » » » switch { | |
954 » » » case p.Name == pkgname: | |
955 » » » » pkg = p | |
956 » » » » break loop // 1st choice; we are done | |
957 » » » case p.Name == dirname: | |
958 » » » » pkg = p // 2nd choice | |
959 » » » case p.Name != "main": | |
960 » » » » choice3 = p | |
961 » » » } | |
962 } | 940 } |
963 if pkg == nil && len(pkgs) == 2 { | |
964 pkg = choice3 | |
965 } | |
966 | |
967 // Compute the list of other packages | |
968 // (excluding the selected package, if any). | |
969 plist = make([]string, len(pkgs)) | |
970 i := 0 | |
971 for name := range pkgs { | |
972 if pkg == nil || name != pkg.Name { | |
973 plist[i] = name | |
974 i++ | |
975 } | |
976 } | |
977 plist = plist[0:i] | |
978 sort.Strings(plist) | |
979 } | 941 } |
980 | 942 |
981 // get examples from *_test.go files | 943 // get examples from *_test.go files |
982 var examples []*doc.Example | 944 var examples []*doc.Example |
983 filter = func(d os.FileInfo) bool { | 945 filter = func(d os.FileInfo) bool { |
984 return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go") | 946 return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go") |
985 } | 947 } |
986 if testpkgs, err := parseDir(fset, abspath, filter); err != nil { | 948 if testpkgs, err := parseDir(fset, abspath, filter); err != nil { |
987 log.Println("parsing test files:", err) | 949 log.Println("parsing test files:", err) |
988 } else { | 950 } else { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1034 // no directory tree present (too early after startup or | 996 // no directory tree present (too early after startup or |
1035 // command-line mode); compute one level for this page | 997 // command-line mode); compute one level for this page |
1036 // note: cannot use path filter here because in general | 998 // note: cannot use path filter here because in general |
1037 // it doesn't contain the fsTree path | 999 // it doesn't contain the fsTree path |
1038 dir = newDirectory(abspath, 1) | 1000 dir = newDirectory(abspath, 1) |
1039 timestamp = time.Now() | 1001 timestamp = time.Now() |
1040 } | 1002 } |
1041 | 1003 |
1042 return PageInfo{ | 1004 return PageInfo{ |
1043 Dirname: abspath, | 1005 Dirname: abspath, |
1044 PList: plist, | |
1045 FSet: fset, | 1006 FSet: fset, |
1046 PAst: past, | 1007 PAst: past, |
1047 PDoc: pdoc, | 1008 PDoc: pdoc, |
1048 Examples: examples, | 1009 Examples: examples, |
1049 Dirs: dir.listing(true), | 1010 Dirs: dir.listing(true), |
1050 DirTime: timestamp, | 1011 DirTime: timestamp, |
1051 DirFlat: mode&flatDir != 0, | 1012 DirFlat: mode&flatDir != 0, |
1052 IsPkg: h.isPkg, | 1013 IsPkg: h.isPkg, |
1053 Err: nil, | 1014 Err: nil, |
1054 } | 1015 } |
1055 } | 1016 } |
1056 | 1017 |
1057 func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { | 1018 func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
1058 if redirect(w, r) { | 1019 if redirect(w, r) { |
1059 return | 1020 return |
1060 } | 1021 } |
1061 | 1022 |
1062 relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):]) | 1023 relpath := pathpkg.Clean(r.URL.Path[len(h.pattern):]) |
1063 abspath := pathpkg.Join(h.fsRoot, relpath) | 1024 abspath := pathpkg.Join(h.fsRoot, relpath) |
1064 mode := getPageInfoMode(r) | 1025 mode := getPageInfoMode(r) |
1065 if relpath == builtinPkgPath { | 1026 if relpath == builtinPkgPath { |
1066 mode = noFiltering | 1027 mode = noFiltering |
1067 } | 1028 } |
1068 » info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode) | 1029 » info := h.getPageInfo(abspath, relpath, mode) |
1069 if info.Err != nil { | 1030 if info.Err != nil { |
1070 log.Print(info.Err) | 1031 log.Print(info.Err) |
1071 serveError(w, r, relpath, info.Err) | 1032 serveError(w, r, relpath, info.Err) |
1072 return | 1033 return |
1073 } | 1034 } |
1074 | 1035 |
1075 if mode&noHtml != 0 { | 1036 if mode&noHtml != 0 { |
1076 serveText(w, applyTemplate(packageText, "packageText", info)) | 1037 serveText(w, applyTemplate(packageText, "packageText", info)) |
1077 return | 1038 return |
1078 } | 1039 } |
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1440 updateIndex() | 1401 updateIndex() |
1441 } | 1402 } |
1442 delay := 60 * time.Second // by default, try every 60s | 1403 delay := 60 * time.Second // by default, try every 60s |
1443 if *testDir != "" { | 1404 if *testDir != "" { |
1444 // in test mode, try once a second for fast startup | 1405 // in test mode, try once a second for fast startup |
1445 delay = 1 * time.Second | 1406 delay = 1 * time.Second |
1446 } | 1407 } |
1447 time.Sleep(delay) | 1408 time.Sleep(delay) |
1448 } | 1409 } |
1449 } | 1410 } |
OLD | NEW |