Skip to content

Commit

Permalink
Add cycle widget
Browse files Browse the repository at this point in the history
  • Loading branch information
tmcw committed Jun 12, 2024
1 parent d11f6d0 commit 7fb0755
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 13 deletions.
29 changes: 29 additions & 0 deletions demo/demo.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,32 @@ main {
.cm-ghostText:hover {
background: #eee;
}

.cm-codeium-cycle {
font-size: 9px;
background-color: #eee;
padding: 2px;
border-radius: 2px;
display: inline-block;
}

.cm-codeium-cycle-key {
font-size: 9px;
font-family: monospace;
display: inline-block;
padding: 2px;
border-radius: 2px;
border: none;
background-color: lightblue;
margin-left: 5px;
}

.cm-codeium-cycle-key:hover {
background-color: deepskyblue;
}

.cm-codeium-cycle-explanation {
font-family: monospace;
display: inline-block;
padding: 2px;
}
33 changes: 24 additions & 9 deletions src/completionDecoration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
clearSuggestion,
} from "./effects.js";
import type { CompletionState } from "./types.js";
import { codeiumConfig } from "./config.js";

const ghostMark = Decoration.mark({ class: "cm-ghostText" });

Expand All @@ -24,22 +25,36 @@ export const completionDecoration = StateField.define<CompletionState>({
return null;
},
update(state: CompletionState, transaction: Transaction) {
const config = transaction.state.facet(codeiumConfig);
for (const effect of transaction.effects) {
if (effect.is(addSuggestions)) {
const { changeSpecs, index } = effect.value;

// NOTE: here we're adjusting the decoration range
// to refer to locations in the document _after_ we've
// inserted the text.
const decorations = Decoration.set(
changeSpecs[index]!.map((suggestionRange) => {
const range = ghostMark.range(
suggestionRange.absoluteStartPos,
suggestionRange.absoluteEndPos,
);
return range;
}),
);
const ranges = changeSpecs[index]!.map((suggestionRange) => {
const range = ghostMark.range(
suggestionRange.absoluteStartPos,
suggestionRange.absoluteEndPos,
);
return range;
});
const widgetPos = ranges.at(-1)?.to;

const decorations = Decoration.set([
...ranges,
...(widgetPos !== undefined &&
changeSpecs.length > 1 &&
config.widgetClass
? [
Decoration.widget({
widget: new config.widgetClass(index, changeSpecs.length),
side: 1,
}).range(widgetPos),
]
: []),
]);

return {
index,
Expand Down
8 changes: 8 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Language } from "./api/proto/exa/codeium_common_pb/codeium_common_pb.js
import type { Document } from "./api/proto/exa/language_server_pb/language_server_pb.js";
import type { PartialMessage } from "@bufbuild/protobuf";
import type { CompletionContext } from "@codemirror/autocomplete";
import { DefaultCycleWidget } from "./defaultCycleWidget.js";

export interface CodeiumConfig {
/**
Expand Down Expand Up @@ -30,6 +31,12 @@ export interface CodeiumConfig {
* autocomplete sources.
*/
shouldComplete?: (context: CompletionContext) => boolean;

/**
* The class for the widget that is shown at the end a suggestion
* when there are multiple suggestions to cycle through.
*/
widgetClass?: typeof DefaultCycleWidget | null;
}

export const codeiumConfig = Facet.define<
Expand All @@ -42,6 +49,7 @@ export const codeiumConfig = Facet.define<
{
language: Language.TYPESCRIPT,
timeout: 150,
widgetClass: DefaultCycleWidget,
},
{},
);
Expand Down
34 changes: 34 additions & 0 deletions src/defaultCycleWidget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { WidgetType } from "@codemirror/view";

/**
* Shown at the end of a suggestion if there are multiple
* suggestions to cycle through.
*/
export class DefaultCycleWidget extends WidgetType {
index: number;
total: number;

constructor(index: number, total: number) {
super();
this.index = index;
this.total = total;
}

toDOM() {
const wrap = document.createElement("span");
wrap.setAttribute("aria-hidden", "true");
wrap.className = "cm-codeium-cycle";
const words = wrap.appendChild(document.createElement("span"));
words.className = "cm-codeium-cycle-explanation";
words.innerText = `${this.index + 1}/${this.total}`;
const key = wrap.appendChild(document.createElement("button"));
key.className = "cm-codeium-cycle-key";
key.innerText = "⌥-]";
key.dataset.action = "codeium-cycle";
return wrap;
}

ignoreEvent() {
return false;
}
}
15 changes: 13 additions & 2 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,18 @@ function completionPlugin() {
}
return false;
},
mouseup(_event, view) {
mouseup(event, view) {
const target = event.target as HTMLElement;
if (
target.nodeName === "BUTTON" &&
target.dataset.action === "codeium-cycle"
) {
nextSuggestionCommand(view);
event.stopPropagation();
event.preventDefault();
console.log("got click, doing it");
return true;
}
if (isDecorationClicked(view)) {
return acceptSuggestionCommand(view);
}
Expand All @@ -78,7 +89,7 @@ function completionPlugin() {
function nextCompletionPlugin() {
return keymap.of([
{
key: "Ctrl-]",
key: "Alt-]",
run: nextSuggestionCommand,
},
]);
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ChangeSet } from "@codemirror/state";
import type { DecorationSet } from "@codemirror/view";
import type { Range, ChangeSet } from "@codemirror/state";
import type { Decoration, DecorationSet } from "@codemirror/view";

/**
* We dispatch an effect that updates the CompletionState.
Expand Down

0 comments on commit 7fb0755

Please sign in to comment.