Skip to content

Commit 966d02a

Browse files
author
annkamsk
committed
Make renderCodicons function return HTMLElement instead of string
1 parent def4753 commit 966d02a

11 files changed

Lines changed: 109 additions & 36 deletions

File tree

src/vs/base/browser/codicons.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 'vs/base/browser/dom';
7+
import { renderCodiconsRegex } from 'vs/base/common/codicons';
8+
9+
export function renderCodiconsAsElement(text: string): Array<HTMLSpanElement | string> {
10+
const elements = new Array<HTMLSpanElement | string>();
11+
let match: RegExpMatchArray | null;
12+
13+
let textStart = 0, textStop = 0;
14+
while ((match = renderCodiconsRegex.exec(text)) !== null) {
15+
textStop = match.index || 0;
16+
elements.push(text.substring(textStart, textStop));
17+
textStart = (match.index || 0) + match[0].length;
18+
19+
const [, escaped, codicon, name, animation] = match;
20+
elements.push(escaped ? `$(${codicon})` : dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`));
21+
}
22+
23+
if (textStart < text.length) {
24+
elements.push(text.substring(textStart));
25+
}
26+
return elements;
27+
}

src/vs/base/browser/ui/button/button.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import { mixin } from 'vs/base/common/objects';
1212
import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
1313
import { Disposable } from 'vs/base/common/lifecycle';
1414
import { Gesture, EventType } from 'vs/base/browser/touch';
15-
import { renderCodicons } from 'vs/base/common/codicons';
16-
import { escape } from 'vs/base/common/strings';
15+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
1716

1817
export interface IButtonOptions extends IButtonStyles {
1918
readonly title?: boolean | string;
@@ -180,7 +179,7 @@ export class Button extends Disposable {
180179
DOM.addClass(this._element, 'monaco-text-button');
181180
}
182181
if (this.options.supportCodicons) {
183-
this._element.innerHTML = renderCodicons(escape(value));
182+
DOM.reset(this._element, ...renderCodiconsAsElement(value));
184183
} else {
185184
this._element.textContent = value;
186185
}

src/vs/base/browser/ui/codicons/codiconLabel.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { escape } from 'vs/base/common/strings';
7-
import { renderCodicons } from 'vs/base/common/codicons';
6+
import { reset } from 'vs/base/browser/dom';
7+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
88

99
export class CodiconLabel {
1010

@@ -13,7 +13,7 @@ export class CodiconLabel {
1313
) { }
1414

1515
set text(text: string) {
16-
this._container.innerHTML = renderCodicons(escape(text ?? ''));
16+
reset(this._container, ...renderCodiconsAsElement(text ?? ''));
1717
}
1818

1919
set title(title: string) {

src/vs/base/common/codicons.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ export function markdownUnescapeCodicons(text: string): string {
498498
return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${codicon})`);
499499
}
500500

501-
const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
501+
export const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
502502
export function renderCodicons(text: string): string {
503503
return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => {
504504
// If the class for codicons is changed, it should also be updated in src\vs\base\browser\markdownRenderer.ts
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 { renderCodiconsAsElement } from 'vs/base/browser/codicons';
7+
import * as assert from 'assert';
8+
9+
suite('renderCodicons', () => {
10+
11+
test('no codicons', () => {
12+
const result = renderCodiconsAsElement(' hello World .');
13+
14+
assert.equal(elementsToString(result), ' hello World .');
15+
});
16+
17+
test('codicon only', () => {
18+
const result = renderCodiconsAsElement('$(alert)');
19+
20+
assert.equal(elementsToString(result), '<span class="codicon codicon-alert"></span>');
21+
});
22+
23+
test('codicon and non-codicon strings', () => {
24+
const result = renderCodiconsAsElement(` $(alert) Unresponsive`);
25+
26+
assert.equal(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
27+
});
28+
29+
test('multiple codicons', () => {
30+
const result = renderCodiconsAsElement('$(check)$(error)');
31+
32+
assert.equal(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
33+
});
34+
35+
test('escaped codicon', () => {
36+
const result = renderCodiconsAsElement('\\$(escaped)');
37+
38+
assert.equal(elementsToString(result), '$(escaped)');
39+
});
40+
41+
test('codicon with animation', () => {
42+
const result = renderCodiconsAsElement('$(zip~anim)');
43+
44+
assert.equal(elementsToString(result), '<span class="codicon codicon-zip codicon-animation-anim"></span>');
45+
});
46+
47+
const elementsToString = (elements: Array<HTMLElement | string>): string => {
48+
return elements
49+
.map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem)
50+
.reduce((a, b) => a + b, '');
51+
};
52+
});

src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
1717
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
1818
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
1919
import { WorkbenchList } from 'vs/platform/list/browser/listService';
20-
import { append, $, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom';
20+
import { append, $, reset, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom';
2121
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
2222
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
2323
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -38,8 +38,7 @@ import { randomPort } from 'vs/base/node/ports';
3838
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
3939
import { IStorageService } from 'vs/platform/storage/common/storage';
4040
import { ILabelService } from 'vs/platform/label/common/label';
41-
import { renderCodicons } from 'vs/base/common/codicons';
42-
import { escape } from 'vs/base/common/strings';
41+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
4342
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
4443
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
4544
import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
@@ -410,32 +409,28 @@ export class RuntimeExtensionsEditor extends EditorPane {
410409
clearNode(data.msgContainer);
411410

412411
if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.identifier)) {
413-
const el = $('span');
414-
el.innerHTML = renderCodicons(escape(` $(alert) Unresponsive`));
412+
const el = $('span', undefined, ...renderCodiconsAsElement(` $(alert) Unresponsive`));
415413
el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze.");
416414
data.msgContainer.appendChild(el);
417415
}
418416

419417
if (isNonEmptyArray(element.status.runtimeErrors)) {
420-
const el = $('span');
421-
el.innerHTML = renderCodicons(escape(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`));
418+
const el = $('span', undefined, ...renderCodiconsAsElement(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`));
422419
data.msgContainer.appendChild(el);
423420
}
424421

425422
if (element.status.messages && element.status.messages.length > 0) {
426-
const el = $('span');
427-
el.innerHTML = renderCodicons(escape(`$(alert) ${element.status.messages[0].message}`));
423+
const el = $('span', undefined, ...renderCodiconsAsElement(`$(alert) ${element.status.messages[0].message}`));
428424
data.msgContainer.appendChild(el);
429425
}
430426

431427
if (element.description.extensionLocation.scheme !== 'file') {
432-
const el = $('span');
433-
el.innerHTML = renderCodicons(escape(`$(remote) ${element.description.extensionLocation.authority}`));
428+
const el = $('span', undefined, ...renderCodiconsAsElement(`$(remote) ${element.description.extensionLocation.authority}`));
434429
data.msgContainer.appendChild(el);
435430

436431
const hostLabel = this._labelService.getHostLabel(REMOTE_HOST_SCHEME, this._environmentService.configuration.remoteAuthority);
437432
if (hostLabel) {
438-
el.innerHTML = renderCodicons(escape(`$(remote) ${hostLabel}`));
433+
reset(el, ...renderCodiconsAsElement(`$(remote) ${hostLabel}`));
439434
}
440435
}
441436

src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_M
1212
import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
1313
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
1414
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
15-
import { renderCodicons } from 'vs/base/common/codicons';
15+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
1616
import { IModelService } from 'vs/editor/common/services/modelService';
1717
import { IModeService } from 'vs/editor/common/services/modeService';
1818
import { format } from 'vs/base/common/jsonFormatter';
@@ -194,9 +194,9 @@ class PropertyHeader extends Disposable {
194194

195195
private _updateFoldingIcon() {
196196
if (this.accessor.getFoldingState(this.cell) === PropertyFoldingState.Collapsed) {
197-
this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)');
197+
DOM.reset(this._foldingIndicator, ...renderCodiconsAsElement('$(chevron-right)'));
198198
} else {
199-
this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)');
199+
DOM.reset(this._foldingIndicator, ...renderCodiconsAsElement('$(chevron-down)'));
200200
}
201201
}
202202
}

src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis
1010
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
1111
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
1212
import { IAction } from 'vs/base/common/actions';
13-
import { renderCodicons } from 'vs/base/common/codicons';
13+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
1414
import { Color } from 'vs/base/common/color';
1515
import { Emitter, Event } from 'vs/base/common/event';
1616
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -341,8 +341,7 @@ abstract class AbstractCellRenderer {
341341
}
342342

343343
protected setupCollapsedPart(container: HTMLElement): { collapsedPart: HTMLElement, expandButton: HTMLElement } {
344-
const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part'));
345-
collapsedPart.innerHTML = renderCodicons('$(unfold)');
344+
const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part', undefined, ...renderCodiconsAsElement('$(unfold)')));
346345
const expandButton = collapsedPart.querySelector('.codicon') as HTMLElement;
347346
const keybinding = this.keybindingService.lookupKeybinding(EXPAND_CELL_CONTENT_COMMAND_ID);
348347
let title = localize('cellExpandButtonLabel', "Expand");
@@ -949,11 +948,11 @@ export class RunStateRenderer {
949948
}
950949

951950
if (runState === NotebookCellRunState.Success) {
952-
this.element.innerHTML = renderCodicons('$(check)');
951+
DOM.reset(this.element, ...renderCodiconsAsElement('$(check)'));
953952
} else if (runState === NotebookCellRunState.Error) {
954-
this.element.innerHTML = renderCodicons('$(error)');
953+
DOM.reset(this.element, ...renderCodiconsAsElement('$(error)'));
955954
} else if (runState === NotebookCellRunState.Running) {
956-
this.element.innerHTML = renderCodicons('$(sync~spin)');
955+
DOM.reset(this.element, ...renderCodiconsAsElement('$(sync~spin)'));
957956

958957
this.spinnerTimer = setTimeout(() => {
959958
this.spinnerTimer = undefined;

src/vs/workbench/contrib/notebook/browser/view/renderers/commonViewComponents.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { renderCodicons } from 'vs/base/common/codicons';
6+
import * as DOM from 'vs/base/browser/dom';
77
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
88
import { MenuItemAction } from 'vs/platform/actions/common/actions';
99
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
1010
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1111
import { INotificationService } from 'vs/platform/notification/common/notification';
12+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
1213

1314
export class CodiconActionViewItem extends MenuEntryActionViewItem {
1415
constructor(
@@ -21,7 +22,7 @@ export class CodiconActionViewItem extends MenuEntryActionViewItem {
2122
}
2223
updateLabel(): void {
2324
if (this.options.label && this.label) {
24-
this.label.innerHTML = renderCodicons(this._commandAction.label ?? '');
25+
DOM.reset(this.label, ...renderCodiconsAsElement(this._commandAction.label ?? ''));
2526
}
2627
}
2728
}

src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as DOM from 'vs/base/browser/dom';
77
import { raceCancellation } from 'vs/base/common/async';
88
import { CancellationTokenSource } from 'vs/base/common/cancellation';
9-
import { renderCodicons } from 'vs/base/common/codicons';
9+
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
1010
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
1111
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
1212
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
@@ -315,10 +315,10 @@ export class StatefulMarkdownCell extends Disposable {
315315
this.templateData.foldingIndicator.innerText = '';
316316
break;
317317
case CellFoldingState.Collapsed:
318-
this.templateData.foldingIndicator.innerHTML = renderCodicons('$(chevron-right)');
318+
DOM.reset(this.templateData.foldingIndicator, ...renderCodiconsAsElement('$(chevron-right)'));
319319
break;
320320
case CellFoldingState.Expanded:
321-
this.templateData.foldingIndicator.innerHTML = renderCodicons('$(chevron-down)');
321+
DOM.reset(this.templateData.foldingIndicator, ...renderCodiconsAsElement('$(chevron-down)'));
322322
break;
323323

324324
default:

0 commit comments

Comments
 (0)