Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completion Event Handlers #96

Merged
merged 4 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@


## [0.16.8](https://github.com/arshad-yaseen/monacopilot/compare/v0.16.7...v0.16.8) (2025-01-19)


### 📚 Documentation

* beautiful note box ([f8ced6f](https://github.com/arshad-yaseen/monacopilot/commit/f8ced6fdec179dbe29ca6f303e77ddbbb52c429f))
- beautiful note box ([f8ced6f](https://github.com/arshad-yaseen/monacopilot/commit/f8ced6fdec179dbe29ca6f303e77ddbbb52c429f))

## [0.16.7](https://github.com/arshad-yaseen/monacopilot/compare/v0.16.6...v0.16.7) (2025-01-07)

Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
- [Max Context Lines](#max-context-lines)
- [Caching Completions](#caching-completions)
- [Handling Errors](#handling-errors)
- [Completion Event Handlers](#completion-event-handlers)
- [Custom Request Handler](#custom-request-handler)
- [Copilot Options](#copilot-options)
- [Changing the Provider and Model](#changing-the-provider-and-model)
Expand Down Expand Up @@ -419,6 +420,45 @@ registerCompletion(monaco, editor, {
});
```

### Completion Event Handlers

The editor provides several events to handle completion suggestions. These events allow you to respond to different stages of the completion process, such as when a suggestion is shown or accepted by the user.

#### onCompletionShown

This event is triggered when a completion suggestion is shown to the user. You can use this event to log or perform actions when a suggestion is displayed.

**Parameters:**

- `completion` (string): The completion text that is being shown
- `range` (EditorRange | undefined): The editor range where the completion will be inserted

**Example:**

```javascript
registerCompletion(monaco, editor, {
// ... other options
onCompletionShown: (completion, range) => {
console.log('Completion suggestion:', {completion, range});
},
});
```

#### onCompletionAccepted

Event triggered when a completion suggestion is accepted by the user.

**Example**

```javascript
registerCompletion(monaco, editor, {
// ... other options
onCompletionAccepted: () => {
console.log('Completion accepted');
},
});
```

## Copilot Options

### Changing the Provider and Model
Expand Down
36 changes: 28 additions & 8 deletions examples/vue/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
"include": [
"src"
],
"compilerOptions": {
"lib": [
"DOM",
"DOM.Iterable",
"ES2022"
],
"types": [],
"isolatedModules": true,
"esModuleInterop": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
},
{
"path": "./tsconfig.app.json"
}
]
// Vite takes care of building everything, not tsc.
"noEmit": true
}
}
3 changes: 0 additions & 3 deletions src/core/completion/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ const handleInlineCompletions = async ({
pos,
token,
isCompletionAccepted,
onShowCompletion,
options,
}: InlineCompletionHandlerParams): Promise<EditorInlineCompletionsResult> => {
const {
Expand All @@ -67,7 +66,6 @@ const handleInlineCompletions = async ({
}));

if (cachedCompletions.length > 0) {
onShowCompletion();
return createInlineCompletionResult(cachedCompletions);
}
}
Expand Down Expand Up @@ -134,7 +132,6 @@ const handleInlineCompletions = async ({
});
}

onShowCompletion();
return createInlineCompletionResult([
{insertText: formattedCompletion, range: completionInsertionRange},
]);
Expand Down
50 changes: 32 additions & 18 deletions src/core/completion/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,47 +58,62 @@ export const registerCompletion = (
});

try {
// Retrieve the editor's completion state once here
const state = editorCompletionState.get(editor);
if (!state) {
warn('Completion is not registered properly. State not found.');
return {
deregister: () => {
/* No-op */
},
trigger: () => {
/* No-op */
},
};
}

// Register inline completions provider
const inlineCompletionsProvider =
monaco.languages.registerInlineCompletionsProvider(options.language, {
provideInlineCompletions: (mdl, pos, _, token) => {
const state = editorCompletionState.get(editor);

if (!state) return;

const isOnDemandTrigger =
options.trigger === TriggerType.OnDemand && !state.isManualTrigger;

if (isOnDemandTrigger) return;
// Skip if trigger is on-demand but user did not manually trigger
if (
options.trigger === TriggerType.OnDemand &&
!state.isManualTrigger
) {
return;
}

return handleInlineCompletions({
monaco,
mdl,
pos,
token,
isCompletionAccepted: state.isCompletionAccepted,
onShowCompletion: () => {
state.isCompletionVisible = true;
state.isManualTrigger = false;
},
options,
});
},
handleItemDidShow: (_, item, completion) => {
state.isCompletionVisible = true;
state.isManualTrigger = false;

if (state.isCompletionAccepted) return;
options.onCompletionShown?.(completion, item.range);
},
freeInlineCompletions: () => {
// No-op
/* No-op */
},
});
disposables.push(inlineCompletionsProvider);

// Listen for keydown events to detect completion acceptance
const keyDownListener = editor.onKeyDown(event => {
const state = editorCompletionState.get(editor);
if (!state) return;

const isTabOrCmdRightArrow =
event.keyCode === monaco.KeyCode.Tab ||
(event.keyCode === monaco.KeyCode.RightArrow && event.metaKey);

if (state.isCompletionVisible && isTabOrCmdRightArrow) {
options.onCompletionAccepted?.();
state.isCompletionAccepted = true;
state.isCompletionVisible = false;
} else {
Expand All @@ -119,7 +134,6 @@ export const registerCompletion = (
};

activeCompletionRegistration = registration;

return registration;
} catch (error) {
if (options.onError) {
Expand All @@ -135,7 +149,7 @@ export const registerCompletion = (
activeCompletionRegistration = null;
},
trigger: () => {
// No-op
/* No-op */
},
};
}
Expand Down
16 changes: 15 additions & 1 deletion src/types/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ export interface RegisterCompletionOptions {
* @returns {Promise<{completion: string | null}>} An object containing the completion or null if no completion is available.
*/
requestHandler?: FetchCompletionItemHandler;

/**
* Callback function that is triggered when a completion is shown in the editor.
* @param completion - The completion text that is being shown.
* @param range - The editor range where the completion will be inserted.
*/
onCompletionShown?: (
completion: string,
range: EditorRange | undefined,
) => void;

/**
* Callback function triggered when a completion is accepted by the user.
*/
onCompletionAccepted?: () => void;
}

export enum TriggerType {
Expand Down Expand Up @@ -130,7 +145,6 @@ export interface InlineCompletionHandlerParams {
token: EditorCancellationToken;

isCompletionAccepted: boolean;
onShowCompletion: () => void;
options: RegisterCompletionOptions;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function Home() {

const completion = registerCompletion(monaco, editor, {
endpoint: '/api/complete',
language: 'java',
language: 'javascript',
maxContextLines: 60,
});

Expand All @@ -29,7 +29,7 @@ export default function Home() {

return (
<Editor
language="java"
language="javascript"
onMount={(editor: StandaloneCodeEditor, monaco: Monaco) => {
setMonaco(monaco);
setEditor(editor);
Expand Down
Loading