Merge pull request #77 from dbeaver/feat-sql-editor-context

feat(sql-editor): open new editor for current context
This commit is contained in:
Serge Rider
2020-06-10 11:26:16 +03:00
committed by GitHub
8 changed files with 223 additions and 81 deletions
@@ -75,10 +75,16 @@ export function useNavigationTree(nodeId: string, parentId: string) {
}, [isExpanded, children.isLoaded, children.isLoading, children.children, nodeLoaded, nodeId]);
// Here we subscribe to selected nodes if current node selected (mobx)
if (isSelected && !navigationTreeService.selectedNodes.includes(nodeId)) {
if (isSelected && !navigationTreeService.isNodeSelected(nodeId)) {
switchSelect(false);
}
useEffect(() => () => {
if (navigationTreeService.isNodeSelected(nodeId)) {
navigationTreeService.selectNode(nodeId, true);
}
}, [navigationTreeService]);
return {
name,
node,
@@ -7,6 +7,7 @@
*/
import { observable } from 'mobx';
import { Subject, Observable } from 'rxjs';
import { injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
@@ -15,13 +16,18 @@ import { NavNodeManagerService, ROOT_NODE_PATH } from '../shared/NodesManager/Na
@injectable()
export class NavigationTreeService {
readonly selectedNodes = observable.array<string>([]);
readonly onNodeSelect: Observable<[string, boolean]>;
selectedNodes = observable.array<string>([]);
private nodeSelectSubject: Subject<[string, boolean]>;
constructor(
private NavNodeManagerService: NavNodeManagerService,
private notificationService: NotificationService
) { }
) {
this.nodeSelectSubject = new Subject();
this.onNodeSelect = this.nodeSelectSubject.asObservable();
}
async loadNestedNodes(id = ROOT_NODE_PATH) {
try {
@@ -35,13 +41,22 @@ export class NavigationTreeService {
selectNode(id: string, isMultiple?: boolean) {
if (!isMultiple) {
for (const id of this.selectedNodes) {
this.nodeSelectSubject.next([id, false]);
}
this.selectedNodes.clear();
}
if (!this.selectedNodes.includes(id)) {
if (!this.isNodeSelected(id)) {
this.selectedNodes.push(id);
this.nodeSelectSubject.next([id, true]);
return true;
}
this.selectedNodes.remove(id);
this.nodeSelectSubject.next([id, false]);
return false;
}
isNodeSelected(navNodeId: string) {
return this.selectedNodes.includes(navNodeId);
}
}
@@ -10,7 +10,9 @@ import { computed, observable } from 'mobx';
import { injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
import { IExtension } from '@dbeaver/core/extensions';
import { NavigationTreeService } from '../../NavigationTree/NavigationTreeService';
import { ConnectionsManagerService, ObjectContainer } from '../../shared/ConnectionsManager/ConnectionsManagerService';
import { isConnectionProvider, IConnectionProvider } from '../../shared/ConnectionsManager/extensions/IConnectionProvider';
import { isConnectionSetter, IConnectionSetter } from '../../shared/ConnectionsManager/extensions/IConnectionSetter';
@@ -20,34 +22,46 @@ import { IObjectCatalogProvider, isObjectCatalogProvider } from '../../shared/No
import { IObjectCatalogSetter, isObjectCatalogSetter } from '../../shared/NodesManager/extensions/IObjectCatalogSetter';
import { IObjectSchemaProvider, isObjectSchemaProvider } from '../../shared/NodesManager/extensions/IObjectSchemaProvider';
import { IObjectSchemaSetter, isObjectSchemaSetter } from '../../shared/NodesManager/extensions/IObjectSchemaSetter';
import { NavNodeExtensionsService } from '../../shared/NodesManager/NavNodeExtensionsService';
export interface IConnectionInfo {
name?: string;
driverIcon?: string;
}
interface IActiveItem<T> {
id: string;
context: T;
getCurrentConnectionId?: IConnectionProvider<T>;
getCurrentSchemaId?: IObjectSchemaProvider<T>;
getCurrentCatalogId?: IObjectCatalogProvider<T>;
changeConnectionId?: IConnectionSetter<T>;
changeCatalogId?: IObjectCatalogSetter<T>;
changeSchemaId?: IObjectSchemaSetter<T>;
}
@injectable()
export class ConnectionSchemaManagerService {
get currentConnectionId(): string | undefined {
if (!this.getCurrentConnectionId || !this.currentTab) {
if (!this.activeItem?.getCurrentConnectionId) {
return;
}
return this.getCurrentConnectionId(this.currentTab);
return this.activeItem.getCurrentConnectionId(this.activeItem.context);
}
get currentObjectCatalogId(): string | undefined {
if (!this.getCurrentCatalogId || !this.currentTab) {
if (!this.activeItem?.getCurrentCatalogId) {
return;
}
return this.getCurrentCatalogId(this.currentTab);
return this.activeItem.getCurrentCatalogId(this.activeItem.context);
}
get currentObjectSchemaId(): string | undefined {
if (!this.getCurrentSchemaId || !this.currentTab) {
if (!this.activeItem?.getCurrentSchemaId) {
return;
}
return this.getCurrentSchemaId(this.currentTab);
return this.activeItem.getCurrentSchemaId(this.activeItem.context);
}
@computed get currentObjectCatalog(): ObjectContainer | undefined {
@@ -82,31 +96,30 @@ export class ConnectionSchemaManagerService {
}
get isConnectionChangeable(): boolean {
return !!this.changeConnectionId
return !!this.activeItem?.changeConnectionId
&& !this.connectionsManagerService.connectionObjectContainers.isLoading();
}
get isObjectCatalogChangeable(): boolean {
return !!this.changeCatalogId
return !!this.activeItem?.changeCatalogId
&& !this.connectionsManagerService.connectionObjectContainers.isLoading();
}
get isObjectSchemaChangeable(): boolean {
return !!this.changeSchemaId
return !!this.activeItem?.changeSchemaId
&& !this.connectionsManagerService.connectionObjectContainers.isLoading();
}
@observable private currentTab: ITab | null = null;
@observable private getCurrentConnectionId: IConnectionProvider<ITab> | null = null;
@observable private getCurrentSchemaId: IObjectSchemaProvider<ITab> | null = null;
@observable private getCurrentCatalogId: IObjectCatalogProvider<ITab> | null = null;
@observable private changeConnectionId: IConnectionSetter<ITab> | null = null;
@observable private changeCatalogId: IObjectCatalogSetter<ITab> | null = null;
@observable private changeSchemaId: IObjectSchemaSetter<ITab> | null = null;
@observable private activeItem: IActiveItem<any> | null = null;
@observable private activeItemHistory: IActiveItem<any>[] = [];
constructor(private navigationTabsService: NavigationTabsService,
private connectionsManagerService: ConnectionsManagerService,
private notificationService: NotificationService) {
constructor(
private navigationTabsService: NavigationTabsService,
private connectionsManagerService: ConnectionsManagerService,
private notificationService: NotificationService,
private navigationTreeService: NavigationTreeService,
private navNodeExtensionsService: NavNodeExtensionsService,
) {
}
@@ -116,16 +129,19 @@ export class ConnectionSchemaManagerService {
this.navigationTabsService.onTabClose
.subscribe(this.onTabClose.bind(this));
this.navigationTreeService.onNodeSelect
.subscribe(this.onNodeSelect.bind(this));
}
/**
* Trigger when user select connection in dropdown
*/
async selectConnection(connectionId: string) {
if (!this.changeConnectionId || !this.currentTab) {
if (!this.activeItem?.changeConnectionId) {
return;
}
this.changeConnectionId(connectionId, this.currentTab);
this.activeItem.changeConnectionId(connectionId, this.activeItem);
this.loadConnection(connectionId);
}
@@ -133,10 +149,10 @@ export class ConnectionSchemaManagerService {
* Trigger when user select catalog in dropdown
*/
selectCatalog(catalogId: string) {
if (!this.changeCatalogId || !this.currentTab) {
if (!this.activeItem?.changeCatalogId) {
throw new Error('The try to change catalog without connection');
}
this.changeCatalogId(catalogId, this.currentTab);
this.activeItem.changeCatalogId(catalogId, this.activeItem);
this.loadConnection(this.currentConnectionId!, catalogId);
}
@@ -144,60 +160,91 @@ export class ConnectionSchemaManagerService {
* Trigger when user select schema in dropdown
*/
selectSchema(schemaId: string) {
if (!this.changeSchemaId || !this.currentTab) {
if (!this.activeItem?.changeSchemaId) {
throw new Error('The try to change schema without connection');
}
this.changeSchemaId(schemaId, this.currentTab);
this.activeItem.changeSchemaId(schemaId, this.activeItem);
}
private onNodeSelect([navNodeId, selected]: [string, boolean]) {
if (selected) {
const item: IActiveItem<string> = {
id: navNodeId,
context: navNodeId,
};
this.setExtensions(item, this.navNodeExtensionsService.extensions);
this.setActiveItem(item);
} else {
this.removeActiveItem(navNodeId);
}
}
private onTabSelect(tab: ITab) {
this.clear();
this.currentTab = tab;
const item: IActiveItem<ITab> = {
id: tab.id,
context: tab,
};
const handler = this.navigationTabsService.getTabHandler(tab.handlerId);
if (handler && handler.extensions) {
for (const extension of handler.extensions) {
if (isConnectionProvider(extension)) {
this.getCurrentConnectionId = extension;
}
if (isObjectCatalogProvider(extension)) {
this.getCurrentCatalogId = extension;
}
if (isObjectSchemaProvider(extension)) {
this.getCurrentSchemaId = extension;
}
this.setExtensions(item, handler.extensions);
}
if (isConnectionSetter(extension)) {
this.changeConnectionId = extension;
}
if (isObjectCatalogSetter(extension)) {
this.changeCatalogId = extension;
}
if (isObjectSchemaSetter(extension)) {
this.changeSchemaId = extension;
}
this.setActiveItem(item);
}
private onTabClose(tab: ITab) {
this.removeActiveItem(tab.id);
}
private setExtensions<T>(item: IActiveItem<T>, extensions: IExtension<T>[]) {
for (const extension of extensions) {
if (isConnectionProvider(extension)) {
item.getCurrentConnectionId = extension;
}
if (isObjectCatalogProvider(extension)) {
item.getCurrentCatalogId = extension;
}
if (isObjectSchemaProvider(extension)) {
item.getCurrentSchemaId = extension;
}
if (isConnectionSetter(extension)) {
item.changeConnectionId = extension;
}
if (isObjectCatalogSetter(extension)) {
item.changeCatalogId = extension;
}
if (isObjectSchemaSetter(extension)) {
item.changeSchemaId = extension;
}
}
}
private removeActiveItem(id: string) {
this.clearHistory(id);
if (id === this.activeItem?.id) {
this.setActiveItem(this.activeItemHistory[0] || null);
}
}
private setActiveItem(item: IActiveItem<any> | null) {
if (!item) {
this.activeItem = item;
return;
}
this.clearHistory(item.id);
this.activeItem = item;
this.activeItemHistory.push(item);
if (this.currentConnectionId) {
this.loadConnection(this.currentConnectionId, this.currentObjectCatalogId);
}
}
private onTabClose(tab: ITab) {
if (tab.id === this.currentTab?.id) {
this.clear();
}
}
private clear() {
this.getCurrentConnectionId = null;
this.getCurrentCatalogId = null;
this.getCurrentSchemaId = null;
this.changeConnectionId = null;
this.changeCatalogId = null;
this.changeSchemaId = null;
this.currentTab = null;
private clearHistory(id: string) {
this.activeItemHistory = this.activeItemHistory.filter(item => item.id !== id);
}
private async loadConnection(connectionId: string, catalogId?: string) {
@@ -207,7 +254,7 @@ export class ConnectionSchemaManagerService {
this.notificationService.logException(exception, 'Can\'t load database drivers', true);
}
if (this.changeCatalogId || this.changeSchemaId) {
if (this.activeItem?.changeCatalogId || this.activeItem?.changeSchemaId) {
try {
await this.connectionsManagerService.loadObjectContainer(connectionId, catalogId);
} catch (exception) {
+1
View File
@@ -17,6 +17,7 @@ export * from './shared/NodesManager/extensions/IObjectSchemaProvider';
export * from './shared/NodesManager/extensions/IObjectSchemaSetter';
export * from './shared/NodesManager/DBObjectService';
export * from './shared/NodesManager/NavNodeManagerService';
export * from './shared/NodesManager/NavNodeExtensionsService';
export * from './shared/NodesManager/NodeManagerUtils';
export * from './shared/Navigation/NavigationService';
export * from './shared/NavigationTabs/NavigationTabsService';
@@ -0,0 +1,56 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 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 { injectable } from '@dbeaver/core/di';
import { IExtension } from '@dbeaver/core/extensions';
import { connectionProvider } from '../ConnectionsManager/extensions/IConnectionProvider';
import { objectCatalogProvider } from './extensions/IObjectCatalogProvider';
import { objectSchemaProvider } from './extensions/IObjectSchemaProvider';
import { NavNodeManagerService } from './NavNodeManagerService';
import { NodeManagerUtils } from './NodeManagerUtils';
@injectable()
export class NavNodeExtensionsService {
readonly extensions: IExtension<string>[];
constructor(
private navNodeManagerService: NavNodeManagerService,
) {
this.extensions = [
connectionProvider(this.getConnection.bind(this)),
objectCatalogProvider(this.getDBObjectCatalog.bind(this)),
objectSchemaProvider(this.getDBObjectSchema.bind(this)),
];
}
private getConnection(context: string) {
const nodeInfo = this.navNodeManagerService
.getNodeContainerInfo(context);
if (!nodeInfo.connectionId) {
return;
}
// connection node id differs from connection id
return NodeManagerUtils.connectionNodeIdToConnectionId(nodeInfo.connectionId);
}
private getDBObjectCatalog(context: string) {
const nodeInfo = this.navNodeManagerService
.getNodeContainerInfo(context);
return nodeInfo.catalogId;
}
private getDBObjectSchema(context: string) {
const nodeInfo = this.navNodeManagerService
.getNodeContainerInfo(context);
return nodeInfo.schemaId;
}
}
+3 -1
View File
@@ -8,7 +8,8 @@
import {
ConnectionDialogsService, ConnectionsManagerService,
MainMenuService, NavigationService, NavigationTreeService, NavNodeManagerService, DBObjectService,
MainMenuService, NavigationService, NavigationTreeService,
NavNodeManagerService, DBObjectService, NavNodeExtensionsService,
ConnectionSchemaManagerService, NavigationTabsService,
NavigationTreeContextMenuService,
SettingsMenuService, LogViewerService, LogViewerMenuService, TopNavService,
@@ -65,6 +66,7 @@ export const coreManifest: PluginManifest = {
NavigationTreeContextMenuService,
NavigationTreeService,
NavNodeManagerService,
NavNodeExtensionsService,
DBObjectService,
NotificationService,
SessionService,
@@ -11,7 +11,7 @@ import {
NavigationTreeContextMenuService,
EObjectFeature,
NodeManagerUtils,
NavNode, ConnectionsManagerService,
NavNode, ConnectionsManagerService, ConnectionSchemaManagerService,
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { ContextMenuService, IContextMenuItem, IMenuContext } from '@dbeaver/core/dialogs';
@@ -26,7 +26,8 @@ export class SqlEditorBootstrap {
private contextMenuService: ContextMenuService,
private connectionsManagerService: ConnectionsManagerService,
private sqlEditorTabService: SqlEditorTabService,
private sqlEditorNavigatorService: SqlEditorNavigatorService
private sqlEditorNavigatorService: SqlEditorNavigatorService,
private connectionSchemaManagerService: ConnectionSchemaManagerService,
) {}
async bootstrap() {
@@ -37,7 +38,18 @@ export class SqlEditorBootstrap {
id: 'sql-editor',
title: 'SQL',
order: 2,
onClick: () => this.sqlEditorNavigatorService.openNewEditor(),
onClick: () => {
console.log(
this.connectionSchemaManagerService.currentConnectionId,
this.connectionSchemaManagerService.currentObjectCatalogId,
this.connectionSchemaManagerService.currentObjectSchemaId
);
this.sqlEditorNavigatorService.openNewEditor(
this.connectionSchemaManagerService.currentConnectionId,
this.connectionSchemaManagerService.currentObjectCatalogId,
this.connectionSchemaManagerService.currentObjectSchemaId,
);
},
isDisabled: () => !this.connectionsManagerService.hasAnyConnection(),
}
);
@@ -77,10 +77,12 @@ export class SqlEditorNavigatorService {
registerTabHandler() {
}
openNewEditor(connectionId?: string) {
openNewEditor(connectionId?: string, catalogId?: string, schemaId?: string) {
this.navigator.navigateTo({
type: SQLEditorNavigationAction.create,
connectionId,
catalogId,
schemaId,
});
}
@@ -125,7 +127,7 @@ export class SqlEditorNavigatorService {
const tabInfo = await contexts.getContext(this.navigationTabsService.navigationTabContext);
if (data.type === SQLEditorNavigationAction.create) {
const tabOptions = await this.createNewEditor(data.connectionId);
const tabOptions = await this.createNewEditor(data.connectionId, data.catalogId, data.schemaId);
if (tabOptions) {
tabInfo.openNewTab(tabOptions);
} else {
@@ -159,19 +161,20 @@ export class SqlEditorNavigatorService {
}
}
private async createNewEditor(connectionId?: string): Promise<ITabOptions<ISqlEditorTabState> | null> {
private async createNewEditor(
connectionId?: string,
catalogId?: string,
schemaId?: string
): Promise<ITabOptions<ISqlEditorTabState> | null> {
const order = this.getFreeEditorId();
const connection = connectionId
? this.connectionsManagerService.getConnectionById(connectionId)
: this.connectionsManagerService.connections[0];
if (!connection) {
return null;
if (!connectionId) {
connectionId = this.connectionsManagerService.connections[0].id;
}
await this.sqlDialectInfoService.loadSqlDialectInfo(connection.id);
const context = await this.createSqlContext(connection.id);
await this.sqlDialectInfoService.loadSqlDialectInfo(connectionId);
const context = await this.createSqlContext(connectionId, catalogId, schemaId);
return {
handlerId: sqlEditorTabHandlerKey,
@@ -179,7 +182,7 @@ export class SqlEditorNavigatorService {
query: '',
order,
contextId: context.contextId,
connectionId: connection.id,
connectionId,
objectCatalogId: context.objectCatalogId,
objectSchemaId: context.objectSchemaId,
sqlExecutionState: new SqlExecutionState(),