mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Add Notebook <-> SQL convert (#11590)
* Add Notebook <-> SQL convert * Update STS
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "3.0.0-release.8",
|
||||
"version": "3.0.0-release.11",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-netcoreapp3.1.zip",
|
||||
"Windows_64": "win-x64-netcoreapp3.1.zip",
|
||||
|
||||
@@ -21,6 +21,18 @@
|
||||
},
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "mssql.exportSqlAsNotebook",
|
||||
"title": "%mssql.exportSqlAsNotebook%"
|
||||
},
|
||||
{
|
||||
"command": "mssql.exportNotebookToSql",
|
||||
"title": "%mssql.exportNotebookToSql%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/export_blue_dark.svg",
|
||||
"light": "resources/light/export_blue_light.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "mssqlCluster.uploadFiles",
|
||||
"title": "%mssqlCluster.uploadFiles%"
|
||||
@@ -355,6 +367,14 @@
|
||||
},
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "mssql.exportSqlAsNotebook",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "mssql.exportNotebookToSql",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "mssqlCluster.uploadFiles",
|
||||
"when": "false"
|
||||
@@ -450,6 +470,12 @@
|
||||
"when": "nodeType == mssqlCluster:file && nodeSubType =~/:spark:/",
|
||||
"group": "1mssqlCluster@6"
|
||||
}
|
||||
],
|
||||
"notebook/toolbar": [
|
||||
{
|
||||
"command": "mssql.exportNotebookToSql",
|
||||
"when": "providerId == sql"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashboard": {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"mssql.disabled": "Disabled",
|
||||
"mssql.enabled": "Enabled",
|
||||
|
||||
"mssql.exportNotebookToSql": "Export Notebook as SQL",
|
||||
"mssql.exportSqlAsNotebook": "Export SQL as Notebook",
|
||||
"mssql.configuration.title": "MSSQL configuration",
|
||||
"mssql.query.displayBitAsNumber": "Should BIT columns be displayed as numbers (1 or 0)? If false, BIT columns will be displayed as 'true' or 'false'",
|
||||
"mssql.query.maxXmlCharsToStore": "Number of XML characters to store after running a query",
|
||||
|
||||
15
extensions/mssql/resources/dark/export_blue_dark.svg
Normal file
15
extensions/mssql/resources/dark/export_blue_dark.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<g clip-path="url(#clip1)">
|
||||
<path d="M11.6943 4.60156L14.1006 7.01562H2.0459V8.01562H14.0771L11.6943 10.3984L12.3975 11.1016L15.999 7.5L12.3975 3.89844L11.6943 4.60156ZM1.0459 4H0.0458984V11H1.0459V4Z" fill="#3AA0F3"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1">
|
||||
<rect width="16" height="16" fill="white" transform="translate(-0.000976562)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 567 B |
15
extensions/mssql/resources/light/export_blue_light.svg
Normal file
15
extensions/mssql/resources/light/export_blue_light.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<g clip-path="url(#clip1)">
|
||||
<path d="M11.6943 4.60156L14.1006 7.01562H2.0459V8.01562H14.0771L11.6943 10.3984L12.3975 11.1016L15.999 7.5L12.3975 3.89844L11.6943 4.60156ZM1.0459 4H0.0458984V11H1.0459V4Z" fill="#0078D4"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1">
|
||||
<rect width="16" height="16" fill="white" transform="translate(-0.000976562)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 567 B |
@@ -31,6 +31,8 @@ export const hdfsRootPath = '/';
|
||||
export const clusterEndpointsProperty = 'clusterEndpoints';
|
||||
export const isBigDataClusterProperty = 'isBigDataCluster';
|
||||
|
||||
export const ViewType = 'view';
|
||||
|
||||
// SERVICE NAMES //////////////////////////////////////////////////////////
|
||||
export const ObjectExplorerService = 'objectexplorer';
|
||||
export const CmsService = 'cmsService';
|
||||
@@ -38,8 +40,8 @@ export const DacFxService = 'dacfxService';
|
||||
export const SchemaCompareService = 'schemaCompareService';
|
||||
export const LanguageExtensionService = 'languageExtensionService';
|
||||
export const objectExplorerPrefix: string = 'objectexplorer://';
|
||||
export const ViewType = 'view';
|
||||
export const SqlAssessmentService = 'sqlAssessmentService';
|
||||
export const NotebookConvertService = 'notebookConvertService';
|
||||
|
||||
export enum BuiltInCommands {
|
||||
SetContext = 'setContext'
|
||||
|
||||
@@ -747,3 +747,33 @@ export class CompletionExtensionParams {
|
||||
export namespace CompletionExtLoadRequest {
|
||||
export const type = new RequestType<CompletionExtensionParams, boolean, void, void>('completion/extLoad');
|
||||
}
|
||||
|
||||
// ------------------------------- < Load Completion Extension Request > ------------------------------------
|
||||
|
||||
/// ------------------------------- <Convert Notebook> -----------------------------
|
||||
|
||||
export interface ConvertNotebookToSqlParams {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export namespace ConvertNotebookToSqlRequest {
|
||||
export const type = new RequestType<ConvertNotebookToSqlParams, ConvertNotebookToSqlResult, void, void>('notebookconvert/convertnotebooktosql');
|
||||
}
|
||||
|
||||
export interface ConvertNotebookToSqlResult extends azdata.ResultStatus {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ConvertSqlToNotebookParams {
|
||||
clientUri: string;
|
||||
}
|
||||
|
||||
export namespace ConvertSqlToNotebookRequest {
|
||||
export const type = new RequestType<ConvertSqlToNotebookParams, ConvertSqlToNotebookResult, void, void>('notebookconvert/convertsqltonotebook');
|
||||
}
|
||||
|
||||
export interface ConvertSqlToNotebookResult extends azdata.ResultStatus {
|
||||
content: string;
|
||||
}
|
||||
|
||||
// ------------------------------- <Convert Notebook> -----------------------------
|
||||
|
||||
@@ -30,6 +30,7 @@ import { SqlToolsServer } from './sqlToolsServer';
|
||||
import { promises as fs } from 'fs';
|
||||
import { IconPathHelper } from './iconHelper';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { INotebookConvertService } from './notebookConvert/notebookConvertService';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', "This sample code loads the file into a data frame and shows the first 10 results.");
|
||||
@@ -79,6 +80,24 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
|
||||
context.subscriptions.push(server);
|
||||
await server.start(appContext);
|
||||
|
||||
vscode.commands.registerCommand('mssql.exportSqlAsNotebook', async (uri: vscode.Uri) => {
|
||||
const result = await appContext.getService<INotebookConvertService>(Constants.NotebookConvertService).convertSqlToNotebook(uri.toString());
|
||||
const title = findNextUntitledEditorName();
|
||||
const untitledUri = vscode.Uri.parse(`untitled:${title}`);
|
||||
await azdata.nb.showNotebookDocument(untitledUri, { initialContent: result.content });
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('mssql.exportNotebookToSql', async (uri: vscode.Uri) => {
|
||||
// SqlToolsService doesn't currently store anything about Notebook documents so we have to pass the raw JSON to it directly
|
||||
// We use vscode.workspace.textDocuments here because the azdata.nb.notebookDocuments don't actually contain their contents
|
||||
// (they're left out for perf purposes)
|
||||
const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString());
|
||||
const result = await appContext.getService<INotebookConvertService>(Constants.NotebookConvertService).convertNotebookToSql(doc.getText());
|
||||
|
||||
const sqlDoc = await vscode.workspace.openTextDocument({ language: 'sql', content: result.content });
|
||||
await vscode.commands.executeCommand('vscode.open', sqlDoc.uri);
|
||||
});
|
||||
|
||||
return createMssqlApi(appContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AppContext } from '../appContext';
|
||||
import { SqlOpsDataClient, ISqlOpsFeature } from 'dataprotocol-client';
|
||||
import { ClientCapabilities } from 'vscode-languageclient';
|
||||
import * as constants from '../constants';
|
||||
import * as contracts from '../contracts';
|
||||
|
||||
export interface INotebookConvertService {
|
||||
convertNotebookToSql(content: string): Promise<contracts.ConvertNotebookToSqlResult | undefined>;
|
||||
convertSqlToNotebook(content: string): Promise<contracts.ConvertSqlToNotebookResult | undefined>;
|
||||
}
|
||||
|
||||
export class NotebookConvertService implements INotebookConvertService {
|
||||
public static asFeature(context: AppContext): ISqlOpsFeature {
|
||||
return class extends NotebookConvertService {
|
||||
constructor(client: SqlOpsDataClient) {
|
||||
super(context, client);
|
||||
}
|
||||
|
||||
fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||
}
|
||||
|
||||
initialize(): void {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private constructor(context: AppContext, protected readonly client: SqlOpsDataClient) {
|
||||
context.registerService(constants.NotebookConvertService, this);
|
||||
}
|
||||
|
||||
async convertNotebookToSql(content: string): Promise<contracts.ConvertNotebookToSqlResult | undefined> {
|
||||
let params: contracts.ConvertNotebookToSqlParams = { content: content };
|
||||
try {
|
||||
return this.client.sendRequest(contracts.ConvertNotebookToSqlRequest.type, params);
|
||||
}
|
||||
catch (e) {
|
||||
this.client.logFailedRequest(contracts.ConvertNotebookToSqlRequest.type, e);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
async convertSqlToNotebook(content: string): Promise<contracts.ConvertSqlToNotebookResult | undefined> {
|
||||
let params: contracts.ConvertSqlToNotebookParams = { clientUri: content };
|
||||
try {
|
||||
return this.client.sendRequest(contracts.ConvertSqlToNotebookRequest.type, params);
|
||||
}
|
||||
catch (e) {
|
||||
this.client.logFailedRequest(contracts.ConvertSqlToNotebookRequest.type, e);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { promises as fs } from 'fs';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { LanguageExtensionService } from './languageExtension/languageExtensionService';
|
||||
import { SqlAssessmentService } from './sqlAssessment/sqlAssessmentService';
|
||||
import { NotebookConvertService } from './notebookConvert/notebookConvertService';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
|
||||
@@ -160,7 +161,8 @@ function getClientOptions(context: AppContext): ClientOptions {
|
||||
LanguageExtensionService.asFeature(context),
|
||||
DacFxService.asFeature(context),
|
||||
CmsService.asFeature(context),
|
||||
SqlAssessmentService.asFeature(context)
|
||||
SqlAssessmentService.asFeature(context),
|
||||
NotebookConvertService.asFeature(context)
|
||||
],
|
||||
outputChannel: new CustomOutputChannel()
|
||||
};
|
||||
|
||||
@@ -118,3 +118,9 @@
|
||||
background-image: url('disable_sqlcmd_inverse.svg');
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.carbon-taskbar .codicon.export {
|
||||
background-origin: initial;
|
||||
background-position: left;
|
||||
background-size: 11px
|
||||
}
|
||||
|
||||
@@ -287,6 +287,15 @@
|
||||
background-image: url("stop_inverse.svg");
|
||||
}
|
||||
|
||||
.hc-black .codicon.export,
|
||||
.vs-dark .codicon.export {
|
||||
background: url("export_inverse.svg") center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .codicon.export {
|
||||
background: url("export.svg") center center no-repeat;
|
||||
}
|
||||
|
||||
/* Notebook cells */
|
||||
.codicon.toolbarIconRunInactive {
|
||||
background-image: url("execute_cell_grey.svg");
|
||||
|
||||
15
src/sql/media/icons/export.svg
Normal file
15
src/sql/media/icons/export.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<g clip-path="url(#clip1)">
|
||||
<path d="M11.6943 4.60156L14.1006 7.01562H2.0459V8.01562H14.0771L11.6943 10.3984L12.3975 11.1016L15.999 7.5L12.3975 3.89844L11.6943 4.60156ZM1.0459 4H0.0458984V11H1.0459V4Z" fill="#323130"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1">
|
||||
<rect width="16" height="16" fill="white" transform="translate(-0.000976562)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 567 B |
15
src/sql/media/icons/export_inverse.svg
Normal file
15
src/sql/media/icons/export_inverse.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<g clip-path="url(#clip1)">
|
||||
<path d="M11.6943 4.60156L14.1006 7.01562H2.0459V8.01562H14.0771L11.6943 10.3984L12.3975 11.1016L15.999 7.5L12.3975 3.89844L11.6943 4.60156ZM1.0459 4H0.0458984V11H1.0459V4Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1">
|
||||
<rect width="16" height="16" fill="white" transform="translate(-0.000976562)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 565 B |
@@ -553,7 +553,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
// This is similar behavior that exists in MenuItemActionItem
|
||||
if (action instanceof MenuItemAction) {
|
||||
|
||||
if (action.item.id.includes('jupyter.cmd') && this.previewFeaturesEnabled) {
|
||||
if ((action.item.id.includes('jupyter.cmd') && this.previewFeaturesEnabled) || action.item.id.includes('mssql')) {
|
||||
action.tooltip = action.label;
|
||||
action.label = '';
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/q
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { getCurrentGlobalConnection } from 'sql/workbench/browser/taskUtilities';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { OEAction } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeViewItemHandleArg } from 'sql/workbench/common/views';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
@@ -813,3 +813,27 @@ export class ListDatabasesActionItem extends Disposable implements IActionViewIt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Action class that sends the request to convert the contents of the sql editor
|
||||
* into a Notebook document
|
||||
*/
|
||||
export class ExportAsNotebookAction extends QueryTaskbarAction {
|
||||
|
||||
public static IconClass = 'export';
|
||||
public static ID = 'exportAsNotebookAction';
|
||||
|
||||
constructor(
|
||||
editor: QueryEditor,
|
||||
@IConnectionManagementService connectionManagementService: IConnectionManagementService,
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
super(connectionManagementService, editor, ConnectDatabaseAction.ID, ExportAsNotebookAction.IconClass);
|
||||
|
||||
this.label = nls.localize('queryEditor.exportSqlAsNotebook', "Export as Notebook");
|
||||
}
|
||||
|
||||
public async run(): Promise<void> {
|
||||
this._commandService.executeCommand('mssql.exportSqlAsNotebook', this.editor.input.uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ export class QueryEditor extends BaseEditor {
|
||||
private _actualQueryPlanAction: actions.ActualQueryPlanAction;
|
||||
private _listDatabasesActionItem: actions.ListDatabasesActionItem;
|
||||
private _toggleSqlcmdMode: actions.ToggleSqlCmdModeAction;
|
||||
private _exportAsNotebookAction: actions.ExportAsNotebookAction;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -183,6 +184,7 @@ export class QueryEditor extends BaseEditor {
|
||||
this._estimatedQueryPlanAction = this.instantiationService.createInstance(actions.EstimatedQueryPlanAction, this);
|
||||
this._actualQueryPlanAction = this.instantiationService.createInstance(actions.ActualQueryPlanAction, this);
|
||||
this._toggleSqlcmdMode = this.instantiationService.createInstance(actions.ToggleSqlCmdModeAction, this, false);
|
||||
this._exportAsNotebookAction = this.instantiationService.createInstance(actions.ExportAsNotebookAction, this);
|
||||
|
||||
this.setTaskbarContent();
|
||||
|
||||
@@ -266,13 +268,14 @@ export class QueryEditor extends BaseEditor {
|
||||
{ action: this._listDatabasesAction },
|
||||
{ element: separator },
|
||||
{ action: this._estimatedQueryPlanAction },
|
||||
{ action: this._toggleSqlcmdMode }
|
||||
{ action: this._toggleSqlcmdMode },
|
||||
{ action: this._exportAsNotebookAction }
|
||||
];
|
||||
|
||||
// Remove the estimated query plan action if preview features are not enabled
|
||||
let previewFeaturesEnabled = this.configurationService.getValue('workbench')['enablePreviewFeatures'];
|
||||
if (!previewFeaturesEnabled) {
|
||||
content = content.slice(0, -2);
|
||||
content.splice(7, 1);
|
||||
}
|
||||
|
||||
this.taskbar.setContent(content);
|
||||
|
||||
Reference in New Issue
Block a user