-
-
Save dmitshur/7e055acb298b42549f9a7d53784545c9 to your computer and use it in GitHub Desktop.
A prototype of adding support for parsing module comments to cmd/go. It's related to a proposal that is in progress.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 17fa3a2c59d8cb9710df96a8d3aebab779a202ae Mon Sep 17 00:00:00 2001 | |
From: Dmitri Shuralyov <dmitshur@golang.org> | |
Date: Mon, 4 Mar 2019 09:42:00 -0500 | |
Subject: [PATCH] cmd/go: add support for parsing module comments | |
DO NOT SUBMIT, DO NOT REVIEW: This is a prototype of a proposal | |
that is in progress. It has not been accepted. | |
Change-Id: I9f2aa9acc039e9f1e10e4f0c8a25cb14e4c3fc20 | |
--- | |
src/cmd/go/internal/list/list.go | 3 +- | |
src/cmd/go/internal/load/pkg.go | 2 +- | |
src/cmd/go/internal/moddoc/synopsis.go | 37 ++++++++++ | |
src/cmd/go/internal/moddoc/synopsis_test.go | 82 +++++++++++++++++++++ | |
src/cmd/go/internal/modinfo/info.go | 1 + | |
src/cmd/go/internal/modload/build.go | 3 + | |
src/cmd/go/internal/modload/load.go | 20 +++-- | |
7 files changed, 141 insertions(+), 7 deletions(-) | |
create mode 100644 src/cmd/go/internal/moddoc/synopsis.go | |
create mode 100644 src/cmd/go/internal/moddoc/synopsis_test.go | |
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go | |
index 4a6633d9a1..14601b9324 100644 | |
--- a/src/cmd/go/internal/list/list.go | |
+++ b/src/cmd/go/internal/list/list.go | |
@@ -51,7 +51,7 @@ to -f '{{.ImportPath}}'. The struct being passed to the template is: | |
ImportPath string // import path of package in dir | |
ImportComment string // path in import comment on package statement | |
Name string // package name | |
- Doc string // package documentation string | |
+ Doc string // package documentation synopsis | |
Target string // install path | |
Shlib string // the shared library that contains this package (only set when -linkshared) | |
Goroot bool // is this package in the Go root? | |
@@ -204,6 +204,7 @@ applied to a Go struct, but now a Module struct: | |
type Module struct { | |
Path string // module path | |
Version string // module version | |
+ Doc string // module documentation synopsis | |
Versions []string // available module versions (with -versions) | |
Replace *Module // replaced by this module | |
Time *time.Time // time version was created | |
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go | |
index e6c893c257..47c64a6927 100644 | |
--- a/src/cmd/go/internal/load/pkg.go | |
+++ b/src/cmd/go/internal/load/pkg.go | |
@@ -58,7 +58,7 @@ type PackagePublic struct { | |
ImportPath string `json:",omitempty"` // import path of package in dir | |
ImportComment string `json:",omitempty"` // path in import comment on package statement | |
Name string `json:",omitempty"` // package name | |
- Doc string `json:",omitempty"` // package documentation string | |
+ Doc string `json:",omitempty"` // package documentation synopsis | |
Target string `json:",omitempty"` // installed target for this package (may be executable) | |
Shlib string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared) | |
Root string `json:",omitempty"` // Go root or Go path dir containing this package | |
diff --git a/src/cmd/go/internal/moddoc/synopsis.go b/src/cmd/go/internal/moddoc/synopsis.go | |
new file mode 100644 | |
index 0000000000..e4d7555d96 | |
--- /dev/null | |
+++ b/src/cmd/go/internal/moddoc/synopsis.go | |
@@ -0,0 +1,37 @@ | |
+// Copyright 2019 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. | |
+ | |
+// Package moddoc extracts module documentation from a parsed go.mod file. | |
+package moddoc | |
+ | |
+import ( | |
+ "cmd/go/internal/modfile" | |
+ "go/ast" | |
+ "go/doc" | |
+) | |
+ | |
+// TODO: This is a package that contains a single simple function, | |
+// and it's used only in the modload package. Maybe it should | |
+// be moved into modload as moddoc.go and moddoc_test.go. | |
+// | |
+// This functionality could also live inside the modfile package, | |
+// but that would require it to import "go/ast" and "go/doc" packages. | |
+// I opted against that for now, since this is higher-order functionality. | |
+ | |
+// Synopsis ... | |
+func Synopsis(f *modfile.File) string { | |
+ // Check that the module statement is present. | |
+ if f.Module == nil { | |
+ return "" | |
+ } | |
+ | |
+ // Compute the module comment. | |
+ var mc ast.CommentGroup | |
+ for _, c := range f.Module.Syntax.Comments.Before { | |
+ mc.List = append(mc.List, &ast.Comment{Text: c.Token}) | |
+ } | |
+ | |
+ // Return its synopsis. | |
+ return doc.Synopsis(mc.Text()) | |
+} | |
diff --git a/src/cmd/go/internal/moddoc/synopsis_test.go b/src/cmd/go/internal/moddoc/synopsis_test.go | |
new file mode 100644 | |
index 0000000000..48219a912c | |
--- /dev/null | |
+++ b/src/cmd/go/internal/moddoc/synopsis_test.go | |
@@ -0,0 +1,82 @@ | |
+// Copyright 2019 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. | |
+ | |
+package moddoc_test | |
+ | |
+import ( | |
+ "cmd/go/internal/moddoc" | |
+ "cmd/go/internal/modfile" | |
+ "testing" | |
+) | |
+ | |
+func TestSynopsis(t *testing.T) { | |
+ for _, test := range []struct { | |
+ in string | |
+ want string | |
+ }{ | |
+ { | |
+ in: `// Module c is a documented module. | |
+module a/b/c | |
+`, | |
+ want: "Module c is a documented module.", | |
+ }, | |
+ { | |
+ in: `// Module c is a module | |
+// that is documented | |
+// across multiple lines. | |
+module a/b/c | |
+`, | |
+ want: "Module c is a module that is documented across multiple lines.", | |
+ }, | |
+ { | |
+ in: `// Module c is a documented module. It happens | |
+// to have a more detailed description. This description is not | |
+// a part of the synposis. | |
+module a/b/c | |
+`, | |
+ want: "Module c is a documented module.", | |
+ }, | |
+ { | |
+ in: `// This is not a module comment. | |
+ | |
+module foo/bar/m | |
+`, | |
+ want: "", | |
+ }, | |
+ { | |
+ in: `// This is not a module comment. | |
+ | |
+// But this is a module comment. | |
+module foo/bar/m | |
+`, | |
+ want: "But this is a module comment.", | |
+ }, | |
+ { | |
+ in: "", | |
+ want: "", | |
+ }, | |
+ { | |
+ in: `//Module c is a documented module, without a leading space. | |
+module a/b/c | |
+`, | |
+ want: "Module c is a documented module, without a leading space.", | |
+ }, | |
+ { | |
+ in: `//Module c is a documented module, | |
+//without leading spaces on any of its lines. | |
+module a/b/c | |
+`, | |
+ want: "Module c is a documented module, without leading spaces on any of its lines.", | |
+ }, | |
+ } { | |
+ f, err := modfile.ParseLax("", []byte(test.in), nil) | |
+ if err != nil { | |
+ t.Fatal("modfile.ParseLax:", err) | |
+ } | |
+ got := moddoc.Synopsis(f) | |
+ if got != test.want { | |
+ t.Errorf("got Synopsis = %q; want %q\n", got, test.want) | |
+ } | |
+ } | |
+} | |
diff --git a/src/cmd/go/internal/modinfo/info.go b/src/cmd/go/internal/modinfo/info.go | |
index 07248d1a61..edc61b29cc 100644 | |
--- a/src/cmd/go/internal/modinfo/info.go | |
+++ b/src/cmd/go/internal/modinfo/info.go | |
@@ -12,6 +12,7 @@ import "time" | |
type ModulePublic struct { | |
Path string `json:",omitempty"` // module path | |
Version string `json:",omitempty"` // module version | |
+ Doc string `json:",omitempty"` // module documentation synopsis | |
Versions []string `json:",omitempty"` // available module versions | |
Replace *ModulePublic `json:",omitempty"` // replaced by this module | |
Time *time.Time `json:",omitempty"` // time version was created | |
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go | |
index 4d4e512ef5..366a80ce23 100644 | |
--- a/src/cmd/go/internal/modload/build.go | |
+++ b/src/cmd/go/internal/modload/build.go | |
@@ -8,6 +8,7 @@ import ( | |
"bytes" | |
"cmd/go/internal/base" | |
"cmd/go/internal/cfg" | |
+ "cmd/go/internal/moddoc" | |
"cmd/go/internal/modfetch" | |
"cmd/go/internal/modinfo" | |
"cmd/go/internal/module" | |
@@ -103,6 +104,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { | |
if HasModRoot() { | |
info.Dir = ModRoot() | |
info.GoMod = filepath.Join(info.Dir, "go.mod") | |
+ info.Doc = moddoc.Synopsis(modFile) | |
if modFile.Go != nil { | |
info.GoVersion = modFile.Go.Version | |
} | |
@@ -116,6 +118,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { | |
Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path], | |
} | |
if loaded != nil { | |
+ info.Doc = loaded.doc[m.Path] | |
info.GoVersion = loaded.goVersion[m.Path] | |
} | |
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go | |
index 6d6c037af2..40d9af23e2 100644 | |
--- a/src/cmd/go/internal/modload/load.go | |
+++ b/src/cmd/go/internal/modload/load.go | |
@@ -20,6 +20,7 @@ import ( | |
"cmd/go/internal/base" | |
"cmd/go/internal/cfg" | |
"cmd/go/internal/imports" | |
+ "cmd/go/internal/moddoc" | |
"cmd/go/internal/modfetch" | |
"cmd/go/internal/modfile" | |
"cmd/go/internal/module" | |
@@ -447,6 +448,7 @@ type loader struct { | |
// computed at end of iterations | |
direct map[string]bool // imported directly by main module | |
+ doc map[string]string // documentation synopsis of each module | |
goVersion map[string]string // go version recorded in each module | |
} | |
@@ -552,9 +554,12 @@ func (ld *loader) load(roots func() []string) { | |
} | |
} | |
- // Add Go versions, computed during walk. | |
+ // Add documentation synopses and Go versions, computed during walk. | |
+ ld.doc = make(map[string]string) | |
ld.goVersion = make(map[string]string) | |
for _, m := range buildList { | |
+ d, _ := reqs.(*mvsReqs).docs.Load(m) | |
+ ld.doc[m.Path], _ = d.(string) | |
v, _ := reqs.(*mvsReqs).versions.Load(m) | |
ld.goVersion[m.Path], _ = v.(string) | |
} | |
@@ -832,6 +837,7 @@ func Replacement(mod module.Version) module.Version { | |
type mvsReqs struct { | |
buildList []module.Version | |
cache par.Cache | |
+ docs sync.Map | |
versions sync.Map | |
} | |
@@ -918,11 +924,13 @@ func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version { | |
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { | |
if mod == Target { | |
- if modFile != nil && modFile.Go != nil { | |
- r.versions.LoadOrStore(mod, modFile.Go.Version) | |
+ if modFile != nil { | |
+ r.docs.LoadOrStore(mod, moddoc.Synopsis(modFile)) | |
+ if modFile.Go != nil { | |
+ r.versions.LoadOrStore(mod, modFile.Go.Version) | |
+ } | |
} | |
- var list []module.Version | |
- return append(list, r.buildList[1:]...), nil | |
+ return append([]module.Version(nil), r.buildList[1:]...), nil | |
} | |
if cfg.BuildMod == "vendor" { | |
@@ -951,6 +959,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { | |
base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err) | |
return nil, ErrRequire | |
} | |
+ r.docs.LoadOrStore(mod, moddoc.Synopsis(f)) | |
if f.Go != nil { | |
r.versions.LoadOrStore(mod, f.Go.Version) | |
} | |
@@ -987,6 +996,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { | |
base.Errorf("go: %s@%s: parsing go.mod: unexpected module path %q", mod.Path, mod.Version, mpath) | |
return nil, ErrRequire | |
} | |
+ r.docs.LoadOrStore(mod, moddoc.Synopsis(f)) | |
if f.Go != nil { | |
r.versions.LoadOrStore(mod, f.Go.Version) | |
} | |
-- | |
2.19.1.1215.g8438c0b245-goog | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment