mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 09:18:59 +00:00
notebook editor worker service.
This commit is contained in:
parent
065fde64cc
commit
509bc25f02
|
@ -653,6 +653,20 @@
|
|||
"**/vs/workbench/services/**/{common,browser}/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "**/vs/workbench/contrib/notebook/common/**",
|
||||
"restrictions": [
|
||||
"vs/nls",
|
||||
"vs/css!./**/*",
|
||||
"**/vs/base/**/{common,worker}/**",
|
||||
"**/vs/platform/**/common/**",
|
||||
"**/vs/editor/**",
|
||||
"**/vs/workbench/common/**",
|
||||
"**/vs/workbench/api/common/**",
|
||||
"**/vs/workbench/services/**/common/**",
|
||||
"**/vs/workbench/contrib/**/common/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "**/vs/workbench/contrib/**/common/**",
|
||||
"restrictions": [
|
||||
|
|
|
@ -42,8 +42,10 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
|||
import { INotebookEditorModelResolverService, NotebookModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { NotebookDiffEditorInput } from './notebookDiffEditorInput';
|
||||
import { NotebookDiffEditor } from './notebookDiffEditor';
|
||||
import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput';
|
||||
import { NotebookDiffEditor } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditor';
|
||||
import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService';
|
||||
import { NotebookEditorWorkerServiceImpl } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl';
|
||||
|
||||
// Editor Contribution
|
||||
|
||||
|
@ -438,6 +440,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContributio
|
|||
workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting);
|
||||
|
||||
registerSingleton(INotebookService, NotebookService);
|
||||
registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl);
|
||||
registerSingleton(INotebookEditorModelResolverService, NotebookModelResolverService, true);
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
|
|
|
@ -16,15 +16,13 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note
|
|||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { NotebookDiffEditorInput } from './notebookDiffEditorInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDiffResult, LcsDiff } from 'vs/base/common/diff/diff';
|
||||
import { CellSequence, INotebookDiffEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookDiffEditorModel, INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { DiffComputer } from 'vs/editor/common/diff/diffComputer';
|
||||
import { createDecoration, DECORATIONS, isChangeOrDelete, isChangeOrInsert } from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
@ -32,6 +30,7 @@ import { IModelDeltaDecoration, IReadonlyTextBuffer } from 'vs/editor/common/mod
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { defaultInsertColor, defaultRemoveColor, diffInserted, diffRemoved } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService';
|
||||
|
||||
export class NotebookDiffEditor extends BaseEditor {
|
||||
static readonly ID: string = 'workbench.editor.notebookDiffEditor';
|
||||
|
@ -52,6 +51,7 @@ export class NotebookDiffEditor extends BaseEditor {
|
|||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService
|
||||
) {
|
||||
super(NotebookDiffEditor.ID, telemetryService, themeService, storageService);
|
||||
|
||||
|
@ -149,10 +149,9 @@ export class NotebookDiffEditor extends BaseEditor {
|
|||
}
|
||||
|
||||
private _update(model: INotebookDiffEditorModel) {
|
||||
const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook));
|
||||
const diffResult = diff.ComputeDiff(false);
|
||||
|
||||
this._adjustHeight(diffResult);
|
||||
this.notebookEditorWorkerService.computeDiff(model.original.notebook.uri, model.modified.notebook.uri).then(diffResult => {
|
||||
this._adjustHeight(diffResult);
|
||||
});
|
||||
}
|
||||
|
||||
private _setStrategy(newStrategy: DiffEditorWidgetSideBySide): void {
|
||||
|
@ -162,13 +161,12 @@ export class NotebookDiffEditor extends BaseEditor {
|
|||
|
||||
this._strategy = newStrategy;
|
||||
newStrategy.applyColors(this.themeService.getColorTheme());
|
||||
|
||||
// if (this._diffComputationResult) {
|
||||
// this._updateDecorations();
|
||||
// }
|
||||
}
|
||||
|
||||
private _adjustHeight(diffResult: IDiffResult) {
|
||||
private _adjustHeight(notebookDiffResult: INotebookDiffResult) {
|
||||
const diffResult = notebookDiffResult.cellsDiff;
|
||||
const linesDiffResult = notebookDiffResult.linesDiff;
|
||||
|
||||
if (!this._widget || !this._originalWidget) {
|
||||
return;
|
||||
}
|
||||
|
@ -211,67 +209,37 @@ export class NotebookDiffEditor extends BaseEditor {
|
|||
};
|
||||
|
||||
viewLayoutUpdateDisposables.push(...[
|
||||
...originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(e => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(e => update())),
|
||||
...modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(e => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(e => update())),
|
||||
...originalCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(() => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(() => update())),
|
||||
...modifiedCells.map(cell => (cell instanceof CodeCellViewModel) ? cell.onDidChangeLayout(() => update()) : (cell as MarkdownCellViewModel).onDidChangeLayout(() => update())),
|
||||
]);
|
||||
|
||||
update();
|
||||
});
|
||||
|
||||
// console.log(diffResult);
|
||||
|
||||
diffResult.changes.forEach(change => {
|
||||
if (change.modifiedLength === 0) {
|
||||
// deletion ...
|
||||
linesDiffResult.forEach(diff => {
|
||||
const originalCell = this._originalWidget!.viewModel!.viewCells.find(cell => cell.handle === diff.originalCellhandle);
|
||||
const modifiedCell = this._widget!.viewModel!.viewCells.find(cell => cell.handle === diff.modifiedCellhandle);
|
||||
|
||||
if (!originalCell || !modifiedCell) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (change.originalLength === 0) {
|
||||
// insertion
|
||||
return;
|
||||
}
|
||||
const lineDecorations = this._strategy.getEditorsDiffDecorations(diff.lineChanges, false, false, originalCell.textBuffer, modifiedCell.textBuffer);
|
||||
|
||||
for (let i = 0, len = Math.min(change.modifiedLength, change.originalLength); i < len; i++) {
|
||||
let originalIndex = change.originalStart + i;
|
||||
let modifiedIndex = change.modifiedStart + i;
|
||||
this._originalWidget?.changeModelDecorations(accessor => {
|
||||
accessor.deltaDecorations([], [{
|
||||
ownerId: diff.originalCellhandle,
|
||||
decorations: lineDecorations.original.decorations
|
||||
}]);
|
||||
});
|
||||
|
||||
const originalCell = this._originalWidget!.viewModel!.viewCells[originalIndex];
|
||||
const modifiedCell = this._widget!.viewModel!.viewCells[modifiedIndex];
|
||||
|
||||
if (originalCell.getText() !== modifiedCell.getText()) {
|
||||
// console.log(`original cell ${originalIndex} content change`);
|
||||
const originalLines = originalCell.textBuffer.getLinesContent();
|
||||
const modifiedLines = modifiedCell.textBuffer.getLinesContent();
|
||||
const diffComputer = new DiffComputer(originalLines, modifiedLines, {
|
||||
shouldComputeCharChanges: true,
|
||||
shouldPostProcessCharChanges: true,
|
||||
shouldIgnoreTrimWhitespace: false,
|
||||
shouldMakePrettyDiff: true,
|
||||
maxComputationTime: 5000
|
||||
});
|
||||
|
||||
const lineChanges = diffComputer.computeDiff().changes;
|
||||
const lineDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, false, false, originalCell.textBuffer, modifiedCell.textBuffer);
|
||||
|
||||
this._originalWidget?.changeModelDecorations(accessor => {
|
||||
accessor.deltaDecorations([], [{
|
||||
ownerId: originalCell.handle,
|
||||
decorations: lineDecorations.original.decorations
|
||||
}]);
|
||||
});
|
||||
|
||||
this._widget?.changeModelDecorations(accessor => {
|
||||
accessor.deltaDecorations([], [{
|
||||
ownerId: modifiedCell.handle,
|
||||
decorations: lineDecorations.modified.decorations
|
||||
}]);
|
||||
});
|
||||
|
||||
// console.log(lineDecorations);
|
||||
|
||||
} else {
|
||||
// console.log(`original cell ${originalIndex} metadata change`);
|
||||
}
|
||||
|
||||
}
|
||||
this._widget?.changeModelDecorations(accessor => {
|
||||
accessor.deltaDecorations([], [{
|
||||
ownerId: diff.modifiedCellhandle,
|
||||
decorations: lineDecorations.modified.decorations
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
this._originalCellDecorations = this._originalWidget.deltaCellDecorations(this._originalCellDecorations, originalDecorations);
|
||||
|
@ -362,15 +330,6 @@ export class DiffEditorWidgetSideBySide extends Disposable {
|
|||
}
|
||||
|
||||
public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalModel: IReadonlyTextBuffer, modifiedModel: IReadonlyTextBuffer): IEditorsDiffDecorationsWithZones {
|
||||
// Get view zones
|
||||
// modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => {
|
||||
// return a.afterLineNumber - b.afterLineNumber;
|
||||
// });
|
||||
// originalWhitespaces = originalWhitespaces.sort((a, b) => {
|
||||
// return a.afterLineNumber - b.afterLineNumber;
|
||||
// });
|
||||
// let zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, originalEditor, modifiedEditor, renderIndicators);
|
||||
|
||||
// Get decorations & overview ruler zones
|
||||
let originalDecorations = this._getOriginalEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, originalModel);
|
||||
let modifiedDecorations = this._getModifiedEditorDecorations(lineChanges, ignoreTrimWhitespace, renderIndicators, modifiedModel);
|
||||
|
@ -378,13 +337,9 @@ export class DiffEditorWidgetSideBySide extends Disposable {
|
|||
return {
|
||||
original: {
|
||||
decorations: originalDecorations.decorations,
|
||||
// overviewZones: originalDecorations.overviewZones,
|
||||
// zones: zones.original
|
||||
},
|
||||
modified: {
|
||||
decorations: modifiedDecorations.decorations,
|
||||
// overviewZones: modifiedDecorations.overviewZones,
|
||||
// zones: zones.modified
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
|||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { ISequence } from 'vs/base/common/diff/diff';
|
||||
import { IDiffResult, ISequence } from 'vs/base/common/diff/diff';
|
||||
|
||||
export enum CellKind {
|
||||
Markdown = 1,
|
||||
|
@ -715,3 +715,8 @@ export class CellSequence implements ISequence {
|
|||
return hashValue;
|
||||
}
|
||||
}
|
||||
|
||||
export interface INotebookDiffResult {
|
||||
cellsDiff: IDiffResult,
|
||||
linesDiff: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRequestHandler } from 'vs/base/common/worker/simpleWorker';
|
||||
import * as model from 'vs/editor/common/model';
|
||||
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
|
||||
import { CellKind, ICellDto2, IMainCellDto, INotebookDiffResult, IProcessedOutput, NotebookCellMetadata, NotebookDataDto, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { DiffComputer } from 'vs/editor/common/diff/diffComputer';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { EditorWorkerHost } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerServiceImpl';
|
||||
|
||||
class MirrorCell {
|
||||
private _textBuffer!: model.IReadonlyTextBuffer;
|
||||
|
||||
get textBuffer() {
|
||||
if (this._textBuffer) {
|
||||
return this._textBuffer;
|
||||
}
|
||||
|
||||
const builder = new PieceTreeTextBufferBuilder();
|
||||
builder.acceptChunk(Array.isArray(this._source) ? this._source.join('\n') : this._source);
|
||||
const bufferFactory = builder.finish(true);
|
||||
this._textBuffer = bufferFactory.create(model.DefaultEndOfLine.LF);
|
||||
|
||||
return this._textBuffer;
|
||||
}
|
||||
|
||||
private _hash: number | null = null;
|
||||
|
||||
|
||||
constructor(
|
||||
readonly handle: number,
|
||||
private _source: string | string[],
|
||||
readonly language: string,
|
||||
readonly cellKind: CellKind,
|
||||
readonly outputs: IProcessedOutput[],
|
||||
readonly metadata?: NotebookCellMetadata
|
||||
|
||||
) { }
|
||||
|
||||
getFullModelRange() {
|
||||
const lineCount = this.textBuffer.getLineCount();
|
||||
return new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1);
|
||||
}
|
||||
|
||||
getValue(): string {
|
||||
const fullRange = this.getFullModelRange();
|
||||
const eol = this.textBuffer.getEOL();
|
||||
if (eol === '\n') {
|
||||
return this.textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.LF);
|
||||
} else {
|
||||
return this.textBuffer.getValueInRange(fullRange, model.EndOfLinePreference.CRLF);
|
||||
}
|
||||
}
|
||||
|
||||
getHashValue(): number {
|
||||
if (this._hash !== null) {
|
||||
return this._hash;
|
||||
}
|
||||
|
||||
this._hash = hash([hash(this.getValue()), this.metadata]);
|
||||
// this._hash = hash(this.getValue());
|
||||
return this._hash;
|
||||
}
|
||||
}
|
||||
|
||||
class MirrorNotebookDocument {
|
||||
constructor(
|
||||
readonly uri: URI,
|
||||
readonly cells: MirrorCell[],
|
||||
readonly languages: string[],
|
||||
readonly metadata: NotebookDocumentMetadata,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class CellSequence implements ISequence {
|
||||
|
||||
constructor(readonly textModel: MirrorNotebookDocument) {
|
||||
}
|
||||
|
||||
getElements(): string[] | number[] | Int32Array {
|
||||
const hashValue = new Int32Array(this.textModel.cells.length);
|
||||
for (let i = 0; i < this.textModel.cells.length; i++) {
|
||||
hashValue[i] = this.textModel.cells[i].getHashValue();
|
||||
}
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
getCellHash(cell: ICellDto2) {
|
||||
const source = Array.isArray(cell.source) ? cell.source.join('\n') : cell.source;
|
||||
const hashVal = hash([hash(source), cell.metadata]);
|
||||
return hashVal;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
_requestHandlerBrand: any;
|
||||
|
||||
private _models: { [uri: string]: MirrorNotebookDocument; };
|
||||
|
||||
constructor() {
|
||||
this._models = Object.create(null);
|
||||
}
|
||||
dispose(): void {
|
||||
}
|
||||
|
||||
public acceptNewModel(uri: string, data: NotebookDataDto): void {
|
||||
this._models[uri] = new MirrorNotebookDocument(URI.parse(uri), data.cells.map(dto => new MirrorCell(
|
||||
(dto as IMainCellDto).handle,
|
||||
dto.source,
|
||||
dto.language,
|
||||
dto.cellKind,
|
||||
dto.outputs,
|
||||
dto.metadata
|
||||
)), data.languages, data.metadata);
|
||||
}
|
||||
|
||||
public acceptRemovedModel(strURL: string): void {
|
||||
if (!this._models[strURL]) {
|
||||
return;
|
||||
}
|
||||
delete this._models[strURL];
|
||||
}
|
||||
|
||||
computeDiff(originalUrl: string, modifiedUrl: string): INotebookDiffResult {
|
||||
const original = this._getModel(originalUrl);
|
||||
const modified = this._getModel(modifiedUrl);
|
||||
|
||||
const diff = new LcsDiff(new CellSequence(original), new CellSequence(modified));
|
||||
const diffResult = diff.ComputeDiff(false);
|
||||
|
||||
let cellLineChanges: { originalCellhandle: number, modifiedCellhandle: number, lineChanges: editorCommon.ILineChange[] }[] = [];
|
||||
|
||||
diffResult.changes.forEach(change => {
|
||||
if (change.modifiedLength === 0) {
|
||||
// deletion ...
|
||||
return;
|
||||
}
|
||||
|
||||
if (change.originalLength === 0) {
|
||||
// insertion
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0, len = Math.min(change.modifiedLength, change.originalLength); i < len; i++) {
|
||||
let originalIndex = change.originalStart + i;
|
||||
let modifiedIndex = change.modifiedStart + i;
|
||||
|
||||
const originalCell = original.cells[originalIndex];
|
||||
const modifiedCell = modified.cells[modifiedIndex];
|
||||
|
||||
if (originalCell.getValue() !== modifiedCell.getValue()) {
|
||||
// console.log(`original cell ${originalIndex} content change`);
|
||||
const originalLines = originalCell.textBuffer.getLinesContent();
|
||||
const modifiedLines = modifiedCell.textBuffer.getLinesContent();
|
||||
const diffComputer = new DiffComputer(originalLines, modifiedLines, {
|
||||
shouldComputeCharChanges: true,
|
||||
shouldPostProcessCharChanges: true,
|
||||
shouldIgnoreTrimWhitespace: false,
|
||||
shouldMakePrettyDiff: true,
|
||||
maxComputationTime: 5000
|
||||
});
|
||||
|
||||
const lineChanges = diffComputer.computeDiff().changes;
|
||||
|
||||
cellLineChanges.push({
|
||||
originalCellhandle: originalCell.handle,
|
||||
modifiedCellhandle: modifiedCell.handle,
|
||||
lineChanges
|
||||
});
|
||||
|
||||
// console.log(lineDecorations);
|
||||
|
||||
} else {
|
||||
// console.log(`original cell ${originalIndex} metadata change`);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
cellsDiff: diffResult,
|
||||
linesDiff: cellLineChanges
|
||||
};
|
||||
}
|
||||
|
||||
protected _getModel(uri: string): MirrorNotebookDocument {
|
||||
return this._models[uri];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the worker side
|
||||
* @internal
|
||||
*/
|
||||
export function create(host: EditorWorkerHost): IRequestHandler {
|
||||
return new NotebookEditorSimpleWorker();
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ILineChange } from 'vs/editor/common/editorCommon';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
export const ID_NOTEBOOK_EDITOR_WORKER_SERVICE = 'notebookEditorWorkerService';
|
||||
export const INotebookEditorWorkerService = createDecorator<INotebookEditorWorkerService>(ID_NOTEBOOK_EDITOR_WORKER_SERVICE);
|
||||
|
||||
export interface IDiffComputationResult {
|
||||
quitEarly: boolean;
|
||||
identical: boolean;
|
||||
changes: ILineChange[];
|
||||
}
|
||||
|
||||
export interface INotebookEditorWorkerService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
canComputeDiff(original: URI, modified: URI): boolean;
|
||||
computeDiff(original: URI, modified: URI): Promise<INotebookDiffResult>;
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker';
|
||||
import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory';
|
||||
import { INotebookDiffResult } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { NotebookEditorSimpleWorker } from 'vs/workbench/contrib/notebook/common/services/notebookSimpleWorker';
|
||||
import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService';
|
||||
|
||||
export class NotebookEditorWorkerServiceImpl extends Disposable implements INotebookEditorWorkerService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _workerManager: WorkerManager;
|
||||
|
||||
constructor(
|
||||
@INotebookService notebookService: INotebookService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._workerManager = this._register(new WorkerManager(notebookService));
|
||||
}
|
||||
canComputeDiff(original: URI, modified: URI): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
computeDiff(original: URI, modified: URI): Promise<INotebookDiffResult> {
|
||||
return this._workerManager.withWorker().then(client => {
|
||||
return client.computeDiff(original, modified);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerManager extends Disposable {
|
||||
private _editorWorkerClient: NotebookWorkerClient | null;
|
||||
// private _lastWorkerUsedTime: number;
|
||||
|
||||
constructor(
|
||||
private readonly _notebookService: INotebookService
|
||||
) {
|
||||
super();
|
||||
this._editorWorkerClient = null;
|
||||
// this._lastWorkerUsedTime = (new Date()).getTime();
|
||||
}
|
||||
|
||||
withWorker(): Promise<NotebookWorkerClient> {
|
||||
// this._lastWorkerUsedTime = (new Date()).getTime();
|
||||
if (!this._editorWorkerClient) {
|
||||
this._editorWorkerClient = new NotebookWorkerClient(this._notebookService, 'notebookEditorWorkerService');
|
||||
}
|
||||
return Promise.resolve(this._editorWorkerClient);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkerClient<W> {
|
||||
getProxyObject(): Promise<W>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export class NotebookEditorModelManager extends Disposable {
|
||||
private _syncedModels: { [modelUrl: string]: IDisposable; } = Object.create(null);
|
||||
private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null);
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: NotebookEditorSimpleWorker,
|
||||
private readonly _notebookService: INotebookService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public ensureSyncedResources(resources: URI[]): void {
|
||||
for (const resource of resources) {
|
||||
let resourceStr = resource.toString();
|
||||
|
||||
if (!this._syncedModels[resourceStr]) {
|
||||
this._beginModelSync(resource);
|
||||
}
|
||||
if (this._syncedModels[resourceStr]) {
|
||||
this._syncedModelsLastUsedTime[resourceStr] = (new Date()).getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _beginModelSync(resource: URI): void {
|
||||
let model = this._notebookService.listNotebookDocuments().find(document => document.uri.toString() === resource.toString());
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
let modelUrl = resource.toString();
|
||||
|
||||
this._proxy.acceptNewModel(
|
||||
model.uri.toString(),
|
||||
{
|
||||
cells: model.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
})),
|
||||
languages: model.languages,
|
||||
metadata: model.metadata
|
||||
}
|
||||
);
|
||||
|
||||
const toDispose = new DisposableStore();
|
||||
|
||||
// TODO, accept Model change
|
||||
|
||||
// toDispose.add(model.onDidChangeContent((e) => {
|
||||
// this._proxy.acceptModelChanged(modelUrl.toString(), e);
|
||||
// }));
|
||||
toDispose.add(model.onWillDispose(() => {
|
||||
this._stopModelSync(modelUrl);
|
||||
}));
|
||||
toDispose.add(toDisposable(() => {
|
||||
this._proxy.acceptRemovedModel(modelUrl);
|
||||
}));
|
||||
|
||||
this._syncedModels[modelUrl] = toDispose;
|
||||
}
|
||||
|
||||
private _stopModelSync(modelUrl: string): void {
|
||||
let toDispose = this._syncedModels[modelUrl];
|
||||
delete this._syncedModels[modelUrl];
|
||||
delete this._syncedModelsLastUsedTime[modelUrl];
|
||||
dispose(toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorWorkerHost {
|
||||
|
||||
private readonly _workerClient: NotebookWorkerClient;
|
||||
|
||||
constructor(workerClient: NotebookWorkerClient) {
|
||||
this._workerClient = workerClient;
|
||||
}
|
||||
|
||||
// foreign host request
|
||||
public fhr(method: string, args: any[]): Promise<any> {
|
||||
return this._workerClient.fhr(method, args);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookWorkerClient extends Disposable {
|
||||
private _worker: IWorkerClient<NotebookEditorSimpleWorker> | null;
|
||||
private readonly _workerFactory: DefaultWorkerFactory;
|
||||
private _modelManager: NotebookEditorModelManager | null;
|
||||
|
||||
|
||||
constructor(private readonly _notebookService: INotebookService, label: string) {
|
||||
super();
|
||||
this._workerFactory = new DefaultWorkerFactory(label);
|
||||
this._worker = null;
|
||||
this._modelManager = null;
|
||||
|
||||
}
|
||||
|
||||
// foreign host request
|
||||
public fhr(method: string, args: any[]): Promise<any> {
|
||||
throw new Error(`Not implemented!`);
|
||||
}
|
||||
|
||||
computeDiff(original: URI, modified: URI) {
|
||||
return this._withSyncedResources([original, modified]).then(proxy => {
|
||||
return proxy.computeDiff(original.toString(), modified.toString());
|
||||
});
|
||||
}
|
||||
|
||||
private _getOrCreateModelManager(proxy: NotebookEditorSimpleWorker): NotebookEditorModelManager {
|
||||
if (!this._modelManager) {
|
||||
this._modelManager = this._register(new NotebookEditorModelManager(proxy, this._notebookService));
|
||||
}
|
||||
return this._modelManager;
|
||||
}
|
||||
|
||||
protected _withSyncedResources(resources: URI[]): Promise<NotebookEditorSimpleWorker> {
|
||||
return this._getProxy().then((proxy) => {
|
||||
this._getOrCreateModelManager(proxy).ensureSyncedResources(resources);
|
||||
return proxy;
|
||||
});
|
||||
}
|
||||
|
||||
private _getOrCreateWorker(): IWorkerClient<NotebookEditorSimpleWorker> {
|
||||
if (!this._worker) {
|
||||
try {
|
||||
this._worker = this._register(new SimpleWorkerClient<NotebookEditorSimpleWorker, EditorWorkerHost>(
|
||||
this._workerFactory,
|
||||
'vs/workbench/contrib/notebook/common/services/notebookSimpleWorker',
|
||||
new EditorWorkerHost(this)
|
||||
));
|
||||
} catch (err) {
|
||||
// logOnceWebWorkerWarning(err);
|
||||
// this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null));
|
||||
throw (err);
|
||||
}
|
||||
}
|
||||
return this._worker;
|
||||
}
|
||||
|
||||
protected _getProxy(): Promise<NotebookEditorSimpleWorker> {
|
||||
return this._getOrCreateWorker().getProxyObject().then(undefined, (err) => {
|
||||
// logOnceWebWorkerWarning(err);
|
||||
// this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null));
|
||||
// return this._getOrCreateWorker().getProxyObject();
|
||||
throw (err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue