Skip to content

Commit

Permalink
Refactor to avoid waiting for multiple R callbacks.
Browse files Browse the repository at this point in the history
  • Loading branch information
dfalbel committed Feb 21, 2024
1 parent 096f5d5 commit 0d15856
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 48 deletions.
109 changes: 63 additions & 46 deletions extensions/positron-connections/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ export class ConnectionItem {
readonly name: string,
readonly client: positron.RuntimeClientInstance) {
}

async icon(): Promise<string | vscode.Uri | { light: vscode.Uri; dark: vscode.Uri }> {
return '';
}

async contains_data(): Promise<boolean> {
return false;
}
}

/**
Expand Down Expand Up @@ -74,6 +82,26 @@ export class ConnectionItemNode extends ConnectionItem {
this.iconPath = this.kind;
return this.iconPath;
}

override async contains_data() {
if (this.containsData) {
return this.containsData;
}

const response = await this.client.performRpc({ msg_type: 'contains_data_request', path: this.path }) as any;
this.containsData = response.contains_data as boolean;

return this.containsData;
}

async preview() {
if (!this.contains_data()) {
// This should never happen, as no UI is provided to preview data when the item
// does not contain data.
throw new Error('This item does not contain data');
}
await this.client.performRpc({ msg_type: 'preview_table', table: this.name, path: this.path });
}
}

/**
Expand All @@ -89,18 +117,6 @@ export class ConnectionItemDatabase extends ConnectionItemNode {
}
}

/**
* A connection item representing a table in a database
*/
export class ConnectionItemTable extends ConnectionItemNode {
/**
* Preview the table's contents
*/
preview() {
this.client.performRpc({ msg_type: 'preview_table', table: this.name, path: this.path });
}
}

/**
* A connection item representing a field in a table
*/
Expand All @@ -109,6 +125,10 @@ export class ConnectionItemField extends ConnectionItem {
super(name, client);
this.dtype = dtype;
}

override async icon() {
return 'field';
}
}

/**
Expand Down Expand Up @@ -157,16 +177,15 @@ export class ConnectionItemsProvider implements vscode.TreeDataProvider<Connecti
vscode.TreeItemCollapsibleState.Collapsed :
vscode.TreeItemCollapsibleState.None);

if (item instanceof ConnectionItemTable) {
// Set the icon for tables
treeItem.iconPath = await this.getTreeItemIcon(item);
treeItem.iconPath = await this.getTreeItemIcon(item);

if (await item.contains_data()) {
// if the item contains data, we set the contextValue enabling the UI for previewing the data
treeItem.contextValue = 'table';
} else if (item instanceof ConnectionItemNode) {
// Set the icon for other levels in the hierarchy.
treeItem.iconPath = await this.getTreeItemIcon(item);
} else if (item instanceof ConnectionItemField) {
// Set the icon for fields
treeItem.iconPath = vscode.Uri.file(path.join(this.context.extensionPath, 'media', 'field.svg'));
}

if (item instanceof ConnectionItemField) {
// shows the field datatype as the treeItem description
treeItem.description = '<' + item.dtype + '>';
}

Expand All @@ -179,7 +198,7 @@ export class ConnectionItemsProvider implements vscode.TreeDataProvider<Connecti
return treeItem;
}

async getTreeItemIcon(item: ConnectionItemNode): Promise<vscode.Uri | { light: vscode.Uri; dark: vscode.Uri }> {
async getTreeItemIcon(item: ConnectionItem): Promise<vscode.Uri | { light: vscode.Uri; dark: vscode.Uri }> {
const icon = await item.icon();

if (typeof icon === 'string') {
Expand Down Expand Up @@ -223,36 +242,34 @@ export class ConnectionItemsProvider implements vscode.TreeDataProvider<Connecti
* @param element The element to get the children for
* @returns The children of the element
*/
async getChildren(element?: ConnectionItem): Promise<ConnectionItem[]> {
async getChildren(element?: ConnectionItemNode): Promise<ConnectionItem[]> {

if (!element) {
// At the root, return the top-level connections
return this._connections;
}

// Fields don't have children
if (element instanceof ConnectionItemField) {
return [];
}

if (element) {
if (element instanceof ConnectionItemTable) {
const response = await element.client.performRpc({ msg_type: 'fields_request', table: element.name, path: element.path }) as any;
const fields = response.fields as Array<{ name: string; dtype: string }>;
return fields.map((field) => {
return new ConnectionItemField(field.name, field.dtype, element.client);
});
} else if (element instanceof ConnectionItemNode) {
const response = await element.client.performRpc({ msg_type: 'tables_request', name: element.name, kind: element.kind, path: element.path }) as any;
const children = response.tables as Array<{ name: string; kind: string }>;
return await Promise.all(children.map(async (obj) => {
const path = [...element.path, { name: obj.name, kind: obj.kind }];
const response = await element.client.performRpc({ msg_type: 'contains_data_request', path: path }) as any;
const contains_data = response.contains_data as boolean;
if (contains_data) {
return new ConnectionItemTable(obj.name, obj.kind, path, element.client);
} else {
return new ConnectionItemNode(obj.name, obj.kind, path, element.client);
}
}));
}
// The node is a view or a table so we want to get the fields on it.
if (await element.contains_data()) {
const response = await element.client.performRpc({ msg_type: 'fields_request', table: element.name, path: element.path }) as any;
const fields = response.fields as Array<{ name: string; dtype: string }>;
return fields.map((field) => {
return new ConnectionItemField(field.name, field.dtype, element.client);
});
}

// At the root, return the top-level connections
return this._connections;
// The node is a database, schema, or catalog, so we want to get the next set of elements in
// the tree.
const response = await element.client.performRpc({ msg_type: 'tables_request', name: element.name, kind: element.kind, path: element.path }) as any;
const children = response.tables as Array<{ name: string; kind: string }>;
return children.map((obj) => {
const path = [...element.path, { name: obj.name, kind: obj.kind }];
return new ConnectionItemNode(obj.name, obj.kind, path, element.client);
});
}
}
4 changes: 2 additions & 2 deletions extensions/positron-connections/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as vscode from 'vscode';
import * as positron from 'positron';
import { ConnectionItemDatabase, ConnectionItemTable, ConnectionItemsProvider } from './connection';
import { ConnectionItemDatabase, ConnectionItemNode, ConnectionItemsProvider } from './connection';

/**
* Activates the extension.
Expand Down Expand Up @@ -32,7 +32,7 @@ export function activate(context: vscode.ExtensionContext) {
// Register a command to preview a table
context.subscriptions.push(
vscode.commands.registerCommand('positron.connections.previewTable',
(item: ConnectionItemTable) => {
(item: ConnectionItemNode) => {
item.preview();
}));

Expand Down

0 comments on commit 0d15856

Please sign in to comment.