Skip to content

Commit c2ecce1

Browse files
committed
feat(vscode): redirect to folder from cli
1 parent 15b3a7b commit c2ecce1

File tree

3 files changed

+84
-76
lines changed

3 files changed

+84
-76
lines changed

src/node/cli.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,7 @@ import { promises as fs } from "fs"
33
import yaml from "js-yaml"
44
import * as os from "os"
55
import * as path from "path"
6-
import {
7-
canConnect,
8-
generateCertificate,
9-
generatePassword,
10-
humanPath,
11-
paths,
12-
isNodeJSErrnoException,
13-
isFile,
14-
} from "./util"
6+
import { canConnect, generateCertificate, generatePassword, humanPath, paths, isNodeJSErrnoException } from "./util"
157

168
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
179

@@ -434,7 +426,7 @@ export interface DefaultedArgs extends ConfigArgs {
434426
"extensions-dir": string
435427
"user-data-dir": string
436428
/* Positional arguments. */
437-
_: []
429+
_: string[] | []
438430
}
439431

440432
/**

src/node/routes/vscode.ts

+31-14
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { logger } from "@coder/logger"
22
import * as express from "express"
3+
import * as path from "path"
34
import { WebsocketRequest } from "../../../typings/pluginapi"
45
import { logError } from "../../common/util"
56
import { toVsCodeArgs } from "../cli"
67
import { isDevMode } from "../constants"
78
import { authenticated, ensureAuthenticated, redirect, self } from "../http"
89
import { SocketProxyProvider } from "../socket"
9-
import { loadAMDModule } from "../util"
10+
import { isFile, loadAMDModule } from "../util"
1011
import { Router as WsRouter } from "../wsRouter"
1112
import { errorHandler } from "./errors"
1213

@@ -33,27 +34,43 @@ export class CodeServerRouteWrapper {
3334
})
3435
}
3536

36-
const { query } = await req.settings.read()
37-
if (query) {
38-
// Ew means the workspace was closed so clear the last folder/workspace.
39-
if (req.query.ew) {
40-
delete query.folder
41-
delete query.workspace
42-
}
37+
let folder = undefined
38+
let workspace = undefined
39+
const to = self(req)
40+
const settings = await req.settings.read()
41+
const lastOpened = settings.query || {}
42+
43+
// Ew means the workspace was closed so clear the last folder/workspace.
44+
if (req.query.ew) {
45+
delete lastOpened.folder
46+
delete lastOpened.workspace
47+
}
4348

49+
if (!req.query.folder && !req.query.workspace) {
4450
// Redirect to the last folder/workspace if nothing else is opened.
4551
if (
46-
!req.query.folder &&
47-
!req.query.workspace &&
48-
(query.folder || query.workspace) &&
52+
(lastOpened.folder || lastOpened.workspace) &&
4953
!req.args["ignore-last-opened"] // This flag disables this behavior.
5054
) {
51-
const to = self(req)
5255
return redirect(req, res, to, {
53-
folder: query.folder,
54-
workspace: query.workspace,
56+
folder: lastOpened.folder,
57+
workspace: lastOpened.workspace,
5558
})
59+
} else if (req.args._.length > 0) {
60+
if (req.args._.length) {
61+
const lastEntry = path.resolve(req.args._[req.args._.length - 1])
62+
const entryIsFile = await isFile(lastEntry)
63+
if (entryIsFile && path.extname(lastEntry) === ".code-workspace") {
64+
workspace = lastEntry
65+
} else if (!entryIsFile) {
66+
folder = lastEntry
67+
}
68+
}
5669
}
70+
return redirect(req, res, to, {
71+
folder,
72+
workspace,
73+
})
5774
}
5875

5976
// Store the query parameters so we can use them on the next load. This

test/unit/node/routes/vscode.test.ts

+51-52
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
11
import { promises as fs } from "fs"
2-
import { Response } from "node-fetch"
32
import * as path from "path"
43
import { clean, tmpdir } from "../../../utils/helpers"
54
import * as httpserver from "../../../utils/httpserver"
65
import * as integration from "../../../utils/integration"
76

8-
interface WorkbenchConfig {
9-
folderUri?: {
10-
path: string
11-
}
12-
workspaceUri?: {
13-
path: string
14-
}
15-
}
16-
177
describe("vscode", () => {
188
let codeServer: httpserver.HttpServer | undefined
199

@@ -52,52 +42,25 @@ describe("vscode", () => {
5242
}
5343
})
5444

55-
/**
56-
* Get the workbench config from the provided response.
57-
*/
58-
const getConfig = async (resp: Response): Promise<WorkbenchConfig> => {
59-
expect(resp.status).toBe(200)
60-
const html = await resp.text()
61-
const match = html.match(/<meta id="vscode-workbench-web-configuration" data-settings="(.+)">/)
62-
if (!match || !match[1]) {
63-
throw new Error("Unable to find workbench configuration")
64-
}
65-
const config = match[1].replace(/&quot;/g, '"')
66-
try {
67-
return JSON.parse(config)
68-
} catch (error) {
69-
console.error("Failed to parse workbench configuration", config)
70-
throw error
71-
}
72-
}
73-
74-
it("should have no default folder or workspace", async () => {
75-
codeServer = await integration.setup(["--auth=none"], "")
76-
77-
const config = await getConfig(await codeServer.fetch("/"))
78-
expect(config.folderUri).toBeUndefined()
79-
expect(config.workspaceUri).toBeUndefined()
80-
})
81-
82-
it("should have a default folder", async () => {
83-
const defaultDir = await tmpdir(testName)
84-
codeServer = await integration.setup(["--auth=none", defaultDir], "")
45+
it("should redirect to the passed in workspace", async () => {
46+
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
47+
await fs.writeFile(workspace, "")
48+
codeServer = await integration.setup(["--auth=none", workspace], "")
8549

86-
// At first it will load the directory provided on the command line.
87-
const config = await getConfig(await codeServer.fetch("/"))
88-
expect(config.folderUri?.path).toBe(defaultDir)
89-
expect(config.workspaceUri).toBeUndefined()
50+
const resp = await codeServer.fetch("/")
51+
const url = new URL(resp.url)
52+
expect(url.pathname).toBe("/")
53+
expect(url.search).toBe(`?workspace=${workspace}`)
9054
})
9155

92-
it("should have a default workspace", async () => {
93-
const defaultWorkspace = path.join(await tmpdir(testName), "test.code-workspace")
94-
await fs.writeFile(defaultWorkspace, "")
95-
codeServer = await integration.setup(["--auth=none", defaultWorkspace], "")
56+
it("should redirect to the passed in directory", async () => {
57+
const folder = await tmpdir(testName)
58+
codeServer = await integration.setup(["--auth=none", folder], "")
9659

97-
// At first it will load the workspace provided on the command line.
98-
const config = await getConfig(await codeServer.fetch("/"))
99-
expect(config.folderUri).toBeUndefined()
100-
expect(config.workspaceUri?.path).toBe(defaultWorkspace)
60+
const resp = await codeServer.fetch("/")
61+
const url = new URL(resp.url)
62+
expect(url.pathname).toBe("/")
63+
expect(url.search).toBe(`?folder=${folder}`)
10164
})
10265

10366
it("should redirect to last query folder/workspace", async () => {
@@ -136,6 +99,42 @@ describe("vscode", () => {
13699
await resp.text()
137100
})
138101

102+
it("should add the workspace as a query param maintaining the slashes", async () => {
103+
const workspace = path.join(await tmpdir(testName), "test.code-workspace")
104+
await fs.writeFile(workspace, "")
105+
codeServer = await integration.setup(["--auth=none", workspace], "")
106+
107+
let resp = await codeServer.fetch("/", undefined)
108+
109+
expect(resp.status).toBe(200)
110+
const url = new URL(resp.url)
111+
expect(url.search).toBe(`?workspace=${workspace}`)
112+
await resp.text()
113+
})
114+
115+
it("should do nothing when nothing is passed in", async () => {
116+
codeServer = await integration.setup(["--auth=none"], "")
117+
118+
let resp = await codeServer.fetch("/", undefined)
119+
120+
expect(resp.status).toBe(200)
121+
const url = new URL(resp.url)
122+
expect(url.search).toBe("")
123+
await resp.text()
124+
})
125+
126+
it("should add the folder as a query param maintaining the slashes", async () => {
127+
const folder = await tmpdir(testName)
128+
codeServer = await integration.setup(["--auth=none", folder], "")
129+
130+
let resp = await codeServer.fetch("/", undefined)
131+
132+
expect(resp.status).toBe(200)
133+
const url = new URL(resp.url)
134+
expect(url.search).toBe(`?folder=${folder}`)
135+
await resp.text()
136+
})
137+
139138
it("should not redirect when last opened is ignored", async () => {
140139
codeServer = await integration.setup(["--auth=none", "--ignore-last-opened"], "")
141140

0 commit comments

Comments
 (0)