UNPKG

monaco-editor

Version:
287 lines (286 loc) • 14.1 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { 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 extendStatics(d, b); } return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); import './bracketMatching.css'; import * as nls from '../../../nls.js'; import { RunOnceScheduler } from '../../../base/common/async.js'; import { Disposable } from '../../../base/common/lifecycle.js'; import { EditorAction, registerEditorAction, registerEditorContribution } from '../../browser/editorExtensions.js'; import { Position } from '../../common/core/position.js'; import { Selection } from '../../common/core/selection.js'; import { EditorContextKeys } from '../../common/editorContextKeys.js'; import { OverviewRulerLane } from '../../common/model.js'; import { ModelDecorationOptions } from '../../common/model/textModel.js'; import { editorBracketMatchBackground, editorBracketMatchBorder } from '../../common/view/editorColorRegistry.js'; import { registerColor } from '../../../platform/theme/common/colorRegistry.js'; import { registerThemingParticipant, themeColorFromId } from '../../../platform/theme/common/themeService.js'; var overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.')); var JumpToBracketAction = /** @class */ (function (_super) { __extends(JumpToBracketAction, _super); function JumpToBracketAction() { return _super.call(this, { id: 'editor.action.jumpToBracket', label: nls.localize('smartSelect.jumpBracket', "Go to Bracket"), alias: 'Go to Bracket', precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: 2048 /* CtrlCmd */ | 1024 /* Shift */ | 88 /* US_BACKSLASH */, weight: 100 /* EditorContrib */ } }) || this; } JumpToBracketAction.prototype.run = function (accessor, editor) { var controller = BracketMatchingController.get(editor); if (!controller) { return; } controller.jumpToBracket(); }; return JumpToBracketAction; }(EditorAction)); var SelectToBracketAction = /** @class */ (function (_super) { __extends(SelectToBracketAction, _super); function SelectToBracketAction() { return _super.call(this, { id: 'editor.action.selectToBracket', label: nls.localize('smartSelect.selectToBracket', "Select to Bracket"), alias: 'Select to Bracket', precondition: null }) || this; } SelectToBracketAction.prototype.run = function (accessor, editor) { var controller = BracketMatchingController.get(editor); if (!controller) { return; } controller.selectToBracket(); }; return SelectToBracketAction; }(EditorAction)); var BracketsData = /** @class */ (function () { function BracketsData(position, brackets) { this.position = position; this.brackets = brackets; } return BracketsData; }()); var BracketMatchingController = /** @class */ (function (_super) { __extends(BracketMatchingController, _super); function BracketMatchingController(editor) { var _this = _super.call(this) || this; _this._editor = editor; _this._lastBracketsData = []; _this._lastVersionId = 0; _this._decorations = []; _this._updateBracketsSoon = _this._register(new RunOnceScheduler(function () { return _this._updateBrackets(); }, 50)); _this._matchBrackets = _this._editor.getConfiguration().contribInfo.matchBrackets; _this._updateBracketsSoon.schedule(); _this._register(editor.onDidChangeCursorPosition(function (e) { if (!_this._matchBrackets) { // 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; } _this._updateBracketsSoon.schedule(); })); _this._register(editor.onDidChangeModelContent(function (e) { _this._updateBracketsSoon.schedule(); })); _this._register(editor.onDidChangeModel(function (e) { _this._lastBracketsData = []; _this._decorations = []; _this._updateBracketsSoon.schedule(); })); _this._register(editor.onDidChangeModelLanguageConfiguration(function (e) { _this._lastBracketsData = []; _this._updateBracketsSoon.schedule(); })); _this._register(editor.onDidChangeConfiguration(function (e) { _this._matchBrackets = _this._editor.getConfiguration().contribInfo.matchBrackets; if (!_this._matchBrackets && _this._decorations.length > 0) { // Remove existing decorations if bracket matching is off _this._decorations = _this._editor.deltaDecorations(_this._decorations, []); } _this._updateBracketsSoon.schedule(); })); return _this; } BracketMatchingController.get = function (editor) { return editor.getContribution(BracketMatchingController.ID); }; BracketMatchingController.prototype.getId = function () { return BracketMatchingController.ID; }; BracketMatchingController.prototype.jumpToBracket = function () { if (!this._editor.hasModel()) { return; } var model = this._editor.getModel(); var newSelections = this._editor.getSelections().map(function (selection) { var position = selection.getStartPosition(); // find matching brackets if position is on a bracket var brackets = model.matchBracket(position); var newCursorPosition = null; if (brackets) { if (brackets[0].containsPosition(position)) { newCursorPosition = brackets[1].getStartPosition(); } else if (brackets[1].containsPosition(position)) { newCursorPosition = brackets[0].getStartPosition(); } } else { // find the next bracket if the position isn't on a matching bracket var nextBracket = model.findNextBracket(position); if (nextBracket && nextBracket.range) { newCursorPosition = nextBracket.range.getStartPosition(); } } if (newCursorPosition) { return new Selection(newCursorPosition.lineNumber, newCursorPosition.column, newCursorPosition.lineNumber, newCursorPosition.column); } return new Selection(position.lineNumber, position.column, position.lineNumber, position.column); }); this._editor.setSelections(newSelections); this._editor.revealRange(newSelections[0]); }; BracketMatchingController.prototype.selectToBracket = function () { if (!this._editor.hasModel()) { return; } var model = this._editor.getModel(); var newSelections = []; this._editor.getSelections().forEach(function (selection) { var position = selection.getStartPosition(); var brackets = model.matchBracket(position); var openBracket = null; var closeBracket = null; if (!brackets) { var nextBracket = model.findNextBracket(position); if (nextBracket && nextBracket.range) { brackets = model.matchBracket(nextBracket.range.getStartPosition()); } } if (brackets) { if (brackets[0].startLineNumber === brackets[1].startLineNumber) { openBracket = brackets[1].startColumn < brackets[0].startColumn ? brackets[1].getStartPosition() : brackets[0].getStartPosition(); closeBracket = brackets[1].startColumn < brackets[0].startColumn ? brackets[0].getEndPosition() : brackets[1].getEndPosition(); } else { openBracket = brackets[1].startLineNumber < brackets[0].startLineNumber ? brackets[1].getStartPosition() : brackets[0].getStartPosition(); closeBracket = brackets[1].startLineNumber < brackets[0].startLineNumber ? brackets[0].getEndPosition() : brackets[1].getEndPosition(); } } if (openBracket && closeBracket) { newSelections.push(new Selection(openBracket.lineNumber, openBracket.column, closeBracket.lineNumber, closeBracket.column)); } }); if (newSelections.length > 0) { this._editor.setSelections(newSelections); this._editor.revealRange(newSelections[0]); } }; BracketMatchingController.prototype._updateBrackets = function () { if (!this._matchBrackets) { return; } this._recomputeBrackets(); var newDecorations = [], newDecorationsLen = 0; for (var i = 0, len = this._lastBracketsData.length; i < len; i++) { var brackets = this._lastBracketsData[i].brackets; if (brackets) { newDecorations[newDecorationsLen++] = { range: brackets[0], options: BracketMatchingController._DECORATION_OPTIONS }; newDecorations[newDecorationsLen++] = { range: brackets[1], options: BracketMatchingController._DECORATION_OPTIONS }; } } this._decorations = this._editor.deltaDecorations(this._decorations, newDecorations); }; BracketMatchingController.prototype._recomputeBrackets = function () { if (!this._editor.hasModel()) { // no model => no brackets! this._lastBracketsData = []; this._lastVersionId = 0; return; } var model = this._editor.getModel(); var versionId = model.getVersionId(); var previousData = []; if (this._lastVersionId === versionId) { // use the previous data only if the model is at the same version id previousData = this._lastBracketsData; } var selections = this._editor.getSelections(); var positions = [], positionsLen = 0; for (var i = 0, len = selections.length; i < len; i++) { var selection = selections[i]; if (selection.isEmpty()) { // will bracket match a cursor only if the selection is collapsed positions[positionsLen++] = selection.getStartPosition(); } } // sort positions for `previousData` cache hits if (positions.length > 1) { positions.sort(Position.compare); } var newData = [], newDataLen = 0; var previousIndex = 0, previousLen = previousData.length; for (var i = 0, len = positions.length; i < len; i++) { var position = positions[i]; while (previousIndex < previousLen && previousData[previousIndex].position.isBefore(position)) { previousIndex++; } if (previousIndex < previousLen && previousData[previousIndex].position.equals(position)) { newData[newDataLen++] = previousData[previousIndex]; } else { var brackets = model.matchBracket(position); newData[newDataLen++] = new BracketsData(position, brackets); } } this._lastBracketsData = newData; this._lastVersionId = versionId; }; BracketMatchingController.ID = 'editor.contrib.bracketMatchingController'; BracketMatchingController._DECORATION_OPTIONS = ModelDecorationOptions.register({ stickiness: 1 /* NeverGrowsWhenTypingAtEdges */, className: 'bracket-match', overviewRuler: { color: themeColorFromId(overviewRulerBracketMatchForeground), position: OverviewRulerLane.Center } }); return BracketMatchingController; }(Disposable)); export { BracketMatchingController }; registerEditorContribution(BracketMatchingController); registerEditorAction(SelectToBracketAction); registerEditorAction(JumpToBracketAction); registerThemingParticipant(function (theme, collector) { var bracketMatchBackground = theme.getColor(editorBracketMatchBackground); if (bracketMatchBackground) { collector.addRule(".monaco-editor .bracket-match { background-color: " + bracketMatchBackground + "; }"); } var bracketMatchBorder = theme.getColor(editorBracketMatchBorder); if (bracketMatchBorder) { collector.addRule(".monaco-editor .bracket-match { border: 1px solid " + bracketMatchBorder + "; }"); } });