Skip to content

Commit d50e8b3

Browse files
authored
feat: DH-20578: Groovy remote file source (#273)
DH-20578: Adds Groovy support for remote file source features in VS Code extension. The supporting plugin work has not fully landed yet mostly waiting on tests, but I don't think that should block this PR: **Related Pending PRs** Changes for these are deployed to `bmingles-remote-file-source2` BHS - deephaven/deephaven-core#7451 - deephaven-ent/iris#3878 - TBD: PR to integrate Core changes into gplus ## Testing - Test on a grizzly or gplus server without the plugin installed. Verify that we can still run python and groovy scripts without remote file sourcing. This is just a regression test. - `bmingles-remote-file-source2` has Groovy and Python remote plugins installed, so full feature set can be tested there ### Setup - Start `bmingles-remote-file-source2` vm if not already running - Clone this test repo `git@github.com:bmingles/deephaven-controller-script-test` - Checkout the `local` git branch (the `main` branch is configured as a controller source on `bmingles-remote-file-source2`). ### Local override tests - Connect to `bmingles-remote-file-source2` server in VS Code - Open and run `src/main/groovy/docs-sample.groovy` against the BHS - Should see STDOUT entries in OUTPUT -> Deephaven panel with `"Server"` prefix e.g. ``` 12:16:14.829 STDOUT Server: Notebook Level Var 12:16:14.830 STDOUT Server: Notebook Level Var 12:16:14.830 STDOUT Server: Top Level Var 12:16:14.830 STDOUT Server: Top Level Var ... ``` - Cells in the `testTable` should also include the `"Server"` prefix - Add `src/main/groovy/test` folder as a Groovy remote file source - Run the script again. Should see `"Local"` prefix e.g. ``` 12:21:12.336 STDOUT Local: Notebook Level Var 12:21:12.336 STDOUT Local: Notebook Level Var 12:21:12.337 STDOUT Local: Top Level Var 12:21:12.337 STDOUT Local: Top Level Var ... ``` - Cells in the `testTable` should also include the `"Local"` prefix - Remove the `src/main/groovy/test` as a remote file source - Run the script again. STDOUT and table cells should all go back to the `"Server"` prefixes ### Tree Selection Behavior There was a bug fixed in this PR that impacts how partial unmarking works in the Remote file source tree view. - Navigate to Deephaven activity bar tab (left sidebar) - Expand Groovy workspace tree until you get to `src/main/groovy/nested/ws1` - Add `ws1` folder as remote source with the `+` button - Should see ws1 and all of its children turn green - Hover over `ws1/sub1` subfolder and click the `-` sign to remove it - Result should be that `ws1/sub2` is now the marked folder. `ws1/sub1` and `ws1/file1.groovy` should both be unmarked
1 parent 1373334 commit d50e8b3

38 files changed

Lines changed: 2314 additions & 453 deletions

__mocks__/vscode.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,20 @@ export class DiagnosticCollection
128128
}
129129
}
130130

131-
export class EventEmitter {
132-
fire = vi.fn().mockName('fire');
131+
export class EventEmitter<T> {
132+
listeners = new Set<(...args: any[]) => void>();
133+
event = (listener: (e: T) => any) => {
134+
this.listeners.add(listener);
135+
return () => {
136+
this.listeners.delete(listener);
137+
};
138+
};
139+
fire = vi
140+
.fn()
141+
.mockName('fire')
142+
.mockImplementation((event: T) => {
143+
this.listeners.forEach(listener => listener(event));
144+
});
133145
}
134146

135147
export class MarkdownString {
@@ -306,6 +318,7 @@ export const workspace = {
306318
.fn()
307319
.mockName('createFileSystemWatcher')
308320
.mockReturnValue({
321+
onDidChange: vi.fn().mockName('onDidChange'),
309322
onDidCreate: vi.fn().mockName('onDidCreate'),
310323
onDidDelete: vi.fn().mockName('onDidDelete'),
311324
}),

e2e-testing/src/runner.mts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,21 @@ const isSetup = args.includes('--setup');
1010
const __dirname = import.meta.dirname;
1111
const e2eTestingPath = path.resolve(__dirname, '..');
1212

13-
const storagePath = path.join(e2eTestingPath, '.resources');
13+
const storagePath =
14+
process.env.TEST_RESOURCES ?? path.join(e2eTestingPath, '.resources');
15+
16+
// Warn about Unix socket path length constraints
17+
// Electron/VS Code creates an IPC socket under storagePath/settings/<version>-main.sock
18+
// Unix platforms (macOS, Linux) have varying socket path length limits.
19+
// Storage path length > 70 is conservative to avoid constraints on different OSs.
20+
if (process.platform !== 'win32' && storagePath.length > 70) {
21+
console.warn(
22+
`\nWARNING: Storage path length (${storagePath.length} chars) may hit socket path length ` +
23+
`constraints on some OSs.\n` +
24+
` Path: ${storagePath}\n` +
25+
`If e2e tests fail with connection errors, try setting a shorter TEST_RESOURCES path.\n`
26+
);
27+
}
1428
const extensionsPath = path.join(e2eTestingPath, '.test-extensions');
1529
const testFilesPattern = path.join(e2eTestingPath, 'out', '**', '*.spec.js');
1630
const mochaConfig = path.join(

package.json

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,34 @@
326326
"icon": "$(close)"
327327
},
328328
{
329-
"command": "vscode-deephaven.addRemoteFileSource",
330-
"title": "Add to Deephaven remote file sources",
329+
"command": "vscode-deephaven.addGroovyRemoteFileSource",
330+
"title": "Add to Deephaven Groovy remote file sources",
331331
"icon": "$(add)"
332332
},
333333
{
334-
"command": "vscode-deephaven.removeRemoteFileSource",
335-
"title": "Remove from Deephaven remote file sources",
334+
"command": "vscode-deephaven.removeGroovyRemoteFileSource",
335+
"title": "Remove from Deephaven Groovy remote file sources",
336+
"icon": "$(remove)"
337+
},
338+
{
339+
"command": "vscode-deephaven.addPythonRemoteFileSource",
340+
"title": "Add to Deephaven Python remote file sources",
341+
"icon": "$(add)"
342+
},
343+
{
344+
"command": "vscode-deephaven.removePythonRemoteFileSource",
345+
"title": "Remove from Deephaven Python remote file sources",
336346
"icon": "$(remove)"
337347
},
338348
{
339349
"command": "vscode-deephaven.deleteVariable",
340350
"title": "Delete Deephaven Variable",
341351
"icon": "$(trash)"
352+
},
353+
{
354+
"command": "vscode-deephaven.revealInExplorer",
355+
"title": "Reveal in Explorer",
356+
"icon": "$(eye)"
342357
}
343358
],
344359
"icons": {
@@ -865,16 +880,28 @@
865880
"when": "false"
866881
},
867882
{
868-
"command": "vscode-deephaven.addRemoteFileSource",
883+
"command": "vscode-deephaven.addGroovyRemoteFileSource",
869884
"when": "false"
870885
},
871886
{
872-
"command": "vscode-deephaven.removeRemoteFileSource",
887+
"command": "vscode-deephaven.removeGroovyRemoteFileSource",
888+
"when": "false"
889+
},
890+
{
891+
"command": "vscode-deephaven.addPythonRemoteFileSource",
892+
"when": "false"
893+
},
894+
{
895+
"command": "vscode-deephaven.removePythonRemoteFileSource",
873896
"when": "false"
874897
},
875898
{
876899
"command": "vscode-deephaven.deleteVariable",
877900
"when": "false"
901+
},
902+
{
903+
"command": "vscode-deephaven.revealInExplorer",
904+
"when": "false"
878905
}
879906
],
880907
"editor/context": [
@@ -903,12 +930,22 @@
903930
],
904931
"explorer/context": [
905932
{
906-
"command": "vscode-deephaven.addRemoteFileSource",
933+
"command": "vscode-deephaven.addGroovyRemoteFileSource",
907934
"group": "deephaven",
908935
"when": "explorerResourceIsFolder"
909936
},
910937
{
911-
"command": "vscode-deephaven.removeRemoteFileSource",
938+
"command": "vscode-deephaven.removeGroovyRemoteFileSource",
939+
"group": "deephaven",
940+
"when": "explorerResourceIsFolder"
941+
},
942+
{
943+
"command": "vscode-deephaven.addPythonRemoteFileSource",
944+
"group": "deephaven",
945+
"when": "explorerResourceIsFolder"
946+
},
947+
{
948+
"command": "vscode-deephaven.removePythonRemoteFileSource",
912949
"group": "deephaven",
913950
"when": "explorerResourceIsFolder"
914951
}
@@ -999,14 +1036,28 @@
9991036
"group": "inline@2"
10001037
},
10011038
{
1002-
"command": "vscode-deephaven.addRemoteFileSource",
1003-
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem == canAddRemoteFileSource",
1039+
"command": "vscode-deephaven.addGroovyRemoteFileSource",
1040+
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem == canAddRemoteFileSource:groovy",
10041041
"group": "inline"
10051042
},
10061043
{
1007-
"command": "vscode-deephaven.removeRemoteFileSource",
1008-
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem == canRemoveRemoteFileSource",
1044+
"command": "vscode-deephaven.removeGroovyRemoteFileSource",
1045+
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem == canRemoveRemoteFileSource:groovy",
10091046
"group": "inline"
1047+
},
1048+
{
1049+
"command": "vscode-deephaven.addPythonRemoteFileSource",
1050+
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem == canAddRemoteFileSource:python",
1051+
"group": "inline"
1052+
},
1053+
{
1054+
"command": "vscode-deephaven.removePythonRemoteFileSource",
1055+
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem == canRemoveRemoteFileSource:python",
1056+
"group": "inline"
1057+
},
1058+
{
1059+
"command": "vscode-deephaven.revealInExplorer",
1060+
"when": "view == vscode-deephaven.view.remoteImportSourceTree && viewItem != root && viewItem != languageRoot"
10101061
}
10111062
]
10121063
},
8.53 MB
Binary file not shown.

skills/deephaven-vscode-using/SKILL.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,14 @@ Community servers always support panel URLs. Enterprise servers require Grizzly+
7676

7777
### Remote File Sources
7878

79-
Enable server to fetch source files during execution:
79+
Configure which workspace folders the server fetches source files from during execution:
8080

8181
- Add folder URIs as remote sources (`addRemoteFileSources`)
8282
- List current sources (`listRemoteFileSources`)
8383
- Remove sources when done (`removeRemoteFileSources`)
8484

85+
If an import error occurs, follow the hint in the tool response — it will specify how to resolve it.
86+
8587
### Troubleshooting
8688

8789
**MCP Server Not Running:**
@@ -114,6 +116,10 @@ Server connections are managed through MCP tools. Use `listConnections`, `listSe
114116

115117
All MCP tools are available when the MCP server is enabled via `deephaven.mcp.enabled` setting. If tools aren't available, ensure MCP is enabled (see Troubleshooting).
116118

119+
### Tool Response Hints
120+
121+
Tool responses may include hints that guide next steps. Consider them before deciding how to proceed.
122+
117123
### Variable Management
118124

119125
- Only list panel variables when explicitly asked

src/common/commands.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import type {
1010
WorkerURL,
1111
} from '../types';
1212

13-
/** Arguments passed to `ADD_REMOTE_FILE_SOURCE_CMD` handler */
13+
/** Arguments passed to `ADD_GROOVY_REMOTE_FILE_SOURCE_CMD` and `ADD_PYTHON_REMOTE_FILE_SOURCE_CMD` handlers */
1414
export type AddRemoteFileSourceCmdArgs = [
15+
languageId: 'groovy' | 'python',
1516
folderElementOrUri:
1617
| RemoteImportSourceTreeFolderElement
1718
| vscode.Uri
@@ -37,8 +38,9 @@ export type RefreshVariablePanelsCmdArgs = [
3738
variables: NonEmptyArray<VariableDefintion>,
3839
];
3940

40-
/** Arguments passed to `REMOVE_REMOTE_FILE_SOURCE_CMD` handler */
41+
/** Arguments passed to `REMOVE_GROOVY_REMOTE_FILE_SOURCE_CMD` and `REMOVE_PYTHON_REMOTE_FILE_SOURCE_CMD` handlers */
4142
export type RemoveRemoteFileSourceCmdArgs = [
43+
languageId: 'groovy' | 'python',
4244
folderElementOrUri:
4345
| RemoteImportSourceTreeFolderElement
4446
| vscode.Uri
@@ -77,6 +79,12 @@ function cmd<T extends string>(cmd: T): `${typeof EXTENSION_ID}.${T}` {
7779
return `${EXTENSION_ID}.${cmd}`;
7880
}
7981

82+
export const ADD_GROOVY_REMOTE_FILE_SOURCE_CMD = cmd(
83+
'addGroovyRemoteFileSource'
84+
);
85+
export const ADD_PYTHON_REMOTE_FILE_SOURCE_CMD = cmd(
86+
'addPythonRemoteFileSource'
87+
);
8088
export const CLEAR_SECRET_STORAGE_CMD = cmd('clearSecretStorage');
8189
export const CLOSE_CREATE_QUERY_VIEW_CMD = cmd('view.createQuery.close');
8290
export const CONNECT_TO_SERVER_CMD = cmd('connectToServer');
@@ -106,6 +114,13 @@ export const REFRESH_SERVER_CONNECTION_TREE_CMD = cmd(
106114
);
107115
export const REFRESH_PANELS_TREE_CMD = cmd('refreshPanelsTree');
108116
export const REFRESH_VARIABLE_PANELS_CMD = cmd('refreshVariablePanels');
117+
export const REMOVE_GROOVY_REMOTE_FILE_SOURCE_CMD = cmd(
118+
'removeGroovyRemoteFileSource'
119+
);
120+
export const REMOVE_PYTHON_REMOTE_FILE_SOURCE_CMD = cmd(
121+
'removePythonRemoteFileSource'
122+
);
123+
export const REVEAL_IN_EXPLORER_CMD = cmd('revealInExplorer');
109124
export const RUN_CODE_COMMAND = cmd('runCode');
110125
export const RUN_MARKDOWN_CODEBLOCK_CMD = cmd('runMarkdownCodeBlock');
111126
export const RUN_SELECTION_COMMAND = cmd('runSelection');
@@ -116,17 +131,22 @@ export const SHOW_MCP_QUICK_PICK_CMD = cmd('showMcpQuickPick');
116131
export const START_SERVER_CMD = cmd('startServer');
117132
export const STOP_SERVER_CMD = cmd('stopServer');
118133
export const TOGGLE_MCP_CMD = cmd('toggleMcp');
119-
export const ADD_REMOTE_FILE_SOURCE_CMD = cmd('addRemoteFileSource');
120-
export const REMOVE_REMOTE_FILE_SOURCE_CMD = cmd('removeRemoteFileSource');
121134

122135
/**
123136
* Execute the add remote file source command with type safety.
124-
* @param uris The folder URIs to add as remote file sources.
137+
* @param args The arguments for adding the remote file source, including the language ID and folder URI(s).
138+
* @returns A Thenable that resolves when the command has been executed.
125139
*/
126140
export function execAddRemoteFileSource(
127141
...args: AddRemoteFileSourceCmdArgs
128142
): Thenable<void> {
129-
return vscode.commands.executeCommand(ADD_REMOTE_FILE_SOURCE_CMD, ...args);
143+
const [languageId, ...rest] = args;
144+
return vscode.commands.executeCommand(
145+
languageId === 'groovy'
146+
? ADD_GROOVY_REMOTE_FILE_SOURCE_CMD
147+
: ADD_PYTHON_REMOTE_FILE_SOURCE_CMD,
148+
...rest
149+
);
130150
}
131151

132152
/**
@@ -153,12 +173,21 @@ export function execOpenVariablePanels(
153173

154174
/**
155175
* Execute the remove remote file source command with type safety.
156-
* @param uris The folder URIs to remove as remote file sources.
176+
* @param args The arguments for removing the remote file source, including the
177+
* language ID and folder URI(s).
178+
* @returns A Thenable that resolves when the command has been executed.
157179
*/
158180
export function execRemoveRemoteFileSource(
159181
...args: RemoveRemoteFileSourceCmdArgs
160182
): Thenable<void> {
161-
return vscode.commands.executeCommand(REMOVE_REMOTE_FILE_SOURCE_CMD, ...args);
183+
const [languageId, ...rest] = args;
184+
185+
return vscode.commands.executeCommand(
186+
languageId === 'groovy'
187+
? REMOVE_GROOVY_REMOTE_FILE_SOURCE_CMD
188+
: REMOVE_PYTHON_REMOTE_FILE_SOURCE_CMD,
189+
...rest
190+
);
162191
}
163192

164193
/**

src/common/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ export const ICON_ID = {
123123
connected: 'vm-connect',
124124
connecting: 'sync~spin',
125125
disconnected: 'plug',
126+
groovy: 'coffee',
127+
python: 'dh-python',
126128
runAll: 'run-all',
127129
runSelection: 'run',
128130
runningCode: 'sync~spin',

0 commit comments

Comments
 (0)