Skip to content

Commit

Permalink
Show variables associated with connections (#116-2)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmingles committed Sep 6, 2024
1 parent 8e6f8ee commit 4cb81d0
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 46 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@
"id": "vscode-deephaven.serverConnectionTree",
"name": "Connections",
"type": "tree"
},
{
"id": "vscode-deephaven.serverConnectionPanelTree",
"name": "Panels",
"type": "tree"
}
]
}
Expand Down
26 changes: 25 additions & 1 deletion src/common/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'node:path';
import type { ConsoleType, Port } from '../types';
import type { ConsoleType, Port, VariableType } from '../types';

export const EXTENSION_ID = 'vscode-deephaven' as const;

Expand Down Expand Up @@ -48,6 +48,7 @@ export const TMP_DIR_ROOT = path.join(__dirname, '..', 'tmp');
export const VIEW_ID = {
serverTree: `${EXTENSION_ID}.serverTree`,
serverConnectionTree: `${EXTENSION_ID}.serverConnectionTree`,
serverConnectionPanelTree: `${EXTENSION_ID}.serverConnectionPanelTree`,
} as const;

export const ICON_ID = {
Expand All @@ -61,8 +62,31 @@ export const ICON_ID = {
serverConnected: 'circle-large-filled',
serverRunning: 'circle-large-outline',
serverStopped: 'circle-slash',
table: 'table',
varFigure: 'graph-line',
varExpFigure: 'pie-chart',
varTable: 'preview',
varElement: 'extensions',
} as const;

/* eslint-disable @typescript-eslint/naming-convention */
export const VARIABLE_UNICODE_ICONS = {
'deephaven.plot.express.DeephavenFigure': '📈',
'deephaven.ui.Element': '✨',
Figure: '📈',
Table: '⬜',
} as const satisfies Record<VariableType, string>;
/* eslint-enable @typescript-eslint/naming-convention */

/* eslint-disable @typescript-eslint/naming-convention */
export const VARIABLE_ICONS = {
'deephaven.plot.express.DeephavenFigure': ICON_ID.varExpFigure,
'deephaven.ui.Element': ICON_ID.varElement,
Figure: ICON_ID.varFigure,
Table: ICON_ID.varTable,
} as const satisfies Record<VariableType, string>;
/* eslint-enable @typescript-eslint/naming-convention */

export const CONNECTION_TREE_ITEM_CONTEXT = {
isConnection: 'isConnection',
isUri: 'isUri',
Expand Down
29 changes: 27 additions & 2 deletions src/controllers/ExtensionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
RunCommandCodeLensProvider,
ServerTreeProvider,
ServerConnectionTreeProvider,
ServerConnectionPanelTreeProvider,
runSelectedLinesHoverProvider,
} from '../providers';
import { DhcServiceFactory, PanelService, ServerManager } from '../services';
Expand All @@ -40,6 +41,7 @@ import type {
IPanelService,
IServerManager,
IToastService,
ServerConnectionPanelTreeView,
ServerConnectionTreeView,
ServerState,
ServerTreeView,
Expand Down Expand Up @@ -84,11 +86,19 @@ export class ExtensionController implements Disposable {
private _pipServerController: PipServerController | null = null;
private _dhcServiceFactory: IDhServiceFactory | null = null;
private _serverManager: IServerManager | null = null;

// Tree providers
private _serverTreeProvider: ServerTreeProvider | null = null;
private _serverConnectionTreeProvider: ServerConnectionTreeProvider | null =
null;
private _serverConnectionPanelTreeProvider: ServerConnectionPanelTreeProvider | null =
null;

// Tree views
private _serverTreeView: ServerTreeView | null = null;
private _serverConnectionTreeView: ServerConnectionTreeView | null = null;
private _serverConnectionPanelTreeView: ServerConnectionPanelTreeView | null =
null;

private _pythonDiagnostics: vscode.DiagnosticCollection | null = null;
private _outputChannel: vscode.OutputChannel | null = null;
Expand Down Expand Up @@ -327,6 +337,7 @@ export class ExtensionController implements Disposable {
* Register web views for the extension.
*/
initializeWebViews = (): void => {
assertDefined(this._panelService, 'panelService');
assertDefined(this._serverManager, 'serverManager');

// Server tree
Expand All @@ -352,10 +363,24 @@ export class ExtensionController implements Disposable {
}
);

// Connection Panel tree
this._serverConnectionPanelTreeProvider =
new ServerConnectionPanelTreeProvider(
this._serverManager,
this._panelService
);
this._serverConnectionPanelTreeView = vscode.window.createTreeView(
VIEW_ID.serverConnectionPanelTree,
{
showCollapseAll: true,
treeDataProvider: this._serverConnectionPanelTreeProvider,
}
);

this._context.subscriptions.push(
this._serverManager,
this._serverTreeView,
this._serverConnectionTreeView
this._serverConnectionTreeView,
this._serverConnectionPanelTreeView
);
};

Expand Down
50 changes: 50 additions & 0 deletions src/providers/ServerConnectionPanelTreeProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as vscode from 'vscode';
import type {
IDhService,
IPanelService,
IServerManager,
ServerConnectionPanelNode,
} from '../types';
import { TreeDataProviderBase } from './TreeDataProviderBase';
import {
getPanelConnectionTreeItem,
getPanelVariableTreeItem,
sortByStringProp,
} from '../util';

export class ServerConnectionPanelTreeProvider extends TreeDataProviderBase<ServerConnectionPanelNode> {
constructor(serverManager: IServerManager, panelService: IPanelService) {
super(serverManager);
this._panelService = panelService;

this._panelService.onDidUpdate(() => {
this._onDidChangeTreeData.fire();
});
}

private readonly _panelService: IPanelService;

getTreeItem = async (
connectionOrVariable: ServerConnectionPanelNode
): Promise<vscode.TreeItem> => {
if ('id' in connectionOrVariable) {
return getPanelVariableTreeItem(connectionOrVariable);
}

return getPanelConnectionTreeItem(connectionOrVariable);
};

getChildren = (
connectionOrRoot?: IDhService
): vscode.ProviderResult<ServerConnectionPanelNode[]> => {
if (connectionOrRoot == null) {
return this.serverManager
.getConnections()
.sort(sortByStringProp('serverUrl'));
}

return [
...this._panelService.getVariables(connectionOrRoot.serverUrl),
].sort((a, b) => a.title.localeCompare(b.title));
};
}
13 changes: 6 additions & 7 deletions src/providers/ServerConnectionTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import * as vscode from 'vscode';
import { TreeDataProviderBase } from './TreeDataProviderBase';
import { CONNECTION_TREE_ITEM_CONTEXT, ICON_ID } from '../common';
import type { IDhService, ServerConnectionNode } from '../types';
import { sortByStringProp } from '../util';

/**
* Provider for the server connection tree view.
*/
export class ServerConnectionTreeProvider extends TreeDataProviderBase<ServerConnectionNode> {
async getTreeItem(
getTreeItem = async (
connectionOrUri: ServerConnectionNode
): Promise<vscode.TreeItem> {
): Promise<vscode.TreeItem> => {
// Uri node associated with a parent connection node
if (connectionOrUri instanceof vscode.Uri) {
return {
Expand Down Expand Up @@ -41,17 +42,15 @@ export class ServerConnectionTreeProvider extends TreeDataProviderBase<ServerCon
connectionOrUri.isConnected ? ICON_ID.connected : ICON_ID.connecting
),
};
}
};

getChildren = (
elementOrRoot?: IDhService
): vscode.ProviderResult<IDhService[] | vscode.Uri[]> => {
): vscode.ProviderResult<ServerConnectionNode[]> => {
if (elementOrRoot == null) {
return this.serverManager
.getConnections()
.sort((a, b) =>
a.serverUrl.toString().localeCompare(b.serverUrl.toString())
);
.sort(sortByStringProp('serverUrl'));
}

return this.serverManager.getConnectionUris(elementOrRoot);
Expand Down
6 changes: 2 additions & 4 deletions src/providers/ServerTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ function isServerGroupState(node: ServerNode): node is ServerGroupState {
* Provider for the server tree view.
*/
export class ServerTreeProvider extends TreeDataProviderBase<ServerNode> {
getTreeItem(
element: ServerNode
): vscode.TreeItem | Thenable<vscode.TreeItem> {
getTreeItem = (element: ServerNode): vscode.TreeItem => {
if (isServerGroupState(element)) {
return getServerGroupTreeItem(element, this.serverManager.canStartServer);
}
Expand All @@ -33,7 +31,7 @@ export class ServerTreeProvider extends TreeDataProviderBase<ServerNode> {
isManaged,
isRunning,
});
}
};

getChildren(elementOrRoot?: ServerNode): vscode.ProviderResult<ServerNode[]> {
const { managed, running, stopped } = groupServers(
Expand Down
10 changes: 7 additions & 3 deletions src/providers/TreeDataProviderBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import type { IServerManager } from '../types';
export abstract class TreeDataProviderBase<T>
implements vscode.TreeDataProvider<T>
{
constructor(readonly serverManager: IServerManager) {
serverManager.onDidUpdate(() => {
constructor(serverManager: IServerManager) {
this.serverManager = serverManager;

this.serverManager.onDidUpdate(() => {
this._onDidChangeTreeData.fire();
});
}

private readonly _onDidChangeTreeData = new vscode.EventEmitter<
protected readonly serverManager: IServerManager;

protected readonly _onDidChangeTreeData = new vscode.EventEmitter<
T | undefined | void
>();

Expand Down
1 change: 1 addition & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './RunCommandCodeLensProvider';
export * from './RunSelectedLinesHoverProvider';
export * from './ServerConnectionTreeProvider';
export * from './ServerTreeProvider';
export * from './ServerConnectionPanelTreeProvider';
export * from './TreeDataProviderBase';
32 changes: 17 additions & 15 deletions src/services/DhService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,21 @@ import type {
IDhService,
IPanelService,
IToastService,
VariableChanges,
VariableDefintion,
} from '../types';
import {
assertIsVariableID,
formatTimestamp,
getCombinedSelectedLinesText,
isAggregateError,
Logger,
NoConsoleTypesError,
parseServerError,
} from '../util';
import { VARIABLE_UNICODE_ICONS } from '../common';

const logger = new Logger('DhService');

/* eslint-disable @typescript-eslint/naming-convention */
const icons = {
Figure: '📈',
'deephaven.plot.express.DeephavenFigure': '📈',
Table: '⬜',
'deephaven.ui.Element': '✨',
} as const;
type IconType = keyof typeof icons;
/* eslint-enable @typescript-eslint/naming-convention */

export abstract class DhService<TDH = unknown, TClient = unknown>
implements IDhService<TDH, TClient>
{
Expand Down Expand Up @@ -168,6 +160,13 @@ export abstract class DhService<TDH = unknown, TClient = unknown>
try {
const { cn, session } = await this.cachedCreateSession;

cn.subscribeToFieldUpdates(changes => {
this.panelService.updateVariables(
this.serverUrl,
changes as VariableChanges
);
});

// TODO: Use constant 'disconnect' event name
this.subscriptions.push(
cn.addEventListener('disconnect', () => {
Expand Down Expand Up @@ -303,16 +302,19 @@ export abstract class DhService<TDH = unknown, TClient = unknown>
return;
}

const changed = [...result!.changes.created, ...result!.changes.updated];
const changed = [
...result!.changes.created,
...result!.changes.updated,
] as VariableDefintion[];

// Have to set this with type assertion since TypeScript can't figure out
// assignments inside of the `forEach` and will treat `lastPanel` as `null`.
let lastPanel = null as vscode.WebviewPanel | null;

changed.forEach(({ id, title = 'Unknown', type }) => {
assertIsVariableID(id, 'id');
changed.forEach(variable => {
const { id, title, type } = variable;

const icon = icons[type as IconType] ?? type;
const icon = VARIABLE_UNICODE_ICONS[type] ?? type;
this.outputChannel.appendLine(`${icon} ${title}`);

// Don't show panels for variables starting with '_'
Expand Down
Loading

0 comments on commit 4cb81d0

Please sign in to comment.