Skip to content

Commit

Permalink
Update completer part 2 (jupyterlab#11969)
Browse files Browse the repository at this point in the history
* Refactor `completer` and `completer-extension` package

* Apply suggestions

* Update completer - part 2

* Update setting UI

* Apply reviewer's suggestions

* Improve wording.
  • Loading branch information
trungleduc authored and JasonWeill committed Mar 2, 2022
1 parent 8a5eb9d commit 6cda4ff
Show file tree
Hide file tree
Showing 46 changed files with 1,169 additions and 433 deletions.
4 changes: 2 additions & 2 deletions examples/cell/src/index.ts
Expand Up @@ -94,7 +94,7 @@ function main(): void {
const timeout = 1000;
const provider = new KernelCompleterProvider();
const connector = new ConnectorProxy(
{ editor, session: sessionContext.session },
{ widget: cellWidget, editor, session: sessionContext.session },
[provider],
timeout
);
Expand All @@ -104,7 +104,7 @@ function main(): void {
void sessionContext.ready.then(() => {
const provider = new KernelCompleterProvider();
handler.connector = new ConnectorProxy(
{ editor, session: sessionContext.session },
{ widget: cellWidget, editor, session: sessionContext.session },
[provider],
timeout
);
Expand Down
9 changes: 5 additions & 4 deletions packages/completer-extension/package.json
Expand Up @@ -39,10 +39,11 @@
"dependencies": {
"@jupyterlab/application": "^4.0.0-alpha.4",
"@jupyterlab/completer": "^4.0.0-alpha.4",
"@jupyterlab/console": "^4.0.0-alpha.4",
"@jupyterlab/fileeditor": "^4.0.0-alpha.4",
"@jupyterlab/notebook": "^4.0.0-alpha.4",
"@jupyterlab/settingregistry": "^4.0.0-alpha.4"
"@jupyterlab/settingregistry": "^4.0.0-alpha.4",
"@jupyterlab/ui-components": "^4.0.0-alpha.19",
"@lumino/coreutils": "^1.12.0",
"@rjsf/core": "^3.1.0",
"react": "^17.0.1"
},
"devDependencies": {
"rimraf": "~3.0.0",
Expand Down
18 changes: 15 additions & 3 deletions packages/completer-extension/schema/tracker.json
Expand Up @@ -5,8 +5,8 @@
"jupyter.lab.transform": true,
"properties": {
"availableProviders": {
"title": "Installed completion providers",
"description": "Configuration for installed completion providers, the keys of this dictionary are the identifiers of providers, the corresponding value is the priority rank of the provider. Providers with higher rank will be shown before the ones with lower rank, providers with negative rank are disabled.",
"title": "Completion providers rank setting.",
"description": "Providers with higher rank will be shown before the ones with lower rank, providers with negative rank are disabled.",
"type": "object",
"patternProperties": {
"^.*$": {
Expand All @@ -20,10 +20,22 @@
}
},
"providerTimeout": {
"title": "Default timeout for a provider",
"title": "Default timeout for a provider.",
"description": "If a provider can not return the response for a completer request before timeout, the result of this provider will be ignored. Value is in millisecond",
"type": "number",
"default": 1000
},
"showDocumentationPanel": {
"title": "Show the documentation panel.",
"description": "Documentation panel setting.",
"type": "boolean",
"default": false
},
"autoCompletion": {
"title": "Enable autocompletion.",
"description": "Autocompletion setting.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false,
Expand Down
139 changes: 17 additions & 122 deletions packages/completer-extension/src/index.ts
Expand Up @@ -15,31 +15,11 @@ import {
ICompletionProviderManager,
KernelCompleterProvider
} from '@jupyterlab/completer';
import { IEditorTracker } from '@jupyterlab/fileeditor';
import { INotebookTracker } from '@jupyterlab/notebook';
import { IConsoleTracker } from '@jupyterlab/console';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { IFormComponentRegistry } from '@jupyterlab/ui-components';
import { FieldProps } from '@rjsf/core';

/**
* The command IDs used by the completer plugin.
*/
namespace CommandIDs {
export const invoke = 'completer:invoke';

export const invokeConsole = 'completer:invoke-console';

export const invokeNotebook = 'completer:invoke-notebook';

export const invokeFile = 'completer:invoke-file';

export const select = 'completer:select';

export const selectConsole = 'completer:select-console';

export const selectNotebook = 'completer:select-notebook';

export const selectFile = 'completer:select-file';
}
import { renderAvailableProviders } from './renderer';

const COMPLETION_MANAGER_PLUGIN = '@jupyterlab/completer-extension:tracker';

Expand All @@ -58,31 +38,31 @@ const defaultProvider: JupyterFrontEndPlugin<void> = {

const manager: JupyterFrontEndPlugin<ICompletionProviderManager> = {
id: COMPLETION_MANAGER_PLUGIN,
requires: [
INotebookTracker,
IEditorTracker,
IConsoleTracker,
ISettingRegistry
],
requires: [ISettingRegistry],
optional: [IFormComponentRegistry],
provides: ICompletionProviderManager,
autoStart: true,
activate: (
app: JupyterFrontEnd,
notebooks: INotebookTracker,
editorTracker: IEditorTracker,
consoles: IConsoleTracker,
settings: ISettingRegistry
settings: ISettingRegistry,
editorRegistry: IFormComponentRegistry | null
): ICompletionProviderManager => {
const AVAILABLE_PROVIDERS = 'availableProviders';
const PROVIDER_TIMEOUT = 'providerTimeout';
const SHOW_DOCUMENT_PANEL = 'showDocumentationPanel';
const CONTINUOUS_HINTING = 'autoCompletion';
const manager = new CompletionProviderManager();
const updateSetting = (
settingValues: ISettingRegistry.ISettings,
availableProviders: string[]
): void => {
const providersData = settingValues.get(AVAILABLE_PROVIDERS);
const timeout = settingValues.get(PROVIDER_TIMEOUT);
const showDoc = settingValues.get(SHOW_DOCUMENT_PANEL);
const continuousHinting = settingValues.get(CONTINUOUS_HINTING);
manager.setTimeout(timeout.composite as number);
manager.setShowDocumentFlag(showDoc.composite as boolean);
manager.setContinuousHinting(continuousHinting.composite as boolean);
const selectedProviders = providersData.user ?? providersData.composite;
const sortedProviders = Object.entries(selectedProviders ?? {})
.filter(val => val[1] >= 0 && availableProviders.includes(val[0]))
Expand Down Expand Up @@ -113,97 +93,12 @@ const manager: JupyterFrontEndPlugin<ICompletionProviderManager> = {
});
});

app.commands.addCommand(CommandIDs.invokeNotebook, {
execute: args => {
const panel = notebooks.currentWidget;
if (panel && panel.content.activeCell?.model.type === 'code') {
manager.invoke(panel.id);
}
}
});

app.commands.addCommand(CommandIDs.selectNotebook, {
execute: () => {
const id = notebooks.currentWidget && notebooks.currentWidget.id;

if (id) {
return manager.select(id);
}
}
});

// Add console completer invoke command.
app.commands.addCommand(CommandIDs.invokeFile, {
execute: () => {
const id =
editorTracker.currentWidget && editorTracker.currentWidget.id;
if (id) {
return manager.invoke(id);
}
}
});

app.commands.addCommand(CommandIDs.selectFile, {
execute: () => {
const id =
editorTracker.currentWidget && editorTracker.currentWidget.id;

if (id) {
return manager.select(id);
}
}
});

app.commands.addCommand(CommandIDs.invokeConsole, {
execute: () => {
const id = consoles.currentWidget && consoles.currentWidget.id;

if (id) {
return manager.invoke(id);
}
}
});

app.commands.addCommand(CommandIDs.selectConsole, {
execute: () => {
const id = consoles.currentWidget && consoles.currentWidget.id;

if (id) {
return manager.select(id);
}
}
});

const addKeyBinding = (command: string, selector: string): void => {
app.commands.addKeyBinding({
command,
keys: ['Enter'],
selector
if (editorRegistry) {
editorRegistry.addRenderer('availableProviders', (props: FieldProps) => {
return renderAvailableProviders(props);
});
};
addKeyBinding(
CommandIDs.selectNotebook,
`.jp-Notebook .jp-mod-completer-active`
);
addKeyBinding(
CommandIDs.selectFile,
`.jp-FileEditor .jp-mod-completer-active`
);
addKeyBinding(
CommandIDs.selectConsole,
`.jp-ConsolePanel .jp-mod-completer-active`
);
}

notebooks.widgetAdded.connect(
async (_, notebook) => await manager.attachPanel(notebook)
);
editorTracker.widgetAdded.connect(
async (_, widget) =>
await manager.attachEditor(widget, app.serviceManager.sessions)
);
consoles.widgetAdded.connect(
async (_, console) => await manager.attachConsole(console)
);
return manager;
}
};
Expand Down
75 changes: 75 additions & 0 deletions packages/completer-extension/src/renderer.tsx
@@ -0,0 +1,75 @@
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
import { FieldProps } from '@rjsf/core';
import React, { useState } from 'react';

const AVAILABLE_PROVIDERS = 'availableProviders';

/**
* Custom setting renderer for provider rank.
*/
export function renderAvailableProviders(props: FieldProps): JSX.Element {
const { schema } = props;
const title = schema.title;
const desc = schema.description;
const settings: ISettingRegistry.ISettings = props.formContext.settings;
const userData = settings.get(AVAILABLE_PROVIDERS).user as
| ReadonlyPartialJSONObject
| undefined;

const items = {
...(schema.default as { [key: string]: number })
};
if (userData) {
for (const key of Object.keys(items)) {
if (key in userData) {
items[key] = userData[key] as number;
} else {
items[key] = -1;
}
}
}

const [settingValue, setValue] = useState(items);
const onSettingChange = (
key: string,
e: React.ChangeEvent<HTMLInputElement>
) => {
const newValue = {
...settingValue,
[key]: parseInt(e.target.value)
};

settings.set(AVAILABLE_PROVIDERS, newValue);

setValue(newValue);
};
return (
//TODO Remove hard coded class names
<div>
<fieldset>
<legend>{title}</legend>
<p className="field-description">{desc}</p>
{Object.keys(items).map(key => {
return (
<div key={key} className="form-group small-field">
<div>
<h3> {key}</h3>
<div className="inputFieldWrapper">
<input
className="form-control"
type="number"
value={settingValue[key]}
onChange={e => {
onSettingChange(key, e);
}}
/>
</div>
</div>
</div>
);
})}
</fieldset>
</div>
);
}
4 changes: 1 addition & 3 deletions packages/completer-extension/style/index.css
Expand Up @@ -4,8 +4,6 @@
|----------------------------------------------------------------------------*/

/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
@import url('~@jupyterlab/ui-components/style/index.css');
@import url('~@jupyterlab/application/style/index.css');
@import url('~@jupyterlab/console/style/index.css');
@import url('~@jupyterlab/fileeditor/style/index.css');
@import url('~@jupyterlab/notebook/style/index.css');
@import url('~@jupyterlab/completer/style/index.css');
4 changes: 1 addition & 3 deletions packages/completer-extension/style/index.js
Expand Up @@ -4,8 +4,6 @@
|----------------------------------------------------------------------------*/

/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
import '@jupyterlab/ui-components/style/index.js';
import '@jupyterlab/application/style/index.js';
import '@jupyterlab/console/style/index.js';
import '@jupyterlab/fileeditor/style/index.js';
import '@jupyterlab/notebook/style/index.js';
import '@jupyterlab/completer/style/index.js';
10 changes: 2 additions & 8 deletions packages/completer-extension/tsconfig.json
Expand Up @@ -13,16 +13,10 @@
"path": "../completer"
},
{
"path": "../console"
},
{
"path": "../fileeditor"
},
{
"path": "../notebook"
"path": "../settingregistry"
},
{
"path": "../settingregistry"
"path": "../ui-components"
}
]
}
6 changes: 2 additions & 4 deletions packages/completer/package.json
Expand Up @@ -46,11 +46,9 @@
"dependencies": {
"@jupyterlab/apputils": "^4.0.0-alpha.4",
"@jupyterlab/codeeditor": "^4.0.0-alpha.4",
"@jupyterlab/console": "^4.0.0-alpha.4",
"@jupyterlab/coreutils": "^6.0.0-alpha.4",
"@jupyterlab/docregistry": "^4.0.0-alpha.4",
"@jupyterlab/fileeditor": "^4.0.0-alpha.4",
"@jupyterlab/notebook": "^4.0.0-alpha.4",
"@jupyterlab/observables": "^5.0.0-alpha.4",
"@jupyterlab/rendermime": "^4.0.0-alpha.4",
"@jupyterlab/services": "^7.0.0-alpha.4",
"@jupyterlab/statedb": "^4.0.0-alpha.4",
"@jupyterlab/ui-components": "^4.0.0-alpha.19",
Expand Down

0 comments on commit 6cda4ff

Please sign in to comment.