/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
import * as nls from '../../../nls';
import { Disposable, dispose } from '../../../base/common/lifecycle';
import { KeyChord } from '../../../base/common/keyCodes';
import { RunOnceScheduler } from '../../../base/common/async';
import { TrackedRangeStickiness, OverviewRulerLane } from '../../common/model';
import { EditorContextKeys } from '../../common/editorContextKeys';
import { registerEditorAction, registerEditorContribution, EditorAction } from '../../browser/editorExtensions';
import { Range } from '../../common/core/range';
import { Selection } from '../../common/core/selection';
import { CursorChangeReason } from '../../common/controller/cursorEvents';
import { CursorMoveCommands } from '../../common/controller/cursorMoveCommands';
import { DocumentHighlightProviderRegistry } from '../../common/modes';
import { CommonFindController } from '../find/findController';
import { ModelDecorationOptions } from '../../common/model/textModel';
import { overviewRulerSelectionHighlightForeground } from '../../../platform/theme/common/colorRegistry';
import { themeColorFromId } from '../../../platform/theme/common/themeService';
var InsertCursorAbove = /** @class */ (function (_super) {
    __extends(InsertCursorAbove, _super);
    function InsertCursorAbove() {
        return _super.call(this, {
            id: 'editor.action.insertCursorAbove',
            label: nls.localize('mutlicursor.insertAbove', "Add Cursor Above"),
            alias: 'Add Cursor Above',
            precondition: null,
            kbOpts: {
                kbExpr: EditorContextKeys.textFocus,
                primary: 2048 /* CtrlCmd */ | 512 /* Alt */ | 16 /* UpArrow */,
                linux: {
                    primary: 1024 /* Shift */ | 512 /* Alt */ | 16 /* UpArrow */,
                    secondary: [2048 /* CtrlCmd */ | 1024 /* Shift */ | 16 /* UpArrow */]
                }
            }
        }) || this;
    }
    InsertCursorAbove.prototype.run = function (accessor, editor, args) {
        var cursors = editor._getCursors();
        var context = cursors.context;
        if (context.config.readOnly) {
            return;
        }
        context.model.pushStackElement();
        cursors.setStates(args.source, CursorChangeReason.Explicit, CursorMoveCommands.addCursorUp(context, cursors.getAll()));
        cursors.reveal(true, 1 /* TopMost */, 0 /* Smooth */);
    };
    return InsertCursorAbove;
}(EditorAction));
export { InsertCursorAbove };
var InsertCursorBelow = /** @class */ (function (_super) {
    __extends(InsertCursorBelow, _super);
    function InsertCursorBelow() {
        return _super.call(this, {
            id: 'editor.action.insertCursorBelow',
            label: nls.localize('mutlicursor.insertBelow', "Add Cursor Below"),
            alias: 'Add Cursor Below',
            precondition: null,
            kbOpts: {
                kbExpr: EditorContextKeys.textFocus,
                primary: 2048 /* CtrlCmd */ | 512 /* Alt */ | 18 /* DownArrow */,
                linux: {
                    primary: 1024 /* Shift */ | 512 /* Alt */ | 18 /* DownArrow */,
                    secondary: [2048 /* CtrlCmd */ | 1024 /* Shift */ | 18 /* DownArrow */]
                }
            }
        }) || this;
    }
    InsertCursorBelow.prototype.run = function (accessor, editor, args) {
        var cursors = editor._getCursors();
        var context = cursors.context;
        if (context.config.readOnly) {
            return;
        }
        context.model.pushStackElement();
        cursors.setStates(args.source, CursorChangeReason.Explicit, CursorMoveCommands.addCursorDown(context, cursors.getAll()));
        cursors.reveal(true, 2 /* BottomMost */, 0 /* Smooth */);
    };
    return InsertCursorBelow;
}(EditorAction));
export { InsertCursorBelow };
var InsertCursorAtEndOfEachLineSelected = /** @class */ (function (_super) {
    __extends(InsertCursorAtEndOfEachLineSelected, _super);
    function InsertCursorAtEndOfEachLineSelected() {
        return _super.call(this, {
            id: 'editor.action.insertCursorAtEndOfEachLineSelected',
            label: nls.localize('mutlicursor.insertAtEndOfEachLineSelected', "Add Cursors to Line Ends"),
            alias: 'Add Cursors to Line Ends',
            precondition: null,
            kbOpts: {
                kbExpr: EditorContextKeys.textFocus,
                primary: 1024 /* Shift */ | 512 /* Alt */ | 39 /* KEY_I */
            }
        }) || this;
    }
    InsertCursorAtEndOfEachLineSelected.prototype.getCursorsForSelection = function (selection, editor) {
        if (selection.isEmpty()) {
            return [];
        }
        var model = editor.getModel();
        var newSelections = [];
        for (var i = selection.startLineNumber; i < selection.endLineNumber; i++) {
            var currentLineMaxColumn = model.getLineMaxColumn(i);
            newSelections.push(new Selection(i, currentLineMaxColumn, i, currentLineMaxColumn));
        }
        if (selection.endColumn > 1) {
            newSelections.push(new Selection(selection.endLineNumber, selection.endColumn, selection.endLineNumber, selection.endColumn));
        }
        return newSelections;
    };
    InsertCursorAtEndOfEachLineSelected.prototype.run = function (accessor, editor) {
        var _this = this;
        var selections = editor.getSelections();
        var newSelections = selections
            .map(function (selection) { return _this.getCursorsForSelection(selection, editor); })
            .reduce(function (prev, curr) { return prev.concat(curr); });
        if (newSelections.length > 0) {
            editor.setSelections(newSelections);
        }
    };
    return InsertCursorAtEndOfEachLineSelected;
}(EditorAction));
var MultiCursorSessionResult = /** @class */ (function () {
    function MultiCursorSessionResult(selections, revealRange, revealScrollType) {
        this.selections = selections;
        this.revealRange = revealRange;
        this.revealScrollType = revealScrollType;
    }
    return MultiCursorSessionResult;
}());
export { MultiCursorSessionResult };
var MultiCursorSession = /** @class */ (function () {
    function MultiCursorSession(_editor, findController, isDisconnectedFromFindController, searchText, wholeWord, matchCase, currentMatch) {
        this._editor = _editor;
        this.findController = findController;
        this.isDisconnectedFromFindController = isDisconnectedFromFindController;
        this.searchText = searchText;
        this.wholeWord = wholeWord;
        this.matchCase = matchCase;
        this.currentMatch = currentMatch;
    }
    MultiCursorSession.create = function (editor, findController) {
        var findState = findController.getState();
        // Find widget owns entirely what we search for if:
        //  - focus is not in the editor (i.e. it is in the find widget)
        //  - and the search widget is visible
        //  - and the search string is non-empty
        if (!editor.isFocused() && findState.isRevealed && findState.searchString.length > 0) {
            // Find widget owns what is searched for
            return new MultiCursorSession(editor, findController, false, findState.searchString, findState.wholeWord, findState.matchCase, null);
        }
        // Otherwise, the selection gives the search text, and the find widget gives the search settings
        // The exception is the find state disassociation case: when beginning with a single, collapsed selection
        var isDisconnectedFromFindController = false;
        var wholeWord;
        var matchCase;
        var selections = editor.getSelections();
        if (selections.length === 1 && selections[0].isEmpty()) {
            isDisconnectedFromFindController = true;
            wholeWord = true;
            matchCase = true;
        }
        else {
            wholeWord = findState.wholeWord;
            matchCase = findState.matchCase;
        }
        // Selection owns what is searched for
        var s = editor.getSelection();
        var searchText;
        var currentMatch = null;
        if (s.isEmpty()) {
            // selection is empty => expand to current word
            var word = editor.getModel().getWordAtPosition(s.getStartPosition());
            if (!word) {
                return null;
            }
            searchText = word.word;
            currentMatch = new Selection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn);
        }
        else {
            searchText = editor.getModel().getValueInRange(s).replace(/\r\n/g, '\n');
        }
        return new MultiCursorSession(editor, findController, isDisconnectedFromFindController, searchText, wholeWord, matchCase, currentMatch);
    };
    MultiCursorSession.prototype.addSelectionToNextFindMatch = function () {
        var nextMatch = this._getNextMatch();
        if (!nextMatch) {
            return null;
        }
        var allSelections = this._editor.getSelections();
        return new MultiCursorSessionResult(allSelections.concat(nextMatch), nextMatch, 0 /* Smooth */);
    };
    MultiCursorSession.prototype.moveSelectionToNextFindMatch = function () {
        var nextMatch = this._getNextMatch();
        if (!nextMatch) {
            return null;
        }
        var allSelections = this._editor.getSelections();
        return new MultiCursorSessionResult(allSelections.slice(0, allSelections.length - 1).concat(nextMatch), nextMatch, 0 /* Smooth */);
    };
    MultiCursorSession.prototype._getNextMatch = function () {
        if (this.currentMatch) {
            var result = this.currentMatch;
            this.currentMatch = null;
            return result;
        }
        this.findController.highlightFindOptions();
        var allSelections = this._editor.getSelections();
        var lastAddedSelection = allSelections[allSelections.length - 1];
        var nextMatch = this._editor.getModel().findNextMatch(this.searchText, lastAddedSelection.getEndPosition(), false, this.matchCase, this.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false);
        if (!nextMatch) {
            return null;
        }
        return new Selection(nextMatch.range.startLineNumber, nextMatch.range.startColumn, nextMatch.range.endLineNumber, nextMatch.range.endColumn);
    };
    MultiCursorSession.prototype.addSelectionToPreviousFindMatch = function () {
        var previousMatch = this._getPreviousMatch();
        if (!previousMatch) {
            return null;
        }
        var allSelections = this._editor.getSelections();
        return new MultiCursorSessionResult(allSelections.concat(previousMatch), previousMatch, 0 /* Smooth */);
    };
    MultiCursorSession.prototype.moveSelectionToPreviousFindMatch = function () {
        var previousMatch = this._getPreviousMatch();
        if (!previousMatch) {
            return null;
        }
        var allSelections = this._editor.getSelections();
        return new MultiCursorSessionResult(allSelections.slice(0, allSelections.length - 1).concat(previousMatch), previousMatch, 0 /* Smooth */);
    };
    MultiCursorSession.prototype._getPreviousMatch = function () {
        if (this.currentMatch) {
            var result = this.currentMatch;
            this.currentMatch = null;
            return result;
        }
        this.findController.highlightFindOptions();
        var allSelections = this._editor.getSelections();
        var lastAddedSelection = allSelections[allSelections.length - 1];
        var previousMatch = this._editor.getModel().findPreviousMatch(this.searchText, lastAddedSelection.getStartPosition(), false, this.matchCase, this.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false);
        if (!previousMatch) {
            return null;
        }
        return new Selection(previousMatch.range.startLineNumber, previousMatch.range.startColumn, previousMatch.range.endLineNumber, previousMatch.range.endColumn);
    };
    MultiCursorSession.prototype.selectAll = function () {
        this.findController.highlightFindOptions();
        return this._editor.getModel().findMatches(this.searchText, true, false, this.matchCase, this.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false, 1073741824 /* MAX_SAFE_SMALL_INTEGER */);
    };
    return MultiCursorSession;
}());
export { MultiCursorSession };
var MultiCursorSelectionController = /** @class */ (function (_super) {
    __extends(MultiCursorSelectionController, _super);
    function MultiCursorSelectionController(editor) {
        var _this = _super.call(this) || this;
        _this._editor = editor;
        _this._ignoreSelectionChange = false;
        _this._session = null;
        _this._sessionDispose = [];
        return _this;
    }
    MultiCursorSelectionController.get = function (editor) {
        return editor.getContribution(MultiCursorSelectionController.ID);
    };
    MultiCursorSelectionController.prototype.dispose = function () {
        this._endSession();
        _super.prototype.dispose.call(this);
    };
    MultiCursorSelectionController.prototype.getId = function () {
        return MultiCursorSelectionController.ID;
    };
    MultiCursorSelectionController.prototype._beginSessionIfNeeded = function (findController) {
        var _this = this;
        if (!this._session) {
            // Create a new session
            var session = MultiCursorSession.create(this._editor, findController);
            if (!session) {
                return;
            }
            this._session = session;
            var newState = { searchString: this._session.searchText };
            if (this._session.isDisconnectedFromFindController) {
                newState.wholeWordOverride = 1 /* True */;
                newState.matchCaseOverride = 1 /* True */;
                newState.isRegexOverride = 2 /* False */;
            }
            findController.getState().change(newState, false);
            this._sessionDispose = [
                this._editor.onDidChangeCursorSelection(function (e) {
                    if (_this._ignoreSelectionChange) {
                        return;
                    }
                    _this._endSession();
                }),
                this._editor.onDidBlurEditorText(function () {
                    _this._endSession();
                }),
                findController.getState().onFindReplaceStateChange(function (e) {
                    if (e.matchCase || e.wholeWord) {
                        _this._endSession();
                    }
                })
            ];
        }
    };
    MultiCursorSelectionController.prototype._endSession = function () {
        this._sessionDispose = dispose(this._sessionDispose);
        if (this._session && this._session.isDisconnectedFromFindController) {
            var newState = {
                wholeWordOverride: 0 /* NotSet */,
                matchCaseOverride: 0 /* NotSet */,
                isRegexOverride: 0 /* NotSet */,
            };
            this._session.findController.getState().change(newState, false);
        }
        this._session = null;
    };
    MultiCursorSelectionController.prototype._setSelections = function (selections) {
        this._ignoreSelectionChange = true;
        this._editor.setSelections(selections);
        this._ignoreSelectionChange = false;
    };
    MultiCursorSelectionController.prototype._expandEmptyToWord = function (model, selection) {
        if (!selection.isEmpty()) {
            return selection;
        }
        var word = model.getWordAtPosition(selection.getStartPosition());
        if (!word) {
            return selection;
        }
        return new Selection(selection.startLineNumber, word.startColumn, selection.startLineNumber, word.endColumn);
    };
    MultiCursorSelectionController.prototype._applySessionResult = function (result) {
        if (!result) {
            return;
        }
        this._setSelections(result.selections);
        if (result.revealRange) {
            this._editor.revealRangeInCenterIfOutsideViewport(result.revealRange, result.revealScrollType);
        }
    };
    MultiCursorSelectionController.prototype.getSession = function (findController) {
        return this._session;
    };
    MultiCursorSelectionController.prototype.addSelectionToNextFindMatch = function (findController) {
        if (!this._session) {
            // If there are multiple cursors, handle the case where they do not all select the same text.
            var allSelections = this._editor.getSelections();
            if (allSelections.length > 1) {
                var findState = findController.getState();
                var matchCase = findState.matchCase;
                var selectionsContainSameText = modelRangesContainSameText(this._editor.getModel(), allSelections, matchCase);
                if (!selectionsContainSameText) {
                    var model = this._editor.getModel();
                    var resultingSelections = [];
                    for (var i = 0, len = allSelections.length; i < len; i++) {
                        resultingSelections[i] = this._expandEmptyToWord(model, allSelections[i]);
                    }
                    this._editor.setSelections(resultingSelections);
                    return;
                }
            }
        }
        this._beginSessionIfNeeded(findController);
        if (this._session) {
            this._applySessionResult(this._session.addSelectionToNextFindMatch());
        }
    };
    MultiCursorSelectionController.prototype.addSelectionToPreviousFindMatch = function (findController) {
        this._beginSessionIfNeeded(findController);
        if (this._session) {
            this._applySessionResult(this._session.addSelectionToPreviousFindMatch());
        }
    };
    MultiCursorSelectionController.prototype.moveSelectionToNextFindMatch = function (findController) {
        this._beginSessionIfNeeded(findController);
        if (this._session) {
            this._applySessionResult(this._session.moveSelectionToNextFindMatch());
        }
    };
    MultiCursorSelectionController.prototype.moveSelectionToPreviousFindMatch = function (findController) {
        this._beginSessionIfNeeded(findController);
        if (this._session) {
            this._applySessionResult(this._session.moveSelectionToPreviousFindMatch());
        }
    };
    MultiCursorSelectionController.prototype.selectAll = function (findController) {
        var matches = null;
        var findState = findController.getState();
        // Special case: find widget owns entirely what we search for if:
        // - focus is not in the editor (i.e. it is in the find widget)
        // - and the search widget is visible
        // - and the search string is non-empty
        // - and we're searching for a regex
        if (findState.isRevealed && findState.searchString.length > 0 && findState.isRegex) {
            matches = this._editor.getModel().findMatches(findState.searchString, true, findState.isRegex, findState.matchCase, findState.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false, 1073741824 /* MAX_SAFE_SMALL_INTEGER */);
        }
        else {
            this._beginSessionIfNeeded(findController);
            if (!this._session) {
                return;
            }
            matches = this._session.selectAll();
        }
        if (matches.length > 0) {
            var editorSelection = this._editor.getSelection();
            // Have the primary cursor remain the one where the action was invoked
            for (var i = 0, len = matches.length; i < len; i++) {
                var match = matches[i];
                var intersection = match.range.intersectRanges(editorSelection);
                if (intersection) {
                    // bingo!
                    matches[i] = matches[0];
                    matches[0] = match;
                    break;
                }
            }
            this._setSelections(matches.map(function (m) { return new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn); }));
        }
    };
    MultiCursorSelectionController.ID = 'editor.contrib.multiCursorController';
    return MultiCursorSelectionController;
}(Disposable));
export { MultiCursorSelectionController };
var MultiCursorSelectionControllerAction = /** @class */ (function (_super) {
    __extends(MultiCursorSelectionControllerAction, _super);
    function MultiCursorSelectionControllerAction() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    MultiCursorSelectionControllerAction.prototype.run = function (accessor, editor) {
        var multiCursorController = MultiCursorSelectionController.get(editor);
        if (!multiCursorController) {
            return;
        }
        var findController = CommonFindController.get(editor);
        if (!findController) {
            return null;
        }
        this._run(multiCursorController, findController);
    };
    return MultiCursorSelectionControllerAction;
}(EditorAction));
export { MultiCursorSelectionControllerAction };
var AddSelectionToNextFindMatchAction = /** @class */ (function (_super) {
    __extends(AddSelectionToNextFindMatchAction, _super);
    function AddSelectionToNextFindMatchAction() {
        return _super.call(this, {
            id: 'editor.action.addSelectionToNextFindMatch',
            label: nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"),
            alias: 'Add Selection To Next Find Match',
            precondition: null,
            kbOpts: {
                kbExpr: EditorContextKeys.focus,
                primary: 2048 /* CtrlCmd */ | 34 /* KEY_D */
            }
        }) || this;
    }
    AddSelectionToNextFindMatchAction.prototype._run = function (multiCursorController, findController) {
        multiCursorController.addSelectionToNextFindMatch(findController);
    };
    return AddSelectionToNextFindMatchAction;
}(MultiCursorSelectionControllerAction));
export { AddSelectionToNextFindMatchAction };
var AddSelectionToPreviousFindMatchAction = /** @class */ (function (_super) {
    __extends(AddSelectionToPreviousFindMatchAction, _super);
    function AddSelectionToPreviousFindMatchAction() {
        return _super.call(this, {
            id: 'editor.action.addSelectionToPreviousFindMatch',
            label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"),
            alias: 'Add Selection To Previous Find Match',
            precondition: null
        }) || this;
    }
    AddSelectionToPreviousFindMatchAction.prototype._run = function (multiCursorController, findController) {
        multiCursorController.addSelectionToPreviousFindMatch(findController);
    };
    return AddSelectionToPreviousFindMatchAction;
}(MultiCursorSelectionControllerAction));
export { AddSelectionToPreviousFindMatchAction };
var MoveSelectionToNextFindMatchAction = /** @class */ (function (_super) {
    __extends(MoveSelectionToNextFindMatchAction, _super);
    function MoveSelectionToNextFindMatchAction() {
        return _super.call(this, {
            id: 'editor.action.moveSelectionToNextFindMatch',
            label: nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"),
            alias: 'Move Last Selection To Next Find Match',
            precondition: null,
            kbOpts: {
                kbExpr: EditorContextKeys.focus,
                primary: KeyChord(2048 /* CtrlCmd */ | 41 /* KEY_K */, 2048 /* CtrlCmd */ | 34 /* KEY_D */)
            }
        }) || this;
    }
    MoveSelectionToNextFindMatchAction.prototype._run = function (multiCursorController, findController) {
        multiCursorController.moveSelectionToNextFindMatch(findController);
    };
    return MoveSelectionToNextFindMatchAction;
}(MultiCursorSelectionControllerAction));
export { MoveSelectionToNextFindMatchAction };
var MoveSelectionToPreviousFindMatchAction = /** @class */ (function (_super) {
    __extends(MoveSelectionToPreviousFindMatchAction, _super);
    function MoveSelectionToPreviousFindMatchAction() {
        return _super.call(this, {
            id: 'editor.action.moveSelectionToPreviousFindMatch',
            label: nls.localize('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"),
            alias: 'Move Last Selection To Previous Find Match',
            precondition: null
        }) || this;
    }
    MoveSelectionToPreviousFindMatchAction.prototype._run = function (multiCursorController, findController) {
        multiCursorController.moveSelectionToPreviousFindMatch(findController);
    };
    return MoveSelectionToPreviousFindMatchAction;
}(MultiCursorSelectionControllerAction));
export { MoveSelectionToPreviousFindMatchAction };
var SelectHighlightsAction = /** @class */ (function (_super) {
    __extends(SelectHighlightsAction, _super);
    function SelectHighlightsAction() {
        return _super.call(this, {
            id: 'editor.action.selectHighlights',
            label: nls.localize('selectAllOccurrencesOfFindMatch', "Select All Occurrences of Find Match"),
            alias: 'Select All Occurrences of Find Match',
            precondition: null,
            kbOpts: {
                kbExpr: EditorContextKeys.focus,
                primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 42 /* KEY_L */
            }
        }) || this;
    }
    SelectHighlightsAction.prototype._run = function (multiCursorController, findController) {
        multiCursorController.selectAll(findController);
    };
    return SelectHighlightsAction;
}(MultiCursorSelectionControllerAction));
export { SelectHighlightsAction };
var CompatChangeAll = /** @class */ (function (_super) {
    __extends(CompatChangeAll, _super);
    function CompatChangeAll() {
        return _super.call(this, {
            id: 'editor.action.changeAll',
            label: nls.localize('changeAll.label', "Change All Occurrences"),
            alias: 'Change All Occurrences',
            precondition: EditorContextKeys.writable,
            kbOpts: {
                kbExpr: EditorContextKeys.textFocus,
                primary: 2048 /* CtrlCmd */ | 60 /* F2 */
            },
            menuOpts: {
                group: '1_modification',
                order: 1.2
            }
        }) || this;
    }
    CompatChangeAll.prototype._run = function (multiCursorController, findController) {
        multiCursorController.selectAll(findController);
    };
    return CompatChangeAll;
}(MultiCursorSelectionControllerAction));
export { CompatChangeAll };
var SelectionHighlighterState = /** @class */ (function () {
    function SelectionHighlighterState(lastWordUnderCursor, searchText, matchCase, wordSeparators) {
        this.lastWordUnderCursor = lastWordUnderCursor;
        this.searchText = searchText;
        this.matchCase = matchCase;
        this.wordSeparators = wordSeparators;
    }
    /**
     * Everything equals except for `lastWordUnderCursor`
     */
    SelectionHighlighterState.softEquals = function (a, b) {
        if (!a && !b) {
            return true;
        }
        if (!a || !b) {
            return false;
        }
        return (a.searchText === b.searchText
            && a.matchCase === b.matchCase
            && a.wordSeparators === b.wordSeparators);
    };
    return SelectionHighlighterState;
}());
var SelectionHighlighter = /** @class */ (function (_super) {
    __extends(SelectionHighlighter, _super);
    function SelectionHighlighter(editor) {
        var _this = _super.call(this) || this;
        _this.editor = editor;
        _this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight;
        _this.decorations = [];
        _this.updateSoon = _this._register(new RunOnceScheduler(function () { return _this._update(); }, 300));
        _this.state = null;
        _this._register(editor.onDidChangeConfiguration(function (e) {
            _this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight;
        }));
        _this._register(editor.onDidChangeCursorSelection(function (e) {
            if (!_this._isEnabled) {
                // Early exit if nothing needs to be done!
                // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
                return;
            }
            if (e.selection.isEmpty()) {
                if (e.reason === CursorChangeReason.Explicit) {
                    if (_this.state && (!_this.state.lastWordUnderCursor || !_this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) {
                        // no longer valid
                        _this._setState(null);
                    }
                    _this.updateSoon.schedule();
                }
                else {
                    _this._setState(null);
                }
            }
            else {
                _this._update();
            }
        }));
        _this._register(editor.onDidChangeModel(function (e) {
            _this._setState(null);
        }));
        _this._register(CommonFindController.get(editor).getState().onFindReplaceStateChange(function (e) {
            _this._update();
        }));
        return _this;
    }
    SelectionHighlighter.prototype.getId = function () {
        return SelectionHighlighter.ID;
    };
    SelectionHighlighter.prototype._update = function () {
        this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor));
    };
    SelectionHighlighter._createState = function (isEnabled, editor) {
        if (!isEnabled) {
            return null;
        }
        var model = editor.getModel();
        if (!model) {
            return null;
        }
        var s = editor.getSelection();
        if (s.startLineNumber !== s.endLineNumber) {
            // multiline forbidden for perf reasons
            return null;
        }
        var multiCursorController = MultiCursorSelectionController.get(editor);
        if (!multiCursorController) {
            return null;
        }
        var findController = CommonFindController.get(editor);
        if (!findController) {
            return null;
        }
        var r = multiCursorController.getSession(findController);
        if (!r) {
            var allSelections = editor.getSelections();
            if (allSelections.length > 1) {
                var findState_1 = findController.getState();
                var matchCase = findState_1.matchCase;
                var selectionsContainSameText = modelRangesContainSameText(editor.getModel(), allSelections, matchCase);
                if (!selectionsContainSameText) {
                    return null;
                }
            }
            r = MultiCursorSession.create(editor, findController);
        }
        if (!r) {
            return null;
        }
        var lastWordUnderCursor = null;
        var hasFindOccurrences = DocumentHighlightProviderRegistry.has(model);
        if (r.currentMatch) {
            // This is an empty selection
            if (hasFindOccurrences) {
                // Do not interfere with semantic word highlighting in the no selection case
                return null;
            }
            var config = editor.getConfiguration();
            if (!config.contribInfo.occurrencesHighlight) {
                return null;
            }
            lastWordUnderCursor = r.currentMatch;
        }
        if (/^[ \t]+$/.test(r.searchText)) {
            // whitespace only selection
            return null;
        }
        if (r.searchText.length > 200) {
            // very long selection
            return null;
        }
        // TODO: better handling of this case
        var findState = findController.getState();
        var caseSensitive = findState.matchCase;
        // Return early if the find widget shows the exact same matches
        if (findState.isRevealed) {
            var findStateSearchString = findState.searchString;
            if (!caseSensitive) {
                findStateSearchString = findStateSearchString.toLowerCase();
            }
            var mySearchString = r.searchText;
            if (!caseSensitive) {
                mySearchString = mySearchString.toLowerCase();
            }
            if (findStateSearchString === mySearchString && r.matchCase === findState.matchCase && r.wholeWord === findState.wholeWord && !findState.isRegex) {
                return null;
            }
        }
        return new SelectionHighlighterState(lastWordUnderCursor, r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null);
    };
    SelectionHighlighter.prototype._setState = function (state) {
        if (SelectionHighlighterState.softEquals(this.state, state)) {
            this.state = state;
            return;
        }
        this.state = state;
        if (!this.state) {
            if (this.decorations.length > 0) {
                this.decorations = this.editor.deltaDecorations(this.decorations, []);
            }
            return;
        }
        var model = this.editor.getModel();
        if (model.isTooLargeForTokenization()) {
            // the file is too large, so searching word under cursor in the whole document takes is blocking the UI.
            return;
        }
        var hasFindOccurrences = DocumentHighlightProviderRegistry.has(model);
        var allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(function (m) { return m.range; });
        allMatches.sort(Range.compareRangesUsingStarts);
        var selections = this.editor.getSelections();
        selections.sort(Range.compareRangesUsingStarts);
        // do not overlap with selection (issue #64 and #512)
        var matches = [];
        for (var i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len;) {
            var match = allMatches[i];
            if (j >= lenJ) {
                // finished all editor selections
                matches.push(match);
                i++;
            }
            else {
                var cmp = Range.compareRangesUsingStarts(match, selections[j]);
                if (cmp < 0) {
                    // match is before sel
                    matches.push(match);
                    i++;
                }
                else if (cmp > 0) {
                    // sel is before match
                    j++;
                }
                else {
                    // sel is equal to match
                    i++;
                    j++;
                }
            }
        }
        var decorations = matches.map(function (r) {
            return {
                range: r,
                // Show in overviewRuler only if model has no semantic highlighting
                options: (hasFindOccurrences ? SelectionHighlighter._SELECTION_HIGHLIGHT : SelectionHighlighter._SELECTION_HIGHLIGHT_OVERVIEW)
            };
        });
        this.decorations = this.editor.deltaDecorations(this.decorations, decorations);
    };
    SelectionHighlighter.prototype.dispose = function () {
        this._setState(null);
        _super.prototype.dispose.call(this);
    };
    SelectionHighlighter.ID = 'editor.contrib.selectionHighlighter';
    SelectionHighlighter._SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({
        stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
        className: 'selectionHighlight',
        overviewRuler: {
            color: themeColorFromId(overviewRulerSelectionHighlightForeground),
            darkColor: themeColorFromId(overviewRulerSelectionHighlightForeground),
            position: OverviewRulerLane.Center
        }
    });
    SelectionHighlighter._SELECTION_HIGHLIGHT = ModelDecorationOptions.register({
        stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
        className: 'selectionHighlight',
    });
    return SelectionHighlighter;
}(Disposable));
export { SelectionHighlighter };
function modelRangesContainSameText(model, ranges, matchCase) {
    var selectedText = getValueInRange(model, ranges[0], !matchCase);
    for (var i = 1, len = ranges.length; i < len; i++) {
        var range = ranges[i];
        if (range.isEmpty()) {
            return false;
        }
        var thisSelectedText = getValueInRange(model, range, !matchCase);
        if (selectedText !== thisSelectedText) {
            return false;
        }
    }
    return true;
}
function getValueInRange(model, range, toLowerCase) {
    var text = model.getValueInRange(range);
    return (toLowerCase ? text.toLowerCase() : text);
}
registerEditorContribution(MultiCursorSelectionController);
registerEditorContribution(SelectionHighlighter);
registerEditorAction(InsertCursorAbove);
registerEditorAction(InsertCursorBelow);
registerEditorAction(InsertCursorAtEndOfEachLineSelected);
registerEditorAction(AddSelectionToNextFindMatchAction);
registerEditorAction(AddSelectionToPreviousFindMatchAction);
registerEditorAction(MoveSelectionToNextFindMatchAction);
registerEditorAction(MoveSelectionToPreviousFindMatchAction);
registerEditorAction(SelectHighlightsAction);
registerEditorAction(CompatChangeAll);
