Skip to content

Commit

Permalink
Data Explorer: Visually display leading/trailing whitespace and empty…
Browse files Browse the repository at this point in the history
… strings in data values (#5652)

Addresses #3067. Currently in the data explorer, the strings `""`, `"
"`, and `" "` cannot be visually distinguished because of the absence of
quotations.

There are a few approaches we could take:

1. Format with quotations (I believe RStudio does this)
2. Provide a special display for empty strings (this is what's described
as #3067)
3. Replace whitespace with a unicode character to indicate the presence
of leading/trailing whitespace characters

This PR does the latter two, using a mid-dot as the unicode
representation and the special string `<empty>` for empty strings.
  • Loading branch information
wesm authored Dec 11, 2024
1 parent 2425a9f commit bb6af97
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
.data-grid-row-cell .content .text-container .text-value.special-value {
opacity: 0.60;
}

.data-grid-row-cell .content .text-container .text-value .whitespace {
opacity: 0.50;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'vs/css!./tableDataCell';
import * as React from 'react';

// Other dependencies.
import { localize } from 'vs/nls';
import { positronClassNames } from 'vs/base/common/positronUtilities';
import { DataCell, DataCellKind } from 'vs/workbench/services/positronDataExplorer/common/tableDataCache';
import { PositronDataExplorerColumn } from 'vs/workbench/services/positronDataExplorer/browser/positronDataExplorerColumn';
Expand All @@ -28,17 +29,63 @@ interface TableDataCellProps {
* @returns The rendered component.
*/
export const TableDataCell = (props: TableDataCellProps) => {
const EMPTY_SPACE_SYMBOL = '\u00B7';

let isSpecialValue = props.dataCell.kind !== DataCellKind.NON_NULL;

// Render empty strings as special value
// Initialize rendered output parts
const parts: (string | JSX.Element)[] = [];
const formattedText = props.dataCell.formatted
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n');

// Handle text that is only whitespace
if (formattedText.trim() === '') {
parts.push(
<span className='whitespace'>
{EMPTY_SPACE_SYMBOL.repeat(formattedText.length)}
</span>
);
} else {
// Handle leading whitespace
const leadingMatch = formattedText.match(/^\s+/);
if (leadingMatch) {
parts.push(
<span className='whitespace'>
{EMPTY_SPACE_SYMBOL.repeat(leadingMatch[0].length)}
</span>
);
}

// Add the main content
const mainContent = formattedText.trim();
parts.push(mainContent);

// Handle trailing whitespace
const trailingMatch = formattedText.match(/\s+$/);
if (trailingMatch) {
parts.push(
<span className='whitespace'>
{EMPTY_SPACE_SYMBOL.repeat(trailingMatch[0].length)}
</span>
);
}
}
let renderedOutput = parts;
if (props.dataCell.kind === DataCellKind.NON_NULL && formattedText === '') {
isSpecialValue = true;
renderedOutput = [`<${localize('positron.dataExplorer.emptyString', "empty")}>`];
}

// Set the class names.
const classNames = positronClassNames(
'text-value',
{ 'special-value': props.dataCell.kind !== DataCellKind.NON_NULL }
);
const classNames = positronClassNames('text-value', { 'special-value': isSpecialValue });

// Render.
return (
<div className={positronClassNames('text-container', props.column.alignment)}>
<div className={classNames}>
{props.dataCell.formatted.replace(/\r/g, '\\r').replace(/\n/g, '\\n')}
{renderedOutput}
</div>
</div>
);
Expand Down

0 comments on commit bb6af97

Please sign in to comment.