Merge pull request #115630 from microsoft/rebornix/multi-selection

multi selection for notebook cells
This commit is contained in:
Peng Lyu 2021-02-02 12:48:06 -08:00 committed by GitHub
commit acb300b004
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 72 additions and 219 deletions

View file

@ -1303,7 +1303,7 @@ suite('notebook working copy', () => {
assert.notEqual(firstNotebookEditor, secondNotebookEditor);
assert.equal(firstNotebookEditor?.document, secondNotebookEditor?.document, 'split notebook editors share the same document');
assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')));
// assert.notEqual(firstNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')), secondNotebookEditor?.asWebviewUri(vscode.Uri.file('./hello.png')));
await saveAllFilesAndCloseAll(resource);

View file

@ -21,7 +21,6 @@ export function smokeTestActivate(context: vscode.ExtensionContext): any {
}));
context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookSmokeTest', {
onDidChangeNotebook: new vscode.EventEmitter<vscode.NotebookDocumentEditEvent>().event,
openNotebook: async (_resource: vscode.Uri) => {
const dto: vscode.NotebookData = {
languages: ['typescript'],

View file

@ -9,10 +9,7 @@ import { smokeTestActivate } from './notebookSmokeTestMain';
export function activate(context: vscode.ExtensionContext): any {
smokeTestActivate(context);
const _onDidChangeNotebook = new vscode.EventEmitter<vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent>();
context.subscriptions.push(_onDidChangeNotebook);
context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
onDidChangeNotebook: _onDidChangeNotebook.event,
openNotebook: async (_resource: vscode.Uri) => {
if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) {
return {
@ -91,7 +88,7 @@ export function activate(context: vscode.ExtensionContext): any {
return;
}
const previousOutputs = cell.outputs;
// const previousOutputs = cell.outputs;
const newOutputs: vscode.CellOutput[] = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
@ -100,20 +97,6 @@ export function activate(context: vscode.ExtensionContext): any {
}];
cell.outputs = newOutputs;
_onDidChangeNotebook.fire({
document: document,
undo: () => {
if (cell) {
cell.outputs = previousOutputs;
}
},
redo: () => {
if (cell) {
cell.outputs = newOutputs;
}
}
});
return;
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
@ -151,7 +134,6 @@ export function activate(context: vscode.ExtensionContext): any {
return;
}
const previousOutputs = cell.outputs;
const newOutputs: vscode.CellOutput[] = [{
outputKind: vscode.CellOutputKind.Rich,
data: {
@ -160,20 +142,6 @@ export function activate(context: vscode.ExtensionContext): any {
}];
cell.outputs = newOutputs;
_onDidChangeNotebook.fire({
document: document,
undo: () => {
if (cell) {
cell.outputs = previousOutputs;
}
},
redo: () => {
if (cell) {
cell.outputs = newOutputs;
}
}
});
return;
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }

View file

@ -1237,30 +1237,6 @@ declare module 'vscode' {
// @rebornix REMOVE/REplace NotebookCommunication
// todo@API fishy? notebooks are public objects, there should be a "global" events for this
readonly onDidDispose: Event<void>;
/**
* Fired when the output hosting webview posts a message.
*/
// @rebornix REMOVE
// todo@API notebook editors are public -> ANY extension can listen to these event
readonly onDidReceiveMessage: Event<any>;
/**
* Post a message to the output hosting webview.
*
* Messages are only delivered if the editor is live.
*
* @param message Body of the message. This must be a string or other json serializable object.
*/
// @rebornix REMOVE
// todo@API notebook editors are public -> ANY extension can send messages
postMessage(message: any): Thenable<boolean>;
/**
* Convert a uri for the local file system to one that can be used inside outputs webview.
*/
// @rebornix REMOVE
// todo@API unsure about that, how do you this when executing a cell without having an editor
asWebviewUri(localResource: Uri): Uri;
}
// todo@API stale?
@ -1357,48 +1333,6 @@ declare module 'vscode' {
readonly metadata: NotebookDocumentMetadata;
}
interface NotebookDocumentContentChangeEvent {
/**
* The document that the edit is for.
*/
readonly document: NotebookDocument;
}
// @rebornix p2, remove
// todo@API is this still needed? With transient metadata have we everything covered?
interface NotebookDocumentEditEvent {
/**
* The document that the edit is for.
*/
readonly document: NotebookDocument;
/**
* Undo the edit operation.
*
* This is invoked by VS Code when the user undoes this edit. To implement `undo`, your
* extension should restore the document and editor to the state they were in just before this
* edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
*/
undo(): Thenable<void> | void;
/**
* Redo the edit operation.
*
* This is invoked by VS Code when the user redoes this edit. To implement `redo`, your
* extension should restore the document and editor to the state they were in just after this
* edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
*/
redo(): Thenable<void> | void;
/**
* Display name describing the edit.
*
* This will be shown to users in the UI for undo/redo operations.
*/
readonly label?: string;
}
/**
* Communication object passed to the {@link NotebookContentProvider} and
@ -1625,11 +1559,6 @@ declare module 'vscode' {
export interface NotebookContentProvider {
readonly options?: NotebookDocumentContentOptions;
readonly onDidChangeNotebookContentOptions?: Event<NotebookDocumentContentOptions>;
// @rebornix
// todo@API should be removed
readonly onDidChangeNotebook: Event<NotebookDocumentContentChangeEvent | NotebookDocumentEditEvent>;
/**
* Content providers should always use [file system providers](#FileSystemProvider) to
* resolve the raw content for `uri` as the resouce is not necessarily a file on disk.

View file

@ -10,7 +10,6 @@ import { Emitter } from 'vs/base/common/event';
import { IRelativePattern } from 'vs/base/common/glob';
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle';
import { ResourceMap } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
import { IExtUri } from 'vs/base/common/resources';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
@ -31,7 +30,6 @@ import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection }
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
class DocumentAndEditorState {
@ -131,7 +129,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@ILogService private readonly logService: ILogService,
@INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService,
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ -605,20 +602,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
return false;
}
$onUndoableContentChange(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void {
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
if (textModel) {
textModel.handleUnknownUndoableEdit(label, () => {
const isDirty = this._workingCopyService.isDirty(textModel.uri.with({ scheme: Schemas.vscodeNotebook }));
return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, isDirty);
}, () => {
const isDirty = this._workingCopyService.isDirty(textModel.uri.with({ scheme: Schemas.vscodeNotebook }));
return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, isDirty);
});
}
}
$onContentChange(resource: UriComponents, viewType: string): void {
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));

View file

@ -809,7 +809,6 @@ export interface MainThreadNotebookShape extends IDisposable {
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
$removeNotebookEditorDecorationType(key: string): void;
$trySetDecorations(id: string, range: ICellRange, decorationKey: string): void;
$onUndoableContentChange(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
$onContentChange(resource: UriComponents, viewType: string): void;
}
@ -1805,9 +1804,6 @@ export interface ExtHostNotebookShape {
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
$undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
$redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
}
export interface ExtHostStorageShape {

View file

@ -321,23 +321,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
this._notebookContentProviders.set(viewType, { extension, provider });
const listeners: vscode.Disposable[] = [];
listeners.push(provider.onDidChangeNotebook
? provider.onDidChangeNotebook(e => {
const document = this._documents.get(URI.revive(e.document.uri));
if (!document) {
throw new Error(`Notebook document ${e.document.uri.toString()} not found`);
}
if (isEditEvent(e)) {
const editId = document.addEdit(e);
this._proxy.$onUndoableContentChange(e.document.uri, viewType, editId, e.label);
} else {
this._proxy.$onContentChange(e.document.uri, viewType);
}
})
: Disposable.None);
listeners.push(provider.onDidChangeNotebookContentOptions
? provider.onDidChangeNotebookContentOptions(() => {
this._proxy.$updateNotebookProviderOptions(viewType, provider.options);
@ -544,26 +527,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
return false;
}
async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
const document = this._documents.get(URI.revive(uri));
if (!document) {
return;
}
document.undo(editId, isDirty);
}
async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
const document = this._documents.get(URI.revive(uri));
if (!document) {
return;
}
document.redo(editId, isDirty);
}
async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined> {
const document = this._documents.get(URI.revive(uri));
const provider = this._notebookContentProviders.get(viewType);
@ -892,11 +855,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
}
function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent {
return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function'
&& typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function';
}
export class NotebookCellStatusBarItemInternal extends Disposable {
private static NEXT_ID = 0;

View file

@ -15,7 +15,6 @@ import { CellKind, INotebookDocumentPropertiesChangeData, IWorkspaceCellEditDto,
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { CellEditType, CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { Cache } from './cache';
interface IObservable<T> {
@ -231,8 +230,6 @@ export class ExtHostNotebookDocument extends Disposable {
private _disposed = false;
private _languages: string[] = [];
private readonly _edits = new Cache<vscode.NotebookDocumentEditEvent>('notebook documents');
constructor(
private readonly _proxy: MainThreadNotebookShape,
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
@ -496,37 +493,4 @@ export class ExtHostNotebookDocument extends Disposable {
getCellIndex(cell: ExtHostCell): number {
return this._cells.indexOf(cell);
}
addEdit(item: vscode.NotebookDocumentEditEvent): number {
return this._edits.add([item]);
}
async undo(editId: number, isDirty: boolean): Promise<void> {
await this.getEdit(editId).undo();
// if (!isDirty) {
// this.disposeBackup();
// }
}
async redo(editId: number, isDirty: boolean): Promise<void> {
await this.getEdit(editId).redo();
// if (!isDirty) {
// this.disposeBackup();
// }
}
private getEdit(editId: number): vscode.NotebookDocumentEditEvent {
const edit = this._edits.get(editId, 0);
if (!edit) {
throw new Error('No edit found');
}
return edit;
}
disposeEdits(editIds: number[]): void {
for (const id of editIds) {
this._edits.delete(id);
}
}
}

View file

@ -351,6 +351,7 @@ export interface INotebookEditor extends IEditor, ICommonNotebookEditor {
getOverflowContainerDomNode(): HTMLElement;
getInnerWebview(): Webview | undefined;
getSelectionHandles(): number[];
getSelectionViewModels(): ICellViewModel[];
/**
* Focus the notebook editor cell list

View file

@ -300,6 +300,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return this.viewModel?.selectionHandles || [];
}
getSelectionViewModels(): ICellViewModel[] {
if (!this.viewModel) {
return [];
}
return this.viewModel.selectionHandles.map(handle => this.viewModel!.getCellByHandle(handle)) as ICellViewModel[];
}
hasModel(): this is IActiveNotebookEditor {
return !!this._notebookViewModel;
}
@ -475,7 +483,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
horizontalScrolling: false,
keyboardSupport: false,
mouseSupport: true,
multipleSelectionSupport: false,
multipleSelectionSupport: true,
enableKeyboardNavigation: true,
additionalScrollHeight: 0,
transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native',
@ -2130,6 +2138,13 @@ export const focusedCellBackground = registerColor('notebook.focusedCellBackgrou
hc: null
}, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused."));
export const selectedCellBackground = registerColor('notebook.selectedCellBackground', {
dark: null,
light: null,
hc: null
}, nls.localize('selectedCellBackground', "The background color of a cell when the cell is selected."));
export const cellHoverBackground = registerColor('notebook.cellHoverBackground', {
dark: transparent(focusedCellBackground, .5),
light: transparent(focusedCellBackground, .7),
@ -2266,6 +2281,14 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .code-cell-row.focused .cell-collapsed-part { background-color: ${focusedCellBackgroundColor} !important; }`);
}
const selectedCellBackgroundColor = theme.getColor(selectedCellBackground);
if (selectedCellBackground) {
collector.addRule(`.notebookOverlay .monaco-list.selection-multiple .markdown-cell-row.selected { background-color: ${selectedCellBackgroundColor} !important; }`);
collector.addRule(`.notebookOverlay .monaco-list.selection-multiple .code-cell-row.selected { background-color: ${selectedCellBackgroundColor} !important; }`);
collector.addRule(`.notebookOverlay .monaco-list.selection-multiple .markdown-cell-row.selected .cell-focus-indicator-bottom { background-color: ${selectedCellBackgroundColor} !important; }`);
collector.addRule(`.notebookOverlay .monaco-list.selection-multiple .code-cell-row.selected .cell-focus-indicator-bottom { background-color: ${selectedCellBackgroundColor} !important; }`);
}
const cellHoverBackgroundColor = theme.getColor(cellHoverBackground);
if (cellHoverBackgroundColor) {
collector.addRule(`.notebookOverlay .code-cell-row:not(.focused):hover .cell-focus-indicator,

View file

@ -27,12 +27,12 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Memento } from 'vs/workbench/common/memento';
import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { CellEditState, getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, getActiveNotebookEditor, ICellViewModel, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellKind, CellOutputKind, DisplayOrderKey, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
@ -437,8 +437,8 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return false;
}
const { editor, activeCell } = getContext();
if (!editor || !activeCell) {
const { editor } = getContext();
if (!editor) {
return false;
}
@ -449,8 +449,14 @@ export class NotebookService extends Disposable implements INotebookService, ICu
const clipboardService = accessor.get<IClipboardService>(IClipboardService);
const notebookService = accessor.get<INotebookService>(INotebookService);
clipboardService.writeText(activeCell.getText());
notebookService.setToCopy([activeCell.model], true);
const selectedCells = editor.getSelectionViewModels();
if (!selectedCells.length) {
return false;
}
clipboardService.writeText(selectedCells.map(cell => cell.getText()).join('\n'));
notebookService.setToCopy(selectedCells.map(cell => cell.model), true);
return true;
}));
@ -573,8 +579,8 @@ export class NotebookService extends Disposable implements INotebookService, ICu
return false;
}
const { editor, activeCell } = getContext();
if (!editor || !activeCell) {
const { editor } = getContext();
if (!editor) {
return false;
}
@ -590,9 +596,20 @@ export class NotebookService extends Disposable implements INotebookService, ICu
const clipboardService = accessor.get<IClipboardService>(IClipboardService);
const notebookService = accessor.get<INotebookService>(INotebookService);
clipboardService.writeText(activeCell.getText());
viewModel.deleteCell(viewModel.getCellIndex(activeCell), true);
notebookService.setToCopy([activeCell.model], false);
const selectedCells = editor.getSelectionViewModels();
if (!selectedCells.length) {
return false;
}
clipboardService.writeText(selectedCells.map(cell => cell.getText()).join('\n'));
const edits: ICellEditOperation[] = selectedCells.map(cell => [cell, viewModel.getCellIndex(cell)] as [ICellViewModel, number]).sort((a, b) => b[1] - a[1]).map(value => {
return { editType: CellEditType.Replace, index: value[1], count: 1, cells: [] };
});
viewModel.notebookDocument.applyEdits(viewModel.notebookDocument.versionId, edits, true, editor.getSelectionHandles(), () => { return undefined; }, undefined, true);
notebookService.setToCopy(selectedCells.map(cell => cell.model), false);
return true;
});

View file

@ -368,7 +368,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
const viewSelections = model.selectionHandles.map(handle => {
return model.getCellByHandle(handle);
}).filter(cell => !!cell).map(cell => this._getViewIndexUpperBound(cell!));
this.setFocus(viewSelections, undefined, true);
this.setSelection(viewSelections, undefined, true);
}));
const hiddenRanges = model.getHiddenRanges();
@ -592,6 +592,18 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
}
setFocus(indexes: number[], browserEvent?: UIEvent, ignoreTextModelUpdate?: boolean): void {
// if (!indexes.length) {
// return;
// }
// if (this._viewModel && !ignoreTextModelUpdate) {
// this._viewModel.selectionHandles = indexes.map(index => this.element(index)).map(cell => cell.handle);
// }
super.setFocus(indexes, browserEvent);
}
setSelection(indexes: number[], browserEvent?: UIEvent | undefined, ignoreTextModelUpdate?: boolean) {
if (!indexes.length) {
return;
}
@ -600,7 +612,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
this._viewModel.selectionHandles = indexes.map(index => this.element(index)).map(cell => cell.handle);
}
super.setFocus(indexes, browserEvent);
super.setSelection(indexes, browserEvent);
}
revealElementsInView(range: ICellRange) {

View file

@ -66,6 +66,9 @@ export class TestNotebookEditor implements INotebookEditor {
constructor(
) { }
getSelectionViewModels(): ICellViewModel[] {
throw new Error('Method not implemented.');
}
revealCellRangeInView(range: ICellRange): void {
throw new Error('Method not implemented.');
}