diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts index 5a68f3d194a..6d4a59b01ed 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts @@ -743,6 +743,7 @@ export class TextAreaEditContext extends AbstractEditContext { // Try to render the textarea with the color/font style to match the text under it const lineHeight = this._context.viewLayout.getLineHeightForLineNumber(startPosition.lineNumber); + const fontSize = this._context.viewModel.getFontSizeAtPosition(this._primaryCursorPosition); const viewLineData = this._context.viewModel.getViewLineData(startPosition.lineNumber); const startTokenIndex = viewLineData.tokens.findTokenIndexAtOffset(startPosition.column - 1); const endTokenIndex = viewLineData.tokens.findTokenIndexAtOffset(endPosition.column - 1); @@ -765,7 +766,8 @@ export class TextAreaEditContext extends AbstractEditContext { italic: presentation.italic, bold: presentation.bold, underline: presentation.underline, - strikethrough: presentation.strikethrough + strikethrough: presentation.strikethrough, + fontSize }); } return; @@ -850,6 +852,7 @@ export class TextAreaEditContext extends AbstractEditContext { ta.setHeight(renderData.height); ta.setLineHeight(renderData.height); + ta.setFontSize(renderData.fontSize ?? this._fontInfo.fontSize); ta.setColor(renderData.color ? Color.Format.CSS.formatHex(renderData.color) : ''); ta.setFontStyle(renderData.italic ? 'italic' : ''); if (renderData.bold) { @@ -885,6 +888,7 @@ interface IRenderData { height: number; useCover: boolean; + fontSize?: string | null; color?: Color | null; italic?: boolean; bold?: boolean; diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 6b4bd69b3df..3de37886d17 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -19,7 +19,7 @@ import { IDiffComputationResult, ILineChange } from '../common/diff/legacyLinesD import * as editorCommon from '../common/editorCommon.js'; import { GlyphMarginLane, ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, PositionAffinity } from '../common/model.js'; import { InjectedText } from '../common/modelLineProjectionData.js'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelLineHeightChangedEvent } from '../common/textModelEvents.js'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelFontChangedEvent, ModelLineHeightChangedEvent } from '../common/textModelEvents.js'; import { IEditorWhitespace, IViewModel } from '../common/viewModel.js'; import { OverviewRulerZone } from '../common/viewModel/overviewZoneManager.js'; import { MenuId } from '../../platform/actions/common/actions.js'; @@ -900,6 +900,13 @@ export interface ICodeEditor extends editorCommon.IEditor { */ onDidChangeLineHeight: Event; + /** + * An event emitted when the font of the editor has changed. + * @internal + * @event + */ + onDidChangeFont: Event; + /** * Get value of the current model attached to this editor. * @see {@link ITextModel.getValue} @@ -1020,6 +1027,12 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getDecorationsInRange(range: Range): IModelDecoration[] | null; + /** + * Get the font size at a given position + * @param position the position for which to fetch the font size + */ + getFontSizeAtPosition(position: IPosition): string | null; + /** * All decorations added through this call will get the ownerId of this editor. * @deprecated Use `createDecorationsCollection` diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 3f77e2b2cc3..42538d39b71 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -461,6 +461,10 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { public glyphMarginClassName: string | undefined; public isWholeLine: boolean; public lineHeight: number | undefined; + public fontSize: string | undefined; + public fontFamily: string | undefined; + public fontWeight: string | undefined; + public fontStyle: string | undefined; public overviewRuler: IModelDecorationOverviewRulerOptions | undefined; public stickiness: TrackedRangeStickiness | undefined; public beforeInjectedText: InjectedTextOptions | undefined; @@ -522,6 +526,10 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { const options = providerArgs.options; this.isWholeLine = Boolean(options.isWholeLine); this.lineHeight = options.lineHeight; + this.fontFamily = options.fontFamily; + this.fontSize = options.fontSize; + this.fontWeight = options.fontWeight; + this.fontStyle = options.fontStyle; this.stickiness = options.rangeBehavior; const lightOverviewRulerColor = options.light && options.light.overviewRulerColor || options.overviewRulerColor; @@ -552,6 +560,10 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { glyphMarginClassName: this.glyphMarginClassName, isWholeLine: this.isWholeLine, lineHeight: this.lineHeight, + fontFamily: this.fontFamily, + fontSize: this.fontSize, + fontWeight: this.fontWeight, + fontStyle: this.fontStyle, overviewRuler: this.overviewRuler, stickiness: this.stickiness, before: this.beforeInjectedText, @@ -759,7 +771,7 @@ class DecorationCSSRules { return ''; } const cssTextArr: string[] = []; - this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'textDecoration', 'cursor', 'color', 'opacity', 'letterSpacing'], cssTextArr); + this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'fontFamily', 'fontSize', 'textDecoration', 'cursor', 'color', 'opacity', 'letterSpacing'], cssTextArr); if (opts.letterSpacing) { this._hasLetterSpacing = true; } diff --git a/src/vs/editor/browser/viewParts/viewLines/viewLine.ts b/src/vs/editor/browser/viewParts/viewLines/viewLine.ts index 0b97fd500c9..962c31a4bef 100644 --- a/src/vs/editor/browser/viewParts/viewLines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/viewLines/viewLine.ts @@ -11,7 +11,7 @@ import { RangeUtil } from './rangeUtil.js'; import { StringBuilder } from '../../../common/core/stringBuilder.js'; import { FloatHorizontalRange, VisibleRanges } from '../../view/renderingContext.js'; import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js'; -import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, DomPosition } from '../../../common/viewLayout/viewLineRenderer.js'; +import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, DomPosition, RenderWhitespace } from '../../../common/viewLayout/viewLineRenderer.js'; import { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import { InlineDecorationType } from '../../../common/viewModel.js'; import { isHighContrast } from '../../../../platform/theme/common/theme.js'; @@ -91,7 +91,7 @@ export class ViewLine implements IVisibleLine { this._options = newOptions; } public onSelectionChanged(): boolean { - if (isHighContrast(this._options.themeType) || this._options.renderWhitespace === 'selection') { + if (isHighContrast(this._options.themeType) || this._renderedViewLine?.getRenderWhitespace() === RenderWhitespace.Selection) { this._isMaybeInvalid = true; return true; } @@ -115,10 +115,12 @@ export class ViewLine implements IVisibleLine { const lineData = viewportData.getViewLineRenderingData(lineNumber); const options = this._options; const actualInlineDecorations = LineDecoration.filter(lineData.inlineDecorations, lineNumber, lineData.minColumn, lineData.maxColumn); + const renderWhitespace = (lineData.hasVariableFonts || options.experimentalWhitespaceRendering === 'off') ? options.renderWhitespace : 'none'; + const allowFastRendering = !lineData.hasVariableFonts; // Only send selection information when needed for rendering whitespace let selectionsOnLine: OffsetRange[] | null = null; - if (isHighContrast(options.themeType) || this._options.renderWhitespace === 'selection') { + if (isHighContrast(options.themeType) || renderWhitespace === 'selection') { const selections = viewportData.selections; for (const selection of selections) { @@ -134,7 +136,7 @@ export class ViewLine implements IVisibleLine { if (isHighContrast(options.themeType)) { actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', InlineDecorationType.Regular)); } - if (this._options.renderWhitespace === 'selection') { + if (renderWhitespace === 'selection') { if (!selectionsOnLine) { selectionsOnLine = []; } @@ -161,7 +163,7 @@ export class ViewLine implements IVisibleLine { options.middotWidth, options.wsmiddotWidth, options.stopRenderingLineAfter, - options.renderWhitespace, + renderWhitespace, options.renderControlCharacters, options.fontLigatures !== EditorFontLigatures.OFF, selectionsOnLine @@ -187,7 +189,7 @@ export class ViewLine implements IVisibleLine { sb.appendString(''); let renderedViewLine: IRenderedViewLine | null = null; - if (monospaceAssumptionsAreValid && canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && output.containsForeignElements === ForeignElementType.None) { + if (allowFastRendering && monospaceAssumptionsAreValid && canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && output.containsForeignElements === ForeignElementType.None) { renderedViewLine = new FastRenderedViewLine( this._renderedViewLine ? this._renderedViewLine.domNode : null, renderLineInput, @@ -301,6 +303,7 @@ interface IRenderedViewLine { readonly input: RenderLineInput; getWidth(context: DomReadingContext | null): number; getWidthIsFast(): boolean; + getRenderWhitespace(): RenderWhitespace; getVisibleRangesForRange(lineNumber: number, startColumn: number, endColumn: number, context: DomReadingContext): FloatHorizontalRange[] | null; getColumnOfNodeOffset(spanNode: HTMLElement, offset: number): number; } @@ -346,6 +349,10 @@ class FastRenderedViewLine implements IRenderedViewLine { this._charWidth = renderLineInput.spaceWidth; } + public getRenderWhitespace(): RenderWhitespace { + return this.input.renderWhitespace; + } + public getWidth(context: DomReadingContext | null): number { if (!this.domNode || this.input.lineContent.length < Constants.MaxMonospaceDistance) { const horizontalOffset = this._characterMapping.getHorizontalOffset(this._characterMapping.length); @@ -478,6 +485,13 @@ class RenderedViewLine implements IRenderedViewLine { return myDomNode.domNode.firstChild; } + /** + * The render whitespace setting for this line + */ + public getRenderWhitespace(): RenderWhitespace { + return this.input.renderWhitespace; + } + /** * Width of the line in pixels */ diff --git a/src/vs/editor/browser/viewParts/viewLines/viewLineOptions.ts b/src/vs/editor/browser/viewParts/viewLines/viewLineOptions.ts index c75ea1740fe..c200979eb14 100644 --- a/src/vs/editor/browser/viewParts/viewLines/viewLineOptions.ts +++ b/src/vs/editor/browser/viewParts/viewLines/viewLineOptions.ts @@ -10,6 +10,7 @@ import { EditorOption } from '../../../common/config/editorOptions.js'; export class ViewLineOptions { public readonly themeType: ColorScheme; public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; + public readonly experimentalWhitespaceRendering: 'svg' | 'font' | 'off'; public readonly renderControlCharacters: boolean; public readonly spaceWidth: number; public readonly middotWidth: number; @@ -25,13 +26,8 @@ export class ViewLineOptions { this.themeType = themeType; const options = config.options; const fontInfo = options.get(EditorOption.fontInfo); - const experimentalWhitespaceRendering = options.get(EditorOption.experimentalWhitespaceRendering); - if (experimentalWhitespaceRendering === 'off') { - this.renderWhitespace = options.get(EditorOption.renderWhitespace); - } else { - // whitespace is rendered in a different layer - this.renderWhitespace = 'none'; - } + this.renderWhitespace = options.get(EditorOption.renderWhitespace); + this.experimentalWhitespaceRendering = options.get(EditorOption.experimentalWhitespaceRendering); this.renderControlCharacters = options.get(EditorOption.renderControlCharacters); this.spaceWidth = fontInfo.spaceWidth; this.middotWidth = fontInfo.middotWidth; @@ -51,6 +47,7 @@ export class ViewLineOptions { return ( this.themeType === other.themeType && this.renderWhitespace === other.renderWhitespace + && this.experimentalWhitespaceRendering === other.experimentalWhitespaceRendering && this.renderControlCharacters === other.renderControlCharacters && this.spaceWidth === other.spaceWidth && this.middotWidth === other.middotWidth diff --git a/src/vs/editor/browser/viewParts/whitespace/whitespace.ts b/src/vs/editor/browser/viewParts/whitespace/whitespace.ts index 1cbc649f674..5e4aaddb3da 100644 --- a/src/vs/editor/browser/viewParts/whitespace/whitespace.ts +++ b/src/vs/editor/browser/viewParts/whitespace/whitespace.ts @@ -9,7 +9,7 @@ import { Selection } from '../../../common/core/selection.js'; import { RenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; -import { ViewLineData } from '../../../common/viewModel.js'; +import { ViewLineRenderingData } from '../../../common/viewModel.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; import * as strings from '../../../../base/common/strings.js'; @@ -97,12 +97,11 @@ export class WhitespaceOverlay extends DynamicViewOverlay { for (let i = 0; i < lineCount; i++) { needed[i] = true; } - const viewportData = this._context.viewModel.getMinimapLinesRenderingData(ctx.viewportData.startLineNumber, ctx.viewportData.endLineNumber, needed); this._renderResult = []; for (let lineNumber = ctx.viewportData.startLineNumber; lineNumber <= ctx.viewportData.endLineNumber; lineNumber++) { const lineIndex = lineNumber - ctx.viewportData.startLineNumber; - const lineData = viewportData.data[lineIndex]!; + const lineData = this._context.viewModel.getViewLineRenderingData(lineNumber); let selectionsOnLine: OffsetRange[] | null = null; if (this._options.renderWhitespace === 'selection') { @@ -130,7 +129,10 @@ export class WhitespaceOverlay extends DynamicViewOverlay { } } - private _applyRenderWhitespace(ctx: RenderingContext, lineNumber: number, selections: OffsetRange[] | null, lineData: ViewLineData): string { + private _applyRenderWhitespace(ctx: RenderingContext, lineNumber: number, selections: OffsetRange[] | null, lineData: ViewLineRenderingData): string { + if (lineData.hasVariableFonts) { + return ''; + } if (this._options.renderWhitespace === 'selection' && !selections) { return ''; } diff --git a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index b4c31f7bea5..efcbb6a7e50 100644 --- a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -26,7 +26,7 @@ import { ICommandDelegate } from '../../view/viewController.js'; import { ViewUserInputEvents } from '../../view/viewUserInputEvents.js'; import { CodeEditorContributions } from './codeEditorContributions.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; -import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, filterValidationDecorations } from '../../../common/config/editorOptions.js'; +import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, filterFontDecorations, filterValidationDecorations } from '../../../common/config/editorOptions.js'; import { CursorColumns } from '../../../common/core/cursorColumns.js'; import { IDimension } from '../../../common/core/2d/dimension.js'; import { editorUnnecessaryCodeOpacity } from '../../../common/core/editorColorRegistry.js'; @@ -44,7 +44,7 @@ import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IIdentifiedSi import { ClassName } from '../../../common/model/intervalTree.js'; import { ModelDecorationOptions } from '../../../common/model/textModel.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelLineHeightChangedEvent } from '../../../common/textModelEvents.js'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelFontChangedEvent, ModelLineHeightChangedEvent } from '../../../common/textModelEvents.js'; import { VerticalRevealType } from '../../../common/viewEvents.js'; import { IEditorWhitespace, IViewModel } from '../../../common/viewModel.js'; import { MonospaceLineBreaksComputerFactory } from '../../../common/viewModel/monospaceLineBreaksComputer.js'; @@ -96,6 +96,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onDidChangeLineHeight: Emitter = this._register(new Emitter({ deliveryQueue: this._deliveryQueue })); public readonly onDidChangeLineHeight: Event = this._onDidChangeLineHeight.event; + private readonly _onDidChangeFont: Emitter = this._register(new Emitter({ deliveryQueue: this._deliveryQueue })); + public readonly onDidChangeFont: Event = this._onDidChangeFont.event; + private readonly _onDidChangeModelTokens: Emitter = this._register(new Emitter({ deliveryQueue: this._deliveryQueue })); public readonly onDidChangeModelTokens: Event = this._onDidChangeModelTokens.event; @@ -1309,14 +1312,23 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return null; } - return this._modelData.model.getLineDecorations(lineNumber, this._id, filterValidationDecorations(this._configuration.options)); + const options = this._configuration.options; + return this._modelData.model.getLineDecorations(lineNumber, this._id, filterValidationDecorations(options), filterFontDecorations(options)); } public getDecorationsInRange(range: Range): IModelDecoration[] | null { if (!this._modelData) { return null; } - return this._modelData.model.getDecorationsInRange(range, this._id, filterValidationDecorations(this._configuration.options)); + const options = this._configuration.options; + return this._modelData.model.getDecorationsInRange(range, this._id, filterValidationDecorations(options), filterFontDecorations(options)); + } + + public getFontSizeAtPosition(position: IPosition): string | null { + if (!this._modelData) { + return null; + } + return this._modelData.viewModel.getFontSizeAtPosition(position); } /** @@ -1809,7 +1821,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE case OutgoingViewModelEventKind.ModelLineHeightChanged: this._onDidChangeLineHeight.fire(e.event); break; - + case OutgoingViewModelEventKind.ModelFontChangedEvent: + this._onDidChangeFont.fire(e.event); + break; } })); diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts index 58fa085504c..351715f2d1b 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts @@ -184,6 +184,8 @@ export class DiffEditorEditors extends Disposable { clonedOptions.inDiffEditor = true; clonedOptions.automaticLayout = false; clonedOptions.allowVariableLineHeights = false; + clonedOptions.allowVariableFonts = false; + clonedOptions.allowVariableFontsInAccessibilityMode = false; // Clone scrollbar options before changing them clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) }; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index d8f49bcb723..f01c27fdbe6 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -57,6 +57,14 @@ export interface IEditorOptions { * 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). */ @@ -1976,6 +1984,26 @@ class EffectiveEditContextEnabled extends ComputedEditorOption { + + constructor() { + super(EditorOption.effectiveAllowVariableFonts); + } + + 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 fontSize class EditorFontSize extends SimpleEditorOption { @@ -3754,6 +3782,17 @@ export function filterValidationDecorations(options: IComputedEditorOptions): bo //#endregion +//#region filterFontDecorations + +/** + * @internal + */ +export function filterFontDecorations(options: IComputedEditorOptions): boolean { + return !options.get(EditorOption.effectiveAllowVariableFonts); +} + +//#endregion + //#region rulers export interface IRulerOption { @@ -5493,6 +5532,8 @@ export const enum EditorOption { accessibilitySupport, accessibilityPageSize, allowVariableLineHeights, + allowVariableFonts, + allowVariableFontsInAccessibilityMode, ariaLabel, ariaRequired, autoClosingBrackets, @@ -5650,6 +5691,7 @@ export const enum EditorOption { inlineCompletionsAccessibilityVerbose, effectiveEditContext, scrollOnMiddleClick, + effectiveAllowVariableFonts } export const EditorOptions = { @@ -5679,6 +5721,19 @@ export const EditorOptions = { allowVariableLineHeights: register(new EditorBooleanOption( EditorOption.allowVariableLineHeights, 'allowVariableLineHeights', true )), + allowVariableFonts: register(new EditorBooleanOption( + EditorOption.allowVariableFonts, 'allowVariableFonts', true, + { + description: nls.localize('allowVariableFonts', "Controls whether to allow using variable fonts in the editor.") + } + )), + allowVariableFontsInAccessibilityMode: register(new EditorBooleanOption( + EditorOption.allowVariableFontsInAccessibilityMode, 'allowVariableFontsInAccessibilityMode', false, + { + description: nls.localize('allowVariableFontsInAccessibilityMode', "Controls whether to allow using variable fonts in the editor in the accessibility mode."), + tags: ['accessibility'] + } + )), ariaLabel: register(new EditorStringOption( EditorOption.ariaLabel, 'ariaLabel', nls.localize('editorViewAccessibleLabel', "Editor content") )), @@ -6497,7 +6552,8 @@ export const EditorOptions = { wrappingInfo: register(new EditorWrappingInfoComputer()), wrappingIndent: register(new WrappingIndentOption()), wrappingStrategy: register(new WrappingStrategy()), - effectiveEditContextEnabled: register(new EffectiveEditContextEnabled()) + effectiveEditContextEnabled: register(new EffectiveEditContextEnabled()), + effectiveAllowVariableFonts: register(new EffectiveAllowVariableFonts()) }; type EditorOptionsType = typeof EditorOptions; diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index ba900f3a584..c615a7c933b 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -633,6 +633,7 @@ export interface IThemeDecorationRenderOptions { fontStyle?: string; fontWeight?: string; + fontFamily?: string; fontSize?: string; lineHeight?: number; textDecoration?: string; diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 6a869b84832..76483ebff4d 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -19,7 +19,7 @@ import { IWordAtPosition } from './core/wordHelper.js'; import { FormattingOptions } from './languages.js'; import { ILanguageSelection } from './languages/language.js'; import { IBracketPairsTextModelPart } from './textModelBracketPairs.js'; -import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelInjectedTextChangedEvent, ModelLineHeightChangedEvent } from './textModelEvents.js'; +import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelFontChangedEvent, ModelInjectedTextChangedEvent, ModelLineHeightChangedEvent } from './textModelEvents.js'; import { IGuidesTextModelPart } from './textModelGuides.js'; import { ITokenizationTextModelPart } from './tokenizationTextModelPart.js'; import { UndoRedoGroup } from '../../platform/undoRedo/common/undoRedo.js'; @@ -224,6 +224,22 @@ export interface IModelDecorationOptions { * If set, the decoration will override the line height of the lines it spans. Maximum value is 300px. */ lineHeight?: number | null; + /** + * Font family + */ + fontFamily?: string | null; + /** + * Font size + */ + fontSize?: string | null; + /** + * Font weight + */ + fontWeight?: string | null; + /** + * Font style + */ + fontStyle?: string | null; /** * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ @@ -283,6 +299,12 @@ export interface IModelDecorationOptions { * @internal */ hideInStringTokens?: boolean | null; + + /** + * Whether the decoration affects the font. + * @internal + */ + affectsFont?: boolean | null; } /** @@ -1062,9 +1084,17 @@ export interface ITextModel { * @param lineNumber The line number * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. * @return An array with the decorations */ - getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; + + /** + * Gets all the font decorations for the line `lineNumber` as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @internal + */ + getFontDecorationsInRange(range: IRange, ownerId?: number): IModelDecoration[]; /** * Gets all the decorations for the lines between `startLineNumber` and `endLineNumber` as an array. @@ -1072,9 +1102,10 @@ export interface ITextModel { * @param endLineNumber The end line number * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. * @return An array with the decorations */ - getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. @@ -1082,18 +1113,20 @@ export interface ITextModel { * @param range The range to search in * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. * @param onlyMinimapDecorations If set, it will return only decorations that render in the minimap. * @param onlyMarginDecorations If set, it will return only decorations that render in the glyph margin. * @return An array with the decorations */ - getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, onlyMinimapDecorations?: boolean, onlyMarginDecorations?: boolean): IModelDecoration[]; + getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean, onlyMinimapDecorations?: boolean, onlyMarginDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations as an array. * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. */ - getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getAllDecorations(ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all decorations that render in the glyph margin as an array. @@ -1105,8 +1138,9 @@ export interface ITextModel { * Gets all the decorations that should be rendered in the overview ruler as an array. * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. */ - getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations that contain injected text. @@ -1265,6 +1299,14 @@ export interface ITextModel { * @event */ readonly onDidChangeLineHeight: Event; + /** + * An event emitted when the font from decorations changes. + * This event is emitted only when adding, removing or changing a decoration + * and not when doing edits in the model (i.e. when decoration ranges change) + * @internal + * @event + */ + readonly onDidChangeFont: Event; /** * An event emitted when the model options have changed. * @event diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 99809d0f967..ada8cbdffd1 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -51,6 +51,10 @@ const enum Constants { IsMarginMaskInverse = 0b10111111, IsMarginOffset = 6, + AffectsFontMask = 0b10000000, + AffectsFontMaskInverse = 0b01111111, + AffectsFontOffset = 7, + /** * Due to how deletion works (in order to avoid always walking the right subtree of the deleted node), * the deltas for nodes can grow and shrink dramatically. It has been observed, in practice, that unless @@ -106,6 +110,14 @@ function setNodeIsInGlyphMargin(node: IntervalNode, value: boolean): void { (node.metadata & Constants.IsMarginMaskInverse) | ((value ? 1 : 0) << Constants.IsMarginOffset) ); } +function getNodeAffectsFont(node: IntervalNode): boolean { + return ((node.metadata & Constants.AffectsFontMask) >>> Constants.AffectsFontOffset) === 1; +} +function setNodeAffectsFont(node: IntervalNode, value: boolean): void { + node.metadata = ( + (node.metadata & Constants.AffectsFontMaskInverse) | ((value ? 1 : 0) << Constants.AffectsFontOffset) + ); +} function getNodeStickiness(node: IntervalNode): TrackedRangeStickiness { return ((node.metadata & Constants.StickinessMask) >>> Constants.StickinessOffset); } @@ -172,6 +184,7 @@ export class IntervalNode { setNodeIsInGlyphMargin(this, false); _setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); setCollapseOnReplaceEdit(this, false); + setNodeAffectsFont(this, false); this.cachedVersionId = 0; this.cachedAbsoluteStart = start; @@ -202,6 +215,7 @@ export class IntervalNode { setNodeIsInGlyphMargin(this, this.options.glyphMarginClassName !== null); _setNodeStickiness(this, this.options.stickiness); setCollapseOnReplaceEdit(this, this.options.collapseOnReplaceEdit); + setNodeAffectsFont(this, this.options.affectsFont ?? false); } public setCachedOffsets(absoluteStart: number, absoluteEnd: number, cachedVersionId: number): void { @@ -236,18 +250,18 @@ export class IntervalTree { this.requestNormalizeDelta = false; } - public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { + public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { if (this.root === SENTINEL) { return []; } - return intervalSearch(this, start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + return intervalSearch(this, start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); } - public search(filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { + public search(filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { if (this.root === SENTINEL) { return []; } - return search(this, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + return search(this, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); } /** @@ -319,7 +333,7 @@ export class IntervalTree { } public getAllInOrder(): IntervalNode[] { - return search(this, 0, false, 0, false); + return search(this, 0, false, false, 0, false); } private _normalizeDeltaIfNecessary(): void { @@ -694,7 +708,7 @@ function collectNodesPostOrder(T: IntervalTree): IntervalNode[] { return result; } -function search(T: IntervalTree, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { +function search(T: IntervalTree, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { let node = T.root; let delta = 0; let nodeStart = 0; @@ -732,6 +746,9 @@ function search(T: IntervalTree, filterOwnerId: number, filterOutValidation: boo if (filterOutValidation && getNodeIsForValidation(node)) { include = false; } + if (filterFontDecorations && getNodeAffectsFont(node)) { + include = false; + } if (onlyMarginDecorations && !getNodeIsInGlyphMargin(node)) { include = false; } @@ -755,7 +772,7 @@ function search(T: IntervalTree, filterOwnerId: number, filterOutValidation: boo return result; } -function intervalSearch(T: IntervalTree, intervalStart: number, intervalEnd: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { +function intervalSearch(T: IntervalTree, intervalStart: number, intervalEnd: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree // Now, it is known that two intervals A and B overlap only when both // A.low <= B.high and A.high >= B.low. When searching the trees for @@ -821,6 +838,9 @@ function intervalSearch(T: IntervalTree, intervalStart: number, intervalEnd: num if (filterOutValidation && getNodeIsForValidation(node)) { include = false; } + if (filterFontDecorations && getNodeAffectsFont(node)) { + include = false; + } if (onlyMarginDecorations && !getNodeIsInGlyphMargin(node)) { include = false; } diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 75f07ba4141..12a6ceb5a96 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -40,7 +40,7 @@ import { SearchParams, TextModelSearch } from './textModelSearch.js'; import { TokenizationTextModelPart } from './tokens/tokenizationTextModelPart.js'; import { AttachedViews } from './tokens/abstractSyntaxTokenBackend.js'; import { IBracketPairsTextModelPart } from '../textModelBracketPairs.js'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelOptionsChangedEvent, InternalModelContentChangeEvent, LineInjectedText, ModelInjectedTextChangedEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted, ModelLineHeightChangedEvent, ModelLineHeightChanged } from '../textModelEvents.js'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelOptionsChangedEvent, InternalModelContentChangeEvent, ModelInjectedTextChangedEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted, ModelLineHeightChangedEvent, ModelLineHeightChanged, ModelFontChangedEvent, ModelFontChanged, LineInjectedText } from '../textModelEvents.js'; import { IGuidesTextModelPart } from '../textModelGuides.js'; import { ITokenizationTextModelPart } from '../tokenizationTextModelPart.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; @@ -217,7 +217,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati private readonly _onWillDispose: Emitter = this._register(new Emitter()); public readonly onWillDispose: Event = this._onWillDispose.event; - private readonly _onDidChangeDecorations: DidChangeDecorationsEmitter = this._register(new DidChangeDecorationsEmitter((affectedInjectedTextLines, affectedLineHeights) => this.handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines, affectedLineHeights))); + private readonly _onDidChangeDecorations: DidChangeDecorationsEmitter = this._register(new DidChangeDecorationsEmitter((affectedInjectedTextLines, affectedLineHeights, affectedFontLines) => this.handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines, affectedLineHeights, affectedFontLines))); public readonly onDidChangeDecorations: Event = this._onDidChangeDecorations.event; public get onDidChangeLanguage() { return this._tokenizationTextModelPart.onDidChangeLanguage; } @@ -235,6 +235,9 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati private readonly _onDidChangeLineHeight: Emitter = this._register(new Emitter()); public readonly onDidChangeLineHeight: Event = this._onDidChangeLineHeight.event; + private readonly _onDidChangeFont: Emitter = this._register(new Emitter()); + public readonly onDidChangeFont: Event = this._onDidChangeFont.event; + private readonly _eventEmitter: DidChangeContentEmitter = this._register(new DidChangeContentEmitter()); public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable { return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent)); @@ -421,6 +424,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati || this._onDidChangeAttached.hasListeners() || this._onDidChangeInjectedText.hasListeners() || this._onDidChangeLineHeight.hasListeners() + || this._onDidChangeFont.hasListeners() || this._eventEmitter.hasListeners() ); } @@ -1597,7 +1601,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati //#region Decorations - private handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines: Set | null, affectedLineHeights: Set | null): void { + private handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines: Set | null, affectedLineHeights: Set | null, affectedFontLines: Set | null): void { // This is called before the decoration changed event is fired. if (affectedInjectedTextLines && affectedInjectedTextLines.size > 0) { @@ -1610,6 +1614,11 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati const lineHeightChangeEvent = affectedLines.map(specialLineHeightChange => new ModelLineHeightChanged(specialLineHeightChange.ownerId, specialLineHeightChange.decorationId, specialLineHeightChange.lineNumber, specialLineHeightChange.lineHeight)); this._onDidChangeLineHeight.fire(new ModelLineHeightChangedEvent(lineHeightChangeEvent)); } + if (affectedFontLines && affectedFontLines.size > 0) { + const affectedLines = Array.from(affectedFontLines); + const fontChangeEvent = affectedLines.map(fontChange => new ModelFontChanged(fontChange.ownerId, fontChange.lineNumber)); + this._onDidChangeFont.fire(new ModelFontChangedEvent(fontChangeEvent)); + } } public changeDecorations(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T | null { @@ -1749,35 +1758,35 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati return this._decorationsTree.getNodeRange(this, node); } - public getLineDecorations(lineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false): model.IModelDecoration[] { + public getLineDecorations(lineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] { if (lineNumber < 1 || lineNumber > this.getLineCount()) { return []; } - return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation); + return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation, filterFontDecorations); } - public getLinesDecorations(_startLineNumber: number, _endLineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] { + public getLinesDecorations(_startLineNumber: number, _endLineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] { const lineCount = this.getLineCount(); const startLineNumber = Math.min(lineCount, Math.max(1, _startLineNumber)); const endLineNumber = Math.min(lineCount, Math.max(1, _endLineNumber)); const endColumn = this.getLineMaxColumn(endLineNumber); const range = new Range(startLineNumber, 1, endLineNumber, endColumn); - const decorations = this._getDecorationsInRange(range, ownerId, filterOutValidation, onlyMarginDecorations); + const decorations = this._getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations); pushMany(decorations, this._decorationProvider.getDecorationsInRange(range, ownerId, filterOutValidation)); return decorations; } - public getDecorationsInRange(range: IRange, ownerId: number = 0, filterOutValidation: boolean = false, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] { + public getDecorationsInRange(range: IRange, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] { const validatedRange = this.validateRange(range); - const decorations = this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation, onlyMarginDecorations); + const decorations = this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations); pushMany(decorations, this._decorationProvider.getDecorationsInRange(validatedRange, ownerId, filterOutValidation, onlyMinimapDecorations)); return decorations; } - public getOverviewRulerDecorations(ownerId: number = 0, filterOutValidation: boolean = false): model.IModelDecoration[] { - return this._decorationsTree.getAll(this, ownerId, filterOutValidation, true, false); + public getOverviewRulerDecorations(ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] { + return this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, true, false); } public getInjectedTextDecorations(ownerId: number = 0): model.IModelDecoration[] { @@ -1796,20 +1805,26 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati return LineInjectedText.fromDecorations(result).filter(t => t.lineNumber === lineNumber); } - public getAllDecorations(ownerId: number = 0, filterOutValidation: boolean = false): model.IModelDecoration[] { - let result = this._decorationsTree.getAll(this, ownerId, filterOutValidation, false, false); + public getFontDecorationsInRange(range: IRange, ownerId: number = 0): model.IModelDecoration[] { + const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn); + const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn); + return this._decorationsTree.getFontDecorationsInInterval(this, startOffset, endOffset, ownerId); + } + + public getAllDecorations(ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] { + let result = this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, false, false); result = result.concat(this._decorationProvider.getAllDecorations(ownerId, filterOutValidation)); return result; } public getAllMarginDecorations(ownerId: number = 0): model.IModelDecoration[] { - return this._decorationsTree.getAll(this, ownerId, false, false, true); + return this._decorationsTree.getAll(this, ownerId, false, false, false, true); } - private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] { + private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] { const startOffset = this._buffer.getOffsetAt(filterRange.startLineNumber, filterRange.startColumn); const endOffset = this._buffer.getOffsetAt(filterRange.endLineNumber, filterRange.endColumn); - return this._decorationsTree.getAllInInterval(this, startOffset, endOffset, filterOwnerId, filterOutValidation, onlyMarginDecorations); + return this._decorationsTree.getAllInInterval(this, startOffset, endOffset, filterOwnerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations); } public getRangeAt(start: number, end: number): Range { @@ -1834,6 +1849,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati const oldRange = this.getDecorationRange(decorationId); this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, oldRange!.startLineNumber, null); } + if (node.options.affectsFont) { + const oldRange = this.getDecorationRange(decorationId); + this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, oldRange!.startLineNumber); + } const range = this._validateRangeRelaxedNoAllocations(_range); const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn); @@ -1853,6 +1872,9 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati if (node.options.lineHeight !== null) { this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, range.startLineNumber, node.options.lineHeight); } + if (node.options.affectsFont) { + this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber); + } } private _changeDecorationOptionsImpl(ownerId: number, decorationId: string, options: ModelDecorationOptions): void { @@ -1879,6 +1901,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati const nodeRange = this._decorationsTree.getNodeRange(this, node); this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, options.lineHeight); } + if (node.options.affectsFont || options.affectsFont) { + const nodeRange = this._decorationsTree.getNodeRange(this, node); + this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber); + } const movedInOverviewRuler = nodeWasInOverviewRuler !== nodeIsInOverviewRuler; const changedWhetherInjectedText = isOptionsInjectedText(options) !== isNodeInjectedText(node); @@ -1929,6 +1955,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati const nodeRange = this._decorationsTree.getNodeRange(this, node); this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, null); } + if (node.options.affectsFont) { + const nodeRange = this._decorationsTree.getNodeRange(this, node); + this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber); + } this._decorationsTree.delete(node); if (!suppressEvents) { @@ -1966,6 +1996,9 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati if (node.options.lineHeight !== null) { this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, node.id, range.startLineNumber, node.options.lineHeight); } + if (node.options.affectsFont) { + this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber); + } if (!suppressEvents) { this._onDidChangeDecorations.checkAffectedAndFire(options); } @@ -2098,7 +2131,7 @@ class DecorationsTrees { } public ensureAllNodesHaveRanges(host: IDecorationsTreesHost): void { - this.getAll(host, 0, false, false, false); + this.getAll(host, 0, false, false, false, false); } private _ensureNodesHaveRanges(host: IDecorationsTreesHost, nodes: IntervalNode[]): model.IModelDecoration[] { @@ -2110,50 +2143,56 @@ class DecorationsTrees { return nodes; } - public getAllInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] { + public getAllInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] { const versionId = host.getVersionId(); - const result = this._intervalSearch(start, end, filterOwnerId, filterOutValidation, versionId, onlyMarginDecorations); + const result = this._intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, versionId, onlyMarginDecorations); return this._ensureNodesHaveRanges(host, result); } - private _intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { - const r0 = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); - const r1 = this._decorationsTree1.intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); - const r2 = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + private _intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { + const r0 = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); + const r1 = this._decorationsTree1.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); + const r2 = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); return r0.concat(r1).concat(r2); } public getInjectedTextInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] { const versionId = host.getVersionId(); - const result = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, false, versionId, false); + const result = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, false, false, versionId, false); return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty()); } + public getFontDecorationsInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] { + const versionId = host.getVersionId(); + const decorations = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, false, false, versionId, false); + return this._ensureNodesHaveRanges(host, decorations).filter((i) => i.options.affectsFont); + } + public getAllInjectedText(host: IDecorationsTreesHost, filterOwnerId: number): model.IModelDecoration[] { const versionId = host.getVersionId(); - const result = this._injectedTextDecorationsTree.search(filterOwnerId, false, versionId, false); + const result = this._injectedTextDecorationsTree.search(filterOwnerId, false, false, versionId, false); return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty()); } public getAllCustomLineHeights(host: IDecorationsTreesHost, filterOwnerId: number): model.IModelDecoration[] { const versionId = host.getVersionId(); - const result = this._search(filterOwnerId, false, false, versionId, false); + const result = this._search(filterOwnerId, false, false, false, versionId, false); return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number'); } - public getAll(host: IDecorationsTreesHost, filterOwnerId: number, filterOutValidation: boolean, overviewRulerOnly: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] { + public getAll(host: IDecorationsTreesHost, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] { const versionId = host.getVersionId(); - const result = this._search(filterOwnerId, filterOutValidation, overviewRulerOnly, versionId, onlyMarginDecorations); + const result = this._search(filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, versionId, onlyMarginDecorations); return this._ensureNodesHaveRanges(host, result); } - private _search(filterOwnerId: number, filterOutValidation: boolean, overviewRulerOnly: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { + private _search(filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { if (overviewRulerOnly) { - return this._decorationsTree1.search(filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + return this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); } else { - const r0 = this._decorationsTree0.search(filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); - const r1 = this._decorationsTree1.search(filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); - const r2 = this._injectedTextDecorationsTree.search(filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + const r0 = this._decorationsTree0.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); + const r1 = this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); + const r2 = this._injectedTextDecorationsTree.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); return r0.concat(r1).concat(r2); } } @@ -2368,6 +2407,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[] | null; readonly isWholeLine: boolean; readonly lineHeight: number | null; + readonly fontSize: string | null; readonly showIfCollapsed: boolean; readonly collapseOnReplaceEdit: boolean; readonly overviewRuler: ModelDecorationOverviewRulerOptions | null; @@ -2388,6 +2428,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { readonly before: ModelDecorationInjectedTextOptions | null; readonly hideInCommentTokens: boolean | null; readonly hideInStringTokens: boolean | null; + readonly affectsFont: boolean | null; private constructor(options: model.IModelDecorationOptions) { this.description = options.description; @@ -2404,6 +2445,8 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { this.lineNumberHoverMessage = options.lineNumberHoverMessage || null; this.isWholeLine = options.isWholeLine || false; this.lineHeight = options.lineHeight ? Math.min(options.lineHeight, LINE_HEIGHT_CEILING) : null; + this.fontSize = options.fontSize || null; + this.affectsFont = !!options.fontSize || !!options.fontFamily || !!options.fontWeight || !!options.fontStyle; this.showIfCollapsed = options.showIfCollapsed || false; this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false; this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null; @@ -2458,6 +2501,19 @@ class LineHeightChangingDecoration { ) { } } +class LineFontChangingDecoration { + + public static toKey(obj: LineFontChangingDecoration): string { + return `${obj.ownerId};${obj.decorationId};${obj.lineNumber}`; + } + + constructor( + public readonly ownerId: number, + public readonly decorationId: string, + public readonly lineNumber: number + ) { } +} + class DidChangeDecorationsEmitter extends Disposable { private readonly _actual: Emitter = this._register(new Emitter()); @@ -2469,10 +2525,11 @@ class DidChangeDecorationsEmitter extends Disposable { private _affectsOverviewRuler: boolean; private _affectedInjectedTextLines: Set | null = null; private _affectedLineHeights: SetWithKey | null = null; + private _affectedFontLines: SetWithKey | null = null; private _affectsGlyphMargin: boolean; private _affectsLineNumber: boolean; - constructor(private readonly handleBeforeFire: (affectedInjectedTextLines: Set | null, affectedLineHeights: SetWithKey | null) => void) { + constructor(private readonly handleBeforeFire: (affectedInjectedTextLines: Set | null, affectedLineHeights: SetWithKey | null, affectedFontLines: SetWithKey | null) => void) { super(); this._deferredCnt = 0; this._shouldFireDeferred = false; @@ -2501,6 +2558,8 @@ class DidChangeDecorationsEmitter extends Disposable { this._affectedInjectedTextLines = null; this._affectedLineHeights?.clear(); this._affectedLineHeights = null; + this._affectedFontLines?.clear(); + this._affectedFontLines = null; } } @@ -2518,6 +2577,13 @@ class DidChangeDecorationsEmitter extends Disposable { this._affectedLineHeights.add(new LineHeightChangingDecoration(ownerId, decorationId, lineNumber, lineHeight)); } + public recordLineAffectedByFontChange(ownerId: number, decorationId: string, lineNumber: number): void { + if (!this._affectedFontLines) { + this._affectedFontLines = new SetWithKey([], LineFontChangingDecoration.toKey); + } + this._affectedFontLines.add(new LineFontChangingDecoration(ownerId, decorationId, lineNumber)); + } + public checkAffectedAndFire(options: ModelDecorationOptions): void { this._affectsMinimap ||= !!options.minimap?.position; this._affectsOverviewRuler ||= !!options.overviewRuler?.color; @@ -2542,7 +2608,7 @@ class DidChangeDecorationsEmitter extends Disposable { } private doFire() { - this.handleBeforeFire(this._affectedInjectedTextLines, this._affectedLineHeights); + this.handleBeforeFire(this._affectedInjectedTextLines, this._affectedLineHeights, this._affectedFontLines); const event: IModelDecorationsChangedEvent = { affectsMinimap: this._affectsMinimap, diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 21ab65b75fc..8585cfba39b 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -179,162 +179,165 @@ export enum EditorOption { accessibilitySupport = 2, accessibilityPageSize = 3, allowVariableLineHeights = 4, - ariaLabel = 5, - ariaRequired = 6, - autoClosingBrackets = 7, - autoClosingComments = 8, - screenReaderAnnounceInlineSuggestion = 9, - autoClosingDelete = 10, - autoClosingOvertype = 11, - autoClosingQuotes = 12, - autoIndent = 13, - autoIndentOnPaste = 14, - autoIndentOnPasteWithinString = 15, - automaticLayout = 16, - autoSurround = 17, - bracketPairColorization = 18, - guides = 19, - codeLens = 20, - codeLensFontFamily = 21, - codeLensFontSize = 22, - colorDecorators = 23, - colorDecoratorsLimit = 24, - columnSelection = 25, - comments = 26, - contextmenu = 27, - copyWithSyntaxHighlighting = 28, - cursorBlinking = 29, - cursorSmoothCaretAnimation = 30, - cursorStyle = 31, - cursorSurroundingLines = 32, - cursorSurroundingLinesStyle = 33, - cursorWidth = 34, - disableLayerHinting = 35, - disableMonospaceOptimizations = 36, - domReadOnly = 37, - dragAndDrop = 38, - dropIntoEditor = 39, - editContext = 40, - emptySelectionClipboard = 41, - experimentalGpuAcceleration = 42, - experimentalWhitespaceRendering = 43, - extraEditorClassName = 44, - fastScrollSensitivity = 45, - find = 46, - fixedOverflowWidgets = 47, - folding = 48, - foldingStrategy = 49, - foldingHighlight = 50, - foldingImportsByDefault = 51, - foldingMaximumRegions = 52, - unfoldOnClickAfterEndOfLine = 53, - fontFamily = 54, - fontInfo = 55, - fontLigatures = 56, - fontSize = 57, - fontWeight = 58, - fontVariations = 59, - formatOnPaste = 60, - formatOnType = 61, - glyphMargin = 62, - gotoLocation = 63, - hideCursorInOverviewRuler = 64, - hover = 65, - inDiffEditor = 66, - inlineSuggest = 67, - letterSpacing = 68, - lightbulb = 69, - lineDecorationsWidth = 70, - lineHeight = 71, - lineNumbers = 72, - lineNumbersMinChars = 73, - linkedEditing = 74, - links = 75, - matchBrackets = 76, - minimap = 77, - mouseStyle = 78, - mouseWheelScrollSensitivity = 79, - mouseWheelZoom = 80, - multiCursorMergeOverlapping = 81, - multiCursorModifier = 82, - multiCursorPaste = 83, - multiCursorLimit = 84, - occurrencesHighlight = 85, - occurrencesHighlightDelay = 86, - overtypeCursorStyle = 87, - overtypeOnPaste = 88, - overviewRulerBorder = 89, - overviewRulerLanes = 90, - padding = 91, - pasteAs = 92, - parameterHints = 93, - peekWidgetDefaultFocus = 94, - placeholder = 95, - definitionLinkOpensInPeek = 96, - quickSuggestions = 97, - quickSuggestionsDelay = 98, - readOnly = 99, - readOnlyMessage = 100, - renameOnType = 101, - renderControlCharacters = 102, - renderFinalNewline = 103, - renderLineHighlight = 104, - renderLineHighlightOnlyWhenFocus = 105, - renderValidationDecorations = 106, - renderWhitespace = 107, - revealHorizontalRightPadding = 108, - roundedSelection = 109, - rulers = 110, - scrollbar = 111, - scrollBeyondLastColumn = 112, - scrollBeyondLastLine = 113, - scrollPredominantAxis = 114, - selectionClipboard = 115, - selectionHighlight = 116, - selectOnLineNumbers = 117, - showFoldingControls = 118, - showUnused = 119, - snippetSuggestions = 120, - smartSelect = 121, - smoothScrolling = 122, - stickyScroll = 123, - stickyTabStops = 124, - stopRenderingLineAfter = 125, - suggest = 126, - suggestFontSize = 127, - suggestLineHeight = 128, - suggestOnTriggerCharacters = 129, - suggestSelection = 130, - tabCompletion = 131, - tabIndex = 132, - unicodeHighlighting = 133, - unusualLineTerminators = 134, - useShadowDOM = 135, - useTabStops = 136, - wordBreak = 137, - wordSegmenterLocales = 138, - wordSeparators = 139, - wordWrap = 140, - wordWrapBreakAfterCharacters = 141, - wordWrapBreakBeforeCharacters = 142, - wordWrapColumn = 143, - wordWrapOverride1 = 144, - wordWrapOverride2 = 145, - wrappingIndent = 146, - wrappingStrategy = 147, - showDeprecated = 148, - inlayHints = 149, - effectiveCursorStyle = 150, - editorClassName = 151, - pixelRatio = 152, - tabFocusMode = 153, - layoutInfo = 154, - wrappingInfo = 155, - defaultColorDecorators = 156, - colorDecoratorsActivatedOn = 157, - inlineCompletionsAccessibilityVerbose = 158, - effectiveEditContext = 159, - scrollOnMiddleClick = 160 + allowVariableFonts = 5, + allowVariableFontsInAccessibilityMode = 6, + ariaLabel = 7, + ariaRequired = 8, + autoClosingBrackets = 9, + autoClosingComments = 10, + screenReaderAnnounceInlineSuggestion = 11, + autoClosingDelete = 12, + autoClosingOvertype = 13, + autoClosingQuotes = 14, + autoIndent = 15, + autoIndentOnPaste = 16, + autoIndentOnPasteWithinString = 17, + automaticLayout = 18, + autoSurround = 19, + bracketPairColorization = 20, + guides = 21, + codeLens = 22, + codeLensFontFamily = 23, + codeLensFontSize = 24, + colorDecorators = 25, + colorDecoratorsLimit = 26, + columnSelection = 27, + comments = 28, + contextmenu = 29, + copyWithSyntaxHighlighting = 30, + cursorBlinking = 31, + cursorSmoothCaretAnimation = 32, + cursorStyle = 33, + cursorSurroundingLines = 34, + cursorSurroundingLinesStyle = 35, + cursorWidth = 36, + disableLayerHinting = 37, + disableMonospaceOptimizations = 38, + domReadOnly = 39, + dragAndDrop = 40, + dropIntoEditor = 41, + editContext = 42, + emptySelectionClipboard = 43, + experimentalGpuAcceleration = 44, + experimentalWhitespaceRendering = 45, + extraEditorClassName = 46, + fastScrollSensitivity = 47, + find = 48, + fixedOverflowWidgets = 49, + folding = 50, + foldingStrategy = 51, + foldingHighlight = 52, + foldingImportsByDefault = 53, + foldingMaximumRegions = 54, + unfoldOnClickAfterEndOfLine = 55, + fontFamily = 56, + fontInfo = 57, + fontLigatures = 58, + fontSize = 59, + fontWeight = 60, + fontVariations = 61, + formatOnPaste = 62, + formatOnType = 63, + glyphMargin = 64, + gotoLocation = 65, + hideCursorInOverviewRuler = 66, + hover = 67, + inDiffEditor = 68, + inlineSuggest = 69, + letterSpacing = 70, + lightbulb = 71, + lineDecorationsWidth = 72, + lineHeight = 73, + lineNumbers = 74, + lineNumbersMinChars = 75, + linkedEditing = 76, + links = 77, + matchBrackets = 78, + minimap = 79, + mouseStyle = 80, + mouseWheelScrollSensitivity = 81, + mouseWheelZoom = 82, + multiCursorMergeOverlapping = 83, + multiCursorModifier = 84, + multiCursorPaste = 85, + multiCursorLimit = 86, + occurrencesHighlight = 87, + occurrencesHighlightDelay = 88, + overtypeCursorStyle = 89, + overtypeOnPaste = 90, + overviewRulerBorder = 91, + overviewRulerLanes = 92, + padding = 93, + pasteAs = 94, + parameterHints = 95, + peekWidgetDefaultFocus = 96, + placeholder = 97, + definitionLinkOpensInPeek = 98, + quickSuggestions = 99, + quickSuggestionsDelay = 100, + readOnly = 101, + readOnlyMessage = 102, + renameOnType = 103, + renderControlCharacters = 104, + renderFinalNewline = 105, + renderLineHighlight = 106, + renderLineHighlightOnlyWhenFocus = 107, + renderValidationDecorations = 108, + renderWhitespace = 109, + revealHorizontalRightPadding = 110, + roundedSelection = 111, + rulers = 112, + scrollbar = 113, + scrollBeyondLastColumn = 114, + scrollBeyondLastLine = 115, + scrollPredominantAxis = 116, + selectionClipboard = 117, + selectionHighlight = 118, + selectOnLineNumbers = 119, + showFoldingControls = 120, + showUnused = 121, + snippetSuggestions = 122, + smartSelect = 123, + smoothScrolling = 124, + stickyScroll = 125, + stickyTabStops = 126, + stopRenderingLineAfter = 127, + suggest = 128, + suggestFontSize = 129, + suggestLineHeight = 130, + suggestOnTriggerCharacters = 131, + suggestSelection = 132, + tabCompletion = 133, + tabIndex = 134, + unicodeHighlighting = 135, + unusualLineTerminators = 136, + useShadowDOM = 137, + useTabStops = 138, + wordBreak = 139, + wordSegmenterLocales = 140, + wordSeparators = 141, + wordWrap = 142, + wordWrapBreakAfterCharacters = 143, + wordWrapBreakBeforeCharacters = 144, + wordWrapColumn = 145, + wordWrapOverride1 = 146, + wordWrapOverride2 = 147, + wrappingIndent = 148, + wrappingStrategy = 149, + showDeprecated = 150, + inlayHints = 151, + effectiveCursorStyle = 152, + editorClassName = 153, + pixelRatio = 154, + tabFocusMode = 155, + layoutInfo = 156, + wrappingInfo = 157, + defaultColorDecorators = 158, + colorDecoratorsActivatedOn = 159, + inlineCompletionsAccessibilityVerbose = 160, + effectiveEditContext = 161, + scrollOnMiddleClick = 162, + effectiveAllowVariableFonts = 163 } /** @@ -988,4 +991,4 @@ export enum WrappingIndent { * DeepIndent => wrapped lines get +2 indentation toward the parent. */ DeepIndent = 3 -} \ No newline at end of file +} diff --git a/src/vs/editor/common/textModelEvents.ts b/src/vs/editor/common/textModelEvents.ts index 5cd78a1330b..dbcacec4945 100644 --- a/src/vs/editor/common/textModelEvents.ts +++ b/src/vs/editor/common/textModelEvents.ts @@ -318,6 +318,26 @@ export class ModelLineHeightChanged { } } +/** + * An event describing that a line height has changed in the model. + * @internal + */ +export class ModelFontChanged { + /** + * Editor owner ID + */ + public readonly ownerId: number; + /** + * The line that has changed. + */ + public readonly lineNumber: number; + + constructor(ownerId: number, lineNumber: number) { + this.ownerId = ownerId; + this.lineNumber = lineNumber; + } +} + /** * An event describing that line(s) have been deleted in a model. * @internal @@ -476,6 +496,19 @@ export class ModelLineHeightChangedEvent { } } +/** + * An event describing a change in fonts. + * @internal + */ +export class ModelFontChangedEvent { + + public readonly changes: ModelFontChanged[]; + + constructor(changes: ModelFontChanged[]) { + this.changes = changes; + } +} + /** * @internal */ diff --git a/src/vs/editor/common/viewModel.ts b/src/vs/editor/common/viewModel.ts index d9233c5c06c..3071cbf0ff3 100644 --- a/src/vs/editor/common/viewModel.ts +++ b/src/vs/editor/common/viewModel.ts @@ -44,6 +44,7 @@ export interface IViewModel extends ICursorSimpleModel { onCompositionStart(): void; onCompositionEnd(): void; + getFontSizeAtPosition(position: IPosition): string | null; getMinimapDecorationsInRange(range: Range): ViewModelDecoration[]; getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; getViewportViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData; @@ -349,6 +350,10 @@ export class ViewLineRenderingData { * The visible column at the start of the line (after the fauxIndent) */ public readonly startVisibleColumn: number; + /** + * Whether the line has variable fonts + */ + public readonly hasVariableFonts: boolean; constructor( minColumn: number, @@ -361,6 +366,7 @@ export class ViewLineRenderingData { inlineDecorations: InlineDecoration[], tabSize: number, startVisibleColumn: number, + hasVariableFonts: boolean ) { this.minColumn = minColumn; this.maxColumn = maxColumn; @@ -374,6 +380,7 @@ export class ViewLineRenderingData { this.inlineDecorations = inlineDecorations; this.tabSize = tabSize; this.startVisibleColumn = startVisibleColumn; + this.hasVariableFonts = hasVariableFonts; } public static isBasicASCII(lineContent: string, mightContainNonBasicASCII: boolean): boolean { diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index 553cc7d3389..beab19d6d79 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -10,7 +10,7 @@ import { IEditorConfiguration } from '../config/editorConfiguration.js'; import { IModelDecoration, ITextModel, PositionAffinity } from '../model.js'; import { IViewModelLines } from './viewModelLines.js'; import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from '../viewModel.js'; -import { filterValidationDecorations } from '../config/editorOptions.js'; +import { filterFontDecorations, filterValidationDecorations } from '../config/editorOptions.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; export interface IDecorationsViewportData { @@ -22,6 +22,10 @@ export interface IDecorationsViewportData { * inline decorations grouped by each line in the viewport. */ readonly inlineDecorations: InlineDecoration[][]; + /** + * Whether the decorations affects the fonts. + */ + readonly hasVariableFonts: boolean; } export class ViewModelDecorations implements IDisposable { @@ -110,13 +114,14 @@ export class ViewModelDecorations implements IDisposable { return this._cachedModelDecorationsResolver!; } - public getInlineDecorationsOnLine(lineNumber: number, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): InlineDecoration[] { + public getInlineDecorationsOnLine(lineNumber: number, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): { inlineDecorations: InlineDecoration[]; hasVariableFonts: boolean } { const range = new Range(lineNumber, this._linesCollection.getViewLineMinColumn(lineNumber), lineNumber, this._linesCollection.getViewLineMaxColumn(lineNumber)); - return this._getDecorationsInRange(range, onlyMinimapDecorations, onlyMarginDecorations).inlineDecorations[0]; + const decorations = this._getDecorationsInRange(range, onlyMinimapDecorations, onlyMarginDecorations); + return { inlineDecorations: decorations.inlineDecorations[0], hasVariableFonts: decorations.hasVariableFonts }; } private _getDecorationsInRange(viewRange: Range, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IDecorationsViewportData { - const modelDecorations = this._linesCollection.getDecorationsInRange(viewRange, this.editorId, filterValidationDecorations(this.configuration.options), onlyMinimapDecorations, onlyMarginDecorations); + const modelDecorations = this._linesCollection.getDecorationsInRange(viewRange, this.editorId, filterValidationDecorations(this.configuration.options), filterFontDecorations(this.configuration.options), onlyMinimapDecorations, onlyMarginDecorations); const startLineNumber = viewRange.startLineNumber; const endLineNumber = viewRange.endLineNumber; @@ -127,6 +132,7 @@ export class ViewModelDecorations implements IDisposable { inlineDecorations[j - startLineNumber] = []; } + let hasVariableFonts = false; for (let i = 0, len = modelDecorations.length; i < len; i++) { const modelDecoration = modelDecorations[i]; const decorationOptions = modelDecoration.options; @@ -168,11 +174,15 @@ export class ViewModelDecorations implements IDisposable { inlineDecorations[viewRange.endLineNumber - startLineNumber].push(inlineDecoration); } } + if (decorationOptions.affectsFont) { + hasVariableFonts = true; + } } return { decorations: decorationsInViewport, - inlineDecorations: inlineDecorations + inlineDecorations: inlineDecorations, + hasVariableFonts }; } } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 27d59e57dee..7e65e7a1d45 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -10,7 +10,7 @@ import { Event } from '../../../base/common/event.js'; import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; import * as platform from '../../../base/common/platform.js'; import * as strings from '../../../base/common/strings.js'; -import { ConfigurationChangedEvent, EditorOption, EDITOR_FONT_DEFAULTS, filterValidationDecorations } from '../config/editorOptions.js'; +import { ConfigurationChangedEvent, EditorOption, EDITOR_FONT_DEFAULTS, filterValidationDecorations, filterFontDecorations } from '../config/editorOptions.js'; import { CursorsController } from '../cursor/cursor.js'; import { CursorConfiguration, CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from '../cursorCommon.js'; import { CursorChangeReason } from '../cursorEvents.js'; @@ -36,7 +36,7 @@ import { ILineBreaksComputer, ILineBreaksComputerFactory, InjectedText } from '. import { ViewEventHandler } from '../viewEventHandler.js'; import { ICoordinatesConverter, InlineDecoration, ILineHeightChangeAccessor, IViewModel, IWhitespaceChangeAccessor, MinimapLinesRenderingData, OverviewRulerDecorationsGroup, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from '../viewModel.js'; import { ViewModelDecorations } from './viewModelDecorations.js'; -import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelLineHeightChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent, WidgetFocusChangedEvent } from '../viewModelEventDispatcher.js'; +import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelFontChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelLineHeightChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent, WidgetFocusChangedEvent } from '../viewModelEventDispatcher.js'; import { IViewModelLines, ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js'; import { IThemeService } from '../../../platform/theme/common/themeService.js'; import { GlyphMarginLanesModel } from './glyphLanesModel.js'; @@ -464,6 +464,18 @@ export class ViewModel extends Disposable implements IViewModel { })); } + const allowVariableFonts = this._configuration.options.get(EditorOption.effectiveAllowVariableFonts); + if (allowVariableFonts) { + this._register(this.model.onDidChangeFont((e) => { + const filteredChanges = e.changes.filter((change) => change.ownerId === this._editorId || change.ownerId === 0); + // recreate the model event using the filtered changes + if (filteredChanges.length > 0) { + const filteredEvent = new textModelEvents.ModelFontChangedEvent(filteredChanges); + this._eventDispatcher.emitOutgoingEvent(new ModelFontChangedEvent(filteredEvent)); + } + })); + } + this._register(this.model.onDidChangeTokens((e) => { const viewRanges: { fromLineNumber: number; toLineNumber: number }[] = []; for (let j = 0, lenJ = e.ranges.length; j < lenJ; j++) { @@ -525,6 +537,22 @@ export class ViewModel extends Disposable implements IViewModel { private readonly hiddenAreasModel = new HiddenAreasModel(); private previousHiddenAreas: readonly Range[] = []; + public getFontSizeAtPosition(position: IPosition): string | null { + const allowVariableFonts = this._configuration.options.get(EditorOption.effectiveAllowVariableFonts); + if (!allowVariableFonts) { + return null; + } + const fontDecorations = this.model.getFontDecorationsInRange(Range.fromPositions(position), this._editorId); + let fontSize: string = this._configuration.options.get(EditorOption.fontInfo).fontSize + 'px'; + for (const fontDecoration of fontDecorations) { + if (fontDecoration.options.fontSize) { + fontSize = fontDecoration.options.fontSize; + break; + } + } + return fontSize; + } + /** * @param forceUpdate If true, the hidden areas will be updated even if the new ranges are the same as the previous ranges. * This is because the model might have changed, which resets the hidden areas, but not the last cached value. @@ -769,17 +797,18 @@ export class ViewModel extends Disposable implements IViewModel { } public getViewportViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData { - const allInlineDecorations = this._decorations.getDecorationsViewportData(visibleRange).inlineDecorations; + const decorationViewportData = this._decorations.getDecorationsViewportData(visibleRange); + const allInlineDecorations = decorationViewportData.inlineDecorations; const inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber]; - return this._getViewLineRenderingData(lineNumber, inlineDecorations); + return this._getViewLineRenderingData(lineNumber, inlineDecorations, decorationViewportData.hasVariableFonts); } public getViewLineRenderingData(lineNumber: number): ViewLineRenderingData { - const inlineDecorations = this._decorations.getInlineDecorationsOnLine(lineNumber); - return this._getViewLineRenderingData(lineNumber, inlineDecorations); + const decorations = this._decorations.getInlineDecorationsOnLine(lineNumber); + return this._getViewLineRenderingData(lineNumber, decorations.inlineDecorations, decorations.hasVariableFonts); } - private _getViewLineRenderingData(lineNumber: number, inlineDecorations: InlineDecoration[]): ViewLineRenderingData { + private _getViewLineRenderingData(lineNumber: number, inlineDecorations: InlineDecoration[], hasVariableFonts: boolean): ViewLineRenderingData { const mightContainRTL = this.model.mightContainRTL(); const mightContainNonBasicASCII = this.model.mightContainNonBasicASCII(); const tabSize = this.getTabSize(); @@ -804,7 +833,8 @@ export class ViewModel extends Disposable implements IViewModel { lineData.tokens, inlineDecorations, tabSize, - lineData.startVisibleColumn + lineData.startVisibleColumn, + hasVariableFonts ); } @@ -821,7 +851,7 @@ export class ViewModel extends Disposable implements IViewModel { } public getAllOverviewRulerDecorations(theme: EditorTheme): OverviewRulerDecorationsGroup[] { - const decorations = this.model.getOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options)); + const decorations = this.model.getOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options), filterFontDecorations(this._configuration.options)); const result = new OverviewRulerDecorations(); for (const decoration of decorations) { const decorationOptions = decoration.options; diff --git a/src/vs/editor/common/viewModel/viewModelLines.ts b/src/vs/editor/common/viewModel/viewModelLines.ts index 8c1142270c5..d66abd71aad 100644 --- a/src/vs/editor/common/viewModel/viewModelLines.ts +++ b/src/vs/editor/common/viewModel/viewModelLines.ts @@ -45,7 +45,7 @@ export interface IViewModelLines extends IDisposable { getViewLineData(viewLineNumber: number): ViewLineData; getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): Array; - getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IModelDecoration[]; + getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IModelDecoration[]; getInjectedTextAt(viewPosition: Position): InjectedText | null; @@ -905,14 +905,14 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { return this.modelLineProjections[lineIndex].getViewLineNumberOfModelPosition(deltaLineNumber, this.model.getLineMaxColumn(lineIndex + 1)); } - public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IModelDecoration[] { + public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IModelDecoration[] { const modelStart = this.convertViewPositionToModelPosition(range.startLineNumber, range.startColumn); const modelEnd = this.convertViewPositionToModelPosition(range.endLineNumber, range.endColumn); if (modelEnd.lineNumber - modelStart.lineNumber <= range.endLineNumber - range.startLineNumber) { // most likely there are no hidden lines => fast path // fetch decorations from column 1 to cover the case of wrapped lines that have whole line decorations at column 1 - return this.model.getDecorationsInRange(new Range(modelStart.lineNumber, 1, modelEnd.lineNumber, modelEnd.column), ownerId, filterOutValidation, onlyMinimapDecorations, onlyMarginDecorations); + return this.model.getDecorationsInRange(new Range(modelStart.lineNumber, 1, modelEnd.lineNumber, modelEnd.column), ownerId, filterOutValidation, filterFontDecorations, onlyMinimapDecorations, onlyMarginDecorations); } let result: IModelDecoration[] = []; @@ -931,14 +931,14 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { // hit invisible line => flush request if (reqStart !== null) { const maxLineColumn = this.model.getLineMaxColumn(modelLineIndex); - result = result.concat(this.model.getDecorationsInRange(new Range(reqStart.lineNumber, reqStart.column, modelLineIndex, maxLineColumn), ownerId, filterOutValidation, onlyMinimapDecorations)); + result = result.concat(this.model.getDecorationsInRange(new Range(reqStart.lineNumber, reqStart.column, modelLineIndex, maxLineColumn), ownerId, filterOutValidation, filterFontDecorations, onlyMinimapDecorations)); reqStart = null; } } } if (reqStart !== null) { - result = result.concat(this.model.getDecorationsInRange(new Range(reqStart.lineNumber, reqStart.column, modelEnd.lineNumber, modelEnd.column), ownerId, filterOutValidation, onlyMinimapDecorations)); + result = result.concat(this.model.getDecorationsInRange(new Range(reqStart.lineNumber, reqStart.column, modelEnd.lineNumber, modelEnd.column), ownerId, filterOutValidation, filterFontDecorations, onlyMinimapDecorations)); reqStart = null; } @@ -1238,8 +1238,8 @@ export class ViewModelLinesFromModelAsIs implements IViewModelLines { return result; } - public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IModelDecoration[] { - return this.model.getDecorationsInRange(range, ownerId, filterOutValidation, onlyMinimapDecorations, onlyMarginDecorations); + public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IModelDecoration[] { + return this.model.getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations, onlyMinimapDecorations, onlyMarginDecorations); } normalizePosition(position: Position, affinity: PositionAffinity): Position { diff --git a/src/vs/editor/common/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModelEventDispatcher.ts index 81516adf725..8fb67dbbef1 100644 --- a/src/vs/editor/common/viewModelEventDispatcher.ts +++ b/src/vs/editor/common/viewModelEventDispatcher.ts @@ -10,7 +10,7 @@ import { Emitter } from '../../base/common/event.js'; import { Selection } from './core/selection.js'; import { Disposable } from '../../base/common/lifecycle.js'; import { CursorChangeReason } from './cursorEvents.js'; -import { ModelLineHeightChangedEvent as OriginalModelLineHeightChangedEvent, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from './textModelEvents.js'; +import { ModelLineHeightChangedEvent as OriginalModelLineHeightChangedEvent, ModelFontChangedEvent as OriginalModelFontChangedEvent, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from './textModelEvents.js'; export class ViewModelEventDispatcher extends Disposable { @@ -189,6 +189,7 @@ export type OutgoingViewModelEvent = ( | ModelOptionsChangedEvent | ModelTokensChangedEvent | ModelLineHeightChangedEvent + | ModelFontChangedEvent ); export const enum OutgoingViewModelEventKind { @@ -207,6 +208,7 @@ export const enum OutgoingViewModelEventKind { ModelOptionsChanged, ModelTokensChanged, ModelLineHeightChanged, + ModelFontChangedEvent } export class ContentSizeChangedEvent implements IContentSizeChangedEvent { @@ -571,3 +573,19 @@ export class ModelLineHeightChangedEvent { return null; } } + +export class ModelFontChangedEvent { + public readonly kind = OutgoingViewModelEventKind.ModelFontChangedEvent; + + constructor( + public readonly event: OriginalModelFontChangedEvent + ) { } + + public isNoOp(): boolean { + return false; + } + + public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null { + return null; + } +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts index 2c71b1c8d80..fe32ff410c2 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts @@ -82,12 +82,17 @@ export class InlineCompletionsView extends Disposable { this._register(createStyleSheetFromObservable(derived(reader => { const fontFamily = this._fontFamily.read(reader); - if (fontFamily === '' || fontFamily === 'default') { return ''; } + let fontSize: string = this._editor.getOption(EditorOption.fontSize) + 'px'; + const cursorSelection = this._editorObs.cursorSelection.read(reader); + if (cursorSelection) { + fontSize = this._editor.getFontSizeAtPosition(cursorSelection.getEndPosition()) ?? fontSize; + } return ` .monaco-editor .ghost-text-decoration, .monaco-editor .ghost-text-decoration-preview, .monaco-editor .ghost-text { font-family: ${fontFamily}; + font-size: ${fontSize}; }`; }))); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 736492e7292..133d288e1c4 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -104,6 +104,14 @@ export class StickyScrollController extends Disposable implements IEditorContrib } }); })); + this._register(this._editor.onDidChangeFont((e) => { + e.changes.forEach((change) => { + const lineNumber = change.lineNumber; + if (this._widgetState.startLineNumbers.includes(lineNumber)) { + this._renderStickyScroll(lineNumber); + } + }); + })); this._register(this._editor.onDidChangeConfiguration(e => { this._readConfigurationChange(e); })); diff --git a/src/vs/editor/test/common/model/intervalTree.test.ts b/src/vs/editor/test/common/model/intervalTree.test.ts index b6b09e38314..5ea04985bf7 100644 --- a/src/vs/editor/test/common/model/intervalTree.test.ts +++ b/src/vs/editor/test/common/model/intervalTree.test.ts @@ -111,7 +111,7 @@ suite('IntervalTree 1', () => { this._oracle.insert(this._oracleNodes[op.id]!); } else { - const actualNodes = this._tree.intervalSearch(op.begin, op.end, 0, false, 0, false); + const actualNodes = this._tree.intervalSearch(op.begin, op.end, 0, false, false, 0, false); const actual = actualNodes.map(n => new Interval(n.cachedAbsoluteStart, n.cachedAbsoluteEnd)); const expected = this._oracle.search(new Interval(op.begin, op.end)); assert.deepStrictEqual(actual, expected); @@ -501,7 +501,7 @@ suite('IntervalTree 1', () => { const T = createCormenTree(); function assertIntervalSearch(start: number, end: number, expected: [number, number][]): void { - const actualNodes = T.intervalSearch(start, end, 0, false, 0, false); + const actualNodes = T.intervalSearch(start, end, 0, false, false, 0, false); const actual = actualNodes.map((n) => <[number, number]>[n.cachedAbsoluteStart, n.cachedAbsoluteEnd]); assert.deepStrictEqual(actual, expected); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b687a32d66f..d87109d2f84 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1748,6 +1748,22 @@ declare namespace monaco.editor { * If set, the decoration will override the line height of the lines it spans. Maximum value is 300px. */ lineHeight?: number | null; + /** + * Font family + */ + fontFamily?: string | null; + /** + * Font size + */ + fontSize?: string | null; + /** + * Font weight + */ + fontWeight?: string | null; + /** + * Font style + */ + fontStyle?: string | null; /** * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ @@ -2237,35 +2253,39 @@ declare namespace monaco.editor { * @param lineNumber The line number * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. * @return An array with the decorations */ - getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations for the lines between `startLineNumber` and `endLineNumber` as an array. * @param startLineNumber The start line number * @param endLineNumber The end line number * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. * @return An array with the decorations */ - getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. * So for now it returns all the decorations on the same line as `range`. * @param range The range to search in * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. * @param onlyMinimapDecorations If set, it will return only decorations that render in the minimap. * @param onlyMarginDecorations If set, it will return only decorations that render in the glyph margin. * @return An array with the decorations */ - getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, onlyMinimapDecorations?: boolean, onlyMarginDecorations?: boolean): IModelDecoration[]; + getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean, onlyMinimapDecorations?: boolean, onlyMarginDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations as an array. * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. */ - getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getAllDecorations(ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all decorations that render in the glyph margin as an array. * @param ownerId If set, it will ignore decorations belonging to other owners. @@ -2275,8 +2295,9 @@ declare namespace monaco.editor { * Gets all the decorations that should be rendered in the overview ruler as an array. * @param ownerId If set, it will ignore decorations belonging to other owners. * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param filterFontDecorations If set, it will ignore font decorations. */ - getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean, filterFontDecorations?: boolean): IModelDecoration[]; /** * Gets all the decorations that contain injected text. * @param ownerId If set, it will ignore decorations belonging to other owners. @@ -3156,6 +3177,14 @@ declare namespace monaco.editor { * 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). */ @@ -4965,162 +4994,165 @@ declare namespace monaco.editor { accessibilitySupport = 2, accessibilityPageSize = 3, allowVariableLineHeights = 4, - ariaLabel = 5, - ariaRequired = 6, - autoClosingBrackets = 7, - autoClosingComments = 8, - screenReaderAnnounceInlineSuggestion = 9, - autoClosingDelete = 10, - autoClosingOvertype = 11, - autoClosingQuotes = 12, - autoIndent = 13, - autoIndentOnPaste = 14, - autoIndentOnPasteWithinString = 15, - automaticLayout = 16, - autoSurround = 17, - bracketPairColorization = 18, - guides = 19, - codeLens = 20, - codeLensFontFamily = 21, - codeLensFontSize = 22, - colorDecorators = 23, - colorDecoratorsLimit = 24, - columnSelection = 25, - comments = 26, - contextmenu = 27, - copyWithSyntaxHighlighting = 28, - cursorBlinking = 29, - cursorSmoothCaretAnimation = 30, - cursorStyle = 31, - cursorSurroundingLines = 32, - cursorSurroundingLinesStyle = 33, - cursorWidth = 34, - disableLayerHinting = 35, - disableMonospaceOptimizations = 36, - domReadOnly = 37, - dragAndDrop = 38, - dropIntoEditor = 39, - editContext = 40, - emptySelectionClipboard = 41, - experimentalGpuAcceleration = 42, - experimentalWhitespaceRendering = 43, - extraEditorClassName = 44, - fastScrollSensitivity = 45, - find = 46, - fixedOverflowWidgets = 47, - folding = 48, - foldingStrategy = 49, - foldingHighlight = 50, - foldingImportsByDefault = 51, - foldingMaximumRegions = 52, - unfoldOnClickAfterEndOfLine = 53, - fontFamily = 54, - fontInfo = 55, - fontLigatures = 56, - fontSize = 57, - fontWeight = 58, - fontVariations = 59, - formatOnPaste = 60, - formatOnType = 61, - glyphMargin = 62, - gotoLocation = 63, - hideCursorInOverviewRuler = 64, - hover = 65, - inDiffEditor = 66, - inlineSuggest = 67, - letterSpacing = 68, - lightbulb = 69, - lineDecorationsWidth = 70, - lineHeight = 71, - lineNumbers = 72, - lineNumbersMinChars = 73, - linkedEditing = 74, - links = 75, - matchBrackets = 76, - minimap = 77, - mouseStyle = 78, - mouseWheelScrollSensitivity = 79, - mouseWheelZoom = 80, - multiCursorMergeOverlapping = 81, - multiCursorModifier = 82, - multiCursorPaste = 83, - multiCursorLimit = 84, - occurrencesHighlight = 85, - occurrencesHighlightDelay = 86, - overtypeCursorStyle = 87, - overtypeOnPaste = 88, - overviewRulerBorder = 89, - overviewRulerLanes = 90, - padding = 91, - pasteAs = 92, - parameterHints = 93, - peekWidgetDefaultFocus = 94, - placeholder = 95, - definitionLinkOpensInPeek = 96, - quickSuggestions = 97, - quickSuggestionsDelay = 98, - readOnly = 99, - readOnlyMessage = 100, - renameOnType = 101, - renderControlCharacters = 102, - renderFinalNewline = 103, - renderLineHighlight = 104, - renderLineHighlightOnlyWhenFocus = 105, - renderValidationDecorations = 106, - renderWhitespace = 107, - revealHorizontalRightPadding = 108, - roundedSelection = 109, - rulers = 110, - scrollbar = 111, - scrollBeyondLastColumn = 112, - scrollBeyondLastLine = 113, - scrollPredominantAxis = 114, - selectionClipboard = 115, - selectionHighlight = 116, - selectOnLineNumbers = 117, - showFoldingControls = 118, - showUnused = 119, - snippetSuggestions = 120, - smartSelect = 121, - smoothScrolling = 122, - stickyScroll = 123, - stickyTabStops = 124, - stopRenderingLineAfter = 125, - suggest = 126, - suggestFontSize = 127, - suggestLineHeight = 128, - suggestOnTriggerCharacters = 129, - suggestSelection = 130, - tabCompletion = 131, - tabIndex = 132, - unicodeHighlighting = 133, - unusualLineTerminators = 134, - useShadowDOM = 135, - useTabStops = 136, - wordBreak = 137, - wordSegmenterLocales = 138, - wordSeparators = 139, - wordWrap = 140, - wordWrapBreakAfterCharacters = 141, - wordWrapBreakBeforeCharacters = 142, - wordWrapColumn = 143, - wordWrapOverride1 = 144, - wordWrapOverride2 = 145, - wrappingIndent = 146, - wrappingStrategy = 147, - showDeprecated = 148, - inlayHints = 149, - effectiveCursorStyle = 150, - editorClassName = 151, - pixelRatio = 152, - tabFocusMode = 153, - layoutInfo = 154, - wrappingInfo = 155, - defaultColorDecorators = 156, - colorDecoratorsActivatedOn = 157, - inlineCompletionsAccessibilityVerbose = 158, - effectiveEditContext = 159, - scrollOnMiddleClick = 160 + allowVariableFonts = 5, + allowVariableFontsInAccessibilityMode = 6, + ariaLabel = 7, + ariaRequired = 8, + autoClosingBrackets = 9, + autoClosingComments = 10, + screenReaderAnnounceInlineSuggestion = 11, + autoClosingDelete = 12, + autoClosingOvertype = 13, + autoClosingQuotes = 14, + autoIndent = 15, + autoIndentOnPaste = 16, + autoIndentOnPasteWithinString = 17, + automaticLayout = 18, + autoSurround = 19, + bracketPairColorization = 20, + guides = 21, + codeLens = 22, + codeLensFontFamily = 23, + codeLensFontSize = 24, + colorDecorators = 25, + colorDecoratorsLimit = 26, + columnSelection = 27, + comments = 28, + contextmenu = 29, + copyWithSyntaxHighlighting = 30, + cursorBlinking = 31, + cursorSmoothCaretAnimation = 32, + cursorStyle = 33, + cursorSurroundingLines = 34, + cursorSurroundingLinesStyle = 35, + cursorWidth = 36, + disableLayerHinting = 37, + disableMonospaceOptimizations = 38, + domReadOnly = 39, + dragAndDrop = 40, + dropIntoEditor = 41, + editContext = 42, + emptySelectionClipboard = 43, + experimentalGpuAcceleration = 44, + experimentalWhitespaceRendering = 45, + extraEditorClassName = 46, + fastScrollSensitivity = 47, + find = 48, + fixedOverflowWidgets = 49, + folding = 50, + foldingStrategy = 51, + foldingHighlight = 52, + foldingImportsByDefault = 53, + foldingMaximumRegions = 54, + unfoldOnClickAfterEndOfLine = 55, + fontFamily = 56, + fontInfo = 57, + fontLigatures = 58, + fontSize = 59, + fontWeight = 60, + fontVariations = 61, + formatOnPaste = 62, + formatOnType = 63, + glyphMargin = 64, + gotoLocation = 65, + hideCursorInOverviewRuler = 66, + hover = 67, + inDiffEditor = 68, + inlineSuggest = 69, + letterSpacing = 70, + lightbulb = 71, + lineDecorationsWidth = 72, + lineHeight = 73, + lineNumbers = 74, + lineNumbersMinChars = 75, + linkedEditing = 76, + links = 77, + matchBrackets = 78, + minimap = 79, + mouseStyle = 80, + mouseWheelScrollSensitivity = 81, + mouseWheelZoom = 82, + multiCursorMergeOverlapping = 83, + multiCursorModifier = 84, + multiCursorPaste = 85, + multiCursorLimit = 86, + occurrencesHighlight = 87, + occurrencesHighlightDelay = 88, + overtypeCursorStyle = 89, + overtypeOnPaste = 90, + overviewRulerBorder = 91, + overviewRulerLanes = 92, + padding = 93, + pasteAs = 94, + parameterHints = 95, + peekWidgetDefaultFocus = 96, + placeholder = 97, + definitionLinkOpensInPeek = 98, + quickSuggestions = 99, + quickSuggestionsDelay = 100, + readOnly = 101, + readOnlyMessage = 102, + renameOnType = 103, + renderControlCharacters = 104, + renderFinalNewline = 105, + renderLineHighlight = 106, + renderLineHighlightOnlyWhenFocus = 107, + renderValidationDecorations = 108, + renderWhitespace = 109, + revealHorizontalRightPadding = 110, + roundedSelection = 111, + rulers = 112, + scrollbar = 113, + scrollBeyondLastColumn = 114, + scrollBeyondLastLine = 115, + scrollPredominantAxis = 116, + selectionClipboard = 117, + selectionHighlight = 118, + selectOnLineNumbers = 119, + showFoldingControls = 120, + showUnused = 121, + snippetSuggestions = 122, + smartSelect = 123, + smoothScrolling = 124, + stickyScroll = 125, + stickyTabStops = 126, + stopRenderingLineAfter = 127, + suggest = 128, + suggestFontSize = 129, + suggestLineHeight = 130, + suggestOnTriggerCharacters = 131, + suggestSelection = 132, + tabCompletion = 133, + tabIndex = 134, + unicodeHighlighting = 135, + unusualLineTerminators = 136, + useShadowDOM = 137, + useTabStops = 138, + wordBreak = 139, + wordSegmenterLocales = 140, + wordSeparators = 141, + wordWrap = 142, + wordWrapBreakAfterCharacters = 143, + wordWrapBreakBeforeCharacters = 144, + wordWrapColumn = 145, + wordWrapOverride1 = 146, + wordWrapOverride2 = 147, + wrappingIndent = 148, + wrappingStrategy = 149, + showDeprecated = 150, + inlayHints = 151, + effectiveCursorStyle = 152, + editorClassName = 153, + pixelRatio = 154, + tabFocusMode = 155, + layoutInfo = 156, + wrappingInfo = 157, + defaultColorDecorators = 158, + colorDecoratorsActivatedOn = 159, + inlineCompletionsAccessibilityVerbose = 160, + effectiveEditContext = 161, + scrollOnMiddleClick = 162, + effectiveAllowVariableFonts = 163 } export const EditorOptions: { @@ -5129,6 +5161,8 @@ declare namespace monaco.editor { accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; allowVariableLineHeights: IEditorOption; + allowVariableFonts: IEditorOption; + allowVariableFontsInAccessibilityMode: IEditorOption; ariaLabel: IEditorOption; ariaRequired: IEditorOption; screenReaderAnnounceInlineSuggestion: IEditorOption; @@ -5285,6 +5319,7 @@ declare namespace monaco.editor { wrappingIndent: IEditorOption; wrappingStrategy: IEditorOption; effectiveEditContextEnabled: IEditorOption; + effectiveAllowVariableFonts: IEditorOption; }; type EditorOptionsType = typeof EditorOptions; @@ -6143,6 +6178,11 @@ declare namespace monaco.editor { * Get all the decorations for a range (filtering out decorations from other editors). */ getDecorationsInRange(range: Range): IModelDecoration[] | null; + /** + * Get the font size at a given position + * @param position the position for which to fetch the font size + */ + getFontSizeAtPosition(position: IPosition): string | null; /** * All decorations added through this call will get the ownerId of this editor. * @deprecated Use `createDecorationsCollection` diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 56a7c1409b1..5d50d93e9c3 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -44,13 +44,13 @@ class DecorationsTree { this._decorationsTree = new IntervalTree(); } - public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number, onlyMarginDecorations: boolean = false): IntervalNode[] { - const r1 = this._decorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean = false): IntervalNode[] { + const r1 = this._decorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); return r1; } - public search(filterOwnerId: number, filterOutValidation: boolean, overviewRulerOnly: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { - return this._decorationsTree.search(filterOwnerId, filterOutValidation, cachedVersionId, onlyMarginDecorations); + public search(filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] { + return this._decorationsTree.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); }