提交 cef3c064 编写于 作者: R rebornix

find widget attempt

上级 298461ed
......@@ -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) {
return;
}
let editorViewType = (editorControl! as NotebookEditor).viewType;
let viewType = notebookProviders[0].id;
if (viewType !== editorViewType) {
if (!editor) {
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) {
return;
}
let editorViewType = (editorControl! as NotebookEditor).viewType;
let viewType = notebookProviders[0].id;
if (viewType !== editorViewType) {
if (!editor) {
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
) {
......
/*---------------------------------------------------------------------------------------------
* 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;
}
/**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册