Skip to content

Commit 1c7de56

Browse files
committed
Fix first item not being pre-selected
1 parent 94a33bc commit 1c7de56

File tree

2 files changed

+65
-40
lines changed

2 files changed

+65
-40
lines changed

src/completionProvider.ts

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,37 @@ import { TypingHintProvider } from "./typingHintProvider";
1919

2020
export abstract class CompletionProvider {
2121

22-
protected bottomOfListSortPrefix: number = 999;
22+
protected itemSortPrefix: number = 90;
2323

2424
/**
2525
* Push type hints to the end of an array of completion items.
2626
*/
27-
protected pushHintsToItems(typeHints: string[], completionItems: CompletionItem[]) {
28-
const sortTextPrefix = this.bottomOfListSortPrefix.toString();
29-
for (const hint of typeHints) {
30-
const item = new CompletionItem(this.labelFor(hint), CompletionItemKind.TypeParameter);
31-
item.sortText = sortTextPrefix + hint;
32-
completionItems.push(item);
27+
protected pushHintsToItems(typeHints: string[], completionItems: CompletionItem[], firstItemSelected: boolean) {
28+
const sortTextPrefix = this.itemSortPrefix.toString();
29+
completionItems.push(
30+
firstItemSelected
31+
? this.selectedCompletionItem(typeHints[0])
32+
: this.newCompletionitem(typeHints[0], sortTextPrefix)
33+
);
34+
35+
for (let i = 1; i < typeHints.length; i++) {
36+
completionItems.push(this.newCompletionitem(typeHints[i], sortTextPrefix));
3337
}
3438
}
3539

40+
private newCompletionitem = (hint: string, sortTextPrefix: string): CompletionItem => {
41+
const item = new CompletionItem(this.labelFor(hint), CompletionItemKind.TypeParameter);
42+
item.sortText = sortTextPrefix + hint;
43+
return item;
44+
};
45+
46+
protected selectedCompletionItem(typeHint: string, sortTextPrefix: string = "0b"): CompletionItem {
47+
let item = new CompletionItem(this.labelFor(typeHint), CompletionItemKind.TypeParameter);
48+
item.sortText = `${sortTextPrefix}${typeHint}`;
49+
item.preselect = true;
50+
return item;
51+
}
52+
3653
protected labelFor(typeHint: string): string {
3754
return " " + typeHint;
3855
}
@@ -74,33 +91,28 @@ export class ParamHintCompletionProvider extends CompletionProvider implements C
7491
const typeContainer = getDataTypeContainer();
7592
const provider = new TypeHintProvider(typeContainer);
7693
const wsSearcher = new WorkspaceSearcher(doc.uri, this.settings, typeContainer);
77-
94+
let estimations: string[] = [];
95+
7896
if (param) {
7997
const workspaceHintSearch = this.settings.workspaceSearchEnabled
8098
? this.workspaceHintSearch(param, wsSearcher, documentText)
8199
: null;
82100
try {
83-
const estimations = await provider.estimateTypeHints(param, documentText);
101+
estimations = await provider.estimateTypeHints(param, documentText);
84102
if (estimations.length > 0) {
85103
this.pushEstimationsToItems(estimations, items);
86104
wsSearcher.cancel();
87105
}
88106
} catch {
89107
}
90108

91-
const sortTextPrefix = (this.bottomOfListSortPrefix - 1).toString();
92-
for (const hint of provider.remainingTypeHints()) {
93-
let item = new CompletionItem(this.labelFor(hint), CompletionItemKind.TypeParameter);
94-
item.sortText = sortTextPrefix + hint;
95-
items.push(item);
96-
}
97-
98-
if (provider.typingImported) {
99-
this.pushHintsToItems(provider.remainingTypingHints(), items);
100-
}
109+
this.pushHintsToItems(provider.remainingTypeHints(), items, estimations.length === 0);
110+
this.itemSortPrefix++;
111+
this.pushHintsToItems(provider.remainingTypingHints(), items, false);
112+
101113
const hint = await workspaceHintSearch;
102114
if (hint && provider.hintNotProvided(hint)) {
103-
items.unshift(this.toSelectedCompletionItem(hint));
115+
items.unshift(this.selectedCompletionItem(hint, "0a"));
104116
}
105117
return Promise.resolve(new CompletionList(items, false));
106118
}
@@ -131,7 +143,7 @@ export class ParamHintCompletionProvider extends CompletionProvider implements C
131143
private pushEstimationsToItems(typeHints: string[], items: CompletionItem[]) {
132144

133145
if (typeHints.length > 0) {
134-
items.push(this.toSelectedCompletionItem(typeHints[0]));
146+
items.push(this.selectedCompletionItem(typeHints[0]));
135147

136148
for (let i = 1; i < typeHints.length; i++) {
137149
let item = new CompletionItem(this.labelFor(typeHints[i]), CompletionItemKind.TypeParameter);
@@ -141,13 +153,6 @@ export class ParamHintCompletionProvider extends CompletionProvider implements C
141153
}
142154
}
143155

144-
private toSelectedCompletionItem(typeHint: string): CompletionItem {
145-
let item = new CompletionItem(this.labelFor(typeHint), CompletionItemKind.TypeParameter);
146-
item.sortText = `0${typeHint}`;
147-
item.preselect = true;
148-
return item;
149-
}
150-
151156
private shouldProvideItems(precedingText: string, activePos: Position, doc: TextDocument): boolean {
152157

153158
if (activePos.character > 0 && !/#/.test(precedingText)) {
@@ -185,11 +190,13 @@ export class ReturnHintCompletionProvider extends CompletionProvider implements
185190

186191
if (this.shouldProvideItems(line, pos)) {
187192
const provider = new TypingHintProvider(getDataTypeContainer());
188-
await provider.detectTypingImport(doc.getText());
189-
this.pushHintsToItems(provider.getRemainingHints(), items);
190-
this.bottomOfListSortPrefix--;
193+
const detectTypingImport = provider.detectTypingImport(doc.getText());
194+
195+
this.pushHintsToItems(Object.values(PythonType), items, true);
196+
this.itemSortPrefix++;
191197

192-
this.pushHintsToItems(Object.values(PythonType), items);
198+
await detectTypingImport;
199+
this.pushHintsToItems(provider.getRemainingHints(), items, false);
193200
}
194201
return Promise.resolve(new CompletionList(items, false));
195202
}

test/providers/completionProvider.test.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,25 @@ suite('ParamHintCompletionProvider', () => {
6060
assert.notEqual(actual, null, messageFor(data, expected, actual));
6161
});
6262

63-
test("provides default items", async () => {
63+
test("provides default items if nothing is detected", async () => {
6464
let param = "notFound:";
65-
let expected = Object.values(PythonType).sort();
65+
let expected = typeHints().concat(typingHints());
6666
let result = await providerResult(provider, param);
6767

6868
assert.notEqual(result, null);
69-
let actual: string[] = [];
70-
result?.items.forEach((item) => { actual.push(item.label.trim()); });
71-
69+
const actual: string[] | undefined = result?.items.map(item => item.label.trim());
7270
assert.deepEqual(actual, expected);
7371
});
7472

7573
test("provides type estimations + default items", async () => {
7674
let param = "param:";
77-
let expected = Object.values(PythonType).length + 1;
75+
let expected = ["Class"].concat(typeHints()).concat(typingHints());
76+
7877
let result = await providerResult(provider, param, "\n\nparam = Class()");
79-
assert.equal(result?.items.length, expected);
78+
79+
assert.notEqual(result, null);
80+
const actual: string[] | undefined = result?.items.map(item => item.label.trim());
81+
assert.deepEqual(actual, expected);
8082
});
8183

8284
test("does not provide items unless a function def is detected", async () => {
@@ -176,4 +178,20 @@ async function provideCompletionItems(
176178
const ctx = { triggerCharacter: paramHintTrigger, triggerKind: vsc.CompletionTriggerKind.TriggerCharacter };
177179

178180
return provider.provideCompletionItems(doc, pos, token, ctx);
179-
}
181+
}
182+
183+
const typeHints = (): string[] => Object.values(PythonType).sort();
184+
185+
const typingHints = (): string[] => {
186+
const prefix = "typing.";
187+
return [
188+
`Dict[`,
189+
`List[`,
190+
`Set[`,
191+
`Tuple[`,
192+
`${prefix}Dict[`,
193+
`${prefix}List[`,
194+
`${prefix}Set[`,
195+
`${prefix}Tuple[`
196+
];
197+
};

0 commit comments

Comments
 (0)