@@ -57,77 +57,75 @@ func WorkspaceSymbols(ctx context.Context, matcherType SymbolMatcher, style Symb
57
57
// See the comment for symbolCollector for more information.
58
58
type matcherFunc func (name string ) float64
59
59
60
- // A symbolizer returns the best symbol match for name with pkg, according to
61
- // some heuristic.
60
+ // A symbolizer returns the best symbol match for a name with pkg, according to
61
+ // some heuristic. The symbol name is passed as the slice nameParts of logical
62
+ // name pieces. For example, for myType.field the caller can pass either
63
+ // []string{"myType.field"} or []string{"myType.", "field"}.
62
64
//
63
65
// See the comment for symbolCollector for more information.
64
- type symbolizer func (name string , pkg Package , m matcherFunc ) (string , float64 )
66
+ type symbolizer func (nameParts [] string , pkg Package , m matcherFunc ) (string , float64 )
65
67
66
- func fullyQualifiedSymbolMatch (name string , pkg Package , matcher matcherFunc ) (string , float64 ) {
67
- _ , score := dynamicSymbolMatch (name , pkg , matcher )
68
+ func fullyQualifiedSymbolMatch (nameParts []string , pkg Package , matcher matcherFunc ) (string , float64 ) {
69
+ _ , score := dynamicSymbolMatch (nameParts , pkg , matcher )
70
+ path := append ([]string {pkg .PkgPath () + "." }, nameParts ... )
68
71
if score > 0 {
69
- return pkg . PkgPath () + "." + name , score
72
+ return strings . Join ( path , "" ) , score
70
73
}
71
74
return "" , 0
72
75
}
73
76
74
- func dynamicSymbolMatch (name string , pkg Package , matcher matcherFunc ) (string , float64 ) {
75
- // Prefer any package-qualified match.
76
- pkgQualified := pkg .Name () + "." + name
77
- if match , score := bestMatch (pkgQualified , matcher ); match != "" {
78
- return match , score
77
+ func dynamicSymbolMatch (nameParts []string , pkg Package , matcher matcherFunc ) (string , float64 ) {
78
+ var best string
79
+ fullName := strings .Join (nameParts , "" )
80
+ var score float64
81
+ var name string
82
+
83
+ // Compute the match score by finding the highest scoring suffix. In these
84
+ // cases the matched symbol is still the full name: it is confusing to match
85
+ // an unqualified nested field or method.
86
+ if match := bestMatch ("" , nameParts , matcher ); match > score {
87
+ best = fullName
88
+ score = match
79
89
}
80
- fullyQualified := pkg .PkgPath () + "." + name
81
- if match , score := bestMatch (fullyQualified , matcher ); match != "" {
82
- return match , score
90
+
91
+ // Next: try to match a package-qualified name.
92
+ name = pkg .Name () + "." + fullName
93
+ if match := matcher (name ); match > score {
94
+ best = name
95
+ score = match
83
96
}
84
- return "" , 0
85
- }
86
97
87
- func packageSymbolMatch (name string , pkg Package , matcher matcherFunc ) (string , float64 ) {
88
- qualified := pkg .Name () + "." + name
89
- if matcher (qualified ) > 0 {
90
- return qualified , 1
98
+ // Finally: consider a fully qualified name.
99
+ prefix := pkg .PkgPath () + "."
100
+ fullyQualified := prefix + fullName
101
+ // As with field/method selectors, consider suffixes from right to left, but
102
+ // always return a fully-qualified symbol.
103
+ pathParts := strings .SplitAfter (prefix , "/" )
104
+ if match := bestMatch (fullName , pathParts , matcher ); match > score {
105
+ best = fullyQualified
106
+ score = match
91
107
}
92
- return "" , 0
108
+ return best , score
93
109
}
94
110
95
- // bestMatch returns the highest scoring symbol suffix of fullPath, starting
96
- // from the right and splitting on selectors and path components.
97
- //
98
- // e.g. given a symbol path of the form 'host.com/dir/pkg.type.field', we
99
- // check the match quality of the following:
100
- // - field
101
- // - type.field
102
- // - pkg.type.field
103
- // - dir/pkg.type.field
104
- // - host.com/dir/pkg.type.field
105
- //
106
- // and return the best match, along with its score.
107
- //
108
- // This is used to implement the 'dynamic' symbol style.
109
- func bestMatch (fullPath string , matcher matcherFunc ) (string , float64 ) {
110
- pathParts := strings .Split (fullPath , "/" )
111
- dottedParts := strings .Split (pathParts [len (pathParts )- 1 ], "." )
112
-
113
- var best string
111
+ func bestMatch (name string , prefixParts []string , matcher matcherFunc ) float64 {
114
112
var score float64
115
-
116
- for i := 0 ; i < len (dottedParts ); i ++ {
117
- path := strings .Join (dottedParts [len (dottedParts )- 1 - i :], "." )
118
- if match := matcher (path ); match > score {
119
- best = path
113
+ for i := len (prefixParts ) - 1 ; i >= 0 ; i -- {
114
+ name = prefixParts [i ] + name
115
+ if match := matcher (name ); match > score {
120
116
score = match
121
117
}
122
118
}
123
- for i := 0 ; i < len (pathParts ); i ++ {
124
- path := strings .Join (pathParts [len (pathParts )- 1 - i :], "/" )
125
- if match := matcher (path ); match > score {
126
- best = path
127
- score = match
128
- }
119
+ return score
120
+ }
121
+
122
+ func packageSymbolMatch (components []string , pkg Package , matcher matcherFunc ) (string , float64 ) {
123
+ path := append ([]string {pkg .Name () + "." }, components ... )
124
+ qualified := strings .Join (path , "" )
125
+ if matcher (qualified ) > 0 {
126
+ return qualified , 1
129
127
}
130
- return best , score
128
+ return "" , 0
131
129
}
132
130
133
131
// symbolCollector holds context as we walk Packages, gathering symbols that
@@ -420,7 +418,13 @@ func (sc *symbolCollector) walkType(typ ast.Expr, path ...*ast.Ident) {
420
418
// or named. path is the path of nested identifiers containing the field.
421
419
func (sc * symbolCollector ) walkField (field * ast.Field , unnamedKind , namedKind protocol.SymbolKind , path ... * ast.Ident ) {
422
420
if len (field .Names ) == 0 {
423
- sc .match (types .ExprString (field .Type ), unnamedKind , field , path ... )
421
+ switch typ := field .Type .(type ) {
422
+ case * ast.SelectorExpr :
423
+ // embedded qualified type
424
+ sc .match (typ .Sel .Name , unnamedKind , field , path ... )
425
+ default :
426
+ sc .match (types .ExprString (field .Type ), unnamedKind , field , path ... )
427
+ }
424
428
}
425
429
for _ , name := range field .Names {
426
430
sc .match (name .Name , namedKind , name , path ... )
@@ -466,18 +470,14 @@ func (sc *symbolCollector) match(name string, kind protocol.SymbolKind, node ast
466
470
}
467
471
468
472
isExported := isExported (name )
469
- if len (path ) > 0 {
470
- var nameBuilder strings.Builder
471
- for _ , ident := range path {
472
- nameBuilder .WriteString (ident .Name )
473
- nameBuilder .WriteString ("." )
474
- if ! ident .IsExported () {
475
- isExported = false
476
- }
473
+ var names []string
474
+ for _ , ident := range path {
475
+ names = append (names , ident .Name + "." )
476
+ if ! ident .IsExported () {
477
+ isExported = false
477
478
}
478
- nameBuilder .WriteString (name )
479
- name = nameBuilder .String ()
480
479
}
480
+ names = append (names , name )
481
481
482
482
// Factors to apply to the match score for the purpose of downranking
483
483
// results.
@@ -501,7 +501,7 @@ func (sc *symbolCollector) match(name string, kind protocol.SymbolKind, node ast
501
501
// can be noisy.
502
502
fieldFactor = 0.5
503
503
)
504
- symbol , score := sc .symbolizer (name , sc .current .pkg , sc .matcher )
504
+ symbol , score := sc .symbolizer (names , sc .current .pkg , sc .matcher )
505
505
506
506
// Downrank symbols outside of the workspace.
507
507
if ! sc .current .isWorkspace {
0 commit comments