Skip to content

Commit d96a9f4

Browse files
committed
Support explicit completion request
1 parent 88b2c0d commit d96a9f4

File tree

4 files changed

+64
-8
lines changed

4 files changed

+64
-8
lines changed

demo/index.html

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ <h3>TypeScript AI autocompletion</h3>
2424

2525
<h3>Python AI autocompletion</h3>
2626
<div id="editor-python"></div>
27+
28+
<h3>TypeScript AI autocompletion (cmd+k to trigger completion)</h3>
29+
<div id="editor-explicit"></div>
2730
</main>
2831
<script type="module" src="./index.ts"></script>
2932
</body>

demo/index.ts

+43
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
copilotPlugin,
77
} from "../src/plugin.js";
88
import { python } from "@codemirror/lang-python";
9+
import { keymap } from "@codemirror/view";
10+
import { startCompletion } from "../src/commands.js";
911

1012
new EditorView({
1113
doc: "// Factorial function",
@@ -41,6 +43,47 @@ const hiddenValue = "https://macwright.com/"`,
4143
parent: document.querySelector("#editor")!,
4244
});
4345

46+
new EditorView({
47+
doc: "// Factorial function (explicit trigger)",
48+
extensions: [
49+
basicSetup,
50+
javascript({
51+
typescript: true,
52+
jsx: true,
53+
}),
54+
codeiumOtherDocumentsConfig.of({
55+
override: () => [
56+
{
57+
absolutePath: "https://esm.town/v/foo.ts",
58+
text: `export const foo = 10;
59+
60+
const hiddenValue = "https://macwright.com/"`,
61+
language: Language.TYPESCRIPT,
62+
editorLanguage: "typescript",
63+
},
64+
],
65+
}),
66+
copilotPlugin({
67+
apiKey: "d49954eb-cfba-4992-980f-d8fb37f0e942",
68+
shouldComplete(context) {
69+
if (context.tokenBefore(["String"])) {
70+
return true;
71+
}
72+
const match = context.matchBefore(/(@(?:\w*))(?:[./](\w*))?/);
73+
return !match;
74+
},
75+
alwaysOn: false,
76+
}),
77+
keymap.of([
78+
{
79+
key: "Cmd-k",
80+
run: startCompletion,
81+
},
82+
]),
83+
],
84+
parent: document.querySelector("#editor-explicit")!,
85+
});
86+
4487
new EditorView({
4588
doc: "def hi_python():",
4689
extensions: [

src/commands.ts

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
addSuggestions,
88
clearSuggestion,
99
} from "./effects.js";
10+
import { requestCompletion } from "./completionRequester.js";
1011

1112
/**
1213
* Accepting a suggestion: we remove the ghost text, which
@@ -174,3 +175,8 @@ export function sameKeyCommand(view: EditorView, key: string) {
174175
}
175176
return rejectSuggestionCommand(view);
176177
}
178+
179+
export const startCompletion: Command = (view: EditorView) => {
180+
requestCompletion(view);
181+
return true;
182+
};

src/completionRequester.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,18 @@ function shouldIgnoreUpdate(update: ViewUpdate) {
4848
}
4949
}
5050

51-
async function requestCompletion(update: ViewUpdate, lastPos?: number) {
52-
const config = update.view.state.facet(codeiumConfig);
53-
const { override } = update.view.state.facet(codeiumOtherDocumentsConfig);
51+
/**
52+
* Inner 'requestCompletion' API, which can optionally
53+
* be run all the time if you set `alwaysOn`
54+
*/
55+
export async function requestCompletion(view: EditorView, lastPos?: number) {
56+
const config = view.state.facet(codeiumConfig);
57+
const { override } = view.state.facet(codeiumOtherDocumentsConfig);
5458

5559
const otherDocuments = await override();
5660

5761
// Get the current position and source
58-
const state = update.state;
62+
const state = view.state;
5963
const pos = state.selection.main.head;
6064
const source = state.doc.toString();
6165

@@ -78,8 +82,8 @@ async function requestCompletion(update: ViewUpdate, lastPos?: number) {
7882
if (
7983
!(
8084
(lastPos === undefined || pos === lastPos) &&
81-
completionStatus(update.view.state) !== "active" &&
82-
update.view.hasFocus
85+
completionStatus(view.state) !== "active" &&
86+
view.hasFocus
8387
)
8488
) {
8589
return;
@@ -96,7 +100,7 @@ async function requestCompletion(update: ViewUpdate, lastPos?: number) {
96100
const insertChangeSet = ChangeSet.of(firstSpec, state.doc.length);
97101
const reverseChangeSet = insertChangeSet.invert(state.doc);
98102

99-
update.view.dispatch({
103+
view.dispatch({
100104
changes: insertChangeSet,
101105
effects: addSuggestions.of({
102106
index,
@@ -160,7 +164,7 @@ export function completionRequester() {
160164
// Check if the position has changed
161165
if (pos !== lastPos) return;
162166

163-
await requestCompletion(update, lastPos);
167+
await requestCompletion(update.view, lastPos);
164168
}, config.timeout);
165169

166170
// Update the last position

0 commit comments

Comments
 (0)