Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CB-5832 Add SQL editor shortcut to save script #3167

Open
wants to merge 10 commits into
base: devel
Choose a base branch
from
5 changes: 5 additions & 0 deletions webapp/packages/plugin-help/src/Shortcuts/SHORTCUTS_DATA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
KEY_BINDING_SQL_EDITOR_FORMAT,
KEY_BINDING_SQL_EDITOR_SHOW_EXECUTION_PLAN,
} from '@cloudbeaver/plugin-sql-editor';
import { KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT } from '@cloudbeaver/plugin-sql-editor-navigation-tab-script';

import type { IShortcut } from './IShortcut.js';

Expand Down Expand Up @@ -64,6 +65,10 @@ export const SQL_EDITOR_SHORTCUTS: IShortcut[] = [
label: 'sql_editor_shortcut_format',
code: transformKeys(KEY_BINDING_SQL_EDITOR_FORMAT),
},
{
label: 'sql_editor_shortcut_save_as_script',
code: transformKeys(KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT),
},
{
label: 'sql_editor_shortcut_undo',
code: transformKeys(KEY_BINDING_UNDO),
Expand Down
8 changes: 8 additions & 0 deletions webapp/packages/plugin-help/src/locales/en.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export default [
['shortcuts_title', 'Shortcuts'],

Expand All @@ -14,6 +21,7 @@ export default [
['sql_editor_shortcut_execute_script', 'Execute script'],
['sql_editor_shortcut_show_execution_plan', 'Show Execution plan'],
['sql_editor_shortcut_format', 'Format script'],
['sql_editor_shortcut_save_as_script', 'Save as script'],
['sql_editor_shortcut_open_editor_in_new_tab', 'Open SQL Editor in the separate browser Tab'],
['sql_editor_shortcut_undo', 'Undo'],
['sql_editor_shortcut_redo', 'Redo'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default [
['sql_editor_shortcut_execute_script', 'Exécuter le script'],
['sql_editor_shortcut_show_execution_plan', "Afficher le plan d'exécution"],
['sql_editor_shortcut_format', 'Formater le script'],
['sql_editor_shortcut_save_as_script', 'Save as script'],
['sql_editor_shortcut_open_editor_in_new_tab', "Ouvrir l'éditeur SQL dans un nouvel onglet"],
['sql_editor_shortcut_undo', 'Annuler'],
['sql_editor_shortcut_redo', 'Rétablir'],
Expand Down
8 changes: 8 additions & 0 deletions webapp/packages/plugin-help/src/locales/it.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export default [
['shortcuts_title', 'Shortcuts'],

Expand All @@ -14,6 +21,7 @@ export default [
['sql_editor_shortcut_execute_script', 'Execute script'],
['sql_editor_shortcut_show_execution_plan', 'Show Execution plan'],
['sql_editor_shortcut_format', 'Format script'],
['sql_editor_shortcut_save_as_script', 'Save as script'],
['sql_editor_shortcut_open_editor_in_new_tab', 'Open SQL Editor in the separate browser Tab'],
['sql_editor_shortcut_undo', 'Undo'],
['sql_editor_shortcut_redo', 'Redo'],
Expand Down
8 changes: 8 additions & 0 deletions webapp/packages/plugin-help/src/locales/ru.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export default [
['shortcuts_title', 'Горячие клавиши'],

Expand All @@ -14,6 +21,7 @@ export default [
['sql_editor_shortcut_execute_script', 'Выполнить скрипт'],
['sql_editor_shortcut_show_execution_plan', 'Показать план выполнения'],
['sql_editor_shortcut_format', 'Форматировать скрипт'],
['sql_editor_shortcut_save_as_script', 'Сохранить скрипт'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сохранить как скрипт

['sql_editor_shortcut_open_editor_in_new_tab', 'Открыть SQL редактор в новой бразуерной вкладке'],
['sql_editor_shortcut_undo', 'Отменить'],
['sql_editor_shortcut_redo', 'Повторить'],
Expand Down
1 change: 1 addition & 0 deletions webapp/packages/plugin-help/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default [
['sql_editor_shortcut_execute_script', '执行脚本'],
['sql_editor_shortcut_show_execution_plan', '显示执行计划'],
['sql_editor_shortcut_format', '格式化脚本'],
['sql_editor_shortcut_save_as_script', 'Save as script'],
['sql_editor_shortcut_open_editor_in_new_tab', '在单独的浏览器标签下打开SQL编辑器'],
['sql_editor_shortcut_undo', '撤销'],
['sql_editor_shortcut_redo', '重做'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createKeyBinding } from '@cloudbeaver/core-view';

export const KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT = createKeyBinding({
id: 'save-as-script',
keys: 'shift+mod+s',
preventDefault: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { isResourceOfType, ProjectInfoResource, ProjectsService } from '@cloudbe
import { CachedMapAllKey, CachedTreeChildrenKey } from '@cloudbeaver/core-resource';
import { getRmResourcePath, NAV_NODE_TYPE_RM_RESOURCE, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
import { createPath, getPathName } from '@cloudbeaver/core-utils';
import { ActionService, MenuService } from '@cloudbeaver/core-view';
import { ActionService, KeyBindingService, MenuService } from '@cloudbeaver/core-view';
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
import { getResourceKeyFromNodeId } from '@cloudbeaver/plugin-navigation-tree-rm';
import { RESOURCE_NAME_REGEX, ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
Expand All @@ -33,6 +33,7 @@ import {
import { isSQLEditorTab, SqlEditorNavigatorService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';

import { ACTION_SAVE_AS_SCRIPT } from './ACTION_SAVE_AS_SCRIPT.js';
import { KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT } from './KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT.js';
import { ResourceSqlDataSource } from './ResourceSqlDataSource.js';
import { SqlEditorTabResourceService } from './SqlEditorTabResourceService.js';

Expand All @@ -55,6 +56,7 @@ export class PluginBootstrap extends Bootstrap {
private readonly sqlEditorSettingsService: SqlEditorSettingsService,
private readonly resourceManagerResource: ResourceManagerResource,
private readonly resourceManagerScriptsService: ResourceManagerScriptsService,
private readonly keyBindingService: KeyBindingService,
) {
super();
}
Expand All @@ -78,105 +80,7 @@ export class PluginBootstrap extends Bootstrap {

return dataSource instanceof MemorySqlDataSource || dataSource instanceof LocalStorageSqlDataSource;
},
handler: async (context, action) => {
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!;

let dataSource: ISqlDataSource | ResourceSqlDataSource | undefined = this.sqlDataSourceService.get(state.editorId);

if (!dataSource) {
return;
}

if (action === ACTION_SAVE_AS_SCRIPT) {
let projectId = dataSource.executionContext?.projectId ?? null;
await this.projectInfoResource.load(CachedMapAllKey);
const name = getSqlEditorName(state, dataSource);

if (projectId) {
const project = this.projectInfoResource.get(projectId);

if (!project?.canEditResources) {
projectId = null;
}
}

const result = await this.commonDialogService.open(SaveScriptDialog, {
defaultScriptName: name,
projectId,
validation: async ({ name, projectId }, setMessage) => {
const trimmedName = name.trim();

if (!projectId || !trimmedName.length) {
return false;
}

if (!RESOURCE_NAME_REGEX.test(trimmedName)) {
setMessage('plugin_resource_manager_scripts_script_name_invalid_characters_message');
return false;
}

const project = this.projectInfoResource.get(projectId);
const nameWithExtension = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, trimmedName);
const rootFolder = project ? this.resourceManagerScriptsService.getRootFolder(project) : undefined;
const key = getRmResourcePath(projectId, rootFolder);

try {
await this.resourceManagerResource.load(CachedTreeChildrenKey(key));
return !this.resourceManagerResource.has(createPath(key, nameWithExtension));
} catch (exception: any) {
return false;
}
},
});

if (result !== DialogueStateResult.Rejected && result !== DialogueStateResult.Resolved) {
try {
projectId = result.projectId;

if (!projectId) {
throw new Error('Project not selected');
}

const project = this.projectInfoResource.get(projectId);
if (!project) {
throw new Error('Project not found');
}

const nameWithoutExtension = result.name.trim();
const scriptName = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, nameWithoutExtension);
const scriptsRootFolder = this.resourceManagerScriptsService.getRootFolder(project);
const folderResourceKey = getResourceKeyFromNodeId(createPath(RESOURCES_NODE_PATH, projectId, scriptsRootFolder));

if (!folderResourceKey) {
this.notificationService.logError({ title: 'ui_error', message: 'plugin_sql_editor_navigation_tab_resource_save_script_error' });
return;
}

const resourceKey = createPath(folderResourceKey, scriptName);

await this.resourceManagerScriptsService.createScript(resourceKey, dataSource.executionContext, dataSource.script);

dataSource = this.sqlDataSourceService.create(state, ResourceSqlDataSource.key, {
script: dataSource.script,
executionContext: dataSource.executionContext,
});

(dataSource as ResourceSqlDataSource).setResourceKey(resourceKey);

this.notificationService.logSuccess({
title: 'plugin_sql_editor_navigation_tab_resource_save_script_success',
message: nameWithoutExtension,
});

if (!this.resourceManagerScriptsService.active) {
this.resourceManagerScriptsService.togglePanel();
}
} catch (exception) {
this.notificationService.logException(exception as any, 'plugin_sql_editor_navigation_tab_resource_save_script_error');
}
}
}
},
handler: this.saveAsScriptHandler.bind(this),
getActionInfo: (context, action) => {
if (action === ACTION_SAVE_AS_SCRIPT) {
return {
Expand All @@ -189,6 +93,8 @@ export class PluginBootstrap extends Bootstrap {
},
});

this.navigationTabsService.registerAction(ACTION_SAVE_AS_SCRIPT);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use SqlEditorView instead


this.menuService.addCreator({
menus: [SQL_EDITOR_TOOLS_MENU],
contexts: [DATA_CONTEXT_SQL_EDITOR_STATE],
Expand All @@ -201,6 +107,115 @@ export class PluginBootstrap extends Bootstrap {
},
getItems: (context, items) => [...items, ACTION_SAVE_AS_SCRIPT],
});

this.keyBindingService.addKeyBindingHandler({
id: 'save-as-script',
binding: KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT,
actions: [ACTION_SAVE_AS_SCRIPT],
contexts: [DATA_CONTEXT_SQL_EDITOR_STATE],
isBindingApplicable: (_, action) => action === ACTION_SAVE_AS_SCRIPT,
handler: this.saveAsScriptHandler.bind(this),
});
}

private async saveAsScriptHandler(context: any, action: any) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add types to context arg. Also no need to check and pass action type as we already know it from a method name. Check it only in handler above

const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE)!;

let dataSource: ISqlDataSource | ResourceSqlDataSource | undefined = this.sqlDataSourceService.get(state.editorId);

if (!dataSource) {
return;
}

if (action === ACTION_SAVE_AS_SCRIPT) {
let projectId = dataSource.executionContext?.projectId ?? null;
await this.projectInfoResource.load(CachedMapAllKey);
const name = getSqlEditorName(state, dataSource);

if (projectId) {
const project = this.projectInfoResource.get(projectId);

if (!project?.canEditResources) {
projectId = null;
}
}

const result = await this.commonDialogService.open(SaveScriptDialog, {
defaultScriptName: name,
projectId,
validation: async ({ name, projectId }, setMessage) => {
const trimmedName = name.trim();

if (!projectId || !trimmedName.length) {
return false;
}

if (!RESOURCE_NAME_REGEX.test(trimmedName)) {
setMessage('plugin_resource_manager_scripts_script_name_invalid_characters_message');
return false;
}

const project = this.projectInfoResource.get(projectId);
const nameWithExtension = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, trimmedName);
const rootFolder = project ? this.resourceManagerScriptsService.getRootFolder(project) : undefined;
const key = getRmResourcePath(projectId, rootFolder);

try {
await this.resourceManagerResource.load(CachedTreeChildrenKey(key));
return !this.resourceManagerResource.has(createPath(key, nameWithExtension));
} catch (exception: any) {
return false;
}
},
});

if (result !== DialogueStateResult.Rejected && result !== DialogueStateResult.Resolved) {
try {
projectId = result.projectId;

if (!projectId) {
throw new Error('Project not selected');
}

const project = this.projectInfoResource.get(projectId);
if (!project) {
throw new Error('Project not found');
}

const nameWithoutExtension = result.name.trim();
const scriptName = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, nameWithoutExtension);
const scriptsRootFolder = this.resourceManagerScriptsService.getRootFolder(project);
const folderResourceKey = getResourceKeyFromNodeId(createPath(RESOURCES_NODE_PATH, projectId, scriptsRootFolder));

if (!folderResourceKey) {
this.notificationService.logError({ title: 'ui_error', message: 'plugin_sql_editor_navigation_tab_resource_save_script_error' });
return;
}

const resourceKey = createPath(folderResourceKey, scriptName);

await this.resourceManagerScriptsService.createScript(resourceKey, dataSource.executionContext, dataSource.script);

dataSource = this.sqlDataSourceService.create(state, ResourceSqlDataSource.key, {
script: dataSource.script,
executionContext: dataSource.executionContext,
});

(dataSource as ResourceSqlDataSource).setResourceKey(resourceKey);

this.notificationService.logSuccess({
title: 'plugin_sql_editor_navigation_tab_resource_save_script_success',
message: nameWithoutExtension,
});

if (!this.resourceManagerScriptsService.active) {
this.resourceManagerScriptsService.togglePanel();
}
} catch (exception) {
this.notificationService.logException(exception as any, 'plugin_sql_editor_navigation_tab_resource_save_script_error');
}
}
}
}

private canOpenHandler(data: INodeNavigationData, contexts: IExecutionContextProvider<INodeNavigationData>): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
*/
import { manifest } from './manifest.js';

export * from './KEY_BINDING_SQL_EDITOR_SAVE_AS_SCRIPT.js';

export default manifest;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
export default [
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script'],
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script (Shift + Ctrl + S)'],
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
export default [
['plugin_sql_editor_navigation_tab_resource_save_title', 'Enregistrer comme script'],
['plugin_sql_editor_navigation_tab_resource_save_title', 'Enregistrer comme script (Shift + Ctrl + S)'],
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renommer le script...'],
['plugin_sql_editor_navigation_tab_script_state_reading', 'Lecture du script...'],
['plugin_sql_editor_navigation_tab_script_state_saving', 'Enregistrement du script...'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
export default [
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script'],
['plugin_sql_editor_navigation_tab_resource_save_script_title', 'Save as script (Shift + Ctrl + S)'],
['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
Expand Down
Loading
Loading