UNPKG

@21epub/epub-thirdparty

Version:
1,049 lines 133 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as nls from '../../../nls.js'; import * as platform from '../../../base/common/platform.js'; import { USUAL_WORD_SEPARATORS } from '../model/wordHelper.js'; /** * @internal * The width of the minimap gutter, in pixels. */ export const MINIMAP_GUTTER_WIDTH = 8; //#endregion /** * An event describing that the configuration of the editor has changed. */ export class ConfigurationChangedEvent { /** * @internal */ constructor(values) { this._values = values; } hasChanged(id) { return this._values[id]; } } /** * @internal */ export class ValidatedEditorOptions { constructor() { this._values = []; } _read(option) { return this._values[option]; } get(id) { return this._values[id]; } _write(option, value) { this._values[option] = value; } } /** * @internal */ export class ComputeOptionsMemory { constructor() { this.stableMinimapLayoutInput = null; this.stableFitMaxMinimapScale = 0; this.stableFitRemainingWidth = 0; } } /** * @internal */ class BaseEditorOption { constructor(id, name, defaultValue, schema) { this.id = id; this.name = name; this.defaultValue = defaultValue; this.schema = schema; } compute(env, options, value) { return value; } } /** * @internal */ class ComputedEditorOption { constructor(id, deps = null) { this.schema = undefined; this.id = id; this.name = '_never_'; this.defaultValue = undefined; this.deps = deps; } validate(input) { return this.defaultValue; } } class SimpleEditorOption { constructor(id, name, defaultValue, schema) { this.id = id; this.name = name; this.defaultValue = defaultValue; this.schema = schema; } validate(input) { if (typeof input === 'undefined') { return this.defaultValue; } return input; } compute(env, options, value) { return value; } } /** * @internal */ export function boolean(value, defaultValue) { 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, name, defaultValue, schema = undefined) { if (typeof schema !== 'undefined') { schema.type = 'boolean'; schema.default = defaultValue; } super(id, name, defaultValue, schema); } validate(input) { return boolean(input, this.defaultValue); } } /** * @internal */ export function clampedInt(value, defaultValue, minimum, maximum) { if (typeof value === 'undefined') { return defaultValue; } let r = parseInt(value, 10); if (isNaN(r)) { return defaultValue; } r = Math.max(minimum, r); r = Math.min(maximum, r); return r | 0; } class EditorIntOption extends SimpleEditorOption { constructor(id, name, defaultValue, minimum, maximum, schema = 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; } static clampedInt(value, defaultValue, minimum, maximum) { return clampedInt(value, defaultValue, minimum, maximum); } validate(input) { return EditorIntOption.clampedInt(input, this.defaultValue, this.minimum, this.maximum); } } class EditorFloatOption extends SimpleEditorOption { constructor(id, name, defaultValue, validationFn, schema) { if (typeof schema !== 'undefined') { schema.type = 'number'; schema.default = defaultValue; } super(id, name, defaultValue, schema); this.validationFn = validationFn; } static clamp(n, min, max) { if (n < min) { return min; } if (n > max) { return max; } return n; } static float(value, defaultValue) { if (typeof value === 'number') { return value; } if (typeof value === 'undefined') { return defaultValue; } const r = parseFloat(value); return (isNaN(r) ? defaultValue : r); } validate(input) { return this.validationFn(EditorFloatOption.float(input, this.defaultValue)); } } class EditorStringOption extends SimpleEditorOption { static string(value, defaultValue) { if (typeof value !== 'string') { return defaultValue; } return value; } constructor(id, name, defaultValue, schema = undefined) { if (typeof schema !== 'undefined') { schema.type = 'string'; schema.default = defaultValue; } super(id, name, defaultValue, schema); } validate(input) { return EditorStringOption.string(input, this.defaultValue); } } /** * @internal */ export function stringSet(value, defaultValue, allowedValues) { if (typeof value !== 'string') { return defaultValue; } if (allowedValues.indexOf(value) === -1) { return defaultValue; } return value; } class EditorStringEnumOption extends SimpleEditorOption { constructor(id, name, defaultValue, allowedValues, schema = undefined) { if (typeof schema !== 'undefined') { schema.type = 'string'; schema.enum = allowedValues; schema.default = defaultValue; } super(id, name, defaultValue, schema); this._allowedValues = allowedValues; } validate(input) { return stringSet(input, this.defaultValue, this._allowedValues); } } class EditorEnumOption extends BaseEditorOption { constructor(id, name, defaultValue, defaultStringValue, allowedValues, convert, schema = 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; } validate(input) { 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) { switch (autoIndent) { case 'none': return 0 /* None */; case 'keep': return 1 /* Keep */; case 'brackets': return 2 /* Brackets */; case 'advanced': return 3 /* Advanced */; case 'full': return 4 /* Full */; } } //#endregion //#region accessibilitySupport class EditorAccessibilitySupport extends BaseEditorOption { constructor() { super(2 /* accessibilitySupport */, 'accessibilitySupport', 0 /* Unknown */, { type: 'string', enum: ['auto', 'on', 'off'], enumDescriptions: [ nls.localize('accessibilitySupport.auto', "The editor will use platform APIs to detect when a Screen Reader is attached."), nls.localize('accessibilitySupport.on', "The editor will be permanently optimized for usage with a Screen Reader. Word wrapping will be disabled."), nls.localize('accessibilitySupport.off', "The editor will never be optimized for usage with a Screen Reader."), ], default: 'auto', description: nls.localize('accessibilitySupport', "Controls whether the editor should run in a mode where it is optimized for screen readers. Setting to on will disable word wrapping.") }); } validate(input) { switch (input) { case 'auto': return 0 /* Unknown */; case 'off': return 1 /* Disabled */; case 'on': return 2 /* Enabled */; } return this.defaultValue; } compute(env, options, value) { if (value === 0 /* Unknown */) { // The editor reads the `accessibilitySupport` from the environment return env.accessibilitySupport; } return value; } } class EditorComments extends BaseEditorOption { constructor() { const defaults = { insertSpace: true, ignoreEmptyLines: true, }; super(19 /* 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.') }, }); } validate(_input) { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input; return { insertSpace: boolean(input.insertSpace, this.defaultValue.insertSpace), ignoreEmptyLines: boolean(input.ignoreEmptyLines, this.defaultValue.ignoreEmptyLines), }; } } function _cursorBlinkingStyleFromString(cursorBlinkingStyle) { switch (cursorBlinkingStyle) { case 'blink': return 1 /* Blink */; case 'smooth': return 2 /* Smooth */; case 'phase': return 3 /* Phase */; case 'expand': return 4 /* Expand */; case 'solid': return 5 /* Solid */; } } //#endregion //#region cursorStyle /** * The style in which the editor's cursor should be rendered. */ export var TextEditorCursorStyle; (function (TextEditorCursorStyle) { /** * As a vertical line (sitting between two characters). */ TextEditorCursorStyle[TextEditorCursorStyle["Line"] = 1] = "Line"; /** * As a block (sitting on top of a character). */ TextEditorCursorStyle[TextEditorCursorStyle["Block"] = 2] = "Block"; /** * As a horizontal line (sitting under a character). */ TextEditorCursorStyle[TextEditorCursorStyle["Underline"] = 3] = "Underline"; /** * As a thin vertical line (sitting between two characters). */ TextEditorCursorStyle[TextEditorCursorStyle["LineThin"] = 4] = "LineThin"; /** * As an outlined block (sitting on top of a character). */ TextEditorCursorStyle[TextEditorCursorStyle["BlockOutline"] = 5] = "BlockOutline"; /** * As a thin horizontal line (sitting under a character). */ TextEditorCursorStyle[TextEditorCursorStyle["UnderlineThin"] = 6] = "UnderlineThin"; })(TextEditorCursorStyle || (TextEditorCursorStyle = {})); function _cursorStyleFromString(cursorStyle) { 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(126 /* editorClassName */, [65 /* mouseStyle */, 33 /* extraEditorClassName */]); } compute(env, options, _) { const classNames = ['monaco-editor']; if (options.get(33 /* extraEditorClassName */)) { classNames.push(options.get(33 /* extraEditorClassName */)); } if (env.extraEditorClassName) { classNames.push(env.extraEditorClassName); } if (options.get(65 /* mouseStyle */) === 'default') { classNames.push('mouse-default'); } else if (options.get(65 /* mouseStyle */) === 'copy') { classNames.push('mouse-copy'); } if (options.get(99 /* showUnused */)) { classNames.push('showUnused'); } if (options.get(124 /* showDeprecated */)) { classNames.push('showDeprecated'); } return classNames.join(' '); } } //#endregion //#region emptySelectionClipboard class EditorEmptySelectionClipboard extends EditorBooleanOption { constructor() { super(32 /* emptySelectionClipboard */, 'emptySelectionClipboard', true, { description: nls.localize('emptySelectionClipboard', "Controls whether copying without a selection copies the current line.") }); } compute(env, options, value) { return value && env.emptySelectionClipboard; } } class EditorFind extends BaseEditorOption { constructor() { const defaults = { cursorMoveOnType: true, seedSearchStringFromSelection: 'always', autoFindInSelection: 'never', globalFindClipboard: false, addExtraSpaceOnTop: true, loop: true }; super(35 /* 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.") }, }); } validate(_input) { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input; return { cursorMoveOnType: boolean(input.cursorMoveOnType, this.defaultValue.cursorMoveOnType), seedSearchStringFromSelection: typeof _input.seedSearchStringFromSelection === 'boolean' ? (_input.seedSearchStringFromSelection ? 'always' : 'never') : stringSet(input.seedSearchStringFromSelection, this.defaultValue.seedSearchStringFromSelection, ['never', 'always', 'selection']), autoFindInSelection: typeof _input.autoFindInSelection === 'boolean' ? (_input.autoFindInSelection ? 'always' : 'never') : stringSet(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), }; } } //#endregion //#region fontLigatures /** * @internal */ export class EditorFontLigatures extends BaseEditorOption { constructor() { super(44 /* 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 }); } validate(input) { if (typeof input === 'undefined') { return this.defaultValue; } if (typeof input === 'string') { if (input === 'false') { return EditorFontLigatures.OFF; } if (input === 'true') { return EditorFontLigatures.ON; } return input; } if (Boolean(input)) { return EditorFontLigatures.ON; } return EditorFontLigatures.OFF; } } EditorFontLigatures.OFF = '"liga" off, "calt" off'; EditorFontLigatures.ON = '"liga" on, "calt" on'; //#endregion //#region fontInfo class EditorFontInfo extends ComputedEditorOption { constructor() { super(43 /* fontInfo */); } compute(env, options, _) { return env.fontInfo; } } //#endregion //#region fontSize class EditorFontSize extends SimpleEditorOption { constructor() { super(45 /* 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.") }); } validate(input) { let r = EditorFloatOption.float(input, this.defaultValue); if (r === 0) { return EDITOR_FONT_DEFAULTS.fontSize; } return EditorFloatOption.clamp(r, 6, 100); } compute(env, options, value) { // 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 { constructor() { super(46 /* 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.") }); } validate(input) { if (input === 'normal' || input === 'bold') { return input; } return String(EditorIntOption.clampedInt(input, EDITOR_FONT_DEFAULTS.fontWeight, EditorFontWeight.MINIMUM_VALUE, EditorFontWeight.MAXIMUM_VALUE)); } } EditorFontWeight.SUGGESTION_VALUES = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; EditorFontWeight.MINIMUM_VALUE = 1; EditorFontWeight.MAXIMUM_VALUE = 1000; class EditorGoToLocation extends BaseEditorOption { constructor() { const defaults = { multiple: 'peek', multipleDefinitions: 'peek', multipleTypeDefinitions: 'peek', multipleDeclarations: 'peek', multipleImplementations: 'peek', multipleReferences: 'peek', alternativeDefinitionCommand: 'editor.action.goToReferences', alternativeTypeDefinitionCommand: 'editor.action.goToReferences', alternativeDeclarationCommand: 'editor.action.goToReferences', alternativeImplementationCommand: '', alternativeReferenceCommand: '', }; const jsonSubset = { 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(50 /* 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': Object.assign({ description: nls.localize('editor.editor.gotoLocation.multipleDefinitions', "Controls the behavior the 'Go to Definition'-command when multiple target locations exist.") }, jsonSubset), 'editor.gotoLocation.multipleTypeDefinitions': Object.assign({ 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': Object.assign({ description: nls.localize('editor.editor.gotoLocation.multipleDeclarations', "Controls the behavior the 'Go to Declaration'-command when multiple target locations exist.") }, jsonSubset), 'editor.gotoLocation.multipleImplementations': Object.assign({ description: nls.localize('editor.editor.gotoLocation.multipleImplemenattions', "Controls the behavior the 'Go to Implementations'-command when multiple target locations exist.") }, jsonSubset), 'editor.gotoLocation.multipleReferences': Object.assign({ 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.") }, }); } validate(_input) { var _a, _b, _c, _d, _e; if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input; return { multiple: stringSet(input.multiple, this.defaultValue.multiple, ['peek', 'gotoAndPeek', 'goto']), multipleDefinitions: (_a = input.multipleDefinitions) !== null && _a !== void 0 ? _a : stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleTypeDefinitions: (_b = input.multipleTypeDefinitions) !== null && _b !== void 0 ? _b : stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleDeclarations: (_c = input.multipleDeclarations) !== null && _c !== void 0 ? _c : stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleImplementations: (_d = input.multipleImplementations) !== null && _d !== void 0 ? _d : stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleReferences: (_e = input.multipleReferences) !== null && _e !== void 0 ? _e : stringSet(input.multipleReferences, '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), }; } } class EditorHover extends BaseEditorOption { constructor() { const defaults = { enabled: true, delay: 300, sticky: true, above: true, }; super(52 /* hover */, 'hover', defaults, { 'editor.hover.enabled': { type: 'boolean', default: defaults.enabled, description: nls.localize('hover.enabled', "Controls whether the hover is shown.") }, 'editor.hover.delay': { type: 'number', default: defaults.delay, 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.above': { type: 'boolean', default: defaults.above, description: nls.localize('hover.above', "Prefer showing hovers above the line, if there's space.") }, }); } validate(_input) { if (!_input || typeof _input !== 'object') { return this.defaultValue; } const input = _input; return { enabled: boolean(input.enabled, this.defaultValue.enabled), delay: EditorIntOption.clampedInt(input.delay, this.defaultValue.delay, 0, 10000), sticky: boolean(input.sticky, this.defaultValue.sticky), above: boolean(input.above, this.defaultValue.above), }; } } /** * @internal */ export class EditorLayoutInfoComputer extends ComputedEditorOption { constructor() { super(129 /* layoutInfo */, [ 49 /* glyphMargin */, 57 /* lineDecorationsWidth */, 37 /* folding */, 64 /* minimap */, 91 /* scrollbar */, 59 /* lineNumbers */, 60 /* lineNumbersMinChars */, 93 /* scrollBeyondLastLine */, 116 /* wordWrap */, 119 /* wordWrapColumn */, 120 /* wordWrapOverride1 */, 121 /* wordWrapOverride2 */, 2 /* accessibilitySupport */ ]); } compute(env, options, _) { 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 }); } static computeContainedMinimapLineCount(input) { const typicalViewportLineCount = input.height / input.lineHeight; const extraLinesBeyondLastLine = input.scrollBeyondLastLine ? (typicalViewportLineCount - 1) : 0; const desiredRatio = (input.viewLineCount + extraLinesBeyondLastLine) / (input.pixelRatio * input.height); const minimapLineCount = Math.floor(input.viewLineCount / desiredRatio); return { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount }; } static _computeMinimapLayout(input, memory) { const outerWidth = input.outerWidth; const outerHeight = input.outerHeight; const pixelRatio = input.pixelRatio; if (!input.minimap.enabled) { return { renderMinimap: 0 /* 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.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 = 1; if (minimapSize === 'fill' || minimapSize === 'fit') { const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({ viewLineCount: viewLineCount, scrollBeyondLastLine: scrollBeyondLastLine, 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((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, 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 ? 1 /* Text */ : 2 /* Blocks */); const minimapLeft = (minimapSide === 'left' ? 0 : (outerWidth - minimapWidth - verticalScrollbarWidth)); return { renderMinimap, minimapLeft, minimapWidth, minimapHeightIsEditorHeight, minimapIsSampling, minimapScale, minimapLineHeight, minimapCanvasInnerWidth, minimapCanvasInnerHeight, minimapCanvasOuterWidth, minimapCanvasOuterHeight, }; } static computeLayout(options, env) { 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(121 /* wordWrapOverride2 */); const wordWrapOverride1 = (wordWrapOverride2 === 'inherit' ? options.get(120 /* wordWrapOverride1 */) : wordWrapOverride2); const wordWrap = (wordWrapOverride1 === 'inherit' ? options.get(116 /* wordWrap */) : wordWrapOverride1); const wordWrapColumn = options.get(119 /* wordWrapColumn */); const accessibilitySupport = options.get(2 /* accessibilitySupport */); const isDominatedByLongLines = env.isDominatedByLongLines; const showGlyphMargin = options.get(49 /* glyphMargin */); const showLineNumbers = (options.get(59 /* lineNumbers */).renderType !== 0 /* Off */); const lineNumbersMinChars = options.get(60 /* lineNumbersMinChars */); const scrollBeyondLastLine = options.get(93 /* scrollBeyondLastLine */); const minimap = options.get(64 /* minimap */); const scrollbar = options.get(91 /* scrollbar */); const verticalScrollbarWidth = scrollbar.verticalScrollbarSize; const verticalScrollbarHasArrows = scrollbar.verticalHasArrows; const scrollbarArrowSize = scrollbar.arrowSize; const horizontalScrollbarHeight = scrollbar.horizontalScrollbarSize; const rawLineDecorationsWidth = options.get(57 /* lineDecorationsWidth */); const folding = options.get(37 /* folding */); let lineDecorationsWidth; if (typeof rawLineDecorationsWidth === 'string' && /^\d+(\.\d+)?ch$/.test(rawLineDecorationsWidth)) { const multiple = parseFloat(rawLineDecorationsWidth.substr(0, rawLineDecorationsWidth.length - 2)); lineDecorationsWidth = EditorIntOption.clampedInt(multiple * typicalHalfwidthCharacterWidth, 0, 0, 1000); } else { lineDecorationsWidth = EditorIntOption.clampedInt(rawLineDecorationsWidth, 0, 0, 1000); } if (folding) { 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; } 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 (accessibilitySupport !== 2 /* Enabled */) { // See https://github.com/microsoft/vscode/issues/27766 // Never enable wrapping when a screen reader is attached // because arrow down etc. will not move the cursor in the way // a screen reader expects. if (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, minimap: minimap, verticalScrollbarWidth: verticalScrollbarWidth, viewLineCount: viewLineCount, remainingWidth: remainingWidth, isViewportWrapping: isViewportWrapping, }, env.memory || new ComputeOptionsMemory()); if (minimapLayout.renderMinimap !== 0 /* 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 wrappingColumn = Math.max(1, viewportColumn); if (wordWrap === 'bounded') { wrappingColumn = Math.min(wrappingColumn, wordWrapColumn); } } return { width: outerWidth, height: outerHeight, glyphMarginLeft: glyphMarginLeft, glyphMarginWidth: glyphMarginWidth, lineNumbersLeft: lineNumbersLeft, lineNumbersWidth: lineNumbersWidth, decorationsLeft: decorationsLeft, decorationsWidth: lineDecorationsWidth, contentLeft: contentLeft, contentWidth: contentWidth, minimap: minimapLayout, viewportColumn: viewportColumn, isWordWrapMinified: isWordWrapMinified, isViewportWrapping: isViewportWrapping, wrappingColumn