From b9cd07d8bf7a3dc6654209f439f7cacba3047f18 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 14 Aug 2023 11:44:28 -0700 Subject: [PATCH] move html link creation to linkify function --- extensions/notebook-renderers/src/ansi.ts | 7 +----- extensions/notebook-renderers/src/linkify.ts | 26 ++++++++++++++------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/extensions/notebook-renderers/src/ansi.ts b/extensions/notebook-renderers/src/ansi.ts index bf3516b0252..0a2c5ff9c7e 100644 --- a/extensions/notebook-renderers/src/ansi.ts +++ b/extensions/notebook-renderers/src/ansi.ts @@ -396,14 +396,9 @@ function appendStylizedStringToContainer( let container = document.createElement('span'); - if (trustHtml) { - const trustedHtml = ttPolicy?.createHTML(stringContent) ?? stringContent; - container.innerHTML = trustedHtml as string; - } - if (container.childElementCount === 0) { // plain text - container = linkify(stringContent, true, workspaceFolder); + container = linkify(stringContent, true, workspaceFolder, trustHtml); } container.className = cssClasses.join(' '); diff --git a/extensions/notebook-renderers/src/linkify.ts b/extensions/notebook-renderers/src/linkify.ts index c466c9e8c65..ec7415a44b9 100644 --- a/extensions/notebook-renderers/src/linkify.ts +++ b/extensions/notebook-renderers/src/linkify.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ttPolicy } from './htmlHelper'; + const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f'; const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug'); @@ -13,10 +15,11 @@ const POSIX_PATH = /(?<=^|\s)((?:\~|\.)?(?:\/[\w\.-]*)+)/; const LINE_COLUMN = /(?:\:([\d]+))?(?:\:([\d]+))?/; const isWindows = (typeof navigator !== 'undefined') ? navigator.userAgent && navigator.userAgent.indexOf('Windows') >= 0 : false; const PATH_LINK_REGEX = new RegExp(`${isWindows ? WIN_PATH.source : POSIX_PATH.source}${LINE_COLUMN.source}`, 'g'); +const HTML_LINK_REGEX = /]*?\s+)?href=(["'])(.*?)\1[^>]*?>.*?<\/a>/gi; const MAX_LENGTH = 2000; -type LinkKind = 'web' | 'path' | 'text'; +type LinkKind = 'web' | 'path' | 'html' | 'text'; type LinkPart = { kind: LinkKind; value: string; @@ -36,7 +39,7 @@ export class LinkDetector { * When splitLines is true, each line of the text, even if it contains no links, is wrapped in a * and added as a child of the returned . */ - linkify(text: string, splitLines?: boolean, workspaceFolder?: string): HTMLElement { + linkify(text: string, splitLines?: boolean, workspaceFolder?: string, trustHtml?: boolean): HTMLElement { if (splitLines) { const lines = text.split('\n'); for (let i = 0; i < lines.length - 1; i++) { @@ -46,7 +49,7 @@ export class LinkDetector { // Remove the last element ('') that split added. lines.pop(); } - const elements = lines.map(line => this.linkify(line, false, workspaceFolder)); + const elements = lines.map(line => this.linkify(line, false, workspaceFolder, trustHtml)); if (elements.length === 1) { // Do not wrap single line with extra span. return elements[0]; @@ -67,6 +70,15 @@ export class LinkDetector { case 'path': container.appendChild(this.createWebLink(part.value)); break; + case 'html': + if (ttPolicy && trustHtml) { + const span = document.createElement('span'); + span.innerHTML = ttPolicy.createHTML(part.value).toString(); + container.appendChild(span); + } else { + container.appendChild(document.createTextNode(part.value)); + } + break; } } catch (e) { container.appendChild(document.createTextNode(part.value)); @@ -130,8 +142,8 @@ export class LinkDetector { return [{ kind: 'text', value: text, captures: [] }]; } - const regexes: RegExp[] = [WEB_LINK_REGEX, PATH_LINK_REGEX]; - const kinds: LinkKind[] = ['web', 'path']; + const regexes: RegExp[] = [WEB_LINK_REGEX, PATH_LINK_REGEX, HTML_LINK_REGEX]; + const kinds: LinkKind[] = ['web', 'path', 'html']; const result: LinkPart[] = []; const splitOne = (text: string, regexIndex: number) => { @@ -168,6 +180,6 @@ export class LinkDetector { } const linkDetector = new LinkDetector(); -export function linkify(text: string, splitLines?: boolean, workspaceFolder?: string) { - return linkDetector.linkify(text, splitLines, workspaceFolder); +export function linkify(text: string, splitLines?: boolean, workspaceFolder?: string, trustHtml = false) { + return linkDetector.linkify(text, splitLines, workspaceFolder, trustHtml); }