Skip to content

Commit 3e8f596

Browse files
author
Calvinn Ng
committed
rename Continue to Ahrefs-Continue and fix errors
1 parent aa77908 commit 3e8f596

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1134
-2213
lines changed

core/config/ConfigHandler.ts

Lines changed: 122 additions & 264 deletions
Original file line numberDiff line numberDiff line change
@@ -1,273 +1,131 @@
1-
import { ControlPlaneClient } from "../control-plane/client.js";
21
import {
3-
BrowserSerializedContinueConfig,
4-
ContinueConfig,
5-
IContextProvider,
6-
IDE,
7-
IdeSettings,
8-
ILLM,
9-
} from "../index.js";
10-
import { GlobalContext } from "../util/GlobalContext.js";
11-
import { finalToBrowserConfig } from "./load.js";
12-
import ControlPlaneProfileLoader from "./profile/ControlPlaneProfileLoader.js";
13-
import { IProfileLoader } from "./profile/IProfileLoader.js";
14-
import LocalProfileLoader from "./profile/LocalProfileLoader.js";
15-
16-
export interface ProfileDescription {
17-
title: string;
18-
id: string;
19-
}
20-
21-
// Separately manages saving/reloading each profile
22-
class ProfileLifecycleManager {
23-
private savedConfig: ContinueConfig | undefined;
24-
private savedBrowserConfig?: BrowserSerializedContinueConfig;
25-
private pendingConfigPromise?: Promise<ContinueConfig>;
26-
27-
constructor(private readonly profileLoader: IProfileLoader) {}
28-
29-
get profileId() {
30-
return this.profileLoader.profileId;
31-
}
32-
33-
get profileTitle() {
34-
return this.profileLoader.profileTitle;
35-
}
36-
37-
get profileDescription(): ProfileDescription {
38-
return {
39-
title: this.profileTitle,
40-
id: this.profileId,
41-
};
42-
}
43-
44-
clearConfig() {
45-
this.savedConfig = undefined;
46-
this.savedBrowserConfig = undefined;
47-
this.pendingConfigPromise = undefined;
48-
}
49-
50-
// Clear saved config and reload
51-
reloadConfig(): Promise<ContinueConfig> {
52-
this.savedConfig = undefined;
53-
this.savedBrowserConfig = undefined;
54-
this.pendingConfigPromise = undefined;
55-
56-
return this.profileLoader.doLoadConfig();
57-
}
58-
59-
async loadConfig(
60-
additionalContextProviders: IContextProvider[],
61-
): Promise<ContinueConfig> {
62-
// If we already have a config, return it
63-
if (this.savedConfig) {
64-
return this.savedConfig;
65-
} else if (this.pendingConfigPromise) {
66-
return this.pendingConfigPromise;
2+
BrowserSerializedContinueConfig,
3+
ContinueConfig,
4+
ContinueRcJson,
5+
IContextProvider,
6+
IDE,
7+
IdeSettings,
8+
ILLM,
9+
} from "../index.js";
10+
import { Telemetry } from "../util/posthog.js";
11+
import { IConfigHandler } from "./IConfigHandler.js";
12+
import { finalToBrowserConfig, loadFullConfigNode } from "./load.js";
13+
14+
export class ConfigHandler implements IConfigHandler {
15+
private savedConfig: ContinueConfig | undefined;
16+
private savedBrowserConfig?: BrowserSerializedContinueConfig;
17+
private additionalContextProviders: IContextProvider[] = [];
18+
19+
constructor(
20+
private readonly ide: IDE,
21+
private ideSettingsPromise: Promise<IdeSettings>,
22+
private readonly writeLog: (text: string) => Promise<void>,
23+
) {
24+
this.ide = ide;
25+
this.ideSettingsPromise = ideSettingsPromise;
26+
this.writeLog = writeLog;
27+
try {
28+
this.loadConfig();
29+
} catch (e) {
30+
console.error("Failed to load config: ", e);
31+
}
32+
}
33+
34+
updateIdeSettings(ideSettings: IdeSettings) {
35+
this.ideSettingsPromise = Promise.resolve(ideSettings);
36+
this.reloadConfig();
6737
}
68-
69-
// Set pending config promise
70-
this.pendingConfigPromise = new Promise(async (resolve, reject) => {
71-
const newConfig = await this.profileLoader.doLoadConfig();
72-
73-
// Add registered context providers
74-
newConfig.contextProviders = (newConfig.contextProviders ?? []).concat(
75-
additionalContextProviders,
76-
);
77-
78-
this.savedConfig = newConfig;
79-
resolve(newConfig);
80-
});
81-
82-
// Wait for the config promise to resolve
83-
this.savedConfig = await this.pendingConfigPromise;
84-
this.pendingConfigPromise = undefined;
85-
return this.savedConfig;
86-
}
87-
88-
async getSerializedConfig(
89-
additionalContextProviders: IContextProvider[],
90-
): Promise<BrowserSerializedContinueConfig> {
91-
if (!this.savedBrowserConfig) {
92-
const continueConfig = await this.loadConfig(additionalContextProviders);
93-
this.savedBrowserConfig = finalToBrowserConfig(continueConfig);
38+
39+
private updateListeners: ((newConfig: ContinueConfig) => void)[] = [];
40+
onConfigUpdate(listener: (newConfig: ContinueConfig) => void) {
41+
this.updateListeners.push(listener);
9442
}
95-
return this.savedBrowserConfig;
96-
}
97-
}
98-
99-
export class ConfigHandler {
100-
private readonly globalContext = new GlobalContext();
101-
private additionalContextProviders: IContextProvider[] = [];
102-
private profiles: ProfileLifecycleManager[];
103-
private selectedProfileId: string;
104-
105-
// This will be the local profile
106-
private get fallbackProfile() {
107-
return this.profiles[0];
108-
}
109-
110-
get currentProfile() {
111-
return (
112-
this.profiles.find((p) => p.profileId === this.selectedProfileId) ??
113-
this.fallbackProfile
114-
);
115-
}
116-
117-
get inactiveProfiles() {
118-
return this.profiles.filter((p) => p.profileId !== this.selectedProfileId);
119-
}
120-
121-
constructor(
122-
private readonly ide: IDE,
123-
private ideSettingsPromise: Promise<IdeSettings>,
124-
private readonly writeLog: (text: string) => Promise<void>,
125-
private readonly controlPlaneClient: ControlPlaneClient,
126-
) {
127-
this.ide = ide;
128-
this.ideSettingsPromise = ideSettingsPromise;
129-
this.writeLog = writeLog;
130-
131-
// Set local profile as default
132-
const localProfileLoader = new LocalProfileLoader(
133-
ide,
134-
ideSettingsPromise,
135-
writeLog,
136-
);
137-
this.profiles = [new ProfileLifecycleManager(localProfileLoader)];
138-
this.selectedProfileId = localProfileLoader.profileId;
139-
140-
// Always load local profile immediately in case control plane doesn't load
141-
try {
142-
this.loadConfig();
143-
} catch (e) {
144-
console.error("Failed to load config: ", e);
43+
44+
async reloadConfig() {
45+
this.savedConfig = undefined;
46+
this.savedBrowserConfig = undefined;
47+
this._pendingConfigPromise = undefined;
48+
49+
const newConfig = await this.loadConfig();
50+
51+
for (const listener of this.updateListeners) {
52+
listener(newConfig);
53+
}
54+
}
55+
56+
async getSerializedConfig(): Promise<BrowserSerializedContinueConfig> {
57+
if (!this.savedBrowserConfig) {
58+
this.savedConfig = await this.loadConfig();
59+
this.savedBrowserConfig = finalToBrowserConfig(this.savedConfig);
60+
}
61+
return this.savedBrowserConfig;
14562
}
146-
147-
// Load control plane profiles
148-
// TODO
149-
// Get the profiles and create their lifecycle managers
150-
this.controlPlaneClient.listWorkspaces().then(async (workspaces) => {
151-
workspaces.forEach((workspace) => {
152-
const profileLoader = new ControlPlaneProfileLoader(
153-
workspace.id,
154-
workspace.name,
155-
this.controlPlaneClient,
156-
ide,
157-
ideSettingsPromise,
158-
writeLog,
159-
this.reloadConfig.bind(this),
63+
64+
private _pendingConfigPromise?: Promise<ContinueConfig>;
65+
async loadConfig(): Promise<ContinueConfig> {
66+
if (this.savedConfig) {
67+
return this.savedConfig;
68+
} else if (this._pendingConfigPromise) {
69+
return this._pendingConfigPromise;
70+
}
71+
72+
this._pendingConfigPromise = new Promise(async (resolve, reject) => {
73+
let workspaceConfigs: ContinueRcJson[] = [];
74+
try {
75+
workspaceConfigs = await this.ide.getWorkspaceConfigs();
76+
} catch (e) {
77+
console.warn("Failed to load workspace configs");
78+
}
79+
80+
const ideInfo = await this.ide.getIdeInfo();
81+
const uniqueId = await this.ide.getUniqueId();
82+
const ideSettings = await this.ideSettingsPromise;
83+
84+
const newConfig = await loadFullConfigNode(
85+
this.ide,
86+
workspaceConfigs,
87+
ideSettings,
88+
ideInfo.ideType,
89+
uniqueId,
90+
this.writeLog,
16091
);
161-
this.profiles.push(new ProfileLifecycleManager(profileLoader));
162-
});
163-
164-
this.notifyProfileListeners(
165-
this.profiles.map((profile) => profile.profileDescription),
166-
);
167-
168-
// Check the last selected workspace, and reload if it isn't local
169-
const workspaceId = await this.getWorkspaceId();
170-
const lastSelectedWorkspaceIds =
171-
this.globalContext.get("lastSelectedProfileForWorkspace") ?? {};
172-
const selectedWorkspaceId = lastSelectedWorkspaceIds[workspaceId];
173-
if (selectedWorkspaceId) {
174-
this.selectedProfileId = selectedWorkspaceId;
175-
this.loadConfig();
176-
} else {
177-
// Otherwise we stick with local profile, and record choice
178-
lastSelectedWorkspaceIds[workspaceId] = this.selectedProfileId;
179-
this.globalContext.update(
180-
"lastSelectedProfileForWorkspace",
181-
lastSelectedWorkspaceIds,
92+
newConfig.allowAnonymousTelemetry =
93+
newConfig.allowAnonymousTelemetry &&
94+
(await this.ide.isTelemetryEnabled());
95+
96+
// Setup telemetry only after (and if) we know it is enabled
97+
await Telemetry.setup(
98+
newConfig.allowAnonymousTelemetry ?? true,
99+
await this.ide.getUniqueId(),
100+
ideInfo.extensionVersion,
182101
);
183-
}
184-
});
185-
}
186-
187-
async setSelectedProfile(profileId: string) {
188-
this.selectedProfileId = profileId;
189-
const newConfig = await this.loadConfig();
190-
this.notifyConfigListerners(newConfig);
191-
const selectedProfiles =
192-
this.globalContext.get("lastSelectedProfileForWorkspace") ?? {};
193-
selectedProfiles[await this.getWorkspaceId()] = profileId;
194-
this.globalContext.update(
195-
"lastSelectedProfileForWorkspace",
196-
selectedProfiles,
197-
);
198-
}
199-
200-
// A unique ID for the current workspace, built from folder names
201-
private async getWorkspaceId(): Promise<string> {
202-
const dirs = await this.ide.getWorkspaceDirs();
203-
return dirs.join("&");
204-
}
205-
206-
// Automatically refresh config when Continue-related IDE (e.g. VS Code) settings are changed
207-
updateIdeSettings(ideSettings: IdeSettings) {
208-
this.ideSettingsPromise = Promise.resolve(ideSettings);
209-
this.reloadConfig();
210-
}
211-
212-
private profilesListeners: ((profiles: ProfileDescription[]) => void)[] = [];
213-
onDidChangeAvailableProfiles(
214-
listener: (profiles: ProfileDescription[]) => void,
215-
) {
216-
this.profilesListeners.push(listener);
217-
}
218-
219-
private notifyProfileListeners(profiles: ProfileDescription[]) {
220-
for (const listener of this.profilesListeners) {
221-
listener(profiles);
102+
103+
(newConfig.contextProviders ?? []).push(
104+
...this.additionalContextProviders,
105+
);
106+
107+
this.savedConfig = newConfig;
108+
resolve(newConfig);
109+
});
110+
111+
this.savedConfig = await this._pendingConfigPromise;
112+
this._pendingConfigPromise = undefined;
113+
return this.savedConfig;
222114
}
223-
}
224-
225-
private notifyConfigListerners(newConfig: ContinueConfig) {
226-
// Notify listeners that config changed
227-
for (const listener of this.updateListeners) {
228-
listener(newConfig);
115+
116+
async llmFromTitle(title?: string): Promise<ILLM> {
117+
const config = await this.loadConfig();
118+
const model =
119+
config.models.find((m) => m.title === title) || config.models[0];
120+
if (!model) {
121+
throw new Error("No model found");
122+
}
123+
124+
return model;
229125
}
230-
}
231-
232-
private updateListeners: ((newConfig: ContinueConfig) => void)[] = [];
233-
onConfigUpdate(listener: (newConfig: ContinueConfig) => void) {
234-
this.updateListeners.push(listener);
235-
}
236-
237-
async reloadConfig() {
238-
// TODO: this isn't right, there are two different senses in which you want to "reload"
239-
const newConfig = await this.currentProfile.reloadConfig();
240-
this.inactiveProfiles.forEach((profile) => profile.clearConfig());
241-
this.notifyConfigListerners(newConfig);
242-
}
243-
244-
getSerializedConfig(): Promise<BrowserSerializedContinueConfig> {
245-
return this.currentProfile.getSerializedConfig(
246-
this.additionalContextProviders,
247-
);
248-
}
249-
250-
listProfiles(): ProfileDescription[] {
251-
return this.profiles.map((p) => p.profileDescription);
252-
}
253-
254-
async loadConfig(): Promise<ContinueConfig> {
255-
return this.currentProfile.loadConfig(this.additionalContextProviders);
256-
}
257-
258-
async llmFromTitle(title?: string): Promise<ILLM> {
259-
const config = await this.loadConfig();
260-
const model =
261-
config.models.find((m) => m.title === title) || config.models[0];
262-
if (!model) {
263-
throw new Error("No model found");
126+
127+
registerCustomContextProvider(contextProvider: IContextProvider) {
128+
this.additionalContextProviders.push(contextProvider);
129+
this.reloadConfig();
264130
}
265-
266-
return model;
267-
}
268-
269-
registerCustomContextProvider(contextProvider: IContextProvider) {
270-
this.additionalContextProviders.push(contextProvider);
271-
this.reloadConfig();
272-
}
273-
}
131+
}

0 commit comments

Comments
 (0)