@@ -15,7 +15,9 @@ export type Declarations = { [word: string]: LSP.SymbolInformation[] }
15
15
16
16
/**
17
17
* Returns declarations (functions or variables) from a given root node
18
- * that would be available after sourcing the file.
18
+ * that would be available after sourcing the file. This currently does
19
+ * not include global variables defined inside function as we do not do
20
+ * any flow tracing.
19
21
*
20
22
* Will only return one declaration per symbol name – the latest definition.
21
23
* This behavior is consistent with how Bash behaves, but differs between
@@ -64,6 +66,7 @@ export function getGlobalDeclarations({
64
66
65
67
/**
66
68
* Returns all declarations (functions or variables) from a given tree.
69
+ * This includes local variables.
67
70
*/
68
71
export function getAllDeclarationsInTree ( {
69
72
tree,
@@ -91,12 +94,10 @@ export function getAllDeclarationsInTree({
91
94
92
95
/**
93
96
* Returns declarations available for the given file and location.
94
- * Done by traversing the tree upwards (which is a simplification for
95
- * actual bash behaviour but deemed good enough, compared to the complexity of flow tracing).
96
- * Filters out duplicate definitions. Used when getting declarations for the current scope.
97
+ * The heuristics used is a simplification compared to bash behaviour,
98
+ * but deemed good enough, compared to the complexity of flow tracing.
97
99
*
98
- * FIXME: unfortunately this doesn't capture all global variables defined inside functions.
99
- * Wondering if getGlobalDeclarations should return this or we should make that a custom feature of this function.
100
+ * Used when getting declarations for the current scope.
100
101
*/
101
102
export function getLocalDeclarations ( {
102
103
node,
@@ -107,8 +108,7 @@ export function getLocalDeclarations({
107
108
} ) : Declarations {
108
109
const declarations : Declarations = { }
109
110
110
- // bottom up traversal of the tree to capture all local declarations
111
-
111
+ // Bottom up traversal to capture all local and scoped declarations
112
112
const walk = ( node : Parser . SyntaxNode | null ) => {
113
113
// NOTE: there is also node.walk
114
114
if ( node ) {
@@ -145,6 +145,58 @@ export function getLocalDeclarations({
145
145
146
146
walk ( node )
147
147
148
+ // Top down traversal to add missing global variables from within functions
149
+ if ( node ) {
150
+ const rootNode =
151
+ node . type === 'program'
152
+ ? node
153
+ : TreeSitterUtil . findParent ( node , ( p ) => p . type === 'program' )
154
+ if ( ! rootNode ) {
155
+ throw new Error ( 'did not find root node' )
156
+ }
157
+
158
+ Object . entries (
159
+ getAllGlobalVariableDeclarations ( {
160
+ rootNode,
161
+ uri,
162
+ } ) ,
163
+ ) . map ( ( [ name , symbols ] ) => {
164
+ if ( ! declarations [ name ] ) {
165
+ declarations [ name ] = symbols
166
+ }
167
+ } )
168
+ }
169
+
170
+ return declarations
171
+ }
172
+
173
+ function getAllGlobalVariableDeclarations ( {
174
+ uri,
175
+ rootNode,
176
+ } : {
177
+ uri : string
178
+ rootNode : Parser . SyntaxNode
179
+ } ) {
180
+ const declarations : Declarations = { }
181
+
182
+ TreeSitterUtil . forEach ( rootNode , ( node : Parser . SyntaxNode ) => {
183
+ if (
184
+ node . type === 'variable_assignment' &&
185
+ // exclude local variables
186
+ node . parent ?. type !== 'declaration_command'
187
+ ) {
188
+ const symbol = nodeToSymbolInformation ( { node, uri } )
189
+ if ( symbol ) {
190
+ if ( ! declarations [ symbol . name ] ) {
191
+ declarations [ symbol . name ] = [ ]
192
+ }
193
+ declarations [ symbol . name ] . push ( symbol )
194
+ }
195
+ }
196
+
197
+ return
198
+ } )
199
+
148
200
return declarations
149
201
}
150
202
0 commit comments