Skip to content

Commit cdec1e5

Browse files
authored
Merge pull request #4526 from Tyriar/4499_overline
Add support for SGR 53 overline and SGR 55 not overline
2 parents e9b0593 + eb56356 commit cdec1e5

File tree

9 files changed

+57
-4
lines changed

9 files changed

+57
-4
lines changed

css/xterm.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,16 @@
172172
.xterm-underline-4 { text-decoration: dotted underline; }
173173
.xterm-underline-5 { text-decoration: dashed underline; }
174174

175+
.xterm-overline {
176+
text-decoration: overline;
177+
}
178+
179+
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
180+
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
181+
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
182+
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
183+
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
184+
175185
.xterm-strikethrough {
176186
text-decoration: line-through;
177187
}

demo/client.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,9 @@ function sgrTest(): void {
976976
{ ps: 45, name: 'Background Magenta' },
977977
{ ps: 46, name: 'Background Cyan' },
978978
{ ps: 47, name: 'Background White' },
979-
{ ps: 49, name: 'Background default' }
979+
{ ps: 49, name: 'Background default' },
980+
{ ps: 53, name: 'Overlined' },
981+
{ ps: 55, name: 'Not overlined' }
980982
];
981983
const maxNameLength = entries.reduce<number>((p, c) => Math.max(c.name.length, p), 0);
982984
for (const e of entries) {
@@ -988,7 +990,8 @@ function sgrTest(): void {
988990
}
989991
const comboEntries: { ps: number[] }[] = [
990992
{ ps: [1, 2, 3, 4, 5, 6, 7, 9] },
991-
{ ps: [2, 41] }
993+
{ ps: [2, 41] },
994+
{ ps: [4, 53] }
992995
];
993996
term.write('\n\n\r');
994997
term.writeln(`Combinations`);

src/browser/renderer/dom/DomRendererRowFactory.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ describe('DomRendererRowFactory', () => {
167167
});
168168
});
169169

170+
it('should add class for overline', () => {
171+
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
172+
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.OVERLINE;
173+
lineData.setCell(0, cell);
174+
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_ELEM_MAPPING);
175+
assert.equal(getFragmentHtml(fragment),
176+
'<span class="xterm-overline">a</span>'
177+
);
178+
});
179+
170180
it('should add class for strikethrough', () => {
171181
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
172182
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.STRIKETHROUGH;

src/browser/renderer/dom/DomRendererRowFactory.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const BOLD_CLASS = 'xterm-bold';
1818
export const DIM_CLASS = 'xterm-dim';
1919
export const ITALIC_CLASS = 'xterm-italic';
2020
export const UNDERLINE_CLASS = 'xterm-underline';
21+
export const OVERLINE_CLASS = 'xterm-overline';
2122
export const STRIKETHROUGH_CLASS = 'xterm-strikethrough';
2223
export const CURSOR_CLASS = 'xterm-cursor';
2324
export const CURSOR_BLINK_CLASS = 'xterm-cursor-blink';
@@ -185,6 +186,13 @@ export class DomRendererRowFactory {
185186
}
186187
}
187188

189+
if (cell.isOverline()) {
190+
charElement.classList.add(OVERLINE_CLASS);
191+
if (charElement.textContent === ' ') {
192+
charElement.textContent = '\xa0'; // = &nbsp;
193+
}
194+
}
195+
188196
if (cell.isStrikethrough()) {
189197
charElement.classList.add(STRIKETHROUGH_CLASS);
190198
}

src/browser/renderer/shared/TextureAtlas.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ export class TextureAtlas implements ITextureAtlas {
449449
const italic = !!this._workAttributeData.isItalic();
450450
const underline = !!this._workAttributeData.isUnderline();
451451
const strikethrough = !!this._workAttributeData.isStrikethrough();
452+
const overline = !!this._workAttributeData.isOverline();
452453
let fgColor = this._workAttributeData.getFgColor();
453454
let fgColorMode = this._workAttributeData.getFgColorMode();
454455
let bgColor = this._workAttributeData.getBgColor();
@@ -632,12 +633,24 @@ export class TextureAtlas implements ITextureAtlas {
632633
}
633634
}
634635

636+
// Overline
637+
if (overline) {
638+
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 15));
639+
const yOffset = lineWidth % 2 === 1 ? 0.5 : 0;
640+
this._tmpCtx.lineWidth = lineWidth;
641+
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
642+
this._tmpCtx.beginPath();
643+
this._tmpCtx.moveTo(padding, padding + yOffset);
644+
this._tmpCtx.lineTo(padding + this._config.deviceCharWidth * chWidth, padding + yOffset);
645+
this._tmpCtx.stroke();
646+
}
647+
635648
// Draw the character
636649
if (!customGlyph) {
637650
this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight);
638651
}
639652

640-
// If this charcater is underscore and beyond the cell bounds, shift it up until it is visible
653+
// If this character is underscore and beyond the cell bounds, shift it up until it is visible
641654
// even on the bottom row, try for a maximum of 5 pixels.
642655
if (chars === '_' && !this._config.allowTransparency) {
643656
let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck);

src/common/InputHandler.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,6 +2551,12 @@ export class InputHandler extends Disposable implements IInputHandler {
25512551
} else if (p === 38 || p === 48 || p === 58) {
25522552
// fg color 256 and RGB
25532553
i += this._extractColor(params, i, attr);
2554+
} else if (p === 53) {
2555+
// overline
2556+
attr.bg |= BgFlags.OVERLINE;
2557+
} else if (p === 55) {
2558+
// not overline
2559+
attr.bg &= ~BgFlags.OVERLINE;
25542560
} else if (p === 59) {
25552561
attr.extended = attr.extended.clone();
25562562
attr.extended.underlineColor = -1;

src/common/Types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export interface IAttributeData {
165165
isDim(): number;
166166
isStrikethrough(): number;
167167
isProtected(): number;
168+
isOverline(): number;
168169

169170
/**
170171
* The color mode of the foreground color which determines how to decode {@link getFgColor},

src/common/buffer/AttributeData.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export class AttributeData implements IAttributeData {
4747
public isDim(): number { return this.bg & BgFlags.DIM; }
4848
public isStrikethrough(): number { return this.fg & FgFlags.STRIKETHROUGH; }
4949
public isProtected(): number { return this.bg & BgFlags.PROTECTED; }
50+
public isOverline(): number { return this.bg & BgFlags.OVERLINE; }
5051

5152
// color modes
5253
public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; }

src/common/buffer/Constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ export const enum BgFlags {
128128
ITALIC = 0x4000000,
129129
DIM = 0x8000000,
130130
HAS_EXTENDED = 0x10000000,
131-
PROTECTED = 0x20000000
131+
PROTECTED = 0x20000000,
132+
OVERLINE = 0x40000000
132133
}
133134

134135
export const enum ExtFlags {

0 commit comments

Comments
 (0)