@valtown/codemirror-codeium
Version:
codemirror integration for codeium
119 lines • 3.95 kB
JavaScript
import { EditorView, keymap } from "@codemirror/view";
import { Prec } from "@codemirror/state";
import { completionDecoration } from "./completionDecoration.js";
import { completionRequester } from "./completionRequester.js";
import { sameKeyCommand, rejectSuggestionCommand, acceptSuggestionCommand, nextSuggestionCommand, startCompletion, } from "./commands.js";
import { codeiumConfig, codeiumOtherDocumentsConfig, } from "./config.js";
import { Language } from "./api/proto/exa/codeium_common_pb/codeium_common_pb.js";
import { copilotIgnore } from "./annotations.js";
/**
* Clicking a completion accepts it. This figures out
* whether a given click event is within the completion's area.
*/
function isDecorationClicked(view) {
let inRange = false;
const head = view.state.selection.asSingle().ranges.at(0)?.head;
const stateField = view.state.field(completionDecoration);
if (head !== undefined && stateField) {
stateField.decorations?.between(head, head, () => {
inRange = true;
});
return inRange;
}
return false;
}
/**
* Handles the behavior in which if you have a completion like
*
* foo|bar
*
* (the cursor is at |) and you type an x, it rejects
* the completion because that isn't part of the suggested
* code.
*/
function completionPlugin() {
return EditorView.domEventHandlers({
keydown(event, view) {
// Ideally, we handle infighting between
// the nextSuggestionCommand and this handler
// by using precedence, but I can't get that to work
// yet.
if (event.key === "]" && event.ctrlKey) {
return false;
}
if (event.key !== "Shift" &&
event.key !== "Control" &&
event.key !== "Alt" &&
event.key !== "Meta") {
return sameKeyCommand(view, event.key);
}
return false;
},
mouseup(event, view) {
const target = event.target;
if (target.nodeName === "BUTTON" &&
target.dataset.action === "codeium-cycle") {
nextSuggestionCommand(view);
event.stopPropagation();
event.preventDefault();
return true;
}
if (isDecorationClicked(view)) {
return acceptSuggestionCommand(view);
}
return rejectSuggestionCommand(view);
},
});
}
/**
* Next completion map
*/
function nextCompletionPlugin() {
return keymap.of([
{
key: "Ctrl-]",
run: nextSuggestionCommand,
},
]);
}
/**
* Changing the editor's focus - blurring it by clicking outside -
* rejects the suggestion
*/
function viewCompletionPlugin() {
return EditorView.updateListener.of((update) => {
if (update.focusChanged) {
rejectSuggestionCommand(update.view);
}
});
}
export { Language, copilotIgnore, codeiumConfig, codeiumOtherDocumentsConfig, nextSuggestionCommand, startCompletion, };
/**
* A combination of configuration, the keymap, the
* requester - as a composite extension for simplicity.
*/
export function copilotPlugin(config) {
return [
codeiumConfig.of(config),
completionDecoration,
Prec.highest(nextCompletionPlugin()),
Prec.highest(viewCompletionPlugin()),
Prec.high(completionPlugin()),
completionRequester(),
];
}
/**
* Returns false if this ViewUpdate is just the plugin
* adding or removing ghost text, and it should not be
* considered when saving this CodeMirror state into other
* systems, like draft recovery.
*/
export function shouldTakeUpdate(update) {
for (const tr of update.transactions) {
if (tr.annotation(copilotIgnore) !== undefined) {
return false;
}
}
return true;
}
//# sourceMappingURL=plugin.js.map