diff --git a/resources/xlf/sqlops-core/sql.xlf b/resources/xlf/sqlops-core/sql.xlf
index a1791f64c6..8d786fc087 100644
--- a/resources/xlf/sqlops-core/sql.xlf
+++ b/resources/xlf/sqlops-core/sql.xlf
@@ -62,6 +62,12 @@
[Optional] File encoding used when saving results as CSV
+
+ [Optional] When true, XML output will be formatted when saving results as XML
+
+
+ [Optional] File encoding used when saving results as XML
+
@@ -596,6 +602,9 @@
Save As Excel
+
+ Save As XML
+
Copy
@@ -1142,6 +1151,9 @@
Save as Excel
+
+ Save as XML
+
View as Chart
diff --git a/src/sql/parts/grid/common/gridContentEvents.ts b/src/sql/parts/grid/common/gridContentEvents.ts
index 9b2f0eea8f..265cc4f2e9 100644
--- a/src/sql/parts/grid/common/gridContentEvents.ts
+++ b/src/sql/parts/grid/common/gridContentEvents.ts
@@ -17,6 +17,7 @@ export let SelectAllMessages = 'SelectAllMessages';
export let SaveAsCsv = 'SaveAsCSV';
export let SaveAsJSON = 'SaveAsJSON';
export let SaveAsExcel = 'SaveAsExcel';
+export let SaveAsXML = 'SaveAsXML';
export let ViewAsChart = 'ViewAsChart';
export let GoToNextQueryOutputTab = 'GoToNextQueryOutputTab';
export let GoToNextGrid = 'GoToNextGrid';
diff --git a/src/sql/parts/grid/media/saveXml.svg b/src/sql/parts/grid/media/saveXml.svg
new file mode 100644
index 0000000000..d6682e3606
--- /dev/null
+++ b/src/sql/parts/grid/media/saveXml.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/src/sql/parts/grid/media/saveXml_inverse.svg b/src/sql/parts/grid/media/saveXml_inverse.svg
new file mode 100644
index 0000000000..c48e6aaed1
--- /dev/null
+++ b/src/sql/parts/grid/media/saveXml_inverse.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/src/sql/parts/grid/media/slickColorTheme.css b/src/sql/parts/grid/media/slickColorTheme.css
index a5d8ee1b8b..24e7b8d966 100644
--- a/src/sql/parts/grid/media/slickColorTheme.css
+++ b/src/sql/parts/grid/media/slickColorTheme.css
@@ -112,6 +112,11 @@
background-image: url("saveExcel.svg");
}
+.vs .icon.saveXml {
+ /* ResultToXML_16x_vscode */
+ background-image: url("saveXml.svg");
+}
+
.vs .icon.viewChart {
/* ResultToXlsx_16x_vscode */
background-image: url("viewChart.svg");
@@ -243,6 +248,12 @@
background-image: url("saveExcel_inverse.svg");
}
+.vs-dark .icon.saveXml,
+.hc-black .icon.saveXml {
+ /* ResultToXml_16x_vscode_inverse.svg */
+ background-image: url("saveXml_inverse.svg");
+}
+
.vs-dark .icon.viewChart,
.hc-black .icon.viewChart {
/* ResultToXlsx_16x_vscode */
diff --git a/src/sql/parts/grid/views/gridActions.ts b/src/sql/parts/grid/views/gridActions.ts
index 7cc7d319d7..6099b01b32 100644
--- a/src/sql/parts/grid/views/gridActions.ts
+++ b/src/sql/parts/grid/views/gridActions.ts
@@ -17,6 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
export const GRID_SAVECSV_ID = 'grid.saveAsCsv';
export const GRID_SAVEJSON_ID = 'grid.saveAsJson';
export const GRID_SAVEEXCEL_ID = 'grid.saveAsExcel';
+export const GRID_SAVEXML_ID = 'grid.saveAsXml';
export const GRID_COPY_ID = 'grid.copySelection';
export const GRID_COPYWITHHEADERS_ID = 'grid.copyWithHeaders';
export const GRID_SELECTALL_ID = 'grid.selectAll';
@@ -46,6 +47,7 @@ export class GridActionProvider {
actions.push(new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveFormat.CSV, this._dataService));
actions.push(new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveFormat.JSON, this._dataService));
actions.push(new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveFormat.EXCEL, this._dataService));
+ actions.push(new SaveResultAction(SaveResultAction.SAVEXML_ID, SaveResultAction.SAVEXML_LABEL, SaveFormat.XML, this._dataService));
actions.push(new SelectAllGridAction(SelectAllGridAction.ID, SelectAllGridAction.LABEL, this._selectAllCallback));
actions.push(new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false, this._dataService));
actions.push(new CopyResultAction(CopyResultAction.COPYWITHHEADERS_ID, CopyResultAction.COPYWITHHEADERS_LABEL, true, this._dataService));
@@ -74,6 +76,9 @@ export class SaveResultAction extends Action {
public static SAVEEXCEL_ID = GRID_SAVEEXCEL_ID;
public static SAVEEXCEL_LABEL = localize('saveAsExcel', 'Save As Excel');
+ public static SAVEXML_ID = GRID_SAVEXML_ID;
+ public static SAVEXML_LABEL = localize('saveAsXml', 'Save As XML');
+
constructor(
id: string,
label: string,
diff --git a/src/sql/parts/grid/views/gridCommands.ts b/src/sql/parts/grid/views/gridCommands.ts
index 2a987c61ed..1723d50887 100644
--- a/src/sql/parts/grid/views/gridCommands.ts
+++ b/src/sql/parts/grid/views/gridCommands.ts
@@ -69,6 +69,10 @@ export const saveAsExcel = (accessor: ServicesAccessor) => {
runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsExcel);
};
+export const saveAsXml = (accessor: ServicesAccessor) => {
+ runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsXML);
+};
+
export const selectAll = (accessor: ServicesAccessor) => {
runActionOnActiveResultsEditor(accessor, GridContentEvents.SelectAll);
};
diff --git a/src/sql/parts/grid/views/gridParentComponent.ts b/src/sql/parts/grid/views/gridParentComponent.ts
index 596ccc67bd..c7f43c3c5f 100644
--- a/src/sql/parts/grid/views/gridParentComponent.ts
+++ b/src/sql/parts/grid/views/gridParentComponent.ts
@@ -154,6 +154,9 @@ export abstract class GridParentComponent {
case GridContentEvents.SaveAsExcel:
self.sendSaveRequest(SaveFormat.EXCEL);
break;
+ case GridContentEvents.SaveAsXML:
+ self.sendSaveRequest(SaveFormat.XML);
+ break;
case GridContentEvents.GoToNextQueryOutputTab:
self.goToNextQueryOutputTab();
break;
@@ -320,6 +323,9 @@ export abstract class GridParentComponent {
'SaveAsExcel': () => {
this.sendSaveRequest(SaveFormat.EXCEL);
},
+ 'SaveAsXML': () => {
+ this.sendSaveRequest(SaveFormat.XML);
+ },
'GoToNextQueryOutputTab': () => {
this.goToNextQueryOutputTab();
}
@@ -345,6 +351,9 @@ export abstract class GridParentComponent {
case 'saveexcel':
this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.EXCEL, selection: event.selection });
break;
+ case 'savexml':
+ this.dataService.sendSaveRequest({ batchIndex: event.batchId, resultSetNumber: event.resultId, format: SaveFormat.XML, selection: event.selection });
+ break;
case 'selectall':
this.activeGrid = event.index;
this.onSelectAllForActiveGrid();
diff --git a/src/sql/parts/grid/views/query/query.component.ts b/src/sql/parts/grid/views/query/query.component.ts
index b20606c921..db9a1bf78b 100644
--- a/src/sql/parts/grid/views/query/query.component.ts
+++ b/src/sql/parts/grid/views/query/query.component.ts
@@ -127,6 +127,19 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
}
},
+ {
+ showCondition: () => { return true; },
+ icon: () => { return 'saveXml'; },
+ hoverText: () => { return LocalizedConstants.saveXMLLabel; },
+ functionality: (batchId, resultId, index) => {
+ let selection = this.getSelection(index);
+ if (selection.length <= 1) {
+ this.handleContextClick({ type: 'savexml', batchId: batchId, resultId: resultId, index: index, selection: selection });
+ } else {
+ this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections);
+ }
+ }
+ },
{
showCondition: () => {
return this.configurationService.getValue('workbench')['enablePreviewFeatures'];
diff --git a/src/sql/parts/query/common/constants.ts b/src/sql/parts/query/common/constants.ts
index 5a083d300b..6d85f7d7b2 100644
--- a/src/sql/parts/query/common/constants.ts
+++ b/src/sql/parts/query/common/constants.ts
@@ -5,6 +5,7 @@
export const copyIncludeHeaders = 'copyIncludeHeaders';
export const configSaveAsCsv = 'saveAsCsv';
+export const configSaveAsXml = 'saveAsXml';
export const configCopyRemoveNewLine = 'copyRemoveNewLine';
export const configShowBatchTime = 'showBatchTime';
export const querySection = 'query';
diff --git a/src/sql/parts/query/common/localizedConstants.ts b/src/sql/parts/query/common/localizedConstants.ts
index 84e7fbd73c..1cc2dfd422 100644
--- a/src/sql/parts/query/common/localizedConstants.ts
+++ b/src/sql/parts/query/common/localizedConstants.ts
@@ -24,6 +24,7 @@ export const restoreLabel = localize('resultsPane.restoreLabel', 'Restore');
export const saveCSVLabel = localize('saveCSVLabel', 'Save as CSV');
export const saveJSONLabel = localize('saveJSONLabel', 'Save as JSON');
export const saveExcelLabel = localize('saveExcelLabel', 'Save as Excel');
+export const saveXMLLabel = localize('saveXMLLabel', 'Save as XML');
export const viewChartLabel = localize('viewChartLabel', 'View as Chart');
export const resultPaneLabel = localize('resultPaneLabel', 'Results');
diff --git a/src/sql/parts/query/common/query.contribution.ts b/src/sql/parts/query/common/query.contribution.ts
index a650c239db..9ab111ffdc 100644
--- a/src/sql/parts/query/common/query.contribution.ts
+++ b/src/sql/parts/query/common/query.contribution.ts
@@ -233,6 +233,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: gridCommands.saveAsExcel
});
+KeybindingsRegistry.registerCommandAndKeybindingRule({
+ id: gridActions.GRID_SAVEXML_ID,
+ weight: KeybindingWeight.EditorContrib,
+ when: ResultsGridFocusCondition,
+ primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_R, KeyMod.CtrlCmd | KeyCode.KEY_X),
+ handler: gridCommands.saveAsXml
+});
+
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: gridActions.GRID_VIEWASCHART_ID,
weight: KeybindingWeight.EditorContrib,
@@ -310,6 +318,16 @@ let registryProperties = {
'description': localize('sql.results.streaming', 'Enable results streaming; contains few minor visual issues'),
'default': false
},
+ 'sql.saveAsXml.formatted': {
+ 'type': 'string',
+ 'description': localize('sql.saveAsXml.formatted', '[Optional] When true, XML output will be formatted when saving results as XML'),
+ 'default': true
+ },
+ 'sql.saveAsXml.encoding': {
+ 'type': 'string',
+ 'description': localize('sql.saveAsXml.encoding', '[Optional] File encoding used when saving results as XML'),
+ 'default': 'utf-8'
+ },
'sql.copyIncludeHeaders': {
'type': 'boolean',
'description': localize('sql.copyIncludeHeaders', '[Optional] Configuration options for copying results from the Results View'),
diff --git a/src/sql/parts/query/common/resultSerializer.ts b/src/sql/parts/query/common/resultSerializer.ts
index 4b9c7b089e..dd0417d2d6 100644
--- a/src/sql/parts/query/common/resultSerializer.ts
+++ b/src/sql/parts/query/common/resultSerializer.ts
@@ -254,6 +254,24 @@ export class ResultSerializer {
return config;
}
+ private getConfigForXml(): SaveResultsRequestParams {
+ let saveResultsParams = { resultFormat: SaveFormat.XML as string };
+
+ // get save results config from vscode config
+ let saveConfig = WorkbenchUtils.getSqlConfigSection(this._workspaceConfigurationService, Constants.configSaveAsXml);
+ // if user entered config, set options
+ if (saveConfig) {
+ if (saveConfig.formatted !== undefined) {
+ saveResultsParams.formatted = saveConfig.formatted;
+ }
+ if (saveConfig.encoding !== undefined) {
+ saveResultsParams.encoding = saveConfig.encoding;
+ }
+ }
+
+ return saveResultsParams;
+ }
+
private getParameters(filePath: string, batchIndex: number, resultSetNo: number, format: string, selection: Slick.Range): SaveResultsRequestParams {
let saveResultsParams: SaveResultsRequestParams;
if (!path.isAbsolute(filePath)) {
@@ -268,6 +286,8 @@ export class ResultSerializer {
saveResultsParams = this.getConfigForJson();
} else if (format === SaveFormat.EXCEL) {
saveResultsParams = this.getConfigForExcel();
+ } else if (format === SaveFormat.XML) {
+ saveResultsParams = this.getConfigForXml();
}
saveResultsParams.filePath = this._filePath;
diff --git a/src/sql/parts/query/editor/actions.ts b/src/sql/parts/query/editor/actions.ts
index 0db77835e9..ce68aa0846 100644
--- a/src/sql/parts/query/editor/actions.ts
+++ b/src/sql/parts/query/editor/actions.ts
@@ -57,6 +57,10 @@ export class SaveResultAction extends Action {
public static SAVEEXCEL_LABEL = localize('saveAsExcel', 'Save As Excel');
public static SAVEEXCEL_ICON = 'saveExcel';
+ public static SAVEXML_ID = 'grid.saveAsXml';
+ public static SAVEXML_LABEL = localize('saveAsXml', 'Save As XML');
+ public static SAVEXML_ICON = 'saveXml';
+
constructor(
id: string,
label: string,
diff --git a/src/sql/parts/query/editor/gridPanel.ts b/src/sql/parts/query/editor/gridPanel.ts
index d79b342dcc..9b365f955c 100644
--- a/src/sql/parts/query/editor/gridPanel.ts
+++ b/src/sql/parts/query/editor/gridPanel.ts
@@ -695,6 +695,7 @@ class GridTable extends Disposable implements IView {
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
+ new SaveResultAction(SaveResultAction.SAVEXML_ID, SaveResultAction.SAVEXML_LABEL, SaveResultAction.SAVEXML_ICON, SaveFormat.XML),
this.instantiationService.createInstance(ChartDataAction)
);
@@ -753,6 +754,7 @@ class GridTable extends Disposable implements IView {
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
+ new SaveResultAction(SaveResultAction.SAVEXML_ID, SaveResultAction.SAVEXML_LABEL, SaveResultAction.SAVEXML_ICON, SaveFormat.XML),
new Separator(),
new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false),
new CopyResultAction(CopyResultAction.COPYWITHHEADERS_ID, CopyResultAction.COPYWITHHEADERS_LABEL, true)
diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts
index cf40c14bd4..8964aca9c5 100644
--- a/src/sql/sqlops.d.ts
+++ b/src/sql/sqlops.d.ts
@@ -882,7 +882,7 @@ declare module 'sqlops' {
// Save Results ===============================================================================
export interface SaveResultsRequestParams {
/**
- * 'csv', 'json', 'excel'
+ * 'csv', 'json', 'excel', 'xml'
*/
resultFormat: string;
ownerUri: string;
@@ -898,6 +898,7 @@ declare module 'sqlops' {
lineSeperator?: string;
textIdentifier?: string;
encoding?: string;
+ formatted?: boolean;
}
export interface SaveResultRequestResult {