var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { h } from '../../../../base/browser/dom.js';
import { onUnexpectedError } from '../../../../base/common/errors.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { derived, keepAlive, observableValue } from '../../../../base/common/observable.js';
import './style.css';
import { EditorExtensionsRegistry } from '../../editorExtensions.js';
import { ICodeEditorService } from '../../services/codeEditorService.js';
import { CodeEditorWidget } from '../codeEditorWidget.js';
import { diffAddDecoration, diffDeleteDecoration, diffFullLineAddDecoration, diffFullLineDeleteDecoration } from './decorations.js';
import { DiffEditorSash } from './diffEditorSash.js';
import { ViewZoneAlignment } from './lineAlignment.js';
import { OverviewRulerPart } from './overviewRulerPart.js';
import { UnchangedRangesFeature } from './unchangedRanges.js';
import { ObservableElementSizeObserver, applyObservableDecorations } from './utils.js';
import { WorkerBasedDocumentDiffProvider } from '../workerBasedDocumentDiffProvider.js';
import { EditorOptions, clampedFloat, clampedInt, boolean as validateBooleanOption, stringSet as validateStringSetOption } from '../../../common/config/editorOptions.js';
import { EditorType } from '../../../common/editorCommon.js';
import { localize } from '../../../../nls.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
import { DelegatingEditor } from './delegatingEditorImpl.js';
import { DiffModel } from './diffModel.js';
const diffEditorDefaultOptions = {
    enableSplitViewResizing: true,
    splitViewDefaultRatio: 0.5,
    renderSideBySide: true,
    renderMarginRevertIcon: true,
    maxComputationTime: 5000,
    maxFileSize: 50,
    ignoreTrimWhitespace: true,
    renderIndicators: true,
    originalEditable: false,
    diffCodeLens: false,
    renderOverviewRuler: true,
    diffWordWrap: 'inherit',
    diffAlgorithm: 'advanced',
    accessibilityVerbose: false,
    experimental: {
        collapseUnchangedRegions: false,
    }
};
export let DiffEditorWidget2 = class DiffEditorWidget2 extends DelegatingEditor {
    constructor(_domElement, options, codeEditorWidgetOptions, _parentContextKeyService, _parentInstantiationService, codeEditorService) {
        var _a;
        super();
        this._domElement = _domElement;
        this._parentContextKeyService = _parentContextKeyService;
        this._parentInstantiationService = _parentInstantiationService;
        this.elements = h('div.monaco-diff-editor.side-by-side', { style: { position: 'relative', height: '100%' } }, [
            h('div.editor.original@original', { style: { position: 'absolute', height: '100%' } }),
            h('div.editor.modified@modified', { style: { position: 'absolute', height: '100%' } }),
        ]);
        this._model = observableValue('diffEditorModel', null);
        this.onDidChangeModel = Event.fromObservableLight(this._model);
        this._diffModel = observableValue('diffModel', null);
        this._onDidContentSizeChange = this._register(new Emitter());
        this._contextKeyService = this._register(this._parentContextKeyService.createScoped(this._domElement));
        this._instantiationService = this._parentInstantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService]));
        this._isHandlingScrollEvent = false;
        this._layoutInfo = derived('modifiedEditorLayoutInfo', (reader) => {
            const width = this._rootSizeObserver.width.read(reader);
            const height = this._rootSizeObserver.height.read(reader);
            const sashLeft = this._sash.sashLeft.read(reader);
            this.elements.original.style.width = sashLeft + 'px';
            this.elements.original.style.left = '0px';
            this.elements.modified.style.width = (width - sashLeft) + 'px';
            this.elements.modified.style.left = sashLeft + 'px';
            this._originalEditor.layout({ width: sashLeft, height: height });
            this._modifiedEditor.layout({
                width: width - sashLeft -
                    (this._renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0),
                height
            });
            return { modifiedEditor: this._modifiedEditor.getLayoutInfo() };
        });
        this._decorations = derived('decorations', (reader) => {
            var _a;
            const diff = (_a = this._diffModel.read(reader)) === null || _a === void 0 ? void 0 : _a.diff.read(reader);
            if (!diff) {
                return null;
            }
            const originalDecorations = [];
            const modifiedDecorations = [];
            for (const c of diff.changes) {
                const fullRangeOriginal = c.originalRange.toInclusiveRange();
                if (fullRangeOriginal) {
                    originalDecorations.push({ range: fullRangeOriginal, options: diffFullLineDeleteDecoration });
                }
                const fullRangeModified = c.modifiedRange.toInclusiveRange();
                if (fullRangeModified) {
                    modifiedDecorations.push({ range: fullRangeModified, options: diffFullLineAddDecoration });
                }
                for (const i of c.innerChanges || []) {
                    originalDecorations.push({ range: i.originalRange, options: diffDeleteDecoration });
                    modifiedDecorations.push({ range: i.modifiedRange, options: diffAddDecoration });
                }
            }
            return { originalDecorations, modifiedDecorations };
        });
        this.onDidUpdateDiff = e => {
            return { dispose: () => { } };
        };
        codeEditorService.willCreateDiffEditor();
        this._contextKeyService.createKey('isInDiffEditor', true);
        this._contextKeyService.createKey('diffEditorVersion', 2);
        this._contextKeyService.createKey('isInEmbeddedDiffEditor', typeof options.isInEmbeddedEditor !== 'undefined' ? options.isInEmbeddedEditor : false);
        this._options = observableValue('options', validateDiffEditorOptions(options || {}, diffEditorDefaultOptions));
        this._domElement.appendChild(this.elements.root);
        this._rootSizeObserver = this._register(new ObservableElementSizeObserver(this.elements.root, options.dimension));
        this._rootSizeObserver.setAutomaticLayout((_a = options.automaticLayout) !== null && _a !== void 0 ? _a : false);
        this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {});
        this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {});
        this._register(applyObservableDecorations(this._originalEditor, this._decorations.map(d => (d === null || d === void 0 ? void 0 : d.originalDecorations) || [])));
        this._register(applyObservableDecorations(this._modifiedEditor, this._decorations.map(d => (d === null || d === void 0 ? void 0 : d.modifiedDecorations) || [])));
        this._renderOverviewRuler = this._options.map(o => o.renderOverviewRuler);
        this._sash = this._register(new DiffEditorSash(this._options.map(o => o.enableSplitViewResizing), this._options.map(o => o.splitViewDefaultRatio), this.elements.root, {
            height: this._rootSizeObserver.height,
            width: this._rootSizeObserver.width.map((w, reader) => w - (this._renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)),
        }));
        this._register(new UnchangedRangesFeature(this._originalEditor, this._modifiedEditor, this._diffModel));
        this._register(new ViewZoneAlignment(this._originalEditor, this._modifiedEditor, this._diffModel));
        this._register(this._instantiationService.createInstance(OverviewRulerPart, this._originalEditor, this._modifiedEditor, this.elements.root, this._diffModel, this._rootSizeObserver.width, this._rootSizeObserver.height, this._layoutInfo.map(i => i.modifiedEditor), this._renderOverviewRuler));
        this._createDiffEditorContributions();
        codeEditorService.addDiffEditor(this);
        this._register(keepAlive(this._layoutInfo, true));
    }
    _createDiffEditorContributions() {
        const contributions = EditorExtensionsRegistry.getDiffEditorContributions();
        for (const desc of contributions) {
            try {
                this._register(this._instantiationService.createInstance(desc.ctor, this));
            }
            catch (err) {
                onUnexpectedError(err);
            }
        }
    }
    _createLeftHandSideEditor(options, codeEditorWidgetOptions) {
        const editor = this._createInnerEditor(this._instantiationService, this.elements.original, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions);
        const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus());
        this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true)));
        this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false)));
        return editor;
    }
    _createRightHandSideEditor(options, codeEditorWidgetOptions) {
        const editor = this._createInnerEditor(this._instantiationService, this.elements.modified, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions);
        const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus());
        this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true)));
        this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false)));
        // Revert change when an arrow is clicked.
        /*TODO
        this._register(editor.onMouseDown(event => {
            if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) {
                const lineNumber = event.target.position.lineNumber;
                const viewZone = event.target as editorBrowser.IMouseTargetViewZone | undefined;
                const change = this._diffComputationResult?.changes.find(c =>
                    // delete change
                    viewZone?.detail.afterLineNumber === c.modifiedStartLineNumber ||
                    // other changes
                    (c.modifiedEndLineNumber > 0 && c.modifiedStartLineNumber === lineNumber));
                if (change) {
                    this.revertChange(change);
                }
                event.event.stopPropagation();
                this._updateDecorations();
                return;
            }
        }));*/
        return editor;
    }
    _createInnerEditor(instantiationService, container, options, editorWidgetOptions) {
        const editor = instantiationService.createInstance(CodeEditorWidget, container, options, editorWidgetOptions);
        this._register(editor.onDidContentSizeChange(e => {
            const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH;
            const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight());
            this._onDidContentSizeChange.fire({
                contentHeight: height,
                contentWidth: width,
                contentHeightChanged: e.contentHeightChanged,
                contentWidthChanged: e.contentWidthChanged
            });
        }));
        this._register(editor.onDidScrollChange((e) => {
            if (this._isHandlingScrollEvent) {
                return;
            }
            if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) {
                return;
            }
            this._isHandlingScrollEvent = true;
            try {
                const otherEditor = editor === this._originalEditor ? this._modifiedEditor : this._originalEditor;
                otherEditor.setScrollPosition({
                    scrollLeft: e.scrollLeft,
                    scrollTop: e.scrollTop
                });
            }
            finally {
                this._isHandlingScrollEvent = false;
            }
        }));
        return editor;
    }
    _adjustOptionsForLeftHandSide(options) {
        const result = this._adjustOptionsForSubEditor(options);
        if (!options.renderSideBySide) {
            // never wrap hidden editor
            result.wordWrapOverride1 = 'off';
            result.wordWrapOverride2 = 'off';
            result.stickyScroll = { enabled: false };
        }
        else {
            result.wordWrapOverride1 = this._options.get().diffWordWrap;
        }
        if (options.originalAriaLabel) {
            result.ariaLabel = options.originalAriaLabel;
        }
        result.ariaLabel = this._updateAriaLabel(result.ariaLabel);
        result.readOnly = !options.originalEditable;
        result.dropIntoEditor = { enabled: !result.readOnly };
        result.extraEditorClassName = 'original-in-monaco-diff-editor';
        return result;
    }
    _adjustOptionsForRightHandSide(options) {
        const result = this._adjustOptionsForSubEditor(options);
        if (options.modifiedAriaLabel) {
            result.ariaLabel = options.modifiedAriaLabel;
        }
        result.ariaLabel = this._updateAriaLabel(result.ariaLabel);
        result.wordWrapOverride1 = this._options.get().diffWordWrap;
        result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH;
        result.scrollbar.verticalHasArrows = false;
        result.extraEditorClassName = 'modified-in-monaco-diff-editor';
        return result;
    }
    _adjustOptionsForSubEditor(options) {
        const clonedOptions = Object.assign(Object.assign({}, options), { dimension: {
                height: 0,
                width: 0
            } });
        clonedOptions.inDiffEditor = true;
        clonedOptions.automaticLayout = false;
        // Clone scrollbar options before changing them
        clonedOptions.scrollbar = Object.assign({}, (clonedOptions.scrollbar || {}));
        clonedOptions.scrollbar.vertical = 'visible';
        clonedOptions.folding = false;
        clonedOptions.codeLens = this._options.get().diffCodeLens;
        clonedOptions.fixedOverflowWidgets = true;
        // clonedOptions.lineDecorationsWidth = '2ch';
        // Clone minimap options before changing them
        clonedOptions.minimap = Object.assign({}, (clonedOptions.minimap || {}));
        clonedOptions.minimap.enabled = false;
        return clonedOptions;
    }
    _updateAriaLabel(ariaLabel) {
        const ariaNavigationTip = localize('diff-aria-navigation-tip', ' use Shift + F7 to navigate changes');
        if (this._options.get().accessibilityVerbose) {
            return ariaLabel + ariaNavigationTip;
        }
        else if (ariaLabel) {
            return ariaLabel.replaceAll(ariaNavigationTip, '');
        }
        return undefined;
    }
    get _targetEditor() { return this._modifiedEditor; }
    getEditorType() { return EditorType.IDiffEditor; }
    layout(dimension) {
        this._rootSizeObserver.observe(dimension);
    }
    hasTextFocus() {
        return this._originalEditor.hasTextFocus() || this._modifiedEditor.hasTextFocus();
    }
    saveViewState() {
        return null;
        //throw new Error('Method not implemented.');
    }
    restoreViewState(state) {
        //throw new Error('Method not implemented.');
    }
    getModel() { return this._model.get(); }
    setModel(model) {
        this._originalEditor.setModel(model ? model.original : null);
        this._modifiedEditor.setModel(model ? model.modified : null);
        this._model.set(model, undefined);
        this._diffModel.set(model ? new DiffModel(model, this._options.map(o => o.ignoreTrimWhitespace), this._options.map(o => o.maxComputationTime), this._options.map(o => o.experimental.collapseUnchangedRegions), this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider, this._options.get())) : null, undefined);
    }
    updateOptions(_newOptions) {
        const newOptions = validateDiffEditorOptions(_newOptions, this._options.get());
        this._options.set(newOptions, undefined);
        this._modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(_newOptions));
        this._originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(_newOptions));
    }
    getContainerDomNode() { return this._domElement; }
    getOriginalEditor() { return this._originalEditor; }
    getModifiedEditor() { return this._modifiedEditor; }
    getLineChanges() {
        return null;
        //throw new Error('Method not implemented.');
    }
    getDiffLineInformationForOriginal(lineNumber) {
        return null;
        //throw new Error('Method not implemented.');
    }
    getDiffLineInformationForModified(lineNumber) {
        return null;
        //throw new Error('Method not implemented.');
    }
};
DiffEditorWidget2 = __decorate([
    __param(3, IContextKeyService),
    __param(4, IInstantiationService),
    __param(5, ICodeEditorService)
], DiffEditorWidget2);
function validateDiffEditorOptions(options, defaults) {
    var _a;
    return {
        enableSplitViewResizing: validateBooleanOption(options.enableSplitViewResizing, defaults.enableSplitViewResizing),
        splitViewDefaultRatio: clampedFloat(options.splitViewDefaultRatio, 0.5, 0.1, 0.9),
        renderSideBySide: validateBooleanOption(options.renderSideBySide, defaults.renderSideBySide),
        renderMarginRevertIcon: validateBooleanOption(options.renderMarginRevertIcon, defaults.renderMarginRevertIcon),
        maxComputationTime: clampedInt(options.maxComputationTime, defaults.maxComputationTime, 0, 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */),
        maxFileSize: clampedInt(options.maxFileSize, defaults.maxFileSize, 0, 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */),
        ignoreTrimWhitespace: validateBooleanOption(options.ignoreTrimWhitespace, defaults.ignoreTrimWhitespace),
        renderIndicators: validateBooleanOption(options.renderIndicators, defaults.renderIndicators),
        originalEditable: validateBooleanOption(options.originalEditable, defaults.originalEditable),
        diffCodeLens: validateBooleanOption(options.diffCodeLens, defaults.diffCodeLens),
        renderOverviewRuler: validateBooleanOption(options.renderOverviewRuler, defaults.renderOverviewRuler),
        diffWordWrap: validateStringSetOption(options.diffWordWrap, defaults.diffWordWrap, ['off', 'on', 'inherit']),
        diffAlgorithm: validateStringSetOption(options.diffAlgorithm, defaults.diffAlgorithm, ['legacy', 'advanced'], { 'smart': 'legacy', 'experimental': 'advanced' }),
        accessibilityVerbose: validateBooleanOption(options.accessibilityVerbose, defaults.accessibilityVerbose),
        experimental: {
            collapseUnchangedRegions: validateBooleanOption((_a = options.experimental) === null || _a === void 0 ? void 0 : _a.collapseUnchangedRegions, defaults.experimental.collapseUnchangedRegions),
        },
    };
}
