Skip to content

Commit 85293fe

Browse files
jaggederestclaude
andcommitted
refactor: extract classes into separate files for better organization
- Extract ExtensionDependencies, RemoteEnvironmentHandler, and ExtensionInitializer from extension.ts - Extract all tree item classes from workspacesProvider.ts to workspacesProvider/treeItems.ts - Follow TypeScript camelCase naming conventions for files and directories - Improve code organization and maintainability by following single responsibility principle 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c17f927 commit 85293fe

File tree

6 files changed

+436
-394
lines changed

6 files changed

+436
-394
lines changed

src/extension.ts

Lines changed: 2 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,15 @@
11
"use strict";
2-
import axios, { isAxiosError } from "axios";
3-
import { getErrorMessage } from "coder/site/src/api/errors";
42
import * as module from "module";
53
import * as vscode from "vscode";
64
import { makeCoderSdk, needToken } from "./api";
7-
import { errToStr } from "./api-helper";
85
import { Commands } from "./commands";
9-
import { getErrorDetail } from "./error";
6+
import { ExtensionDependencies } from "./extension/dependencies";
7+
import { ExtensionInitializer } from "./extension/initializer";
108
import { Logger } from "./logger";
11-
import { Remote } from "./remote";
129
import { Storage } from "./storage";
13-
import { DefaultUIProvider } from "./uiProvider";
1410
import { toSafeHost } from "./util";
1511
import { WorkspaceQuery, WorkspaceProvider } from "./workspacesProvider";
1612

17-
class ExtensionDependencies {
18-
public readonly vscodeProposed: typeof vscode;
19-
public readonly remoteSSHExtension: vscode.Extension<unknown> | undefined;
20-
public readonly output: vscode.OutputChannel;
21-
public readonly storage: Storage;
22-
public readonly logger: Logger;
23-
public readonly restClient: ReturnType<typeof makeCoderSdk>;
24-
public readonly uiProvider: DefaultUIProvider;
25-
public readonly commands: Commands;
26-
public readonly myWorkspacesProvider: WorkspaceProvider;
27-
public readonly allWorkspacesProvider: WorkspaceProvider;
28-
29-
private constructor(
30-
vscodeProposed: typeof vscode,
31-
remoteSSHExtension: vscode.Extension<unknown> | undefined,
32-
output: vscode.OutputChannel,
33-
storage: Storage,
34-
logger: Logger,
35-
restClient: ReturnType<typeof makeCoderSdk>,
36-
uiProvider: DefaultUIProvider,
37-
commands: Commands,
38-
myWorkspacesProvider: WorkspaceProvider,
39-
allWorkspacesProvider: WorkspaceProvider,
40-
) {
41-
this.vscodeProposed = vscodeProposed;
42-
this.remoteSSHExtension = remoteSSHExtension;
43-
this.output = output;
44-
this.storage = storage;
45-
this.logger = logger;
46-
this.restClient = restClient;
47-
this.uiProvider = uiProvider;
48-
this.commands = commands;
49-
this.myWorkspacesProvider = myWorkspacesProvider;
50-
this.allWorkspacesProvider = allWorkspacesProvider;
51-
}
52-
53-
static async create(
54-
ctx: vscode.ExtensionContext,
55-
): Promise<ExtensionDependencies> {
56-
// Setup remote SSH extension
57-
const { vscodeProposed, remoteSSHExtension } = setupRemoteSSHExtension();
58-
59-
// Create output channel
60-
const output = vscode.window.createOutputChannel("Coder");
61-
62-
// Initialize infrastructure
63-
const { storage, logger } = await initializeInfrastructure(ctx, output);
64-
65-
// Initialize REST client
66-
const restClient = await initializeRestClient(storage);
67-
68-
// Setup tree views
69-
const { myWorkspacesProvider, allWorkspacesProvider } = setupTreeViews(
70-
restClient,
71-
storage,
72-
);
73-
74-
// Create UI provider and commands
75-
const uiProvider = new DefaultUIProvider(vscodeProposed.window);
76-
const commands = new Commands(
77-
vscodeProposed,
78-
restClient,
79-
storage,
80-
uiProvider,
81-
);
82-
83-
return new ExtensionDependencies(
84-
vscodeProposed,
85-
remoteSSHExtension,
86-
output,
87-
storage,
88-
logger,
89-
restClient,
90-
uiProvider,
91-
commands,
92-
myWorkspacesProvider,
93-
allWorkspacesProvider,
94-
);
95-
}
96-
}
97-
9813
export function setupRemoteSSHExtension(): {
9914
vscodeProposed: typeof vscode;
10015
remoteSSHExtension: vscode.Extension<unknown> | undefined;
@@ -367,153 +282,6 @@ export function registerCommands(
367282
);
368283
}
369284

370-
class RemoteEnvironmentHandler {
371-
private readonly vscodeProposed: typeof vscode;
372-
private readonly remoteSSHExtension: vscode.Extension<unknown> | undefined;
373-
private readonly restClient: ReturnType<typeof makeCoderSdk>;
374-
private readonly storage: Storage;
375-
private readonly commands: Commands;
376-
private readonly extensionMode: vscode.ExtensionMode;
377-
378-
constructor(
379-
deps: ExtensionDependencies,
380-
extensionMode: vscode.ExtensionMode,
381-
) {
382-
this.vscodeProposed = deps.vscodeProposed;
383-
this.remoteSSHExtension = deps.remoteSSHExtension;
384-
this.restClient = deps.restClient;
385-
this.storage = deps.storage;
386-
this.commands = deps.commands;
387-
this.extensionMode = extensionMode;
388-
}
389-
390-
async initialize(): Promise<boolean> {
391-
// Skip if no remote SSH extension or no remote authority
392-
if (!this.remoteSSHExtension || !this.vscodeProposed.env.remoteAuthority) {
393-
return true; // No remote environment to handle
394-
}
395-
396-
const remote = new Remote(
397-
this.vscodeProposed,
398-
this.storage,
399-
this.commands,
400-
this.extensionMode,
401-
);
402-
403-
try {
404-
const details = await remote.setup(
405-
this.vscodeProposed.env.remoteAuthority,
406-
);
407-
if (details) {
408-
// Authenticate the plugin client
409-
this.restClient.setHost(details.url);
410-
this.restClient.setSessionToken(details.token);
411-
}
412-
return true; // Success
413-
} catch (ex) {
414-
await this.handleRemoteError(ex);
415-
// Always close remote session when we fail to open a workspace
416-
await remote.closeRemote();
417-
return false; // Failed
418-
}
419-
}
420-
421-
private async handleRemoteError(error: unknown): Promise<void> {
422-
if (
423-
error &&
424-
typeof error === "object" &&
425-
"x509Err" in error &&
426-
"showModal" in error
427-
) {
428-
const certError = error as {
429-
x509Err?: string;
430-
message?: string;
431-
showModal: (title: string) => Promise<void>;
432-
};
433-
this.storage.writeToCoderOutputChannel(
434-
certError.x509Err || certError.message || "Certificate error",
435-
);
436-
await certError.showModal("Failed to open workspace");
437-
} else if (isAxiosError(error)) {
438-
const msg = getErrorMessage(error, "None");
439-
const detail = getErrorDetail(error) || "None";
440-
const urlString = axios.getUri(error.config);
441-
const method = error.config?.method?.toUpperCase() || "request";
442-
const status = error.response?.status || "None";
443-
const message = `API ${method} to '${urlString}' failed.\nStatus code: ${status}\nMessage: ${msg}\nDetail: ${detail}`;
444-
this.storage.writeToCoderOutputChannel(message);
445-
await this.vscodeProposed.window.showErrorMessage(
446-
"Failed to open workspace",
447-
{
448-
detail: message,
449-
modal: true,
450-
useCustom: true,
451-
},
452-
);
453-
} else {
454-
const message = errToStr(error, "No error message was provided");
455-
this.storage.writeToCoderOutputChannel(message);
456-
await this.vscodeProposed.window.showErrorMessage(
457-
"Failed to open workspace",
458-
{
459-
detail: message,
460-
modal: true,
461-
useCustom: true,
462-
},
463-
);
464-
}
465-
}
466-
}
467-
468-
class ExtensionInitializer {
469-
private readonly deps: ExtensionDependencies;
470-
private readonly ctx: vscode.ExtensionContext;
471-
472-
constructor(deps: ExtensionDependencies, ctx: vscode.ExtensionContext) {
473-
this.deps = deps;
474-
this.ctx = ctx;
475-
}
476-
477-
async initialize(): Promise<void> {
478-
// Register URI handler and commands
479-
this.registerHandlers();
480-
481-
// Handle remote environment if applicable
482-
const remoteHandler = new RemoteEnvironmentHandler(
483-
this.deps,
484-
this.ctx.extensionMode,
485-
);
486-
const remoteHandled = await remoteHandler.initialize();
487-
if (!remoteHandled) {
488-
return; // Exit early if remote setup failed
489-
}
490-
491-
// Initialize authentication
492-
await initializeAuthentication(
493-
this.deps.restClient,
494-
this.deps.storage,
495-
this.deps.myWorkspacesProvider,
496-
this.deps.allWorkspacesProvider,
497-
);
498-
}
499-
500-
private registerHandlers(): void {
501-
// Register URI handler
502-
registerUriHandler(
503-
this.deps.commands,
504-
this.deps.restClient,
505-
this.deps.storage,
506-
);
507-
508-
// Register commands
509-
registerCommands(
510-
this.deps.commands,
511-
this.deps.myWorkspacesProvider,
512-
this.deps.allWorkspacesProvider,
513-
);
514-
}
515-
}
516-
517285
export async function initializeAuthentication(
518286
restClient: ReturnType<typeof makeCoderSdk>,
519287
storage: Storage,

src/extension/dependencies.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import * as vscode from "vscode";
2+
import { makeCoderSdk } from "../api";
3+
import { Commands } from "../commands";
4+
import {
5+
setupRemoteSSHExtension,
6+
initializeInfrastructure,
7+
initializeRestClient,
8+
setupTreeViews,
9+
} from "../extension";
10+
import { Logger } from "../logger";
11+
import { Storage } from "../storage";
12+
import { DefaultUIProvider } from "../uiProvider";
13+
import { WorkspaceProvider } from "../workspacesProvider";
14+
15+
export class ExtensionDependencies {
16+
public readonly vscodeProposed: typeof vscode;
17+
public readonly remoteSSHExtension: vscode.Extension<unknown> | undefined;
18+
public readonly output: vscode.OutputChannel;
19+
public readonly storage: Storage;
20+
public readonly logger: Logger;
21+
public readonly restClient: ReturnType<typeof makeCoderSdk>;
22+
public readonly uiProvider: DefaultUIProvider;
23+
public readonly commands: Commands;
24+
public readonly myWorkspacesProvider: WorkspaceProvider;
25+
public readonly allWorkspacesProvider: WorkspaceProvider;
26+
27+
private constructor(
28+
vscodeProposed: typeof vscode,
29+
remoteSSHExtension: vscode.Extension<unknown> | undefined,
30+
output: vscode.OutputChannel,
31+
storage: Storage,
32+
logger: Logger,
33+
restClient: ReturnType<typeof makeCoderSdk>,
34+
uiProvider: DefaultUIProvider,
35+
commands: Commands,
36+
myWorkspacesProvider: WorkspaceProvider,
37+
allWorkspacesProvider: WorkspaceProvider,
38+
) {
39+
this.vscodeProposed = vscodeProposed;
40+
this.remoteSSHExtension = remoteSSHExtension;
41+
this.output = output;
42+
this.storage = storage;
43+
this.logger = logger;
44+
this.restClient = restClient;
45+
this.uiProvider = uiProvider;
46+
this.commands = commands;
47+
this.myWorkspacesProvider = myWorkspacesProvider;
48+
this.allWorkspacesProvider = allWorkspacesProvider;
49+
}
50+
51+
static async create(
52+
ctx: vscode.ExtensionContext,
53+
): Promise<ExtensionDependencies> {
54+
// Setup remote SSH extension
55+
const { vscodeProposed, remoteSSHExtension } = setupRemoteSSHExtension();
56+
57+
// Create output channel
58+
const output = vscode.window.createOutputChannel("Coder");
59+
60+
// Initialize infrastructure
61+
const { storage, logger } = await initializeInfrastructure(ctx, output);
62+
63+
// Initialize REST client
64+
const restClient = await initializeRestClient(storage);
65+
66+
// Setup tree views
67+
const { myWorkspacesProvider, allWorkspacesProvider } = setupTreeViews(
68+
restClient,
69+
storage,
70+
);
71+
72+
// Create UI provider and commands
73+
const uiProvider = new DefaultUIProvider(vscodeProposed.window);
74+
const commands = new Commands(
75+
vscodeProposed,
76+
restClient,
77+
storage,
78+
uiProvider,
79+
);
80+
81+
return new ExtensionDependencies(
82+
vscodeProposed,
83+
remoteSSHExtension,
84+
output,
85+
storage,
86+
logger,
87+
restClient,
88+
uiProvider,
89+
commands,
90+
myWorkspacesProvider,
91+
allWorkspacesProvider,
92+
);
93+
}
94+
}

0 commit comments

Comments
 (0)