提交 d256d562 编写于 作者: R rebornix

replace first cut

上级 c41d6313
......@@ -212,7 +212,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
label: NLS_REPLACE_BTN_LABEL,
className: 'codicon codicon-replace',
onTrigger: () => {
// this._controller.replace();
this.replaceOne();
}
}));
......@@ -221,7 +221,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
label: NLS_REPLACE_ALL_BTN_LABEL,
className: 'codicon codicon-replace-all',
onTrigger: () => {
// this._controller.replaceAll();
this.replaceAll();
}
}));
......@@ -234,6 +234,8 @@ export abstract class SimpleFindReplaceWidget extends Widget {
protected abstract onInputChanged(): boolean;
protected abstract find(previous: boolean): void;
protected abstract findFirst(): void;
protected abstract replaceOne(): void;
protected abstract replaceAll(): void;
protected abstract onFocusTrackerFocus(): void;
protected abstract onFocusTrackerBlur(): void;
protected abstract onFindInputFocusTrackerFocus(): void;
......@@ -245,6 +247,10 @@ export abstract class SimpleFindReplaceWidget extends Widget {
return this._findInput.getValue();
}
protected get replaceValue() {
return this._replaceInput.getValue();
}
public get focusTracker(): dom.IFocusTracker {
return this._focusTracker;
}
......
......@@ -858,6 +858,39 @@ registerAction2(class extends Action2 {
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.notebook.undo',
title: 'Notebook Undo',
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z,
weight: KeybindingWeight.WorkbenchContrib
}
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const editorService = accessor.get(IEditorService);
const editor = getActiveNotebookEditor(editorService);
if (!editor) {
return;
}
const viewModel = editor.viewModel;
if (!viewModel) {
return;
}
if (viewModel.canUndo()) {
viewModel.undo();
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({
......
......@@ -82,6 +82,27 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget {
this.revealCellRange(nextIndex.index, nextIndex.remainder);
}
protected replaceOne() {
if (!this._findMatches.length) {
return;
}
if (!this._findMatchesStarts) {
this.set(this._findMatches);
}
const nextIndex = this._findMatchesStarts!.getIndexOf(this._currentMatch);
const cell = this._findMatches[nextIndex.index].cell;
const match = this._findMatches[nextIndex.index].matches[nextIndex.remainder];
return this._notebookEditor.viewModel!.replaceOne(cell, match.range, this.replaceValue);
}
protected replaceAll() {
return this._notebookEditor.viewModel!.replaceAll(this._findMatches, this.replaceValue);
}
private revealCellRange(cellIndex: number, matchIndex: number) {
this._findMatches[cellIndex].cell.state = CellState.Editing;
this._notebookEditor.selectElement(this._findMatches[cellIndex].cell);
......
......@@ -185,6 +185,14 @@ export class StatefullMarkdownCell extends Disposable {
const clientHeight = this.cellContainer.clientHeight;
notebookEditor.layoutNotebookCell(viewCell, clientHeight);
}));
this.localDisposables.add(viewCell.onDidChangeContent(() => {
this.cellContainer.innerHTML = '';
let renderedHTML = viewCell.getHTML();
if (renderedHTML) {
this.cellContainer.appendChild(renderedHTML);
}
}));
}
}
};
......
......@@ -110,6 +110,8 @@ export class CellViewModel extends Disposable implements ICellViewModel {
private _editorViewStates: editorCommon.ICodeEditorViewState | null;
private _lastDecorationId: number = 0;
private _resolvedDecorations = new Map<string, { id?: string, options: model.IModelDeltaDecoration }>();
private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
private readonly _onDidChangeCursorSelection: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChangeCursorSelection: Event<void> = this._onDidChangeCursorSelection.event;
......@@ -275,6 +277,8 @@ export class CellViewModel extends Disposable implements ICellViewModel {
this._register(ref);
this._register(this._textModel.onDidChangeContent(() => {
this.cell.isDirty = true;
this._html = null;
this._onDidChangeContent.fire();
}));
}
return this._textModel;
......
......@@ -13,6 +13,11 @@ import { NotebookCellsSplice, ICell } from 'vs/workbench/contrib/notebook/common
import { IModelDeltaDecoration } from 'vs/editor/common/model';
import { onUnexpectedError } from 'vs/base/common/errors';
import { CellFindMatch, CellState, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { Range } from 'vs/editor/common/core/range';
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
import { URI } from 'vs/base/common/uri';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
export interface INotebookEditorViewState {
editingCells: { [key: number]: boolean };
......@@ -67,10 +72,21 @@ export class NotebookViewModel extends Disposable {
private readonly _onDidChangeCells = new Emitter<NotebookCellsSplice[]>();
get onDidChangeCells(): Event<NotebookCellsSplice[]> { return this._onDidChangeCells.event; }
private _lastNotebookEditResource: URI[] = [];
get lastNotebookEditResource(): URI | null {
if (this._lastNotebookEditResource.length) {
return this._lastNotebookEditResource[this._lastNotebookEditResource.length - 1];
}
return null;
}
constructor(
public viewType: string,
private _model: NotebookEditorModel,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IBulkEditService private readonly bulkEditService: IBulkEditService,
@IUndoRedoService private readonly undoService: IUndoRedoService
) {
super();
......@@ -98,22 +114,6 @@ export class NotebookViewModel extends Disposable {
return this._viewCells.indexOf(cell as CellViewModel);
}
/**
* Search in notebook text model
* @param value
*/
find(value: string): CellFindMatch[] {
const matches: CellFindMatch[] = [];
this._viewCells.forEach(cell => {
const cellMatches = cell.startFind(value);
if (cellMatches) {
matches.push(cellMatches);
}
});
return matches;
}
insertCell(index: number, cell: ICell): CellViewModel {
const newCell = this.instantiationService.createInstance(CellViewModel, this.viewType, this.handle, cell);
this._viewCells!.splice(index, 0, newCell);
......@@ -245,6 +245,87 @@ export class NotebookViewModel extends Disposable {
return ret;
}
/**
* Search in notebook text model
* @param value
*/
find(value: string): CellFindMatch[] {
const matches: CellFindMatch[] = [];
this._viewCells.forEach(cell => {
const cellMatches = cell.startFind(value);
if (cellMatches) {
matches.push(cellMatches);
}
});
return matches;
}
replaceOne(cell: ICellViewModel, range: Range, text: string): Promise<void> {
const viewCell = cell as CellViewModel;
this._lastNotebookEditResource.push(viewCell.uri);
return viewCell.resolveTextModel().then(() => {
this.bulkEditService.apply({ edits: [{ edit: { range: range, text: text }, resource: cell.uri }] }, { quotableLabel: 'Notebook Replace' });
});
}
async replaceAll(matches: CellFindMatch[], text: string): Promise<void> {
if (!matches.length) {
return;
}
let textEdits: WorkspaceTextEdit[] = [];
this._lastNotebookEditResource.push(matches[0].cell.uri);
matches.forEach(match => {
match.matches.forEach(singleMatch => {
textEdits.push({
edit: { range: singleMatch.range, text: text },
resource: match.cell.uri
});
});
});
return Promise.all(matches.map(match => {
return match.cell.resolveTextModel();
})).then(async () => {
this.bulkEditService.apply({ edits: textEdits }, { quotableLabel: 'Notebook Replace All' });
return;
});
}
canUndo(): boolean {
const lastResource = this.lastNotebookEditResource;
if (!lastResource) {
return false;
}
const lastElement = this.undoService.getLastElement(lastResource);
if (lastElement?.label === 'Notebook Replace' || lastElement?.label === 'Notebook Replace All') {
return true;
}
return false;
}
undo() {
const lastResource = this.lastNotebookEditResource;
if (!lastResource) {
return;
}
const lastElement = this.undoService.getLastElement(lastResource);
if (lastElement?.label === 'Notebook Replace' || lastElement?.label === 'Notebook Replace All') {
this.undoService.undo(lastResource);
this._lastNotebookEditResource.pop();
}
}
equal(model: NotebookEditorModel) {
return this._model === model;
}
......
......@@ -10,20 +10,26 @@ import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/noteb
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { TestNotebook, withTestNotebook, TestCell } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
suite('NotebookViewModel', () => {
const instantiationService = new TestInstantiationService();
const blukEditService = instantiationService.get(IBulkEditService);
const undoRedoService = instantiationService.get(IUndoRedoService);
test('ctor', function () {
const notebook = new TestNotebook(0, 'notebook', URI.parse('test'));
const model = new NotebookEditorModel(notebook);
const viewModel = new NotebookViewModel('notebook', model, instantiationService);
const viewModel = new NotebookViewModel('notebook', model, instantiationService, blukEditService, undoRedoService);
assert.equal(viewModel.viewType, 'notebook');
});
test('insert/delete', function () {
withTestNotebook(
instantiationService,
blukEditService,
undoRedoService,
[
[['var a = 1;'], 'javascript', CellKind.Code, []],
[['var b = 2;'], 'javascript', CellKind.Code, []]
......@@ -45,6 +51,8 @@ suite('NotebookViewModel', () => {
test('index', function () {
withTestNotebook(
instantiationService,
blukEditService,
undoRedoService,
[
[['var a = 1;'], 'javascript', CellKind.Code, []],
[['var b = 2;'], 'javascript', CellKind.Code, []]
......
......@@ -17,6 +17,8 @@ import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { Range } from 'vs/editor/common/core/range';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
export class TestCell implements ICell {
uri: URI;
......@@ -200,7 +202,7 @@ export function createTestCellViewModel(instantiationService: IInstantiationServ
return instantiationService.createInstance(CellViewModel, viewType, notebookHandle, mockCell);
}
export function withTestNotebook(instantiationService: IInstantiationService, cells: [string[], string, CellKind, IOutput[]][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel) => void) {
export function withTestNotebook(instantiationService: IInstantiationService, blukEditService: IBulkEditService, undoRedoService: IUndoRedoService, cells: [string[], string, CellKind, IOutput[]][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel) => void) {
const viewType = 'notebook';
const editor = new TestNotebookEditor();
const notebook = new TestNotebook(0, viewType, URI.parse('test'));
......@@ -208,7 +210,7 @@ export function withTestNotebook(instantiationService: IInstantiationService, ce
return new TestCell(viewType, index, cell[0], cell[1], cell[2], cell[3]);
});
const model = new NotebookEditorModel(notebook);
const viewModel = new NotebookViewModel(viewType, model, instantiationService);
const viewModel = new NotebookViewModel(viewType, model, instantiationService, blukEditService, undoRedoService);
callback(editor, viewModel);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册