From 96b8229539c434d7f7289e0cf1a4119dbae88636 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 14 May 2024 12:01:03 -0800 Subject: [PATCH 1/5] Add a way to open the most recent workspace/folder If you add openRecent to the VS Code link, it will open the most recent instead of whatever is in the folder param (if there is a most recent). Also decided to open the most recent when opening from the sidebar, seemed reasonable to me anyway. --- src/commands.ts | 51 +++++++++++++++++++++++++++--------------------- src/extension.ts | 5 ++++- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index b51d9719..02166045 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -223,6 +223,7 @@ export class Commands { treeItem.workspaceName, treeItem.workspaceAgent, treeItem.workspaceFolderPath, + true, ) } } @@ -232,6 +233,7 @@ export class Commands { let workspaceName: string let workspaceAgent: string | undefined let folderPath: string | undefined + let openRecent: boolean | undefined if (args.length === 0) { const quickPick = vscode.window.createQuickPick() @@ -340,9 +342,10 @@ export class Commands { workspaceName = args[1] as string // workspaceAgent is reserved for args[2], but multiple agents aren't supported yet. folderPath = args[3] as string | undefined + openRecent = args[4] as boolean | undefined } - await openWorkspace(workspaceOwner, workspaceName, workspaceAgent, folderPath) + await openWorkspace(workspaceOwner, workspaceName, workspaceAgent, folderPath, openRecent) } public async updateWorkspace(): Promise { @@ -369,6 +372,7 @@ async function openWorkspace( workspaceName: string, workspaceAgent: string | undefined, folderPath: string | undefined, + openRecent: boolean | undefined, ) { // A workspace can have multiple agents, but that's handled // when opening a workspace unless explicitly specified. @@ -383,36 +387,39 @@ async function openWorkspace( newWindow = false } - // If a folder isn't specified, we can try to open a recently opened folder. - if (!folderPath) { + // If a folder isn't specified or we have been asked to open the most recent, + // we can try to open a recently opened folder/workspace. + if (!folderPath || openRecent) { const output: { workspaces: { folderUri: vscode.Uri; remoteAuthority: string }[] } = await vscode.commands.executeCommand("_workbench.getRecentlyOpened") const opened = output.workspaces.filter( - // Filter out `/` since that's added below. + // Remove recents that do not belong to this connection. The remote + // authority maps to a workspace or workspace/agent combination (using the + // SSH host name). This means, at the moment, you can have a different + // set of recents for a workspace versus workspace/agent combination, even + // if that agent is the default for the workspace. (opened) => opened.folderUri?.authority === remoteAuthority, ) - if (opened.length > 0) { - let selected: (typeof opened)[0] - if (opened.length > 1) { - const items: vscode.QuickPickItem[] = opened.map((folder): vscode.QuickPickItem => { - return { - label: folder.folderUri.path, - } - }) - const item = await vscode.window.showQuickPick(items, { - title: "Select a recently opened folder", - }) - if (!item) { - return + // openRecent will always use the most recent. Otherwise, if there are + // multiple we ask the user which to use. + if (opened.length === 1 || (opened.length > 1 && openRecent)) { + folderPath = opened[0].folderUri.path + } else if (opened.length > 1) { + const items: vscode.QuickPickItem[] = opened.map((folder): vscode.QuickPickItem => { + return { + label: folder.folderUri.path, } - selected = opened[items.indexOf(item)] - } else { - selected = opened[0] + }) + const item = await vscode.window.showQuickPick(items, { + title: "Select a recently opened folder", + }) + if (!item) { + // User aborted. + return } - - folderPath = selected.folderUri.path + folderPath = opened[items.indexOf(item)].folderUri.path } } diff --git a/src/extension.ts b/src/extension.ts index 4148a6fd..0176d71f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -140,6 +140,9 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { const workspace = params.get("workspace") const agent = params.get("agent") const folder = params.get("folder") + const openRecent = + params.has("openRecent") && (!params.get("openRecent") || params.get("openRecent") === "true") + if (!owner) { throw new Error("owner must be specified as a query parameter") } @@ -166,7 +169,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { await storage.setSessionToken(token) } - vscode.commands.executeCommand("coder.open", owner, workspace, agent, folder) + vscode.commands.executeCommand("coder.open", owner, workspace, agent, folder, openRecent) } }, }) From d7d1b82a63aaf28e5be845977896bc860870bc26 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 14 May 2024 12:40:58 -0800 Subject: [PATCH 2/5] Add notes on testing the link --- CONTRIBUTING.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26375083..468469f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,28 @@ The `vscodessh` subcommand on the `coder` binary periodically flushes it's netwo Coder Remote periodically reads the `network-info-dir + "/" + matchingSSHPID` file to display network information. +## Testing + +There are a few ways you can test the "Open in VS Code" flow: + +- Use the "VS Code Desktop" button from a Coder dashboard. +- Manually open the link with `Developer: Open URL` from inside VS Code. +- Use `code --open-url ` on the command line. + +The link format is `vscode://coder.coder-remote/open?${query}`. For example: + +```bash +code --open-url 'vscode://coder.coder-remote/open?url=dev.coder.com&owner=my-username&workspace=my-ws&agent=my-agent' +``` + +There are some unit tests as well: + +```bash +yarn test +``` + +However, fully testing the plugin is blocked on switching back to the standard VS Code extension testing framework. + ## Development 1. Run `yarn watch` in the background. From 9a3734d06bdde42cd9fd941c05cc0b5354f3b6d1 Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 20 May 2024 13:54:10 -0800 Subject: [PATCH 3/5] Remove from open-url option Feels self-explanatory? --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 468469f7..709de7fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ There are a few ways you can test the "Open in VS Code" flow: - Use the "VS Code Desktop" button from a Coder dashboard. - Manually open the link with `Developer: Open URL` from inside VS Code. -- Use `code --open-url ` on the command line. +- Use `code --open-url` on the command line. The link format is `vscode://coder.coder-remote/open?${query}`. For example: From 28e11dfbb077190dcbb9efae8a61804036cff1dc Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 20 May 2024 13:54:57 -0800 Subject: [PATCH 4/5] Better typed map --- src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.ts b/src/commands.ts index 02166045..32904a77 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -407,7 +407,7 @@ async function openWorkspace( if (opened.length === 1 || (opened.length > 1 && openRecent)) { folderPath = opened[0].folderUri.path } else if (opened.length > 1) { - const items: vscode.QuickPickItem[] = opened.map((folder): vscode.QuickPickItem => { + const items = opened.map((folder) => { return { label: folder.folderUri.path, } From 26c58adf53f1a81228860b64a9a89e4a724a7a7e Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 20 May 2024 14:04:53 -0800 Subject: [PATCH 5/5] Simplify choosing a recent folder We can just use the selected value directly. --- src/commands.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index 32904a77..99f5e930 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -407,19 +407,14 @@ async function openWorkspace( if (opened.length === 1 || (opened.length > 1 && openRecent)) { folderPath = opened[0].folderUri.path } else if (opened.length > 1) { - const items = opened.map((folder) => { - return { - label: folder.folderUri.path, - } - }) - const item = await vscode.window.showQuickPick(items, { + const items = opened.map((f) => f.folderUri.path) + folderPath = await vscode.window.showQuickPick(items, { title: "Select a recently opened folder", }) - if (!item) { + if (!folderPath) { // User aborted. return } - folderPath = opened[items.indexOf(item)].folderUri.path } }