Skip to content

Latest commit

 

History

History

completer

Custom Completer

Provide a connector to customize tab completion results in a notebook.

Custom completion

In this example, you will learn how to customize the behavior of JupyterLab notebooks' tab completion.

Code structure

The code is split into two parts:

  1. the JupyterLab plugin that activates all the extension components and connects them to the main JupyterLab application via commands,
  2. CustomConnector, a lightweight source of mocked completion results.

The first part is contained in the index.ts file, the second is in customconnector.ts.

Creating a custom DataConnector

src/customconnector.ts defines a CustomConnector to generate mock autocomplete suggestions. It implements JupyterLab's interface ICompletionProvider class.

The two methods which must be implemented in your CustomConnector from ICompletionProvider are fetch and isApplicable, which must be implemented in your CustomConnector.

// src/customconnector.ts#L25-L43

/**
 * Fetch completion requests.
 *
 * @param request - The completion request text and details.
 * @returns Completion reply
 */
fetch(
  request: CompletionHandler.IRequest,
  context: ICompletionContext
): Promise<CompletionHandler.ICompletionItemsReply> {
  const editor = context.editor;

  if (!editor) {
    return Promise.reject('No editor');
  }
  return new Promise<CompletionHandler.ICompletionItemsReply>(resolve => {
    resolve(Private.completionHint(editor!));
  });
}

This calls a private completionHint function, which uses the CodeEditor.IEditor widget to determine the token to suggest matches for.

// src/customconnector.ts#L74-L78

export function completionHint(
  editor: CodeEditor.IEditor
): CompletionHandler.ICompletionItemsReply {
  // Find the token at the cursor
  const token = editor.getTokenAtCursor();

A list of mock completion tokens is then created to return as ICompletionItemsReply response.

// src/customconnector.ts#L80-L99

// Create a list of matching tokens.
const tokenList = [
  { value: token.value + 'Magic', offset: token.offset, type: 'magic' },
  { value: token.value + 'Science', offset: token.offset, type: 'science' },
  { value: token.value + 'Neither', offset: token.offset }
];

// Only choose the ones that have a non-empty type field, which are likely to be of interest.
const completionList = tokenList.filter(t => t.type).map(t => t.value);
// Remove duplicate completions from the list
const matches = Array.from(new Set<string>(completionList));

const items = new Array<CompletionHandler.ICompletionItem>();
matches.forEach(label => items.push({ label }));

return {
  start: token.offset,
  end: token.offset + token.value.length,
  items
};

Aggregating connector responses

JupyterLab's CompletionManager fetches and merges completion responses from KernelConnector and ContextConnector (https://github.com/jupyterlab/jupyterlab/blob/b279092d88de650ea36460689257e1b8e8a418bf/packages/completer-extension/src/index.ts#L29). We add our new completer provider to it:

// src/index.ts#L23-L23

completionManager.registerProvider(new CustomCompleterProvider());

Where to go next

Create a server extension to serve up custom completion matches.