Change:
<!-- Do you have a question? Please ask it on --<!-- Use Help Report Issue to prefill these. --- VSCode Version:1.22.1 OS Version:win10 Steps to Reproduce: use package to colorful my output (('hello')+" world") this will output string "hello world" that "hello" with color green(debug console,terminal) this work fine in v1.21.1 upgrade to v.1.22.1,it still work fine in terminal,but not work in debug console, all the text color is green,not only 'hello' terminal: ! debug console: ! <!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes/No yes
I also noticed this after the update. !
this seems to have broke with your pr Would you be interested in fixing this issue?
Yeah I definately can, but not for a little while. If someone else wants to take a crack at it in the mean time go right ahead!
great. Tackle it when you have time then, assigning this to May so we do not hurry.
Hey I'm working on this issue now. While I was looking at the code again, I've noticed discrepencies in the way that various ANSI codes are being handled. This, in combination with the issue that prompted my changes originally, have led to a rewrite of the whole ANSI handling function. It should be much more robust when I'm done, but I have a question regarding your preference for currently unsupported ANSI codes. This code for example is currently unsupported by us: ! Would we like to print out , or hide the code on the debug console entirely? The actions taken currently vary substantially, so I'd like to define a specific way of handling situations like this. Certain codes such as (which is completely invalid) are currently ignored, while the code mentioned above is printed to the console as .
first of all thanks for looking into this. As for what to do in that specific scenario: my preference would be to not show it because that would be consistent with some other places where we However I could also be convinced to show it if you are leaning towards that.
Sound good. Unless you think it really matters, I'm going to match all numbers from 0-107 as valid codes despite certain ranges not really being included as real ANSI codes. () While I'm at it, I'm adding background colours as they're not currently supported. I'd like to add high-intensity colours too, but the colours all seem a bit off. I wanted to check, are there any sort of guidelines for maximum colour intensity? Certainly on the dark theme, yellow looks more like mud... !
great. Makes sense for ansi code ranges If possible try to split it in two PRs, the second one that adds background colors. As for the guidelines we do not really have them. If the color is exactly specified by ansi then there is not much we can do as we should not change it or make it lighter. fyi our designer
I haven't changed the actual colours at the moment, though I can if it's worth looking into. As stated in , the first commit in the pull request should be ignored, as it just provides the context of the other review.
Am I doing something wrong? Install Run code:No colored output: !
I believe you are doing everything right. Though we stil plan to support some missing colors, but green should probably be supported can comment more
this fails for me in Isiders but works fine when running out of source: light theme: ! dark theme: !
I can try again once there is a new insiders (though none of my changes should affect this behavior)
Probably the code relies on some source workspace detail that differs between the built and packaged product. Does it work for you if you build the packaged product locally?
Works fine out of source. Does not work in insiders and when built locally. The issue is that the output recieved from the adapter when run out of source nicely contains the anssi sequences. When using insiders the ansi characters are stripped. Assigning to also and to investiagte on the adapter side Looking at this file it seems like the same extension version is used in both cases It might be somewhere between the adapter and the UI the ansi sequences get stripped Out of source: ! Insiders: !
When running out of source the adapters are identical to the ones in Insiders (because they are loaded from the Marketplace). So it cannot be something in the adapter. (And when using an integrated or external terminal the colors show up fine too).
It is (again) the environment, stupid... When running Insiders from the command line, the colors work fine because the environment has been configured properly by a preceding login-shell or terminal emulator. And colors fail when running Insiders from the Finder where there is no preceding login-shell (and where VS Code tries to simulate the login behaviour but obviously fails for "color"). And when using an integrated or external terminal colors work fine because both run as login-shells and are terminal emulators. See for more background info in .
great catch and good anlaysis. Thanks! Should we close as duplicate then?
No, has the background info but would not fix this. Leave it open until I understand the problem (and a workaround/fix).
The npm module "colors" () relies on  and some environment variables to figure out whether colors are supported. Since the debug console in VS Code is not based on a TTY, the check fails and becomes a no-op. In addition VS Code does not define any of the environment variables used in "colors" when launched from the Finder. As a workaround we could use an undocumented "FORCECOLOR" environment variable of the "colors" module: However this workaround would only work for the "colors" module and probably not for anything else. So adding this variable to the VS Code environment automatically, doesn't help much (and I'm not planning to fix this issue on the VS Code side). I suggest that the "colors" module documents the use of and the "FORCECOLOR" variable so that the workaround becomes more obvious.

Discussion:
<ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; /** * @param text The content to stylize. * @returns An {@link HTMLSpanElement} that contains the potentially stylized text. */ export function handleANSIOutput(text: string, linkDetector: LinkDetector): HTMLSpanElement { const root: HTMLSpanElement = document.createElement('span'); const textLength: number = text.length; let styleNames: string[] = []; let currentPos: number = 0; let buffer: string = ''; while (currentPos < textLength) { let sequenceFound: boolean = false; // Potentially an ANSI escape sequence. // See http://ascii-table.com/ansi-escape-sequences.php & https://en.wikipedia.org/wiki/ANSI_escape_code if (text.charCodeAt(currentPos) === 27 && text.charAt(currentPos + 1) === '[') { const startPos: number = currentPos; currentPos += 2; // Ignore 'Esc[' as it's in every sequence. let ansiSequence: string = ''; while (currentPos < textLength) { const char: string = text.charAt(currentPos); ansiSequence += char; currentPos++; // Look for a known sequence terminating character. if (char.match(/^[ABCDHIJKfhmpsu]$/)) { sequenceFound = true; break; } } if (sequenceFound) { // Flush buffer with previous styles. appendStylizedStringToContainer(root, buffer, styleNames, linkDetector); buffer = ''; /* * Certain ranges that are matched here do not contain real graphics rendition sequences. For * the sake of having a simpler expression, they have been included anyway. */ if (ansiSequence.match(/^(?:[39][0-7]|[0-8]|39)(?:;(?:[39][0-7]|[0-8]|39))*;?m$/)) { const styleCodes: number[] = ansiSequence.slice(0, -1)	// Remove final 'm' character. .split(';')											// Separate style codes. .filter(elem => elem !== '')						// Filter empty elems as '34;m' -> ['34', '']. .map(elem => parseInt(elem, 10));					// Convert to numbers. for (let code of styleCodes) { if (code === 0) { styleNames = []; } else if (code === 1) { styleNames.push('code-bold'); } else if (code === 4) { styleNames.push('code-underline'); } else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) { styleNames.push('code-foreground-' + code); } else if (code === 39) { // Remove all foreground colour codes styleNames = styleNames.filter(style => !style.match(/^code-foreground-d+$/)); } } } else { // Unsupported sequence so simply hide it. } } else { currentPos = startPos; } } if (sequenceFound === false) { buffer += text.charAt(currentPos); currentPos++; } } // Flush remaining text buffer if not empty. if (buffer) { appendStylizedStringToContainer(root, buffer, styleNames, linkDetector); } return root; } /** * @param root The {@link HTMLElement} to append the content to. * @param stringContent The text content to be appended. * @param cssClasses The list of CSS styles to apply to the text content. * @param linkDetector The {@link LinkDetector} responsible for generating links from {@param stringContent}. */ export function appendStylizedStringToContainer(root: HTMLElement, stringContent: string, cssClasses: string[], linkDetector: LinkDetector): void { if (!root || !stringContent) { return; } const content = linkDetector.handleLinks(stringContent); let container: HTMLElement; if (typeof content === 'string') { container = document.createElement('span'); container.textContent = content; } else { container = content; } container.className = cssClasses.join(' '); root.appendChild(container); } </ins>  No newline at end of file

.monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-bold	{ font-weight: bold; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-underline { text-decoration: underline; } <del> .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90	{ color: gray; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91	{ color: #BE1717; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92	{ color: #338A2F; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93	{ color: #BEB817; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94	{ color: darkblue; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95	{ color: darkmagenta; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96	{ color: darkcyan; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97	{ color: #BDBDBD; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90	{ color: #A0A0A0; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91	{ color: #A74747; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92	{ color: #348F34; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93	{ color: #5F4C29; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94	{ color: #6286BB; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95	{ color: #914191; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96	{ color: #218D8D; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97	{ color: #707070; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code30, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code90	{ color: gray; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code31, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code91	{ color: #A74747; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code32, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code92	{ color: #348F34; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code33, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code93	{ color: #5F4C29; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code34, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code94	{ color: #6286BB; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code35, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code95	{ color: #914191; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code36, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code96	{ color: #218D8D; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code37, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code97	{ color: #707070; } </del> <ins> /* Regular and bright color codes are currently treated the same. */ .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-30, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-90	{ color: gray; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-31, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-91	{ color: #BE1717; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-32, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-92	{ color: #338A2F; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-33, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-93	{ color: #BEB817; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-34, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-94	{ color: darkblue; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-35, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-95	{ color: darkmagenta; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-36, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-96	{ color: darkcyan; } .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-37, .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-97	{ color: #BDBDBD; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-30, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-90  { color: #A0A0A0; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-31, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-91  { color: #A74747; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-32, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-92  { color: #348F34; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-33, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-93  { color: #5F4C29; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-34, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-94  { color: #6286BB; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-35, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-95  { color: #914191; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-36, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-96  { color: #218D8D; } .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-37, .vs-dark .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-97  { color: #707070; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-30, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-90	{ color: gray; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-31, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-91	{ color: #A74747; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-32, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-92	{ color: #348F34; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-33, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-93	{ color: #5F4C29; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-34, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-94	{ color: #6286BB; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-35, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-95	{ color: #914191; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-36, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-96	{ color: #218D8D; } .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-37, .hc-black .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression .code-foreground-97	{ color: #707070; } </ins> /* Links */ .monaco-workbench .repl .repl-tree .monaco-tree .monaco-tree-row > .content > .output.expression a {

import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; <ins> import { handleANSIOutput } from 'vs/workbench/parts/debug/browser/debugANSIHandling'; </ins> const $ = dom.$;

dom.clearNode(templateData.value); // Reset classes to clear ansi decorations since templates are reused templateData.value.className = 'value'; <del> let result = this.handleANSIOutput(element.value); if (typeof result === 'string') { renderExpressionValue(result, templateData.value, { preserveWhitespace: true, showHover: false }); } else { templateData.value.appendChild(result); } </del> <ins> let result = handleANSIOutput(element.value, this.linkDetector); templateData.value.appendChild(result); </ins> dom.addClass(templateData.value, (element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : 'info'); templateData.source.textContent = element.sourceData ? `${element.sourceData.source.name}:${element.sourceData.lineNumber}` : '';

} } <del> private handleANSIOutput(text: string): HTMLElement | string { let tokensContainer: HTMLSpanElement; let currentToken: HTMLSpanElement; let buffer: string = ''; for (let i = 0, len = text.length; i < len; i++) { // start of ANSI escape sequence (see http://ascii-table.com/ansi-escape-sequences.php) if (text.charCodeAt(i) === 27) { let index = i; let chr = (++index < len ? text.charAt(index) : null); let codes = []; if (chr && chr === '[') { let code: string = null; while (chr !== 'm' && codes.length <= 7) { chr = (++index < len ? text.charAt(index) : null); if (chr && chr >= '0' && chr <= '9') { code = chr; chr = (++index < len ? text.charAt(index) : null); } if (chr && chr >= '0' && chr <= '9') { code += chr; chr = (++index < len ? text.charAt(index) : null); } if (code === null) { code = '0'; } codes.push(code); } if (chr === 'm') { // set text color/mode. code = null; // only respect text-foreground ranges and ignore the values for "black" & "white" because those // only make sense in combination with text-background ranges which we currently not support let token = document.createElement('span'); token.className = ''; while (codes.length > 0) { code = codes.pop(); let parsedMode = parseInt(code, 10); if (token.className.length > 0) { token.className += ' '; } if ((parsedMode >= 30 && parsedMode <= 37) || (parsedMode >= 90 && parsedMode <= 97)) { token.className += 'code' + parsedMode; } else if (parsedMode === 1) { token.className += 'code-bold'; } else if (parsedMode === 4) { token.className += 'code-underline'; } } // we need a tokens container now if (!tokensContainer) { tokensContainer = document.createElement('span'); } // flush text buffer if we have any if (buffer) { this.insert(this.linkDetector.handleLinks(buffer), currentToken || tokensContainer); buffer = ''; } currentToken = token; // get child until deepest nested node is found let childPointer: Node = tokensContainer; while (childPointer.hasChildNodes() && childPointer.firstChild.nodeName !== '#text') { childPointer = childPointer.firstChild; } childPointer.appendChild(token); i = index; } } } // normal text else { buffer += text[i]; } } // flush remaining text buffer if we have any if (buffer) { let res = this.linkDetector.handleLinks(buffer); if (typeof res !== 'string' || currentToken) { if (!tokensContainer) { tokensContainer = document.createElement('span'); } this.insert(res, currentToken || tokensContainer); } } return tokensContainer || buffer; } private insert(arg: HTMLElement | string, target: HTMLElement): void { if (typeof arg === 'string') { target.textContent = arg; } else { target.appendChild(arg); } } </del> public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { if (templateData.toDispose) { lifecycle.dispose(templateData.toDispose);

<ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as dom from 'vs/base/browser/dom'; import { generateUuid } from 'vs/base/common/uuid'; import { appendStylizedStringToContainer, handleANSIOutput } from 'vs/workbench/parts/debug/browser/debugANSIHandling'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; suite('Debug - ANSI Handling', () => { let linkDetector: LinkDetector; /** * Instantiate a {@link LinkDetector} for use by the functions being tested. */ setup(() => { const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService(); linkDetector = instantiationService.createInstance(LinkDetector); }); // todo: function here test('appendStylizedStringToContainer', () => { const root: HTMLSpanElement = document.createElement('span'); let child: Node; assert.equal(0, root.children.length); appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector); appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector); assert.equal(2, root.children.length); child = root.firstChild; if (child instanceof HTMLSpanElement) { assert.equal('content1', child.textContent); assert(dom.hasClass(child, 'class1')); assert(dom.hasClass(child, 'class2')); } else { assert.fail(); } child = root.lastChild; if (child instanceof HTMLSpanElement) { assert.equal('content2', child.textContent); assert(dom.hasClass(child, 'class2')); assert(dom.hasClass(child, 'class3')); } else { assert.fail(); } }); /** * Apply an ANSI sequence to {@link #getSequenceOutput}. * * @param sequence The ANSI sequence to stylize. * @returns An {@link HTMLSpanElement} that contains the stylized text. */ function getSequenceOutput(sequence: string): HTMLSpanElement { const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector); assert.equal(1, root.children.length); const child: Node = root.lastChild; if (child instanceof HTMLSpanElement) { return child; } else { assert.fail(); return null; } } /** * Assert that a given ANSI sequence maintains added content following the ANSI code, and that * the provided {@param assertion} passes. * * @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes * only, and should not include actual text content as it is provided by this function. * @param assertion The function used to verify the output. */ function assertSingleSequenceElement(sequence: string, assertion: (child: HTMLSpanElement) => void): void { const child: HTMLSpanElement = getSequenceOutput(sequence + 'content'); assert.equal('content', child.textContent); assertion(child); } test('Expected single sequence operation', () => { // Bold code assertSingleSequenceElement('x1b[1m', (child) => { assert(dom.hasClass(child, 'code-bold')); }); // Underline code assertSingleSequenceElement('x1b[4m', (child) => { assert(dom.hasClass(child, 'code-underline')); }); for (let i = 30; i <= 37; i++) { const style: string = 'code-foreground-' + i; // Foreground colour codes assertSingleSequenceElement('x1b[' + i + 'm', (child) => { assert(dom.hasClass(child, style)); }); // Cancellation code removes colour code assertSingleSequenceElement('x1b[' + i + ';39m', (child) => { assert(dom.hasClass(child, style) === false); }); } // Codes do not interfere assertSingleSequenceElement('x1b[1;4;30;31;32;33;34;35;36;37m', (child) => { assert.equal(10, child.classList.length); assert(dom.hasClass(child, 'code-bold')); assert(dom.hasClass(child, 'code-underline')); for (let i = 30; i <= 37; i++) { assert(dom.hasClass(child, 'code-foreground-' + i)); } }); // Duplicate codes do not change output assertSingleSequenceElement('x1b[1;1;4;1;4;4;1;4m', (child) => { assert(dom.hasClass(child, 'code-bold')); assert(dom.hasClass(child, 'code-underline')); }); // Extra terminating semicolon does not change output assertSingleSequenceElement('x1b[1;4;m', (child) => { assert(dom.hasClass(child, 'code-bold')); assert(dom.hasClass(child, 'code-underline')); }); // Cancellation code removes all codes assertSingleSequenceElement('x1b[1;4;30;31;32;33;34;35;36;37;0m', (child) => { assert.equal(0, child.classList.length); }); }); /** * Assert that a given ANSI sequence produces the expected number of {@link HTMLSpanElement} children. For * each child, run the provided assertion. * * @param sequence The ANSI sequence to verify. * @param assertions A set of assertions to run on the resulting children. */ function assertMultipleSequenceElements(sequence: string, assertions: Array<(child: HTMLSpanElement) => void>): void { const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector); assert.equal(assertions.length, root.children.length); for (let i = 0; i < assertions.length; i++) { const child: Node = root.children[i]; if (child instanceof HTMLSpanElement) { assertions[i](child); } else { assert.fail(); } } } test('Expected multiple sequence operation', () => { // Multiple codes affect the same text assertSingleSequenceElement('x1b[1mx1b[4mx1b[32m', (child) => { assert(dom.hasClass(child, 'code-bold')); assert(dom.hasClass(child, 'code-underline')); assert(dom.hasClass(child, 'code-foreground-32')); }); // Consecutive codes do not affect previous ones assertMultipleSequenceElements('x1b[1mboldx1b[32mgreenx1b[4munderlinex1b[0mnothing', [ (bold) => { assert.equal(1, bold.classList.length); assert(dom.hasClass(bold, 'code-bold')); }, (green) => { assert.equal(2, green.classList.length); assert(dom.hasClass(green, 'code-bold')); assert(dom.hasClass(green, 'code-foreground-32')); }, (underline) => { assert.equal(3, underline.classList.length); assert(dom.hasClass(underline, 'code-bold')); assert(dom.hasClass(underline, 'code-foreground-32')); assert(dom.hasClass(underline, 'code-underline')); }, (nothing) => { assert.equal(0, nothing.classList.length); }, ]); }); /** * Assert that the provided ANSI sequence exactly matches the text content of the resulting * {@link HTMLSpanElement}. * * @param sequence The ANSI sequence to verify. */ function assertSequenceEqualToContent(sequence: string): void { const child: HTMLSpanElement = getSequenceOutput(sequence); assert(child.textContent === sequence); } test('Invalid codes treated as regular text', () => { // Individual components of ANSI code start are printed assertSequenceEqualToContent('x1b'); assertSequenceEqualToContent('['); // Unsupported sequence prints both characters assertSequenceEqualToContent('x1b['); // Random strings are displayed properly for (let i = 0; i < 50; i++) { const uuid: string = generateUuid(); assertSequenceEqualToContent(uuid); } }); /** * Assert that a given ANSI sequence maintains added content following the ANSI code, and that * the expression itself is thrown away. * * @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes * only, and should not include actual text content as it is provided by this function. */ function assertEmptyOutput(sequence: string) { const child: HTMLSpanElement = getSequenceOutput(sequence + 'content'); assert.equal('content', child.textContent); assert.equal(0, child.classList.length); } test('Empty sequence output', () => { const sequences: string[] = [ // No colour codes '', 'x1b[;m', 'x1b[1;;m', 'x1b[m', // Unsupported colour codes 'x1b[30;40m', 'x1b[100m' ]; sequences.forEach(sequence => { assertEmptyOutput(sequence); }); // Check other possible ANSI terminators const terminators: string[] = 'ABCDHIJKfhmpsu'.split(''); terminators.forEach(terminator => { assertEmptyOutput('x1b[content' + terminator); }); }); }); </ins>  No newline at end of file