Skip to content

Commit 69e86ed

Browse files
committed
fix non-alphabetic auto-completion
Signed-off-by: Qingpeng Li <qingpeng9802@gmail.com>
1 parent 147b470 commit 69e86ed

File tree

11 files changed

+212
-144
lines changed

11 files changed

+212
-144
lines changed

.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"plugins": [
1616
"node",
1717
"import",
18-
"@typescript-eslint"
18+
"@typescript-eslint",
19+
"unused-imports"
1920
],
2021
"extends": [
2122
"eslint:recommended",

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change Log
22

3+
## [1.2.3] - 2023-06-27
4+
### Fixed
5+
- fix non-alphabetic auto-completion
6+
37
## [1.2.2] - 2023-06-26
48
### Changed
59
- `README.md` update README

package-lock.json

Lines changed: 33 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"name": "Qingpeng Li",
77
"email": "qingpeng9802@gmail.com"
88
},
9-
"version": "1.2.2",
9+
"version": "1.2.3",
1010
"publisher": "qingpeng",
1111
"engines": {
1212
"vscode": "^1.63.0"
@@ -302,6 +302,7 @@
302302
"eslint-import-resolver-typescript": "^3.5.5",
303303
"eslint-plugin-import": "^2.27.5",
304304
"eslint-plugin-node": "^11.1.0",
305+
"eslint-plugin-unused-imports": "^2.0.0",
305306
"fs": "^0.0.1-security",
306307
"js-yaml": "^4.1.0",
307308
"process": "^0.11.10",

src/web/builders/call_hierarchy_builder/call_hierarchy_builder.ts

Lines changed: 106 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,28 @@ function buildEdge(
4141
return;
4242
}
4343

44-
function getRealCaller(
44+
function getRealIsCalled(
4545
globalOrderedRange: [string, [number, number]], currDocSymbolInfo: DocSymbolInfo,
4646
): [string, vscode.Range, SymbolInfo] | undefined {
4747

48-
const [currCallerName, currCallerRange] = globalOrderedRange;
49-
const [realCaller, shadow] = currDocSymbolInfo.getSymbolWithShadowByRange(currCallerName, currCallerRange, undefined);
50-
if (realCaller === undefined) {
48+
const [isCalledName, isCalledRange] = globalOrderedRange;
49+
const [realIsCalled, shadow] = currDocSymbolInfo.getSymbolWithShadowByRange(isCalledName, isCalledRange, undefined);
50+
if (realIsCalled === undefined) {
5151
return undefined;
5252
}
53-
if (shadow !== undefined && shadow.length !== 0 && isShadowed(currCallerRange, shadow)) {
53+
if (shadow !== undefined && shadow.length !== 0 && isShadowed(isCalledRange, shadow)) {
5454
return undefined;
5555
}
56-
if (isRangeIntExcludedRanges(currCallerRange, currDocSymbolInfo.docRes.commentAndStringRange)) {
56+
if (isRangeIntExcludedRanges(isCalledRange, currDocSymbolInfo.docRes.commentAndStringRange)) {
5757
return undefined;
5858
}
5959

6060
const callAppearRange = new vscode.Range(
61-
currDocSymbolInfo.document.positionAt(currCallerRange[0]),
62-
currDocSymbolInfo.document.positionAt(currCallerRange[1]),
61+
currDocSymbolInfo.document.positionAt(isCalledRange[0]),
62+
currDocSymbolInfo.document.positionAt(isCalledRange[1]),
6363
);
64-
return [currCallerName, callAppearRange, realCaller];
64+
65+
return [isCalledName, callAppearRange, realIsCalled];
6566
}
6667

6768
function genAllCallHierarchyItems(
@@ -73,44 +74,28 @@ function genAllCallHierarchyItems(
7374
const callHierarchyICs = callHrchyInfo.incomingCall;
7475
const callHierarchyOGs = callHrchyInfo.outgoingCall;
7576

76-
// fileItem
77+
// make the file as definition, that is,
78+
// make the file as caller
7779
const fileName = currDocSymbolInfo.document.uri.path.split('/').pop();
78-
const isCalledName = (fileName !== undefined) ? fileName : 'Untitled';
79-
if (!callHierarchyOGs.has(isCalledName)) {
80-
callHierarchyOGs.set(isCalledName, new Map<string, vscode.CallHierarchyOutgoingCall>());
80+
const callerName = (fileName !== undefined) ? fileName : 'Untitled';
81+
if (!callHierarchyOGs.has(callerName)) {
82+
callHierarchyOGs.set(callerName, new Map<string, vscode.CallHierarchyOutgoingCall>());
8183
}
8284

85+
// find isCalled ranges who is NOT visited. (orphans)
8386
for (let i = 0; i < globalOrderedRanges.length; ++i) {
8487
if (visited.has(i)) {
8588
continue;
8689
}
8790

88-
const realCallerRes = getRealCaller(globalOrderedRanges[i], currDocSymbolInfo);
89-
if (realCallerRes === undefined) {
90-
continue;
91-
}
92-
const [currCallerName, callAppearRange, realCaller] = realCallerRes;
93-
const realCallerStr = realCaller.stringify();
94-
if (callHrchyItems.get(currCallerName) === undefined) {
95-
callHrchyItems.set(currCallerName, new Map([[realCallerStr, buildCallHierarchyItem(realCaller)]]));
96-
}
97-
const toItem = callHrchyItems.get(currCallerName)?.get(realCallerStr);
98-
99-
// make the range circle back
100-
const fromItem = new vscode.CallHierarchyItem(
101-
vscode.SymbolKind.Namespace, isCalledName, currDocSymbolInfo.document.uri.path, currDocSymbolInfo.document.uri,
102-
realCaller.loc.range, realCaller.loc.range
91+
const isCalledRange = globalOrderedRanges[i];
92+
// only build IncomingCall Edge since the file cannot be called,
93+
// that is, cannot be `toItems`
94+
buildEdgeForIsCalledRange(
95+
isCalledRange,
96+
currDocSymbolInfo, callHrchyItems, callHierarchyICs, callHierarchyOGs,
97+
callerName, undefined, undefined
10398
);
104-
105-
if (!callHierarchyICs.has(currCallerName)) {
106-
callHierarchyICs.set(currCallerName, new Map<string, vscode.CallHierarchyIncomingCall>());
107-
}
108-
109-
buildEdge(
110-
callHierarchyICs.get(currCallerName)!, realCallerStr, fromItem,
111-
callHierarchyOGs.get(isCalledName)!, undefined, toItem, [callAppearRange]
112-
);
113-
11499
}
115100
return callHrchyInfo;
116101
}
@@ -119,6 +104,7 @@ function genAllCallHierarchyItems(
119104
function genAllCallHierarchyItemsNonOrphan(
120105
currDocSymbolInfo: DocSymbolInfo, globalOrderedRanges: [string, [number, number]][]
121106
): [CallHrchyInfo, Set<number>] {
107+
// init
122108
const visited: Set<number> = new Set();
123109

124110
const callHrchyItems: Map<string, Map<string, vscode.CallHierarchyItem>> =
@@ -134,69 +120,108 @@ function genAllCallHierarchyItemsNonOrphan(
134120
return a.numRange[0] - b.numRange[0];
135121
});
136122

123+
// iterate all globalDef as caller
124+
// for example a() {b, c}, `a` as the caller
137125
for (const info of infos) {
138-
const isCalledName = info.name;
139-
const infoStr = info.stringify();
140-
if (callHrchyItems.get(isCalledName) === undefined) {
141-
callHrchyItems.set(isCalledName, new Map([[infoStr, buildCallHierarchyItem(info)]]));
126+
const callerName = info.name;
127+
const callerSymbolStr = info.stringify();
128+
129+
if (callHrchyItems.get(callerName) === undefined) {
130+
callHrchyItems.set(callerName, new Map([[callerSymbolStr, buildCallHierarchyItem(info)]]));
142131
}
143-
const fromItem = callHrchyItems.get(isCalledName)?.get(infoStr);
132+
const fromItem = callHrchyItems.get(callerName)?.get(callerSymbolStr);
144133
if (fromItem === undefined) {
145134
continue;
146135
}
147136

148-
// caller's range
149-
const callerRange = info.globalDefRange;
150-
if (callerRange === undefined) {
137+
// isCalled is in this range
138+
// for example a() {b, c}, `b` and `c` are in the range
139+
const isCalledRange = info.globalDefRange;
140+
if (isCalledRange === undefined) {
151141
continue;
152142
}
153143

154-
if (!callHierarchyOGs.has(isCalledName)) {
155-
callHierarchyOGs.set(isCalledName, new Map<string, vscode.CallHierarchyOutgoingCall>());
144+
if (!callHierarchyOGs.has(callerName)) {
145+
callHierarchyOGs.set(callerName, new Map<string, vscode.CallHierarchyOutgoingCall>());
156146
}
157147

158-
const idxStart = bisectRight(globalOrderedRanges, callerRange[0], item => item[1][0]);
159-
const idxEnd = bisectRight(globalOrderedRanges, callerRange[1], item => item[1][0]);
148+
const idxStart = bisectRight(globalOrderedRanges, isCalledRange[0], item => item[1][0]);
149+
const idxEnd = bisectRight(globalOrderedRanges, isCalledRange[1], item => item[1][0]);
150+
151+
// find possible isCalleds ranges
160152
for (let i = idxStart; i < idxEnd; ++i) {
161153
visited.add(i);
162154

163-
const realCallerRes = getRealCaller(globalOrderedRanges[i], currDocSymbolInfo);
164-
if (realCallerRes === undefined) {
165-
continue;
166-
}
167-
const [currCallerName, callAppearRange, realCaller] = realCallerRes;
168-
const realCallerStr = realCaller.stringify();
169-
if (callHrchyItems.get(currCallerName) === undefined) {
170-
callHrchyItems.set(currCallerName, new Map([[realCallerStr, buildCallHierarchyItem(realCaller)]]));
171-
}
172-
const toItem = callHrchyItems.get(currCallerName)?.get(realCallerStr);
173-
174-
// this is kind of twisted,
175-
// from-to relation represents the curr context
176-
// definition must be the ISCALLED, execution must be the CALLER, curr context is not matter
177-
// we are creating two directional edges
178-
//
179-
// fromItems |
180-
// \ | / |
181-
// key: 1. currCaller 2. isCalled |
182-
// / | \ |
183-
// toItems \|/
184-
//
185-
// that is, our dictionary needs the opposite info to build an edge
186-
if (!callHierarchyICs.has(currCallerName)) {
187-
callHierarchyICs.set(currCallerName, new Map<string, vscode.CallHierarchyIncomingCall>());
188-
}
189-
190-
buildEdge(
191-
callHierarchyICs.get(currCallerName)!, realCallerStr, fromItem,
192-
callHierarchyOGs.get(isCalledName)!, infoStr, toItem, [callAppearRange]
155+
const isCalledRange = globalOrderedRanges[i];
156+
buildEdgeForIsCalledRange(
157+
isCalledRange,
158+
currDocSymbolInfo, callHrchyItems, callHierarchyICs, callHierarchyOGs,
159+
callerName, callerSymbolStr, fromItem
193160
);
194-
195161
}
162+
196163
}
197164

198165
return [currCallHierarchyInfo, visited];
166+
}
199167

168+
function buildEdgeForIsCalledRange(
169+
isCalledRange: [string, [number, number]],
170+
currDocSymbolInfo: DocSymbolInfo, callHrchyItems: Map<string, Map<string, vscode.CallHierarchyItem>>,
171+
callHierarchyICs: Map<string, Map<string, vscode.CallHierarchyIncomingCall>>,
172+
callHierarchyOGs: Map<string, Map<string, vscode.CallHierarchyOutgoingCall>>,
173+
callerName: string, callerSymbolStr: string | undefined, fromItem: vscode.CallHierarchyItem | undefined,
174+
) {
175+
const realIsCalledRes = getRealIsCalled(isCalledRange, currDocSymbolInfo);
176+
if (realIsCalledRes === undefined) {
177+
return;
178+
}
179+
180+
const [isCalledName, callAppearRange, realIsCalled] = realIsCalledRes;
181+
182+
fromItem = (fromItem === undefined) ?
183+
// when the file is the caller, the file has no `range`.
184+
// Therefore, make the range circle back, that is,
185+
// let the range points to the isCalled itself
186+
new vscode.CallHierarchyItem(
187+
vscode.SymbolKind.Namespace, callerName,
188+
currDocSymbolInfo.document.uri.path, currDocSymbolInfo.document.uri,
189+
realIsCalled.loc.range, realIsCalled.loc.range
190+
) : fromItem;
191+
192+
const realIsCalledStr = realIsCalled.stringify();
193+
if (callHrchyItems.get(isCalledName) === undefined) {
194+
callHrchyItems.set(isCalledName, new Map([[realIsCalledStr, buildCallHierarchyItem(realIsCalled)]]));
195+
}
196+
const toItem = callHrchyItems.get(isCalledName)?.get(realIsCalledStr);
197+
198+
// we are creating two directional edges
199+
//
200+
// IncomingCall OutgoingCall
201+
//
202+
// fromItems
203+
// |
204+
// |
205+
// \|/
206+
// key: 1. isCalled 2. caller
207+
// |
208+
// |
209+
// \|/
210+
// toItems
211+
//
212+
// note that
213+
// `fromItems` is constructed from caller and
214+
// `toItems` is constructed from isCalled.
215+
// that is, our dictionary needs two infos to build an edge
216+
if (!callHierarchyICs.has(isCalledName)) {
217+
callHierarchyICs.set(isCalledName, new Map<string, vscode.CallHierarchyIncomingCall>());
218+
}
219+
220+
buildEdge(
221+
callHierarchyICs.get(isCalledName)!, realIsCalledStr, fromItem,
222+
callHierarchyOGs.get(callerName)!, callerSymbolStr, toItem,
223+
[callAppearRange]
224+
);
200225
}
201226

202227

src/web/builders/comp_item_builder/comp_item_ori_builder.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ function assignKindAndDoc(
3636
): vscode.CompletionItem[] {
3737
const citems: vscode.CompletionItem[] = [];
3838
for (const s of symbolsArr) {
39+
// prevent duplicated items in NonAlphabetic
40+
const prefix = s[0];
41+
if (prefix === '&' || prefix === '*') {
42+
continue;
43+
}
44+
3945
const ci = new vscode.CompletionItem(s, kind);
4046
const doc = getDocByName(s);
4147
if (doc !== undefined) {
@@ -53,7 +59,8 @@ function genOriSymbols(): vscode.CompletionItem[] {
5359
const kind = clKindToVscodeCIKind.get(k)!;
5460
citems.push(...assignKindAndDoc(partSymbols, kind));
5561
}
56-
if (citems.length !== 978) {
62+
if (citems.length !== 923) {
63+
// not 978 since we filtered `&` and `*` out
5764
console.warn(`[Autocompletion] Built incomplete kind list (${citems.length}) of symbols`);
5865
}
5966

@@ -66,7 +73,8 @@ function assignKindAndDocNonAlphabetic(
6673
): vscode.CompletionItem[] {
6774
const citems: vscode.CompletionItem[] = [];
6875
for (const s of symbolsArr) {
69-
const ci = new vscode.CompletionItem(s, kind);
76+
const fullKeyword = prefix + s;
77+
const ci = new vscode.CompletionItem(fullKeyword, kind);
7078

7179
if (prefix === '#' || prefix === '~') {
7280
// we do not get doc for `#` and `~`
@@ -75,7 +83,7 @@ function assignKindAndDocNonAlphabetic(
7583
ci.documentation.supportHtml = true;
7684

7785
} else if (prefix === '&' || prefix === '*') {
78-
const doc = getDocByName(prefix + s);
86+
const doc = getDocByName(fullKeyword);
7987
ci.documentation = doc;
8088

8189
} else if (prefix === ':') {

0 commit comments

Comments
 (0)