Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts
5257 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as dom from '../../dom.js';
7
import type { IManagedHover } from '../hover/hover.js';
8
import { IHoverDelegate } from '../hover/hoverDelegate.js';
9
import { getBaseLayerHoverDelegate } from '../hover/hoverDelegate2.js';
10
import { getDefaultHoverDelegate } from '../hover/hoverDelegateFactory.js';
11
import { renderLabelWithIcons } from '../iconLabel/iconLabels.js';
12
import { Disposable } from '../../../common/lifecycle.js';
13
import * as objects from '../../../common/objects.js';
14
15
/**
16
* A range to be highlighted.
17
*/
18
export interface IHighlight {
19
start: number;
20
end: number;
21
readonly extraClasses?: readonly string[];
22
}
23
24
export interface IHighlightedLabelOptions {
25
readonly hoverDelegate?: IHoverDelegate;
26
}
27
28
/**
29
* A widget which can render a label with substring highlights, often
30
* originating from a filter function like the fuzzy matcher.
31
*/
32
export class HighlightedLabel extends Disposable {
33
34
private readonly domNode: HTMLElement;
35
private text: string = '';
36
private title: string = '';
37
private highlights: readonly IHighlight[] = [];
38
private didEverRender: boolean = false;
39
private customHover: IManagedHover | undefined;
40
41
/**
42
* Create a new {@link HighlightedLabel}.
43
*
44
* @param container The parent container to append to.
45
*/
46
constructor(container: HTMLElement, private readonly options?: IHighlightedLabelOptions) {
47
super();
48
49
this.domNode = dom.append(container, dom.$('span.monaco-highlighted-label'));
50
}
51
52
/**
53
* The label's DOM node.
54
*/
55
get element(): HTMLElement {
56
return this.domNode;
57
}
58
59
/**
60
* Set the label and highlights.
61
*
62
* @param text The label to display.
63
* @param highlights The ranges to highlight.
64
* @param title An optional title for the hover tooltip.
65
* @param escapeNewLines Whether to escape new lines.
66
* @returns
67
*/
68
set(text: string | undefined, highlights: readonly IHighlight[] = [], title: string = '', escapeNewLines?: boolean, supportIcons?: boolean) {
69
if (!text) {
70
text = '';
71
}
72
73
if (escapeNewLines) {
74
// adjusts highlights inplace
75
text = HighlightedLabel.escapeNewLines(text, highlights);
76
}
77
78
if (this.didEverRender && this.text === text && this.title === title && objects.equals(this.highlights, highlights)) {
79
return;
80
}
81
82
this.text = text;
83
this.title = title;
84
this.highlights = highlights;
85
this.render(supportIcons);
86
}
87
88
private render(supportIcons?: boolean): void {
89
90
const children: Array<HTMLSpanElement | string> = [];
91
let pos = 0;
92
93
for (const highlight of this.highlights) {
94
if (highlight.end === highlight.start) {
95
continue;
96
}
97
98
if (pos < highlight.start) {
99
const substring = this.text.substring(pos, highlight.start);
100
if (supportIcons) {
101
children.push(...renderLabelWithIcons(substring));
102
} else {
103
children.push(substring);
104
}
105
pos = highlight.start;
106
}
107
108
const substring = this.text.substring(pos, highlight.end);
109
const element = dom.$('span.highlight', undefined, ...supportIcons ? renderLabelWithIcons(substring) : [substring]);
110
111
if (highlight.extraClasses) {
112
element.classList.add(...highlight.extraClasses);
113
}
114
115
children.push(element);
116
pos = highlight.end;
117
}
118
119
if (pos < this.text.length) {
120
const substring = this.text.substring(pos,);
121
if (supportIcons) {
122
children.push(...renderLabelWithIcons(substring));
123
} else {
124
children.push(substring);
125
}
126
}
127
128
dom.reset(this.domNode, ...children);
129
130
if (!this.customHover && this.title !== '') {
131
const hoverDelegate = this.options?.hoverDelegate ?? getDefaultHoverDelegate('mouse');
132
this.customHover = this._register(getBaseLayerHoverDelegate().setupManagedHover(hoverDelegate, this.domNode, this.title));
133
} else if (this.customHover) {
134
this.customHover.update(this.title);
135
}
136
137
this.didEverRender = true;
138
}
139
140
static escapeNewLines(text: string, highlights: readonly IHighlight[]): string {
141
let total = 0;
142
let extra = 0;
143
144
return text.replace(/\r\n|\r|\n/g, (match, offset) => {
145
extra = match === '\r\n' ? -1 : 0;
146
offset += total;
147
148
for (const highlight of highlights) {
149
if (highlight.end <= offset) {
150
continue;
151
}
152
if (highlight.start >= offset) {
153
highlight.start += extra;
154
}
155
if (highlight.end >= offset) {
156
highlight.end += extra;
157
}
158
}
159
160
total += extra;
161
return '\u23CE';
162
});
163
}
164
}
165
166