Skip to content

Commit edc0e70

Browse files
author
Calvinn Ng
committed
improve autocompletion to include recent edits and recent files
1 parent f4ca90b commit edc0e70

File tree

11 files changed

+1075
-247
lines changed

11 files changed

+1075
-247
lines changed

core/autocomplete/brackets.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* We follow the policy of only completing bracket pairs that we started
3+
* But sometimes we started the pair in a previous autocomplete suggestion
4+
*/
5+
export class BracketMatchingService {
6+
private openingBracketsFromLastCompletion: string[] = [];
7+
private lastCompletionFile: string | undefined = undefined;
8+
9+
static BRACKETS: { [key: string]: string } = { "(": ")", "{": "}", "[": "]" };
10+
static BRACKETS_REVERSE: { [key: string]: string } = {
11+
")": "(",
12+
"}": "{",
13+
"]": "[",
14+
};
15+
16+
handleAcceptedCompletion(completion: string, filepath: string) {
17+
this.openingBracketsFromLastCompletion = [];
18+
const stack: string[] = [];
19+
20+
for (let i = 0; i < completion.length; i++) {
21+
const char = completion[i];
22+
if (Object.keys(BracketMatchingService.BRACKETS).includes(char)) {
23+
// It's an opening bracket
24+
stack.push(char);
25+
} else if (
26+
Object.values(BracketMatchingService.BRACKETS).includes(char)
27+
) {
28+
// It's a closing bracket
29+
if (
30+
stack.length === 0 ||
31+
BracketMatchingService.BRACKETS[stack.pop()!] !== char
32+
) {
33+
break;
34+
}
35+
}
36+
}
37+
38+
// Any remaining opening brackets in the stack are uncompleted
39+
this.openingBracketsFromLastCompletion = stack;
40+
this.lastCompletionFile = filepath;
41+
}
42+
43+
async *stopOnUnmatchedClosingBracket(
44+
stream: AsyncGenerator<string>,
45+
suffix: string,
46+
filepath: string,
47+
): AsyncGenerator<string> {
48+
// Add opening brackets from the previous response
49+
let stack: string[] = [];
50+
if (this.lastCompletionFile === filepath) {
51+
stack = [...this.openingBracketsFromLastCompletion];
52+
} else {
53+
this.lastCompletionFile = undefined;
54+
}
55+
56+
// Add corresponding open brackets from suffix to stack
57+
for (let i = 0; i < suffix.length; i++) {
58+
if (suffix[i] === " ") continue;
59+
const openBracket = BracketMatchingService.BRACKETS_REVERSE[suffix[i]];
60+
if (!openBracket) break;
61+
stack.unshift(openBracket);
62+
}
63+
64+
let all = "";
65+
let seenNonWhitespaceOrClosingBracket = false;
66+
for await (let chunk of stream) {
67+
// Allow closing brackets before any non-whitespace characters
68+
if (!seenNonWhitespaceOrClosingBracket) {
69+
const firstNonWhitespaceOrClosingBracketIndex =
70+
chunk.search(/[^\s\)\}\]]/);
71+
if (firstNonWhitespaceOrClosingBracketIndex !== -1) {
72+
yield chunk.slice(0, firstNonWhitespaceOrClosingBracketIndex);
73+
chunk = chunk.slice(firstNonWhitespaceOrClosingBracketIndex);
74+
seenNonWhitespaceOrClosingBracket = true;
75+
} else {
76+
yield chunk;
77+
continue;
78+
}
79+
}
80+
81+
all += chunk;
82+
const allLines = all.split("\n");
83+
for (let i = 0; i < chunk.length; i++) {
84+
const char = chunk[i];
85+
if (Object.values(BracketMatchingService.BRACKETS).includes(char)) {
86+
// It's a closing bracket
87+
if (
88+
stack.length === 0 ||
89+
BracketMatchingService.BRACKETS[stack.pop()!] !== char
90+
) {
91+
// If the stack is empty or the top of the stack doesn't match the current closing bracket
92+
yield chunk.slice(0, i);
93+
return; // Stop the generator if the closing bracket doesn't have a matching opening bracket in the stream
94+
}
95+
} else if (
96+
Object.keys(BracketMatchingService.BRACKETS).includes(char)
97+
) {
98+
// It's an opening bracket
99+
stack.push(char);
100+
}
101+
}
102+
yield chunk;
103+
}
104+
}
105+
}

core/autocomplete/charStream.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export async function* onlyWhitespaceAfterEndOfLine(
22
stream: AsyncGenerator<string>,
33
endOfLine: string[],
4+
fullStop: () => void,
45
): AsyncGenerator<string> {
56
let pending = "";
67
for await (let chunk of stream) {
@@ -12,6 +13,7 @@ export async function* onlyWhitespaceAfterEndOfLine(
1213
chunk[i + 1].trim() === chunk[i + 1]
1314
) {
1415
yield chunk.slice(0, i + 1);
16+
fullStop();
1517
return;
1618
}
1719
}
@@ -27,11 +29,13 @@ export async function* onlyWhitespaceAfterEndOfLine(
2729

2830
export async function* noFirstCharNewline(stream: AsyncGenerator<string>) {
2931
let first = true;
30-
for await (let char of stream) {
32+
for await (const char of stream) {
3133
if (first) {
3234
first = false;
33-
if (char === "\n") return;
35+
if (char.startsWith("\n") || char.startsWith("\r")) {
36+
return;
37+
}
3438
}
3539
yield char;
3640
}
37-
}
41+
}

0 commit comments

Comments
 (0)