|
@@ -1,6 +1,10 @@
|
|
|
// Copyright (c) Jupyter Development Team.
|
|
|
// Distributed under the terms of the Modified BSD License.
|
|
|
|
|
|
+import {
|
|
|
+ Kernel, KernelMessage
|
|
|
+} from '@jupyterlab/services';
|
|
|
+
|
|
|
import {
|
|
|
Widget
|
|
|
} from 'phosphor/lib/ui/widget';
|
|
@@ -9,6 +13,10 @@ import {
|
|
|
JupyterLab, JupyterLabPlugin
|
|
|
} from '../application';
|
|
|
|
|
|
+import {
|
|
|
+ CodeEditor
|
|
|
+} from '../codeeditor';
|
|
|
+
|
|
|
import {
|
|
|
IConsoleTracker
|
|
|
} from '../console';
|
|
@@ -18,7 +26,11 @@ import {
|
|
|
} from '../notebook';
|
|
|
|
|
|
import {
|
|
|
- CommandIDs, ITooltipManager, TooltipModel, TooltipWidget
|
|
|
+ RenderMime
|
|
|
+} from '../rendermime';
|
|
|
+
|
|
|
+import {
|
|
|
+ CommandIDs, ITooltipManager, TooltipWidget
|
|
|
} from './';
|
|
|
|
|
|
|
|
@@ -32,13 +44,19 @@ const service: JupyterLabPlugin<ITooltipManager> = {
|
|
|
activate: (app: JupyterLab): ITooltipManager => {
|
|
|
let tooltip: TooltipWidget | null = null;
|
|
|
return {
|
|
|
- invoke(options: ITooltipManager.IOptions): void {
|
|
|
+ invoke(options: ITooltipManager.IOptions): Promise<void> {
|
|
|
+ const detail: 0 | 1 = 0;
|
|
|
const { anchor, editor, kernel, rendermime } = options;
|
|
|
|
|
|
if (tooltip) {
|
|
|
tooltip.dispose();
|
|
|
tooltip = null;
|
|
|
}
|
|
|
+
|
|
|
+ return Private.fetch({ detail, editor, kernel }).then(bundle => {
|
|
|
+ tooltip = new TooltipWidget({ anchor, bundle, editor, rendermime });
|
|
|
+ Widget.attach(tooltip, document.body);
|
|
|
+ }).catch(() => { /* Fails silently. */ });
|
|
|
}
|
|
|
};
|
|
|
}
|
|
@@ -69,7 +87,7 @@ const consolePlugin: JupyterLabPlugin<void> = {
|
|
|
|
|
|
// If all components necessary for rendering exist, create a tooltip.
|
|
|
if (!!editor && !!kernel && !!rendermime) {
|
|
|
- manager.invoke({ anchor, editor, kernel, rendermime });
|
|
|
+ return manager.invoke({ anchor, editor, kernel, rendermime });
|
|
|
}
|
|
|
}
|
|
|
});
|
|
@@ -102,7 +120,7 @@ const notebookPlugin: JupyterLabPlugin<void> = {
|
|
|
|
|
|
// If all components necessary for rendering exist, create a tooltip.
|
|
|
if (!!editor && !!kernel && !!rendermime) {
|
|
|
- manager.invoke({ anchor, editor, kernel, rendermime });
|
|
|
+ return manager.invoke({ anchor, editor, kernel, rendermime });
|
|
|
}
|
|
|
}
|
|
|
});
|
|
@@ -118,3 +136,75 @@ const plugins: JupyterLabPlugin<any>[] = [
|
|
|
service, consolePlugin, notebookPlugin
|
|
|
];
|
|
|
export default plugins;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * A namespace for private data.
|
|
|
+ */
|
|
|
+namespace Private {
|
|
|
+ /**
|
|
|
+ * A counter for outstanding requests.
|
|
|
+ */
|
|
|
+ let pending = 0;
|
|
|
+
|
|
|
+ export
|
|
|
+ interface IFetchOptions {
|
|
|
+ /**
|
|
|
+ * The detail level requested from the API.
|
|
|
+ *
|
|
|
+ * #### Notes
|
|
|
+ * The only acceptable values are 0 and 1. The default value is 0.
|
|
|
+ * @see http://jupyter-client.readthedocs.io/en/latest/messaging.html#introspection
|
|
|
+ */
|
|
|
+ detail?: 0 | 1;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The referent editor for the tooltip.
|
|
|
+ */
|
|
|
+ editor: CodeEditor.IEditor;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The kernel against which the API request will be made.
|
|
|
+ */
|
|
|
+ kernel: Kernel.IKernel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fetch a tooltip's content from the API server.
|
|
|
+ */
|
|
|
+ export
|
|
|
+ function fetch(options: IFetchOptions): Promise<RenderMime.MimeMap<string>> {
|
|
|
+ let { detail, editor, kernel } = options;
|
|
|
+ let code = editor.model.value.text;
|
|
|
+ let position = editor.getCursorPosition();
|
|
|
+ let offset = editor.getOffsetAt(position);
|
|
|
+
|
|
|
+ // Clear hints if the new text value is empty or kernel is unavailable.
|
|
|
+ if (!code || !kernel) {
|
|
|
+ return Promise.reject(void 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ let contents: KernelMessage.IInspectRequest = {
|
|
|
+ code,
|
|
|
+ cursor_pos: offset,
|
|
|
+ detail_level: detail || 0
|
|
|
+ };
|
|
|
+ let current = ++pending;
|
|
|
+
|
|
|
+ return kernel.requestInspect(contents).then(msg => {
|
|
|
+ let value = msg.content;
|
|
|
+
|
|
|
+ // If a newer request is pending, bail.
|
|
|
+ if (current !== pending) {
|
|
|
+ return Promise.reject(void 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If request fails or returns negative results, bail.
|
|
|
+ if (value.status !== 'ok' || !value.found) {
|
|
|
+ return Promise.reject(void 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ return Promise.resolve(value.data as RenderMime.MimeMap<string>);
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|