no recommendation for now.

This commit is contained in:
rebornix 2021-08-19 14:04:38 -07:00
parent 715d3c85d8
commit a0630ea154
7 changed files with 159 additions and 17 deletions

View file

@ -91,6 +91,8 @@ import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransfo
import { editorOptionsRegistry } from 'vs/editor/common/config/editorOptions';
import { NotebookExecutionService } from 'vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl';
import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
import { INotebookKeymapService } from 'vs/workbench/contrib/notebook/common/notebookKeymapService';
import { NotebookKeymapService } from 'vs/workbench/contrib/notebook/browser/notebookKeymapServiceImpl';
/*--------------------------------------------------------------------------------------------- */
@ -607,6 +609,7 @@ registerSingleton(INotebookEditorService, NotebookEditorWidgetService, true);
registerSingleton(INotebookKernelService, NotebookKernelService, true);
registerSingleton(INotebookExecutionService, NotebookExecutionService, true);
registerSingleton(INotebookRendererMessagingService, NotebookRendererMessagingService, true);
registerSingleton(INotebookKeymapService, NotebookKeymapService, true);
const schemas: IJSONSchemaMap = {};
function isConfigurationPropertySchema(x: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema; }): x is IConfigurationPropertySchema {

View file

@ -92,9 +92,10 @@ export const EXPAND_CELL_OUTPUT_COMMAND_ID = 'notebook.cell.expandCellOutput';
// Hardcoding viewType/extension ID for now. TODO these should be replaced once we can
// look them up in the marketplace dynamically.
export const IPYNB_VIEW_TYPE = 'jupyter-notebook';
export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter';
export const KERNEL_EXTENSIONS = new Map<string, string>([
['jupyter-notebook', JUPYTER_EXTENSION_ID],
[IPYNB_VIEW_TYPE, JUPYTER_EXTENSION_ID],
]);
//#endregion

View file

@ -0,0 +1,114 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { getInstalledExtensions, IExtensionStatus } from 'vs/workbench/contrib/extensions/common/extensionsUtils';
import { INotebookKeymapService } from 'vs/workbench/contrib/notebook/common/notebookKeymapService';
import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IExtensionIdentifier, IExtensionManagementService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Memento, MementoObject } from 'vs/workbench/common/memento';
function onExtensionChanged(accessor: ServicesAccessor): Event<IExtensionIdentifier[]> {
const extensionService = accessor.get(IExtensionManagementService);
const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService);
const onDidInstallExtensions = Event.chain(extensionService.onDidInstallExtensions)
.filter(e => e.some(({ operation }) => operation === InstallOperation.Install))
.map(e => e.map(({ identifier }) => identifier))
.event;
return Event.debounce<IExtensionIdentifier[], IExtensionIdentifier[]>(Event.any(
Event.chain(Event.any(onDidInstallExtensions, Event.map(extensionService.onDidUninstallExtension, e => [e.identifier])))
.event,
Event.map(extensionEnablementService.onEnablementChanged, extensions => extensions.map(e => e.identifier))
), (result: IExtensionIdentifier[] | undefined, identifiers: IExtensionIdentifier[]) => {
result = result || (identifiers.length ? [identifiers[0]] : []);
for (const identifier of identifiers) {
if (result.some(l => !areSameExtensions(l, identifier))) {
result.push(identifier);
}
}
return result;
});
}
const hasRecommendedKeymapKey = 'hasRecommendedKeymap';
export class NotebookKeymapService extends Disposable implements INotebookKeymapService {
_serviceBrand: undefined;
private notebookKeymapMemento: Memento;
private notebookKeymap: MementoObject;
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@INotificationService private readonly notificationService: INotificationService,
@IStorageService storageService: IStorageService,
@ILifecycleService lifecycleService: ILifecycleService,
) {
super();
this.notebookKeymapMemento = new Memento('notebookKeymap', storageService);
this.notebookKeymap = this.notebookKeymapMemento.getMemento(StorageScope.GLOBAL, StorageTarget.USER);
this._register(lifecycleService.onDidShutdown(() => this.dispose()));
this._register(this.instantiationService.invokeFunction(onExtensionChanged)((identifiers => {
Promise.all(identifiers.map(identifier => this.checkForOtherKeymaps(identifier)))
.then(undefined, onUnexpectedError);
})));
}
private checkForOtherKeymaps(extensionIdentifier: IExtensionIdentifier): Promise<void> {
return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => {
const keymaps = extensions.filter(extension => isNotebookKeymapExtension(extension));
const extension = keymaps.find(extension => areSameExtensions(extension.identifier, extensionIdentifier));
if (extension && extension.globallyEnabled) {
// there is already a keymap extension
this.notebookKeymap[hasRecommendedKeymapKey] = true;
this.notebookKeymapMemento.saveMemento();
const otherKeymaps = keymaps.filter(extension => !areSameExtensions(extension.identifier, extensionIdentifier) && extension.globallyEnabled);
if (otherKeymaps.length) {
return this.promptForDisablingOtherKeymaps(extension, otherKeymaps);
}
}
return undefined;
});
}
private promptForDisablingOtherKeymaps(newKeymap: IExtensionStatus, oldKeymaps: IExtensionStatus[]): void {
const onPrompt = (confirmed: boolean) => {
if (confirmed) {
this.extensionEnablementService.setEnablement(oldKeymaps.map(keymap => keymap.local), EnablementState.DisabledGlobally);
}
};
this.notificationService.prompt(Severity.Info, localize('disableOtherKeymapsConfirmation', "Disable other keymaps ({0}) to avoid conflicts between keybindings?", oldKeymaps.map(k => `'${k.local.manifest.displayName}'`).join(', ')),
[{
label: localize('yes', "Yes"),
run: () => onPrompt(true)
}, {
label: localize('no', "No"),
run: () => onPrompt(false)
}]
);
}
}
export function isNotebookKeymapExtension(extension: IExtensionStatus): boolean {
const keywords = extension.local.manifest.keywords;
if (!keywords) {
return false;
}
return keywords.indexOf('notebook-keymap') !== -1;
}

View file

@ -5,21 +5,21 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable, IReference } from 'vs/base/common/lifecycle';
import { Mimes } from 'vs/base/common/mime';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IPosition } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IPosition } from 'vs/editor/common/core/position';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, INotebookSearchOptions, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { Mimes } from 'vs/base/common/mime';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { CellEditState, CellFocusMode, CellViewModelStateChangeEvent, CursorAtBoundary, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, INotebookCellStatusBarItem, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export abstract class BaseCellViewModel extends Disposable {
@ -109,7 +109,7 @@ export abstract class BaseCellViewModel extends Disposable {
get editorAttached(): boolean {
return !!this._textEditor;
}
private _cursorChangeListener: IDisposable | null = null;
private _editorListeners: IDisposable[] = [];
private _editorViewStates: editorCommon.ICodeEditorViewState | null = null;
private _resolvedCellDecorations = new Map<string, INotebookCellDecorationOptions>();
private _cellDecorationsChanged = new Emitter<{ added: INotebookCellDecorationOptions[], removed: INotebookCellDecorationOptions[] }>();
@ -151,7 +151,8 @@ export abstract class BaseCellViewModel extends Disposable {
private readonly _viewContext: ViewContext,
private readonly _configurationService: IConfigurationService,
private readonly _modelService: ITextModelService,
private readonly _undoRedoService: IUndoRedoService
private readonly _undoRedoService: IUndoRedoService,
// private readonly _keymapService: INotebookKeymapService
) {
super();
@ -189,14 +190,21 @@ export abstract class BaseCellViewModel extends Disposable {
return false;
}
// private handleKeyDown(e: IKeyboardEvent) {
// if (this.viewType === IPYNB_VIEW_TYPE && isWindows && e.ctrlKey && e.keyCode === KeyCode.Enter) {
// this._keymapService.promptKeymapRecommendation();
// }
// }
attachTextEditor(editor: ICodeEditor) {
if (!editor.hasModel()) {
throw new Error('Invalid editor: model is missing');
}
if (this._textEditor === editor) {
if (this._cursorChangeListener === null) {
this._cursorChangeListener = this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); });
if (this._editorListeners.length === 0) {
this._editorListeners.push(this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); }));
// this._editorListeners.push(this._textEditor.onKeyDown(e => this.handleKeyDown(e)));
this._onDidChangeState.fire({ selectionChanged: true });
}
return;
@ -220,7 +228,8 @@ export abstract class BaseCellViewModel extends Disposable {
}
});
this._cursorChangeListener = this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); });
this._editorListeners.push(this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); }));
// this._editorListeners.push(this._textEditor.onKeyDown(e => this.handleKeyDown(e)));
this._onDidChangeState.fire({ selectionChanged: true });
this._onDidChangeEditorAttachState.fire();
}
@ -237,8 +246,8 @@ export abstract class BaseCellViewModel extends Disposable {
});
this._textEditor = undefined;
this._cursorChangeListener?.dispose();
this._cursorChangeListener = null;
this._editorListeners.forEach(e => e.dispose());
this._editorListeners = [];
this._onDidChangeEditorAttachState.fire();
if (this._textModelRef) {
@ -540,6 +549,7 @@ export abstract class BaseCellViewModel extends Disposable {
override dispose() {
super.dispose();
this._editorListeners.forEach(e => e.dispose());
this._undoRedoService.removeElements(this.uri);
if (this._textModelRef) {

View file

@ -15,6 +15,7 @@ import { CellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/viewM
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookKeymapService } from 'vs/workbench/contrib/notebook/common/notebookKeymapService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { BaseCellViewModel } from './baseCellViewModel';
@ -104,7 +105,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
@IConfigurationService configurationService: IConfigurationService,
@INotebookService private readonly _notebookService: INotebookService,
@ITextModelService modelService: ITextModelService,
@IUndoRedoService undoRedoService: IUndoRedoService
@IUndoRedoService undoRedoService: IUndoRedoService,
@INotebookKeymapService keymapService: INotebookKeymapService
) {
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, modelService, undoRedoService);
this._outputViewModels = this.model.outputs.map(output => new CellOutputViewModel(this, output, this._notebookService));

View file

@ -115,7 +115,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM
@IConfigurationService configurationService: IConfigurationService,
@ITextModelService textModelService: ITextModelService,
@IInstantiationService instantiationService: IInstantiationService,
@IUndoRedoService undoRedoService: IUndoRedoService
@IUndoRedoService undoRedoService: IUndoRedoService,
) {
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, textModelService, undoRedoService);

View file

@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const INotebookKeymapService = createDecorator<INotebookKeymapService>('notebookKeymapService');
export interface INotebookKeymapService {
readonly _serviceBrand: undefined;
}