diff --git a/docs/changelog.md b/docs/changelog.md index 6074fa94..5f00d455 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,7 +1,10 @@ ## Unreleased -- Add `sema4ai.dropDataSource` command -- Add Datasources to the UI +- Add Data Sources to the Packages tree + - Hover (tree item) action: remove Data Source + - Hover (tree item) action: setup Data Source + - Hover (tree item) action: open Data Source definition location + - Tooltip showing Data Source information - Add `sema4ai.getActionsMetadata` command - Add `query` and `predict` actions to UI - Update Action Server to `2.3.0`. diff --git a/sema4ai/codegen/commands.py b/sema4ai/codegen/commands.py index b502463e..2e6a0003 100644 --- a/sema4ai/codegen/commands.py +++ b/sema4ai/codegen/commands.py @@ -1021,7 +1021,9 @@ def __init__( ), Command( "sema4ai.dropDataSource", - "Drop Data Sources", + # Note: changed from Drop to Remove so that it appears after the open in the tree + # (the order is defined by the name). + "Remove Data Source", server_handled=False, add_to_package_json=True, icon="$(trash)", @@ -1030,9 +1032,16 @@ def __init__( "sema4ai.setupDataSource", "Setup Data Source", server_handled=False, - hide_from_command_palette=False, + hide_from_command_palette=True, icon="$(diff-added)", ), + Command( + "sema4ai.openDataSourceDefinition", + "Open Data Source definition", + server_handled=False, + hide_from_command_palette=True, + icon="$(go-to-file)", + ), ] diff --git a/sema4ai/codegen/views.py b/sema4ai/codegen/views.py index f243e6cb..4d07e23b 100644 --- a/sema4ai/codegen/views.py +++ b/sema4ai/codegen/views.py @@ -149,6 +149,16 @@ def __init__( MenuGroup.NAVIGATION, "viewItem == actionItem", ), + Menu( + "sema4ai.openDataSourceDefinition", + MenuGroup.INLINE, + when="viewItem == datasourceItem", + ), + Menu( + "sema4ai.setupDataSource", + MenuGroup.INLINE, + when="viewItem == datasourceItem", + ), Menu( "sema4ai.dropDataSource", MenuGroup.INLINE, @@ -160,11 +170,6 @@ def __init__( MenuGroup.NAVIGATION, when="viewItem == robotItem", ), - Menu( - "sema4ai.setupDataSource", - MenuGroup.INLINE, - when="viewItem == datasourceItem", - ), Menu( "sema4ai.openRobotTreeSelection", MenuGroup.NAVIGATION, diff --git a/sema4ai/package.json b/sema4ai/package.json index 9e69eb4e..d7843978 100644 --- a/sema4ai/package.json +++ b/sema4ai/package.json @@ -176,6 +176,7 @@ "onCommand:sema4ai.getActionsMetadata", "onCommand:sema4ai.dropDataSource", "onCommand:sema4ai.setupDataSource", + "onCommand:sema4ai.openDataSourceDefinition", "onDebugInitialConfigurations", "onDebugResolve:sema4ai", "onView:sema4ai-task-packages-tree", @@ -1011,7 +1012,7 @@ }, { "command": "sema4ai.dropDataSource", - "title": "Drop Data Sources", + "title": "Remove Data Source", "category": "Sema4.ai", "icon": "$(trash)" }, @@ -1020,6 +1021,12 @@ "title": "Setup Data Source", "category": "Sema4.ai", "icon": "$(diff-added)" + }, + { + "command": "sema4ai.openDataSourceDefinition", + "title": "Open Data Source definition", + "category": "Sema4.ai", + "icon": "$(go-to-file)" } ], "menus": { @@ -1142,20 +1149,25 @@ "group": "navigation" }, { - "command": "sema4ai.dropDataSource", + "command": "sema4ai.openDataSourceDefinition", "when": "view == sema4ai-task-packages-tree && viewItem == datasourceItem", "group": "inline" }, { - "command": "sema4ai.revealRobotInExplorer", - "when": "view == sema4ai-task-packages-tree && viewItem == robotItem", - "group": "navigation" + "command": "sema4ai.setupDataSource", + "when": "view == sema4ai-task-packages-tree && viewItem == datasourceItem", + "group": "inline" }, { - "command": "sema4ai.setupDataSource", + "command": "sema4ai.dropDataSource", "when": "view == sema4ai-task-packages-tree && viewItem == datasourceItem", "group": "inline" }, + { + "command": "sema4ai.revealRobotInExplorer", + "when": "view == sema4ai-task-packages-tree && viewItem == robotItem", + "group": "navigation" + }, { "command": "sema4ai.openRobotTreeSelection", "when": "view == sema4ai-task-packages-tree && viewItem == robotItem", @@ -1562,6 +1574,14 @@ { "command": "sema4ai.getActionsMetadata", "when": "false" + }, + { + "command": "sema4ai.setupDataSource", + "when": "false" + }, + { + "command": "sema4ai.openDataSourceDefinition", + "when": "false" } ] } diff --git a/sema4ai/src/sema4ai_code/commands.py b/sema4ai/src/sema4ai_code/commands.py index 2fd8eadc..1f57a856 100644 --- a/sema4ai/src/sema4ai_code/commands.py +++ b/sema4ai/src/sema4ai_code/commands.py @@ -147,8 +147,9 @@ SEMA4AI_IMPORT_ACTION_PACKAGE = "sema4ai.importActionPackage" # Import Action Package SEMA4AI_RUN_ACTION_PACKAGE_DEV_TASK = "sema4ai.runActionPackageDevTask" # Run dev-task (from Action Package) SEMA4AI_GET_ACTIONS_METADATA = "sema4ai.getActionsMetadata" # Get Actions Metadata -SEMA4AI_DROP_DATA_SOURCE = "sema4ai.dropDataSource" # Drop Data Sources +SEMA4AI_DROP_DATA_SOURCE = "sema4ai.dropDataSource" # Remove Data Source SEMA4AI_SETUP_DATA_SOURCE = "sema4ai.setupDataSource" # Setup Data Source +SEMA4AI_OPEN_DATA_SOURCE_DEFINITION = "sema4ai.openDataSourceDefinition" # Open Data Source definition ALL_SERVER_COMMANDS = [ SEMA4AI_GET_PLUGINS_DIR, diff --git a/sema4ai/src/sema4ai_code/robocorp_language_server.py b/sema4ai/src/sema4ai_code/robocorp_language_server.py index 77b50440..6e2af2bc 100644 --- a/sema4ai/src/sema4ai_code/robocorp_language_server.py +++ b/sema4ai/src/sema4ai_code/robocorp_language_server.py @@ -21,8 +21,6 @@ TextDocumentCodeActionTypedDict, ) from sema4ai_ls_core.protocols import ( - ActionInfoTypedDict, - DatasourceInfoTypedDict, DataSourceStateDict, IConfig, IMonitor, diff --git a/sema4ai/vscode-client/src/extension.ts b/sema4ai/vscode-client/src/extension.ts index ab8f4ff2..72c18e47 100644 --- a/sema4ai/vscode-client/src/extension.ts +++ b/sema4ai/vscode-client/src/extension.ts @@ -177,6 +177,7 @@ import { SEMA4AI_DROP_DATA_SOURCE, SEMA4AI_LIST_ACTIONS_INTERNAL, SEMA4AI_SETUP_DATA_SOURCE, + SEMA4AI_OPEN_DATA_SOURCE_DEFINITION, } from "./robocorpCommands"; import { installWorkspaceWatcher } from "./pythonExtIntegration"; import { refreshCloudTreeView } from "./viewsRobocorp"; @@ -550,6 +551,9 @@ function registerRobocorpCodeCommands(C: CommandRegistry, context: ExtensionCont ); C.register(SEMA4AI_GET_ACTIONS_METADATA, getActionsMetadata); C.register(SEMA4AI_DROP_DATA_SOURCE, async (datasource?: RobotEntry) => dropDataSource(datasource)); + C.register(SEMA4AI_OPEN_DATA_SOURCE_DEFINITION, async (datasource?: RobotEntry) => + openDataSourceDefinition(datasource) + ); } async function clearEnvAndRestart() { @@ -1034,6 +1038,41 @@ export const collapseAllEntries = async (): Promise => { vscode.commands.executeCommand(`workbench.actions.treeView.${TREE_VIEW_SEMA4AI_TASK_PACKAGES_TREE}.collapseAll`); }; +export async function openDataSourceDefinition(entry?: RobotEntry) { + if (!entry) { + return; + } + const datasource: DatasourceInfo = entry.extraData?.datasource; + if (!datasource) { + window.showErrorMessage("Data Source object was not provided in extraData."); + return; + } + const uri = Uri.parse(datasource.uri); + if (!(await uriExists(uri))) { + window.showErrorMessage(`Data Source file: ${uri.fsPath} not found.`); + return; + } + const document = await vscode.workspace.openTextDocument(uri); + const editor = await window.showTextDocument(document, { preview: false }); + if (editor) { + editor.revealRange( + new vscode.Range( + datasource.range.start.line - 1, + datasource.range.start.character, + datasource.range.end.line - 1, + datasource.range.end.character + ), + vscode.TextEditorRevealType.InCenter + ); + editor.selection = new vscode.Selection( + datasource.range.start.line - 1, + datasource.range.start.character, + datasource.range.start.line - 1, + datasource.range.start.character + ); + } +} + export async function dropDataSource(entry?: RobotEntry) { if (!(await verifyDataExtensionIsInstalled())) { return; diff --git a/sema4ai/vscode-client/src/robo/actionPackage.ts b/sema4ai/vscode-client/src/robo/actionPackage.ts index 6aab1524..a2c10e3a 100644 --- a/sema4ai/vscode-client/src/robo/actionPackage.ts +++ b/sema4ai/vscode-client/src/robo/actionPackage.ts @@ -9,6 +9,7 @@ import { ProgressLocation, Progress, CancellationToken, + Uri, } from "vscode"; import * as vscode from "vscode"; import * as roboCommands from "../robocorpCommands"; @@ -391,6 +392,37 @@ function convertDataServerInfoToEnvVar(dataServerInfo: DataServerConfig): string }); } +export function getDataSourceTooltip(dataSource: DatasourceInfo): string { + let tooltip = []; + tooltip.push(getDataSourceCaption(dataSource)); + if (dataSource.description) { + tooltip.push(dataSource.description); + } + if (dataSource.python_variable_name) { + tooltip.push(`Python variable name: ${dataSource.python_variable_name}`); + } + if (dataSource.uri) { + let definedAt = dataSource.uri; + try { + const uri = Uri.parse(dataSource.uri); + definedAt = uri.fsPath; + } catch (error) { + // Ignore error + } + + let line = undefined; + if (dataSource.range) { + line = dataSource.range?.start?.line; + } + if (line !== undefined) { + tooltip.push(`Defined at: ${definedAt}, line: ${line}`); + } else { + tooltip.push(`Defined at: ${definedAt}`); + } + } + return tooltip.join("\n"); +} + export function getDataSourceCaption(dataSource: DatasourceInfo): string { if (dataSource.created_table && dataSource.model_name) { return `Bad datasource: ${dataSource.name} - created_table: ${dataSource.created_table} and model_name: ${dataSource.model_name} (${dataSource.engine})`; diff --git a/sema4ai/vscode-client/src/robocorpCommands.ts b/sema4ai/vscode-client/src/robocorpCommands.ts index f2aa7b09..86522ef5 100644 --- a/sema4ai/vscode-client/src/robocorpCommands.ts +++ b/sema4ai/vscode-client/src/robocorpCommands.ts @@ -146,5 +146,6 @@ export const SEMA4AI_COLLAPSE_ALL_ENTRIES = "sema4ai.collapseAllEntries"; // Co export const SEMA4AI_IMPORT_ACTION_PACKAGE = "sema4ai.importActionPackage"; // Import Action Package export const SEMA4AI_RUN_ACTION_PACKAGE_DEV_TASK = "sema4ai.runActionPackageDevTask"; // Run dev-task (from Action Package) export const SEMA4AI_GET_ACTIONS_METADATA = "sema4ai.getActionsMetadata"; // Get Actions Metadata -export const SEMA4AI_DROP_DATA_SOURCE = "sema4ai.dropDataSource"; // Drop Data Sources -export const SEMA4AI_SETUP_DATA_SOURCE = "sema4ai.setupDataSource"; // Setup Data Source \ No newline at end of file +export const SEMA4AI_DROP_DATA_SOURCE = "sema4ai.dropDataSource"; // Remove Data Source +export const SEMA4AI_SETUP_DATA_SOURCE = "sema4ai.setupDataSource"; // Setup Data Source +export const SEMA4AI_OPEN_DATA_SOURCE_DEFINITION = "sema4ai.openDataSourceDefinition"; // Open Data Source definition \ No newline at end of file diff --git a/sema4ai/vscode-client/src/viewsRobots.ts b/sema4ai/vscode-client/src/viewsRobots.ts index efbfd516..7b5c40f4 100644 --- a/sema4ai/vscode-client/src/viewsRobots.ts +++ b/sema4ai/vscode-client/src/viewsRobots.ts @@ -7,7 +7,7 @@ import { basename, RobotEntry, RobotEntryType } from "./viewsCommon"; import { getSelectedRobot } from "./viewsSelection"; import { isActionPackage, isAgentPackage } from "./common"; import path = require("path"); -import { getDataSourceCaption } from "./robo/actionPackage"; +import { getDataSourceCaption, getDataSourceTooltip } from "./robo/actionPackage"; let _globalSentMetric: boolean = false; @@ -533,6 +533,7 @@ export class RobotsTreeDataProvider implements vscode.TreeDataProvider