Skip to content

Commit d5f522f

Browse files
committed
refactor(content): extract content management to dedicated service
- Move content-related methods (openContent, refreshContent, updateContent, readContent) from ClineProvider to a new ContentManager class. - Instantiate ContentManager in ClineProvider and delegate content operations to it. - Improve separation of concerns by centralizing content file system interactions and state updates within ContentManager.
1 parent 4f233b9 commit d5f522f

File tree

3 files changed

+117
-62
lines changed

3 files changed

+117
-62
lines changed

src/core/webview/ClineProvider.ts

Lines changed: 12 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ import { McpServerManager } from "../../services/mcp/McpServerManager"
5151
import { ShadowCheckpointService } from "../../services/checkpoints/ShadowCheckpointService"
5252
import { CodeIndexManager } from "../../services/code-index/manager"
5353
import type { IndexProgressUpdate } from "../../services/code-index/interfaces/manager"
54-
import { fileExistsAtPath, safeReadFile } from "../../utils/fs"
55-
import { openFile } from "../../integrations/misc/open-file"
54+
import { fileExistsAtPath } from "../../utils/fs"
5655
import { setTtsEnabled, setTtsSpeed } from "../../utils/tts"
5756
import { ContextProxy } from "../config/ContextProxy"
5857
import { ProviderSettingsManager } from "../config/ProviderSettingsManager"
@@ -67,6 +66,7 @@ import { webviewMessageHandler } from "./webviewMessageHandler"
6766
import { WebviewMessage } from "../../shared/WebviewMessage"
6867
import { EMBEDDING_MODEL_PROFILES } from "../../shared/embeddingModels"
6968
import { ProfileValidator } from "../../shared/ProfileValidator"
69+
import { ContentManager } from "../../services/content/ContentManager"
7070

7171
/**
7272
* https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -103,6 +103,7 @@ export class ClineProvider
103103
return this._workspaceTracker
104104
}
105105
protected mcpHub?: McpHub // Change from private to protected
106+
public contentManager: ContentManager // Declare private member
106107

107108
public isViewLaunched = false
108109
public settingsImportedAt?: number
@@ -149,6 +150,14 @@ export class ClineProvider
149150
.catch((error) => {
150151
this.log(`Failed to initialize MCP Hub: ${error}`)
151152
})
153+
154+
// Instantiate ContentManager
155+
this.contentManager = new ContentManager({
156+
log: this.log.bind(this),
157+
postMessageToWebview: this.postMessageToWebview.bind(this),
158+
postStateToWebview: this.postStateToWebview.bind(this),
159+
globalStorageUriFsPath: this.contextProxy.globalStorageUri.fsPath,
160+
})
152161
}
153162

154163
// Adds a new Cline instance to clineStack, marking the start of a new task.
@@ -1009,62 +1018,6 @@ export class ClineProvider
10091018
}
10101019
}
10111020

1012-
// Content
1013-
1014-
async openContent(contentId: string): Promise<void> {
1015-
const filePath = await this.getContentPath(contentId)
1016-
1017-
if (!filePath) {
1018-
this.log(`File path could not be determined for contentId: ${contentId}`)
1019-
return
1020-
}
1021-
1022-
await openFile(filePath, { create: true })
1023-
await vscode.commands.executeCommand("workbench.action.files.revert")
1024-
}
1025-
1026-
async refreshContent(contentId: string): Promise<void> {
1027-
const content = await this.readContent(contentId)
1028-
await this.updateContent(contentId, content)
1029-
this.postMessageToWebview({
1030-
type: "contentRefreshed",
1031-
contentId: contentId,
1032-
success: true, // Assuming success for now
1033-
})
1034-
}
1035-
1036-
async updateContent(contentId: string, content?: string) {
1037-
const filePath = await this.getContentPath(contentId)
1038-
1039-
if (!filePath) {
1040-
this.log(`File path could not be determined for contentId: ${contentId}`)
1041-
return
1042-
}
1043-
1044-
if (content && content.trim()) {
1045-
await fs.writeFile(filePath, content.trim(), "utf-8")
1046-
this.log(`Updated content file: ${filePath}`)
1047-
} else {
1048-
try {
1049-
await fs.unlink(filePath)
1050-
this.log(`Deleted content file: ${filePath}`)
1051-
} catch (error) {
1052-
if (error.code !== "ENOENT") {
1053-
this.log(`Error deleting content file: ${error.message}`)
1054-
throw error
1055-
}
1056-
}
1057-
}
1058-
// Update the webview state
1059-
await this.postStateToWebview()
1060-
}
1061-
1062-
private async readContent(contentId: string): Promise<string> {
1063-
const filePath = await this.getContentPath(contentId)
1064-
1065-
return filePath ? ((await safeReadFile(filePath)) ?? "") : ""
1066-
}
1067-
10681021
// MCP
10691022

10701023
async ensureMcpServersDirectoryExists(): Promise<string> {
@@ -1539,7 +1492,7 @@ export class ClineProvider
15391492
return {
15401493
apiConfiguration: providerSettings,
15411494
lastShownAnnouncementId: stateValues.lastShownAnnouncementId,
1542-
customInstructions: await this.readContent(GlobalContentIds.customInstructions),
1495+
customInstructions: await this.contentManager.readContent(GlobalContentIds.customInstructions),
15431496
apiModelId: stateValues.apiModelId,
15441497
alwaysAllowReadOnly: stateValues.alwaysAllowReadOnly ?? false,
15451498
alwaysAllowReadOnlyOutsideWorkspace: stateValues.alwaysAllowReadOnlyOutsideWorkspace ?? false,

src/core/webview/webviewMessageHandler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,19 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
131131
await provider.initClineWithTask(message.text, message.images)
132132
break
133133
case "customInstructions":
134-
await provider.updateContent(GlobalContentIds.customInstructions, message.text)
134+
await provider.contentManager.updateContent(GlobalContentIds.customInstructions, message.text)
135135
break
136136
case "openContent":
137137
if (message.contentId) {
138138
// Check for contentId instead of filePath
139-
await provider.openContent(message.contentId)
139+
await provider.contentManager.openContent(message.contentId)
140140
} else {
141141
console.error("openContent message missing contentId")
142142
}
143143
break
144144
case "refreshContent":
145145
if (message.contentId) {
146-
await provider.refreshContent(message.contentId)
146+
await provider.contentManager.refreshContent(message.contentId)
147147
} else {
148148
// Handle error or log if contentId is missing
149149
console.error("refreshContent message missing contentId")
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import * as vscode from "vscode"
2+
import * as path from "path"
3+
import fs from "fs/promises"
4+
5+
import { GlobalContentIds } from "../../shared/globalContentIds"
6+
import { GlobalFileNames } from "../../shared/globalFileNames"
7+
import { openFile } from "../../integrations/misc/open-file"
8+
import { safeReadFile } from "../../utils/fs"
9+
import { getSettingsDirectoryPath } from "../../utils/storage" // Need to confirm this import path
10+
import { ExtensionMessage } from "../../shared/ExtensionMessage" // Assuming this type is needed for postMessageToWebview
11+
12+
// Define an interface for the dependencies ContentManager needs from ClineProvider
13+
interface ContentManagerDependencies {
14+
log: (message: string) => void
15+
postMessageToWebview: (message: ExtensionMessage) => Promise<void>
16+
postStateToWebview: () => Promise<void>
17+
globalStorageUriFsPath: string // To replace contextProxy.globalStorageUri.fsPath
18+
}
19+
20+
export class ContentManager {
21+
private readonly log: (message: string) => void
22+
private readonly postMessageToWebview: (message: ExtensionMessage) => Promise<void>
23+
private readonly postStateToWebview: () => Promise<void>
24+
private readonly globalStorageUriFsPath: string
25+
26+
constructor(dependencies: ContentManagerDependencies) {
27+
this.log = dependencies.log
28+
this.postMessageToWebview = dependencies.postMessageToWebview
29+
this.postStateToWebview = dependencies.postStateToWebview
30+
this.globalStorageUriFsPath = dependencies.globalStorageUriFsPath
31+
}
32+
33+
private async ensureSettingsDirectoryExists(): Promise<string> {
34+
const globalStoragePath = this.globalStorageUriFsPath
35+
return getSettingsDirectoryPath(globalStoragePath)
36+
}
37+
38+
private async getContentPath(contentId: string): Promise<string | undefined> {
39+
const settingsDir = await this.ensureSettingsDirectoryExists()
40+
switch (contentId) {
41+
case GlobalContentIds.customInstructions:
42+
return path.join(settingsDir, GlobalFileNames.customInstructions)
43+
default:
44+
this.log(`Unknown contentId: ${contentId}`)
45+
return undefined
46+
}
47+
}
48+
49+
async openContent(contentId: string): Promise<void> {
50+
const filePath = await this.getContentPath(contentId)
51+
52+
if (!filePath) {
53+
this.log(`File path could not be determined for contentId: ${contentId}`)
54+
return
55+
}
56+
57+
await openFile(filePath, { create: true })
58+
await vscode.commands.executeCommand("workbench.action.files.revert")
59+
}
60+
61+
async refreshContent(contentId: string): Promise<void> {
62+
const content = await this.readContent(contentId)
63+
await this.updateContent(contentId, content)
64+
this.postMessageToWebview({
65+
type: "contentRefreshed",
66+
contentId: contentId,
67+
success: true, // Assuming success for now
68+
})
69+
}
70+
71+
async updateContent(contentId: string, content?: string) {
72+
const filePath = await this.getContentPath(contentId)
73+
74+
if (!filePath) {
75+
this.log(`File path could not be determined for contentId: ${contentId}`)
76+
return
77+
}
78+
79+
if (content && content.trim()) {
80+
await fs.writeFile(filePath, content.trim(), "utf-8")
81+
this.log(`Updated content file: ${filePath}`)
82+
} else {
83+
try {
84+
await fs.unlink(filePath)
85+
this.log(`Deleted content file: ${filePath}`)
86+
} catch (error: any) {
87+
if (error.code !== "ENOENT") {
88+
this.log(`Error deleting content file: ${error.message}`)
89+
throw error
90+
}
91+
}
92+
}
93+
// Update the webview state
94+
await this.postStateToWebview()
95+
}
96+
97+
async readContent(contentId: string): Promise<string> {
98+
const filePath = await this.getContentPath(contentId)
99+
100+
return filePath ? ((await safeReadFile(filePath)) ?? "") : ""
101+
}
102+
}

0 commit comments

Comments
 (0)