Skip to content

Commit e7f72a1

Browse files
authored
Merge pull request #2857 from Tyriar/decoration_customizations
Allow embedders to customize link decorations independently
2 parents 67fde3e + d0519d6 commit e7f72a1

4 files changed

Lines changed: 74 additions & 27 deletions

File tree

src/browser/Linkifier2.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ import { MockBufferService } from 'common/TestUtils.test';
1010
import { ILink } from 'browser/Types';
1111

1212
class TestLinkifier2 extends Linkifier2 {
13+
protected _currentLinkState = {
14+
decorations: {
15+
underline: true,
16+
pointerCursor: true
17+
},
18+
isHovered: true
19+
};
20+
1321
constructor(bufferService: IBufferService) {
1422
super(bufferService);
1523
}
@@ -47,7 +55,7 @@ describe('Linkifier2', () => {
4755
activate: () => { }
4856
};
4957

50-
it('onLinkHover event range is correct', done => {
58+
it('onShowLinkUnderline event range is correct', done => {
5159
linkifier.onShowLinkUnderline(e => {
5260
assert.equal(link.range.start.x - 1, e.x1);
5361
assert.equal(link.range.start.y - 1, e.y1);
@@ -60,7 +68,7 @@ describe('Linkifier2', () => {
6068
linkifier.linkHover({ classList: { add: () => { } } } as any, link, {} as any);
6169
});
6270

63-
it('onLinkLeave event range is correct', done => {
71+
it('onHideLinkUnderline event range is correct', done => {
6472
linkifier.onHideLinkUnderline(e => {
6573
assert.equal(link.range.start.x - 1, e.x1);
6674
assert.equal(link.range.start.y - 1, e.y1);

src/browser/Linkifier2.ts

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
* @license MIT
44
*/
55

6-
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent } from './Types';
6+
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations } from './Types';
77
import { IDisposable } from 'common/Types';
88
import { IMouseService, IRenderService } from './services/Services';
99
import { IBufferService } from 'common/services/Services';
1010
import { EventEmitter, IEvent } from 'common/EventEmitter';
1111

1212
interface ILinkState {
13-
hideDecorations: boolean;
13+
decorations: ILinkDecorations;
1414
isHovered: boolean;
1515
}
1616

@@ -20,7 +20,7 @@ export class Linkifier2 implements ILinkifier2 {
2020
private _renderService: IRenderService | undefined;
2121
private _linkProviders: ILinkProvider[] = [];
2222
private _currentLink: ILink | undefined;
23-
private _currentLinkState: ILinkState | undefined;
23+
protected _currentLinkState: ILinkState | undefined;
2424
private _lastMouseEvent: MouseEvent | undefined;
2525
private _linkCacheDisposables: IDisposable[] = [];
2626
private _lastBufferCell: IBufferCellPosition | undefined;
@@ -199,21 +199,35 @@ export class Linkifier2 implements ILinkifier2 {
199199
if (this._linkAtPosition(link, position)) {
200200
this._currentLink = link;
201201
this._currentLinkState = {
202-
hideDecorations: link.hideDecorations || false,
202+
decorations: {
203+
underline: link.decorations === undefined ? true : link.decorations.underline,
204+
pointerCursor: link.decorations === undefined ? true : link.decorations.pointerCursor
205+
},
203206
isHovered: true
204207
};
205208
this._linkHover(this._element, link, this._lastMouseEvent);
206209

207-
// Add listener for tracking hideDecorations changes
208-
Object.defineProperties(link, {
209-
hideDecorations: {
210-
get: () => this._currentLinkState?.hideDecorations,
210+
// Add listener for tracking decorations changes
211+
link.decorations = {} as ILinkDecorations;
212+
Object.defineProperties(link.decorations, {
213+
pointerCursor: {
214+
get: () => this._currentLinkState?.decorations.pointerCursor,
211215
set: v => {
212-
if (this._currentLinkState && this._currentLinkState.hideDecorations !== v) {
213-
this._currentLinkState.hideDecorations = v;
214-
if (this._currentLinkState?.isHovered) {
215-
this._fireUnderlineEvent(link, !v);
216-
this._element?.classList.toggle('xterm-cursor-pointer', !v);
216+
if (this._currentLinkState && this._currentLinkState?.decorations.pointerCursor !== v) {
217+
this._currentLinkState.decorations.pointerCursor = v;
218+
if (this._currentLinkState.isHovered) {
219+
this._element?.classList.toggle('xterm-cursor-pointer', v);
220+
}
221+
}
222+
}
223+
},
224+
underline: {
225+
get: () => this._currentLinkState?.decorations.underline,
226+
set: v => {
227+
if (this._currentLinkState && this._currentLinkState?.decorations.underline !== v) {
228+
this._currentLinkState.decorations.underline = v;
229+
if (this._currentLinkState.isHovered) {
230+
this._fireUnderlineEvent(link, v);
217231
}
218232
}
219233
}
@@ -230,12 +244,14 @@ export class Linkifier2 implements ILinkifier2 {
230244
}
231245

232246
protected _linkHover(element: HTMLElement, link: ILink, event: MouseEvent): void {
233-
if (!link.hideDecorations) {
234-
this._fireUnderlineEvent(link, true);
235-
element.classList.add('xterm-cursor-pointer');
236-
}
237247
if (this._currentLinkState) {
238248
this._currentLinkState.isHovered = true;
249+
if (this._currentLinkState.decorations.underline) {
250+
this._fireUnderlineEvent(link, true);
251+
}
252+
if (this._currentLinkState.decorations.pointerCursor) {
253+
element.classList.add('xterm-cursor-pointer');
254+
}
239255
}
240256

241257
if (link.hover) {
@@ -252,12 +268,14 @@ export class Linkifier2 implements ILinkifier2 {
252268
}
253269

254270
protected _linkLeave(element: HTMLElement, link: ILink, event: MouseEvent): void {
255-
if (!link.hideDecorations) {
256-
this._fireUnderlineEvent(link, false);
257-
element.classList.remove('xterm-cursor-pointer');
258-
}
259271
if (this._currentLinkState) {
260272
this._currentLinkState.isHovered = false;
273+
if (this._currentLinkState.decorations.underline) {
274+
this._fireUnderlineEvent(link, false);
275+
}
276+
if (this._currentLinkState.decorations.pointerCursor) {
277+
element.classList.remove('xterm-cursor-pointer');
278+
}
261279
}
262280

263281
if (link.leave) {

src/browser/Types.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,17 @@ interface ILinkProvider {
172172
interface ILink {
173173
range: IBufferRange;
174174
text: string;
175-
hideDecorations?: boolean;
175+
decorations?: ILinkDecorations;
176176
activate(event: MouseEvent, text: string): void;
177177
hover?(event: MouseEvent, text: string): void;
178178
leave?(event: MouseEvent, text: string): void;
179179
}
180180

181+
interface ILinkDecorations {
182+
pointerCursor: boolean;
183+
underline: boolean;
184+
}
185+
181186
interface IBufferRange {
182187
start: IBufferCellPosition;
183188
end: IBufferCellPosition;

typings/xterm.d.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,10 +1121,11 @@ declare module 'xterm' {
11211121
text: string;
11221122

11231123
/**
1124-
* Whether to hide the underline and cursor styles, this property is tracked and changes made
1125-
* after the link is provided will trigger changes.
1124+
* What link decorations to show when hovering the link, this property is tracked and changes
1125+
* made after the link is provided will trigger changes. If not set, all decroations will be
1126+
* enabled.
11261127
*/
1127-
hideDecorations?: boolean;
1128+
decorations?: ILinkDecorations;
11281129

11291130
/**
11301131
* Calls when the link is activated.
@@ -1150,6 +1151,21 @@ declare module 'xterm' {
11501151
leave?(event: MouseEvent, text: string): void;
11511152
}
11521153

1154+
/**
1155+
* A set of decorations that can be applied to links.
1156+
*/
1157+
interface ILinkDecorations {
1158+
/**
1159+
* Whether the cursor is set to pointer.
1160+
*/
1161+
pointerCursor: boolean;
1162+
1163+
/**
1164+
* Whether the underline is visible
1165+
*/
1166+
underline: boolean;
1167+
}
1168+
11531169
/**
11541170
* A range within a buffer.
11551171
*/

0 commit comments

Comments
 (0)