Skip to content

Commit

Permalink
Merge pull request #105589 from annkamsk/render-codicons-elements
Browse files Browse the repository at this point in the history
Make renderCodicons function return HTMLElement instead of string
  • Loading branch information
jrieken committed Sep 1, 2020
2 parents fee0167 + 966d02a commit 11479ca
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 36 deletions.
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}` : ''}`));
}

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

0 comments on commit 11479ca

Please sign in to comment.