mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Allows to pass in a custom diffing algorithm when instantiating the monaco diff editor. (#165179)
* Allows to pass in a custom diffing algorithm when instantiating the monaco diff editor. * Undo launch.json change
This commit is contained in:
parent
8bd052125b
commit
3e19ba91ff
|
@ -73,6 +73,8 @@ export interface ICommandHandler {
|
|||
#include(vs/editor/common/core/wordHelper): IWordAtPosition
|
||||
#includeAll(vs/editor/common/model): IScrollEvent
|
||||
#include(vs/editor/common/diff/smartLinesDiffComputer): IChange, ICharChange, ILineChange
|
||||
#include(vs/editor/common/diff/documentDiffProvider): IDocumentDiffProvider, IDocumentDiffProviderOptions, IDocumentDiff
|
||||
#include(vs/editor/common/diff/linesDiffComputer): LineRangeMapping, LineRange, RangeMapping
|
||||
#include(vs/editor/common/core/dimension): IDimension
|
||||
#includeAll(vs/editor/common/editorCommon): IScrollEvent
|
||||
#includeAll(vs/editor/common/textModelEvents):
|
||||
|
|
|
@ -14,7 +14,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
|||
import * as languages from 'vs/editor/common/languages';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker';
|
||||
import { IDiffComputationResult, IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
|
||||
import { DiffAlgorithmName, IDiffComputationResult, IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
|
||||
import { regExpFlags } from 'vs/base/common/strings';
|
||||
|
@ -26,7 +26,8 @@ import { UnicodeHighlighterOptions } from 'vs/editor/common/services/unicodeText
|
|||
import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
|
||||
import { IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { LineRangeMapping, LineRange, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
|
||||
/**
|
||||
* Stop syncing a model to the worker if it was not needed for 1 min.
|
||||
|
@ -95,8 +96,31 @@ export class EditorWorkerService extends Disposable implements IEditorWorkerServ
|
|||
return this._workerManager.withWorker().then(client => client.computedUnicodeHighlights(uri, options, range));
|
||||
}
|
||||
|
||||
public computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions): Promise<IDiffComputationResult | null> {
|
||||
return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, options));
|
||||
public async computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise<IDocumentDiff | null> {
|
||||
const result = await this._workerManager.withWorker().then(client => client.computeDiff(original, modified, options, algorithm));
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
// Convert from space efficient JSON data to rich objects.
|
||||
const diff: IDocumentDiff = {
|
||||
identical: result.identical,
|
||||
quitEarly: result.quitEarly,
|
||||
changes: result.changes.map(
|
||||
(c) =>
|
||||
new LineRangeMapping(
|
||||
new LineRange(c[0], c[1]),
|
||||
new LineRange(c[2], c[3]),
|
||||
c[4]?.map(
|
||||
(c) =>
|
||||
new RangeMapping(
|
||||
new Range(c[0], c[1], c[2], c[3]),
|
||||
new Range(c[4], c[5], c[6], c[7])
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
};
|
||||
return diff;
|
||||
}
|
||||
|
||||
public canComputeDirtyDiff(original: URI, modified: URI): boolean {
|
||||
|
@ -492,9 +516,9 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
|
|||
});
|
||||
}
|
||||
|
||||
public computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions): Promise<IDiffComputationResult | null> {
|
||||
public computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise<IDiffComputationResult | null> {
|
||||
return this._withSyncedResources([original, modified], /* forceLargeModels */true).then(proxy => {
|
||||
return proxy.computeDiff(original.toString(), modified.toString(), options);
|
||||
return proxy.computeDiff(original.toString(), modified.toString(), options, algorithm);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,61 +3,60 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/diffEditor';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
|
||||
import { ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash';
|
||||
import * as assert from 'vs/base/common/assert';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState, Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/diffEditor';
|
||||
import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo';
|
||||
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
|
||||
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
|
||||
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
|
||||
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { DiffReview } from 'vs/editor/browser/widget/diffReview';
|
||||
import { IDiffEditorOptions, EditorLayoutInfo, EditorOption, EditorOptions, EditorFontLigatures, stringSet as validateStringSetOption, boolean as validateBooleanOption, ValidDiffEditorBaseOptions, clampedInt } from 'vs/editor/common/config/editorOptions';
|
||||
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
|
||||
import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider';
|
||||
import { boolean as validateBooleanOption, clampedInt, EditorFontLigatures, EditorLayoutInfo, EditorOption, EditorOptions, IDiffEditorOptions, stringSet as validateStringSetOption, ValidDiffEditorBaseOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { IDimension } from 'vs/editor/common/core/dimension';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { ISelection, Selection } from 'vs/editor/common/core/selection';
|
||||
import { StringBuilder } from 'vs/editor/common/core/stringBuilder';
|
||||
import { IChange, ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager';
|
||||
import { ILineBreaksComputer } from 'vs/editor/common/modelLineProjectionData';
|
||||
import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
|
||||
import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer';
|
||||
import { IEditorWhitespace, InlineDecoration, InlineDecorationType, IViewModel, ViewLineRenderingData } from 'vs/editor/common/viewModel';
|
||||
import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffDiagonalFill, diffInsertedLineGutter, diffRemovedLineGutter, diffInsertedLine, diffRemovedLine, diffOverviewRulerInserted, diffOverviewRulerRemoved } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
|
||||
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
|
||||
import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffDiagonalFill, diffInserted, diffInsertedLine, diffInsertedLineGutter, diffInsertedOutline, diffOverviewRulerInserted, diffOverviewRulerRemoved, diffRemoved, diffRemovedLine, diffRemovedLineGutter, diffRemovedOutline, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { ILineBreaksComputer } from 'vs/editor/common/modelLineProjectionData';
|
||||
import { IChange, ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
|
||||
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
|
||||
import { IDimension } from 'vs/editor/common/core/dimension';
|
||||
import { isHighContrast } from 'vs/platform/theme/common/theme';
|
||||
import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider';
|
||||
import { getThemeTypeSelector, IColorTheme, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export interface IDiffCodeEditorWidgetOptions {
|
||||
originalEditor?: ICodeEditorWidgetOptions;
|
||||
|
@ -227,7 +226,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
|
||||
private readonly _updateDecorationsRunner: RunOnceScheduler;
|
||||
|
||||
private readonly _documentDiffProvider: IDocumentDiffProvider;
|
||||
private readonly _documentDiffProvider: WorkerBasedDocumentDiffProvider;
|
||||
private readonly _contextKeyService: IContextKeyService;
|
||||
private readonly _instantiationService: IInstantiationService;
|
||||
private readonly _codeEditorService: ICodeEditorService;
|
||||
|
@ -251,7 +250,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
) {
|
||||
super();
|
||||
|
||||
this._documentDiffProvider = instantiationService.createInstance(WorkerBasedDocumentDiffProvider);
|
||||
this._documentDiffProvider = this._register(instantiationService.createInstance(WorkerBasedDocumentDiffProvider, options));
|
||||
this._register(this._documentDiffProvider.onDidChange(e => this._beginUpdateDecorationsSoon()));
|
||||
|
||||
this._codeEditorService = codeEditorService;
|
||||
this._contextKeyService = this._register(contextKeyService.createScoped(domElement));
|
||||
this._instantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService]));
|
||||
|
@ -762,7 +763,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
this._options = newOptions;
|
||||
|
||||
const beginUpdateDecorations = (changed.ignoreTrimWhitespace || changed.renderIndicators || changed.renderMarginRevertIcon);
|
||||
const beginUpdateDecorationsSoon = (this._isVisible && (changed.maxComputationTime || changed.maxFileSize || changed.diffAlgorithm));
|
||||
const beginUpdateDecorationsSoon = (this._isVisible && (changed.maxComputationTime || changed.maxFileSize));
|
||||
this._documentDiffProvider.setOptions(newOptions);
|
||||
|
||||
if (beginUpdateDecorations) {
|
||||
this._beginUpdateDecorations();
|
||||
|
@ -1092,7 +1094,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
}
|
||||
|
||||
private _beginUpdateDecorations(): void {
|
||||
this._beginUpdateDecorationsTimeout = -1;
|
||||
if (this._beginUpdateDecorationsTimeout !== -1) {
|
||||
// Cancel any pending requests in case this method is called directly
|
||||
window.clearTimeout(this._beginUpdateDecorationsTimeout);
|
||||
this._beginUpdateDecorationsTimeout = -1;
|
||||
}
|
||||
const currentOriginalModel = this._originalEditor.getModel();
|
||||
const currentModifiedModel = this._modifiedEditor.getModel();
|
||||
if (!currentOriginalModel || !currentModifiedModel) {
|
||||
|
@ -1126,8 +1132,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
this._setState(editorBrowser.DiffEditorState.ComputingDiff);
|
||||
this._documentDiffProvider.computeDiff(currentOriginalModel, currentModifiedModel, {
|
||||
ignoreTrimWhitespace: this._options.ignoreTrimWhitespace,
|
||||
maxComputationTime: this._options.maxComputationTime,
|
||||
diffAlgorithm: this._options.diffAlgorithm,
|
||||
maxComputationTimeMs: this._options.maxComputationTime,
|
||||
}).then(result => {
|
||||
if (currentToken === this._diffComputationToken
|
||||
&& currentOriginalModel === this._originalEditor.getModel()
|
||||
|
|
|
@ -3,43 +3,63 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { LineRange, LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDocumentDiff, IDocumentDiffProvider, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DiffAlgorithmName, IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
|
||||
export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, IDisposable {
|
||||
private onDidChangeEventEmitter = new Emitter<void>();
|
||||
public readonly onDidChange: Event<void> = this.onDidChangeEventEmitter.event;
|
||||
|
||||
private diffAlgorithm: DiffAlgorithmName | IDocumentDiffProvider = 'smart';
|
||||
private diffAlgorithmOnDidChangeSubscription: IDisposable | undefined = undefined;
|
||||
|
||||
export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider {
|
||||
constructor(
|
||||
options: IWorkerBasedDocumentDiffProviderOptions,
|
||||
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService,
|
||||
) {
|
||||
this.setOptions(options);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.diffAlgorithmOnDidChangeSubscription?.dispose();
|
||||
}
|
||||
|
||||
async computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions): Promise<IDocumentDiff> {
|
||||
const result = await this.editorWorkerService.computeDiff(original.uri, modified.uri, options);
|
||||
if (typeof this.diffAlgorithm !== 'string') {
|
||||
return this.diffAlgorithm.computeDiff(original, modified, options);
|
||||
}
|
||||
|
||||
const result = await this.editorWorkerService.computeDiff(original.uri, modified.uri, options, this.diffAlgorithm);
|
||||
if (!result) {
|
||||
throw new Error('no diff result available');
|
||||
}
|
||||
|
||||
// Convert from space efficient JSON data to rich objects.
|
||||
const diff: IDocumentDiff = {
|
||||
identical: result.identical,
|
||||
quitEarly: result.quitEarly,
|
||||
changes: result.changes.map(
|
||||
(c) =>
|
||||
new LineRangeMapping(
|
||||
new LineRange(c[0], c[1]),
|
||||
new LineRange(c[2], c[3]),
|
||||
c[4]?.map(
|
||||
(c) =>
|
||||
new RangeMapping(
|
||||
new Range(c[0], c[1], c[2], c[3]),
|
||||
new Range(c[4], c[5], c[6], c[7])
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
};
|
||||
return diff;
|
||||
return result;
|
||||
}
|
||||
|
||||
public setOptions(newOptions: IWorkerBasedDocumentDiffProviderOptions): void {
|
||||
let didChange = false;
|
||||
if (newOptions.diffAlgorithm) {
|
||||
if (this.diffAlgorithm !== newOptions.diffAlgorithm) {
|
||||
this.diffAlgorithmOnDidChangeSubscription?.dispose();
|
||||
this.diffAlgorithmOnDidChangeSubscription = undefined;
|
||||
|
||||
this.diffAlgorithm = newOptions.diffAlgorithm;
|
||||
if (typeof newOptions.diffAlgorithm !== 'string') {
|
||||
this.diffAlgorithmOnDidChangeSubscription = newOptions.diffAlgorithm.onDidChange(() => this.onDidChangeEventEmitter.fire());
|
||||
}
|
||||
didChange = true;
|
||||
}
|
||||
}
|
||||
if (didChange) {
|
||||
this.onDidChangeEventEmitter.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IWorkerBasedDocumentDiffProviderOptions {
|
||||
readonly diffAlgorithm?: 'smart' | 'experimental' | IDocumentDiffProvider;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
|||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/core/textModelDefaults';
|
||||
import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
|
||||
//#region typed options
|
||||
|
||||
|
@ -739,7 +740,7 @@ export interface IDiffEditorBaseOptions {
|
|||
/**
|
||||
* Diff Algorithm
|
||||
*/
|
||||
diffAlgorithm?: 'smart' | 'experimental';
|
||||
diffAlgorithm?: 'smart' | 'experimental' | IDocumentDiffProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,21 +3,57 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
/**
|
||||
* A document diff provider computes the diff between two text models.
|
||||
*/
|
||||
export interface IDocumentDiffProvider {
|
||||
/**
|
||||
* Computes the diff between the text models `original` and `modified`.
|
||||
*/
|
||||
computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions): Promise<IDocumentDiff>;
|
||||
|
||||
/**
|
||||
* Is fired when settings of the diff algorithm change that could alter the result of the diffing computation.
|
||||
* Any user of this provider should recompute the diff when this event is fired.
|
||||
*/
|
||||
onDidChange: Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the diff computation.
|
||||
*/
|
||||
export interface IDocumentDiffProviderOptions {
|
||||
/**
|
||||
* When set to true, the diff should ignore whitespace changes.i
|
||||
*/
|
||||
ignoreTrimWhitespace: boolean;
|
||||
maxComputationTime: number;
|
||||
diffAlgorithm: 'smart' | 'experimental';
|
||||
|
||||
/**
|
||||
* A diff computation should throw if it takes longer than this value.
|
||||
*/
|
||||
maxComputationTimeMs: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a diff between two text models.
|
||||
*/
|
||||
export interface IDocumentDiff {
|
||||
/**
|
||||
* If true, both text models are identical (byte-wise).
|
||||
*/
|
||||
readonly identical: boolean;
|
||||
|
||||
/**
|
||||
* If true, the diff computation timed out and the diff might not be accurate.
|
||||
*/
|
||||
readonly quitEarly: boolean;
|
||||
|
||||
/**
|
||||
* Maps all modified line ranges in the original to the corresponding line ranges in the modified text model.
|
||||
*/
|
||||
readonly changes: LineRangeMapping[];
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ export interface ILinesDiffComputer {
|
|||
}
|
||||
|
||||
export interface ILinesDiffComputerOptions {
|
||||
ignoreTrimWhitespace: boolean;
|
||||
maxComputationTime: number;
|
||||
readonly ignoreTrimWhitespace: boolean;
|
||||
readonly maxComputationTimeMs: number;
|
||||
}
|
||||
|
||||
export interface ILinesDiff {
|
||||
|
@ -19,58 +19,125 @@ export interface ILinesDiff {
|
|||
readonly changes: LineRangeMapping[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a line range in the original text model to a line range in the modified text model.
|
||||
*/
|
||||
export class LineRangeMapping {
|
||||
constructor(
|
||||
readonly originalRange: LineRange,
|
||||
readonly modifiedRange: LineRange,
|
||||
/**
|
||||
* Meaning of `undefined` unclear.
|
||||
*/
|
||||
readonly innerChanges: RangeMapping[] | undefined,
|
||||
) { }
|
||||
/**
|
||||
* The line range in the original text model.
|
||||
*/
|
||||
public readonly originalRange: LineRange;
|
||||
|
||||
toString(): string {
|
||||
return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`;
|
||||
/**
|
||||
* The line range in the modified text model.
|
||||
*/
|
||||
public readonly modifiedRange: LineRange;
|
||||
|
||||
/**
|
||||
* If inner changes have not been computed, this is set to undefined.
|
||||
* Otherwise, it represents the character-level diff in this line range.
|
||||
* The original range of each range mapping should be contained in the original line range (same for modified).
|
||||
* Must not be an empty array.
|
||||
*/
|
||||
public readonly innerChanges: RangeMapping[] | undefined;
|
||||
|
||||
constructor(
|
||||
originalRange: LineRange,
|
||||
modifiedRange: LineRange,
|
||||
innerChanges: RangeMapping[] | undefined,
|
||||
) {
|
||||
this.originalRange = originalRange;
|
||||
this.modifiedRange = modifiedRange;
|
||||
this.innerChanges = innerChanges;
|
||||
}
|
||||
}
|
||||
|
||||
export class RangeMapping {
|
||||
constructor(
|
||||
readonly originalRange: Range,
|
||||
readonly modifiedRange: Range,
|
||||
) { }
|
||||
|
||||
toString(): string {
|
||||
public toString(): string {
|
||||
return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-based.
|
||||
*/
|
||||
export class LineRange {
|
||||
constructor(public readonly startLineNumber: number, public readonly endLineNumberExclusive: number) { }
|
||||
* Maps a range in the original text model to a range in the modified text model.
|
||||
*/
|
||||
export class RangeMapping {
|
||||
/**
|
||||
* The original range.
|
||||
*/
|
||||
readonly originalRange: Range;
|
||||
|
||||
/**
|
||||
* The modified range.
|
||||
*/
|
||||
readonly modifiedRange: Range;
|
||||
|
||||
constructor(
|
||||
originalRange: Range,
|
||||
|
||||
modifiedRange: Range,
|
||||
) {
|
||||
this.originalRange = originalRange;
|
||||
this.modifiedRange = modifiedRange;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A range of lines (1-based).
|
||||
*/
|
||||
export class LineRange {
|
||||
/**
|
||||
* The start line number.
|
||||
*/
|
||||
public readonly startLineNumber: number;
|
||||
|
||||
/**
|
||||
* The end line number (exclusive).
|
||||
*/
|
||||
public readonly endLineNumberExclusive: number;
|
||||
|
||||
constructor(
|
||||
startLineNumber: number,
|
||||
endLineNumberExclusive: number,
|
||||
) {
|
||||
this.startLineNumber = startLineNumber;
|
||||
this.endLineNumberExclusive = endLineNumberExclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this line range is empty.
|
||||
*/
|
||||
get isEmpty(): boolean {
|
||||
return this.startLineNumber === this.endLineNumberExclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves this line range by the given offset of line numbers.
|
||||
*/
|
||||
public delta(offset: number): LineRange {
|
||||
return new LineRange(this.startLineNumber + offset, this.endLineNumberExclusive + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of lines this line range spans.
|
||||
*/
|
||||
public get length(): number {
|
||||
return this.endLineNumberExclusive - this.startLineNumber;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `[${this.startLineNumber},${this.endLineNumberExclusive})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a line range that combines this and the given line range.
|
||||
*/
|
||||
public join(other: LineRange): LineRange {
|
||||
return new LineRange(
|
||||
Math.min(this.startLineNumber, other.startLineNumber),
|
||||
Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive)
|
||||
);
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `[${this.startLineNumber},${this.endLineNumberExclusive})`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ const MINIMUM_MATCHING_CHARACTER_LENGTH = 3;
|
|||
export class SmartLinesDiffComputer implements ILinesDiffComputer {
|
||||
computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): ILinesDiff {
|
||||
const diffComputer = new DiffComputer(originalLines, modifiedLines, {
|
||||
maxComputationTime: options.maxComputationTime,
|
||||
maxComputationTime: options.maxComputationTimeMs,
|
||||
shouldIgnoreTrimWhitespace: options.ignoreTrimWhitespace,
|
||||
shouldComputeCharChanges: true,
|
||||
shouldMakePrettyDiff: true,
|
||||
|
|
|
@ -16,7 +16,7 @@ import { ensureValidWordDefinition, getWordAtText, IWordAtPosition } from 'vs/ed
|
|||
import { IInplaceReplaceSupportResult, ILink, TextEdit } from 'vs/editor/common/languages';
|
||||
import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/languages/linkComputer';
|
||||
import { BasicInplaceReplace } from 'vs/editor/common/languages/supports/inplaceReplaceSupport';
|
||||
import { IDiffComputationResult, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
|
||||
import { DiffAlgorithmName, IDiffComputationResult, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
|
||||
import { createMonacoBaseAPI } from 'vs/editor/common/services/editorBaseApi';
|
||||
import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
|
@ -384,18 +384,18 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
|||
|
||||
// ---- BEGIN diff --------------------------------------------------------------------------
|
||||
|
||||
public async computeDiff(originalUrl: string, modifiedUrl: string, options: IDocumentDiffProviderOptions): Promise<IDiffComputationResult | null> {
|
||||
public async computeDiff(originalUrl: string, modifiedUrl: string, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise<IDiffComputationResult | null> {
|
||||
const original = this._getModel(originalUrl);
|
||||
const modified = this._getModel(modifiedUrl);
|
||||
if (!original || !modified) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return EditorSimpleWorker.computeDiff(original, modified, options);
|
||||
return EditorSimpleWorker.computeDiff(original, modified, options, algorithm);
|
||||
}
|
||||
|
||||
private static computeDiff(originalTextModel: ICommonModel | ITextModel, modifiedTextModel: ICommonModel | ITextModel, options: IDocumentDiffProviderOptions): IDiffComputationResult {
|
||||
const diffAlgorithm: ILinesDiffComputer = options.diffAlgorithm === 'experimental' ? linesDiffComputers.experimental : linesDiffComputers.smart;
|
||||
private static computeDiff(originalTextModel: ICommonModel | ITextModel, modifiedTextModel: ICommonModel | ITextModel, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): IDiffComputationResult {
|
||||
const diffAlgorithm: ILinesDiffComputer = algorithm === 'experimental' ? linesDiffComputers.experimental : linesDiffComputers.smart;
|
||||
|
||||
const originalLines = originalTextModel.getLinesContent();
|
||||
const modifiedLines = modifiedTextModel.getLinesContent();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
|
||||
import { IInplaceReplaceSupportResult, TextEdit } from 'vs/editor/common/languages';
|
||||
import { UnicodeHighlighterOptions } from 'vs/editor/common/services/unicodeTextModelHighlighter';
|
||||
|
@ -14,6 +14,8 @@ import type { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleW
|
|||
|
||||
export const IEditorWorkerService = createDecorator<IEditorWorkerService>('editorWorkerService');
|
||||
|
||||
export type DiffAlgorithmName = 'smart' | 'experimental';
|
||||
|
||||
export interface IEditorWorkerService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
|
@ -21,7 +23,7 @@ export interface IEditorWorkerService {
|
|||
computedUnicodeHighlights(uri: URI, options: UnicodeHighlighterOptions, range?: IRange): Promise<IUnicodeHighlightsResult>;
|
||||
|
||||
/** Implementation in {@link EditorSimpleWorker.computeDiff} */
|
||||
computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions): Promise<IDiffComputationResult | null>;
|
||||
computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise<IDocumentDiff | null>;
|
||||
|
||||
canComputeDirtyDiff(original: URI, modified: URI): boolean;
|
||||
computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IChange[] | null>;
|
||||
|
|
|
@ -34,6 +34,7 @@ import { EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensi
|
|||
import { IMenuItem, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry';
|
||||
import { LineRange, LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
|
||||
/**
|
||||
* Create a new editor under `domElement`.
|
||||
|
@ -503,6 +504,9 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
|
|||
TextModelResolvedOptions: <any>TextModelResolvedOptions,
|
||||
FindMatch: <any>FindMatch,
|
||||
ApplyUpdateResult: <any>ApplyUpdateResult,
|
||||
LineRange: <any>LineRange,
|
||||
LineRangeMapping: <any>LineRangeMapping,
|
||||
RangeMapping: <any>RangeMapping,
|
||||
|
||||
// vars
|
||||
EditorType: EditorType,
|
||||
|
|
|
@ -76,7 +76,7 @@ suite('standardLinesDiffCompute', () => {
|
|||
}
|
||||
`.split('\n');
|
||||
|
||||
const diff = c.computeDiff(lines1, lines2, { maxComputationTime: 1000, ignoreTrimWhitespace: false });
|
||||
const diff = c.computeDiff(lines1, lines2, { maxComputationTimeMs: 1000, ignoreTrimWhitespace: false });
|
||||
|
||||
// TODO this diff should only have one inner, not two.
|
||||
assert.deepStrictEqual(
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IDiffComputationResult, IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
|
||||
import { DiffAlgorithmName, IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
|
||||
import { TextEdit, IInplaceReplaceSupportResult } from 'vs/editor/common/languages';
|
||||
import { IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
|
||||
|
||||
export class TestEditorWorkerService implements IEditorWorkerService {
|
||||
|
@ -16,7 +16,7 @@ export class TestEditorWorkerService implements IEditorWorkerService {
|
|||
|
||||
canComputeUnicodeHighlights(uri: URI): boolean { return false; }
|
||||
async computedUnicodeHighlights(uri: URI): Promise<IUnicodeHighlightsResult> { return { ranges: [], hasMore: false, ambiguousCharacterCount: 0, invisibleCharacterCount: 0, nonBasicAsciiCharacterCount: 0 }; }
|
||||
async computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions): Promise<IDiffComputationResult | null> { return null; }
|
||||
async computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise<IDocumentDiff | null> { return null; }
|
||||
canComputeDirtyDiff(original: URI, modified: URI): boolean { return false; }
|
||||
async computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IChange[] | null> { return null; }
|
||||
async computeMoreMinimalEdits(resource: URI, edits: TextEdit[] | null | undefined): Promise<TextEdit[] | undefined> { return undefined; }
|
||||
|
|
120
src/vs/monaco.d.ts
vendored
120
src/vs/monaco.d.ts
vendored
|
@ -2235,6 +2235,124 @@ declare namespace monaco.editor {
|
|||
export interface ILineChange extends IChange {
|
||||
readonly charChanges: ICharChange[] | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A document diff provider computes the diff between two text models.
|
||||
*/
|
||||
export interface IDocumentDiffProvider {
|
||||
/**
|
||||
* Computes the diff between the text models `original` and `modified`.
|
||||
*/
|
||||
computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions): Promise<IDocumentDiff>;
|
||||
/**
|
||||
* Is fired when settings of the diff algorithm change that could alter the result of the diffing computation.
|
||||
* Any user of this provider should recompute the diff when this event is fired.
|
||||
*/
|
||||
onDidChange: IEvent<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the diff computation.
|
||||
*/
|
||||
export interface IDocumentDiffProviderOptions {
|
||||
/**
|
||||
* When set to true, the diff should ignore whitespace changes.i
|
||||
*/
|
||||
ignoreTrimWhitespace: boolean;
|
||||
/**
|
||||
* A diff computation should throw if it takes longer than this value.
|
||||
*/
|
||||
maxComputationTimeMs: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a diff between two text models.
|
||||
*/
|
||||
export interface IDocumentDiff {
|
||||
/**
|
||||
* If true, both text models are identical (byte-wise).
|
||||
*/
|
||||
readonly identical: boolean;
|
||||
/**
|
||||
* If true, the diff computation timed out and the diff might not be accurate.
|
||||
*/
|
||||
readonly quitEarly: boolean;
|
||||
/**
|
||||
* Maps all modified line ranges in the original to the corresponding line ranges in the modified text model.
|
||||
*/
|
||||
readonly changes: LineRangeMapping[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a line range in the original text model to a line range in the modified text model.
|
||||
*/
|
||||
export class LineRangeMapping {
|
||||
/**
|
||||
* The line range in the original text model.
|
||||
*/
|
||||
readonly originalRange: LineRange;
|
||||
/**
|
||||
* The line range in the modified text model.
|
||||
*/
|
||||
readonly modifiedRange: LineRange;
|
||||
/**
|
||||
* If inner changes have not been computed, this is set to undefined.
|
||||
* Otherwise, it represents the character-level diff in this line range.
|
||||
* The original range of each range mapping should be contained in the original line range (same for modified).
|
||||
* Must not be an empty array.
|
||||
*/
|
||||
readonly innerChanges: RangeMapping[] | undefined;
|
||||
constructor(originalRange: LineRange, modifiedRange: LineRange, innerChanges: RangeMapping[] | undefined);
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A range of lines (1-based).
|
||||
*/
|
||||
export class LineRange {
|
||||
/**
|
||||
* The start line number.
|
||||
*/
|
||||
readonly startLineNumber: number;
|
||||
/**
|
||||
* The end line number (exclusive).
|
||||
*/
|
||||
readonly endLineNumberExclusive: number;
|
||||
constructor(startLineNumber: number, endLineNumberExclusive: number);
|
||||
/**
|
||||
* Indicates if this line range is empty.
|
||||
*/
|
||||
get isEmpty(): boolean;
|
||||
/**
|
||||
* Moves this line range by the given offset of line numbers.
|
||||
*/
|
||||
delta(offset: number): LineRange;
|
||||
/**
|
||||
* The number of lines this line range spans.
|
||||
*/
|
||||
get length(): number;
|
||||
/**
|
||||
* Creates a line range that combines this and the given line range.
|
||||
*/
|
||||
join(other: LineRange): LineRange;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a range in the original text model to a range in the modified text model.
|
||||
*/
|
||||
export class RangeMapping {
|
||||
/**
|
||||
* The original range.
|
||||
*/
|
||||
readonly originalRange: Range;
|
||||
/**
|
||||
* The modified range.
|
||||
*/
|
||||
readonly modifiedRange: Range;
|
||||
constructor(originalRange: Range, modifiedRange: Range);
|
||||
toString(): string;
|
||||
}
|
||||
export interface IDimension {
|
||||
width: number;
|
||||
height: number;
|
||||
|
@ -3574,7 +3692,7 @@ declare namespace monaco.editor {
|
|||
/**
|
||||
* Diff Algorithm
|
||||
*/
|
||||
diffAlgorithm?: 'smart' | 'experimental';
|
||||
diffAlgorithm?: 'smart' | 'experimental' | IDocumentDiffProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,7 +11,6 @@ import { derived, IObservable, observableFromEvent, observableValue } from 'vs/b
|
|||
import { basename, isEqual } from 'vs/base/common/resources';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { localize } from 'vs/nls';
|
||||
|
@ -103,15 +102,14 @@ export class TempFileMergeEditorModeFactory implements IMergeEditorInputModelFac
|
|||
);
|
||||
store.add(temporaryResultModel);
|
||||
|
||||
const diffProvider = this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider);
|
||||
const mergeDiffComputer = this._instantiationService.createInstance(MergeDiffComputer);
|
||||
const model = this._instantiationService.createInstance(
|
||||
MergeEditorModel,
|
||||
base.object.textEditorModel,
|
||||
input1Data,
|
||||
input2Data,
|
||||
temporaryResultModel,
|
||||
this._instantiationService.createInstance(MergeDiffComputer, diffProvider),
|
||||
this._instantiationService.createInstance(MergeDiffComputer, diffProvider),
|
||||
mergeDiffComputer,
|
||||
{
|
||||
resetResult: true,
|
||||
},
|
||||
|
@ -314,15 +312,15 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa
|
|||
const hasConflictMarkers = lines.some(l => l.startsWith(conflictMarkers.start));
|
||||
const resetResult = hasConflictMarkers;
|
||||
|
||||
const diffProvider = this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider);
|
||||
const mergeDiffComputer = this._instantiationService.createInstance(MergeDiffComputer);
|
||||
|
||||
const model = this._instantiationService.createInstance(
|
||||
MergeEditorModel,
|
||||
base.object.textEditorModel,
|
||||
input1Data,
|
||||
input2Data,
|
||||
result.object.textEditorModel,
|
||||
this._instantiationService.createInstance(MergeDiffComputer, diffProvider),
|
||||
this._instantiationService.createInstance(MergeDiffComputer, diffProvider),
|
||||
mergeDiffComputer,
|
||||
{
|
||||
resetResult
|
||||
},
|
||||
|
|
|
@ -7,9 +7,9 @@ import { assertFn, checkAdjacentItems } from 'vs/base/common/assert';
|
|||
import { IReader, observableFromEvent } from 'vs/base/common/observable';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { LineRange as DiffLineRange, RangeMapping as DiffRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange';
|
||||
import { DetailedLineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping';
|
||||
|
@ -23,30 +23,33 @@ export interface IMergeDiffComputerResult {
|
|||
}
|
||||
|
||||
export class MergeDiffComputer implements IMergeDiffComputer {
|
||||
|
||||
private readonly mergeAlgorithm = observableFromEvent(
|
||||
this.configurationService.onDidChangeConfiguration,
|
||||
() => /** @description config: mergeAlgorithm.diffAlgorithm */ this.configurationService.getValue<'smart' | 'experimental'>('mergeEditor.diffAlgorithm')
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly documentDiffProvider: IDocumentDiffProvider,
|
||||
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
) {
|
||||
}
|
||||
|
||||
async computeDiff(textModel1: ITextModel, textModel2: ITextModel, reader: IReader): Promise<IMergeDiffComputerResult> {
|
||||
const diffAlgorithm = this.mergeAlgorithm.read(reader);
|
||||
const result = await this.documentDiffProvider.computeDiff(
|
||||
textModel1,
|
||||
textModel2,
|
||||
const result = await this.editorWorkerService.computeDiff(
|
||||
textModel1.uri,
|
||||
textModel2.uri,
|
||||
{
|
||||
ignoreTrimWhitespace: false,
|
||||
maxComputationTime: 0,
|
||||
diffAlgorithm,
|
||||
}
|
||||
maxComputationTimeMs: 0,
|
||||
},
|
||||
diffAlgorithm,
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
throw new Error('Diff computation failed');
|
||||
}
|
||||
|
||||
if (textModel1.isDisposed() || textModel2.isDisposed()) {
|
||||
return { diffs: null };
|
||||
}
|
||||
|
@ -76,15 +79,15 @@ export class MergeDiffComputer implements IMergeDiffComputer {
|
|||
}
|
||||
}
|
||||
|
||||
function toLineRange(range: DiffLineRange): LineRange {
|
||||
export function toLineRange(range: DiffLineRange): LineRange {
|
||||
return new LineRange(range.startLineNumber, range.length);
|
||||
}
|
||||
|
||||
function toRangeMapping(mapping: DiffRangeMapping): RangeMapping {
|
||||
export function toRangeMapping(mapping: DiffRangeMapping): RangeMapping {
|
||||
return new RangeMapping(mapping.originalRange, mapping.modifiedRange);
|
||||
}
|
||||
|
||||
function normalizeRangeMapping(rangeMapping: RangeMapping, inputTextModel: ITextModel, outputTextModel: ITextModel): RangeMapping | undefined {
|
||||
export function normalizeRangeMapping(rangeMapping: RangeMapping, inputTextModel: ITextModel, outputTextModel: ITextModel): RangeMapping | undefined {
|
||||
const inputRangeEmpty = rangeMapping.inputRange.isEmpty();
|
||||
const outputRangeEmpty = rangeMapping.outputRange.isEmpty();
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export interface InputData {
|
|||
export class MergeEditorModel extends EditorModel {
|
||||
private readonly input1TextModelDiffs = this._register(new TextModelDiffs(this.base, this.input1.textModel, this.diffComputer));
|
||||
private readonly input2TextModelDiffs = this._register(new TextModelDiffs(this.base, this.input2.textModel, this.diffComputer));
|
||||
private readonly resultTextModelDiffs = this._register(new TextModelDiffs(this.base, this.resultTextModel, this.diffComputerConflictProjection));
|
||||
private readonly resultTextModelDiffs = this._register(new TextModelDiffs(this.base, this.resultTextModel, this.diffComputer));
|
||||
public readonly modifiedBaseRanges = derived<ModifiedBaseRange[]>('modifiedBaseRanges', (reader) => {
|
||||
const input1Diffs = this.input1TextModelDiffs.diffs.read(reader);
|
||||
const input2Diffs = this.input2TextModelDiffs.diffs.read(reader);
|
||||
|
@ -54,7 +54,6 @@ export class MergeEditorModel extends EditorModel {
|
|||
readonly input2: InputData,
|
||||
readonly resultTextModel: ITextModel,
|
||||
private readonly diffComputer: IMergeDiffComputer,
|
||||
private readonly diffComputerConflictProjection: IMergeDiffComputer,
|
||||
private readonly options: { resetResult: boolean },
|
||||
public readonly telemetry: MergeEditorTelemetry,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IDocumentDiff, IDocumentDiffProvider, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider';
|
||||
import { LineRange, LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { TextModelProjection } from 'vs/workbench/contrib/mergeEditor/browser/model/textModelProjection';
|
||||
|
||||
export class ProjectedDiffComputer implements IDocumentDiffProvider {
|
||||
private readonly projectedTextModel = new Map<ITextModel, TextModelProjection>();
|
||||
|
||||
constructor(
|
||||
private readonly underlyingDiffComputer: IDocumentDiffProvider,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
async computeDiff(
|
||||
textModel1: ITextModel,
|
||||
textModel2: ITextModel,
|
||||
options: IDocumentDiffProviderOptions
|
||||
): Promise<IDocumentDiff> {
|
||||
let proj = this.projectedTextModel.get(textModel2);
|
||||
if (!proj) {
|
||||
proj = TextModelProjection.create(textModel2, {
|
||||
blockToRemoveStartLinePrefix: '<<<<<<<',
|
||||
blockToRemoveEndLinePrefix: '>>>>>>>',
|
||||
}, this.modelService);
|
||||
this.projectedTextModel.set(textModel2, proj);
|
||||
}
|
||||
|
||||
const result = await this.underlyingDiffComputer.computeDiff(textModel1, proj.targetDocument, options);
|
||||
|
||||
const transformer = proj.createMonotonousReverseTransformer();
|
||||
|
||||
return {
|
||||
identical: result.identical,
|
||||
quitEarly: result.quitEarly,
|
||||
|
||||
changes: result.changes.map(d => {
|
||||
const start = transformer.transform(new Position(d.modifiedRange.startLineNumber, 1)).lineNumber;
|
||||
|
||||
const innerChanges = d.innerChanges?.map(m => {
|
||||
const start = transformer.transform(m.modifiedRange.getStartPosition());
|
||||
const end = transformer.transform(m.modifiedRange.getEndPosition());
|
||||
return new RangeMapping(m.originalRange, Range.fromPositions(start, end));
|
||||
});
|
||||
|
||||
const end = transformer.transform(new Position(d.modifiedRange.endLineNumberExclusive, 1)).lineNumber;
|
||||
|
||||
return new LineRangeMapping(
|
||||
d.originalRange,
|
||||
new LineRange(start, end),
|
||||
innerChanges
|
||||
);
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { transaction } from 'vs/base/common/observable';
|
||||
import { IReader, transaction } from 'vs/base/common/observable';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { linesDiffComputers } from 'vs/editor/common/diff/linesDiffComputers';
|
||||
|
@ -13,7 +14,8 @@ import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model';
|
|||
import { createModelServices, createTextModel } from 'vs/editor/test/common/testTextModel';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { MergeDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer';
|
||||
import { IMergeDiffComputer, IMergeDiffComputerResult, normalizeRangeMapping, toLineRange, toRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer';
|
||||
import { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping';
|
||||
import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel';
|
||||
import { MergeEditorTelemetry } from 'vs/workbench/contrib/mergeEditor/browser/telemetry';
|
||||
|
||||
|
@ -260,23 +262,27 @@ class MergeModelInterface extends Disposable {
|
|||
const baseTextModel = this._register(createTextModel(options.base, options.languageId));
|
||||
const resultTextModel = this._register(createTextModel(options.result, options.languageId));
|
||||
|
||||
const diffComputer = instantiationService.createInstance(MergeDiffComputer,
|
||||
{
|
||||
// Don't go through the webworker to improve unit test performance & reduce dependencies
|
||||
async computeDiff(textModel1, textModel2) {
|
||||
const result = linesDiffComputers.smart.computeDiff(
|
||||
textModel1.getLinesContent(),
|
||||
textModel2.getLinesContent(),
|
||||
{ ignoreTrimWhitespace: false, maxComputationTime: 10000 }
|
||||
);
|
||||
return {
|
||||
changes: result.changes,
|
||||
quitEarly: result.quitEarly,
|
||||
identical: result.changes.length === 0
|
||||
};
|
||||
},
|
||||
const diffComputer: IMergeDiffComputer = {
|
||||
async computeDiff(textModel1: ITextModel, textModel2: ITextModel, reader: IReader): Promise<IMergeDiffComputerResult> {
|
||||
const result = await linesDiffComputers.smart.computeDiff(
|
||||
textModel1.getLinesContent(),
|
||||
textModel2.getLinesContent(),
|
||||
{ ignoreTrimWhitespace: false, maxComputationTimeMs: 10000 }
|
||||
);
|
||||
const changes = result.changes.map(c =>
|
||||
new DetailedLineRangeMapping(
|
||||
toLineRange(c.originalRange),
|
||||
textModel1,
|
||||
toLineRange(c.modifiedRange),
|
||||
textModel2,
|
||||
c.innerChanges?.map(ic => normalizeRangeMapping(toRangeMapping(ic), textModel1, textModel2)).filter(isDefined)
|
||||
)
|
||||
);
|
||||
return {
|
||||
diffs: changes
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.mergeModel = this._register(instantiationService.createInstance(MergeEditorModel,
|
||||
baseTextModel,
|
||||
|
@ -294,7 +300,6 @@ class MergeModelInterface extends Disposable {
|
|||
},
|
||||
resultTextModel,
|
||||
diffComputer,
|
||||
diffComputer,
|
||||
{
|
||||
resetResult: false
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue