diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 6ccc6abf46e..6bd2a2ca832 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,8 @@ ## [UNRELEASED] +- Replace certain control codes (`U+0000` - `U+001F`) with their corresponding control labels (`U+2400` - `U+241F`) in the results view. [#963](https://github.com/github/vscode-codeql/pull/963) + ## 1.5.6 - 07 October 2021 - Add progress messages to LGTM download option. This makes the two-step process (selecting a project, then selecting a language) more clear. [#960](https://github.com/github/vscode-codeql/pull/960) diff --git a/extensions/ql-vscode/src/view/RawTableValue.tsx b/extensions/ql-vscode/src/view/RawTableValue.tsx index 013715c1531..5f3ee9fa7f4 100644 --- a/extensions/ql-vscode/src/view/RawTableValue.tsx +++ b/extensions/ql-vscode/src/view/RawTableValue.tsx @@ -9,14 +9,14 @@ interface Props { } export default function RawTableValue(props: Props): JSX.Element { - const v = props.value; + const rawValue = props.value; if ( - typeof v === 'string' - || typeof v === 'number' - || typeof v === 'boolean' + typeof rawValue === 'string' + || typeof rawValue === 'number' + || typeof rawValue === 'boolean' ) { - return {v.toString()}; + return {renderLocation(undefined, rawValue.toString())}; } - return renderLocation(v.url, v.label, props.databaseUri); + return renderLocation(rawValue.url, rawValue.label, props.databaseUri); } diff --git a/extensions/ql-vscode/src/view/result-table-utils.tsx b/extensions/ql-vscode/src/view/result-table-utils.tsx index 64bb10564f7..a96edd3e051 100644 --- a/extensions/ql-vscode/src/view/result-table-utils.tsx +++ b/extensions/ql-vscode/src/view/result-table-utils.tsx @@ -37,6 +37,9 @@ export const oddRowClassName = 'vscode-codeql__result-table-row--odd'; export const pathRowClassName = 'vscode-codeql__result-table-row--path'; export const selectedRowClassName = 'vscode-codeql__result-table-row--selected'; +const CONTROL_CODE = '\u001F'.codePointAt(0)!; +const CONTROL_LABEL = '\u2400'.codePointAt(0)!; + export function jumpToLocationHandler( loc: ResolvableLocationValue, databaseUri: string, @@ -67,24 +70,42 @@ export function openFile(filePath: string): void { }); } +function convertedNonprintableChars(label: string) { + // If the label was empty, use a placeholder instead, so the link is still clickable. + if (!label) { + return '[empty string]'; + } else if (label.match(/^\s+$/)) { + return `[whitespace: "${label}"]`; + } else { + /** + * If the label contains certain non-printable characters, loop through each + * character and replace it with the cooresponding unicode control label. + */ + const convertedLabelArray: any[] = []; + for (let i = 0; i < label.length; i++) { + const labelCheck = label.codePointAt(i)!; + if (labelCheck <= CONTROL_CODE) { + convertedLabelArray[i] = String.fromCodePoint(labelCheck + CONTROL_LABEL); + } else { + convertedLabelArray[i] = label.charAt(i); + } + } + return convertedLabelArray.join(''); + } +} + /** * Render a location as a link which when clicked displays the original location. */ export function renderLocation( - loc: UrlValue | undefined, - label: string | undefined, - databaseUri: string, + loc?: UrlValue, + label?: string, + databaseUri?: string, title?: string, callback?: () => void ): JSX.Element { - // If the label was empty, use a placeholder instead, so the link is still clickable. - let displayLabel = label; - if (!label) { - displayLabel = '[empty string]'; - } else if (label.match(/^\s+$/)) { - displayLabel = `[whitespace: "${label}"]`; - } + const displayLabel = convertedNonprintableChars(label!); if (loc === undefined) { return {displayLabel}; @@ -93,7 +114,7 @@ export function renderLocation( } const resolvableLoc = tryGetResolvableLocation(loc); - if (resolvableLoc !== undefined) { + if (databaseUri !== undefined && resolvableLoc !== undefined) { return (