diff --git a/src/sql/workbench/contrib/query/browser/gridPanel.ts b/src/sql/workbench/contrib/query/browser/gridPanel.ts index 5848ffb8bc..26a8fe52a6 100644 --- a/src/sql/workbench/contrib/query/browser/gridPanel.ts +++ b/src/sql/workbench/contrib/query/browser/gridPanel.ts @@ -65,6 +65,13 @@ const ACTIONBAR_HEIGHT = 120; // this handles min size if rows is greater than the min grid visible rows const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT; +// The regex to check whether a string is a valid JSON string. It is used to determine: +// 1. whether the cell should be rendered as a hyperlink. +// 2. when user clicks a cell, whether the cell content should be displayed in a new text editor as json. +// Based on the requirements, the solution doesn't need to be very accurate, a simple regex is enough since it is more +// performant than trying to parse the string to object. +const IsJsonRegex = /({.*?})/g; + export class GridPanel extends Disposable { private container = document.createElement('div'); private scrollableView: ScrollableView; @@ -395,15 +402,13 @@ export abstract class GridTableBase extends Disposable implements IView { this.container.style.height = '100%'; this.columns = this.resultSet.columnInfo.map((c, i) => { - let isLinked = c.isXml || c.isJson; - return >{ id: i.toString(), name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan' ? localize('xmlShowplan', "XML Showplan") : escape(c.columnName), field: i.toString(), - formatter: isLinked ? hyperLinkFormatter : textFormatter, + formatter: c.isXml ? hyperLinkFormatter : queryResultTextFormatter, width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined }; }); @@ -693,17 +698,20 @@ export abstract class GridTableBase extends Disposable implements IView { private async onTableClick(event: ITableMouseEvent) { // account for not having the number column - let column = this.resultSet.columnInfo[event.cell.cell - 1]; + const column = this.resultSet.columnInfo[event.cell.cell - 1]; // handle if a showplan link was clicked - if (column && (column.isXml || column.isJson)) { + if (column) { const subset = await this.getRowData(event.cell.row, 1); - let value = subset[0][event.cell.cell - 1]; - let content = value.displayValue; - const input = this.untitledEditorService.create({ mode: column.isXml ? 'xml' : 'json', initialValue: content }); - await input.resolve(); - await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, input.textEditorModel, FormattingMode.Explicit, Progress.None, CancellationToken.None); - input.setDirty(false); - await this.editorService.openEditor(input); + const value = subset[0][event.cell.cell - 1]; + const isJson = isJsonCell(value); + if (column.isXml || isJson) { + const content = value.displayValue; + const input = this.untitledEditorService.create({ mode: column.isXml ? 'xml' : 'json', initialValue: content }); + await input.resolve(); + await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, input.textEditorModel, FormattingMode.Explicit, Progress.None, CancellationToken.None); + input.setDirty(false); + await this.editorService.openEditor(input); + } } } @@ -936,3 +944,15 @@ class GridTable extends GridTableBase { ]; } } + +function isJsonCell(value: ICellValue): boolean { + return !!(value && !value.isNull && value.displayValue?.match(IsJsonRegex)); +} + +function queryResultTextFormatter(row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string { + if (isJsonCell(value)) { + return hyperLinkFormatter(row, cell, value, columnDef, dataContext); + } else { + return textFormatter(row, cell, value, columnDef, dataContext); + } +}