Skip to content

Commit a068eac

Browse files
committed
Support global settings
1 parent e75c0a3 commit a068eac

File tree

6 files changed

+280
-53
lines changed

6 files changed

+280
-53
lines changed

src/SettingsManager.ts

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,78 @@ export class SettingsManager {
1919

2020
private context: vscode.ExtensionContext;
2121
private extensionName: string;
22+
private previousValues: Map<
23+
string,
24+
{ global?: unknown; workspace?: unknown; workspaceFolder?: unknown }
25+
> = new Map<
26+
string,
27+
{ global?: unknown; workspace?: unknown; workspaceFolder?: unknown }
28+
>();
2229

2330
constructor(context: vscode.ExtensionContext) {
2431
this.context = context;
2532
this.extensionName = (context.extension.packageJSON as PackageJSON).name;
33+
this.captureCurrentValues();
2634
}
2735

28-
async getSetting<T>(key: string): Promise<T | undefined> {
36+
private captureCurrentValues(): void {
37+
const config = vscode.workspace.getConfiguration(this.extensionName);
38+
39+
for (const settingDef of SettingsManager.SETTING_DEFINITIONS) {
40+
if (!settingDef.secret) {
41+
const inspection = config.inspect(settingDef.key);
42+
this.previousValues.set(settingDef.key, {
43+
global: inspection?.globalValue,
44+
workspace: inspection?.workspaceValue,
45+
workspaceFolder: inspection?.workspaceFolderValue
46+
});
47+
}
48+
}
49+
}
50+
51+
detectChangedScope(keys: string[]): vscode.ConfigurationTarget | undefined {
52+
const config = vscode.workspace.getConfiguration(this.extensionName);
53+
54+
for (const key of keys) {
55+
const settingDef = SettingsManager.SETTING_DEFINITIONS.find(
56+
(s) => s.key === key
57+
);
58+
59+
if (!settingDef || settingDef.secret) {
60+
continue;
61+
}
62+
63+
const inspection = config.inspect(key);
64+
const previousValue = this.previousValues.get(key);
65+
66+
if (!previousValue) {
67+
this.captureCurrentValues();
68+
continue;
69+
}
70+
71+
if (inspection?.workspaceFolderValue !== previousValue.workspaceFolder) {
72+
this.captureCurrentValues();
73+
return vscode.ConfigurationTarget.WorkspaceFolder;
74+
}
75+
76+
if (inspection?.workspaceValue !== previousValue.workspace) {
77+
this.captureCurrentValues();
78+
return vscode.ConfigurationTarget.Workspace;
79+
}
80+
81+
if (inspection?.globalValue !== previousValue.global) {
82+
this.captureCurrentValues();
83+
return vscode.ConfigurationTarget.Global;
84+
}
85+
}
86+
87+
this.captureCurrentValues();
88+
}
89+
90+
async getSetting<T>(
91+
key: string,
92+
target?: vscode.ConfigurationTarget
93+
): Promise<T | undefined> {
2994
const settingDef = SettingsManager.SETTING_DEFINITIONS.find(
3095
(s) => s.key === key
3196
);
@@ -41,11 +106,28 @@ export class SettingsManager {
41106
return value ? (JSON.parse(value) as T) : undefined;
42107
} else {
43108
const config = vscode.workspace.getConfiguration(this.extensionName);
109+
110+
if (target !== undefined) {
111+
const inspection = config.inspect<T>(key);
112+
switch (target) {
113+
case vscode.ConfigurationTarget.Global:
114+
return inspection?.globalValue;
115+
case vscode.ConfigurationTarget.Workspace:
116+
return inspection?.workspaceValue;
117+
case vscode.ConfigurationTarget.WorkspaceFolder:
118+
return inspection?.workspaceFolderValue;
119+
}
120+
}
121+
44122
return config.get<T>(key);
45123
}
46124
}
47125

48-
async setSetting(key: string, value: unknown): Promise<void> {
126+
async setSetting(
127+
key: string,
128+
value: unknown,
129+
target?: vscode.ConfigurationTarget
130+
): Promise<void> {
49131
const settingDef = SettingsManager.SETTING_DEFINITIONS.find(
50132
(s) => s.key === key
51133
);
@@ -61,7 +143,7 @@ export class SettingsManager {
61143
);
62144
} else {
63145
const config = vscode.workspace.getConfiguration(this.extensionName);
64-
await config.update(key, value);
146+
await config.update(key, value, target);
65147
}
66148
}
67149
}

src/extension.ts

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,8 @@ const isPortAvailable = (port: number): Promise<boolean> => {
3737
};
3838

3939
async function initializeClient(
40-
context: vscode.ExtensionContext
40+
settingsManager: SettingsManager
4141
): Promise<void> {
42-
const settingsManager = new SettingsManager(context);
43-
4442
const url = await settingsManager.getSetting<string>("url");
4543
const token = await settingsManager.getSetting<string>("token");
4644
const login = await settingsManager.getSetting<string>("login");
@@ -75,7 +73,8 @@ export async function activate(context: vscode.ExtensionContext) {
7573
}
7674
);
7775

78-
await initializeClient(context);
76+
const settingsManager = new SettingsManager(context);
77+
await initializeClient(settingsManager);
7978

8079
// Listen for configuration changes
8180
const configChangeListener = vscode.workspace.onDidChangeConfiguration(
@@ -87,23 +86,50 @@ export async function activate(context: vscode.ExtensionContext) {
8786
event.affectsConfiguration(`${extensionName}.login`) ||
8887
event.affectsConfiguration(`${extensionName}.password`)
8988
) {
90-
const settingsManager = new SettingsManager(context);
91-
await settingsManager.setSetting("copySettingsToMcp", false);
89+
const changedScope = settingsManager.detectChangedScope([
90+
"url",
91+
"token",
92+
"login",
93+
"password"
94+
]);
95+
96+
const copySettingsToMcp = await settingsManager.getSetting<boolean>(
97+
"copySettingsToMcp",
98+
changedScope
99+
);
92100

93-
await initializeClient(context);
101+
if (changedScope && copySettingsToMcp) {
102+
await settingsManager.setSetting(
103+
"copySettingsToMcp",
104+
false,
105+
changedScope
106+
);
107+
}
108+
109+
await initializeClient(settingsManager);
94110
}
95111

96112
// Update MCP server configuration
97113
if (event.affectsConfiguration(`${extensionName}.copySettingsToMcp`)) {
98-
const settingsManager = new SettingsManager(context);
99-
const url = await settingsManager.getSetting<string>("url");
100-
const token = await settingsManager.getSetting<string>("token");
101-
const copySettingsToMcp =
102-
await settingsManager.getSetting<boolean>("copySettingsToMcp");
114+
const changedScope = settingsManager.detectChangedScope([
115+
"copySettingsToMcp"
116+
]);
117+
const url = await settingsManager.getSetting<string>(
118+
"url",
119+
changedScope
120+
);
121+
const token = await settingsManager.getSetting<string>(
122+
"token",
123+
changedScope
124+
);
125+
const copySettingsToMcp = await settingsManager.getSetting<boolean>(
126+
"copySettingsToMcp",
127+
changedScope
128+
);
103129

104-
if (copySettingsToMcp && url && token) {
130+
if (changedScope && copySettingsToMcp && url && token) {
105131
try {
106-
registerMcpServer(url, token);
132+
registerMcpServer(url, token, changedScope);
107133
} catch (error) {
108134
// eslint-disable-next-line no-console
109135
console.error("MCP server registration failed:", error);

src/ides/getGlobalMcpFolderPath.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os from "os";
2+
import path from "path";
3+
import { getGlobalSettingsFolderPath } from "./getGlobalSettingsFolderPath";
4+
5+
export const getGlobalMcpFolderPath = (ideName: string): string => {
6+
const homeDir = os.homedir();
7+
8+
switch (ideName) {
9+
case "Cursor":
10+
return path.join(homeDir, ".cursor");
11+
case "Visual Studio Code":
12+
return getGlobalSettingsFolderPath(ideName);
13+
case "Windsurf":
14+
return path.join(homeDir, ".codeium/windsurf");
15+
default:
16+
throw new Error(`Unsupported IDE: ${ideName}`);
17+
}
18+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os from "os";
2+
import path from "path";
3+
4+
const getIdeFolderName = (ideName: string): string | undefined => {
5+
switch (ideName) {
6+
case "Cursor":
7+
return "Cursor";
8+
case "Visual Studio Code":
9+
return "Code";
10+
case "Windsurf":
11+
return "Windsurf";
12+
}
13+
};
14+
15+
export const getGlobalSettingsFolderPath = (ideName: string): string => {
16+
const platform = os.platform();
17+
const homeDir = os.homedir();
18+
const ideFolderName = getIdeFolderName(ideName);
19+
20+
// Source: https://code.visualstudio.com/docs/configure/settings#_user-settingsjson-location
21+
switch (platform) {
22+
case "win32": {
23+
const appDataDir = process.env.APPDATA;
24+
if (!appDataDir) {
25+
throw new Error("APPDATA environment variable is not set");
26+
}
27+
return path.join(appDataDir, `${ideFolderName}\\User`);
28+
}
29+
case "darwin":
30+
return path.join(
31+
homeDir,
32+
`Library/Application Support/${ideFolderName}/User`
33+
);
34+
case "linux":
35+
return path.join(homeDir, `.config/${ideFolderName}/User`);
36+
default:
37+
throw new Error(`Unsupported platform: ${platform}`);
38+
}
39+
};

src/ides/getIdeFolderUri.ts renamed to src/ides/getWorkspaceSettingsFolderPath.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import path from "path";
12
import vscode from "vscode";
23

3-
export const getIdeFolderUri = (ideName: string): vscode.Uri | undefined => {
4+
export const getWorkspaceSettingsFolderPath = (ideName: string): string => {
45
if (
56
!vscode.workspace.workspaceFolders ||
67
vscode.workspace.workspaceFolders.length === 0
@@ -11,10 +12,11 @@ export const getIdeFolderUri = (ideName: string): vscode.Uri | undefined => {
1112
const workspaceFolder = vscode.workspace.workspaceFolders[0];
1213

1314
switch (ideName) {
14-
case "Visual Studio Code":
15-
return vscode.Uri.joinPath(workspaceFolder.uri, ".vscode");
1615
case "Cursor":
17-
return vscode.Uri.joinPath(workspaceFolder.uri, ".cursor");
16+
return path.join(workspaceFolder.uri.fsPath, ".cursor");
17+
case "Visual Studio Code":
18+
case "Windsurf":
19+
return path.join(workspaceFolder.uri.fsPath, ".vscode");
1820
default:
1921
throw new Error(`Unsupported IDE: ${ideName}`);
2022
}

0 commit comments

Comments
 (0)