| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 021c084 commit fe19391
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3035,6 +3035,31 @@ index b3c89e51cfc25a53293a352a2a8ad50d5f26d595..e21abe4e13bc25a5b72f556bbfb61085 | |||
| 3035 | 3035 | registerSingleton(IExtHostTerminalService, ExtHostTerminalService); | |
| 3036 | 3036 | registerSingleton(IExtHostTunnelService, ExtHostTunnelService); | |
| 3037 | 3037 | +registerSingleton(IExtHostNodeProxy, class extends NotImplementedProxy<IExtHostNodeProxy>(String(IExtHostNodeProxy)) { whenReady = Promise.resolve(); }); | |
| 3038 | + diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts | ||
| 3039 | + index 7cae126cc0f804273850933468690e0f9f10a5b8..08c2aa5cdae3f3d06bb08b7055dc7e7def260132 100644 | ||
| 3040 | + --- a/src/vs/workbench/api/node/extHostCLIServer.ts | ||
| 3041 | + +++ b/src/vs/workbench/api/node/extHostCLIServer.ts | ||
| 3042 | + @@ -11,6 +11,8 @@ import { IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/ | ||
| 3043 | + import { URI } from 'vs/base/common/uri'; | ||
| 3044 | + import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; | ||
| 3045 | + import { ILogService } from 'vs/platform/log/common/log'; | ||
| 3046 | + +import { join } from 'vs/base/common/path'; | ||
| 3047 | + +import { tmpdir } from 'os'; | ||
| 3048 | + | ||
| 3049 | + export interface OpenCommandPipeArgs { | ||
| 3050 | + type: 'open'; | ||
| 3051 | + @@ -54,6 +56,11 @@ export class CLIServer { | ||
| 3052 | + private async setup(): Promise<string> { | ||
| 3053 | + this._ipcHandlePath = generateRandomPipeName(); | ||
| 3054 | + | ||
| 3055 | + + // NOTE@coder: Write this out so we can get the most recent path. | ||
| 3056 | + + fs.promises.writeFile(join(tmpdir(), "vscode-ipc"), this._ipcHandlePath).catch((error) => { | ||
| 3057 | + + this.logService.error(error); | ||
| 3058 | + + }); | ||
| 3059 | + + | ||
| 3060 | + try { | ||
| 3061 | + this._server.listen(this.ipcHandlePath); | ||
| 3062 | + this._server.on('error', err => this.logService.error(err)); | ||
| 3038 | 3063 | diff --git a/src/vs/workbench/api/worker/extHost.worker.services.ts b/src/vs/workbench/api/worker/extHost.worker.services.ts | |
| 3039 | 3064 | index 3843fdec386edc09a1d361b63de892a04e0070ed..8aac4df527857e964798362a69f5591bef07c165 100644 | |
| 3040 | 3065 | --- a/src/vs/workbench/api/worker/extHost.worker.services.ts | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,7 @@ import * as os from "os" | |||
| 5 | 5 | import * as path from "path" | |
| 6 | 6 | import { Args as VsArgs } from "../../lib/vscode/src/vs/server/ipc" | |
| 7 | 7 | import { AuthType } from "./http" | |
| 8 | - import { generatePassword, humanPath, paths } from "./util" | ||
| 8 | + import { canConnect, generatePassword, humanPath, paths } from "./util" | ||
| 9 | 9 | ||
| 10 | 10 | export class Optional<T> { | |
| 11 | 11 | public constructor(public readonly value?: T) {} | |
@@ -512,7 +512,35 @@ export const shouldOpenInExistingInstance = async (args: Args): Promise<string | | |||
| 512 | 512 | return process.env.VSCODE_IPC_HOOK_CLI | |
| 513 | 513 | } | |
| 514 | 514 | ||
| 515 | - // TODO: implement | ||
| 515 | + const readSocketPath = async (): Promise<string | undefined> => { | ||
| 516 | + try { | ||
| 517 | + return await fs.readFile(path.join(os.tmpdir(), "vscode-ipc"), "utf8") | ||
| 518 | + } catch (error) { | ||
| 519 | + if (error.code !== "ENOENT") { | ||
| 520 | + throw error | ||
| 521 | + } | ||
| 522 | + } | ||
| 523 | + return undefined | ||
| 524 | + } | ||
| 525 | + | ||
| 526 | + // If these flags are set then assume the user is trying to open in an | ||
| 527 | + // existing instance since these flags have no effect otherwise. | ||
| 528 | + const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => { | ||
| 529 | + return args[cur as keyof Args] ? prev + 1 : prev | ||
| 530 | + }, 0) | ||
| 531 | + if (openInFlagCount > 0) { | ||
| 532 | + return readSocketPath() | ||
| 533 | + } | ||
| 534 | + | ||
| 535 | + // It's possible the user is trying to spawn another instance of code-server. | ||
| 536 | + // Check if any unrelated flags are set (add one for `_` which always exists), | ||
| 537 | + // that a file or directory was passed, and that the socket is active. | ||
| 538 | + if (Object.keys(args).length === openInFlagCount + 1 && args._.length > 0) { | ||
| 539 | + const socketPath = await readSocketPath() | ||
| 540 | + if (socketPath && (await canConnect(socketPath))) { | ||
| 541 | + return socketPath | ||
| 542 | + } | ||
| 543 | + } | ||
| 516 | 544 | ||
| 517 | 545 | return undefined | |
| 518 | 546 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,7 +4,7 @@ import * as path from "path" | |||
| 4 | 4 | import * as tls from "tls" | |
| 5 | 5 | import { Emitter } from "../common/emitter" | |
| 6 | 6 | import { generateUuid } from "../common/util" | |
| 7 | - import { tmpdir } from "./util" | ||
| 7 | + import { canConnect, tmpdir } from "./util" | ||
| 8 | 8 | ||
| 9 | 9 | /** | |
| 10 | 10 | * Provides a way to proxy a TLS socket. Can be used when you need to pass a | |
@@ -89,17 +89,6 @@ export class SocketProxyProvider { | |||
| 89 | 89 | } | |
| 90 | 90 | ||
| 91 | 91 | public async findFreeSocketPath(basePath: string, maxTries = 100): Promise<string> { | |
| 92 | - const canConnect = (path: string): Promise<boolean> => { | ||
| 93 | - return new Promise((resolve) => { | ||
| 94 | - const socket = net.connect(path) | ||
| 95 | - socket.once("error", () => resolve(false)) | ||
| 96 | - socket.once("connect", () => { | ||
| 97 | - socket.destroy() | ||
| 98 | - resolve(true) | ||
| 99 | - }) | ||
| 100 | - }) | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | 92 | let i = 0 | |
| 104 | 93 | let path = basePath | |
| 105 | 94 | while ((await canConnect(path)) && i < maxTries) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ import * as cp from "child_process" | |||
| 2 | 2 | import * as crypto from "crypto" | |
| 3 | 3 | import envPaths from "env-paths" | |
| 4 | 4 | import * as fs from "fs-extra" | |
| 5 | + import * as net from "net" | ||
| 5 | 6 | import * as os from "os" | |
| 6 | 7 | import * as path from "path" | |
| 7 | 8 | import * as util from "util" | |
@@ -246,3 +247,17 @@ export function pathToFsPath(path: string, keepDriveLetterCasing = false): strin | |||
| 246 | 247 | } | |
| 247 | 248 | return value | |
| 248 | 249 | } | |
| 250 | + | ||
| 251 | + /** | ||
| 252 | + * Return a promise that resolves with whether the socket path is active. | ||
| 253 | + */ | ||
| 254 | + export function canConnect(path: string): Promise<boolean> { | ||
| 255 | + return new Promise((resolve) => { | ||
| 256 | + const socket = net.connect(path) | ||
| 257 | + socket.once("error", () => resolve(false)) | ||
| 258 | + socket.once("connect", () => { | ||
| 259 | + socket.destroy() | ||
| 260 | + resolve(true) | ||
| 261 | + }) | ||
| 262 | + }) | ||
| 263 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,16 +1,24 @@ | |||
| 1 | 1 | import { Level, logger } from "@coder/logger" | |
| 2 | 2 | import * as assert from "assert" | |
| 3 | + import * as fs from "fs-extra" | ||
| 4 | + import * as net from "net" | ||
| 5 | + import * as os from "os" | ||
| 3 | 6 | import * as path from "path" | |
| 4 | - import { parse, setDefaults } from "../src/node/cli" | ||
| 5 | - import { paths } from "../src/node/util" | ||
| 7 | + import { Args, parse, setDefaults, shouldOpenInExistingInstance } from "../src/node/cli" | ||
| 8 | + import { paths, tmpdir } from "../src/node/util" | ||
| 6 | 9 | ||
| 7 | - describe("cli", () => { | ||
| 10 | + type Mutable<T> = { | ||
| 11 | + -readonly [P in keyof T]: T[P] | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + describe("parser", () => { | ||
| 8 | 15 | beforeEach(() => { | |
| 9 | 16 | delete process.env.LOG_LEVEL | |
| 10 | 17 | }) | |
| 11 | 18 | ||
| 12 | 19 | // The parser should not set any defaults so the caller can determine what | |
| 13 | - // values the user actually set. These are set after calling `setDefaults`. | ||
| 20 | + // values the user actually set. These are only set after explicitly calling | ||
| 21 | + // `setDefaults`. | ||
| 14 | 22 | const defaults = { | |
| 15 | 23 | "extensions-dir": path.join(paths.data, "extensions"), | |
| 16 | 24 | "user-data-dir": paths.data, | |
@@ -225,3 +233,76 @@ describe("cli", () => { | |||
| 225 | 233 | }) | |
| 226 | 234 | }) | |
| 227 | 235 | }) | |
| 236 | + | ||
| 237 | + describe("cli", () => { | ||
| 238 | + let args: Mutable<Args> = { _: [] } | ||
| 239 | + const testDir = path.join(tmpdir, "tests/cli") | ||
| 240 | + const vscodeIpcPath = path.join(os.tmpdir(), "vscode-ipc") | ||
| 241 | + | ||
| 242 | + before(async () => { | ||
| 243 | + await fs.remove(testDir) | ||
| 244 | + await fs.mkdirp(testDir) | ||
| 245 | + }) | ||
| 246 | + | ||
| 247 | + beforeEach(async () => { | ||
| 248 | + delete process.env.VSCODE_IPC_HOOK_CLI | ||
| 249 | + args = { _: [] } | ||
| 250 | + await fs.remove(vscodeIpcPath) | ||
| 251 | + }) | ||
| 252 | + | ||
| 253 | + it("should use existing if inside code-server", async () => { | ||
| 254 | + process.env.VSCODE_IPC_HOOK_CLI = "test" | ||
| 255 | + assert.strictEqual(await shouldOpenInExistingInstance(args), "test") | ||
| 256 | + | ||
| 257 | + args.port = 8081 | ||
| 258 | + args._.push("./file") | ||
| 259 | + assert.strictEqual(await shouldOpenInExistingInstance(args), "test") | ||
| 260 | + }) | ||
| 261 | + | ||
| 262 | + it("should use existing if --reuse-window is set", async () => { | ||
| 263 | + args["reuse-window"] = true | ||
| 264 | + assert.strictEqual(await shouldOpenInExistingInstance(args), undefined) | ||
| 265 | + | ||
| 266 | + await fs.writeFile(vscodeIpcPath, "test") | ||
| 267 | + assert.strictEqual(await shouldOpenInExistingInstance(args), "test") | ||
| 268 | + | ||
| 269 | + args.port = 8081 | ||
| 270 | + assert.strictEqual(await shouldOpenInExistingInstance(args), "test") | ||
| 271 | + }) | ||
| 272 | + | ||
| 273 | + it("should use existing if --new-window is set", async () => { | ||
| 274 | + args["new-window"] = true | ||
| 275 | + assert.strictEqual(await shouldOpenInExistingInstance(args), undefined) | ||
| 276 | + | ||
| 277 | + await fs.writeFile(vscodeIpcPath, "test") | ||
| 278 | + assert.strictEqual(await shouldOpenInExistingInstance(args), "test") | ||
| 279 | + | ||
| 280 | + args.port = 8081 | ||
| 281 | + assert.strictEqual(await shouldOpenInExistingInstance(args), "test") | ||
| 282 | + }) | ||
| 283 | + | ||
| 284 | + it("should use existing if no unrelated flags are set, has positional, and socket is active", async () => { | ||
| 285 | + assert.strictEqual(await shouldOpenInExistingInstance(args), undefined) | ||
| 286 | + | ||
| 287 | + args._.push("./file") | ||
| 288 | + assert.strictEqual(await shouldOpenInExistingInstance(args), undefined) | ||
| 289 | + | ||
| 290 | + const socketPath = path.join(testDir, "socket") | ||
| 291 | + await fs.writeFile(vscodeIpcPath, socketPath) | ||
| 292 | + assert.strictEqual(await shouldOpenInExistingInstance(args), undefined) | ||
| 293 | + | ||
| 294 | + await new Promise((resolve) => { | ||
| 295 | + const server = net.createServer(() => { | ||
| 296 | + // Close after getting the first connection. | ||
| 297 | + server.close() | ||
| 298 | + }) | ||
| 299 | + server.once("listening", () => resolve(server)) | ||
| 300 | + server.listen(socketPath) | ||
| 301 | + }) | ||
| 302 | + | ||
| 303 | + assert.strictEqual(await shouldOpenInExistingInstance(args), socketPath) | ||
| 304 | + | ||
| 305 | + args.port = 8081 | ||
| 306 | + assert.strictEqual(await shouldOpenInExistingInstance(args), undefined) | ||
| 307 | + }) | ||
| 308 | + }) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments