field docs lookup
This commit is contained in:
parent
5c8853f7ae
commit
43a0e6ca33
117
docs.go
117
docs.go
@ -81,12 +81,17 @@ func splitDocs(d string) (string, string) {
|
|||||||
|
|
||||||
func functionParams(d string) []string {
|
func functionParams(d string) []string {
|
||||||
_, p := splitDocs(d)
|
_, p := splitDocs(d)
|
||||||
if p == "" {
|
if len(p) <= len("func()") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p = p[len("func(") : len(p)-1]
|
p = p[len("func(") : len(p)-1]
|
||||||
return strings.Split(p, ", ")
|
pp := strings.Split(p, ",")
|
||||||
|
for i := range pp {
|
||||||
|
pp[i] = strings.TrimSpace(pp[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register is used by the code generated by the docreflect/generate package or the docreflect generate command to register
|
// Register is used by the code generated by the docreflect/generate package or the docreflect generate command to register
|
||||||
@ -127,18 +132,60 @@ func FunctionParams(v reflect.Value) []string {
|
|||||||
func Type(t reflect.Type) string {
|
func Type(t reflect.Type) string {
|
||||||
t = unpack(t)
|
t = unpack(t)
|
||||||
p := fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
p := fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
||||||
return docs(p)
|
return Docs(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func structField(st reflect.Type, name string) (reflect.StructField, bool) {
|
func structField(visited map[reflect.Type]bool, t reflect.Type, fieldPath []string) (reflect.Type, string, bool) {
|
||||||
for i := 0; i < st.NumField(); i++ {
|
t = unpack(t)
|
||||||
f := st.Field(i)
|
if t.Kind() != reflect.Struct {
|
||||||
if f.Name == name {
|
return nil, "", false
|
||||||
return f, true
|
}
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if !f.Anonymous {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ft := unpack(f.Type)
|
||||||
|
if visited[ft] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if visited == nil {
|
||||||
|
visited = make(map[reflect.Type]bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
visited[ft] = true
|
||||||
|
if t, s, ok := structField(visited, ft, fieldPath); ok {
|
||||||
|
return t, s, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflect.StructField{}, false
|
name := fieldPath[0]
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if f.Name != name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fieldPath) == 1 {
|
||||||
|
return t, name, true
|
||||||
|
}
|
||||||
|
|
||||||
|
tt, s, ok := structField(nil, unpack(f.Type), fieldPath[1:])
|
||||||
|
if !ok {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.Name() == "" {
|
||||||
|
return t, fmt.Sprintf("%s.%s", name, s), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return tt, s, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field returns the docuemntation for a struct field.
|
// Field returns the docuemntation for a struct field.
|
||||||
@ -147,56 +194,12 @@ func Field(t reflect.Type, fieldPath ...string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
t = unpack(t)
|
t, s, ok := structField(nil, t, fieldPath)
|
||||||
if t.Kind() != reflect.Struct {
|
if !ok {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
f, found := structField(t, fieldPath[0])
|
return Docs(strings.Join([]string{t.PkgPath(), t.Name(), s}, "."))
|
||||||
if !found {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fieldPath) == 1 {
|
|
||||||
println("returning docs", strings.Join([]string{t.PkgPath(), t.Name(), fieldPath[0]}, "."))
|
|
||||||
return docs(strings.Join([]string{t.PkgPath(), t.Name(), fieldPath[0]}, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
ft := unpack(f.Type)
|
|
||||||
if ft.Kind() != reflect.Struct {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if ft.Name() != "" {
|
|
||||||
return Field(ft, fieldPath[1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
st := ft
|
|
||||||
path := fieldPath[1:]
|
|
||||||
for {
|
|
||||||
f, found = structField(st, path[0])
|
|
||||||
if !found {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(path) == 1 {
|
|
||||||
return docs(strings.Join(append([]string{t.PkgPath(), t.Name()}, fieldPath...), "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
path = path[1:]
|
|
||||||
if len(path) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
st = unpack(f.Type)
|
|
||||||
if st.Kind() != reflect.Struct {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if st.Name() != "" {
|
|
||||||
return Field(st, path...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method returns the documentation for a type method.
|
// Method returns the documentation for a type method.
|
||||||
@ -207,7 +210,7 @@ func Method(t reflect.Type, name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p := fmt.Sprintf("%s.%s.%s", t.PkgPath(), t.Name(), name)
|
p := fmt.Sprintf("%s.%s.%s", t.PkgPath(), t.Name(), name)
|
||||||
return docs(p)
|
return Docs(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodParams returns the list of the parameter names of a type method.
|
// MethodParams returns the list of the parameter names of a type method.
|
||||||
|
35
docs_test.go
35
docs_test.go
@ -105,6 +105,15 @@ func Test(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("field in inline pointer struct type", func(t *testing.T) {
|
||||||
|
s := &testpackage.ExportedType{}
|
||||||
|
typ := reflect.TypeOf(s)
|
||||||
|
d := docreflect.Field(typ, "Qux", "Quux")
|
||||||
|
if !strings.Contains(d, "Quux is a number") {
|
||||||
|
t.Fatal(d)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("field in another type", func(t *testing.T) {
|
t.Run("field in another type", func(t *testing.T) {
|
||||||
s := &testpackage.ExportedType{}
|
s := &testpackage.ExportedType{}
|
||||||
typ := reflect.TypeOf(s)
|
typ := reflect.TypeOf(s)
|
||||||
@ -114,6 +123,15 @@ func Test(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("field in an anon struct field", func(t *testing.T) {
|
||||||
|
s := &testpackage.ExportedType3{}
|
||||||
|
typ := reflect.TypeOf(s)
|
||||||
|
d := docreflect.Field(typ, "Foo")
|
||||||
|
if !strings.Contains(d, "Foo is a field in ExportedType2") {
|
||||||
|
t.Fatal(d)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("method", func(t *testing.T) {
|
t.Run("method", func(t *testing.T) {
|
||||||
s := testpackage.ExportedType{}
|
s := testpackage.ExportedType{}
|
||||||
typ := reflect.TypeOf(s)
|
typ := reflect.TypeOf(s)
|
||||||
@ -131,4 +149,21 @@ func Test(t *testing.T) {
|
|||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("method as function value", func(t *testing.T) {
|
||||||
|
s := testpackage.ExportedType{}
|
||||||
|
d := docreflect.Function(reflect.ValueOf(s.Method))
|
||||||
|
if !strings.Contains(d, "Method is a method of ExportedType") {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
p := docreflect.FunctionParams(reflect.ValueOf(s.Method))
|
||||||
|
if len(p) != 3 {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p[0] != "p1" || p[1] != "p2" || p[2] != "p3" {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -299,7 +299,7 @@ func funcDocs(f *doc.Func) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func findFieldDocs(str *ast.StructType, fieldPath []string) (string, bool) {
|
func findFieldDocs(str *ast.StructType, fieldPath []string) (string, bool) {
|
||||||
if len(fieldPath) == 0 || str.Fields == nil {
|
if str.Fields == nil {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,11 +42,16 @@ type Foo = ExportedType
|
|||||||
type Bar ExportedType
|
type Bar ExportedType
|
||||||
|
|
||||||
type ExportedType2 struct{
|
type ExportedType2 struct{
|
||||||
|
*ExportedType3
|
||||||
|
|
||||||
// Foo is a field in ExportedType2
|
// Foo is a field in ExportedType2
|
||||||
Foo int
|
Foo int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExportedType3 struct {
|
||||||
|
ExportedType2
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
||||||
// A is a number
|
// A is a number
|
||||||
|
@ -15,6 +15,10 @@ code, will have the docs accessible via the top level docreflect package methods
|
|||||||
- the package documentation can be fetched using `docreflect.Docs("absolute/import/path/of/package")`
|
- the package documentation can be fetched using `docreflect.Docs("absolute/import/path/of/package")`
|
||||||
- when passing in the import path of only the selected symbols, the rest of the package level symbols will be ignroed
|
- when passing in the import path of only the selected symbols, the rest of the package level symbols will be ignroed
|
||||||
|
|
||||||
|
**Gotchas:**
|
||||||
|
- type aliases and type definitions based on named types are not resolved
|
||||||
|
- package imports are not resolved, necessary packages need to included in the generate arguments
|
||||||
|
|
||||||
Library documentation: https://godocs.io/code.squareroundforest.org/arpio/docreflect
|
Library documentation: https://godocs.io/code.squareroundforest.org/arpio/docreflect
|
||||||
|
|
||||||
To insall the docreflect command, run:
|
To insall the docreflect command, run:
|
||||||
@ -26,5 +30,5 @@ make install
|
|||||||
Usage of the docreflect command:
|
Usage of the docreflect command:
|
||||||
|
|
||||||
```
|
```
|
||||||
docreflect generate --package-name mypackage coderepos.org/jdoe/mypackage > docreflect.go
|
docreflect generate --package-name mypackage coderepos.org/jdoe/mypackage coderepos.org/jdoe/otherpackage > docreflect.go
|
||||||
```
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user