From 12a3bf6b3b68c005907ff614af25d8db2d1a32dd Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Fri, 10 Feb 2023 19:05:32 -0800 Subject: [PATCH] handle json (#21915) --- src/sql/base/browser/ui/table/formatters.ts | 1 - .../ui/table/media/slickColorTheme.css | 14 ++-- src/sql/platform/query/common/query.ts | 64 +++++++++++-------- .../contrib/query/browser/gridPanel.ts | 22 ++++--- .../query/browser/queryResultsEditor.ts | 10 +-- .../query/common/resultsGrid.contribution.ts | 5 ++ 6 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/sql/base/browser/ui/table/formatters.ts b/src/sql/base/browser/ui/table/formatters.ts index ac97390802..bd0e7f63bf 100644 --- a/src/sql/base/browser/ui/table/formatters.ts +++ b/src/sql/base/browser/ui/table/formatters.ts @@ -70,7 +70,6 @@ export function hyperLinkFormatter(row: number | undefined, cell: any | undefine if (DBCellValue.isDBCellValue(value)) { valueToDisplay = 'NULL'; if (!value.isNull) { - cellClasses += ' xmlLink'; valueToDisplay = getCellDisplayValue(value.displayValue); return `${valueToDisplay}`; } else { diff --git a/src/sql/base/browser/ui/table/media/slickColorTheme.css b/src/sql/base/browser/ui/table/media/slickColorTheme.css index dee9cc4f2c..afdcb7d03c 100644 --- a/src/sql/base/browser/ui/table/media/slickColorTheme.css +++ b/src/sql/base/browser/ui/table/media/slickColorTheme.css @@ -17,18 +17,24 @@ } .slick-cell a, -.slick-cell a:link, -.resultsMessageValue a, -.resultsMessageValue a:link { +.resultsMessageValue a { color: var(--color-grid-link); - text-decoration: underline; cursor: pointer; white-space: inherit; } +.resultsMessageValue a { + text-decoration: underline; +} + +.slick-cell a { + text-decoration: none; +} + .slick-cell a:hover, .resultsMessageValue a:hover { color: var(--color-grid-link-hover); + text-decoration: underline; } /* diff --git a/src/sql/platform/query/common/query.ts b/src/sql/platform/query/common/query.ts index e50b4ad61a..1aec4b4a84 100644 --- a/src/sql/platform/query/common/query.ts +++ b/src/sql/platform/query/common/query.ts @@ -6,40 +6,52 @@ export interface IQueryEditorConfiguration { readonly results: { readonly saveAsCsv: { - readonly includeHeaders: boolean, - readonly delimiter: string, - readonly lineSeperator: string, - readonly textIdentifier: string, - readonly encoding: string - }, + readonly includeHeaders: boolean; + readonly delimiter: string; + readonly lineSeperator: string; + readonly textIdentifier: string; + readonly encoding: string; + }; readonly saveAsExcel: { - readonly includeHeaders: boolean, - }, + readonly includeHeaders: boolean; + }; readonly saveAsMarkdown: { - readonly encoding: string, - readonly includeHeaders: boolean, - readonly lineSeparator: string, - } + readonly encoding: string; + readonly includeHeaders: boolean; + readonly lineSeparator: string; + }; readonly saveAsXml: { - readonly formatted: boolean, - readonly encoding: string - }, - readonly streaming: boolean, - readonly copyIncludeHeaders: boolean, - readonly copyRemoveNewLine: boolean, - readonly optimizedTable: boolean, - readonly inMemoryDataProcessingThreshold: number, - readonly openAfterSave: boolean + readonly formatted: boolean; + readonly encoding: string; + }; + readonly streaming: boolean; + readonly copyIncludeHeaders: boolean; + readonly copyRemoveNewLine: boolean; + readonly optimizedTable: boolean; + readonly inMemoryDataProcessingThreshold: number; + readonly openAfterSave: boolean; readonly showActionBar: boolean; }, readonly messages: { - readonly showBatchTime: boolean, - readonly wordwrap: boolean - }, + readonly showBatchTime: boolean; + readonly wordwrap: boolean; + }; readonly chart: { readonly defaultChartType: 'bar' | 'doughnut' | 'horizontalBar' | 'line' | 'pie' | 'scatter' | 'timeSeries', - }, - readonly tabColorMode: 'off' | 'border' | 'fill', + }; + readonly tabColorMode: 'off' | 'border' | 'fill'; readonly showConnectionInfoInTitle: boolean; readonly promptToSaveGeneratedFiles: boolean; } + +export interface IResultGridConfiguration { + fontFamily: string; + fontWeight: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; + fontSize: number; + letterSpacing: number; + rowHeight: number; + cellPadding: number | number[]; + autoSizeColumns: boolean; + maxColumnWidth: number; + showJsonAsLink: boolean; +} diff --git a/src/sql/workbench/contrib/query/browser/gridPanel.ts b/src/sql/workbench/contrib/query/browser/gridPanel.ts index e972ece250..c3410e56a2 100644 --- a/src/sql/workbench/contrib/query/browser/gridPanel.ts +++ b/src/sql/workbench/contrib/query/browser/gridPanel.ts @@ -43,7 +43,7 @@ import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/commo import { SaveFormat } from 'sql/workbench/services/query/common/resultSerializer'; import { Progress } from 'vs/platform/progress/common/progress'; import { ScrollableView, IView } from 'sql/base/browser/ui/scrollableView/scrollableView'; -import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; +import { IQueryEditorConfiguration, IResultGridConfiguration } from 'sql/platform/query/common/query'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel'; import { FilterButtonWidth, HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin'; @@ -362,6 +362,7 @@ export abstract class GridTableBase extends Disposable implements IView { private dataProvider: HybridDataProvider; private filterPlugin: HeaderFilter; private isDisposed: boolean = false; + private gridConfig: IResultGridConfiguration; private columns: Slick.Column[]; @@ -417,8 +418,8 @@ export abstract class GridTableBase extends Disposable implements IView { super(); this.options = { ...defaultGridTableOptions, ...options }; - let config = this.configurationService.getValue<{ rowHeight: number }>('resultsGrid'); - this.rowHeight = config && config.rowHeight ? config.rowHeight : ROW_HEIGHT; + this.gridConfig = this.configurationService.getValue('resultsGrid'); + this.rowHeight = this.gridConfig.rowHeight ?? ROW_HEIGHT; this.state = state; this.container.style.width = '100%'; this.container.style.height = '100%'; @@ -430,7 +431,9 @@ export abstract class GridTableBase extends Disposable implements IView { ? localize('xmlShowplan', "XML Showplan") : escape(c.columnName), field: i.toString(), - formatter: c.isXml ? hyperLinkFormatter : queryResultTextFormatter, + formatter: c.isXml ? hyperLinkFormatter : (row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string => { + return queryResultTextFormatter(this.gridConfig.showJsonAsLink, row, cell, value, columnDef, dataContext); + }, width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined }; }); @@ -535,8 +538,8 @@ export abstract class GridTableBase extends Disposable implements IView { this.table.setTableTitle(localize('resultsGrid', "Results grid")); this.table.setSelectionModel(this.selectionModel); this.table.registerPlugin(new MouseWheelSupport()); - const autoSizeOnRender: boolean = !this.state.columnSizes && this.configurationService.getValue('resultsGrid.autoSizeColumns'); - this.table.registerPlugin(new AutoColumnSize({ autoSizeOnRender: autoSizeOnRender, maxWidth: this.configurationService.getValue('resultsGrid.maxColumnWidth'), extraColumnHeaderWidth: FilterButtonWidth })); + const autoSizeOnRender: boolean = !this.state.columnSizes && this.gridConfig.autoSizeColumns; + this.table.registerPlugin(new AutoColumnSize({ autoSizeOnRender: autoSizeOnRender, maxWidth: this.gridConfig.maxColumnWidth, extraColumnHeaderWidth: FilterButtonWidth })); this.table.registerPlugin(this.rowNumberColumn); this.table.registerPlugin(new AdditionalKeyBindings()); this._register(this.dataProvider.onFilterStateChange(() => { this.layout(); })); @@ -730,8 +733,7 @@ export abstract class GridTableBase extends Disposable implements IView { if (column) { const subset = await this.getRowData(event.cell.row, 1); const value = subset[0][event.cell.cell - 1]; - const isJson = isJsonCell(value); - if (column.isXml || isJson) { + if (column.isXml || (this.gridConfig.showJsonAsLink && isJsonCell(value))) { const result = await this.executionPlanService.isExecutionPlan(this.providerId, value.displayValue); if (result.isExecutionPlan) { const executionPlanGraphInfo = { @@ -1002,8 +1004,8 @@ 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)) { +function queryResultTextFormatter(showJsonAsLink: boolean, row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string { + if (showJsonAsLink && isJsonCell(value)) { return hyperLinkFormatter(row, cell, value, columnDef, dataContext); } else { return textFormatter(row, cell, value, columnDef, dataContext); diff --git a/src/sql/workbench/contrib/query/browser/queryResultsEditor.ts b/src/sql/workbench/contrib/query/browser/queryResultsEditor.ts index 939b65d283..32d81375c1 100644 --- a/src/sql/workbench/contrib/query/browser/queryResultsEditor.ts +++ b/src/sql/workbench/contrib/query/browser/queryResultsEditor.ts @@ -21,19 +21,13 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { RESULTS_GRID_DEFAULTS } from 'sql/workbench/common/constants'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { IResultGridConfiguration } from 'sql/platform/query/common/query'; export const TextCompareEditorVisible = new RawContextKey('textCompareEditorVisible', false); export class BareResultsGridInfo extends BareFontInfo { - public static override createFromRawSettings(opts: { - fontFamily?: string; - fontWeight?: string; - fontSize?: number; - lineHeight?: number; - letterSpacing?: number; - cellPadding?: number | number[]; - }, zoomLevel: number): BareResultsGridInfo { + public static override createFromRawSettings(opts: IResultGridConfiguration, zoomLevel: number): BareResultsGridInfo { let cellPadding = !types.isUndefinedOrNull(opts.cellPadding) ? opts.cellPadding : RESULTS_GRID_DEFAULTS.cellPadding; return new BareResultsGridInfo(BareFontInfo.createFromRawSettings(opts, PixelRatio.value, false), { cellPadding }); } diff --git a/src/sql/workbench/contrib/query/common/resultsGrid.contribution.ts b/src/sql/workbench/contrib/query/common/resultsGrid.contribution.ts index 873a0828bd..3969f8a32e 100644 --- a/src/sql/workbench/contrib/query/common/resultsGrid.contribution.ts +++ b/src/sql/workbench/contrib/query/common/resultsGrid.contribution.ts @@ -67,6 +67,11 @@ const resultsGridConfiguration: IConfigurationNode = { type: 'number', default: 212, description: nls.localize('maxColumnWidth', "The maximum width in pixels for auto-sized columns") + }, + 'resultsGrid.showJsonAsLink': { + 'type': 'boolean', + 'description': nls.localize('resultsGrid.showJsonAsLink', "Whether to show cells with JSON formatted string as hyperlink. When enabled, upon click the JSON value will be opened in another tab. The default value is true."), + 'default': true } } };