Skip to content

Commit

Permalink
Autocomplete frontend fixes (#583)
Browse files Browse the repository at this point in the history
* remove duplicate definition of inline completion provider

* rename completion variables, plugins, token to be more accurate

* abbreviate JupyterAIInlineProvider => JaiInlineProvider

* bump @jupyterlab/completer and typescript

* WIP: fix Jupyter AI completion settings

* Fix issues with settings population

* read from settings directly instead of using a cache

* disable Jupyter AI completion by default

* improve completion plugin menu items

* revert unnecessary edits to package manifest

* Update packages/jupyter-ai/src/components/statusbar-item.tsx

Co-authored-by: Michał Krassowski <5832902+krassowski@users.noreply.github.com>

* tweak wording

---------

Co-authored-by: krassowski <5832902+krassowski@users.noreply.github.com>
  • Loading branch information
dlqqq and krassowski authored Jan 19, 2024
1 parent c51df0b commit 476c7d2
Show file tree
Hide file tree
Showing 10 changed files with 938 additions and 717 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@
"test": "lerna run test"
},
"devDependencies": {
"@jupyterlab/builder": "^4",
"lerna": "^6.4.1",
"nx": "^15.9.2"
},
"resolutions": {
"@jupyterlab/completer": "4.1.0-beta.0"
},
"nx": {
"includedScripts": []
}
Expand Down
7 changes: 5 additions & 2 deletions packages/jupyter-ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,17 @@
"@emotion/styled": "^11.10.5",
"@jupyter/collaboration": "^1",
"@jupyterlab/application": "^4",
"@jupyterlab/apputils": "^4",
"@jupyterlab/cells": "^4",
"@jupyterlab/codeeditor": "^4",
"@jupyterlab/codemirror": "^4",
"@jupyterlab/completer": "^4.1.0-alpha.3",
"@jupyterlab/completer": "^4",
"@jupyterlab/coreutils": "^6",
"@jupyterlab/docregistry": "^4",
"@jupyterlab/fileeditor": "^4",
"@jupyterlab/notebook": "^4",
"@jupyterlab/services": "^7",
"@jupyterlab/settingregistry": "^4",
"@jupyterlab/ui-components": "^4",
"@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.0",
Expand Down Expand Up @@ -104,7 +107,7 @@
"stylelint-config-standard": "~24.0.0",
"stylelint-prettier": "^2.0.0",
"ts-jest": "^29",
"typescript": "~4.3.0",
"typescript": "~4.9.0",
"y-protocols": "^1.0.5"
},
"sideEffects": [
Expand Down
2 changes: 1 addition & 1 deletion packages/jupyter-ai/src/completions/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { inlineCompletionProvider } from './plugin';
export { completionPlugin } from './plugin';
149 changes: 95 additions & 54 deletions packages/jupyter-ai/src/completions/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
IEditorLanguage
} from '@jupyterlab/codemirror';
import { getEditor } from '../selection-watcher';
import { IJupyternautStatus } from '../tokens';
import { displayName, JupyterAIInlineProvider } from './provider';
import { IJaiStatusItem } from '../tokens';
import { displayName, JaiInlineProvider } from './provider';
import { CompletionWebsocketHandler } from './handler';

export namespace CommandIDs {
Expand All @@ -28,36 +28,56 @@ export namespace CommandIDs {
const INLINE_COMPLETER_PLUGIN =
'@jupyterlab/completer-extension:inline-completer';

export const inlineCompletionProvider: JupyterFrontEndPlugin<void> = {
/**
* Type of the settings object for the inline completer plugin.
*/
type IcPluginSettings = ISettingRegistry.ISettings & {
user: {
providers?: {
[key: string]: unknown;
[JaiInlineProvider.ID]?: JaiInlineProvider.ISettings;
};
};
composite: {
providers: {
[key: string]: unknown;
[JaiInlineProvider.ID]: JaiInlineProvider.ISettings;
};
};
};

export const completionPlugin: JupyterFrontEndPlugin<void> = {
id: 'jupyter_ai:inline-completions',
autoStart: true,
requires: [
ICompletionProviderManager,
IEditorLanguageRegistry,
ISettingRegistry
],
optional: [IJupyternautStatus],
optional: [IJaiStatusItem],
activate: async (
app: JupyterFrontEnd,
manager: ICompletionProviderManager,
completionManager: ICompletionProviderManager,
languageRegistry: IEditorLanguageRegistry,
settingRegistry: ISettingRegistry,
statusMenu: IJupyternautStatus | null
statusItem: IJaiStatusItem | null
): Promise<void> => {
if (typeof manager.registerInlineProvider === 'undefined') {
if (typeof completionManager.registerInlineProvider === 'undefined') {
// Gracefully short-circuit on JupyterLab 4.0 and Notebook 7.0
console.warn(
'Inline completions are only supported in JupyterLab 4.1+ and Jupyter Notebook 7.1+'
);
return;
}

const completionHandler = new CompletionWebsocketHandler();
const provider = new JupyterAIInlineProvider({
const provider = new JaiInlineProvider({
completionHandler,
languageRegistry
});

await completionHandler.initialize();
manager.registerInlineProvider(provider);
completionManager.registerInlineProvider(provider);

const findCurrentLanguage = (): IEditorLanguage | null => {
const widget = app.shell.currentWidget;
Expand All @@ -68,79 +88,100 @@ export const inlineCompletionProvider: JupyterFrontEndPlugin<void> = {
return languageRegistry.findByMIME(editor.model.mimeType);
};

let settings: ISettingRegistry.ISettings | null = null;
// ic := inline completion
async function getIcSettings() {
return (await settingRegistry.load(
INLINE_COMPLETER_PLUGIN
)) as IcPluginSettings;
}

settingRegistry.pluginChanged.connect(async (_emitter, plugin) => {
if (plugin === INLINE_COMPLETER_PLUGIN) {
// Only load the settings once the plugin settings were transformed
settings = await settingRegistry.load(INLINE_COMPLETER_PLUGIN);
}
});
/**
* Gets the composite settings for the Jupyter AI inline completion provider
* (JaiIcp).
*
* This reads from the `ISettings.composite` property, which merges the user
* settings with the provider defaults, defined in
* `JaiInlineProvider.DEFAULT_SETTINGS`.
*/
async function getJaiIcpSettings() {
const icSettings = await getIcSettings();
return icSettings.composite.providers[JaiInlineProvider.ID];
}

app.commands.addCommand(CommandIDs.toggleCompletions, {
execute: () => {
if (!settings) {
return;
/**
* Updates the JaiIcp user settings.
*/
async function updateJaiIcpSettings(
newJaiIcpSettings: Partial<JaiInlineProvider.ISettings>
) {
const icSettings = await getIcSettings();
const oldUserIcpSettings = icSettings.user.providers;
const newUserIcpSettings = {
...oldUserIcpSettings,
[JaiInlineProvider.ID]: {
...oldUserIcpSettings?.[JaiInlineProvider.ID],
...newJaiIcpSettings
}
const providers = Object.assign({}, settings.user.providers) as any;
const ourSettings = {
...JupyterAIInlineProvider.DEFAULT_SETTINGS,
...providers[provider.identifier]
};
const wasEnabled = ourSettings['enabled'];
providers[provider.identifier]['enabled'] = !wasEnabled;
settings.set('providers', providers);
};
icSettings.set('providers', newUserIcpSettings);
}

app.commands.addCommand(CommandIDs.toggleCompletions, {
execute: async () => {
const jaiIcpSettings = await getJaiIcpSettings();
updateJaiIcpSettings({
enabled: !jaiIcpSettings.enabled
});
},
label: 'Enable Jupyternaut Completions',
label: 'Enable completions by Jupyternaut',
isToggled: () => {
return provider.isEnabled();
}
});

app.commands.addCommand(CommandIDs.toggleLanguageCompletions, {
execute: () => {
execute: async () => {
const jaiIcpSettings = await getJaiIcpSettings();
const language = findCurrentLanguage();
if (!settings || !language) {
if (!language) {
return;
}
const providers = Object.assign({}, settings.user.providers) as any;
const ourSettings = {
...JupyterAIInlineProvider.DEFAULT_SETTINGS,
...providers[provider.identifier]
};
const wasDisabled = ourSettings['disabledLanguages'].includes(
language.name
);
const disabledList: string[] =
providers[provider.identifier]['disabledLanguages'];
if (wasDisabled) {
disabledList.filter(name => name !== language.name);
} else {
disabledList.push(language.name);
}
settings.set('providers', providers);

const disabledLanguages = [...jaiIcpSettings.disabledLanguages];
const newDisabledLanguages = disabledLanguages.includes(language.name)
? disabledLanguages.filter(l => l !== language.name)
: disabledLanguages.concat(language.name);

updateJaiIcpSettings({
disabledLanguages: newDisabledLanguages
});
},
label: () => {
const language = findCurrentLanguage();
return language
? `Enable Completions in ${displayName(language)}`
: 'Enable Completions for Language of Current Editor';
? `Disable completions in ${displayName(language)}`
: 'Disable completions in <language> files';
},
isToggled: () => {
const language = findCurrentLanguage();
return !!language && provider.isLanguageEnabled(language.name);
return !!language && !provider.isLanguageEnabled(language.name);
},
isVisible: () => {
const language = findCurrentLanguage();
return !!language;
},
isEnabled: () => {
return !!findCurrentLanguage() && provider.isEnabled();
const language = findCurrentLanguage();
return !!language && provider.isEnabled();
}
});

if (statusMenu) {
statusMenu.addItem({
if (statusItem) {
statusItem.addItem({
command: CommandIDs.toggleCompletions,
rank: 1
});
statusMenu.addItem({
statusItem.addItem({
command: CommandIDs.toggleLanguageCompletions,
rank: 2
});
Expand Down
Loading

0 comments on commit 476c7d2

Please sign in to comment.