/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as arrays from '../../../base/common/arrays.js'; import { IMarkdownString } from '../../../base/common/htmlContent.js'; import { IJSONSchema } from '../../../base/common/jsonSchema.js'; import * as objects from '../../../base/common/objects.js'; import * as platform from '../../../base/common/platform.js'; import { ScrollbarVisibility } from '../../../base/common/scrollable.js'; import { Constants } from '../../../base/common/uint.js'; import { EDITOR_FONT_DEFAULTS, FONT_VARIATION_OFF, FONT_VARIATION_TRANSLATE, FontInfo } from './fontInfo.js'; import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js'; import { USUAL_WORD_SEPARATORS } from '../core/wordHelper.js'; import * as nls from '../../../nls.js'; import { AccessibilitySupport } from '../../../platform/accessibility/common/accessibility.js'; import { IConfigurationPropertySchema } from '../../../platform/configuration/common/configurationRegistry.js'; //#region typed options /** * Configuration options for auto closing quotes and brackets */ export type EditorAutoClosingStrategy = 'always' | 'languageDefined' | 'beforeWhitespace' | 'never'; /** * Configuration options for auto wrapping quotes and brackets */ export type EditorAutoSurroundStrategy = 'languageDefined' | 'quotes' | 'brackets' | 'never'; /** * Configuration options for typing over closing quotes or brackets */ export type EditorAutoClosingEditStrategy = 'always' | 'auto' | 'never'; type Unknown = { [K in keyof T]: unknown }; /** * Configuration options for auto indentation in the editor */ export const enum EditorAutoIndentStrategy { None = 0, Keep = 1, Brackets = 2, Advanced = 3, Full = 4 } /** * Configuration options for the editor. */ export interface IEditorOptions { /** * This editor is used inside a diff editor. */ inDiffEditor?: boolean; /** * This editor is allowed to use variable line heights. */ allowVariableLineHeights?: boolean; /** * This editor is allowed to use variable font-sizes and font-families */ allowVariableFonts?: boolean; /** * This editor is allowed to use variable font-sizes and font-families in accessibility mode */ allowVariableFontsInAccessibilityMode?: boolean; /** * The aria label for the editor's textarea (when it is focused). */ ariaLabel?: string; /** * Whether the aria-required attribute should be set on the editors textarea. */ ariaRequired?: boolean; /** * Control whether a screen reader announces inline suggestion content immediately. */ screenReaderAnnounceInlineSuggestion?: boolean; /** * The `tabindex` property of the editor's textarea */ tabIndex?: number; /** * Render vertical lines at the specified columns. * Defaults to empty array. */ rulers?: (number | IRulerOption)[]; /** * Locales used for segmenting lines into words when doing word related navigations or operations. * * Specify the BCP 47 language tag of the word you wish to recognize (e.g., ja, zh-CN, zh-Hant-TW, etc.). * Defaults to empty array */ wordSegmenterLocales?: string | string[]; /** * A string containing the word separators used when doing word navigation. * Defaults to `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/? */ wordSeparators?: string; /** * Enable Linux primary clipboard. * Defaults to true. */ selectionClipboard?: boolean; /** * Control the rendering of line numbers. * If it is a function, it will be invoked when rendering a line number and the return value will be rendered. * Otherwise, if it is a truthy, line numbers will be rendered normally (equivalent of using an identity function). * Otherwise, line numbers will not be rendered. * Defaults to `on`. */ lineNumbers?: LineNumbersType; /** * Controls the minimal number of visible leading and trailing lines surrounding the cursor. * Defaults to 0. */ cursorSurroundingLines?: number; /** * Controls when `cursorSurroundingLines` should be enforced * Defaults to `default`, `cursorSurroundingLines` is not enforced when cursor position is changed * by mouse. */ cursorSurroundingLinesStyle?: 'default' | 'all'; /** * Render last line number when the file ends with a newline. * Defaults to 'on' for Windows and macOS and 'dimmed' for Linux. */ renderFinalNewline?: 'on' | 'off' | 'dimmed'; /** * Remove unusual line terminators like LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS). * Defaults to 'prompt'. */ unusualLineTerminators?: 'auto' | 'off' | 'prompt'; /** * Should the corresponding line be selected when clicking on the line number? * Defaults to true. */ selectOnLineNumbers?: boolean; /** * Control the width of line numbers, by reserving horizontal space for rendering at least an amount of digits. * Defaults to 5. */ lineNumbersMinChars?: number; /** * Enable the rendering of the glyph margin. * Defaults to true in vscode and to false in monaco-editor. */ glyphMargin?: boolean; /** * The width reserved for line decorations (in px). * Line decorations are placed between line numbers and the editor content. * You can pass in a string in the format floating point followed by "ch". e.g. 1.3ch. * Defaults to 10. */ lineDecorationsWidth?: number | string; /** * When revealing the cursor, a virtual padding (px) is added to the cursor, turning it into a rectangle. * This virtual padding ensures that the cursor gets revealed before hitting the edge of the viewport. * Defaults to 30 (px). */ revealHorizontalRightPadding?: number; /** * Render the editor selection with rounded borders. * Defaults to true. */ roundedSelection?: boolean; /** * Class name to be added to the editor. */ extraEditorClassName?: string; /** * Should the editor be read only. See also `domReadOnly`. * Defaults to false. */ readOnly?: boolean; /** * The message to display when the editor is readonly. */ readOnlyMessage?: IMarkdownString; /** * Should the textarea used for input use the DOM `readonly` attribute. * Defaults to false. */ domReadOnly?: boolean; /** * Enable linked editing. * Defaults to false. */ linkedEditing?: boolean; /** * deprecated, use linkedEditing instead */ renameOnType?: boolean; /** * Should the editor render validation decorations. * Defaults to editable. */ renderValidationDecorations?: 'editable' | 'on' | 'off'; /** * Control the behavior and rendering of the scrollbars. */ scrollbar?: IEditorScrollbarOptions; /** * Control the behavior of sticky scroll options */ stickyScroll?: IEditorStickyScrollOptions; /** * Control the behavior and rendering of the minimap. */ minimap?: IEditorMinimapOptions; /** * Control the behavior of the find widget. */ find?: IEditorFindOptions; /** * Display overflow widgets as `fixed`. * Defaults to `false`. */ fixedOverflowWidgets?: boolean; /** * Allow content widgets and overflow widgets to overflow the editor viewport. * Defaults to `true`. */ allowOverflow?: boolean; /** * The number of vertical lanes the overview ruler should render. * Defaults to 3. */ overviewRulerLanes?: number; /** * Controls if a border should be drawn around the overview ruler. * Defaults to `true`. */ overviewRulerBorder?: boolean; /** * Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'. * Defaults to 'blink'. */ cursorBlinking?: 'blink' | 'smooth' | 'phase' | 'expand' | 'solid'; /** * Zoom the font in the editor when using the mouse wheel in combination with holding Ctrl. * Defaults to false. */ mouseWheelZoom?: boolean; /** * Control the mouse pointer style, either 'text' or 'default' or 'copy' * Defaults to 'text' */ mouseStyle?: 'text' | 'default' | 'copy'; /** * Enable smooth caret animation. * Defaults to 'off'. */ cursorSmoothCaretAnimation?: 'off' | 'explicit' | 'on'; /** * Control the cursor style in insert mode. * Defaults to 'line'. */ cursorStyle?: 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin'; /** * Control the cursor style in overtype mode. * Defaults to 'block'. */ overtypeCursorStyle?: 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin'; /** * Controls whether paste in overtype mode should overwrite or insert. */ overtypeOnPaste?: boolean; /** * Control the width of the cursor when cursorStyle is set to 'line' */ cursorWidth?: number; /** * Control the height of the cursor when cursorStyle is set to 'line' */ cursorHeight?: number; /** * Enable font ligatures. * Defaults to false. */ fontLigatures?: boolean | string; /** * Enable font variations. * Defaults to false. */ fontVariations?: boolean | string; /** * Controls whether to use default color decorations or not using the default document color provider */ defaultColorDecorators?: 'auto' | 'always' | 'never'; /** * Disable the use of `transform: translate3d(0px, 0px, 0px)` for the editor margin and lines layers. * The usage of `transform: translate3d(0px, 0px, 0px)` acts as a hint for browsers to create an extra layer. * Defaults to false. */ disableLayerHinting?: boolean; /** * Disable the optimizations for monospace fonts. * Defaults to false. */ disableMonospaceOptimizations?: boolean; /** * Should the cursor be hidden in the overview ruler. * Defaults to false. */ hideCursorInOverviewRuler?: boolean; /** * Enable that scrolling can go one screen size after the last line. * Defaults to true. */ scrollBeyondLastLine?: boolean; /** * Scroll editor on middle click */ scrollOnMiddleClick?: boolean; /** * Enable that scrolling can go beyond the last column by a number of columns. * Defaults to 5. */ scrollBeyondLastColumn?: number; /** * Enable that the editor animates scrolling to a position. * Defaults to false. */ smoothScrolling?: boolean; /** * Enable that the editor will install a ResizeObserver to check if its container dom node size has changed. * Defaults to false. */ automaticLayout?: boolean; /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. * When `wordWrap` = "on", the lines will wrap at the viewport width. * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). * Defaults to "off". */ wordWrap?: 'off' | 'on' | 'wordWrapColumn' | 'bounded'; /** * Control whether an indicator is rendered at the wrapping column of soft wrapped lines. * Defaults to false. */ wordWrapIndicator?: boolean; /** * Override the `wordWrap` setting. */ wordWrapOverride1?: 'off' | 'on' | 'inherit'; /** * Override the `wordWrapOverride1` setting. */ wordWrapOverride2?: 'off' | 'on' | 'inherit'; /** * Control the wrapping of the editor. * When `wordWrap` = "off", the lines will never wrap. * When `wordWrap` = "on", the lines will wrap at the viewport width. * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). * Defaults to 80. */ wordWrapColumn?: number; /** * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; /** * Controls the wrapping strategy to use. * Defaults to 'simple'. */ wrappingStrategy?: 'simple' | 'advanced'; /** * Create a softwrap on every quoted "\n" literal. * Defaults to false. */ wrapOnEscapedLineFeeds?: boolean; /** * Configure word wrapping characters. A break will be introduced before these characters. */ wordWrapBreakBeforeCharacters?: string; /** * Configure word wrapping characters. A break will be introduced after these characters. */ wordWrapBreakAfterCharacters?: string; /** * Sets whether line breaks appear wherever the text would otherwise overflow its content box. * When wordBreak = 'normal', Use the default line break rule. * When wordBreak = 'keepAll', Word breaks should not be used for Chinese/Japanese/Korean (CJK) text. Non-CJK text behavior is the same as for normal. */ wordBreak?: 'normal' | 'keepAll'; /** * Performance guard: Stop rendering a line after x characters. * Defaults to 10000. * Use -1 to never stop rendering */ stopRenderingLineAfter?: number; /** * Configure the editor's hover. */ hover?: IEditorHoverOptions; /** * Enable detecting links and making them clickable. * Defaults to true. */ links?: boolean; /** * Enable inline color decorators and color picker rendering. */ colorDecorators?: boolean; /** * Controls what is the condition to spawn a color picker from a color dectorator */ colorDecoratorsActivatedOn?: 'clickAndHover' | 'click' | 'hover'; /** * Controls the max number of color decorators that can be rendered in an editor at once. */ colorDecoratorsLimit?: number; /** * Control the behaviour of comments in the editor. */ comments?: IEditorCommentsOptions; /** * Enable custom contextmenu. * Defaults to true. */ contextmenu?: boolean; /** * A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events. * Defaults to 1. */ mouseWheelScrollSensitivity?: number; /** * FastScrolling mulitplier speed when pressing `Alt` * Defaults to 5. */ fastScrollSensitivity?: number; /** * Enable that the editor scrolls only the predominant axis. Prevents horizontal drift when scrolling vertically on a trackpad. * Defaults to true. */ scrollPredominantAxis?: boolean; /** * Make scrolling inertial - mostly useful with touchpad on linux. */ inertialScroll?: boolean; /** * Enable that the selection with the mouse and keys is doing column selection. * Defaults to false. */ columnSelection?: boolean; /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' */ multiCursorModifier?: 'ctrlCmd' | 'alt'; /** * Merge overlapping selections. * Defaults to true */ multiCursorMergeOverlapping?: boolean; /** * Configure the behaviour when pasting text with a line count equal to the cursor count or when pasting column selections into a single cursor. * Defaults to 'spread'. */ multiCursorPaste?: 'spread' | 'full'; /** * Controls the max number of text cursors that can be in an active editor at once. */ multiCursorLimit?: number; /** * Enables middle mouse button to open links and Go To Definition */ mouseMiddleClickAction?: MouseMiddleClickAction; /** * Configure the editor's accessibility support. * Defaults to 'auto'. It is best to leave this to 'auto'. */ accessibilitySupport?: 'auto' | 'off' | 'on'; /** * Controls the number of lines in the editor that can be read out by a screen reader */ accessibilityPageSize?: number; /** * Suggest options. */ suggest?: ISuggestOptions; inlineSuggest?: IInlineSuggestOptions; /** * Smart select options. */ smartSelect?: ISmartSelectOptions; /** * */ gotoLocation?: IGotoLocationOptions; /** * Enable quick suggestions (shadow suggestions) * Defaults to true. */ quickSuggestions?: boolean | QuickSuggestionsValue | IQuickSuggestionsOptions; /** * Quick suggestions show delay (in ms) * Defaults to 10 (ms) */ quickSuggestionsDelay?: number; /** * Controls the spacing around the editor. */ padding?: IEditorPaddingOptions; /** * Parameter hint options. */ parameterHints?: IEditorParameterHintOptions; /** * Options for auto closing brackets. * Defaults to language defined behavior. */ autoClosingBrackets?: EditorAutoClosingStrategy; /** * Options for auto closing comments. * Defaults to language defined behavior. */ autoClosingComments?: EditorAutoClosingStrategy; /** * Options for auto closing quotes. * Defaults to language defined behavior. */ autoClosingQuotes?: EditorAutoClosingStrategy; /** * Options for pressing backspace near quotes or bracket pairs. */ autoClosingDelete?: EditorAutoClosingEditStrategy; /** * Options for typing over closing quotes or brackets. */ autoClosingOvertype?: EditorAutoClosingEditStrategy; /** * Options for auto surrounding. * Defaults to always allowing auto surrounding. */ autoSurround?: EditorAutoSurroundStrategy; /** * Controls whether the editor should automatically adjust the indentation when users type, paste, move or indent lines. * Defaults to advanced. */ autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; /** * Boolean which controls whether to autoindent on paste */ autoIndentOnPaste?: boolean; /** * Boolean which controls whether to autoindent on paste within a string when autoIndentOnPaste is enabled. */ autoIndentOnPasteWithinString?: boolean; /** * Emulate selection behaviour of tab characters when using spaces for indentation. * This means selection will stick to tab stops. */ stickyTabStops?: boolean; /** * Enable format on type. * Defaults to false. */ formatOnType?: boolean; /** * Enable format on paste. * Defaults to false. */ formatOnPaste?: boolean; /** * Controls the width used to render full-width characters. * Defaults to 'font'. */ fullwidthCharacterWidth?: 'font' | 'twoCells'; /** * Controls whether double-clicking next to a bracket or quote selects the content inside. * Defaults to true. */ doubleClickSelectsBlock?: boolean; /** * Controls if the editor should allow to move selections via drag and drop. * Defaults to false. */ dragAndDrop?: boolean; /** * Enable the suggestion box to pop-up on trigger characters. * Defaults to true. */ suggestOnTriggerCharacters?: boolean; /** * Accept suggestions on ENTER. * Defaults to 'on'. */ acceptSuggestionOnEnter?: 'on' | 'smart' | 'off'; /** * Accept suggestions on provider defined characters. * Defaults to true. */ acceptSuggestionOnCommitCharacter?: boolean; /** * Enable snippet suggestions. Default to 'true'. */ snippetSuggestions?: 'top' | 'bottom' | 'inline' | 'none'; /** * Copying without a selection copies the current line. */ emptySelectionClipboard?: boolean; /** * Syntax highlighting is copied. */ copyWithSyntaxHighlighting?: boolean; /** * The history mode for suggestions. */ suggestSelection?: 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'; /** * The font size for the suggest widget. * Defaults to the editor font size. */ suggestFontSize?: number; /** * The line height for the suggest widget. * Defaults to the editor line height. */ suggestLineHeight?: number; /** * Enable tab completion. */ tabCompletion?: 'on' | 'off' | 'onlySnippets'; /** * Enable selection highlight. * Defaults to true. */ selectionHighlight?: boolean; /** * Enable selection highlight for multiline selections. * Defaults to false. */ selectionHighlightMultiline?: boolean; /** * Maximum length (in characters) for selection highlights. * Set to 0 to have an unlimited length. */ selectionHighlightMaxLength?: number; /** * Controls how occurrences of selected text are matched for occurrence selection and highlighting. * Defaults to 'findOptions', which uses the Find widget's match case and whole word settings. */ selectedTextMatchMode?: 'findOptions' | 'caseSensitive' | 'caseInsensitive'; /** * Enable semantic occurrences highlight. * Defaults to 'singleFile'. * 'off' disables occurrence highlighting * 'singleFile' triggers occurrence highlighting in the current document * 'multiFile' triggers occurrence highlighting across valid open documents */ occurrencesHighlight?: 'off' | 'singleFile' | 'multiFile'; /** * Controls delay for occurrences highlighting * Defaults to 250. * Minimum value is 0 * Maximum value is 2000 */ occurrencesHighlightDelay?: number; /** * Merge single-document highlights from all matching providers. * Defaults to false. */ occurrencesHighlightFromAllProviders?: boolean; /** * Show code lens * Defaults to true. */ codeLens?: boolean; /** * Code lens font family. Defaults to editor font family. */ codeLensFontFamily?: string; /** * Code lens font size. Default to 90% of the editor font size */ codeLensFontSize?: number; /** * Control the behavior and rendering of the code action lightbulb. */ lightbulb?: IEditorLightbulbOptions; /** * Timeout for running code actions on save. */ codeActionsOnSaveTimeout?: number; /** * Enable code folding. * Defaults to true. */ folding?: boolean; /** * Selects the folding strategy. 'auto' uses the strategies contributed for the current document, 'indentation' uses the indentation based folding strategy. * Defaults to 'auto'. */ foldingStrategy?: 'auto' | 'indentation'; /** * Enable highlight for folded regions. * Defaults to true. */ foldingHighlight?: boolean; /** * Auto fold imports folding regions. * Defaults to true. */ foldingImportsByDefault?: boolean; /** * Maximum number of foldable regions. * Defaults to 5000. */ foldingMaximumRegions?: number; /** * Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter. * Defaults to 'mouseover'. */ showFoldingControls?: 'always' | 'never' | 'mouseover'; /** * Controls whether clicking on the empty content after a folded line will unfold the line. * Defaults to false. */ unfoldOnClickAfterEndOfLine?: boolean; /** * Enable highlighting of matching brackets. * Defaults to 'always'. */ matchBrackets?: 'never' | 'near' | 'always'; /** * Enable experimental rendering using WebGPU. * Defaults to 'off'. */ experimentalGpuAcceleration?: 'on' | 'off'; /** * Enable experimental whitespace rendering. * Defaults to 'svg'. */ experimentalWhitespaceRendering?: 'svg' | 'font' | 'off'; /** * Enable rendering of whitespace. * Defaults to 'selection'. */ renderWhitespace?: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; /** * Enable rendering of control characters. * Defaults to true. */ renderControlCharacters?: boolean; /** * Enable rendering of current line highlight. * Defaults to all. */ renderLineHighlight?: 'none' | 'gutter' | 'line' | 'all'; /** * Control if the current line highlight should be rendered only the editor is focused. * Defaults to false. */ renderLineHighlightOnlyWhenFocus?: boolean; /** * Inserting and deleting whitespace follows tab stops. */ useTabStops?: boolean; /** * Controls whether the editor should automatically remove indentation whitespace when joining lines with Delete. * Defaults to false. */ trimWhitespaceOnDelete?: boolean; /** * The font family */ fontFamily?: string; /** * The font weight */ fontWeight?: string; /** * The font size */ fontSize?: number; /** * The line height */ lineHeight?: number; /** * The letter spacing */ letterSpacing?: number; /** * Controls fading out of unused variables. */ showUnused?: boolean; /** * Controls whether to focus the inline editor in the peek widget by default. * Defaults to false. */ peekWidgetDefaultFocus?: 'tree' | 'editor'; /** * Sets a placeholder for the editor. * If set, the placeholder is shown if the editor is empty. */ placeholder?: string | undefined; /** * Controls whether the definition link opens element in the peek widget. * Defaults to false. */ definitionLinkOpensInPeek?: boolean; /** * Controls strikethrough deprecated variables. */ showDeprecated?: boolean; /** * Controls whether suggestions allow matches in the middle of the word instead of only at the beginning */ matchOnWordStartOnly?: boolean; /** * Control the behavior and rendering of the inline hints. */ inlayHints?: IEditorInlayHintsOptions; /** * Control if the editor should use shadow DOM. */ useShadowDOM?: boolean; /** * Controls the behavior of editor guides. */ guides?: IGuidesOptions; /** * Controls the behavior of the unicode highlight feature * (by default, ambiguous and invisible characters are highlighted). */ unicodeHighlight?: IUnicodeHighlightOptions; /** * Configures bracket pair colorization (disabled by default). */ bracketPairColorization?: IBracketPairColorizationOptions; /** * Controls dropping into the editor from an external source. * * When enabled, this shows a preview of the drop location and triggers an `onDropIntoEditor` event. */ dropIntoEditor?: IDropIntoEditorOptions; /** * Sets whether the new experimental edit context should be used instead of the text area. */ editContext?: boolean; /** * Controls whether to render rich HTML screen reader content when the EditContext is enabled */ renderRichScreenReaderContent?: boolean; /** * Controls support for changing how content is pasted into the editor. */ pasteAs?: IPasteAsOptions; /** * Controls whether the editor / terminal receives tabs or defers them to the workbench for navigation. */ tabFocusMode?: boolean; /** * Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown. */ inlineCompletionsAccessibilityVerbose?: boolean; } /** * @internal * The width of the minimap gutter, in pixels. */ export const MINIMAP_GUTTER_WIDTH = 8; export type DiffEditorViewMode = 'inline' | 'sideBySide' | 'automatic'; export interface IDiffEditorBaseOptions { /** * Allow the user to resize the diff editor split view. * Defaults to true. */ enableSplitViewResizing?: boolean; /** * The default ratio when rendering side-by-side editors. * Must be a number between 0 and 1, min sizes apply. * Defaults to 0.5 */ splitViewDefaultRatio?: number; /** * Render the differences in two side-by-side editors. * Defaults to true. */ renderSideBySide?: boolean; /** * When `renderSideBySide` is enabled, `useInlineViewWhenSpaceIsLimited` is set, * and the diff editor has a width less than `renderSideBySideInlineBreakpoint`, the inline view is used. */ renderSideBySideInlineBreakpoint?: number | undefined; /** * When `renderSideBySide` is enabled, `useInlineViewWhenSpaceIsLimited` is set, * and the diff editor has a width less than `renderSideBySideInlineBreakpoint`, the inline view is used. */ useInlineViewWhenSpaceIsLimited?: boolean; /** * If set, the diff editor is optimized for small views. * Defaults to `false`. */ compactMode?: boolean; /** * If set, the original editor's line numbers are hidden in the inline view. * Defaults to `false`. * @internal */ hideOriginalLineNumbers?: boolean; /** * Timeout in milliseconds after which diff computation is cancelled. * Defaults to 5000. */ maxComputationTime?: number; /** * Maximum supported file size in MB. * Defaults to 50. */ maxFileSize?: number; /** * Compute the diff by ignoring leading/trailing whitespace * Defaults to true. */ ignoreTrimWhitespace?: boolean; /** * Render +/- indicators for added/deleted changes. * Defaults to true. */ renderIndicators?: boolean; /** * Shows icons in the glyph margin to revert changes. * Default to true. */ renderMarginRevertIcon?: boolean; /** * Indicates if the gutter menu should be rendered. */ renderGutterMenu?: boolean; /** * Original model should be editable? * Defaults to false. */ originalEditable?: boolean; /** * Should the diff editor enable code lens? * Defaults to false. */ diffCodeLens?: boolean; /** * Is the diff editor should render overview ruler * Defaults to true */ renderOverviewRuler?: boolean; /** * Control the wrapping of the diff editor. */ diffWordWrap?: 'off' | 'on' | 'inherit'; /** * Diff Algorithm */ diffAlgorithm?: 'legacy' | 'advanced' | 'advanced-external' | 'advanced-wasm'; /** * Whether the diff editor aria label should be verbose. */ accessibilityVerbose?: boolean; experimental?: { /** * Defaults to false. */ showMoves?: boolean; showEmptyDecorations?: boolean; /** * Only applies when `renderSideBySide` is set to false. */ useTrueInlineView?: boolean; }; /** * Is the diff editor inside another editor * Defaults to false */ isInEmbeddedEditor?: boolean; /** * If the diff editor should only show the difference review mode. */ onlyShowAccessibleDiffViewer?: boolean; hideUnchangedRegions?: { enabled?: boolean; revealLineCount?: number; minimumLineCount?: number; contextLineCount?: number; }; } /** * Configuration options for the diff editor. */ export interface IDiffEditorOptions extends IEditorOptions, IDiffEditorBaseOptions { } /** * @internal */ export type ValidDiffEditorBaseOptions = Readonly>; //#endregion /** * An event describing that the configuration of the editor has changed. */ export class ConfigurationChangedEvent { private readonly _values: boolean[]; /** * @internal */ constructor(values: boolean[]) { this._values = values; } public hasChanged(id: EditorOption): boolean { return this._values[id]; } } /** * All computed editor options. */ export interface IComputedEditorOptions { get(id: T): FindComputedEditorOptionValueById; } //#region IEditorOption /** * @internal */ export interface IEnvironmentalOptions { readonly memory: ComputeOptionsMemory | null; readonly outerWidth: number; readonly outerHeight: number; readonly fontInfo: FontInfo; readonly extraEditorClassName: string; readonly isDominatedByLongLines: boolean; readonly viewLineCount: number; readonly lineNumbersDigitCount: number; readonly emptySelectionClipboard: boolean; readonly pixelRatio: number; readonly tabFocusMode: boolean; readonly inputMode: 'insert' | 'overtype'; readonly accessibilitySupport: AccessibilitySupport; readonly glyphMarginDecorationLaneCount: number; readonly editContextSupported: boolean; } /** * @internal */ export class ComputeOptionsMemory { public stableMinimapLayoutInput: IMinimapLayoutInput | null; public stableFitMaxMinimapScale: number; public stableFitRemainingWidth: number; constructor() { this.stableMinimapLayoutInput = null; this.stableFitMaxMinimapScale = 0; this.stableFitRemainingWidth = 0; } } export interface IEditorOption { readonly id: K; readonly name: string; defaultValue: V; /** * @internal */ readonly schema: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema } | undefined; /** * @internal */ validate(input: unknown): V; /** * @internal */ compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: V): V; /** * Might modify `value`. */ applyUpdate(value: V | undefined, update: V): ApplyUpdateResult; } /** * @internal */ type PossibleKeyName0 = { [K in keyof IEditorOptions]: IEditorOptions[K] extends V | undefined ? K : never }[keyof IEditorOptions]; /** * @internal */ type PossibleKeyName = NonNullable>; /** * @internal */ abstract class BaseEditorOption implements IEditorOption { public readonly id: K; public readonly name: string; public readonly defaultValue: V; public readonly schema: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema } | undefined; constructor(id: K, name: PossibleKeyName, defaultValue: V, schema?: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema }) { this.id = id; this.name = name; this.defaultValue = defaultValue; this.schema = schema; } public applyUpdate(value: V | undefined, update: V): ApplyUpdateResult { return applyUpdate(value, update); } public abstract validate(input: unknown): V; public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: V): V { return value; } } export class ApplyUpdateResult { constructor( public readonly newValue: T, public readonly didChange: boolean ) { } } function applyUpdate(value: T | undefined, update: T): ApplyUpdateResult { if (typeof value !== 'object' || typeof update !== 'object' || !value || !update) { return new ApplyUpdateResult(update, value !== update); } if (Array.isArray(value) || Array.isArray(update)) { const arrayEquals = Array.isArray(value) && Array.isArray(update) && arrays.equals(value, update); return new ApplyUpdateResult(update, !arrayEquals); } let didChange = false; for (const key in update) { if (update.hasOwnProperty(key)) { const result = applyUpdate(value[key], update[key]); if (result.didChange) { value[key] = result.newValue; didChange = true; } } } return new ApplyUpdateResult(value, didChange); } /** * @internal */ abstract class ComputedEditorOption implements IEditorOption { public readonly id: K; public readonly name: '_never_'; public readonly defaultValue: V; public readonly schema: IConfigurationPropertySchema | undefined = undefined; constructor(id: K, defaultValue: V) { this.id = id; this.name = '_never_'; this.defaultValue = defaultValue; } public applyUpdate(value: V | undefined, update: V): ApplyUpdateResult { return applyUpdate(value, update); } public validate(input: unknown): V { return this.defaultValue; } public abstract compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: V): V; } abstract class SimpleEditorOption implements IEditorOption { public readonly id: K; public readonly name: PossibleKeyName; public readonly defaultValue: V; public readonly schema: IConfigurationPropertySchema | undefined; constructor(id: K, name: PossibleKeyName, defaultValue: V, schema?: IConfigurationPropertySchema) { this.id = id; this.name = name; this.defaultValue = defaultValue; this.schema = schema; } public applyUpdate(value: V | undefined, update: V): ApplyUpdateResult { return applyUpdate(value, update); } public abstract validate(input: unknown): V; public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: V): V { return value; } } /** * @internal */ export function boolean(value: unknown, defaultValue: boolean): boolean { if (typeof value === 'undefined') { return defaultValue; } if (value === 'false') { // treat the string 'false' as false return false; } return Boolean(value); } class EditorBooleanOption extends SimpleEditorOption { constructor(id: K, name: PossibleKeyName, defaultValue: boolean, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { schema.type = 'boolean'; schema.default = defaultValue; } super(id, name, defaultValue, schema); } public override validate(input: unknown): boolean { return boolean(input, this.defaultValue); } } /** * @internal */ export function clampedInt(value: unknown, defaultValue: T, minimum: number, maximum: number): number | T { if (typeof value === 'string') { value = parseInt(value, 10); } if (typeof value !== 'number' || isNaN(value)) { return defaultValue; } let r = value; r = Math.max(minimum, r); r = Math.min(maximum, r); return r | 0; } class EditorIntOption extends SimpleEditorOption { public static clampedInt(value: unknown, defaultValue: T, minimum: number, maximum: number): number | T { return clampedInt(value, defaultValue, minimum, maximum); } public readonly minimum: number; public readonly maximum: number; constructor(id: K, name: PossibleKeyName, defaultValue: number, minimum: number, maximum: number, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { schema.type = 'integer'; schema.default = defaultValue; schema.minimum = minimum; schema.maximum = maximum; } super(id, name, defaultValue, schema); this.minimum = minimum; this.maximum = maximum; } public override validate(input: unknown): number { return EditorIntOption.clampedInt(input, this.defaultValue, this.minimum, this.maximum); } } /** * @internal */ export function clampedFloat(value: unknown, defaultValue: T, minimum: number, maximum: number): number | T { if (typeof value === 'undefined') { return defaultValue; } const r = EditorFloatOption.float(value, defaultValue); return EditorFloatOption.clamp(r, minimum, maximum); } class EditorFloatOption extends SimpleEditorOption { public readonly minimum: number | undefined; public readonly maximum: number | undefined; public static clamp(n: number, min: number, max: number): number { if (n < min) { return min; } if (n > max) { return max; } return n; } public static float(value: unknown, defaultValue: number): number { if (typeof value === 'string') { value = parseFloat(value); } if (typeof value !== 'number' || isNaN(value)) { return defaultValue; } return value; } public readonly validationFn: (value: number) => number; constructor(id: K, name: PossibleKeyName, defaultValue: number, validationFn: (value: number) => number, schema?: IConfigurationPropertySchema, minimum?: number, maximum?: number) { if (typeof schema !== 'undefined') { schema.type = 'number'; schema.default = defaultValue; schema.minimum = minimum; schema.maximum = maximum; } super(id, name, defaultValue, schema); this.validationFn = validationFn; this.minimum = minimum; this.maximum = maximum; } public override validate(input: unknown): number { return this.validationFn(EditorFloatOption.float(input, this.defaultValue)); } } class EditorStringOption extends SimpleEditorOption { public static string(value: unknown, defaultValue: string): string { if (typeof value !== 'string') { return defaultValue; } return value; } constructor(id: K, name: PossibleKeyName, defaultValue: string, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { schema.type = 'string'; schema.default = defaultValue; } super(id, name, defaultValue, schema); } public override validate(input: unknown): string { return EditorStringOption.string(input, this.defaultValue); } } /** * @internal */ export function stringSet(value: unknown, defaultValue: T, allowedValues: ReadonlyArray, renamedValues?: Record): T { if (typeof value !== 'string') { return defaultValue; } if (renamedValues && value in renamedValues) { return renamedValues[value]; } if (allowedValues.indexOf(value as T) === -1) { return defaultValue; } return value as T; } class EditorStringEnumOption extends SimpleEditorOption { private readonly _allowedValues: ReadonlyArray; constructor(id: K, name: PossibleKeyName, defaultValue: V, allowedValues: ReadonlyArray, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { schema.type = 'string'; schema.enum = allowedValues.slice(0); schema.default = defaultValue; } super(id, name, defaultValue, schema); this._allowedValues = allowedValues; } public override validate(input: unknown): V { return stringSet(input, this.defaultValue, this._allowedValues); } } class EditorEnumOption extends BaseEditorOption { private readonly _allowedValues: T[]; private readonly _convert: (value: T) => V; constructor(id: K, name: PossibleKeyName, defaultValue: V, defaultStringValue: string, allowedValues: T[], convert: (value: T) => V, schema: IConfigurationPropertySchema | undefined = undefined) { if (typeof schema !== 'undefined') { schema.type = 'string'; schema.enum = allowedValues; schema.default = defaultStringValue; } super(id, name, defaultValue, schema); this._allowedValues = allowedValues; this._convert = convert; } public validate(input: unknown): V { if (typeof input !== 'string') { return this.defaultValue; } if (this._allowedValues.indexOf(input) === -1) { return this.defaultValue; } return this._convert(input); } } //#endregion //#region autoIndent function _autoIndentFromString(autoIndent: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'): EditorAutoIndentStrategy { switch (autoIndent) { case 'none': return EditorAutoIndentStrategy.None; case 'keep': return EditorAutoIndentStrategy.Keep; case 'brackets': return EditorAutoIndentStrategy.Brackets; case 'advanced': return EditorAutoIndentStrategy.Advanced; case 'full': return EditorAutoIndentStrategy.Full; } } //#endregion //#region accessibilitySupport class EditorAccessibilitySupport extends BaseEditorOption { constructor() { super( EditorOption.accessibilitySupport, 'accessibilitySupport', AccessibilitySupport.Unknown, { type: 'string', enum: ['auto', 'on', 'off'], enumDescriptions: [ nls.localize('accessibilitySupport.auto', "Use platform APIs to detect when a Screen Reader is attached."), nls.localize('accessibilitySupport.on', "Optimize for usage with a Screen Reader."), nls.localize('accessibilitySupport.off', "Assume a screen reader is not attached."), ], default: 'auto', tags: ['accessibility'], description: nls.localize('accessibilitySupport', "Controls if the UI should run in a mode where it is optimized for screen readers.") } ); } public validate(input: unknown): AccessibilitySupport { switch (input) { case 'auto': return AccessibilitySupport.Unknown; case 'off': return AccessibilitySupport.Disabled; case 'on': return AccessibilitySupport.Enabled; } return this.defaultValue; } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: AccessibilitySupport): AccessibilitySupport { if (value === AccessibilitySupport.Unknown) { // The editor reads the `accessibilitySupport` from the environment return env.accessibilitySupport; } return value; } } //#endregion //#region comments /** * Configuration options for editor comments */ export interface IEditorCommentsOptions { /** * Insert a space after the line comment token and inside the block comments tokens. * Defaults to true. */ insertSpace?: boolean; /** * Ignore empty lines when inserting line comments. * Defaults to true. */ ignoreEmptyLines?: boolean; } /** * @internal */ export type EditorCommentsOptions = Readonly>; class EditorComments extends BaseEditorOption { constructor() { const defaults: EditorCommentsOptions = { insertSpace: true, ignoreEmptyLines: true, }; super( EditorOption.comments, 'comments', defaults, { 'editor.comments.insertSpace': { type: 'boolean', default: defaults.insertSpace, description: nls.localize('comments.insertSpace', "Controls whether a space character is inserted when commenting.") }, 'editor.comments.ignoreEmptyLines': { type: 'boolean', default: defaults.ignoreEmptyLines, description: nls.localize('comments.ignoreEmptyLines', 'Controls if empty lines should be ignored with toggle, add or remove actions for line comments.') }, } ); } public validate(_input: unknown): EditorCommentsOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; return { insertSpace: boolean(input.insertSpace, this.defaultValue.insertSpace), ignoreEmptyLines: boolean(input.ignoreEmptyLines, this.defaultValue.ignoreEmptyLines), }; } } //#endregion //#region cursorBlinking /** * The kind of animation in which the editor's cursor should be rendered. */ export const enum TextEditorCursorBlinkingStyle { /** * Hidden */ Hidden = 0, /** * Blinking */ Blink = 1, /** * Blinking with smooth fading */ Smooth = 2, /** * Blinking with prolonged filled state and smooth fading */ Phase = 3, /** * Expand collapse animation on the y axis */ Expand = 4, /** * No-Blinking */ Solid = 5 } /** * @internal */ export function cursorBlinkingStyleFromString(cursorBlinkingStyle: 'blink' | 'smooth' | 'phase' | 'expand' | 'solid'): TextEditorCursorBlinkingStyle { switch (cursorBlinkingStyle) { case 'blink': return TextEditorCursorBlinkingStyle.Blink; case 'smooth': return TextEditorCursorBlinkingStyle.Smooth; case 'phase': return TextEditorCursorBlinkingStyle.Phase; case 'expand': return TextEditorCursorBlinkingStyle.Expand; case 'solid': return TextEditorCursorBlinkingStyle.Solid; } } //#endregion //#region cursorStyle /** * The style in which the editor's cursor should be rendered. */ export enum TextEditorCursorStyle { /** * As a vertical line (sitting between two characters). */ Line = 1, /** * As a block (sitting on top of a character). */ Block = 2, /** * As a horizontal line (sitting under a character). */ Underline = 3, /** * As a thin vertical line (sitting between two characters). */ LineThin = 4, /** * As an outlined block (sitting on top of a character). */ BlockOutline = 5, /** * As a thin horizontal line (sitting under a character). */ UnderlineThin = 6 } /** * @internal */ export function cursorStyleToString(cursorStyle: TextEditorCursorStyle): 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin' { switch (cursorStyle) { case TextEditorCursorStyle.Line: return 'line'; case TextEditorCursorStyle.Block: return 'block'; case TextEditorCursorStyle.Underline: return 'underline'; case TextEditorCursorStyle.LineThin: return 'line-thin'; case TextEditorCursorStyle.BlockOutline: return 'block-outline'; case TextEditorCursorStyle.UnderlineThin: return 'underline-thin'; } } /** * @internal */ export function cursorStyleFromString(cursorStyle: 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin'): TextEditorCursorStyle { switch (cursorStyle) { case 'line': return TextEditorCursorStyle.Line; case 'block': return TextEditorCursorStyle.Block; case 'underline': return TextEditorCursorStyle.Underline; case 'line-thin': return TextEditorCursorStyle.LineThin; case 'block-outline': return TextEditorCursorStyle.BlockOutline; case 'underline-thin': return TextEditorCursorStyle.UnderlineThin; } } //#endregion //#region editorClassName class EditorClassName extends ComputedEditorOption { constructor() { super(EditorOption.editorClassName, ''); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: string): string { const classNames = ['monaco-editor']; if (options.get(EditorOption.extraEditorClassName)) { classNames.push(options.get(EditorOption.extraEditorClassName)); } if (env.extraEditorClassName) { classNames.push(env.extraEditorClassName); } if (options.get(EditorOption.mouseStyle) === 'default') { classNames.push('mouse-default'); } else if (options.get(EditorOption.mouseStyle) === 'copy') { classNames.push('mouse-copy'); } if (options.get(EditorOption.showUnused)) { classNames.push('showUnused'); } if (options.get(EditorOption.showDeprecated)) { classNames.push('showDeprecated'); } return classNames.join(' '); } } //#endregion //#region emptySelectionClipboard class EditorEmptySelectionClipboard extends EditorBooleanOption { constructor() { super( EditorOption.emptySelectionClipboard, 'emptySelectionClipboard', true, { description: nls.localize('emptySelectionClipboard', "Controls whether copying without a selection copies the current line.") } ); } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: boolean): boolean { return value && env.emptySelectionClipboard; } } //#endregion //#region find /** * Configuration options for editor find widget */ export interface IEditorFindOptions { /** * Controls whether the cursor should move to find matches while typing. */ cursorMoveOnType?: boolean; /** * Controls whether the find widget should search as you type. */ findOnType?: boolean; /** * Controls if we seed search string in the Find Widget with editor selection. */ seedSearchStringFromSelection?: 'never' | 'always' | 'selection'; /** * Controls if Find in Selection flag is turned on in the editor. */ autoFindInSelection?: 'never' | 'always' | 'multiline'; /* * Controls whether the Find Widget should add extra lines on top of the editor. */ addExtraSpaceOnTop?: boolean; /** * @internal * Controls if the Find Widget should read or modify the shared find clipboard on macOS */ globalFindClipboard?: boolean; /** * Controls whether the search result and diff result automatically restarts from the beginning (or the end) when no further matches can be found */ loop?: boolean; /** * Controls whether to close the Find Widget after an explicit find navigation command lands on a match. */ closeOnResult?: boolean; /** * @internal * Controls how the find widget search history should be stored */ history?: 'never' | 'workspace'; /** * @internal * Controls how the replace widget search history should be stored */ replaceHistory?: 'never' | 'workspace'; } /** * @internal */ export type EditorFindOptions = Readonly>; class EditorFind extends BaseEditorOption { constructor() { const defaults: EditorFindOptions = { cursorMoveOnType: true, findOnType: true, seedSearchStringFromSelection: 'always', autoFindInSelection: 'never', globalFindClipboard: false, addExtraSpaceOnTop: true, loop: true, closeOnResult: false, history: 'workspace', replaceHistory: 'workspace', }; super( EditorOption.find, 'find', defaults, { 'editor.find.cursorMoveOnType': { type: 'boolean', default: defaults.cursorMoveOnType, description: nls.localize('find.cursorMoveOnType', "Controls whether the cursor should jump to find matches while typing.") }, 'editor.find.seedSearchStringFromSelection': { type: 'string', enum: ['never', 'always', 'selection'], default: defaults.seedSearchStringFromSelection, enumDescriptions: [ nls.localize('editor.find.seedSearchStringFromSelection.never', 'Never seed search string from the editor selection.'), nls.localize('editor.find.seedSearchStringFromSelection.always', 'Always seed search string from the editor selection, including word at cursor position.'), nls.localize('editor.find.seedSearchStringFromSelection.selection', 'Only seed search string from the editor selection.') ], description: nls.localize('find.seedSearchStringFromSelection', "Controls whether the search string in the Find Widget is seeded from the editor selection.") }, 'editor.find.autoFindInSelection': { type: 'string', enum: ['never', 'always', 'multiline'], default: defaults.autoFindInSelection, enumDescriptions: [ nls.localize('editor.find.autoFindInSelection.never', 'Never turn on Find in Selection automatically (default).'), nls.localize('editor.find.autoFindInSelection.always', 'Always turn on Find in Selection automatically.'), nls.localize('editor.find.autoFindInSelection.multiline', 'Turn on Find in Selection automatically when multiple lines of content are selected.') ], description: nls.localize('find.autoFindInSelection', "Controls the condition for turning on Find in Selection automatically.") }, 'editor.find.globalFindClipboard': { type: 'boolean', default: defaults.globalFindClipboard, description: nls.localize('find.globalFindClipboard', "Controls whether the Find Widget should read or modify the shared find clipboard on macOS."), included: platform.isMacintosh }, 'editor.find.addExtraSpaceOnTop': { type: 'boolean', default: defaults.addExtraSpaceOnTop, description: nls.localize('find.addExtraSpaceOnTop', "Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible.") }, 'editor.find.loop': { type: 'boolean', default: defaults.loop, description: nls.localize('find.loop', "Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found.") }, 'editor.find.closeOnResult': { type: 'boolean', default: defaults.closeOnResult, description: nls.localize('find.closeOnResult', "Controls whether the Find Widget closes after an explicit find navigation command lands on a result.") }, 'editor.find.history': { type: 'string', enum: ['never', 'workspace'], default: 'workspace', enumDescriptions: [ nls.localize('editor.find.history.never', 'Do not store search history from the find widget.'), nls.localize('editor.find.history.workspace', 'Store search history across the active workspace'), ], description: nls.localize('find.history', "Controls how the find widget history should be stored") }, 'editor.find.replaceHistory': { type: 'string', enum: ['never', 'workspace'], default: 'workspace', enumDescriptions: [ nls.localize('editor.find.replaceHistory.never', 'Do not store history from the replace widget.'), nls.localize('editor.find.replaceHistory.workspace', 'Store replace history across the active workspace'), ], description: nls.localize('find.replaceHistory', "Controls how the replace widget history should be stored") }, 'editor.find.findOnType': { type: 'boolean', default: defaults.findOnType, description: nls.localize('find.findOnType', "Controls whether the Find Widget should search as you type.") }, } ); } public validate(_input: unknown): EditorFindOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; return { cursorMoveOnType: boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), findOnType: boolean(input.findOnType, this.defaultValue.findOnType), seedSearchStringFromSelection: typeof input.seedSearchStringFromSelection === 'boolean' ? (input.seedSearchStringFromSelection ? 'always' : 'never') : stringSet<'never' | 'always' | 'selection'>(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection, ['never', 'always', 'selection']), autoFindInSelection: typeof input.autoFindInSelection === 'boolean' ? (input.autoFindInSelection ? 'always' : 'never') : stringSet<'never' | 'always' | 'multiline'>(input.autoFindInSelection, this.defaultValue.autoFindInSelection, ['never', 'always', 'multiline']), globalFindClipboard: boolean(input.globalFindClipboard, this.defaultValue.globalFindClipboard), addExtraSpaceOnTop: boolean(input.addExtraSpaceOnTop, this.defaultValue.addExtraSpaceOnTop), loop: boolean(input.loop, this.defaultValue.loop), closeOnResult: boolean(input.closeOnResult, this.defaultValue.closeOnResult), history: stringSet<'never' | 'workspace'>(input.history, this.defaultValue.history, ['never', 'workspace']), replaceHistory: stringSet<'never' | 'workspace'>(input.replaceHistory, this.defaultValue.replaceHistory, ['never', 'workspace']), }; } } //#endregion //#region fontLigatures /** * @internal */ export class EditorFontLigatures extends BaseEditorOption { public static OFF = '"liga" off, "calt" off'; public static ON = '"liga" on, "calt" on'; constructor() { super( EditorOption.fontLigatures, 'fontLigatures', EditorFontLigatures.OFF, { anyOf: [ { type: 'boolean', description: nls.localize('fontLigatures', "Enables/Disables font ligatures ('calt' and 'liga' font features). Change this to a string for fine-grained control of the 'font-feature-settings' CSS property."), }, { type: 'string', description: nls.localize('fontFeatureSettings', "Explicit 'font-feature-settings' CSS property. A boolean can be passed instead if one only needs to turn on/off ligatures.") } ], description: nls.localize('fontLigaturesGeneral', "Configures font ligatures or font features. Can be either a boolean to enable/disable ligatures or a string for the value of the CSS 'font-feature-settings' property."), default: false } ); } public validate(input: unknown): string { if (typeof input === 'undefined') { return this.defaultValue; } if (typeof input === 'string') { if (input === 'false' || input.length === 0) { return EditorFontLigatures.OFF; } if (input === 'true') { return EditorFontLigatures.ON; } return input; } if (Boolean(input)) { return EditorFontLigatures.ON; } return EditorFontLigatures.OFF; } } //#endregion //#region fontVariations /** * @internal */ export class EditorFontVariations extends BaseEditorOption { // Text is laid out using default settings. public static OFF = FONT_VARIATION_OFF; // Translate `fontWeight` config to the `font-variation-settings` CSS property. public static TRANSLATE = FONT_VARIATION_TRANSLATE; constructor() { super( EditorOption.fontVariations, 'fontVariations', EditorFontVariations.OFF, { anyOf: [ { type: 'boolean', description: nls.localize('fontVariations', "Enables/Disables the translation from font-weight to font-variation-settings. Change this to a string for fine-grained control of the 'font-variation-settings' CSS property."), }, { type: 'string', description: nls.localize('fontVariationSettings', "Explicit 'font-variation-settings' CSS property. A boolean can be passed instead if one only needs to translate font-weight to font-variation-settings.") } ], description: nls.localize('fontVariationsGeneral', "Configures font variations. Can be either a boolean to enable/disable the translation from font-weight to font-variation-settings or a string for the value of the CSS 'font-variation-settings' property."), default: false } ); } public validate(input: unknown): string { if (typeof input === 'undefined') { return this.defaultValue; } if (typeof input === 'string') { if (input === 'false') { return EditorFontVariations.OFF; } if (input === 'true') { return EditorFontVariations.TRANSLATE; } return input; } if (Boolean(input)) { return EditorFontVariations.TRANSLATE; } return EditorFontVariations.OFF; } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: string): string { // The value is computed from the fontWeight if it is true. // So take the result from env.fontInfo return env.fontInfo.fontVariationSettings; } } //#endregion //#region fontInfo class EditorFontInfo extends ComputedEditorOption { constructor() { super(EditorOption.fontInfo, new FontInfo({ pixelRatio: 0, fontFamily: '', fontWeight: '', fontSize: 0, fontFeatureSettings: '', fontVariationSettings: '', lineHeight: 0, letterSpacing: 0, isMonospace: false, typicalHalfwidthCharacterWidth: 0, typicalFullwidthCharacterWidth: 0, canUseHalfwidthRightwardsArrow: false, spaceWidth: 0, middotWidth: 0, wsmiddotWidth: 0, maxDigitWidth: 0, }, false)); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: FontInfo): FontInfo { return env.fontInfo; } } //#endregion //#region effectiveCursorStyle class EffectiveCursorStyle extends ComputedEditorOption { constructor() { super(EditorOption.effectiveCursorStyle, TextEditorCursorStyle.Line); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: TextEditorCursorStyle): TextEditorCursorStyle { return env.inputMode === 'overtype' ? options.get(EditorOption.overtypeCursorStyle) : options.get(EditorOption.cursorStyle); } } //#endregion //#region effectiveExperimentalEditContext class EffectiveEditContextEnabled extends ComputedEditorOption { constructor() { super(EditorOption.effectiveEditContext, false); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions): boolean { return env.editContextSupported && options.get(EditorOption.editContext); } } //#endregion //#region effectiveAllowVariableFonts class EffectiveAllowVariableFonts extends ComputedEditorOption { constructor() { super(EditorOption.effectiveAllowVariableFonts, false); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions): boolean { const accessibilitySupport = env.accessibilitySupport; if (accessibilitySupport === AccessibilitySupport.Enabled) { return options.get(EditorOption.allowVariableFontsInAccessibilityMode); } else { return options.get(EditorOption.allowVariableFonts); } } } //#engregion //#region effectiveFullwidthCharacterWidth class EffectiveFullwidthCharacterWidth extends ComputedEditorOption { constructor() { super(EditorOption.effectiveFullwidthCharacterWidth, 'font'); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions): 'font' | 'twoCells' { if (options.get(EditorOption.fullwidthCharacterWidth) === 'twoCells' && env.fontInfo.isMonospace) { return 'twoCells'; } return 'font'; } } //#endregion //#region fontSize class EditorFontSize extends SimpleEditorOption { constructor() { super( EditorOption.fontSize, 'fontSize', EDITOR_FONT_DEFAULTS.fontSize, { type: 'number', minimum: 6, maximum: 100, default: EDITOR_FONT_DEFAULTS.fontSize, description: nls.localize('fontSize', "Controls the font size in pixels.") } ); } public override validate(input: unknown): number { const r = EditorFloatOption.float(input, this.defaultValue); if (r === 0) { return EDITOR_FONT_DEFAULTS.fontSize; } return EditorFloatOption.clamp(r, 6, 100); } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: number): number { // The final fontSize respects the editor zoom level. // So take the result from env.fontInfo return env.fontInfo.fontSize; } } //#endregion //#region fontWeight class EditorFontWeight extends BaseEditorOption { private static SUGGESTION_VALUES = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; private static MINIMUM_VALUE = 1; private static MAXIMUM_VALUE = 1000; constructor() { super( EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight, { anyOf: [ { type: 'number', minimum: EditorFontWeight.MINIMUM_VALUE, maximum: EditorFontWeight.MAXIMUM_VALUE, errorMessage: nls.localize('fontWeightErrorMessage', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.") }, { type: 'string', pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$' }, { enum: EditorFontWeight.SUGGESTION_VALUES } ], default: EDITOR_FONT_DEFAULTS.fontWeight, description: nls.localize('fontWeight', "Controls the font weight. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000.") } ); } public validate(input: unknown): string { if (input === 'normal' || input === 'bold') { return input; } return String(EditorIntOption.clampedInt(input, EDITOR_FONT_DEFAULTS.fontWeight, EditorFontWeight.MINIMUM_VALUE, EditorFontWeight.MAXIMUM_VALUE)); } } //#endregion //#region gotoLocation export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto'; /** * Configuration options for go to location */ export interface IGotoLocationOptions { multiple?: GoToLocationValues; multipleDefinitions?: GoToLocationValues; multipleTypeDefinitions?: GoToLocationValues; multipleDeclarations?: GoToLocationValues; multipleImplementations?: GoToLocationValues; multipleReferences?: GoToLocationValues; multipleTests?: GoToLocationValues; alternativeDefinitionCommand?: string; alternativeTypeDefinitionCommand?: string; alternativeDeclarationCommand?: string; alternativeImplementationCommand?: string; alternativeReferenceCommand?: string; alternativeTestsCommand?: string; } /** * @internal */ export type GoToLocationOptions = Readonly>; class EditorGoToLocation extends BaseEditorOption { constructor() { const defaults: GoToLocationOptions = { multiple: 'peek', multipleDefinitions: 'peek', multipleTypeDefinitions: 'peek', multipleDeclarations: 'peek', multipleImplementations: 'peek', multipleReferences: 'peek', multipleTests: 'peek', alternativeDefinitionCommand: 'editor.action.goToReferences', alternativeTypeDefinitionCommand: 'editor.action.goToReferences', alternativeDeclarationCommand: 'editor.action.goToReferences', alternativeImplementationCommand: '', alternativeReferenceCommand: '', alternativeTestsCommand: '', }; const jsonSubset: IJSONSchema = { type: 'string', enum: ['peek', 'gotoAndPeek', 'goto'], default: defaults.multiple, enumDescriptions: [ nls.localize('editor.gotoLocation.multiple.peek', 'Show Peek view of the results (default)'), nls.localize('editor.gotoLocation.multiple.gotoAndPeek', 'Go to the primary result and show a Peek view'), nls.localize('editor.gotoLocation.multiple.goto', 'Go to the primary result and enable Peek-less navigation to others') ] }; const alternativeCommandOptions = ['', 'editor.action.referenceSearch.trigger', 'editor.action.goToReferences', 'editor.action.peekImplementation', 'editor.action.goToImplementation', 'editor.action.peekTypeDefinition', 'editor.action.goToTypeDefinition', 'editor.action.peekDeclaration', 'editor.action.revealDeclaration', 'editor.action.peekDefinition', 'editor.action.revealDefinitionAside', 'editor.action.revealDefinition']; super( EditorOption.gotoLocation, 'gotoLocation', defaults, { 'editor.gotoLocation.multiple': { deprecationMessage: nls.localize('editor.gotoLocation.multiple.deprecated', "This setting is deprecated, please use separate settings like 'editor.editor.gotoLocation.multipleDefinitions' or 'editor.editor.gotoLocation.multipleImplementations' instead."), }, 'editor.gotoLocation.multipleDefinitions': { description: nls.localize('editor.editor.gotoLocation.multipleDefinitions', "Controls the behavior the 'Go to Definition'-command when multiple target locations exist."), ...jsonSubset, }, 'editor.gotoLocation.multipleTypeDefinitions': { description: nls.localize('editor.editor.gotoLocation.multipleTypeDefinitions', "Controls the behavior the 'Go to Type Definition'-command when multiple target locations exist."), ...jsonSubset, }, 'editor.gotoLocation.multipleDeclarations': { description: nls.localize('editor.editor.gotoLocation.multipleDeclarations', "Controls the behavior the 'Go to Declaration'-command when multiple target locations exist."), ...jsonSubset, }, 'editor.gotoLocation.multipleImplementations': { description: nls.localize('editor.editor.gotoLocation.multipleImplemenattions', "Controls the behavior the 'Go to Implementations'-command when multiple target locations exist."), ...jsonSubset, }, 'editor.gotoLocation.multipleReferences': { description: nls.localize('editor.editor.gotoLocation.multipleReferences', "Controls the behavior the 'Go to References'-command when multiple target locations exist."), ...jsonSubset, }, 'editor.gotoLocation.alternativeDefinitionCommand': { type: 'string', default: defaults.alternativeDefinitionCommand, enum: alternativeCommandOptions, description: nls.localize('alternativeDefinitionCommand', "Alternative command id that is being executed when the result of 'Go to Definition' is the current location.") }, 'editor.gotoLocation.alternativeTypeDefinitionCommand': { type: 'string', default: defaults.alternativeTypeDefinitionCommand, enum: alternativeCommandOptions, description: nls.localize('alternativeTypeDefinitionCommand', "Alternative command id that is being executed when the result of 'Go to Type Definition' is the current location.") }, 'editor.gotoLocation.alternativeDeclarationCommand': { type: 'string', default: defaults.alternativeDeclarationCommand, enum: alternativeCommandOptions, description: nls.localize('alternativeDeclarationCommand', "Alternative command id that is being executed when the result of 'Go to Declaration' is the current location.") }, 'editor.gotoLocation.alternativeImplementationCommand': { type: 'string', default: defaults.alternativeImplementationCommand, enum: alternativeCommandOptions, description: nls.localize('alternativeImplementationCommand', "Alternative command id that is being executed when the result of 'Go to Implementation' is the current location.") }, 'editor.gotoLocation.alternativeReferenceCommand': { type: 'string', default: defaults.alternativeReferenceCommand, enum: alternativeCommandOptions, description: nls.localize('alternativeReferenceCommand', "Alternative command id that is being executed when the result of 'Go to Reference' is the current location.") }, } ); } public validate(_input: unknown): GoToLocationOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; return { multiple: stringSet(input.multiple, this.defaultValue.multiple, ['peek', 'gotoAndPeek', 'goto']), multipleDefinitions: stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleTypeDefinitions: stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleDeclarations: stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleImplementations: stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleReferences: stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleTests: stringSet(input.multipleTests, 'peek', ['peek', 'gotoAndPeek', 'goto']), alternativeDefinitionCommand: EditorStringOption.string(input.alternativeDefinitionCommand, this.defaultValue.alternativeDefinitionCommand), alternativeTypeDefinitionCommand: EditorStringOption.string(input.alternativeTypeDefinitionCommand, this.defaultValue.alternativeTypeDefinitionCommand), alternativeDeclarationCommand: EditorStringOption.string(input.alternativeDeclarationCommand, this.defaultValue.alternativeDeclarationCommand), alternativeImplementationCommand: EditorStringOption.string(input.alternativeImplementationCommand, this.defaultValue.alternativeImplementationCommand), alternativeReferenceCommand: EditorStringOption.string(input.alternativeReferenceCommand, this.defaultValue.alternativeReferenceCommand), alternativeTestsCommand: EditorStringOption.string(input.alternativeTestsCommand, this.defaultValue.alternativeTestsCommand), }; } } //#endregion //#region hover /** * Configuration options for editor hover */ export interface IEditorHoverOptions { /** * Enable the hover. * Defaults to 'on'. */ enabled?: 'on' | 'off' | 'onKeyboardModifier'; /** * Delay for showing the hover. * Defaults to 300. */ delay?: number; /** * Is the hover sticky such that it can be clicked and its contents selected? * Defaults to true. */ sticky?: boolean; /** * Controls how long the hover is visible after you hovered out of it. * Require sticky setting to be true. */ hidingDelay?: number; /** * Should the hover be shown above the line if possible? * Defaults to false. */ above?: boolean; /** * Should long line warning hovers be shown (tokenization skipped, rendering paused)? * Defaults to true. */ showLongLineWarning?: boolean; } /** * @internal */ export type EditorHoverOptions = Readonly>; class EditorHover extends BaseEditorOption { constructor() { const defaults: EditorHoverOptions = { enabled: 'on', delay: 300, hidingDelay: 300, sticky: true, above: true, showLongLineWarning: true, }; super( EditorOption.hover, 'hover', defaults, { 'editor.hover.enabled': { type: 'string', enum: ['on', 'off', 'onKeyboardModifier'], default: defaults.enabled, markdownEnumDescriptions: [ nls.localize('hover.enabled.on', "Hover is enabled."), nls.localize('hover.enabled.off', "Hover is disabled."), nls.localize('hover.enabled.onKeyboardModifier', "Hover is shown when holding `{0}` or `Alt` (the opposite modifier of `#editor.multiCursorModifier#`)", platform.isMacintosh ? `Command` : `Control`) ], description: nls.localize('hover.enabled', "Controls whether the hover is shown."), keywords: ['hint', 'info', 'tooltip'] }, 'editor.hover.delay': { type: 'number', default: defaults.delay, minimum: 0, maximum: 10000, description: nls.localize('hover.delay', "Controls the delay in milliseconds after which the hover is shown.") }, 'editor.hover.sticky': { type: 'boolean', default: defaults.sticky, description: nls.localize('hover.sticky', "Controls whether the hover should remain visible when mouse is moved over it.") }, 'editor.hover.hidingDelay': { type: 'integer', minimum: 0, default: defaults.hidingDelay, markdownDescription: nls.localize('hover.hidingDelay', "Controls the delay in milliseconds after which the hover is hidden. Requires `#editor.hover.sticky#` to be enabled.") }, 'editor.hover.above': { type: 'boolean', default: defaults.above, description: nls.localize('hover.above', "Prefer showing hovers above the line, if there's space.") }, 'editor.hover.showLongLineWarning': { type: 'boolean', default: defaults.showLongLineWarning, description: nls.localize('hover.showLongLineWarning', "Controls whether long line warning hovers are shown, such as when tokenization is skipped or rendering is paused.") }, } ); } public validate(_input: unknown): EditorHoverOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; return { enabled: stringSet<'on' | 'off' | 'onKeyboardModifier'>(input.enabled, this.defaultValue.enabled, ['on', 'off', 'onKeyboardModifier']), delay: EditorIntOption.clampedInt(input.delay, this.defaultValue.delay, 0, 10000), sticky: boolean(input.sticky, this.defaultValue.sticky), hidingDelay: EditorIntOption.clampedInt(input.hidingDelay, this.defaultValue.hidingDelay, 0, 600000), above: boolean(input.above, this.defaultValue.above), showLongLineWarning: boolean(input.showLongLineWarning, this.defaultValue.showLongLineWarning), }; } } //#endregion //#region layoutInfo /** * A description for the overview ruler position. */ export interface OverviewRulerPosition { /** * Width of the overview ruler */ readonly width: number; /** * Height of the overview ruler */ readonly height: number; /** * Top position for the overview ruler */ readonly top: number; /** * Right position for the overview ruler */ readonly right: number; } export const enum RenderMinimap { None = 0, Text = 1, Blocks = 2, } /** * The internal layout details of the editor. */ export interface EditorLayoutInfo { /** * Full editor width. */ readonly width: number; /** * Full editor height. */ readonly height: number; /** * Left position for the glyph margin. */ readonly glyphMarginLeft: number; /** * The width of the glyph margin. */ readonly glyphMarginWidth: number; /** * The number of decoration lanes to render in the glyph margin. */ readonly glyphMarginDecorationLaneCount: number; /** * Left position for the line numbers. */ readonly lineNumbersLeft: number; /** * The width of the line numbers. */ readonly lineNumbersWidth: number; /** * Left position for the line decorations. */ readonly decorationsLeft: number; /** * The width of the line decorations. */ readonly decorationsWidth: number; /** * Left position for the content (actual text) */ readonly contentLeft: number; /** * The width of the content (actual text) */ readonly contentWidth: number; /** * Layout information for the minimap */ readonly minimap: EditorMinimapLayoutInfo; /** * The number of columns (of typical characters) fitting on a viewport line. */ readonly viewportColumn: number; readonly isWordWrapMinified: boolean; readonly isViewportWrapping: boolean; readonly wrappingColumn: number; /** * The width of the vertical scrollbar. */ readonly verticalScrollbarWidth: number; /** * The height of the horizontal scrollbar. */ readonly horizontalScrollbarHeight: number; /** * The position of the overview ruler. */ readonly overviewRuler: OverviewRulerPosition; } /** * The internal layout details of the editor. */ export interface EditorMinimapLayoutInfo { readonly renderMinimap: RenderMinimap; readonly minimapLeft: number; readonly minimapWidth: number; readonly minimapHeightIsEditorHeight: boolean; readonly minimapIsSampling: boolean; readonly minimapScale: number; readonly minimapLineHeight: number; readonly minimapCanvasInnerWidth: number; readonly minimapCanvasInnerHeight: number; readonly minimapCanvasOuterWidth: number; readonly minimapCanvasOuterHeight: number; } /** * @internal */ export interface EditorLayoutInfoComputerEnv { readonly memory: ComputeOptionsMemory | null; readonly outerWidth: number; readonly outerHeight: number; readonly isDominatedByLongLines: boolean; readonly lineHeight: number; readonly viewLineCount: number; readonly lineNumbersDigitCount: number; readonly typicalHalfwidthCharacterWidth: number; readonly maxDigitWidth: number; readonly pixelRatio: number; readonly glyphMarginDecorationLaneCount: number; } /** * @internal */ export interface IEditorLayoutComputerInput { readonly outerWidth: number; readonly outerHeight: number; readonly isDominatedByLongLines: boolean; readonly lineHeight: number; readonly lineNumbersDigitCount: number; readonly typicalHalfwidthCharacterWidth: number; readonly maxDigitWidth: number; readonly pixelRatio: number; readonly glyphMargin: boolean; readonly lineDecorationsWidth: string | number; readonly folding: boolean; readonly minimap: Readonly>; readonly scrollbar: InternalEditorScrollbarOptions; readonly lineNumbers: InternalEditorRenderLineNumbersOptions; readonly lineNumbersMinChars: number; readonly scrollBeyondLastLine: boolean; readonly wordWrap: 'wordWrapColumn' | 'on' | 'off' | 'bounded'; readonly wordWrapColumn: number; readonly wordWrapMinified: boolean; readonly accessibilitySupport: AccessibilitySupport; } /** * @internal */ export interface IMinimapLayoutInput { readonly outerWidth: number; readonly outerHeight: number; readonly lineHeight: number; readonly typicalHalfwidthCharacterWidth: number; readonly pixelRatio: number; readonly scrollBeyondLastLine: boolean; readonly paddingTop: number; readonly paddingBottom: number; readonly minimap: Readonly>; readonly verticalScrollbarWidth: number; readonly viewLineCount: number; readonly remainingWidth: number; readonly isViewportWrapping: boolean; } /** * @internal */ export class EditorLayoutInfoComputer extends ComputedEditorOption { constructor() { super(EditorOption.layoutInfo, { width: 0, height: 0, glyphMarginLeft: 0, glyphMarginWidth: 0, glyphMarginDecorationLaneCount: 0, lineNumbersLeft: 0, lineNumbersWidth: 0, decorationsLeft: 0, decorationsWidth: 0, contentLeft: 0, contentWidth: 0, minimap: { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, minimapCanvasInnerWidth: 0, minimapCanvasInnerHeight: 0, minimapCanvasOuterWidth: 0, minimapCanvasOuterHeight: 0, }, viewportColumn: 0, isWordWrapMinified: false, isViewportWrapping: false, wrappingColumn: -1, verticalScrollbarWidth: 0, horizontalScrollbarHeight: 0, overviewRuler: { top: 0, width: 0, height: 0, right: 0 } }); } public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: EditorLayoutInfo): EditorLayoutInfo { return EditorLayoutInfoComputer.computeLayout(options, { memory: env.memory, outerWidth: env.outerWidth, outerHeight: env.outerHeight, isDominatedByLongLines: env.isDominatedByLongLines, lineHeight: env.fontInfo.lineHeight, viewLineCount: env.viewLineCount, lineNumbersDigitCount: env.lineNumbersDigitCount, typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth, maxDigitWidth: env.fontInfo.maxDigitWidth, pixelRatio: env.pixelRatio, glyphMarginDecorationLaneCount: env.glyphMarginDecorationLaneCount }); } public static computeContainedMinimapLineCount(input: { viewLineCount: number; scrollBeyondLastLine: boolean; paddingTop: number; paddingBottom: number; height: number; lineHeight: number; pixelRatio: number; }): { typicalViewportLineCount: number; extraLinesBeforeFirstLine: number; extraLinesBeyondLastLine: number; desiredRatio: number; minimapLineCount: number } { const typicalViewportLineCount = input.height / input.lineHeight; const extraLinesBeforeFirstLine = Math.floor(input.paddingTop / input.lineHeight); let extraLinesBeyondLastLine = Math.floor(input.paddingBottom / input.lineHeight); if (input.scrollBeyondLastLine) { extraLinesBeyondLastLine = Math.max(extraLinesBeyondLastLine, typicalViewportLineCount - 1); } const desiredRatio = (extraLinesBeforeFirstLine + input.viewLineCount + extraLinesBeyondLastLine) / (input.pixelRatio * input.height); const minimapLineCount = Math.floor(input.viewLineCount / desiredRatio); return { typicalViewportLineCount, extraLinesBeforeFirstLine, extraLinesBeyondLastLine, desiredRatio, minimapLineCount }; } private static _computeMinimapLayout(input: IMinimapLayoutInput, memory: ComputeOptionsMemory): EditorMinimapLayoutInfo { const outerWidth = input.outerWidth; const outerHeight = input.outerHeight; const pixelRatio = input.pixelRatio; if (!input.minimap.enabled) { return { renderMinimap: RenderMinimap.None, minimapLeft: 0, minimapWidth: 0, minimapHeightIsEditorHeight: false, minimapIsSampling: false, minimapScale: 1, minimapLineHeight: 1, minimapCanvasInnerWidth: 0, minimapCanvasInnerHeight: Math.floor(pixelRatio * outerHeight), minimapCanvasOuterWidth: 0, minimapCanvasOuterHeight: outerHeight, }; } // Can use memory if only the `viewLineCount` and `remainingWidth` have changed const stableMinimapLayoutInput = memory.stableMinimapLayoutInput; const couldUseMemory = ( stableMinimapLayoutInput // && input.outerWidth === lastMinimapLayoutInput.outerWidth !!! INTENTIONAL OMITTED && input.outerHeight === stableMinimapLayoutInput.outerHeight && input.lineHeight === stableMinimapLayoutInput.lineHeight && input.typicalHalfwidthCharacterWidth === stableMinimapLayoutInput.typicalHalfwidthCharacterWidth && input.pixelRatio === stableMinimapLayoutInput.pixelRatio && input.scrollBeyondLastLine === stableMinimapLayoutInput.scrollBeyondLastLine && input.paddingTop === stableMinimapLayoutInput.paddingTop && input.paddingBottom === stableMinimapLayoutInput.paddingBottom && input.minimap.enabled === stableMinimapLayoutInput.minimap.enabled && input.minimap.side === stableMinimapLayoutInput.minimap.side && input.minimap.size === stableMinimapLayoutInput.minimap.size && input.minimap.showSlider === stableMinimapLayoutInput.minimap.showSlider && input.minimap.renderCharacters === stableMinimapLayoutInput.minimap.renderCharacters && input.minimap.maxColumn === stableMinimapLayoutInput.minimap.maxColumn && input.minimap.scale === stableMinimapLayoutInput.minimap.scale && input.verticalScrollbarWidth === stableMinimapLayoutInput.verticalScrollbarWidth // && input.viewLineCount === lastMinimapLayoutInput.viewLineCount !!! INTENTIONAL OMITTED // && input.remainingWidth === lastMinimapLayoutInput.remainingWidth !!! INTENTIONAL OMITTED && input.isViewportWrapping === stableMinimapLayoutInput.isViewportWrapping ); const lineHeight = input.lineHeight; const typicalHalfwidthCharacterWidth = input.typicalHalfwidthCharacterWidth; const scrollBeyondLastLine = input.scrollBeyondLastLine; const minimapRenderCharacters = input.minimap.renderCharacters; let minimapScale = (pixelRatio >= 2 ? Math.round(input.minimap.scale * 2) : input.minimap.scale); const minimapMaxColumn = input.minimap.maxColumn; const minimapSize = input.minimap.size; const minimapSide = input.minimap.side; const verticalScrollbarWidth = input.verticalScrollbarWidth; const viewLineCount = input.viewLineCount; const remainingWidth = input.remainingWidth; const isViewportWrapping = input.isViewportWrapping; const baseCharHeight = minimapRenderCharacters ? 2 : 3; let minimapCanvasInnerHeight = Math.floor(pixelRatio * outerHeight); const minimapCanvasOuterHeight = minimapCanvasInnerHeight / pixelRatio; let minimapHeightIsEditorHeight = false; let minimapIsSampling = false; let minimapLineHeight = baseCharHeight * minimapScale; let minimapCharWidth = minimapScale / pixelRatio; let minimapWidthMultiplier: number = 1; if (minimapSize === 'fill' || minimapSize === 'fit') { const { typicalViewportLineCount, extraLinesBeforeFirstLine, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({ viewLineCount: viewLineCount, scrollBeyondLastLine: scrollBeyondLastLine, paddingTop: input.paddingTop, paddingBottom: input.paddingBottom, height: outerHeight, lineHeight: lineHeight, pixelRatio: pixelRatio }); // ratio is intentionally not part of the layout to avoid the layout changing all the time // when doing sampling const ratio = viewLineCount / minimapLineCount; if (ratio > 1) { minimapHeightIsEditorHeight = true; minimapIsSampling = true; minimapScale = 1; minimapLineHeight = 1; minimapCharWidth = minimapScale / pixelRatio; } else { let fitBecomesFill = false; let maxMinimapScale = minimapScale + 1; if (minimapSize === 'fit') { const effectiveMinimapHeight = Math.ceil((extraLinesBeforeFirstLine + viewLineCount + extraLinesBeyondLastLine) * minimapLineHeight); if (isViewportWrapping && couldUseMemory && remainingWidth <= memory.stableFitRemainingWidth) { // There is a loop when using `fit` and viewport wrapping: // - view line count impacts minimap layout // - minimap layout impacts viewport width // - viewport width impacts view line count // To break the loop, once we go to a smaller minimap scale, we try to stick with it. fitBecomesFill = true; maxMinimapScale = memory.stableFitMaxMinimapScale; } else { fitBecomesFill = (effectiveMinimapHeight > minimapCanvasInnerHeight); } } if (minimapSize === 'fill' || fitBecomesFill) { minimapHeightIsEditorHeight = true; const configuredMinimapScale = minimapScale; minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio))); if (isViewportWrapping && couldUseMemory && remainingWidth <= memory.stableFitRemainingWidth) { // There is a loop when using `fill` and viewport wrapping: // - view line count impacts minimap layout // - minimap layout impacts viewport width // - viewport width impacts view line count // To break the loop, once we go to a smaller minimap scale, we try to stick with it. maxMinimapScale = memory.stableFitMaxMinimapScale; } minimapScale = Math.min(maxMinimapScale, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); if (minimapScale > configuredMinimapScale) { minimapWidthMultiplier = Math.min(2, minimapScale / configuredMinimapScale); } minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, extraLinesBeforeFirstLine + viewLineCount + extraLinesBeyondLastLine)) * minimapLineHeight); if (isViewportWrapping) { // remember for next time memory.stableMinimapLayoutInput = input; memory.stableFitRemainingWidth = remainingWidth; memory.stableFitMaxMinimapScale = minimapScale; } else { memory.stableMinimapLayoutInput = null; memory.stableFitRemainingWidth = 0; } } } } // Given: // (leaving 2px for the cursor to have space after the last character) // viewportColumn = (contentWidth - verticalScrollbarWidth - 2) / typicalHalfwidthCharacterWidth // minimapWidth = viewportColumn * minimapCharWidth // contentWidth = remainingWidth - minimapWidth // What are good values for contentWidth and minimapWidth ? // minimapWidth = ((contentWidth - verticalScrollbarWidth - 2) / typicalHalfwidthCharacterWidth) * minimapCharWidth // typicalHalfwidthCharacterWidth * minimapWidth = (contentWidth - verticalScrollbarWidth - 2) * minimapCharWidth // typicalHalfwidthCharacterWidth * minimapWidth = (remainingWidth - minimapWidth - verticalScrollbarWidth - 2) * minimapCharWidth // (typicalHalfwidthCharacterWidth + minimapCharWidth) * minimapWidth = (remainingWidth - verticalScrollbarWidth - 2) * minimapCharWidth // minimapWidth = ((remainingWidth - verticalScrollbarWidth - 2) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth) const minimapMaxWidth = Math.floor(minimapMaxColumn * minimapCharWidth); const minimapWidth = Math.min(minimapMaxWidth, Math.max(0, Math.floor(((remainingWidth - verticalScrollbarWidth - 2) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth))) + MINIMAP_GUTTER_WIDTH); let minimapCanvasInnerWidth = Math.floor(pixelRatio * minimapWidth); const minimapCanvasOuterWidth = minimapCanvasInnerWidth / pixelRatio; minimapCanvasInnerWidth = Math.floor(minimapCanvasInnerWidth * minimapWidthMultiplier); const renderMinimap = (minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks); const minimapLeft = (minimapSide === 'left' ? 0 : (outerWidth - minimapWidth - verticalScrollbarWidth)); return { renderMinimap, minimapLeft, minimapWidth, minimapHeightIsEditorHeight, minimapIsSampling, minimapScale, minimapLineHeight, minimapCanvasInnerWidth, minimapCanvasInnerHeight, minimapCanvasOuterWidth, minimapCanvasOuterHeight, }; } public static computeLayout(options: IComputedEditorOptions, env: EditorLayoutInfoComputerEnv): EditorLayoutInfo { const outerWidth = env.outerWidth | 0; const outerHeight = env.outerHeight | 0; const lineHeight = env.lineHeight | 0; const lineNumbersDigitCount = env.lineNumbersDigitCount | 0; const typicalHalfwidthCharacterWidth = env.typicalHalfwidthCharacterWidth; const maxDigitWidth = env.maxDigitWidth; const pixelRatio = env.pixelRatio; const viewLineCount = env.viewLineCount; const wordWrapOverride2 = options.get(EditorOption.wordWrapOverride2); const wordWrapOverride1 = (wordWrapOverride2 === 'inherit' ? options.get(EditorOption.wordWrapOverride1) : wordWrapOverride2); const wordWrap = (wordWrapOverride1 === 'inherit' ? options.get(EditorOption.wordWrap) : wordWrapOverride1); const wordWrapColumn = options.get(EditorOption.wordWrapColumn); const wordWrapIndicator = options.get(EditorOption.wordWrapIndicator); const isDominatedByLongLines = env.isDominatedByLongLines; const showGlyphMargin = options.get(EditorOption.glyphMargin); const showLineNumbers = (options.get(EditorOption.lineNumbers).renderType !== RenderLineNumbersType.Off); const lineNumbersMinChars = options.get(EditorOption.lineNumbersMinChars); const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); const padding = options.get(EditorOption.padding); const minimap = options.get(EditorOption.minimap); const scrollbar = options.get(EditorOption.scrollbar); const verticalScrollbarWidth = scrollbar.verticalScrollbarSize; const verticalScrollbarHasArrows = scrollbar.verticalHasArrows; const scrollbarArrowSize = scrollbar.arrowSize; const horizontalScrollbarHeight = scrollbar.horizontalScrollbarSize; const folding = options.get(EditorOption.folding); const showFoldingDecoration = options.get(EditorOption.showFoldingControls) !== 'never'; let lineDecorationsWidth = options.get(EditorOption.lineDecorationsWidth); if (folding && showFoldingDecoration) { lineDecorationsWidth += 16; } let lineNumbersWidth = 0; if (showLineNumbers) { const digitCount = Math.max(lineNumbersDigitCount, lineNumbersMinChars); lineNumbersWidth = Math.round(digitCount * maxDigitWidth); } let glyphMarginWidth = 0; if (showGlyphMargin) { glyphMarginWidth = lineHeight * env.glyphMarginDecorationLaneCount; } let glyphMarginLeft = 0; let lineNumbersLeft = glyphMarginLeft + glyphMarginWidth; let decorationsLeft = lineNumbersLeft + lineNumbersWidth; let contentLeft = decorationsLeft + lineDecorationsWidth; const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth; let isWordWrapMinified = false; let isViewportWrapping = false; let wrappingColumn = -1; if (options.get(EditorOption.accessibilitySupport) === AccessibilitySupport.Enabled && wordWrapOverride1 === 'inherit' && isDominatedByLongLines) { // Force viewport width wrapping if model is dominated by long lines isWordWrapMinified = true; isViewportWrapping = true; } else if (wordWrap === 'on' || wordWrap === 'bounded') { isViewportWrapping = true; } else if (wordWrap === 'wordWrapColumn') { wrappingColumn = wordWrapColumn; } const minimapLayout = EditorLayoutInfoComputer._computeMinimapLayout({ outerWidth: outerWidth, outerHeight: outerHeight, lineHeight: lineHeight, typicalHalfwidthCharacterWidth: typicalHalfwidthCharacterWidth, pixelRatio: pixelRatio, scrollBeyondLastLine: scrollBeyondLastLine, paddingTop: padding.top, paddingBottom: padding.bottom, minimap: minimap, verticalScrollbarWidth: verticalScrollbarWidth, viewLineCount: viewLineCount, remainingWidth: remainingWidth, isViewportWrapping: isViewportWrapping, }, env.memory || new ComputeOptionsMemory()); if (minimapLayout.renderMinimap !== RenderMinimap.None && minimapLayout.minimapLeft === 0) { // the minimap is rendered to the left, so move everything to the right glyphMarginLeft += minimapLayout.minimapWidth; lineNumbersLeft += minimapLayout.minimapWidth; decorationsLeft += minimapLayout.minimapWidth; contentLeft += minimapLayout.minimapWidth; } const contentWidth = remainingWidth - minimapLayout.minimapWidth; // (leaving 2px for the cursor to have space after the last character) const viewportColumn = Math.max(1, Math.floor((contentWidth - verticalScrollbarWidth - 2) / typicalHalfwidthCharacterWidth)); const verticalArrowSize = (verticalScrollbarHasArrows ? scrollbarArrowSize : 0); if (isViewportWrapping) { // compute the actual wrappingColumn // (leaving the rightmost column for the word wrap indicator so that it does not overlap // the wrapped text or end up underneath the vertical scrollbar) wrappingColumn = Math.max(1, viewportColumn - (wordWrapIndicator ? 1 : 0)); if (wordWrap === 'bounded') { wrappingColumn = Math.min(wrappingColumn, wordWrapColumn); } } return { width: outerWidth, height: outerHeight, glyphMarginLeft: glyphMarginLeft, glyphMarginWidth: glyphMarginWidth, glyphMarginDecorationLaneCount: env.glyphMarginDecorationLaneCount, lineNumbersLeft: lineNumbersLeft, lineNumbersWidth: lineNumbersWidth, decorationsLeft: decorationsLeft, decorationsWidth: lineDecorationsWidth, contentLeft: contentLeft, contentWidth: contentWidth, minimap: minimapLayout, viewportColumn: viewportColumn, isWordWrapMinified: isWordWrapMinified, isViewportWrapping: isViewportWrapping, wrappingColumn: wrappingColumn, verticalScrollbarWidth: verticalScrollbarWidth, horizontalScrollbarHeight: horizontalScrollbarHeight, overviewRuler: { top: verticalArrowSize, width: verticalScrollbarWidth, height: (outerHeight - 2 * verticalArrowSize), right: 0 } }; } } //#endregion //#region WrappingStrategy class WrappingStrategy extends BaseEditorOption { constructor() { super(EditorOption.wrappingStrategy, 'wrappingStrategy', 'simple', { 'editor.wrappingStrategy': { enumDescriptions: [ nls.localize('wrappingStrategy.simple', "Assumes that all characters are of the same width. This is a fast algorithm that works correctly for monospace fonts and certain scripts (like Latin characters) where glyphs are of equal width."), nls.localize('wrappingStrategy.advanced', "Delegates wrapping points computation to the browser. This is a slow algorithm, that might cause freezes for large files, but it works correctly in all cases.") ], type: 'string', enum: ['simple', 'advanced'], default: 'simple', description: nls.localize('wrappingStrategy', "Controls the algorithm that computes wrapping points. Note that when in accessibility mode, advanced will be used for the best experience.") } } ); } public validate(input: unknown): 'simple' | 'advanced' { return stringSet<'simple' | 'advanced'>(input, 'simple', ['simple', 'advanced']); } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: 'simple' | 'advanced'): 'simple' | 'advanced' { const accessibilitySupport = options.get(EditorOption.accessibilitySupport); if (accessibilitySupport === AccessibilitySupport.Enabled) { // if we know for a fact that a screen reader is attached, we switch our strategy to advanced to // help that the editor's wrapping points match the textarea's wrapping points return 'advanced'; } return value; } } //#endregion //#region lightbulb export enum ShowLightbulbIconMode { Off = 'off', OnCode = 'onCode', On = 'on' } /** * Configuration options for editor lightbulb */ export interface IEditorLightbulbOptions { /** * Enable the lightbulb code action. * The three possible values are `off`, `on` and `onCode` and the default is `onCode`. * `off` disables the code action menu. * `on` shows the code action menu on code and on empty lines. * `onCode` shows the code action menu on code only. */ enabled?: ShowLightbulbIconMode; } /** * @internal */ export type EditorLightbulbOptions = Readonly>; class EditorLightbulb extends BaseEditorOption { constructor() { const defaults: EditorLightbulbOptions = { enabled: ShowLightbulbIconMode.OnCode }; super( EditorOption.lightbulb, 'lightbulb', defaults, { 'editor.lightbulb.enabled': { type: 'string', enum: [ShowLightbulbIconMode.Off, ShowLightbulbIconMode.OnCode, ShowLightbulbIconMode.On], default: defaults.enabled, enumDescriptions: [ nls.localize('editor.lightbulb.enabled.off', 'Disable the code action menu.'), nls.localize('editor.lightbulb.enabled.onCode', 'Show the code action menu when the cursor is on lines with code.'), nls.localize('editor.lightbulb.enabled.on', 'Show the code action menu when the cursor is on lines with code or on empty lines.'), ], description: nls.localize('enabled', "Enables the Code Action lightbulb in the editor.") } } ); } public validate(_input: unknown): EditorLightbulbOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; return { enabled: stringSet(input.enabled, this.defaultValue.enabled, [ShowLightbulbIconMode.Off, ShowLightbulbIconMode.OnCode, ShowLightbulbIconMode.On]) }; } } //#endregion //#region stickyScroll export interface IEditorStickyScrollOptions { /** * Enable the sticky scroll */ enabled?: boolean; /** * Maximum number of sticky lines to show */ maxLineCount?: number; /** * Model to choose for sticky scroll by default */ defaultModel?: 'outlineModel' | 'foldingProviderModel' | 'indentationModel'; /** * Define whether to scroll sticky scroll with editor horizontal scrollbae */ scrollWithEditor?: boolean; } /** * @internal */ export type EditorStickyScrollOptions = Readonly>; class EditorStickyScroll extends BaseEditorOption { constructor() { const defaults: EditorStickyScrollOptions = { enabled: true, maxLineCount: 5, defaultModel: 'outlineModel', scrollWithEditor: true }; super( EditorOption.stickyScroll, 'stickyScroll', defaults, { 'editor.stickyScroll.enabled': { type: 'boolean', default: defaults.enabled, description: nls.localize('editor.stickyScroll.enabled', "Shows the nested current scopes during the scroll at the top of the editor.") }, 'editor.stickyScroll.maxLineCount': { type: 'number', default: defaults.maxLineCount, minimum: 1, maximum: 20, description: nls.localize('editor.stickyScroll.maxLineCount', "Defines the maximum number of sticky lines to show.") }, 'editor.stickyScroll.defaultModel': { type: 'string', enum: ['outlineModel', 'foldingProviderModel', 'indentationModel'], default: defaults.defaultModel, description: nls.localize('editor.stickyScroll.defaultModel', "Defines the model to use for determining which lines to stick. If the outline model does not exist, it will fall back on the folding provider model which falls back on the indentation model. This order is respected in all three cases.") }, 'editor.stickyScroll.scrollWithEditor': { type: 'boolean', default: defaults.scrollWithEditor, description: nls.localize('editor.stickyScroll.scrollWithEditor', "Enable scrolling of Sticky Scroll with the editor's horizontal scrollbar.") }, } ); } public validate(_input: unknown): EditorStickyScrollOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; return { enabled: boolean(input.enabled, this.defaultValue.enabled), maxLineCount: EditorIntOption.clampedInt(input.maxLineCount, this.defaultValue.maxLineCount, 1, 20), defaultModel: stringSet<'outlineModel' | 'foldingProviderModel' | 'indentationModel'>(input.defaultModel, this.defaultValue.defaultModel, ['outlineModel', 'foldingProviderModel', 'indentationModel']), scrollWithEditor: boolean(input.scrollWithEditor, this.defaultValue.scrollWithEditor) }; } } //#endregion //#region inlayHints /** * Configuration options for editor inlayHints */ export interface IEditorInlayHintsOptions { /** * Enable the inline hints. * Defaults to true. */ enabled?: 'on' | 'off' | 'offUnlessPressed' | 'onUnlessPressed'; /** * Font size of inline hints. * Default to 90% of the editor font size. */ fontSize?: number; /** * Font family of inline hints. * Defaults to editor font family. */ fontFamily?: string; /** * Enables the padding around the inlay hint. * Defaults to false. */ padding?: boolean; /** * Maximum length for inlay hints per line * Set to 0 to have an unlimited length. */ maximumLength?: number; } /** * @internal */ export type EditorInlayHintsOptions = Readonly>; class EditorInlayHints extends BaseEditorOption { constructor() { const defaults: EditorInlayHintsOptions = { enabled: 'on', fontSize: 0, fontFamily: '', padding: false, maximumLength: 43 }; super( EditorOption.inlayHints, 'inlayHints', defaults, { 'editor.inlayHints.enabled': { type: 'string', default: defaults.enabled, description: nls.localize('inlayHints.enable', "Enables the inlay hints in the editor."), enum: ['on', 'onUnlessPressed', 'offUnlessPressed', 'off'], markdownEnumDescriptions: [ nls.localize('editor.inlayHints.on', "Inlay hints are enabled"), nls.localize('editor.inlayHints.onUnlessPressed', "Inlay hints are showing by default and hide when holding {0}", platform.isMacintosh ? `Ctrl+Option` : `Ctrl+Alt`), nls.localize('editor.inlayHints.offUnlessPressed', "Inlay hints are hidden by default and show when holding {0}", platform.isMacintosh ? `Ctrl+Option` : `Ctrl+Alt`), nls.localize('editor.inlayHints.off', "Inlay hints are disabled"), ], }, 'editor.inlayHints.fontSize': { type: 'number', default: defaults.fontSize, markdownDescription: nls.localize('inlayHints.fontSize', "Controls font size of inlay hints in the editor. As default the {0} is used when the configured value is less than {1} or greater than the editor font size.", '`#editor.fontSize#`', '`5`') }, 'editor.inlayHints.fontFamily': { type: 'string', default: defaults.fontFamily, markdownDescription: nls.localize('inlayHints.fontFamily', "Controls font family of inlay hints in the editor. When set to empty, the {0} is used.", '`#editor.fontFamily#`') }, 'editor.inlayHints.padding': { type: 'boolean', default: defaults.padding, description: nls.localize('inlayHints.padding', "Enables the padding around the inlay hints in the editor.") }, 'editor.inlayHints.maximumLength': { type: 'number', default: defaults.maximumLength, markdownDescription: nls.localize('inlayHints.maximumLength', "Maximum overall length of inlay hints, for a single line, before they get truncated by the editor. Set to `0` to never truncate") } } ); } public validate(_input: unknown): EditorInlayHintsOptions { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input as Unknown; if (typeof input.enabled === 'boolean') { input.enabled = input.enabled ? 'on' : 'off'; } return { enabled: stringSet<'on' | 'off' | 'offUnlessPressed' | 'onUnlessPressed'>(input.enabled, this.defaultValue.enabled, ['on', 'off', 'offUnlessPressed', 'onUnlessPressed']), fontSize: EditorIntOption.clampedInt(input.fontSize, this.defaultValue.fontSize, 0, 100), fontFamily: EditorStringOption.string(input.fontFamily, this.defaultValue.fontFamily), padding: boolean(input.padding, this.defaultValue.padding), maximumLength: EditorIntOption.clampedInt(input.maximumLength, this.defaultValue.maximumLength, 0, Number.MAX_SAFE_INTEGER), }; } } //#endregion //#region lineDecorationsWidth class EditorLineDecorationsWidth extends BaseEditorOption { constructor() { super(EditorOption.lineDecorationsWidth, 'lineDecorationsWidth', 10); } public validate(input: unknown): number { if (typeof input === 'string' && /^\d+(\.\d+)?ch$/.test(input)) { const multiple = parseFloat(input.substring(0, input.length - 2)); return -multiple; // negative numbers signal a multiple } else { return EditorIntOption.clampedInt(input, this.defaultValue, 0, 1000); } } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: number): number { if (value < 0) { // negative numbers signal a multiple return EditorIntOption.clampedInt(-value * env.fontInfo.typicalHalfwidthCharacterWidth, this.defaultValue, 0, 1000); } else { return value; } } } //#endregion //#region lineHeight class EditorLineHeight extends EditorFloatOption { constructor() { super( EditorOption.lineHeight, 'lineHeight', EDITOR_FONT_DEFAULTS.lineHeight, x => EditorFloatOption.clamp(x, 0, 150), { markdownDescription: nls.localize('lineHeight', "Controls the line height. \n - Use 0 to automatically compute the line height from the font size.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values.") }, 0, 150 ); } public override compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, value: number): number { // The lineHeight is computed from the fontSize if it is 0. // Moreover, the final lineHeight respects the editor zoom level. // So take the result from env.fontInfo return env.fontInfo.lineHeight; } } //#endregion //#region minimap /** * Configuration options for editor minimap */ export interface IEditorMinimapOptions { /** * Enable the rendering of the minimap. * Defaults to true. */ enabled?: boolean; /** * Control the rendering of minimap. */ autohide?: 'none' | 'mouseover' | 'scroll'; /** * Control the side of the minimap in editor. * Defaults to 'right'. */ side?: 'right' | 'left'; /** * Control the minimap rendering mode. * Defaults to 'actual'. */ size?: 'proportional' | 'fill' | 'fit'; /** * Control the rendering of the minimap slider. * Defaults to 'mouseover'. */ showSlider?: 'always' | 'mouseover'; /** * Render the actual text on a line (as opposed to color blocks). * Defaults to true. */ renderCharacters?: boolean; /** * Limit the width of the minimap to render at most a certain number of columns. * Defaults to 120. */ maxColumn?: number; /** * Relative size of the font in the minimap. Defaults to 1. */ scale?: number; /** * Whether to show named regions as section headers. Defaults to true. */ showRegionSectionHeaders?: boolean; /** * Whether to show MARK: comments as section headers. Defaults to true. */ showMarkSectionHeaders?: boolean; /** * When specified, is used to create a custom section header parser regexp. * Must contain a match group named 'label' (written as (?