Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make renderCodicons function return HTMLElement instead of string #105589

Merged
merged 1 commit into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/vs/base/browser/codicons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as dom from 'vs/base/browser/dom';
import { renderCodiconsRegex } from 'vs/base/common/codicons';

export function renderCodiconsAsElement(text: string): Array<HTMLSpanElement | string> {
const elements = new Array<HTMLSpanElement | string>();
let match: RegExpMatchArray | null;

let textStart = 0, textStop = 0;
while ((match = renderCodiconsRegex.exec(text)) !== null) {
textStop = match.index || 0;
elements.push(text.substring(textStart, textStop));
textStart = (match.index || 0) + match[0].length;

const [, escaped, codicon, name, animation] = match;
elements.push(escaped ? `$(${codicon})` : dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`));
annkamsk marked this conversation as resolved.
Show resolved Hide resolved
}

if (textStart < text.length) {
elements.push(text.substring(textStart));
}
return elements;
}
5 changes: 2 additions & 3 deletions src/vs/base/browser/ui/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { mixin } from 'vs/base/common/objects';
import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Gesture, EventType } from 'vs/base/browser/touch';
import { renderCodicons } from 'vs/base/common/codicons';
import { escape } from 'vs/base/common/strings';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';

export interface IButtonOptions extends IButtonStyles {
readonly title?: boolean | string;
Expand Down Expand Up @@ -180,7 +179,7 @@ export class Button extends Disposable {
DOM.addClass(this._element, 'monaco-text-button');
}
if (this.options.supportCodicons) {
this._element.innerHTML = renderCodicons(escape(value));
DOM.reset(this._element, ...renderCodiconsAsElement(value));
} else {
this._element.textContent = value;
}
Expand Down
6 changes: 3 additions & 3 deletions src/vs/base/browser/ui/codicons/codiconLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { escape } from 'vs/base/common/strings';
import { renderCodicons } from 'vs/base/common/codicons';
import { reset } from 'vs/base/browser/dom';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';

export class CodiconLabel {

Expand All @@ -13,7 +13,7 @@ export class CodiconLabel {
) { }

set text(text: string) {
this._container.innerHTML = renderCodicons(escape(text ?? ''));
reset(this._container, ...renderCodiconsAsElement(text ?? ''));
}

set title(title: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/common/codicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ export function markdownUnescapeCodicons(text: string): string {
return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${codicon})`);
}

const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
export const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
export function renderCodicons(text: string): string {
return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => {
// If the class for codicons is changed, it should also be updated in src\vs\base\browser\markdownRenderer.ts
Expand Down
52 changes: 52 additions & 0 deletions src/vs/base/test/browser/codicons.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import * as assert from 'assert';

suite('renderCodicons', () => {

test('no codicons', () => {
const result = renderCodiconsAsElement(' hello World .');

assert.equal(elementsToString(result), ' hello World .');
});

test('codicon only', () => {
const result = renderCodiconsAsElement('$(alert)');

assert.equal(elementsToString(result), '<span class="codicon codicon-alert"></span>');
});

test('codicon and non-codicon strings', () => {
const result = renderCodiconsAsElement(` $(alert) Unresponsive`);

assert.equal(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
});

test('multiple codicons', () => {
const result = renderCodiconsAsElement('$(check)$(error)');

assert.equal(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
});

test('escaped codicon', () => {
const result = renderCodiconsAsElement('\\$(escaped)');

assert.equal(elementsToString(result), '$(escaped)');
});

test('codicon with animation', () => {
const result = renderCodiconsAsElement('$(zip~anim)');

assert.equal(elementsToString(result), '<span class="codicon codicon-zip codicon-animation-anim"></span>');
});

const elementsToString = (elements: Array<HTMLElement | string>): string => {
return elements
.map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem)
.reduce((a, b) => a + b, '');
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { append, $, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom';
import { append, $, reset, addClass, toggleClass, Dimension, clearNode } from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
Expand All @@ -38,8 +38,7 @@ import { randomPort } from 'vs/base/node/ports';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ILabelService } from 'vs/platform/label/common/label';
import { renderCodicons } from 'vs/base/common/codicons';
import { escape } from 'vs/base/common/strings';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
Expand Down Expand Up @@ -410,32 +409,28 @@ export class RuntimeExtensionsEditor extends EditorPane {
clearNode(data.msgContainer);

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

if (isNonEmptyArray(element.status.runtimeErrors)) {
const el = $('span');
el.innerHTML = renderCodicons(escape(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`));
const el = $('span', undefined, ...renderCodiconsAsElement(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`));
data.msgContainer.appendChild(el);
}

if (element.status.messages && element.status.messages.length > 0) {
const el = $('span');
el.innerHTML = renderCodicons(escape(`$(alert) ${element.status.messages[0].message}`));
const el = $('span', undefined, ...renderCodiconsAsElement(`$(alert) ${element.status.messages[0].message}`));
data.msgContainer.appendChild(el);
}

if (element.description.extensionLocation.scheme !== 'file') {
const el = $('span');
el.innerHTML = renderCodicons(escape(`$(remote) ${element.description.extensionLocation.authority}`));
const el = $('span', undefined, ...renderCodiconsAsElement(`$(remote) ${element.description.extensionLocation.authority}`));
data.msgContainer.appendChild(el);

const hostLabel = this._labelService.getHostLabel(REMOTE_HOST_SCHEME, this._environmentService.configuration.remoteAuthority);
if (hostLabel) {
el.innerHTML = renderCodicons(escape(`$(remote) ${hostLabel}`));
reset(el, ...renderCodiconsAsElement(`$(remote) ${hostLabel}`));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { CellDiffRenderTemplate, CellDiffViewModelLayoutChangeEvent, DIFF_CELL_M
import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { renderCodicons } from 'vs/base/common/codicons';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { format } from 'vs/base/common/jsonFormatter';
Expand Down Expand Up @@ -194,9 +194,9 @@ class PropertyHeader extends Disposable {

private _updateFoldingIcon() {
if (this.accessor.getFoldingState(this.cell) === PropertyFoldingState.Collapsed) {
this._foldingIndicator.innerHTML = renderCodicons('$(chevron-right)');
DOM.reset(this._foldingIndicator, ...renderCodiconsAsElement('$(chevron-right)'));
} else {
this._foldingIndicator.innerHTML = renderCodicons('$(chevron-down)');
DOM.reset(this._foldingIndicator, ...renderCodiconsAsElement('$(chevron-down)'));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IAction } from 'vs/base/common/actions';
import { renderCodicons } from 'vs/base/common/codicons';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
Expand Down Expand Up @@ -341,8 +341,7 @@ abstract class AbstractCellRenderer {
}

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

if (runState === NotebookCellRunState.Success) {
this.element.innerHTML = renderCodicons('$(check)');
DOM.reset(this.element, ...renderCodiconsAsElement('$(check)'));
} else if (runState === NotebookCellRunState.Error) {
this.element.innerHTML = renderCodicons('$(error)');
DOM.reset(this.element, ...renderCodiconsAsElement('$(error)'));
} else if (runState === NotebookCellRunState.Running) {
this.element.innerHTML = renderCodicons('$(sync~spin)');
DOM.reset(this.element, ...renderCodiconsAsElement('$(sync~spin)'));

this.spinnerTimer = setTimeout(() => {
this.spinnerTimer = undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { renderCodicons } from 'vs/base/common/codicons';
import * as DOM from 'vs/base/browser/dom';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { MenuItemAction } from 'vs/platform/actions/common/actions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';

export class CodiconActionViewItem extends MenuEntryActionViewItem {
constructor(
Expand All @@ -21,7 +22,7 @@ export class CodiconActionViewItem extends MenuEntryActionViewItem {
}
updateLabel(): void {
if (this.options.label && this.label) {
this.label.innerHTML = renderCodicons(this._commandAction.label ?? '');
DOM.reset(this.label, ...renderCodiconsAsElement(this._commandAction.label ?? ''));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import * as DOM from 'vs/base/browser/dom';
import { raceCancellation } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { renderCodicons } from 'vs/base/common/codicons';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
Expand Down Expand Up @@ -315,10 +315,10 @@ export class StatefulMarkdownCell extends Disposable {
this.templateData.foldingIndicator.innerText = '';
break;
case CellFoldingState.Collapsed:
this.templateData.foldingIndicator.innerHTML = renderCodicons('$(chevron-right)');
DOM.reset(this.templateData.foldingIndicator, ...renderCodiconsAsElement('$(chevron-right)'));
break;
case CellFoldingState.Expanded:
this.templateData.foldingIndicator.innerHTML = renderCodicons('$(chevron-down)');
DOM.reset(this.templateData.foldingIndicator, ...renderCodiconsAsElement('$(chevron-down)'));
break;

default:
Expand Down
6 changes: 3 additions & 3 deletions src/vs/workbench/contrib/scm/browser/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { createAndFillInActionBarActions, createAndFillInContextMenuActions } fr
import { equals } from 'vs/base/common/arrays';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { renderCodicons } from 'vs/base/common/codicons';
import { renderCodiconsAsElement } from 'vs/base/browser/codicons';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { Command } from 'vs/editor/common/modes';
import { escape } from 'vs/base/common/strings';
import { basename } from 'vs/base/common/resources';
import { Iterable } from 'vs/base/common/iterator';
import { reset } from 'vs/base/browser/dom';

export function isSCMRepository(element: any): element is ISCMRepository {
return !!(element as ISCMRepository).provider && typeof (element as ISCMRepository).setSelected === 'function';
Expand Down Expand Up @@ -105,7 +105,7 @@ export class StatusBarActionViewItem extends ActionViewItem {

updateLabel(): void {
if (this.options.label && this.label) {
this.label.innerHTML = renderCodicons(escape(this.getAction().label));
reset(this.label, ...renderCodiconsAsElement(this.getAction().label));
}
}
}
Expand Down