diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx
index be12463809da9..99b4380378a64 100644
--- a/packages/notebook/src/browser/notebook-editor-widget.tsx
+++ b/packages/notebook/src/browser/notebook-editor-widget.tsx
@@ -30,6 +30,7 @@ import { NotebookEditorWidgetService } from './service/notebook-editor-widget-se
import { NotebookMainToolbarRenderer } from './view/notebook-main-toolbar';
import { Deferred } from '@theia/core/lib/common/promise-util';
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
+import { NotebookContextManager } from './service/notebook-context-manager';
const PerfectScrollbar = require('react-perfect-scrollbar');
@@ -39,12 +40,16 @@ export function createNotebookEditorWidgetContainer(parent: interfaces.Container
const child = parent.createChild();
child.bind(NotebookEditorProps).toConstantValue(props);
+
+ child.bind(NotebookContextManager).toSelf().inSingletonScope();
+ child.bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope();
+
child.bind(NotebookEditorWidget).toSelf();
return child;
}
-const NotebookEditorProps = Symbol('NotebookEditorProps');
+export const NotebookEditorProps = Symbol('NotebookEditorProps');
interface RenderMessage {
rendererId: string;
@@ -79,6 +84,9 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
@inject(NotebookMainToolbarRenderer)
protected notebookMainToolbarRenderer: NotebookMainToolbarRenderer;
+ @inject(NotebookContextManager)
+ protected notebookContextManager: NotebookContextManager;
+
@inject(NotebookCodeCellRenderer)
protected codeCellRenderer: NotebookCodeCellRenderer;
@inject(NotebookMarkdownCellRenderer)
@@ -159,6 +167,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
// Ensure that the model is loaded before adding the editor
this.notebookEditorService.addNotebookEditor(this);
this.update();
+ this.notebookContextManager.init(this);
return this._model;
}
@@ -186,7 +195,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
protected render(): ReactNode {
if (this._model) {
return
- {this.notebookMainToolbarRenderer.render(this._model)}
+ {this.notebookMainToolbarRenderer.render(this._model, this.node)}
{
bind(NotebookColorContribution).toSelf().inSingletonScope();
@@ -81,7 +80,6 @@ export default new ContainerModule(bind => {
bind(NotebookCodeCellRenderer).toSelf().inSingletonScope();
bind(NotebookMarkdownCellRenderer).toSelf().inSingletonScope();
- bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope();
bind(NotebookEditorWidgetContainerFactory).toFactory(ctx => (props: NotebookEditorProps) =>
createNotebookEditorWidgetContainer(ctx.container, props).get(NotebookEditorWidget)
diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts
new file mode 100644
index 0000000000000..d5b33c3bcac9a
--- /dev/null
+++ b/packages/notebook/src/browser/service/notebook-context-manager.ts
@@ -0,0 +1,63 @@
+// *****************************************************************************
+// Copyright (C) 2024 TypeFox and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// http://www.eclipse.org/legal/epl-2.0.
+//
+// This Source Code may also be made available under the following Secondary
+// Licenses when the conditions for such availability set forth in the Eclipse
+// Public License v. 2.0 are satisfied: GNU General Public License, version 2
+// with the GNU Classpath Exception which is available at
+// https://www.gnu.org/software/classpath/license.html.
+//
+// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
+// *****************************************************************************
+
+import { inject, injectable } from '@theia/core/shared/inversify';
+import { ContextKeyChangeEvent, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
+import { DisposableCollection, Emitter } from '@theia/core';
+import { NotebookKernelService } from './notebook-kernel-service';
+import { NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_VIEW_TYPE } from '../contributions/notebook-context-keys';
+import { NotebookEditorWidget } from '../notebook-editor-widget';
+
+@injectable()
+export class NotebookContextManager {
+ @inject(ContextKeyService) protected contextKeyService: ContextKeyService;
+
+ @inject(NotebookKernelService)
+ protected readonly notebookKernelService: NotebookKernelService;
+
+ protected readonly toDispose = new DisposableCollection();
+
+ protected readonly onDidChangeContextEmitter = new Emitter();
+ readonly onDidChangeContext = this.onDidChangeContextEmitter.event;
+
+ init(widget: NotebookEditorWidget): void {
+ const scopedStore = this.contextKeyService.createScoped(widget.node);
+
+ this.toDispose.dispose();
+
+ scopedStore.setContext(NOTEBOOK_VIEW_TYPE, widget?.notebookType);
+
+ const kernel = widget?.model ? this.notebookKernelService.getSelectedNotebookKernel(widget.model) : undefined;
+ scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!kernel);
+ scopedStore.setContext(NOTEBOOK_KERNEL, kernel?.id);
+ this.toDispose.push(this.notebookKernelService.onDidChangeSelectedKernel(e => {
+ if (e.notebook.toString() === widget?.getResourceUri()?.toString()) {
+ scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!e.newKernel);
+ scopedStore.setContext(NOTEBOOK_KERNEL, e.newKernel);
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL]));
+ }
+ }));
+ this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL]));
+ }
+
+ createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent {
+ return { affects: keys => affectedKeys.some(key => keys.has(key)) };
+ }
+
+ dispose(): void {
+ this.toDispose.dispose();
+ }
+}
diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts
index 111365b8c973d..9f5e0b81f3a38 100644
--- a/packages/notebook/src/browser/service/notebook-kernel-service.ts
+++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts
@@ -24,6 +24,7 @@ import { StorageService } from '@theia/core/lib/browser';
import { NotebookKernelSourceAction } from '../../common';
import { NotebookModel } from '../view-model/notebook-model';
import { NotebookService } from './notebook-service';
+import { NotebookEditorWidgetService } from './notebook-editor-widget-service';
export interface SelectedNotebookKernelChangeEvent {
notebook: URI;
@@ -157,6 +158,9 @@ export class NotebookKernelService {
@inject(StorageService)
protected storageService: StorageService;
+ @inject(NotebookEditorWidgetService)
+ protected notebookEditorService: NotebookEditorWidgetService;
+
protected readonly kernels = new Map();
protected notebookBindings: Record = {};
@@ -239,14 +243,18 @@ export class NotebookKernelService {
const all = kernels.map(obj => obj.kernel);
// bound kernel
- const selectedId = this.notebookBindings[`${notebook.viewType}/${notebook.uri}`];
- const selected = selectedId ? this.kernels.get(selectedId)?.kernel : undefined;
+ const selected = this.getSelectedNotebookKernel(notebook);
const suggestions = kernels.filter(item => item.instanceAffinity > 1).map(item => item.kernel); // TODO implement notebookAffinity
const hidden = kernels.filter(item => item.instanceAffinity < 0).map(item => item.kernel);
return { all, selected, suggestions, hidden };
}
+ getSelectedNotebookKernel(notebook: NotebookTextModelLike): NotebookKernel | undefined {
+ const selectedId = this.notebookBindings[`${notebook.viewType}/${notebook.uri}`];
+ return selectedId ? this.kernels.get(selectedId)?.kernel : undefined;
+ }
+
selectKernelForNotebook(kernel: NotebookKernel | undefined, notebook: NotebookTextModelLike): void {
const key = `${notebook.viewType}/${notebook.uri}`;
const oldKernel = this.notebookBindings[key];
diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx
index 4755e402bde7e..12dff11891552 100644
--- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx
+++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx
@@ -22,6 +22,7 @@ import { NotebookKernelService } from '../service/notebook-kernel-service';
import { inject, injectable } from '@theia/core/shared/inversify';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { NotebookCommand } from '../../common';
+import { NotebookContextManager } from '../service/notebook-context-manager';
export interface NotebookMainToolbarProps {
notebookModel: NotebookModel
@@ -29,6 +30,8 @@ export interface NotebookMainToolbarProps {
notebookKernelService: NotebookKernelService;
commandRegistry: CommandRegistry;
contextKeyService: ContextKeyService;
+ editorNode: HTMLElement;
+ notebookContextManager: NotebookContextManager;
}
@injectable()
@@ -37,14 +40,16 @@ export class NotebookMainToolbarRenderer {
@inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
@inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry;
@inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService;
+ @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager;
- render(notebookModel: NotebookModel): React.ReactNode {
+ render(notebookModel: NotebookModel, editorNode: HTMLElement): React.ReactNode {
return ;
+ editorNode={editorNode}
+ notebookContextManager={this.notebookContextManager} />;
}
}
@@ -70,12 +75,18 @@ export class NotebookMainToolbar extends React.Component();
- this.getMenuItems().filter(item => item.when).forEach(item => props.contextKeyService.parseKeys(item.when!)?.forEach(key => contextKeys.add(key)));
+ this.getAllContextKeys(this.getMenuItems(), contextKeys);
+ props.notebookContextManager.onDidChangeContext(e => {
+ if (e.affects(contextKeys)) {
+ this.forceUpdate();
+ }
+ });
props.contextKeyService.onDidChange(e => {
if (e.affects(contextKeys)) {
this.forceUpdate();
}
});
+
}
override componentWillUnmount(): void {
@@ -103,7 +114,7 @@ export class NotebookMainToolbar extends React.Component 0 && }
;
- } else if (!item.when || this.props.contextKeyService.match(item.when)) {
+ } else if (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode)) {
const visibleCommand = Boolean(this.props.commandRegistry.getVisibleHandler(item.command ?? '', this.props.notebookModel));
if (!visibleCommand) {
return undefined;
@@ -125,6 +136,16 @@ export class NotebookMainToolbar extends React.Component): void {
+ menus.filter(item => item.when)
+ .forEach(item => this.props.contextKeyService.parseKeys(item.when!)?.forEach(key => keySet.add(key)));
+
+ menus.filter(item => item.children && item.children.length > 0)
+ .forEach(item => this.getAllContextKeys(item.children!, keySet));
}
}
diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts
index 2755e258d5053..95cef28663854 100644
--- a/packages/plugin-ext/src/main/browser/main-context.ts
+++ b/packages/plugin-ext/src/main/browser/main-context.ts
@@ -59,10 +59,8 @@ import { UntitledResourceResolver } from '@theia/core/lib/common/resource';
import { ThemeService } from '@theia/core/lib/browser/theming';
import { TabsMainImpl } from './tabs/tabs-main';
import { NotebooksMainImpl } from './notebooks/notebooks-main';
-import { NotebookService } from '@theia/notebook/lib/browser';
import { LocalizationMainImpl } from './localization-main';
import { NotebookRenderersMainImpl } from './notebooks/notebook-renderers-main';
-import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin';
import { NotebookEditorsMainImpl } from './notebooks/notebook-editors-main';
import { NotebookDocumentsMainImpl } from './notebooks/notebook-documents-main';
import { NotebookKernelsMainImpl } from './notebooks/notebook-kernels-main';
@@ -102,9 +100,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container
const documentsMain = new DocumentsMainImpl(editorsAndDocuments, modelService, rpc, editorManager, openerService, shell, untitledResourceResolver, languageService);
rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain);
- const notebookService = container.get(NotebookService);
- const pluginSupport = container.get(HostedPluginSupport);
- rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, new NotebooksMainImpl(rpc, notebookService, pluginSupport));
+ rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, new NotebooksMainImpl(rpc, container, commandRegistryMain));
rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_MAIN, new NotebookRenderersMainImpl(rpc, container));
const notebookEditorsMain = new NotebookEditorsMainImpl(rpc, container);
rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN, notebookEditorsMain);
diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts
index 9764b6242ccde..7263af2d788a6 100644
--- a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts
+++ b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts
@@ -19,11 +19,13 @@ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
import { NotebookCellStatusBarItem, NotebookData, TransientOptions } from '@theia/notebook/lib/common';
import { NotebookService } from '@theia/notebook/lib/browser';
import { Disposable } from '@theia/plugin';
-import { MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain } from '../../../common';
+import { CommandRegistryMain, MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain } from '../../../common';
import { RPCProtocol } from '../../../common/rpc-protocol';
import { NotebookDto } from './notebook-dto';
import { UriComponents } from '@theia/core/lib/common/uri';
import { HostedPluginSupport } from '../../../hosted/browser/hosted-plugin';
+import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model';
+import { interfaces } from '@theia/core/shared/inversify';
export interface NotebookCellStatusBarItemList {
items: NotebookCellStatusBarItem[];
@@ -38,20 +40,33 @@ export interface NotebookCellStatusBarItemProvider {
export class NotebooksMainImpl implements NotebooksMain {
- private readonly disposables = new DisposableCollection();
+ protected readonly disposables = new DisposableCollection();
- private readonly proxy: NotebooksExt;
- private readonly notebookSerializer = new Map();
- private readonly notebookCellStatusBarRegistrations = new Map();
+ protected notebookService: NotebookService;
+
+ protected readonly proxy: NotebooksExt;
+ protected readonly notebookSerializer = new Map();
+ protected readonly notebookCellStatusBarRegistrations = new Map();
constructor(
rpc: RPCProtocol,
- private notebookService: NotebookService,
- plugins: HostedPluginSupport
+ container: interfaces.Container,
+ commands: CommandRegistryMain
) {
+ this.notebookService = container.get(NotebookService);
+ const plugins = container.get(HostedPluginSupport);
+
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTEBOOKS_EXT);
- notebookService.onWillUseNotebookSerializer(async event => plugins.activateByNotebookSerializer(event));
- notebookService.markReady();
+ this.notebookService.onWillUseNotebookSerializer(async event => plugins.activateByNotebookSerializer(event));
+ this.notebookService.markReady();
+ commands.registerArgumentProcessor({
+ processArgument: arg => {
+ if (arg instanceof NotebookModel) {
+ return arg.uri;
+ }
+ return arg;
+ }
+ });
}
dispose(): void {
diff --git a/packages/workspace/src/browser/workspace-trust-service.ts b/packages/workspace/src/browser/workspace-trust-service.ts
index 6572da09024ee..c7ed506edb7b9 100644
--- a/packages/workspace/src/browser/workspace-trust-service.ts
+++ b/packages/workspace/src/browser/workspace-trust-service.ts
@@ -26,6 +26,7 @@ import {
} from './workspace-trust-preferences';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
import { WorkspaceService } from './workspace-service';
+import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
const STORAGE_TRUSTED = 'trusted';
@@ -49,6 +50,9 @@ export class WorkspaceTrustService {
@inject(WindowService)
protected readonly windowService: WindowService;
+ @inject(ContextKeyService)
+ protected readonly contextKeyService: ContextKeyService;
+
protected workspaceTrust = new Deferred();
@postConstruct()
@@ -71,6 +75,7 @@ export class WorkspaceTrustService {
const trust = givenTrust ?? await this.calculateWorkspaceTrust();
if (trust !== undefined) {
await this.storeWorkspaceTrust(trust);
+ this.contextKeyService.setContext('isWorkspaceTrusted', trust);
this.workspaceTrust.resolve(trust);
}
}