Skip to content

Commit f92817a

Browse files
authored
Merge pull request RooCodeInc#1453 from RooVetGit/rooignore_settings
Add setting for whether to include ignored files in lists
2 parents 71f1ab0 + f9d162e commit f92817a

File tree

12 files changed

+178
-21
lines changed

12 files changed

+178
-21
lines changed

src/core/Cline.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2223,11 +2223,13 @@ export class Cline {
22232223
this.consecutiveMistakeCount = 0
22242224
const absolutePath = path.resolve(cwd, relDirPath)
22252225
const [files, didHitLimit] = await listFiles(absolutePath, recursive, 200)
2226+
const { showRooIgnoredFiles } = (await this.providerRef.deref()?.getState()) ?? {}
22262227
const result = formatResponse.formatFilesList(
22272228
absolutePath,
22282229
files,
22292230
didHitLimit,
22302231
this.rooIgnoreController,
2232+
showRooIgnoredFiles ?? true,
22312233
)
22322234
const completeMessage = JSON.stringify({
22332235
...sharedMessageProps,
@@ -3626,7 +3628,14 @@ export class Cline {
36263628
details += "(Desktop files not shown automatically. Use list_files to explore if needed.)"
36273629
} else {
36283630
const [files, didHitLimit] = await listFiles(cwd, true, 200)
3629-
const result = formatResponse.formatFilesList(cwd, files, didHitLimit, this.rooIgnoreController)
3631+
const { showRooIgnoredFiles } = (await this.providerRef.deref()?.getState()) ?? {}
3632+
const result = formatResponse.formatFilesList(
3633+
cwd,
3634+
files,
3635+
didHitLimit,
3636+
this.rooIgnoreController,
3637+
showRooIgnoredFiles,
3638+
)
36303639
details += result
36313640
}
36323641
}

src/core/prompts/__tests__/responses-rooignore.test.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ describe("RooIgnore Response Formatting", () => {
9696
]
9797

9898
// Format with controller
99-
const result = formatResponse.formatFilesList(TEST_CWD, files, false, controller as any)
99+
const result = formatResponse.formatFilesList(TEST_CWD, files, false, controller as any, true)
100100

101101
// Should contain each file
102102
expect(result).toContain("src/app.ts")
@@ -112,6 +112,55 @@ describe("RooIgnore Response Formatting", () => {
112112
expect(result).not.toContain(`${LOCK_TEXT_SYMBOL} README.md`)
113113
})
114114

115+
/**
116+
* Tests formatFilesList when showRooIgnoredFiles is set to false
117+
*/
118+
it("should hide ignored files when showRooIgnoredFiles is false", async () => {
119+
// Create controller
120+
const controller = new RooIgnoreController(TEST_CWD)
121+
await controller.initialize()
122+
123+
// Mock validateAccess to control which files are ignored
124+
controller.validateAccess = jest.fn().mockImplementation((filePath: string) => {
125+
// Only allow files not matching these patterns
126+
return (
127+
!filePath.includes("node_modules") && !filePath.includes(".git") && !filePath.includes("secrets/")
128+
)
129+
})
130+
131+
// Files list with mixed allowed/ignored files
132+
const files = [
133+
"src/app.ts", // allowed
134+
"node_modules/package.json", // ignored
135+
"README.md", // allowed
136+
".git/HEAD", // ignored
137+
"secrets/keys.json", // ignored
138+
]
139+
140+
// Format with controller and showRooIgnoredFiles = false
141+
const result = formatResponse.formatFilesList(
142+
TEST_CWD,
143+
files,
144+
false,
145+
controller as any,
146+
false, // showRooIgnoredFiles = false
147+
)
148+
149+
// Should contain allowed files
150+
expect(result).toContain("src/app.ts")
151+
expect(result).toContain("README.md")
152+
153+
// Should NOT contain ignored files (even with lock symbols)
154+
expect(result).not.toContain("node_modules/package.json")
155+
expect(result).not.toContain(".git/HEAD")
156+
expect(result).not.toContain("secrets/keys.json")
157+
158+
// Double-check with regex to ensure no form of these filenames appears
159+
expect(result).not.toMatch(/node_modules\/package\.json/i)
160+
expect(result).not.toMatch(/\.git\/HEAD/i)
161+
expect(result).not.toMatch(/secrets\/keys\.json/i)
162+
})
163+
115164
/**
116165
* Tests formatFilesList handles truncation correctly with RooIgnoreController
117166
*/
@@ -126,6 +175,7 @@ describe("RooIgnore Response Formatting", () => {
126175
["file1.txt", "file2.txt"],
127176
true, // didHitLimit = true
128177
controller as any,
178+
true,
129179
)
130180

131181
// Should contain truncation message (case-insensitive check)
@@ -142,7 +192,7 @@ describe("RooIgnore Response Formatting", () => {
142192
await controller.initialize()
143193

144194
// Format with empty files array
145-
const result = formatResponse.formatFilesList(TEST_CWD, [], false, controller as any)
195+
const result = formatResponse.formatFilesList(TEST_CWD, [], false, controller as any, true)
146196

147197
// Should show "No files found"
148198
expect(result).toBe("No files found.")

src/core/prompts/responses.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ Otherwise, if you have not completed the task and do not need additional informa
6060
absolutePath: string,
6161
files: string[],
6262
didHitLimit: boolean,
63-
rooIgnoreController?: RooIgnoreController,
63+
rooIgnoreController: RooIgnoreController | undefined,
64+
showRooIgnoredFiles: boolean,
6465
): string => {
6566
const sorted = files
6667
.map((file) => {
@@ -90,20 +91,29 @@ Otherwise, if you have not completed the task and do not need additional informa
9091
return aParts.length - bParts.length
9192
})
9293

93-
const rooIgnoreParsed = rooIgnoreController
94-
? sorted.map((filePath) => {
95-
// path is relative to absolute path, not cwd
96-
// validateAccess expects either path relative to cwd or absolute path
97-
// otherwise, for validating against ignore patterns like "assets/icons", we would end up with just "icons", which would result in the path not being ignored.
98-
const absoluteFilePath = path.resolve(absolutePath, filePath)
99-
const isIgnored = !rooIgnoreController.validateAccess(absoluteFilePath)
100-
if (isIgnored) {
101-
return LOCK_TEXT_SYMBOL + " " + filePath
94+
let rooIgnoreParsed: string[] = sorted
95+
96+
if (rooIgnoreController) {
97+
rooIgnoreParsed = []
98+
for (const filePath of sorted) {
99+
// path is relative to absolute path, not cwd
100+
// validateAccess expects either path relative to cwd or absolute path
101+
// otherwise, for validating against ignore patterns like "assets/icons", we would end up with just "icons", which would result in the path not being ignored.
102+
const absoluteFilePath = path.resolve(absolutePath, filePath)
103+
const isIgnored = !rooIgnoreController.validateAccess(absoluteFilePath)
104+
105+
if (isIgnored) {
106+
// If file is ignored and we're not showing ignored files, skip it
107+
if (!showRooIgnoredFiles) {
108+
continue
102109
}
103-
104-
return filePath
105-
})
106-
: sorted
110+
// Otherwise, mark it with a lock symbol
111+
rooIgnoreParsed.push(LOCK_TEXT_SYMBOL + " " + filePath)
112+
} else {
113+
rooIgnoreParsed.push(filePath)
114+
}
115+
}
116+
}
107117
if (didHitLimit) {
108118
return `${rooIgnoreParsed.join(
109119
"\n",

src/core/webview/ClineProvider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
14681468
await this.updateGlobalState("browserToolEnabled", message.bool ?? true)
14691469
await this.postStateToWebview()
14701470
break
1471+
case "showRooIgnoredFiles":
1472+
await this.updateGlobalState("showRooIgnoredFiles", message.bool ?? true)
1473+
await this.postStateToWebview()
1474+
break
14711475
case "enhancementApiConfigId":
14721476
await this.updateGlobalState("enhancementApiConfigId", message.text)
14731477
await this.postStateToWebview()
@@ -2201,6 +2205,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
22012205
maxOpenTabsContext,
22022206
browserToolEnabled,
22032207
telemetrySetting,
2208+
showRooIgnoredFiles,
22042209
} = await this.getState()
22052210
const telemetryKey = process.env.POSTHOG_API_KEY
22062211
const machineId = vscode.env.machineId
@@ -2262,6 +2267,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
22622267
telemetrySetting,
22632268
telemetryKey,
22642269
machineId,
2270+
showRooIgnoredFiles: showRooIgnoredFiles ?? true,
22652271
}
22662272
}
22672273

@@ -2441,6 +2447,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
24412447
openRouterUseMiddleOutTransform: stateValues.openRouterUseMiddleOutTransform ?? true,
24422448
browserToolEnabled: stateValues.browserToolEnabled ?? true,
24432449
telemetrySetting: stateValues.telemetrySetting || "unset",
2450+
showRooIgnoredFiles: stateValues.showRooIgnoredFiles ?? true,
24442451
}
24452452
}
24462453

src/core/webview/__tests__/ClineProvider.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ describe("ClineProvider", () => {
445445
maxOpenTabsContext: 20,
446446
browserToolEnabled: true,
447447
telemetrySetting: "unset",
448+
showRooIgnoredFiles: true,
448449
}
449450

450451
const message: ExtensionMessage = {
@@ -703,6 +704,27 @@ describe("ClineProvider", () => {
703704
expect(state.browserToolEnabled).toBe(true) // Default value should be true
704705
})
705706

707+
test("handles showRooIgnoredFiles setting", async () => {
708+
await provider.resolveWebviewView(mockWebviewView)
709+
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
710+
711+
// Test showRooIgnoredFiles with true
712+
await messageHandler({ type: "showRooIgnoredFiles", bool: true })
713+
expect(mockContext.globalState.update).toHaveBeenCalledWith("showRooIgnoredFiles", true)
714+
expect(mockPostMessage).toHaveBeenCalled()
715+
716+
// Test showRooIgnoredFiles with false
717+
jest.clearAllMocks() // Clear all mocks including mockContext.globalState.update
718+
await messageHandler({ type: "showRooIgnoredFiles", bool: false })
719+
expect(mockContext.globalState.update).toHaveBeenCalledWith("showRooIgnoredFiles", false)
720+
expect(mockPostMessage).toHaveBeenCalled()
721+
722+
// Verify state includes showRooIgnoredFiles
723+
const state = await provider.getState()
724+
expect(state).toHaveProperty("showRooIgnoredFiles")
725+
expect(state.showRooIgnoredFiles).toBe(true) // Default value should be true
726+
})
727+
706728
test("handles request delay settings messages", async () => {
707729
await provider.resolveWebviewView(mockWebviewView)
708730
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export interface ExtensionState {
140140
telemetrySetting: TelemetrySetting
141141
telemetryKey?: string
142142
machineId?: string
143+
showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings
143144
}
144145

145146
export interface ClineMessage {

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export interface WebviewMessage {
9999
| "humanRelayCancel"
100100
| "browserToolEnabled"
101101
| "telemetrySetting"
102+
| "showRooIgnoredFiles"
102103
text?: string
103104
disabled?: boolean
104105
askResponse?: ClineAskResponse

src/shared/globalState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export const GLOBAL_STATE_KEYS = [
9797
"lmStudioSpeculativeDecodingEnabled",
9898
"lmStudioDraftModelId",
9999
"telemetrySetting",
100+
"showRooIgnoredFiles",
100101
] as const
101102

102103
// Derive the type from the array - creates a union of string literals

webview-ui/src/components/settings/AdvancedSettings.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,25 @@ type AdvancedSettingsProps = HTMLAttributes<HTMLDivElement> & {
1818
maxOpenTabsContext: number
1919
diffEnabled?: boolean
2020
fuzzyMatchThreshold?: number
21+
showRooIgnoredFiles?: boolean
2122
setCachedStateField: SetCachedStateField<
22-
"rateLimitSeconds" | "terminalOutputLimit" | "maxOpenTabsContext" | "diffEnabled" | "fuzzyMatchThreshold"
23+
| "rateLimitSeconds"
24+
| "terminalOutputLimit"
25+
| "maxOpenTabsContext"
26+
| "diffEnabled"
27+
| "fuzzyMatchThreshold"
28+
| "showRooIgnoredFiles"
2329
>
2430
experiments: Record<ExperimentId, boolean>
2531
setExperimentEnabled: SetExperimentEnabled
2632
}
27-
2833
export const AdvancedSettings = ({
2934
rateLimitSeconds,
3035
terminalOutputLimit = TERMINAL_OUTPUT_LIMIT,
3136
maxOpenTabsContext,
3237
diffEnabled,
3338
fuzzyMatchThreshold,
39+
showRooIgnoredFiles,
3440
setCachedStateField,
3541
experiments,
3642
setExperimentEnabled,
@@ -197,6 +203,20 @@ export const AdvancedSettings = ({
197203
</div>
198204
)}
199205
</div>
206+
207+
<div>
208+
<VSCodeCheckbox
209+
checked={showRooIgnoredFiles}
210+
onChange={(e: any) => {
211+
setCachedStateField("showRooIgnoredFiles", e.target.checked)
212+
}}>
213+
<span className="font-medium">Show .rooignore'd files in lists and searches</span>
214+
</VSCodeCheckbox>
215+
<p className="text-vscode-descriptionForeground text-sm mt-0">
216+
When enabled, files matching patterns in .rooignore will be shown in lists with a lock symbol.
217+
When disabled, these files will be completely hidden from file lists and searches.
218+
</p>
219+
</div>
200220
</Section>
201221
</div>
202222
)

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
8282
telemetrySetting,
8383
terminalOutputLimit,
8484
writeDelayMs,
85+
showRooIgnoredFiles,
8586
} = cachedState
8687

8788
// Make sure apiConfiguration is initialized and managed by SettingsView.
@@ -179,6 +180,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
179180
vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds })
180181
vscode.postMessage({ type: "rateLimitSeconds", value: rateLimitSeconds })
181182
vscode.postMessage({ type: "maxOpenTabsContext", value: maxOpenTabsContext })
183+
vscode.postMessage({ type: "showRooIgnoredFiles", bool: showRooIgnoredFiles })
182184
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
183185
vscode.postMessage({ type: "updateExperimental", values: experiments })
184186
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
@@ -400,6 +402,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
400402
maxOpenTabsContext={maxOpenTabsContext}
401403
diffEnabled={diffEnabled}
402404
fuzzyMatchThreshold={fuzzyMatchThreshold}
405+
showRooIgnoredFiles={showRooIgnoredFiles}
403406
setCachedStateField={setCachedStateField}
404407
setExperimentEnabled={setExperimentEnabled}
405408
experiments={experiments}

0 commit comments

Comments
 (0)