Skip to content

Commit 63a0bcf

Browse files
authored
Open external URI (#8)
* Fix tsc commands They were complaining about the -p flag so I removed them since they do not appear necessary. I also removed --outDir since that is already specified in the tsconfig.json. * Uncapitalize workspace It does not seem we capitalize it in our docs so this may be more consistent. * Handle external URIs This makes it so we can have an "open in VS Code" button that automatically launches our extension where it can handle downloading the binary (todo) then opening the workspace (done). * Use remote.SSH.configFile when set Otherwise if it is set (either by the user or an extension like Gitpod) our configuration will not be picked up since it is in the default location. VS Code will not read both the default location and this location.
1 parent 84c23ba commit 63a0bcf

File tree

6 files changed

+78
-12
lines changed

6 files changed

+78
-12
lines changed

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"description": "Connect VS Code to your Coder Workspaces",
66
"repository": "https://github.com/cdr/vscode-coder",
77
"preview": true,
8-
"version": "0.0.3-dev",
8+
"version": "0.0.4",
99
"engines": {
1010
"vscode": "^1.54.0"
1111
},
@@ -21,6 +21,7 @@
2121
"Other"
2222
],
2323
"activationEvents": [
24+
"onUri",
2425
"onView:coderWorkspaces"
2526
],
2627
"main": "./dist/extension.js",
@@ -144,8 +145,8 @@
144145
"watch": "webpack --watch",
145146
"package": "webpack --mode production --devtool hidden-source-map",
146147
"lint": "eslint . --ext ts,md",
147-
"tsc:compile": "tsc -p . --outDir out",
148-
"tsc:watch": "tsc -p -w . --outDir out",
148+
"tsc:compile": "tsc",
149+
"tsc:watch": "tsc -w",
149150
"pretest:ci": "npm run tsc:compile",
150151
"test:ci": "node ./out/test/runTest.js",
151152
"test": "npm run test:ci",

src/extension.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// import * as assert from "assert"
22
// import * as vscode from "vscode"
3+
// import * as extension from "./extension"
34

45
suite("Extension", () => {
56
// TODO: these notifications should lead to each other and also show on
@@ -46,4 +47,7 @@ suite("Extension", () => {
4647

4748
// TODO: several links
4849
test("help and feedback")
50+
51+
// TODO: how to test notification content?
52+
test("uriHandler")
4953
})

src/extension.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,31 @@ import {
1818
CoderWorkspaceListItem,
1919
} from "./workspaces"
2020

21+
export const uriHandler: vscode.UriHandler = {
22+
// URIs look like this: vscode://extension-id/action/resource
23+
// To manually test this you can use "Open URL" from the command pallete or
24+
// use `code --open-url` from the command line.
25+
handleUri(uri: vscode.Uri) {
26+
// fsPath will be /action/resource. Remove the leading slash so we can
27+
// split on the first non-leading trailing slash which separates the
28+
// action from the resource. The action is not allowed to contain slashes
29+
// but the resource can.
30+
const [action, resource] = uri.fsPath.replace(/^\//, "").split("/")
31+
if (!action || !resource) {
32+
vscode.window.showErrorMessage(`URI is malformed: "${uri}"`)
33+
return
34+
}
35+
switch (action) {
36+
case "open-workspace":
37+
openWorkspace(resource)
38+
break
39+
default:
40+
vscode.window.showErrorMessage(`Unknown action "${action}"`)
41+
break
42+
}
43+
},
44+
}
45+
2146
export function activate(): void {
2247
preflightCheckCoderInstalled()
2348

@@ -47,6 +72,8 @@ export function activate(): void {
4772

4873
vscode.workspace.registerTextDocumentContentProvider("coder-logs", coderWorkspaceLogsDocumentProvider)
4974
vscode.workspace.registerTextDocumentContentProvider("coder-inspect", coderWorkspaceInspectDocumentProvider)
75+
76+
vscode.window.registerUriHandler(uriHandler)
5077
}
5178

5279
export const outputChannel = vscode.window.createOutputChannel("Coder")

src/utils.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,10 @@ suite("Utils", () => {
4444
assert.strictEqual(await utils.binaryExists("sh"), true)
4545
assert.strictEqual(await utils.binaryExists("surely-no-binary-named-like-this-exists"), false)
4646
})
47+
48+
test("split", () => {
49+
assert.deepStrictEqual(utils.split("foo/bar/baz", "/"), ["foo", "bar/baz"])
50+
assert.deepStrictEqual(utils.split("foo/", "/"), ["foo", ""])
51+
assert.deepStrictEqual(utils.split("foo", "/"), ["foo", ""])
52+
})
4753
})

src/utils.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,21 @@ export const execJSON = async <T>(command: string): Promise<T> => {
2323
return JSON.parse(output)
2424
}
2525

26-
// binaryExists returns "true" if the binary is found in $PATH
26+
/**
27+
* Return "true" if the binary is found in $PATH.
28+
*/
2729
export const binaryExists = async (bin: string): Promise<boolean> => {
2830
return new Promise((res) => {
2931
nodeWhich(bin, (err) => res(!err))
3032
})
3133
}
34+
35+
/**
36+
* Split a string up to the delimiter. If the delimiter does not exist the
37+
* first item will have all the text and the second item will be an empty
38+
* string.
39+
*/
40+
export const split = (str: string, delimiter: string): [string, string] => {
41+
const index = str.indexOf(delimiter)
42+
return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, ""]
43+
}

src/workspaces.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,45 @@ export class CoderWorkspacesProvider implements vscode.TreeDataProvider<CoderWor
2828
export const rebuildWorkspace = async (name: string): Promise<void> => {
2929
try {
3030
await exec(`${coderBinary} envs rebuild ${name} --force`)
31-
vscode.window.showInformationMessage(`Rebuilding Coder Workspace "${name}"`)
31+
vscode.window.showInformationMessage(`Rebuilding Coder workspace "${name}"`)
3232
} catch (e) {
33-
vscode.window.showErrorMessage(`Failed to rebuild Coder Workspaces: ${e}`)
33+
vscode.window.showErrorMessage(`Failed to rebuild Coder workspaces: ${e}`)
3434
}
3535
}
3636

3737
export const shutdownWorkspace = async (name: string): Promise<void> => {
3838
try {
3939
await exec(`${coderBinary} envs stop ${name}`)
40-
vscode.window.showInformationMessage(`Shutting down Coder Workspace "${name}"`)
40+
vscode.window.showInformationMessage(`Shutting down Coder workspace "${name}"`)
4141
} catch (e) {
42-
vscode.window.showErrorMessage(`Failed to shutdown Coder Workspaces: ${e}`)
42+
vscode.window.showErrorMessage(`Failed to shutdown Coder workspaces: ${e}`)
4343
}
4444
}
4545

46+
/**
47+
* Inject Coder hosts into the SSH config file.
48+
*
49+
* If `remote.SSH.configFile` is set use that otherwise use the default.
50+
*/
51+
const setupSSH = async (): Promise<void> => {
52+
const configFile = vscode.workspace.getConfiguration("remote.SSH").get("configFile")
53+
await exec(`${coderBinary} config-ssh ${configFile ? `--filepath ${configFile}` : ""}`)
54+
}
55+
4656
export const openWorkspace = async (name: string): Promise<void> => {
4757
try {
48-
await exec(
49-
`${coderBinary} config-ssh && code --remote "ssh-remote+coder.${name}" $(${coderBinary} sh ${name} pwd | head -n 1)`,
58+
await setupSSH()
59+
// If the provided workspace does not exist this is the point at which we
60+
// will find out because `coder sh` will exit with 1 causing the exec to
61+
// reject (piping should be avoided since the exit code is swallowed).
62+
const pwd = (await exec(`${coderBinary} sh ${name} pwd`)).trim() || "/"
63+
vscode.window.showInformationMessage(`Opening Coder workspace ${name} to ${pwd}`)
64+
await vscode.commands.executeCommand(
65+
"vscode.openFolder",
66+
vscode.Uri.parse(`vscode-remote://ssh-remote+coder.${name}${pwd}`),
5067
)
51-
vscode.window.showInformationMessage(`Opening Coder Workspace ${name}`)
5268
} catch (e) {
53-
vscode.window.showErrorMessage(`Failed to open Coder Workspace ${name}: ${e}`)
69+
vscode.window.showErrorMessage(`Failed to open Coder workspace ${name}: ${e}`)
5470
}
5571
return
5672
}

0 commit comments

Comments
 (0)