monaco-editor-core
Version:
A browser based code editor
1,062 lines (1,061 loc) • 178 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* 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 * as objects from '../../../base/common/objects.js';
import * as platform from '../../../base/common/platform.js';
import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js';
import { USUAL_WORD_SEPARATORS } from '../core/wordHelper.js';
import * as nls from '../../../nls.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 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;
}
applyUpdate(value, update) {
return applyUpdate(value, update);
}
compute(env, options, value) {
return value;
}
}
export class ApplyUpdateResult {
constructor(newValue, didChange) {
this.newValue = newValue;
this.didChange = didChange;
}
}
function applyUpdate(value, update) {
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
*/
class ComputedEditorOption {
constructor(id) {
this.schema = undefined;
this.id = id;
this.name = '_never_';
this.defaultValue = undefined;
}
applyUpdate(value, update) {
return applyUpdate(value, update);
}
validate(input) {
return this.defaultValue;
}
}
class SimpleEditorOption {
constructor(id, name, defaultValue, schema) {
this.id = id;
this.name = name;
this.defaultValue = defaultValue;
this.schema = schema;
}
applyUpdate(value, update) {
return applyUpdate(value, update);
}
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 {
static clampedInt(value, defaultValue, minimum, maximum) {
return clampedInt(value, defaultValue, minimum, maximum);
}
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;
}
validate(input) {
return EditorIntOption.clampedInt(input, this.defaultValue, this.minimum, this.maximum);
}
}
/**
* @internal
*/
export function clampedFloat(value, defaultValue, minimum, maximum) {
if (typeof value === 'undefined') {
return defaultValue;
}
const r = EditorFloatOption.float(value, defaultValue);
return EditorFloatOption.clamp(r, minimum, maximum);
}
class EditorFloatOption extends SimpleEditorOption {
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);
}
constructor(id, name, defaultValue, validationFn, schema) {
if (typeof schema !== 'undefined') {
schema.type = 'number';
schema.default = defaultValue;
}
super(id, name, defaultValue, schema);
this.validationFn = validationFn;
}
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, renamedValues) {
if (typeof value !== 'string') {
return defaultValue;
}
if (renamedValues && value in renamedValues) {
return renamedValues[value];
}
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 /* EditorAutoIndentStrategy.None */;
case 'keep': return 1 /* EditorAutoIndentStrategy.Keep */;
case 'brackets': return 2 /* EditorAutoIndentStrategy.Brackets */;
case 'advanced': return 3 /* EditorAutoIndentStrategy.Advanced */;
case 'full': return 4 /* EditorAutoIndentStrategy.Full */;
}
}
//#endregion
//#region accessibilitySupport
class EditorAccessibilitySupport extends BaseEditorOption {
constructor() {
super(2 /* EditorOption.accessibilitySupport */, 'accessibilitySupport', 0 /* 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.")
});
}
validate(input) {
switch (input) {
case 'auto': return 0 /* AccessibilitySupport.Unknown */;
case 'off': return 1 /* AccessibilitySupport.Disabled */;
case 'on': return 2 /* AccessibilitySupport.Enabled */;
}
return this.defaultValue;
}
compute(env, options, value) {
if (value === 0 /* AccessibilitySupport.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(23 /* 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.')
},
});
}
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 /* TextEditorCursorBlinkingStyle.Blink */;
case 'smooth': return 2 /* TextEditorCursorBlinkingStyle.Smooth */;
case 'phase': return 3 /* TextEditorCursorBlinkingStyle.Phase */;
case 'expand': return 4 /* TextEditorCursorBlinkingStyle.Expand */;
case 'solid': return 5 /* TextEditorCursorBlinkingStyle.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(143 /* EditorOption.editorClassName */);
}
compute(env, options, _) {
const classNames = ['monaco-editor'];
if (options.get(39 /* EditorOption.extraEditorClassName */)) {
classNames.push(options.get(39 /* EditorOption.extraEditorClassName */));
}
if (env.extraEditorClassName) {
classNames.push(env.extraEditorClassName);
}
if (options.get(74 /* EditorOption.mouseStyle */) === 'default') {
classNames.push('mouse-default');
}
else if (options.get(74 /* EditorOption.mouseStyle */) === 'copy') {
classNames.push('mouse-copy');
}
if (options.get(112 /* EditorOption.showUnused */)) {
classNames.push('showUnused');
}
if (options.get(141 /* EditorOption.showDeprecated */)) {
classNames.push('showDeprecated');
}
return classNames.join(' ');
}
}
//#endregion
//#region emptySelectionClipboard
class EditorEmptySelectionClipboard extends EditorBooleanOption {
constructor() {
super(37 /* EditorOption.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(41 /* 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.")
},
});
}
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 {
static { this.OFF = '"liga" off, "calt" off'; }
static { this.ON = '"liga" on, "calt" on'; }
constructor() {
super(51 /* 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
});
}
validate(input) {
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.
static { this.OFF = 'normal'; }
// Translate `fontWeight` config to the `font-variation-settings` CSS property.
static { this.TRANSLATE = 'translate'; }
constructor() {
super(54 /* 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
});
}
validate(input) {
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;
}
compute(env, options, value) {
// 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(50 /* EditorOption.fontInfo */);
}
compute(env, options, _) {
return env.fontInfo;
}
}
//#endregion
//#region fontSize
class EditorFontSize extends SimpleEditorOption {
constructor() {
super(52 /* 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.")
});
}
validate(input) {
const 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 {
static { this.SUGGESTION_VALUES = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; }
static { this.MINIMUM_VALUE = 1; }
static { this.MAXIMUM_VALUE = 1000; }
constructor() {
super(53 /* 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.")
});
}
validate(input) {
if (input === 'normal' || input === 'bold') {
return input;
}
return String(EditorIntOption.clampedInt(input, EDITOR_FONT_DEFAULTS.fontWeight, EditorFontWeight.MINIMUM_VALUE, EditorFontWeight.MAXIMUM_VALUE));
}
}
class EditorGoToLocation extends BaseEditorOption {
constructor() {
const defaults = {
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 = {
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(58 /* 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.")
},
});
}
validate(_input) {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input;
return {
multiple: stringSet(input.multiple, this.defaultValue.multiple, ['peek', 'gotoAndPeek', 'goto']),
multipleDefinitions: input.multipleDefinitions ?? stringSet(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']),
multipleTypeDefinitions: input.multipleTypeDefinitions ?? stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']),
multipleDeclarations: input.multipleDeclarations ?? stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']),
multipleImplementations: input.multipleImplementations ?? stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']),
multipleReferences: input.multipleReferences ?? stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']),
multipleTests: input.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),
};
}
}
class EditorHover extends BaseEditorOption {
constructor() {
const defaults = {
enabled: true,
delay: 300,
hidingDelay: 300,
sticky: true,
above: true,
};
super(60 /* EditorOption.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,
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,
description: 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.")
},
});
}
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),
hidingDelay: EditorIntOption.clampedInt(input.hidingDelay, this.defaultValue.hidingDelay, 0, 600000),
above: boolean(input.above, this.defaultValue.above),
};
}
}
/**
* @internal
*/
export class EditorLayoutInfoComputer extends ComputedEditorOption {
constructor() {
super(146 /* EditorOption.layoutInfo */);
}
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,
glyphMarginDecorationLaneCount: env.glyphMarginDecorationLaneCount
});
}
static computeContainedMinimapLineCount(input) {
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 };
}
static _computeMinimapLayout(input, memory) {
const outerWidth = input.outerWidth;
const outerHeight = input.outerHeight;
const pixelRatio = input.pixelRatio;
if (!input.minimap.enabled) {
return {
renderMinimap: 0 /* 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 = 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 ? 1 /* RenderMinimap.Text */ : 2 /* RenderMinimap.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(138 /* EditorOption.wordWrapOverride2 */);
const wordWrapOverride1 = (wordWrapOverride2 === 'inherit' ? options.get(137 /* EditorOption.wordWrapOverride1 */) : wordWrapOverride2);
const wordWrap = (wordWrapOverride1 === 'inherit' ? options.get(133 /* EditorOption.wordWrap */) : wordWrapOverride1);
const wordWrapColumn = options.get(136 /* EditorOption.wordWrapColumn */);
const isDominatedByLongLines = env.isDominatedByLongLines;
const showGlyphMargin = options.get(57 /* EditorOption.glyphMargin */);
const showLineNumbers = (options.get(68 /* EditorOption.lineNumbers */).renderType !== 0 /* RenderLineNumbersType.Off */);