mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Add setting for whether to include ignored files in lists
This commit is contained in:
parent
71f1ab01aa
commit
f9d162e157
12 changed files with 178 additions and 21 deletions
|
|
@ -2223,11 +2223,13 @@ export class Cline {
|
|||
this.consecutiveMistakeCount = 0
|
||||
const absolutePath = path.resolve(cwd, relDirPath)
|
||||
const [files, didHitLimit] = await listFiles(absolutePath, recursive, 200)
|
||||
const { showRooIgnoredFiles } = (await this.providerRef.deref()?.getState()) ?? {}
|
||||
const result = formatResponse.formatFilesList(
|
||||
absolutePath,
|
||||
files,
|
||||
didHitLimit,
|
||||
this.rooIgnoreController,
|
||||
showRooIgnoredFiles ?? true,
|
||||
)
|
||||
const completeMessage = JSON.stringify({
|
||||
...sharedMessageProps,
|
||||
|
|
@ -3626,7 +3628,14 @@ export class Cline {
|
|||
details += "(Desktop files not shown automatically. Use list_files to explore if needed.)"
|
||||
} else {
|
||||
const [files, didHitLimit] = await listFiles(cwd, true, 200)
|
||||
const result = formatResponse.formatFilesList(cwd, files, didHitLimit, this.rooIgnoreController)
|
||||
const { showRooIgnoredFiles } = (await this.providerRef.deref()?.getState()) ?? {}
|
||||
const result = formatResponse.formatFilesList(
|
||||
cwd,
|
||||
files,
|
||||
didHitLimit,
|
||||
this.rooIgnoreController,
|
||||
showRooIgnoredFiles,
|
||||
)
|
||||
details += result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ describe("RooIgnore Response Formatting", () => {
|
|||
]
|
||||
|
||||
// Format with controller
|
||||
const result = formatResponse.formatFilesList(TEST_CWD, files, false, controller as any)
|
||||
const result = formatResponse.formatFilesList(TEST_CWD, files, false, controller as any, true)
|
||||
|
||||
// Should contain each file
|
||||
expect(result).toContain("src/app.ts")
|
||||
|
|
@ -112,6 +112,55 @@ describe("RooIgnore Response Formatting", () => {
|
|||
expect(result).not.toContain(`${LOCK_TEXT_SYMBOL} README.md`)
|
||||
})
|
||||
|
||||
/**
|
||||
* Tests formatFilesList when showRooIgnoredFiles is set to false
|
||||
*/
|
||||
it("should hide ignored files when showRooIgnoredFiles is false", async () => {
|
||||
// Create controller
|
||||
const controller = new RooIgnoreController(TEST_CWD)
|
||||
await controller.initialize()
|
||||
|
||||
// Mock validateAccess to control which files are ignored
|
||||
controller.validateAccess = jest.fn().mockImplementation((filePath: string) => {
|
||||
// Only allow files not matching these patterns
|
||||
return (
|
||||
!filePath.includes("node_modules") && !filePath.includes(".git") && !filePath.includes("secrets/")
|
||||
)
|
||||
})
|
||||
|
||||
// Files list with mixed allowed/ignored files
|
||||
const files = [
|
||||
"src/app.ts", // allowed
|
||||
"node_modules/package.json", // ignored
|
||||
"README.md", // allowed
|
||||
".git/HEAD", // ignored
|
||||
"secrets/keys.json", // ignored
|
||||
]
|
||||
|
||||
// Format with controller and showRooIgnoredFiles = false
|
||||
const result = formatResponse.formatFilesList(
|
||||
TEST_CWD,
|
||||
files,
|
||||
false,
|
||||
controller as any,
|
||||
false, // showRooIgnoredFiles = false
|
||||
)
|
||||
|
||||
// Should contain allowed files
|
||||
expect(result).toContain("src/app.ts")
|
||||
expect(result).toContain("README.md")
|
||||
|
||||
// Should NOT contain ignored files (even with lock symbols)
|
||||
expect(result).not.toContain("node_modules/package.json")
|
||||
expect(result).not.toContain(".git/HEAD")
|
||||
expect(result).not.toContain("secrets/keys.json")
|
||||
|
||||
// Double-check with regex to ensure no form of these filenames appears
|
||||
expect(result).not.toMatch(/node_modules\/package\.json/i)
|
||||
expect(result).not.toMatch(/\.git\/HEAD/i)
|
||||
expect(result).not.toMatch(/secrets\/keys\.json/i)
|
||||
})
|
||||
|
||||
/**
|
||||
* Tests formatFilesList handles truncation correctly with RooIgnoreController
|
||||
*/
|
||||
|
|
@ -126,6 +175,7 @@ describe("RooIgnore Response Formatting", () => {
|
|||
["file1.txt", "file2.txt"],
|
||||
true, // didHitLimit = true
|
||||
controller as any,
|
||||
true,
|
||||
)
|
||||
|
||||
// Should contain truncation message (case-insensitive check)
|
||||
|
|
@ -142,7 +192,7 @@ describe("RooIgnore Response Formatting", () => {
|
|||
await controller.initialize()
|
||||
|
||||
// Format with empty files array
|
||||
const result = formatResponse.formatFilesList(TEST_CWD, [], false, controller as any)
|
||||
const result = formatResponse.formatFilesList(TEST_CWD, [], false, controller as any, true)
|
||||
|
||||
// Should show "No files found"
|
||||
expect(result).toBe("No files found.")
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ Otherwise, if you have not completed the task and do not need additional informa
|
|||
absolutePath: string,
|
||||
files: string[],
|
||||
didHitLimit: boolean,
|
||||
rooIgnoreController?: RooIgnoreController,
|
||||
rooIgnoreController: RooIgnoreController | undefined,
|
||||
showRooIgnoredFiles: boolean,
|
||||
): string => {
|
||||
const sorted = files
|
||||
.map((file) => {
|
||||
|
|
@ -90,20 +91,29 @@ Otherwise, if you have not completed the task and do not need additional informa
|
|||
return aParts.length - bParts.length
|
||||
})
|
||||
|
||||
const rooIgnoreParsed = rooIgnoreController
|
||||
? sorted.map((filePath) => {
|
||||
// path is relative to absolute path, not cwd
|
||||
// validateAccess expects either path relative to cwd or absolute path
|
||||
// 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.
|
||||
const absoluteFilePath = path.resolve(absolutePath, filePath)
|
||||
const isIgnored = !rooIgnoreController.validateAccess(absoluteFilePath)
|
||||
if (isIgnored) {
|
||||
return LOCK_TEXT_SYMBOL + " " + filePath
|
||||
}
|
||||
let rooIgnoreParsed: string[] = sorted
|
||||
|
||||
return filePath
|
||||
})
|
||||
: sorted
|
||||
if (rooIgnoreController) {
|
||||
rooIgnoreParsed = []
|
||||
for (const filePath of sorted) {
|
||||
// path is relative to absolute path, not cwd
|
||||
// validateAccess expects either path relative to cwd or absolute path
|
||||
// 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.
|
||||
const absoluteFilePath = path.resolve(absolutePath, filePath)
|
||||
const isIgnored = !rooIgnoreController.validateAccess(absoluteFilePath)
|
||||
|
||||
if (isIgnored) {
|
||||
// If file is ignored and we're not showing ignored files, skip it
|
||||
if (!showRooIgnoredFiles) {
|
||||
continue
|
||||
}
|
||||
// Otherwise, mark it with a lock symbol
|
||||
rooIgnoreParsed.push(LOCK_TEXT_SYMBOL + " " + filePath)
|
||||
} else {
|
||||
rooIgnoreParsed.push(filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (didHitLimit) {
|
||||
return `${rooIgnoreParsed.join(
|
||||
"\n",
|
||||
|
|
|
|||
|
|
@ -1468,6 +1468,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
await this.updateGlobalState("browserToolEnabled", message.bool ?? true)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "showRooIgnoredFiles":
|
||||
await this.updateGlobalState("showRooIgnoredFiles", message.bool ?? true)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "enhancementApiConfigId":
|
||||
await this.updateGlobalState("enhancementApiConfigId", message.text)
|
||||
await this.postStateToWebview()
|
||||
|
|
@ -2201,6 +2205,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
maxOpenTabsContext,
|
||||
browserToolEnabled,
|
||||
telemetrySetting,
|
||||
showRooIgnoredFiles,
|
||||
} = await this.getState()
|
||||
const telemetryKey = process.env.POSTHOG_API_KEY
|
||||
const machineId = vscode.env.machineId
|
||||
|
|
@ -2262,6 +2267,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
telemetrySetting,
|
||||
telemetryKey,
|
||||
machineId,
|
||||
showRooIgnoredFiles: showRooIgnoredFiles ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2441,6 +2447,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
openRouterUseMiddleOutTransform: stateValues.openRouterUseMiddleOutTransform ?? true,
|
||||
browserToolEnabled: stateValues.browserToolEnabled ?? true,
|
||||
telemetrySetting: stateValues.telemetrySetting || "unset",
|
||||
showRooIgnoredFiles: stateValues.showRooIgnoredFiles ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -445,6 +445,7 @@ describe("ClineProvider", () => {
|
|||
maxOpenTabsContext: 20,
|
||||
browserToolEnabled: true,
|
||||
telemetrySetting: "unset",
|
||||
showRooIgnoredFiles: true,
|
||||
}
|
||||
|
||||
const message: ExtensionMessage = {
|
||||
|
|
@ -703,6 +704,27 @@ describe("ClineProvider", () => {
|
|||
expect(state.browserToolEnabled).toBe(true) // Default value should be true
|
||||
})
|
||||
|
||||
test("handles showRooIgnoredFiles setting", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
||||
// Test showRooIgnoredFiles with true
|
||||
await messageHandler({ type: "showRooIgnoredFiles", bool: true })
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("showRooIgnoredFiles", true)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
|
||||
// Test showRooIgnoredFiles with false
|
||||
jest.clearAllMocks() // Clear all mocks including mockContext.globalState.update
|
||||
await messageHandler({ type: "showRooIgnoredFiles", bool: false })
|
||||
expect(mockContext.globalState.update).toHaveBeenCalledWith("showRooIgnoredFiles", false)
|
||||
expect(mockPostMessage).toHaveBeenCalled()
|
||||
|
||||
// Verify state includes showRooIgnoredFiles
|
||||
const state = await provider.getState()
|
||||
expect(state).toHaveProperty("showRooIgnoredFiles")
|
||||
expect(state.showRooIgnoredFiles).toBe(true) // Default value should be true
|
||||
})
|
||||
|
||||
test("handles request delay settings messages", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ export interface ExtensionState {
|
|||
telemetrySetting: TelemetrySetting
|
||||
telemetryKey?: string
|
||||
machineId?: string
|
||||
showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings
|
||||
}
|
||||
|
||||
export interface ClineMessage {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ export interface WebviewMessage {
|
|||
| "humanRelayCancel"
|
||||
| "browserToolEnabled"
|
||||
| "telemetrySetting"
|
||||
| "showRooIgnoredFiles"
|
||||
text?: string
|
||||
disabled?: boolean
|
||||
askResponse?: ClineAskResponse
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ export const GLOBAL_STATE_KEYS = [
|
|||
"lmStudioSpeculativeDecodingEnabled",
|
||||
"lmStudioDraftModelId",
|
||||
"telemetrySetting",
|
||||
"showRooIgnoredFiles",
|
||||
] as const
|
||||
|
||||
// Derive the type from the array - creates a union of string literals
|
||||
|
|
|
|||
|
|
@ -18,19 +18,25 @@ type AdvancedSettingsProps = HTMLAttributes<HTMLDivElement> & {
|
|||
maxOpenTabsContext: number
|
||||
diffEnabled?: boolean
|
||||
fuzzyMatchThreshold?: number
|
||||
showRooIgnoredFiles?: boolean
|
||||
setCachedStateField: SetCachedStateField<
|
||||
"rateLimitSeconds" | "terminalOutputLimit" | "maxOpenTabsContext" | "diffEnabled" | "fuzzyMatchThreshold"
|
||||
| "rateLimitSeconds"
|
||||
| "terminalOutputLimit"
|
||||
| "maxOpenTabsContext"
|
||||
| "diffEnabled"
|
||||
| "fuzzyMatchThreshold"
|
||||
| "showRooIgnoredFiles"
|
||||
>
|
||||
experiments: Record<ExperimentId, boolean>
|
||||
setExperimentEnabled: SetExperimentEnabled
|
||||
}
|
||||
|
||||
export const AdvancedSettings = ({
|
||||
rateLimitSeconds,
|
||||
terminalOutputLimit = TERMINAL_OUTPUT_LIMIT,
|
||||
maxOpenTabsContext,
|
||||
diffEnabled,
|
||||
fuzzyMatchThreshold,
|
||||
showRooIgnoredFiles,
|
||||
setCachedStateField,
|
||||
experiments,
|
||||
setExperimentEnabled,
|
||||
|
|
@ -197,6 +203,20 @@ export const AdvancedSettings = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<VSCodeCheckbox
|
||||
checked={showRooIgnoredFiles}
|
||||
onChange={(e: any) => {
|
||||
setCachedStateField("showRooIgnoredFiles", e.target.checked)
|
||||
}}>
|
||||
<span className="font-medium">Show .rooignore'd files in lists and searches</span>
|
||||
</VSCodeCheckbox>
|
||||
<p className="text-vscode-descriptionForeground text-sm mt-0">
|
||||
When enabled, files matching patterns in .rooignore will be shown in lists with a lock symbol.
|
||||
When disabled, these files will be completely hidden from file lists and searches.
|
||||
</p>
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
|
|||
telemetrySetting,
|
||||
terminalOutputLimit,
|
||||
writeDelayMs,
|
||||
showRooIgnoredFiles,
|
||||
} = cachedState
|
||||
|
||||
// Make sure apiConfiguration is initialized and managed by SettingsView.
|
||||
|
|
@ -179,6 +180,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
|
|||
vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds })
|
||||
vscode.postMessage({ type: "rateLimitSeconds", value: rateLimitSeconds })
|
||||
vscode.postMessage({ type: "maxOpenTabsContext", value: maxOpenTabsContext })
|
||||
vscode.postMessage({ type: "showRooIgnoredFiles", bool: showRooIgnoredFiles })
|
||||
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
|
||||
vscode.postMessage({ type: "updateExperimental", values: experiments })
|
||||
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
|
||||
|
|
@ -400,6 +402,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
|
|||
maxOpenTabsContext={maxOpenTabsContext}
|
||||
diffEnabled={diffEnabled}
|
||||
fuzzyMatchThreshold={fuzzyMatchThreshold}
|
||||
showRooIgnoredFiles={showRooIgnoredFiles}
|
||||
setCachedStateField={setCachedStateField}
|
||||
setExperimentEnabled={setExperimentEnabled}
|
||||
experiments={experiments}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export interface ExtensionStateContextType extends ExtensionState {
|
|||
setAlwaysAllowMcp: (value: boolean) => void
|
||||
setAlwaysAllowModeSwitch: (value: boolean) => void
|
||||
setBrowserToolEnabled: (value: boolean) => void
|
||||
setShowRooIgnoredFiles: (value: boolean) => void
|
||||
setShowAnnouncement: (value: boolean) => void
|
||||
setAllowedCommands: (value: string[]) => void
|
||||
setSoundEnabled: (value: boolean) => void
|
||||
|
|
@ -138,6 +139,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||
cwd: "",
|
||||
browserToolEnabled: true,
|
||||
telemetrySetting: "unset",
|
||||
showRooIgnoredFiles: true, // Default to showing .rooignore'd files with lock symbol (current behavior)
|
||||
})
|
||||
|
||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||
|
|
@ -276,6 +278,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||
setMaxOpenTabsContext: (value) => setState((prevState) => ({ ...prevState, maxOpenTabsContext: value })),
|
||||
setBrowserToolEnabled: (value) => setState((prevState) => ({ ...prevState, browserToolEnabled: value })),
|
||||
setTelemetrySetting: (value) => setState((prevState) => ({ ...prevState, telemetrySetting: value })),
|
||||
setShowRooIgnoredFiles: (value) => setState((prevState) => ({ ...prevState, showRooIgnoredFiles: value })),
|
||||
}
|
||||
|
||||
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
|
||||
|
|
|
|||
|
|
@ -9,14 +9,19 @@ import { ApiConfiguration } from "../../../../src/shared/api"
|
|||
|
||||
// Test component that consumes the context
|
||||
const TestComponent = () => {
|
||||
const { allowedCommands, setAllowedCommands, soundEnabled } = useExtensionState()
|
||||
const { allowedCommands, setAllowedCommands, soundEnabled, showRooIgnoredFiles, setShowRooIgnoredFiles } =
|
||||
useExtensionState()
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="allowed-commands">{JSON.stringify(allowedCommands)}</div>
|
||||
<div data-testid="sound-enabled">{JSON.stringify(soundEnabled)}</div>
|
||||
<div data-testid="show-rooignored-files">{JSON.stringify(showRooIgnoredFiles)}</div>
|
||||
<button data-testid="update-button" onClick={() => setAllowedCommands(["npm install", "git status"])}>
|
||||
Update Commands
|
||||
</button>
|
||||
<button data-testid="toggle-rooignore-button" onClick={() => setShowRooIgnoredFiles(!showRooIgnoredFiles)}>
|
||||
Update Commands
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -42,6 +47,30 @@ describe("ExtensionStateContext", () => {
|
|||
expect(JSON.parse(screen.getByTestId("sound-enabled").textContent!)).toBe(false)
|
||||
})
|
||||
|
||||
it("initializes with showRooIgnoredFiles set to true", () => {
|
||||
render(
|
||||
<ExtensionStateContextProvider>
|
||||
<TestComponent />
|
||||
</ExtensionStateContextProvider>,
|
||||
)
|
||||
|
||||
expect(JSON.parse(screen.getByTestId("show-rooignored-files").textContent!)).toBe(true)
|
||||
})
|
||||
|
||||
it("updates showRooIgnoredFiles through setShowRooIgnoredFiles", () => {
|
||||
render(
|
||||
<ExtensionStateContextProvider>
|
||||
<TestComponent />
|
||||
</ExtensionStateContextProvider>,
|
||||
)
|
||||
|
||||
act(() => {
|
||||
screen.getByTestId("toggle-rooignore-button").click()
|
||||
})
|
||||
|
||||
expect(JSON.parse(screen.getByTestId("show-rooignored-files").textContent!)).toBe(false)
|
||||
})
|
||||
|
||||
it("updates allowedCommands through setAllowedCommands", () => {
|
||||
render(
|
||||
<ExtensionStateContextProvider>
|
||||
|
|
@ -89,7 +118,8 @@ describe("mergeExtensionState", () => {
|
|||
customModes: [],
|
||||
maxOpenTabsContext: 20,
|
||||
apiConfiguration: { providerId: "openrouter" } as ApiConfiguration,
|
||||
telemetrySetting: "unset", // Adding the required telemetrySetting property
|
||||
telemetrySetting: "unset",
|
||||
showRooIgnoredFiles: true,
|
||||
}
|
||||
|
||||
const prevState: ExtensionState = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue