@@ -10,9 +10,15 @@ import (
1010)
1111
1212func ReadDefinition (ctx context.Context , client * lsp.Client , symbolName string ) (string , error ) {
13- symbolResult , err := client .Symbol (ctx , protocol.WorkspaceSymbolParams {
14- Query : symbolName ,
15- })
13+ // Normalize common user inputs, e.g. "foo()" -> "foo"
14+ normalized := strings .TrimSpace (symbolName )
15+ if strings .HasSuffix (normalized , "()" ) {
16+ normalized = strings .TrimSuffix (normalized , "()" )
17+ }
18+
19+ symbolResult , err := client .Symbol (ctx , protocol.WorkspaceSymbolParams {
20+ Query : normalized ,
21+ })
1622 if err != nil {
1723 return "" , fmt .Errorf ("failed to fetch symbol: %v" , err )
1824 }
@@ -23,43 +29,84 @@ func ReadDefinition(ctx context.Context, client *lsp.Client, symbolName string)
2329 }
2430
2531 var definitions []string
26- for _ , symbol := range results {
27- kind := ""
28- container := ""
29-
30- // Skip symbols that we are not looking for. workspace/symbol may return
31- // a large number of fuzzy matches.
32- switch v := symbol .(type ) {
33- case * protocol.SymbolInformation :
34- // SymbolInformation results have richer data.
35- kind = fmt .Sprintf ("Kind: %s\n " , protocol .TableKindMap [v .Kind ])
36- if v .ContainerName != "" {
37- container = fmt .Sprintf ("Container Name: %s\n " , v .ContainerName )
38- }
39-
40- // Handle different matching strategies based on the search term
41- if strings .Contains (symbolName , "." ) {
42- // For qualified names like "Type.Method", require exact match
43- if symbol .GetName () != symbolName {
44- continue
45- }
46- } else {
47- // For unqualified names like "Method"
48- if v .Kind == protocol .Method {
49- // For methods, only match if the method name matches exactly Type.symbolName or Type::symbolName or symbolName
50- if ! strings .HasSuffix (symbol .GetName (), "::" + symbolName ) && ! strings .HasSuffix (symbol .GetName (), "." + symbolName ) && symbol .GetName () != symbolName {
51- continue
52- }
53- } else if symbol .GetName () != symbolName {
54- // For non-methods, exact match only
55- continue
56- }
57- }
58- default :
59- if symbol .GetName () != symbolName {
60- continue
61- }
62- }
32+ // Determine if the query is qualified (e.g., Class.method or ns::Class)
33+ isQualified := strings .Contains (normalized , "." ) || strings .Contains (normalized , "::" )
34+ // Extract container and member parts if qualified
35+ containerWanted := ""
36+ memberWanted := normalized
37+ if isQualified {
38+ // Split on last occurrence of '.' or '::'
39+ // Prefer the right-most separator to support nested containers
40+ if idx := strings .LastIndex (normalized , "::" ); idx != - 1 {
41+ containerWanted = normalized [:idx ]
42+ memberWanted = normalized [idx + 2 :]
43+ } else if idx := strings .LastIndex (normalized , "." ); idx != - 1 {
44+ containerWanted = normalized [:idx ]
45+ memberWanted = normalized [idx + 1 :]
46+ }
47+ }
48+
49+ for _ , symbol := range results {
50+ kind := ""
51+ container := ""
52+
53+ // Skip symbols that we are not looking for. workspace/symbol may return
54+ // a large number of fuzzy matches.
55+ switch v := symbol .(type ) {
56+ case * protocol.SymbolInformation :
57+ // SymbolInformation results have richer data.
58+ kind = fmt .Sprintf ("Kind: %s\n " , protocol .TableKindMap [v .Kind ])
59+ if v .ContainerName != "" {
60+ container = fmt .Sprintf ("Container Name: %s\n " , v .ContainerName )
61+ }
62+
63+ // Matching strategy
64+ name := symbol .GetName ()
65+ if isQualified {
66+ // Qualified lookup
67+ // Accept if:
68+ // - name equals memberWanted; and, if we know the container, it matches containerWanted (exact or suffix)
69+ // - or name equals the full normalized string (some servers include container in name)
70+ if name != memberWanted && name != normalized && ! strings .HasSuffix (name , "::" + memberWanted ) && ! strings .HasSuffix (name , "." + memberWanted ) {
71+ continue
72+ }
73+ if containerWanted != "" && v .ContainerName != "" {
74+ // containerName may be only the immediate container. Allow suffix match to tolerate nesting
75+ if v .ContainerName != containerWanted &&
76+ ! strings .HasSuffix (v .ContainerName , "." + containerWanted ) &&
77+ ! strings .HasSuffix (v .ContainerName , "::" + containerWanted ) &&
78+ ! strings .HasSuffix (containerWanted , "." + v .ContainerName ) &&
79+ ! strings .HasSuffix (containerWanted , "::" + v .ContainerName ) {
80+ continue
81+ }
82+ }
83+ } else {
84+ // Unqualified lookup
85+ if name != normalized {
86+ // For methods, also allow suffix match like *.method
87+ if v .Kind == protocol .Method {
88+ if ! strings .HasSuffix (name , "." + normalized ) && ! strings .HasSuffix (name , "::" + normalized ) {
89+ continue
90+ }
91+ } else {
92+ continue
93+ }
94+ }
95+ }
96+ default :
97+ if ! isQualified {
98+ if symbol .GetName () != normalized {
99+ continue
100+ }
101+ } else {
102+ // For unknown result types, require exact match with full normalized string
103+ if symbol .GetName () != normalized &&
104+ ! strings .HasSuffix (symbol .GetName (), "." + memberWanted ) &&
105+ ! strings .HasSuffix (symbol .GetName (), "::" + memberWanted ) {
106+ continue
107+ }
108+ }
109+ }
63110
64111 toolsLogger .Debug ("Found symbol: %s" , symbol .GetName ())
65112 loc := symbol .GetLocation ()
0 commit comments