From ac9ffd3691682ae8ed118b521d9b2913af53adf0 Mon Sep 17 00:00:00 2001 From: allen-li1231 Date: Tue, 26 Mar 2024 14:39:45 +0800 Subject: [PATCH] add interpreter status indicator to cell status bar --- package.json | 8 ++++ src/common/common.ts | 2 +- src/common/interaction.ts | 49 +++++++++++++++++++- src/component/cellStatusBar.ts | 85 ++++++++++++++++++++++++++++++++++ src/extension/extension.ts | 12 +++++ 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/component/cellStatusBar.ts diff --git a/package.json b/package.json index 9734427..28cf9c4 100644 --- a/package.json +++ b/package.json @@ -203,6 +203,10 @@ { "command": "zeppelin-vscode.setZeppelinCredential", "title": "Zeppelin: Set Zeppelin Credential (username and password)" + }, + { + "command": "zeppelin-vscode.restartInterpreter", + "title": "Zeppelin: restart a interpreter" } ], "menus": { @@ -216,6 +220,10 @@ }, { "command": "zeppelin-vscode.setZeppelinCredential" + }, + { + "command": "zeppelin-vscode.restartInterpreter", + "when": "resourceExtname == .zpln || resourceExtname == .ipynb" } ] } diff --git a/src/common/common.ts b/src/common/common.ts index 0b93892..589321e 100644 --- a/src/common/common.ts +++ b/src/common/common.ts @@ -19,7 +19,7 @@ for (let lang of SUPPORTEDLANGUAGE) { } mapLanguageKind.set('markdown', 1); -export const reInterpreter = RegExp(/([\s\n]*%[\w\d\._]+)\s*\n+/); +export const reInterpreter = RegExp(/[\s\n]*%([\w\d\._]+)\s*\n+/); export const reURL = RegExp(/[-a-zA-Z0-9@:%._\+~#=]{1,256}\.?[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi); export const reCookies = RegExp(/^(JSESSIONID=((?!deleteMe).)*?);/s); export const reBase64= /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; diff --git a/src/common/interaction.ts b/src/common/interaction.ts index b857c8b..fadb11d 100644 --- a/src/common/interaction.ts +++ b/src/common/interaction.ts @@ -270,7 +270,7 @@ export async function doLogin( vscode.window.showErrorMessage(res.response.data); } } - // test if server has configured shiro for multi-users, + // test if server has configured Shiro for multi-users, // server will respond 'UnavailableSecurityManagerException' if not. else if (res.response.data.exception === 'UnavailableSecurityManagerException' @@ -492,4 +492,51 @@ export async function promptZeppelinCredential(kernel: ZeppelinKernel) { } }); }); +} + + +// function that prompts user to restart a interpreter +export async function showRestartInterpreter( + kernel: ZeppelinKernel, interpreterId: string | undefined) { + if (!kernel.isActive()) { + return; + } + + if (interpreterId === undefined) { + interpreterId = await vscode.window.showInputBox({ + title: 'Please specify a interpreter:' + }); + + } + if (interpreterId === undefined || interpreterId.length === 0) { + return; + } + + let selection = await vscode.window.showInformationMessage( + `Please confirm to restart interpreter: ${interpreterId}`, + "Yes", "No" + ); + + if (selection === undefined || selection === "No") { + return; + } + let res = await kernel.getService()?.restartInterpreter(interpreterId); + if (res === undefined) { + return; + } + if (res instanceof AxiosError) { + if (!res.response) { + // local network issue + vscode.window.showErrorMessage(`Failed to restart interpreter '${interpreterId}'`); + } + else { + vscode.window.showErrorMessage(res.response.data); + } + } + else if (res.status !== 200) { + vscode.window.showWarningMessage(res.statusText); + } + else { + vscode.window.showInformationMessage(`${interpreterId} restarted.`); + } } \ No newline at end of file diff --git a/src/component/cellStatusBar.ts b/src/component/cellStatusBar.ts new file mode 100644 index 0000000..ea43885 --- /dev/null +++ b/src/component/cellStatusBar.ts @@ -0,0 +1,85 @@ +import * as vscode from 'vscode'; +import { ZeppelinKernel } from '../extension/notebookKernel'; +import { reInterpreter } from '../common/common'; + +export class CellStatusProvider implements vscode.NotebookCellStatusBarItemProvider { + private _mapInterpreterStatus = new Map(); + private readonly _kernel: ZeppelinKernel; + + constructor(kernel: ZeppelinKernel) { + this._kernel = kernel; + } + + onDidChangeCellStatusBarItems?: vscode.Event | undefined; + + provideCellStatusBarItems(cell: vscode.NotebookCell): + vscode.ProviderResult { + + const items: vscode.NotebookCellStatusBarItem[] = []; + + if (!this._kernel.isActive() || cell.kind === vscode.NotebookCellKind.Markup) { + return items; + } + + let interpreterIds = cell.document.getText().match(reInterpreter); + if (interpreterIds === null || interpreterIds.length === 0) { + return items; + } + this.getInterpreterStatus(cell); + + let interpreterId = interpreterIds[1]; + let res = this._mapInterpreterStatus.get(interpreterId); + if (res === undefined) { + return items; + } + let [lastUpdateDate, status] = res; + const item = new vscode.NotebookCellStatusBarItem( + status, vscode.NotebookCellStatusBarAlignment.Right, + ); + item.command = { + title: status, + command: 'zeppelin-vscode.restartInterpreter', + arguments: [interpreterId], + }; + item.tooltip = `interpreter status (click to restart)`; + items.push(item); + + return items; + } + + private async _updateInterpreterStatus(interpreterId: string) { + const res = await this._kernel.getService()?.getInterpreterSetting(interpreterId); + if (res === undefined || res.data === undefined || res.data.status !== 'OK') { + return undefined; + } + + const status: string = res.data.body.status; + this._mapInterpreterStatus.set(interpreterId, [Date.now(), status]); + return status; + } + + public async getInterpreterStatus(cell: vscode.NotebookCell) { + if (cell.kind === vscode.NotebookCellKind.Markup) { + return undefined; + } + + let interpreterIds = cell.document.getText().match(reInterpreter); + if (interpreterIds === null || interpreterIds.length === 0) { + return undefined; + } + + let interpreterId = interpreterIds[1]; + let res = this._mapInterpreterStatus.get(interpreterId); + if (res === undefined) { + return await this._updateInterpreterStatus(interpreterId); + } + + let [lastUpdateDate, status] = res; + const trackInterval = vscode.workspace.getConfiguration('zeppelin') + .get('interpreter.trackInterval', 5); + if (Date.now() - lastUpdateDate > trackInterval * 1000) { + return await this._updateInterpreterStatus(interpreterId); + } + return status; + } +} \ No newline at end of file diff --git a/src/extension/extension.ts b/src/extension/extension.ts index e0b3bae..51fc910 100644 --- a/src/extension/extension.ts +++ b/src/extension/extension.ts @@ -2,6 +2,7 @@ // Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode'; import * as interact from '../common/interaction'; +import { CellStatusProvider} from '../component/cellStatusBar'; import { ZeppelinSerializer } from './notebookSerializer'; import { ZeppelinKernel } from './notebookKernel'; import { EXTENSION_NAME, NOTEBOOK_SUFFIX, logDebug } from '../common/common'; @@ -24,6 +25,10 @@ export async function activate(context: vscode.ExtensionContext) { ); context.subscriptions.push(disposable); + let cellStatusBar = new CellStatusProvider(kernel); + disposable = vscode.notebooks.registerNotebookCellStatusBarItemProvider( + EXTENSION_NAME, cellStatusBar + ); disposable = vscode.commands.registerCommand( 'zeppelin-vscode.setZeppelinServerURL', @@ -48,6 +53,13 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(disposable); + disposable = vscode.commands.registerCommand( + 'zeppelin-vscode.restartInterpreter', + _.partial(interact.showRestartInterpreter, kernel) + ); + context.subscriptions.push(disposable); + + disposable = vscode.workspace.onDidOpenNotebookDocument(async note => { if (!note.uri.fsPath.endsWith(NOTEBOOK_SUFFIX)) { return;