Skip to content

Commit e4159ac

Browse files
feat: support 'px' format in lineHeight option
Allow lineHeight to accept a string with 'px' suffix (e.g. '23px') in addition to the existing numeric multiplier. This enables setting an absolute pixel line height, which is useful when precise control over row spacing is needed regardless of font size. - Update type definitions to accept `number | string` - Add validation for px string format in OptionsService - Handle px values in DomRenderer and WebglRenderer dimension calculations - Update WebGL char atlas config type Fixes #2612
1 parent dedf3b1 commit e4159ac

File tree

6 files changed

+34
-11
lines changed

6 files changed

+34
-11
lines changed

addons/addon-webgl/src/Types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface ICharAtlasConfig {
4040
devicePixelRatio: number;
4141
deviceMaxTextureSize: number;
4242
letterSpacing: number;
43-
lineHeight: number;
43+
lineHeight: number | string;
4444
fontSize: number;
4545
fontFamily: string;
4646
fontWeight: FontWeight;

addons/addon-webgl/src/WebglRenderer.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -623,14 +623,17 @@ export class WebglRenderer extends Disposable implements IRenderer {
623623
// cell.
624624
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio);
625625

626-
// Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be
627-
// floored since lineHeight can never be lower then 1, this guarentees the device cell height
628-
// will always be larger than device char height.
629-
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
626+
// Calculate the device cell height, if lineHeight is _not_ 1 (or is a px string), the
627+
// resulting value will be floored since lineHeight can never be lower than 1, this guarantees
628+
// the device cell height will always be larger than device char height.
629+
const lineHeight = this._optionsService.rawOptions.lineHeight;
630+
this.dimensions.device.cell.height = typeof lineHeight === 'string'
631+
? Math.max(Math.floor(parseFloat(lineHeight) * this._devicePixelRatio), this.dimensions.device.char.height)
632+
: Math.floor(this.dimensions.device.char.height * lineHeight);
630633

631634
// Calculate the y offset within a cell that glyph should draw at in order for it to be centered
632635
// correctly within the cell.
633-
this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2);
636+
this.dimensions.device.char.top = this.dimensions.device.cell.height === this.dimensions.device.char.height ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2);
634637

635638
// Calculate the device cell width, taking the letterSpacing into account.
636639
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);

src/browser/renderer/dom/DomRenderer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ export class DomRenderer extends Disposable implements IRenderer {
133133
this.dimensions.device.char.width = this._charSizeService.width * dpr;
134134
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * dpr);
135135
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);
136-
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
136+
const lineHeight = this._optionsService.rawOptions.lineHeight;
137+
this.dimensions.device.cell.height = typeof lineHeight === 'string'
138+
? Math.max(Math.floor(parseFloat(lineHeight) * dpr), this.dimensions.device.char.height)
139+
: Math.floor(this.dimensions.device.char.height * lineHeight);
137140
this.dimensions.device.char.left = 0;
138141
this.dimensions.device.char.top = 0;
139142
this.dimensions.device.canvas.width = this.dimensions.device.cell.width * this._bufferService.cols;

src/common/services/OptionsService.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,26 @@ export class OptionsService extends Disposable implements IOptionsService {
178178
case 'cursorWidth':
179179
value = Math.floor(value);
180180
// Fall through for bounds check
181-
case 'lineHeight':
182181
case 'tabStopWidth':
183182
if (value < 1) {
184183
throw new Error(`${key} cannot be less than 1, value: ${value}`);
185184
}
186185
break;
186+
case 'lineHeight':
187+
if (typeof value === 'string') {
188+
if (!value.endsWith('px')) {
189+
throw new Error(`${key} string value must end with 'px', value: ${value}`);
190+
}
191+
const parsed = parseFloat(value);
192+
if (isNaN(parsed) || parsed < 1) {
193+
throw new Error(`${key} must be a number >= 1 when using px format, value: ${value}`);
194+
}
195+
} else if (typeof value === 'number') {
196+
if (value < 1) {
197+
throw new Error(`${key} cannot be less than 1, value: ${value}`);
198+
}
199+
}
200+
break;
187201
case 'minimumContrastRatio':
188202
value = Math.max(1, Math.min(21, Math.round(value * 10) / 10));
189203
break;

src/common/services/Services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export interface ITerminalOptions {
243243
fontWeightBold?: FontWeight;
244244
ignoreBracketedPasteMode?: boolean;
245245
letterSpacing?: number;
246-
lineHeight?: number;
246+
lineHeight?: number | string;
247247
linkHandler?: ILinkHandler | null;
248248
logLevel?: LogLevel;
249249
logger?: ILogger | null;

typings/xterm.d.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,12 @@ declare module '@xterm/xterm' {
145145
letterSpacing?: number;
146146

147147
/**
148-
* The line height used to render text.
148+
* The line height used to render text. When a number is given, it is used
149+
* as a multiplier of the character height (must be >= 1). When a string
150+
* ending in `'px'` is given (e.g. `'23px'`), it sets an absolute pixel
151+
* line height (the parsed value must be >= 1).
149152
*/
150-
lineHeight?: number;
153+
lineHeight?: number | string;
151154

152155
/**
153156
* The handler for OSC 8 hyperlinks. Links will use the `confirm` browser

0 commit comments

Comments
 (0)