From ce70e64b9c32df49066382e3c07dc6030f8ffe00 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 14 May 2024 16:36:04 -0400 Subject: [PATCH] feat: Add shouldComplete option (#27) - Adds some more JSDoc documentation - Adds a new option `shouldComplete`, that accepts a `CompletionContext` and lets you decide whether to complete at a particular part of the document --- demo/index.ts | 7 +++++++ src/completionRequester.ts | 14 +++++++++++++- src/config.ts | 19 +++++++++++++++++++ src/plugin.ts | 19 ++++++++++++++++++- 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/demo/index.ts b/demo/index.ts index 1049c48..421bb67 100644 --- a/demo/index.ts +++ b/demo/index.ts @@ -35,6 +35,13 @@ const hiddenValue = "https://macwright.com/"`, }), copilotPlugin({ apiKey: "d49954eb-cfba-4992-980f-d8fb37f0e942", + shouldComplete(context) { + if (context.tokenBefore(["String"])) { + return true; + } + let match = context.matchBefore(/(@(?:\w*))(?:[./](\w*))?/); + return !match; + }, }), ], parent: document.querySelector("#editor")!, diff --git a/src/completionRequester.ts b/src/completionRequester.ts index 500e96b..a5623df 100644 --- a/src/completionRequester.ts +++ b/src/completionRequester.ts @@ -1,4 +1,4 @@ -import { completionStatus } from "@codemirror/autocomplete"; +import { CompletionContext, completionStatus } from "@codemirror/autocomplete"; import { ChangeSet, Transaction } from "@codemirror/state"; import { EditorView, ViewUpdate } from "@codemirror/view"; import { @@ -77,6 +77,18 @@ export function completionRequester() { // Get the current position and source const state = update.state; const pos = state.selection.main.head; + + // If we've configured a custom rule for when to show completions + // and that rule says no, don't offer completions. + if ( + config.shouldComplete && + !config.shouldComplete( + new CompletionContext(update.view.state, pos, false), + ) + ) { + return; + } + const source = state.doc.toString(); // Set a new timeout to request completion diff --git a/src/config.ts b/src/config.ts index 78b283a..4a106e7 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,12 +2,17 @@ import { Facet, combineConfig } from "@codemirror/state"; import { Language } from "./api/proto/exa/codeium_common_pb/codeium_common_pb.js"; import { Document } from "./api/proto/exa/language_server_pb/language_server_pb.js"; import { type PartialMessage } from "@bufbuild/protobuf"; +import { type CompletionContext } from "@codemirror/autocomplete"; export interface CodeiumConfig { /** * Codeium API key */ apiKey: string; + + /** + * The programming language of the given document. + */ language?: Language; /** * Time in millseconds after typing to fetch @@ -16,6 +21,15 @@ export interface CodeiumConfig { timeout?: number; authSource?: number; + + /** + * An optional method that lets you decide whether Codeium + * should be triggered at a particular place in a document. + * + * Might be useful for if you're fighting with overlapping + * autocomplete sources. + */ + shouldComplete?: (context: CompletionContext) => boolean; } export const codeiumConfig = Facet.define< @@ -40,6 +54,11 @@ export interface CodeiumOtherDocumentsConfig { override?: () => Promise | OtherDocuments; } +/** + * Configuration for other documents included with the completion + * request. Adding other documents helps you get more accurate + * suggestions by adding context. + */ export const codeiumOtherDocumentsConfig = Facet.define< CodeiumOtherDocumentsConfig, Required diff --git a/src/plugin.ts b/src/plugin.ts index 01db495..25cacb0 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -16,7 +16,11 @@ import { import { Language } from "./api/proto/exa/codeium_common_pb/codeium_common_pb.js"; import { copilotIgnore } from "./annotations.js"; -function isDecorationClicked(view: EditorView) { +/** + * Clicking a completion accepts it. This figures out + * whether a given click event is within the completion's area. + */ +function isDecorationClicked(view: EditorView): boolean { let inRange = false; const head = view.state.selection.asSingle().ranges.at(0)?.head; const stateField = view.state.field(completionDecoration); @@ -29,6 +33,15 @@ function isDecorationClicked(view: EditorView) { 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) { @@ -53,6 +66,10 @@ function completionPlugin() { }); } +/** + * Changing the editor's focus - blurring it by clicking outside - + * rejects the suggestion + */ function viewCompletionPlugin() { return EditorView.updateListener.of((update) => { if (update.focusChanged) {