mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-07 23:36:03 +01:00
find widget attempt
This commit is contained in:
@@ -623,6 +623,12 @@ export interface ITextModel {
|
||||
*/
|
||||
equalsTextBuffer(other: ITextBuffer): boolean;
|
||||
|
||||
/**
|
||||
* Get the underling text buffer.
|
||||
* @internal
|
||||
*/
|
||||
getTextBuffer(): ITextBuffer;
|
||||
|
||||
/**
|
||||
* Get the text in a certain range.
|
||||
* @param range The range describing what text to get.
|
||||
|
||||
@@ -389,6 +389,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return this._buffer.equals(other);
|
||||
}
|
||||
|
||||
public getTextBuffer(): model.ITextBuffer {
|
||||
this._assertNotDisposed();
|
||||
return this._buffer;
|
||||
}
|
||||
|
||||
private _emitContentChangedEvent(rawChange: ModelRawContentChangedEvent, change: IModelContentChangedEvent): void {
|
||||
if (this._isDisposing) {
|
||||
// Do not confuse listeners by emitting any event after disposing
|
||||
|
||||
@@ -66,6 +66,7 @@ export interface IFindStartOptions {
|
||||
shouldFocus: FindStartFocusAction;
|
||||
shouldAnimate: boolean;
|
||||
updateSearchScope: boolean;
|
||||
shouldReveal?: boolean;
|
||||
}
|
||||
|
||||
export class CommonFindController extends Disposable implements IEditorContribution {
|
||||
@@ -264,6 +265,10 @@ export class CommonFindController extends Disposable implements IEditorContribut
|
||||
isRevealed: true
|
||||
};
|
||||
|
||||
if (opts.shouldReveal !== undefined) {
|
||||
stateChanges.isRevealed = opts.shouldReveal;
|
||||
}
|
||||
|
||||
if (opts.seedSearchStringFromSelection) {
|
||||
let selectionSearchString = getSelectionSearchString(this._editor);
|
||||
if (selectionSearchString) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ICell, IOutput, INotebook, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellsSplice, NotebookCellOutputsSplice, generateCellPath } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { PieceTreeTextBufferFactory, PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
|
||||
|
||||
export class MainThreadCell implements ICell {
|
||||
private _onDidChangeOutputs = new Emitter<NotebookCellOutputsSplice[]>();
|
||||
@@ -38,6 +39,8 @@ export class MainThreadCell implements ICell {
|
||||
|
||||
readonly uri: URI;
|
||||
|
||||
private _buffer: PieceTreeTextBufferFactory | null = null;
|
||||
|
||||
constructor(
|
||||
parent: MainThreadNotebookDocument,
|
||||
public handle: number,
|
||||
@@ -66,6 +69,17 @@ export class MainThreadCell implements ICell {
|
||||
save() {
|
||||
this._isDirty = false;
|
||||
}
|
||||
|
||||
resolveTextBufferFactory(): PieceTreeTextBufferFactory {
|
||||
if (this._buffer) {
|
||||
return this._buffer;
|
||||
}
|
||||
|
||||
let builder = new PieceTreeTextBufferBuilder();
|
||||
builder.acceptChunk(this.source.join('\n'));
|
||||
this._buffer = builder.finish(true);
|
||||
return this._buffer;
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadNotebookDocument extends Disposable implements INotebook {
|
||||
|
||||
@@ -161,8 +161,9 @@ class CellContentProvider implements ITextModelContentProvider {
|
||||
}
|
||||
for (let cell of notebook.cells) {
|
||||
if (cell.uri.toString() === resource.toString()) {
|
||||
let bufferFactory = cell.resolveTextBufferFactory();
|
||||
return this._modelService.createModel(
|
||||
cell.source.join('\n'),
|
||||
bufferFactory,
|
||||
cell.language ? this._modeService.create(cell.language) : this._modeService.createByFilepathOrFirstLine(resource, cell.source[0]),
|
||||
resource
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
@@ -12,6 +12,7 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebook
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { NOTEBOOK_EDITOR_FOCUSED, NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
@@ -82,25 +83,15 @@ registerAction2(class extends Action2 {
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
let editorService = accessor.get(IEditorService);
|
||||
let notebookService = accessor.get(INotebookService);
|
||||
let editor = getActiveNotebookEditor(editorService, notebookService);
|
||||
|
||||
let resource = editorService.activeEditor?.resource;
|
||||
let editorControl = editorService.activeControl;
|
||||
let notebookProviders = notebookService.getContributedNotebookProviders(resource!);
|
||||
|
||||
if (!resource || !editorControl || notebookProviders.length === 0) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editorViewType = (editorControl! as NotebookEditor).viewType;
|
||||
let viewType = notebookProviders[0].id;
|
||||
|
||||
if (viewType !== editorViewType) {
|
||||
return;
|
||||
}
|
||||
|
||||
let activeCell = (editorControl! as NotebookEditor).getActiveCell();
|
||||
let activeCell = editor.getActiveCell();
|
||||
if (activeCell) {
|
||||
(editorControl! as NotebookEditor).focusNotebookCell(activeCell, false);
|
||||
editor.focusNotebookCell(activeCell, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -125,29 +116,62 @@ registerAction2(class extends Action2 {
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
let editorService = accessor.get(IEditorService);
|
||||
let notebookService = accessor.get(INotebookService);
|
||||
let editor = getActiveNotebookEditor(editorService, notebookService);
|
||||
|
||||
let resource = editorService.activeEditor?.resource;
|
||||
let editorControl = editorService.activeControl;
|
||||
let notebookProviders = notebookService.getContributedNotebookProviders(resource!);
|
||||
|
||||
if (!resource || !editorControl || notebookProviders.length === 0) {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editorViewType = (editorControl! as NotebookEditor).viewType;
|
||||
let viewType = notebookProviders[0].id;
|
||||
|
||||
if (viewType !== editorViewType) {
|
||||
return;
|
||||
}
|
||||
|
||||
let activeCell = (editorControl! as NotebookEditor).getActiveCell();
|
||||
let activeCell = editor.getActiveCell();
|
||||
if (activeCell) {
|
||||
(editorControl! as NotebookEditor).editNotebookCell(undefined, activeCell);
|
||||
editor.editNotebookCell(undefined, activeCell);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.hideFind',
|
||||
title: 'Hide Find in Notebook',
|
||||
keybinding: {
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED),
|
||||
primary: KeyCode.Escape,
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
let editorService = accessor.get(IEditorService);
|
||||
let notebookService = accessor.get(INotebookService);
|
||||
let editor = getActiveNotebookEditor(editorService, notebookService);
|
||||
|
||||
editor?.hideFind();
|
||||
}
|
||||
});
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.notebook.find',
|
||||
title: 'Find in Notebook',
|
||||
keybinding: {
|
||||
when: NOTEBOOK_EDITOR_FOCUSED,
|
||||
primary: KeyCode.KEY_F | KeyMod.CtrlCmd,
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
let editorService = accessor.get(IEditorService);
|
||||
let notebookService = accessor.get(INotebookService);
|
||||
let editor = getActiveNotebookEditor(editorService, notebookService);
|
||||
|
||||
editor?.showFind();
|
||||
}
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: 'workbench.action.executeNotebook',
|
||||
@@ -170,3 +194,23 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
group: 'navigation',
|
||||
when: NOTEBOOK_EDITOR_FOCUSED
|
||||
});
|
||||
|
||||
|
||||
function getActiveNotebookEditor(editorService: IEditorService, notebookService: INotebookService): NotebookEditor | undefined {
|
||||
let resource = editorService.activeEditor?.resource;
|
||||
let editorControl = editorService.activeControl;
|
||||
let notebookProviders = notebookService.getContributedNotebookProviders(resource!);
|
||||
|
||||
if (!resource || !editorControl || notebookProviders.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let editorViewType = (editorControl! as NotebookEditor).viewType;
|
||||
let viewType = notebookProviders[0].id;
|
||||
|
||||
if (viewType !== editorViewType) {
|
||||
return;
|
||||
}
|
||||
|
||||
return editorControl! as NotebookEditor;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/renderers/cellViewModel';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/output/outputRenderer';
|
||||
import { IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey<boolean>('notebookFindWidgetFocused', false);
|
||||
|
||||
export interface INotebookEditor {
|
||||
viewType: string | undefined;
|
||||
|
||||
@@ -28,7 +28,7 @@ import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/output/out
|
||||
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/renderers/backLayerWebView';
|
||||
import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/renderers/cellRenderer';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/renderers/cellViewModel';
|
||||
import { CELL_MARGIN, INotebook, NotebookCellsSplice, IOutput, parseCellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CELL_MARGIN, NotebookCellsSplice, IOutput, parseCellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -37,6 +37,8 @@ import { IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/notebookCellList';
|
||||
import { NotebookFindWidget, NotebookFindDelegate, CellFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookFindWidget';
|
||||
import { FindMatch } from 'vs/editor/common/model';
|
||||
|
||||
const $ = DOM.$;
|
||||
const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState';
|
||||
@@ -107,7 +109,7 @@ class NotebookCodeEditors implements ICompositeCodeEditor {
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
export class NotebookEditor extends BaseEditor implements INotebookEditor, NotebookFindDelegate {
|
||||
static readonly ID: string = 'workbench.editor.notebook';
|
||||
private rootElement!: HTMLElement;
|
||||
private body!: HTMLElement;
|
||||
@@ -118,7 +120,6 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
private control: ICompositeCodeEditor | undefined;
|
||||
private renderedEditors: Map<CellViewModel, ICodeEditor | undefined> = new Map();
|
||||
private model: NotebookEditorModel | undefined;
|
||||
private notebook: INotebook | undefined;
|
||||
viewType: string | undefined;
|
||||
private viewCells: CellViewModel[] = [];
|
||||
private localStore: DisposableStore = this._register(new DisposableStore());
|
||||
@@ -128,6 +129,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
private dimension: DOM.Dimension | null = null;
|
||||
private editorFocus: IContextKey<boolean> | null = null;
|
||||
private outputRenderer: OutputRenderer;
|
||||
private findWidget: NotebookFindWidget;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -145,9 +147,10 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
|
||||
this.editorMemento = this.getEditorMemento<INotebookEditorViewState>(editorGroupService, NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY);
|
||||
this.outputRenderer = new OutputRenderer(this, this.instantiationService);
|
||||
this.findWidget = this.instantiationService.createInstance(NotebookFindWidget, this);
|
||||
this.findWidget.updateTheme(this.themeService.getTheme());
|
||||
}
|
||||
|
||||
|
||||
get minimumWidth(): number { return 375; }
|
||||
get maximumWidth(): number { return Number.POSITIVE_INFINITY; }
|
||||
|
||||
@@ -186,6 +189,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
this.contentWidgets = document.createElement('div');
|
||||
DOM.addClass(this.contentWidgets, 'notebook-content-widgets');
|
||||
DOM.append(this.body, this.contentWidgets);
|
||||
DOM.append(this.body, this.findWidget.getDomNode());
|
||||
}
|
||||
|
||||
private createCellList(): void {
|
||||
@@ -242,7 +246,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
}
|
||||
|
||||
onHide() {
|
||||
|
||||
this.editorFocus?.set(false);
|
||||
if (this.webview) {
|
||||
this.localStore.clear();
|
||||
this.list?.rowsContainer.removeChild(this.webview?.element);
|
||||
@@ -253,9 +257,8 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
this.list?.splice(0, this.list?.length);
|
||||
|
||||
if (this.model && !this.model.isDirty()) {
|
||||
this.notebookService.destoryNotebookDocument(this.viewType!, this.notebook!);
|
||||
this.notebookService.destoryNotebookDocument(this.viewType!, this.model!.notebook!);
|
||||
this.model = undefined;
|
||||
this.notebook = undefined;
|
||||
this.viewType = undefined;
|
||||
}
|
||||
|
||||
@@ -273,6 +276,11 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
}
|
||||
}
|
||||
|
||||
focus() {
|
||||
super.focus();
|
||||
this.editorFocus?.set(true);
|
||||
}
|
||||
|
||||
setInput(input: NotebookEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
|
||||
if (this.input instanceof NotebookEditorInput) {
|
||||
this.saveTextEditorViewState(this.input);
|
||||
@@ -306,12 +314,11 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
}));
|
||||
|
||||
let viewState = this.loadTextEditorViewState(input);
|
||||
this.notebook = model.getNotebook();
|
||||
this.webview.updateRendererPreloads(this.notebook.renderers);
|
||||
this.webview.updateRendererPreloads(this.model!.notebook.renderers);
|
||||
this.viewType = input.viewType;
|
||||
this.viewCells = await Promise.all(this.notebook!.cells.map(async cell => {
|
||||
this.viewCells = await Promise.all(this.model!.notebook!.cells.map(async cell => {
|
||||
const isEditing = viewState && viewState.editingCells[cell.handle];
|
||||
const viewCell = this.instantiationService.createInstance(CellViewModel, input.viewType!, this.notebook!.handle, cell, !!isEditing);
|
||||
const viewCell = this.instantiationService.createInstance(CellViewModel, input.viewType!, this.model!.notebook!.handle, cell, !!isEditing);
|
||||
this.localStore.add(viewCell);
|
||||
return viewCell;
|
||||
}));
|
||||
@@ -395,6 +402,37 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Find Delegate
|
||||
startFind(value: string): CellFindMatch[] {
|
||||
let matches: CellFindMatch[] = [];
|
||||
this.viewCells.forEach(cell => {
|
||||
let cellMatches = cell.startFind(value);
|
||||
matches.push(...cellMatches);
|
||||
});
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
stopFind(keepSelection?: boolean | undefined): void {
|
||||
}
|
||||
|
||||
focusNext(match: CellFindMatch) {
|
||||
let cell = match.cell;
|
||||
let index = this.viewCells.indexOf(cell);
|
||||
|
||||
this.list?.reveal(index);
|
||||
}
|
||||
|
||||
public showFind() {
|
||||
this.findWidget.reveal();
|
||||
}
|
||||
|
||||
public hideFind() {
|
||||
this.findWidget.hide();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Cell operations
|
||||
layoutNotebookCell(cell: CellViewModel, height: number) {
|
||||
let relayout = (cell: CellViewModel, height: number) => {
|
||||
@@ -413,7 +451,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
updateViewCells(splices: NotebookCellsSplice[]) {
|
||||
let update = () => splices.reverse().forEach((diff) => {
|
||||
this.list?.splice(diff[0], diff[1], diff[2].map(cell => {
|
||||
return this.instantiationService.createInstance(CellViewModel, this.viewType!, this.notebook!.handle, cell, false);
|
||||
return this.instantiationService.createInstance(CellViewModel, this.viewType!, this.model!.notebook!.handle, cell, false);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -433,7 +471,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
}
|
||||
|
||||
async insertEmptyNotebookCell(listIndex: number | undefined, cell: CellViewModel, type: 'code' | 'markdown', direction: 'above' | 'below'): Promise<void> {
|
||||
let newLanguages = this.notebook!.languages;
|
||||
let newLanguages = this.model!.notebook!.languages;
|
||||
let language = 'markdown';
|
||||
if (newLanguages && newLanguages.length) {
|
||||
language = newLanguages[0];
|
||||
@@ -442,8 +480,8 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
let index = listIndex ? listIndex : this.model!.getNotebook().cells.indexOf(cell.cell);
|
||||
const insertIndex = direction === 'above' ? index : index + 1;
|
||||
|
||||
let newModeCell = await this.notebookService.createNotebookCell(this.viewType!, this.notebook!.uri, insertIndex, language, type);
|
||||
let newCell = this.instantiationService.createInstance(CellViewModel, this.viewType!, this.notebook!.handle, newModeCell!, false);
|
||||
let newModeCell = await this.notebookService.createNotebookCell(this.viewType!, this.model!.notebook!.uri, insertIndex, language, type);
|
||||
let newCell = this.instantiationService.createInstance(CellViewModel, this.viewType!, this.model!.notebook!.handle, newModeCell!, false);
|
||||
|
||||
this.viewCells!.splice(insertIndex, 0, newCell);
|
||||
this.model!.insertCell(newCell.cell, insertIndex);
|
||||
@@ -498,8 +536,8 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
async deleteNotebookCell(listIndex: number | undefined, cell: CellViewModel): Promise<void> {
|
||||
let index = this.model!.getNotebook().cells.indexOf(cell.cell);
|
||||
|
||||
// await this.notebookService.createNotebookCell(this.viewType!, this.notebook!.uri, insertIndex, language, type);
|
||||
await this.notebookService.deleteNotebookCell(this.viewType!, this.notebook!.uri, index);
|
||||
// await this.notebookService.createNotebookCell(this.viewType!, this.model!.notebook!.uri, insertIndex, language, type);
|
||||
await this.notebookService.deleteNotebookCell(this.viewType!, this.model!.notebook!.uri, index);
|
||||
this.viewCells!.splice(index, 1);
|
||||
this.model!.deleteCell(cell.cell);
|
||||
this.list?.splice(index, 1);
|
||||
@@ -526,7 +564,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
|
||||
return;
|
||||
}
|
||||
|
||||
let preloads = this.notebook!.renderers;
|
||||
let preloads = this.model!.notebook!.renderers;
|
||||
|
||||
if (!this.webview!.insetMapping.has(output)) {
|
||||
let index = this.model!.getNotebook().cells.indexOf(cell.cell);
|
||||
|
||||
@@ -18,6 +18,11 @@ export class NotebookEditorModel extends EditorModel {
|
||||
private readonly _onDidChangeCells = new Emitter<NotebookCellsSplice[]>();
|
||||
get onDidChangeCells(): Event<NotebookCellsSplice[]> { return this._onDidChangeCells.event; }
|
||||
|
||||
|
||||
get notebook() {
|
||||
return this._notebook;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _notebook: INotebook
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SimpleFindWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/renderers/cellViewModel';
|
||||
import { FindMatch } from 'vs/editor/common/model';
|
||||
|
||||
export interface CellFindMatch {
|
||||
cell: CellViewModel,
|
||||
match: FindMatch
|
||||
}
|
||||
|
||||
export interface NotebookFindDelegate {
|
||||
startFind(value: string): CellFindMatch[];
|
||||
stopFind(keepSelection?: boolean): void;
|
||||
focus(): void;
|
||||
focusNext(nextMatch: CellFindMatch): void;
|
||||
}
|
||||
|
||||
|
||||
export class NotebookFindWidget extends SimpleFindWidget {
|
||||
protected _findWidgetFocused: IContextKey<boolean>;
|
||||
private _findMatches: CellFindMatch[] = [];
|
||||
private _currentMatch: CellFindMatch | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly _delegate: NotebookFindDelegate,
|
||||
@IContextViewService contextViewService: IContextViewService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(contextViewService, contextKeyService);
|
||||
this._findWidgetFocused = KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
protected onInputChanged(): boolean {
|
||||
const val = this.inputValue;
|
||||
if (val) {
|
||||
this._findMatches = this._delegate.startFind(val);
|
||||
if (this._findMatches.length) {
|
||||
this._currentMatch = this._findMatches[0];
|
||||
return true;
|
||||
} else {
|
||||
this._currentMatch = null;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
this._delegate.stopFind(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected find(previous: boolean): void {
|
||||
if (this._currentMatch && this._findMatches.length) {
|
||||
let len = this._findMatches.length;
|
||||
let index = this._findMatches.indexOf(this._currentMatch);
|
||||
|
||||
let nextIndex = previous ? (index - 1 + len) % len : index + 1 % len;
|
||||
let nextMatch = this._findMatches[nextIndex];
|
||||
|
||||
this._delegate.focusNext(nextMatch);
|
||||
this._currentMatch = nextMatch;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public hide() {
|
||||
super.hide();
|
||||
this._delegate.stopFind(true);
|
||||
this._delegate.focus();
|
||||
}
|
||||
|
||||
protected findFirst(): void { }
|
||||
|
||||
protected onFocusTrackerFocus() {
|
||||
this._findWidgetFocused.set(true);
|
||||
}
|
||||
|
||||
protected onFocusTrackerBlur() {
|
||||
this._findWidgetFocused.reset();
|
||||
}
|
||||
|
||||
protected onFindInputFocusTrackerFocus(): void { }
|
||||
protected onFindInputFocusTrackerBlur(): void { }
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ class RichRenderer implements IOutputTransformContribution {
|
||||
this._richMimeTypeRenderers.set('image/png', this.renderPNG.bind(this));
|
||||
this._richMimeTypeRenderers.set('image/jpeg', this.renderJavaScript.bind(this));
|
||||
this._richMimeTypeRenderers.set('text/plain', this.renderPlainText.bind(this));
|
||||
this._richMimeTypeRenderers.set('text/x-javascript', this.renderCode.bind(this));
|
||||
}
|
||||
|
||||
render(output: any, container: HTMLElement, preferredMimeType: string | undefined): IRenderOutput {
|
||||
@@ -73,7 +74,7 @@ class RichRenderer implements IOutputTransformContribution {
|
||||
let str = JSON.stringify(data, null, '\t');
|
||||
|
||||
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, {
|
||||
...getJSONSimpleEditorOptions(),
|
||||
...getOutputSimpleEditorOptions(),
|
||||
dimension: {
|
||||
width: 0,
|
||||
height: 0
|
||||
@@ -103,6 +104,41 @@ class RichRenderer implements IOutputTransformContribution {
|
||||
};
|
||||
}
|
||||
|
||||
renderCode(output: any, container: HTMLElement) {
|
||||
let data = output.data['text/x-javascript'];
|
||||
let str = isArray(data) ? data.join('') : data;
|
||||
|
||||
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, {
|
||||
...getOutputSimpleEditorOptions(),
|
||||
dimension: {
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
}, {
|
||||
isSimpleWidget: true
|
||||
});
|
||||
|
||||
let mode = this.modeService.create('javascript');
|
||||
let resource = URI.parse(`notebook-output-${Date.now()}.js`);
|
||||
const textModel = this.modelService.createModel(str, mode, resource, false);
|
||||
editor.setModel(textModel);
|
||||
|
||||
let width = this.notebookEditor.getListDimension()!.width;
|
||||
let fontInfo = this.notebookEditor.getFontInfo();
|
||||
let height = Math.min(textModel.getLineCount(), 16) * (fontInfo?.lineHeight || 18);
|
||||
|
||||
editor.layout({
|
||||
height,
|
||||
width
|
||||
});
|
||||
|
||||
container.style.height = `${height + 16}px`;
|
||||
|
||||
return {
|
||||
hasDynamicHeight: true
|
||||
};
|
||||
}
|
||||
|
||||
renderJavaScript(output: any, container: HTMLElement) {
|
||||
let data = output.data['application/javascript'];
|
||||
let str = isArray(data) ? data.join('') : data;
|
||||
@@ -188,8 +224,9 @@ class RichRenderer implements IOutputTransformContribution {
|
||||
registerOutputTransform('notebook.output.rich', ['display_data', 'execute_result'], RichRenderer);
|
||||
|
||||
|
||||
export function getJSONSimpleEditorOptions(): IEditorOptions {
|
||||
export function getOutputSimpleEditorOptions(): IEditorOptions {
|
||||
return {
|
||||
readOnly: true,
|
||||
wordWrap: 'on',
|
||||
overviewRulerLanes: 0,
|
||||
glyphMargin: false,
|
||||
|
||||
@@ -3,15 +3,19 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/renderers/mdRenderer';
|
||||
import { ICell, NotebookCellOutputsSplice, IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import * as model from 'vs/editor/common/model';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CellFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookFindWidget';
|
||||
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/renderers/mdRenderer';
|
||||
import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING, ICell, IOutput, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
|
||||
|
||||
export class CellViewModel extends Disposable {
|
||||
|
||||
@@ -63,7 +67,10 @@ export class CellViewModel extends Disposable {
|
||||
return this._editorHeight;
|
||||
}
|
||||
|
||||
private _textModel?: ITextModel;
|
||||
private _textModel?: model.ITextModel;
|
||||
private _textEditor?: ICodeEditor;
|
||||
private _buffer: model.ITextBuffer | null;
|
||||
|
||||
readonly id: string = UUID.generateUuid();
|
||||
|
||||
constructor(
|
||||
@@ -84,7 +91,60 @@ export class CellViewModel extends Disposable {
|
||||
}
|
||||
|
||||
this._outputCollection = new Array(this.cell.outputs.length);
|
||||
this._buffer = null;
|
||||
}
|
||||
|
||||
|
||||
//#region Search
|
||||
private readonly _hasFindResult = this._register(new Emitter<boolean>());
|
||||
public readonly hasFindResult: Event<boolean> = this._hasFindResult.event;
|
||||
|
||||
startFind(value: string): CellFindMatch[] {
|
||||
let cellMatches: model.FindMatch[] = [];
|
||||
if (this.assertTextModelAttached()) {
|
||||
cellMatches = this._textModel!.findMatches(value, false, false, false, null, false);
|
||||
} else {
|
||||
if (!this._buffer) {
|
||||
this._buffer = this.cell.resolveTextBufferFactory().create(model.DefaultEndOfLine.LF);
|
||||
}
|
||||
|
||||
const lineCount = this._buffer.getLineCount();
|
||||
const fullRange = new Range(1, 1, lineCount, this._buffer.getLineLength(lineCount) + 1);
|
||||
const searchParams = new SearchParams(value, false, false, null);
|
||||
const searchData = searchParams.parseSearchRequest();
|
||||
|
||||
if (!searchData) {
|
||||
return [];
|
||||
}
|
||||
|
||||
cellMatches = this._buffer.findMatchesLineByLine(fullRange, searchData, false, 1000);
|
||||
}
|
||||
|
||||
return cellMatches.map(match => ({
|
||||
cell: this,
|
||||
match: match
|
||||
}));
|
||||
}
|
||||
|
||||
stopFind(keepSelection?: boolean | undefined): void {
|
||||
if (!this.assertTextModelAttached()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
}
|
||||
|
||||
assertTextModelAttached(): boolean {
|
||||
if (this._textModel && this._textEditor && this._textEditor.getModel() === this._textModel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
hasDynamicHeight() {
|
||||
if (this._dynamicHeight !== null) {
|
||||
return false;
|
||||
@@ -118,7 +178,7 @@ export class CellViewModel extends Disposable {
|
||||
return 100;
|
||||
}
|
||||
else {
|
||||
return this.lineCount * lineHeight + 16;
|
||||
return this.lineCount * lineHeight + 16 + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING;
|
||||
}
|
||||
}
|
||||
setText(strs: string[]) {
|
||||
@@ -132,6 +192,10 @@ export class CellViewModel extends Disposable {
|
||||
}
|
||||
}
|
||||
getText(): string {
|
||||
if (this._textModel) {
|
||||
return this._textModel.getValue();
|
||||
}
|
||||
|
||||
return this.cell.source.join('\n');
|
||||
}
|
||||
|
||||
@@ -147,10 +211,11 @@ export class CellViewModel extends Disposable {
|
||||
return null;
|
||||
}
|
||||
|
||||
async resolveTextModel(): Promise<ITextModel> {
|
||||
async resolveTextModel(): Promise<model.ITextModel> {
|
||||
if (!this._textModel) {
|
||||
const ref = await this._modelService.createModelReference(this.cell.uri);
|
||||
this._textModel = ref.object.textEditorModel;
|
||||
this._buffer = this._textModel.getTextBuffer();
|
||||
this._register(ref);
|
||||
this._register(this._textModel.onDidChangeContent(() => {
|
||||
this.cell.isDirty = true;
|
||||
@@ -159,6 +224,10 @@ export class CellViewModel extends Disposable {
|
||||
return this._textModel;
|
||||
}
|
||||
|
||||
attachTextEditor(editor: ICodeEditor) {
|
||||
this._textEditor = editor;
|
||||
}
|
||||
|
||||
getMarkdownRenderer() {
|
||||
if (!this._mdRenderer) {
|
||||
this._mdRenderer = this._instaService.createInstance(MarkdownRenderer);
|
||||
|
||||
@@ -47,8 +47,9 @@ export class CodeCell extends Disposable {
|
||||
const cts = new CancellationTokenSource();
|
||||
this._register({ dispose() { cts.dispose(true); } });
|
||||
raceCancellation(viewCell.resolveTextModel(), cts.token).then(model => {
|
||||
if (model) {
|
||||
templateData.editor?.setModel(model);
|
||||
if (model && templateData.editor) {
|
||||
templateData.editor.setModel(model);
|
||||
viewCell.attachTextEditor(templateData.editor);
|
||||
|
||||
let realContentHeight = templateData.editor?.getContentHeight();
|
||||
let width: number;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { PieceTreeTextBufferFactory } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
|
||||
|
||||
export const NOTEBOOK_DISPLAY_ORDER = [
|
||||
'application/json',
|
||||
@@ -103,6 +104,7 @@ export interface ICell {
|
||||
outputs: IOutput[];
|
||||
onDidChangeOutputs?: Event<NotebookCellOutputsSplice[]>;
|
||||
isDirty: boolean;
|
||||
resolveTextBufferFactory(): PieceTreeTextBufferFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user