Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/linkify/test/vscode-node/findSymbol.test.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import * as vscode from 'vscode';
8
import { findBestSymbolByPath } from '../../vscode-node/findSymbol';
9
10
suite('Find symbol', () => {
11
function docSymbol(name: string, ...children: vscode.DocumentSymbol[]): vscode.DocumentSymbol {
12
return {
13
name,
14
children,
15
detail: '',
16
range: new vscode.Range(0, 0, 0, 0),
17
selectionRange: new vscode.Range(0, 0, 0, 0),
18
kind: vscode.SymbolKind.Variable,
19
};
20
}
21
22
function symbolInfo(name: string): vscode.SymbolInformation {
23
return {
24
name,
25
containerName: '',
26
kind: vscode.SymbolKind.Variable,
27
location: {
28
uri: vscode.Uri.file('fake'),
29
range: new vscode.Range(0, 0, 0, 0),
30
}
31
};
32
}
33
34
test('Should find exact match', () => {
35
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a')?.name, 'a');
36
assert.strictEqual(findBestSymbolByPath([symbolInfo('a')], 'a')?.name, 'a');
37
});
38
39
test('Should find nested', () => {
40
assert.strictEqual(findBestSymbolByPath([docSymbol('x', docSymbol('a'))], 'a')?.name, 'a');
41
});
42
43
test('Should find child match', () => {
44
assert.strictEqual(findBestSymbolByPath([docSymbol('a', docSymbol('b'))], 'a.b')?.name, 'b');
45
});
46
47
test('Should find child match skipping level', () => {
48
assert.strictEqual(findBestSymbolByPath([docSymbol('a', docSymbol('x', docSymbol('b')))], 'a.b')?.name, 'b');
49
});
50
51
test(`Should find match even when children don't match`, () => {
52
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a.b')?.name, 'a');
53
});
54
55
test(`Should find longest match`, () => {
56
assert.strictEqual(findBestSymbolByPath([
57
docSymbol('a',
58
docSymbol('x')),
59
docSymbol('x',
60
docSymbol('a',
61
docSymbol('b',
62
docSymbol('z'))))
63
], 'a.b')?.name, 'b');
64
});
65
66
test('Should ignore function call notation', () => {
67
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a()')?.name, 'a');
68
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a(1, 2, 3)')?.name, 'a');
69
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a(b, c)')?.name, 'a');
70
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a(b: string)')?.name, 'a');
71
});
72
73
test('Should ignore generic notation', () => {
74
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a<T>')?.name, 'a');
75
assert.strictEqual(findBestSymbolByPath([docSymbol('a')], 'a<T>.b')?.name, 'a');
76
});
77
78
test('Should match on symbols with $', () => {
79
assert.strictEqual(findBestSymbolByPath([docSymbol('$a')], '$a')?.name, '$a');
80
});
81
82
test('Should match on symbols with _', () => {
83
assert.strictEqual(findBestSymbolByPath([docSymbol('_a_')], '_a_')?.name, '_a_');
84
});
85
86
test('Should prefer rightmost symbol in flat symbols', () => {
87
// When symbols are flat (SymbolInformation), prefer the rightmost match
88
// This handles cases like `TextModel.undo()` where we want `undo`, not `TextModel`
89
assert.strictEqual(
90
findBestSymbolByPath([
91
symbolInfo('TextModel'),
92
symbolInfo('undo')
93
], 'TextModel.undo()')?.name,
94
'undo'
95
);
96
});
97
98
test('Should fall back to leftmost symbol if rightmost not found in flat symbols', () => {
99
// If the rightmost part isn't found, fall back to leftmost matches
100
assert.strictEqual(
101
findBestSymbolByPath([
102
symbolInfo('TextModel'),
103
symbolInfo('someOtherMethod')
104
], 'TextModel.undo()')?.name,
105
'TextModel'
106
);
107
});
108
109
test('Should prefer hierarchical match over flat last part match', () => {
110
// When both hierarchical and flat symbols exist, prefer the hierarchical match
111
assert.strictEqual(
112
findBestSymbolByPath([
113
docSymbol('TextModel', docSymbol('undo')),
114
symbolInfo('undo') // This is a different undo from a different class
115
], 'TextModel.undo()')?.name,
116
'undo'
117
);
118
});
119
120
test('Should handle deeply qualified names', () => {
121
// Test multiple levels of qualification
122
assert.strictEqual(
123
findBestSymbolByPath([
124
docSymbol('namespace', docSymbol('TextModel', docSymbol('undo')))
125
], 'namespace.TextModel.undo()')?.name,
126
'undo'
127
);
128
129
// With flat symbols, prefer the rightmost part
130
assert.strictEqual(
131
findBestSymbolByPath([
132
symbolInfo('namespace'),
133
symbolInfo('TextModel'),
134
symbolInfo('undo')
135
], 'namespace.TextModel.undo()')?.name,
136
'undo'
137
);
138
139
// Middle part should be preferred over leftmost
140
assert.strictEqual(
141
findBestSymbolByPath([
142
symbolInfo('namespace'),
143
symbolInfo('TextModel')
144
], 'namespace.TextModel.undo()')?.name,
145
'TextModel'
146
);
147
});
148
149
test('Should handle mixed flat and hierarchical symbols', () => {
150
// Some symbols are flat, some are nested
151
assert.strictEqual(
152
findBestSymbolByPath([
153
symbolInfo('Model'),
154
docSymbol('TextModel', docSymbol('undo')),
155
symbolInfo('OtherClass')
156
], 'TextModel.undo()')?.name,
157
'undo'
158
);
159
});
160
161
test('Should handle Python-style naming conventions', () => {
162
// Python uses underscores instead of camelCase
163
assert.strictEqual(
164
findBestSymbolByPath([
165
docSymbol('MyClass', docSymbol('my_method'))
166
], 'MyClass.my_method()')?.name,
167
'my_method'
168
);
169
170
// Python dunder methods
171
assert.strictEqual(
172
findBestSymbolByPath([
173
docSymbol('MyClass', docSymbol('__init__'))
174
], 'MyClass.__init__()')?.name,
175
'__init__'
176
);
177
178
// Python private methods
179
assert.strictEqual(
180
findBestSymbolByPath([
181
docSymbol('MyClass', docSymbol('_private_method'))
182
], 'MyClass._private_method()')?.name,
183
'_private_method'
184
);
185
});
186
187
test('Should handle Python module qualified names', () => {
188
// Python: module.Class.method
189
assert.strictEqual(
190
findBestSymbolByPath([
191
docSymbol('my_module', docSymbol('MyClass', docSymbol('my_method')))
192
], 'my_module.MyClass.my_method()')?.name,
193
'my_method'
194
);
195
});
196
197
test('Should prefer rightmost match in flat symbols using position-based priority', () => {
198
// When both class and method exist as flat symbols, prefer rightmost
199
assert.strictEqual(
200
findBestSymbolByPath([
201
symbolInfo('TextModel'), // matchCount=1 (index 0)
202
symbolInfo('undo') // matchCount=2 (index 1)
203
], 'TextModel.undo()')?.name,
204
'undo'
205
);
206
207
// Reverse order - should still prefer undo due to higher matchCount
208
assert.strictEqual(
209
findBestSymbolByPath([
210
symbolInfo('undo'), // matchCount=2 (index 1)
211
symbolInfo('TextModel') // matchCount=1 (index 0)
212
], 'TextModel.undo()')?.name,
213
'undo'
214
);
215
216
// Works for longer qualified names too
217
// For 'a.b.c.d' => ['a', 'b', 'c', 'd']:
218
// 'd' (index 3, matchCount=4) > 'c' (index 2, matchCount=3) > 'b' (index 1, matchCount=2) > 'a' (index 0, matchCount=1)
219
assert.strictEqual(
220
findBestSymbolByPath([
221
symbolInfo('a'), // matchCount=1
222
symbolInfo('b'), // matchCount=2
223
symbolInfo('c'), // matchCount=3
224
], 'a.b.c.d')?.name,
225
'c' // Highest matchCount among available symbols
226
);
227
});
228
});
229
230