in gui/extension/src/DocumentCommandHandler.ts [108:858]
public setup(host: ExtensionHost): void {
this.#host = host;
const context = host.context;
this.#codeBlocks.setup(context);
const dbConnectionsTreeView = window.createTreeView("msg.connections", {
treeDataProvider: this.#connectionsProvider,
showCollapseAll: true,
canSelectMany: false,
});
context.subscriptions.push(dbConnectionsTreeView);
// Register expand/collapse handlers
dbConnectionsTreeView.onDidExpandElement((event) => {
this.#connectionsProvider.didExpandElement(event.element);
});
dbConnectionsTreeView.onDidCollapseElement((event) => {
this.#connectionsProvider.didCollapseElement(event.element);
});
this.#openEditorsTreeDataProvider = new OpenEditorsTreeDataProvider(this.#openDocumentsModel);
const openEditorsTreeView = window.createTreeView(
"msg.openEditors",
{
treeDataProvider: this.#openEditorsTreeDataProvider,
showCollapseAll: true,
canSelectMany: false,
});
context.subscriptions.push(openEditorsTreeView);
this.#openEditorsTreeDataProvider.onSelect = (item: OpenDocumentDataModelEntry): void => {
if (!this.#selectionInProgress) {
void openEditorsTreeView.reveal(item, { select: true, focus: false, expand: 3 });
} else {
this.#selectionInProgress = false;
}
};
// Display the DB Connection Overview together with the initial display of the OPEN EDITORS view
openEditorsTreeView.onDidChangeVisibility((e) => {
// Get the user setting for msg.startup.showDbConnectionsTab
const showDbConnectionsTab = workspace.getConfiguration(`msg.startup`)
.get<boolean>("showDbConnectionsTab", true);
if (e.visible && this.#initialDisplayOfOpenEditorsView && showDbConnectionsTab) {
this.#initialDisplayOfOpenEditorsView = false;
// If the extension is already connected to the MySQL Shell websocket,
// open the DB Connection Overview right away
if (this.#isConnected) {
void commands.executeCommand("msg.openDBBrowser");
} else {
// Otherwise open the DB Connection Overview when connected
this.#displayDbConnectionOverviewWhenConnected = true;
}
}
});
requisitions.register("connectedToUrl", this.connectedToUrl);
requisitions.register("executeCodeBlock", this.executeCodeBlock);
requisitions.register("proxyRequest", this.proxyRequest);
context.subscriptions.push(commands.registerCommand("msg.refreshConnections", () => {
void requisitions.execute("refreshConnection", undefined);
}));
const openConnection = (entry?: ICdmConnectionEntry, editor?: InitialEditor) => {
if (entry) {
// "Open connection" acts differently, depending on whether the same connection is already open or not.
let provider;
if (this.#openDocumentsModel.isOpen(entry.details)) {
provider = this.#host.newProvider;
} else {
provider = this.#host.currentProvider;
}
void provider?.show(entry.details.id, editor);
}
};
context.subscriptions.push(commands.registerCommand("msg.openConnection", (entry?: ICdmConnectionEntry) => {
openConnection(entry);
}));
context.subscriptions.push(commands.registerCommand("msg.openConnectionNotebook",
(entry?: ICdmConnectionEntry) => {
openConnection(entry, "notebook");
}));
context.subscriptions.push(commands.registerCommand("msg.openConnectionSqlScript",
(entry?: ICdmConnectionEntry) => {
openConnection(entry, "script");
}));
context.subscriptions.push(commands.registerCommand("msg.openConnectionNewTab",
(entry?: ICdmConnectionEntry) => {
if (entry) {
const provider = this.#host.newProvider;
void provider?.show(entry.details.id);
}
}));
host.context.subscriptions.push(commands.registerCommand("msg.openScriptWithConnection", async (uri?: Uri) => {
if (!uri) {
return;
}
const connection = await host.determineConnection(DBType.MySQL, true);
if (connection) {
const stat = await workspace.fs.stat(uri);
if (stat.size >= 10000000) {
await ui.showInformationMessage(`The file "${uri.fsPath}" ` +
`is too large to edit it in a web view. Instead use the VS Code built-in editor.`, {});
} else {
const content = (await workspace.fs.readFile(uri)).toString();
const provider = this.#host.currentProvider;
if (provider) {
// We load only sql and mysql scripts here.
const language: EditorLanguage = "mysql";
const caption = basename(uri.fsPath);
const request: IScriptRequest = {
id: uuid(),
caption,
content,
language,
};
let scripts = this.#openScripts.get(provider);
if (!scripts) {
scripts = new Map();
this.#openScripts.set(provider, scripts);
}
scripts.set(request.id, uri);
const details = connection.details;
void provider.editScript(details.id, request);
}
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.showTableData", (entry?: ICdmTableEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
const configuration = workspace.getConfiguration(`msg.dbEditor`);
const uppercaseKeywords = configuration.get("upperCaseKeywords", true);
const select = uppercaseKeywords ? "SELECT" : "select";
const from = uppercaseKeywords ? "FROM" : "from";
const query = `${select} * ${from} \`${entry.schema}\`.\`${entry.caption}\``;
const name = `${entry.schema}.${entry.caption} - Data`;
const request: IScriptRequest = {
id: uuid(),
caption: name,
language: "mysql",
content: query,
};
void provider?.runScript(entry.connection.details.id, request);
}
}));
context.subscriptions.push(commands.registerCommand("msg.selectTableRows", (entry?: ICdmTableEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
const configuration = workspace.getConfiguration(`msg.dbEditor`);
const uppercaseKeywords = configuration.get("upperCaseKeywords", true);
const select = uppercaseKeywords ? "SELECT" : "select";
const from = uppercaseKeywords ? "FROM" : "from";
const query = `${select} * ${from} \`${entry.schema}\`.\`${entry.caption}\``;
void provider?.runCode(entry.connection.details.id, {
code: query,
language: "mysql",
linkId: -1,
});
}
}));
const showAdminPage = (provider: IWebviewProvider | undefined, _caption: string, page: ICdmAdminPageEntry) => {
provider ??= this.#host.currentProvider;
if (provider instanceof DBConnectionViewProvider) {
const connectionId = page.connection.details.id;
const latestPageId = this.latestPagesByConnection.get(connectionId);
const documents = this.#openEditorsTreeDataProvider.findAdminDocument(provider,
connectionId, latestPageId);
// Check the list of open admin pages for the type we want to open.
const existing = documents.find((doc) => {
return doc.pageType === page.pageType;
});
const id = existing ? existing.id : uuid();
const data: IDocumentOpenData = {
connection: page.connection.details,
documentDetails: {
id,
type: OdmEntityType.AdminPage,
pageType: page.pageType,
caption: page.caption,
},
};
void provider.showDocument(data);
}
};
context.subscriptions.push(commands.registerCommand("msg.showServerStatus", showAdminPage));
context.subscriptions.push(commands.registerCommand("msg.showClientConnections", showAdminPage));
context.subscriptions.push(commands.registerCommand("msg.showPerformanceDashboard", showAdminPage));
context.subscriptions.push(commands.registerCommand("msg.showLakehouseNavigator", showAdminPage));
context.subscriptions.push(commands.registerCommand("msg.addConnection", () => {
const provider = this.#host.currentProvider;
void provider?.addConnection();
}));
context.subscriptions.push(commands.registerCommand("msg.refreshConnection", (item?: ConnectionTreeItem) => {
void requisitions.execute("refreshConnection", item?.dataModelEntry);
}));
context.subscriptions.push(commands.registerCommand("msg.removeConnection", (entry?: ICdmConnectionEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
void provider?.removeConnection(entry.details.id);
}
}));
context.subscriptions.push(commands.registerCommand("msg.editConnection", (entry?: ICdmConnectionEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
void provider?.editConnection(entry?.details.id);
}
}));
context.subscriptions.push(commands.registerCommand("msg.duplicateConnection",
(entry?: ICdmConnectionEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
void provider?.duplicateConnection(entry.details.id);
}
}));
context.subscriptions.push(commands.registerCommand("msg.showSystemSchemasOnConnection",
(entry?: ICdmConnectionEntry) => {
if (entry) {
if (entry.type === CdmEntityType.Connection) {
entry.state.payload = { showSystemSchemas: true };
void requisitions.execute("refreshConnection", entry);
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.hideSystemSchemasOnConnection",
(entry?: ICdmConnectionEntry) => {
if (entry) {
if (entry.type === CdmEntityType.Connection) {
entry.state.payload = { showSystemSchemas: false };
void requisitions.execute("refreshConnection", entry);
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.openDBBrowser", (provider?: IWebviewProvider) => {
provider ??= this.#host.currentProvider;
if (provider instanceof DBConnectionViewProvider) {
void provider.show();
} else {
const provider = this.#host.currentProvider;
if (provider) {
void provider.show();
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.makeCurrentSchema", (entry?: ICdmSchemaEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
if (provider) {
void provider?.makeCurrentSchema(entry.connection.details.id, entry.caption)
.then((success) => {
if (success) {
this.#connectionsProvider.makeCurrentSchema(entry);
}
});
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.createProcedure",
(entry?: ICdmSchemaGroupEntry<CdmEntityType.StoredProcedure>) => {
if (entry) {
void this.addNewSqlScript(entry.connection.details.id, "msg.createProcedure",
entry.parent.caption, "New Procedure", "my_procedure");
}
}));
context.subscriptions.push(commands.registerCommand("msg.createFunction",
(entry?: ICdmSchemaGroupEntry<CdmEntityType.StoredFunction>) => {
if (entry) {
void this.addNewSqlScript(entry.connection.details.id, "msg.createFunction",
entry.parent.caption, "New Function", "my_function");
}
}));
context.subscriptions.push(commands.registerCommand("msg.createFunctionJs",
(entry?: ICdmSchemaGroupEntry<CdmEntityType.StoredFunction>) => {
if (entry) {
void this.addNewSqlScript(entry.connection.details.id, "msg.createFunctionJs",
entry.parent.caption, "New Function", "my_function");
}
}));
context.subscriptions.push(commands.registerCommand("msg.createProcedureJs",
(entry?: ICdmSchemaGroupEntry<CdmEntityType.StoredProcedure>) => {
if (entry) {
void this.addNewSqlScript(entry.connection.details.id, "msg.createProcedureJs",
entry.parent.caption, "New Procedure", "my_procedure");
}
}));
context.subscriptions.push(commands.registerCommand("msg.editRoutine", async (entry?: ICdmRoutineEntry) => {
if (entry) {
const entryType = entry.type === CdmEntityType.StoredFunction ? "function" : "procedure";
const sql = await this.#connectionsProvider.getCreateSqlScript(entry, entryType, true, true);
void this.addNewSqlScript(entry.connection.details.id, "msg.editRoutine",
entry.parent.caption, "Edit Routine", sql);
}
}));
context.subscriptions.push(commands.registerCommand("msg.dropSchema", (entry?: ICdmSchemaEntry) => {
if (entry) {
this.#connectionsProvider.dropItem(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.dropTable", (entry?: ICdmTableEntry) => {
if (entry) {
this.#connectionsProvider.dropItem(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.dropView", (entry?: ICdmViewEntry) => {
if (entry) {
this.#connectionsProvider.dropItem(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.dropRoutine", (entry?: ICdmRoutineEntry) => {
if (entry) {
this.#connectionsProvider.dropItem(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.dropTrigger", (entry?: ICdmTriggerEntry) => {
if (entry) {
this.#connectionsProvider.dropItem(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.dropEvent", (entry?: ICdmEventEntry) => {
if (entry) {
this.#connectionsProvider.dropItem(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.defaultConnection",
(entry?: ICdmConnectionEntry) => {
if (entry) {
const configuration = workspace.getConfiguration(`msg.editor`);
void configuration.update("defaultDbConnection", entry.details.caption,
ConfigurationTarget.Global).then(() => {
void ui.showInformationMessage(`"${entry.caption}" has been set as default DB Connection.`,
{});
});
}
}));
context.subscriptions.push(commands.registerCommand("msg.copyNameToClipboard",
(entry?: ConnectionDataModelEntry) => {
if (entry) {
this.#connectionsProvider.copyNameToClipboard(entry);
}
}));
context.subscriptions.push(commands.registerCommand("msg.copyCreateScriptToClipboard",
(entry?: ConnectionDataModelEntry) => {
if (entry) {
const typeName = cdbDbEntityTypeName.get(entry.type)!;
void this.#connectionsProvider.copyCreateScriptToClipboard(entry, typeName);
}
}));
context.subscriptions.push(commands.registerCommand("msg.copyCreateScriptWithDelimitersToClipboard",
(entry?: ConnectionDataModelEntry) => {
if (entry) {
const typeName = cdbDbEntityTypeName.get(entry.type)!;
void this.#connectionsProvider.copyCreateScriptToClipboard(entry, typeName, true);
}
}));
context.subscriptions.push(commands.registerCommand("msg.copyDropCreateScriptWithDelimitersToClipboard",
(entry?: ConnectionDataModelEntry) => {
if (entry) {
const typeName = cdbDbEntityTypeName.get(entry.type)!;
void this.#connectionsProvider.copyCreateScriptToClipboard(entry, typeName, true, true);
}
}));
context.subscriptions.push(commands.registerCommand("msg.editInScriptEditor", async (uri?: Uri) => {
if (uri?.scheme === "file") {
if (!fs.existsSync(uri.fsPath)) {
void ui.showErrorMessage(`The file ${uri.fsPath} could not be found.`, {});
} else {
const stat = await workspace.fs.stat(uri);
if (stat.size >= 10000000) {
await ui.showInformationMessage(`The file "${uri.fsPath}" is too large to edit it in a web ` +
`view. Instead use the VS Code built-in editor.`, {});
} else {
const connection = await host.determineConnection();
if (connection) {
await workspace.fs.readFile(uri).then((value) => {
const content = value.toString();
const provider = this.#host.currentProvider;
if (provider) {
const name = basename(uri.fsPath);
const details: IScriptRequest = {
id: uuid(),
caption: name,
language: this.languageFromConnection(connection),
content,
};
let scripts = this.#openScripts.get(provider);
if (!scripts) {
scripts = new Map();
this.#openScripts.set(provider, scripts);
}
scripts.set(details.id, uri);
void provider.editScript(connection.details.id, details);
}
});
}
}
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.loadScriptFromDisk", (
entry?: ICdmConnectionEntry) => {
if (entry) {
void window.showOpenDialog({
title: "Select the script file to load to MySQL Shell",
openLabel: "Select Script File",
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
filters: {
// eslint-disable-next-line @typescript-eslint/naming-convention
SQL: ["sql", "mysql"], TypeScript: ["ts"], JavaScript: ["js"],
},
}).then(async (value) => {
if (value && value.length === 1) {
const uri = value[0];
const stat = await workspace.fs.stat(uri);
if (stat.size >= 10000000) {
await ui.showInformationMessage(`The file "${uri.fsPath}" is too large to edit it in a ` +
`web view. Instead use the VS Code built-in editor.`, {});
} else {
await workspace.fs.readFile(uri).then((value) => {
const content = value.toString();
const provider = this.#host.currentProvider;
if (provider) {
let language: EditorLanguage = "mysql";
const name = basename(uri.fsPath);
const ext = name.substring(name.lastIndexOf(".") ?? 0);
switch (ext) {
case ".ts": {
language = "typescript";
break;
}
case ".js": {
language = "javascript";
break;
}
case ".sql": {
if (entry.details.dbType === DBType.Sqlite) {
language = "sql";
}
break;
}
default:
}
const details: IScriptRequest = {
id: uuid(),
caption: name,
language,
content,
};
let scripts = this.#openScripts.get(provider);
if (!scripts) {
scripts = new Map();
this.#openScripts.set(provider, scripts);
}
scripts.set(details.id, uri);
void provider.editScript(entry.details.id, details);
}
});
}
}
});
}
}));
context.subscriptions.push(commands.registerCommand("msg.selectDocument",
(item?: DocumentTreeBaseItem<OpenDocumentDataModelEntry>) => {
if (item) {
const dataModelEntry = item.dataModelEntry;
let provider;
let connectionId: number | undefined;
let pageId: string | undefined;
switch (dataModelEntry.type) {
case OdmEntityType.Script:
case OdmEntityType.Notebook:
case OdmEntityType.AdminPage: {
provider = dataModelEntry.parent?.parent?.provider as DBConnectionViewProvider;
connectionId = dataModelEntry.parent!.details.id;
pageId = dataModelEntry.parent?.id;
break;
}
case OdmEntityType.ConnectionPage: {
provider = dataModelEntry.parent?.provider as DBConnectionViewProvider;
connectionId = dataModelEntry.details.id;
pageId = dataModelEntry.id;
break;
}
case OdmEntityType.Overview: {
provider = this.#host.currentProvider;
break;
}
case OdmEntityType.AppProvider: {
provider = dataModelEntry.provider as DBConnectionViewProvider;
// Selecting a provider entry means to select the web view tab.
void provider?.reveal();
return;
}
case OdmEntityType.ShellSessionRoot: {
provider = dataModelEntry.parent?.provider as DBConnectionViewProvider;
void provider?.reveal();
return;
}
case OdmEntityType.ShellSession: {
provider = dataModelEntry.parent?.parent?.provider as DBConnectionViewProvider;
break;
}
default:
}
if (provider) {
this.#selectionInProgress = true;
void provider.selectDocument(dataModelEntry.id, connectionId, pageId);
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.mds.createConnectionViaBastionService",
(item?: OciDbSystemTreeItem) => {
if (item) {
const provider = this.#host.currentProvider;
void provider?.addConnection(item.dbSystem, item.profile.profile);
}
}));
context.subscriptions.push(commands.registerTextEditorCommand("msg.executeEmbeddedSqlFromEditor",
(editor?: TextEditor) => {
if (editor) {
void host.determineConnection().then((connection) => {
if (connection) {
this.#codeBlocks.executeSqlFromEditor(editor, connection.details.caption,
connection.details.id);
}
});
}
}));
context.subscriptions.push(commands.registerTextEditorCommand("msg.executeSelectedSqlFromEditor",
(editor?: TextEditor) => {
if (editor) {
void host.determineConnection().then((connection) => {
if (connection) {
const provider = this.#host.currentProvider;
if (provider) {
let sql = "";
if (!editor.selection.isEmpty) {
editor.selections.forEach((selection) => {
sql += editor.document.getText(selection);
});
} else {
sql = editor.document.getText();
}
return provider.runScript(connection.details.id, {
id: uuid(),
language: this.languageFromConnection(connection),
caption: "Selected SQL",
content: sql,
});
}
}
});
}
}));
context.subscriptions.push(commands.registerCommand("msg.closeEditor", (
entry?: IOdmNotebookEntry | IOdmScriptEntry) => {
if (entry?.parent) {
const provider = entry.parent.parent?.provider;
const connection = entry.parent;
if (provider instanceof DBConnectionViewProvider) {
void provider.closeEditor(connection.details.id, entry.id, entry.parent.id);
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.newNotebookMysql",
(entry?: IOdmConnectionPageEntry) => {
void this.createNewEditor({ entry, language: "msg" });
}));
context.subscriptions.push(commands.registerCommand("msg.newNotebookSqlite",
(entry?: IOdmConnectionPageEntry) => {
void this.createNewEditor({ entry, language: "msg" });
}));
context.subscriptions.push(commands.registerCommand("msg.newScriptJs", (entry?: IOdmConnectionPageEntry) => {
void this.createNewEditor({ entry, language: "javascript" });
}));
context.subscriptions.push(commands.registerCommand("msg.newScriptMysql", (entry?: IOdmConnectionPageEntry) => {
void this.createNewEditor({ entry, language: "mysql" });
}));
context.subscriptions.push(commands.registerCommand("msg.newScriptSqlite",
(entry?: IOdmConnectionPageEntry) => {
void this.createNewEditor({ entry, language: "sql" });
}));
context.subscriptions.push(commands.registerCommand("msg.newScriptTs", (entry?: IOdmConnectionPageEntry) => {
void this.createNewEditor({ entry, language: "typescript" });
}));
context.subscriptions.push(commands.registerCommand("msg.mrs.addDbObject", (
entry?: ICdmTableEntry | ICdmViewEntry | ICdmRoutineEntry) => {
if (entry) {
const connection = entry.parent.parent.parent;
if (entry.type === CdmEntityType.Table || entry.type === CdmEntityType.View
|| entry.type === CdmEntityType.StoredFunction || entry.type === CdmEntityType.StoredProcedure) {
// First, create a new temporary dbObject, then call the DbObject dialog
this.createNewDbObject(connection.backend, entry).then((dbObject) => {
const provider = this.#host.currentProvider;
void provider?.editMrsDbObject(connection.details.id,
{ dbObject, createObject: true });
}).catch((reason) => {
void ui.showErrorMessage(`${String(reason)}`, {});
});
} else {
void ui.showErrorMessage(`The database object type '${entry.caption}' is not supported at ` +
`this time`, {});
}
}
}));
context.subscriptions.push(commands.registerCommand("msg.mrs.editDbObject", (entry?: ICdmRestDbObjectEntry) => {
if (entry) {
const provider = this.#host.currentProvider;
const connection = entry.parent.parent.parent.parent;
void provider?.editMrsDbObject(connection.details.id,
{ dbObject: entry.details, createObject: false });
}
}));
context.subscriptions.push(commands.registerCommand("msg.newSession", () => {
const provider = this.#host.currentProvider;
void provider?.openSession({ sessionId: uuid() });
}));
context.subscriptions.push(commands.registerCommand("msg.openSession", (details: IShellSessionDetails) => {
const provider = this.#host.currentProvider;
void provider?.openSession(details);
}));
context.subscriptions.push(commands.registerCommand("msg.newSessionUsingConnection",
(entry: ICdmConnectionEntry | IOdmConnectionPageEntry) => {
const provider = this.#host.currentProvider;
const caption = entry.type === CdmEntityType.Connection ? entry.details.caption : entry.caption;
const dbConnectionId = entry.details.id;
const details: IShellSessionDetails = {
sessionId: uuid(),
caption,
dbConnectionId,
};
void provider?.openSession(details);
}));
context.subscriptions.push(commands.registerCommand("msg.removeSession", (entry: IOdmShellSessionEntry) => {
const provider = entry.parent?.parent?.provider;
if (provider instanceof DBConnectionViewProvider) {
void provider.removeSession(entry.details);
}
}));
}