UNPKG

monaco-editor-core

Version:

A browser based code editor

240 lines (239 loc) • 12.1 kB
/*--------------------------------------------------------------------------------------------- * 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 './core/position.js'; import { Range } from './core/range.js'; import { Selection } from './core/selection.js'; import { createScopedLineTokens } from './languages/supports.js'; import { CursorColumns } from './core/cursorColumns.js'; import { normalizeIndentation } from './core/indentation.js'; const autoCloseAlways = () => true; const autoCloseNever = () => false; const autoCloseBeforeWhitespace = (chr) => (chr === ' ' || chr === '\t'); export class CursorConfiguration { static shouldRecreate(e) { return (e.hasChanged(146 /* EditorOption.layoutInfo */) || e.hasChanged(132 /* EditorOption.wordSeparators */) || e.hasChanged(37 /* EditorOption.emptySelectionClipboard */) || e.hasChanged(77 /* EditorOption.multiCursorMergeOverlapping */) || e.hasChanged(79 /* EditorOption.multiCursorPaste */) || e.hasChanged(80 /* EditorOption.multiCursorLimit */) || e.hasChanged(6 /* EditorOption.autoClosingBrackets */) || e.hasChanged(7 /* EditorOption.autoClosingComments */) || e.hasChanged(11 /* EditorOption.autoClosingQuotes */) || e.hasChanged(9 /* EditorOption.autoClosingDelete */) || e.hasChanged(10 /* EditorOption.autoClosingOvertype */) || e.hasChanged(14 /* EditorOption.autoSurround */) || e.hasChanged(129 /* EditorOption.useTabStops */) || e.hasChanged(50 /* EditorOption.fontInfo */) || e.hasChanged(92 /* EditorOption.readOnly */) || e.hasChanged(131 /* EditorOption.wordSegmenterLocales */)); } constructor(languageId, modelOptions, configuration, languageConfigurationService) { this.languageConfigurationService = languageConfigurationService; this._cursorMoveConfigurationBrand = undefined; this._languageId = languageId; const options = configuration.options; const layoutInfo = options.get(146 /* EditorOption.layoutInfo */); const fontInfo = options.get(50 /* EditorOption.fontInfo */); this.readOnly = options.get(92 /* EditorOption.readOnly */); this.tabSize = modelOptions.tabSize; this.indentSize = modelOptions.indentSize; this.insertSpaces = modelOptions.insertSpaces; this.stickyTabStops = options.get(117 /* EditorOption.stickyTabStops */); this.lineHeight = fontInfo.lineHeight; this.typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; this.pageSize = Math.max(1, Math.floor(layoutInfo.height / this.lineHeight) - 2); this.useTabStops = options.get(129 /* EditorOption.useTabStops */); this.wordSeparators = options.get(132 /* EditorOption.wordSeparators */); this.emptySelectionClipboard = options.get(37 /* EditorOption.emptySelectionClipboard */); this.copyWithSyntaxHighlighting = options.get(25 /* EditorOption.copyWithSyntaxHighlighting */); this.multiCursorMergeOverlapping = options.get(77 /* EditorOption.multiCursorMergeOverlapping */); this.multiCursorPaste = options.get(79 /* EditorOption.multiCursorPaste */); this.multiCursorLimit = options.get(80 /* EditorOption.multiCursorLimit */); this.autoClosingBrackets = options.get(6 /* EditorOption.autoClosingBrackets */); this.autoClosingComments = options.get(7 /* EditorOption.autoClosingComments */); this.autoClosingQuotes = options.get(11 /* EditorOption.autoClosingQuotes */); this.autoClosingDelete = options.get(9 /* EditorOption.autoClosingDelete */); this.autoClosingOvertype = options.get(10 /* EditorOption.autoClosingOvertype */); this.autoSurround = options.get(14 /* EditorOption.autoSurround */); this.autoIndent = options.get(12 /* EditorOption.autoIndent */); this.wordSegmenterLocales = options.get(131 /* EditorOption.wordSegmenterLocales */); this.surroundingPairs = {}; this._electricChars = null; this.shouldAutoCloseBefore = { quote: this._getShouldAutoClose(languageId, this.autoClosingQuotes, true), comment: this._getShouldAutoClose(languageId, this.autoClosingComments, false), bracket: this._getShouldAutoClose(languageId, this.autoClosingBrackets, false), }; this.autoClosingPairs = this.languageConfigurationService.getLanguageConfiguration(languageId).getAutoClosingPairs(); const surroundingPairs = this.languageConfigurationService.getLanguageConfiguration(languageId).getSurroundingPairs(); if (surroundingPairs) { for (const pair of surroundingPairs) { this.surroundingPairs[pair.open] = pair.close; } } const commentsConfiguration = this.languageConfigurationService.getLanguageConfiguration(languageId).comments; this.blockCommentStartToken = commentsConfiguration?.blockCommentStartToken ?? null; } get electricChars() { if (!this._electricChars) { this._electricChars = {}; const electricChars = this.languageConfigurationService.getLanguageConfiguration(this._languageId).electricCharacter?.getElectricCharacters(); if (electricChars) { for (const char of electricChars) { this._electricChars[char] = true; } } } return this._electricChars; } /** * Should return opening bracket type to match indentation with */ onElectricCharacter(character, context, column) { const scopedLineTokens = createScopedLineTokens(context, column - 1); const electricCharacterSupport = this.languageConfigurationService.getLanguageConfiguration(scopedLineTokens.languageId).electricCharacter; if (!electricCharacterSupport) { return null; } return electricCharacterSupport.onElectricCharacter(character, scopedLineTokens, column - scopedLineTokens.firstCharOffset); } normalizeIndentation(str) { return normalizeIndentation(str, this.indentSize, this.insertSpaces); } _getShouldAutoClose(languageId, autoCloseConfig, forQuotes) { switch (autoCloseConfig) { case 'beforeWhitespace': return autoCloseBeforeWhitespace; case 'languageDefined': return this._getLanguageDefinedShouldAutoClose(languageId, forQuotes); case 'always': return autoCloseAlways; case 'never': return autoCloseNever; } } _getLanguageDefinedShouldAutoClose(languageId, forQuotes) { const autoCloseBeforeSet = this.languageConfigurationService.getLanguageConfiguration(languageId).getAutoCloseBeforeSet(forQuotes); return c => autoCloseBeforeSet.indexOf(c) !== -1; } /** * Returns a visible column from a column. * @see {@link CursorColumns} */ visibleColumnFromColumn(model, position) { return CursorColumns.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, this.tabSize); } /** * Returns a visible column from a column. * @see {@link CursorColumns} */ columnFromVisibleColumn(model, lineNumber, visibleColumn) { const result = CursorColumns.columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, this.tabSize); const minColumn = model.getLineMinColumn(lineNumber); if (result < minColumn) { return minColumn; } const maxColumn = model.getLineMaxColumn(lineNumber); if (result > maxColumn) { return maxColumn; } return result; } } export class CursorState { static fromModelState(modelState) { return new PartialModelCursorState(modelState); } static fromViewState(viewState) { return new PartialViewCursorState(viewState); } static fromModelSelection(modelSelection) { const selection = Selection.liftSelection(modelSelection); const modelState = new SingleCursorState(Range.fromPositions(selection.getSelectionStart()), 0 /* SelectionStartKind.Simple */, 0, selection.getPosition(), 0); return CursorState.fromModelState(modelState); } static fromModelSelections(modelSelections) { const states = []; for (let i = 0, len = modelSelections.length; i < len; i++) { states[i] = this.fromModelSelection(modelSelections[i]); } return states; } constructor(modelState, viewState) { this._cursorStateBrand = undefined; this.modelState = modelState; this.viewState = viewState; } equals(other) { return (this.viewState.equals(other.viewState) && this.modelState.equals(other.modelState)); } } export class PartialModelCursorState { constructor(modelState) { this.modelState = modelState; this.viewState = null; } } export class PartialViewCursorState { constructor(viewState) { this.modelState = null; this.viewState = viewState; } } /** * Represents the cursor state on either the model or on the view model. */ export class SingleCursorState { constructor(selectionStart, selectionStartKind, selectionStartLeftoverVisibleColumns, position, leftoverVisibleColumns) { this.selectionStart = selectionStart; this.selectionStartKind = selectionStartKind; this.selectionStartLeftoverVisibleColumns = selectionStartLeftoverVisibleColumns; this.position = position; this.leftoverVisibleColumns = leftoverVisibleColumns; this._singleCursorStateBrand = undefined; this.selection = SingleCursorState._computeSelection(this.selectionStart, this.position); } equals(other) { return (this.selectionStartLeftoverVisibleColumns === other.selectionStartLeftoverVisibleColumns && this.leftoverVisibleColumns === other.leftoverVisibleColumns && this.selectionStartKind === other.selectionStartKind && this.position.equals(other.position) && this.selectionStart.equalsRange(other.selectionStart)); } hasSelection() { return (!this.selection.isEmpty() || !this.selectionStart.isEmpty()); } move(inSelectionMode, lineNumber, column, leftoverVisibleColumns) { if (inSelectionMode) { // move just position return new SingleCursorState(this.selectionStart, this.selectionStartKind, this.selectionStartLeftoverVisibleColumns, new Position(lineNumber, column), leftoverVisibleColumns); } else { // move everything return new SingleCursorState(new Range(lineNumber, column, lineNumber, column), 0 /* SelectionStartKind.Simple */, leftoverVisibleColumns, new Position(lineNumber, column), leftoverVisibleColumns); } } static _computeSelection(selectionStart, position) { if (selectionStart.isEmpty() || !position.isBeforeOrEqual(selectionStart.getStartPosition())) { return Selection.fromPositions(selectionStart.getStartPosition(), position); } else { return Selection.fromPositions(selectionStart.getEndPosition(), position); } } } export class EditOperationResult { constructor(type, commands, opts) { this._editOperationResultBrand = undefined; this.type = type; this.commands = commands; this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore; this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter; } } export function isQuote(ch) { return (ch === '\'' || ch === '"' || ch === '`'); }