diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26375083..709de7fc 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. diff --git a/src/commands.ts b/src/commands.ts index b51d9719..99f5e930 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,34 @@ 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 - } - selected = opened[items.indexOf(item)] - } else { - selected = opened[0] + // 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 = opened.map((f) => f.folderUri.path) + folderPath = await vscode.window.showQuickPick(items, { + title: "Select a recently opened folder", + }) + if (!folderPath) { + // User aborted. + return } - - folderPath = selected.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) } }, })