Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .review/pr-8274
Submodule pr-8274 added at e46929
1 change: 1 addition & 0 deletions tmp/pr-8287-Roo-Code
Submodule pr-8287-Roo-Code added at 88a473
66 changes: 65 additions & 1 deletion webview-ui/src/components/chat/ChatTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,70 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
}

// Special handling for folder selection with concrete value
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good specialized handling for folder selection. Consider adding a clarifying comment that explains why we bypass insertMention (to avoid the extra trailing space) and whether the folder path needs to be escaped here (or if the value is already escaped upstream).

Suggested change
// Special handling for folder selection with concrete value
// Special handling for folder selection with concrete value: bypass insertMention to avoid extra trailing space. Folder path is assumed to be already escaped upstream.

if (type === ContextMenuOptionType.Folder && value) {
if (textAreaRef.current) {
// Ensure the path ends with "/"
let folderPath = value
if (!folderPath.endsWith("/")) {
folderPath += "/"
}

// Insert the folder mention without a trailing space
const beforeCursor = textAreaRef.current.value.slice(0, cursorPosition)
const afterCursor = textAreaRef.current.value.slice(cursorPosition)
const lastAtIndex = beforeCursor.lastIndexOf("@")

let newValue: string
let newCursorPosition: number

if (lastAtIndex !== -1) {
// Replace everything after the @ with the folder path
const beforeMention = textAreaRef.current.value.slice(0, lastAtIndex)
// Don't add a trailing space after the folder path
newValue = beforeMention + "@" + folderPath + afterCursor
newCursorPosition = lastAtIndex + 1 + folderPath.length
} else {
// Insert at cursor position
newValue = beforeCursor + "@" + folderPath + afterCursor
newCursorPosition = cursorPosition + 1 + folderPath.length
}

setInputValue(newValue)
setCursorPosition(newCursorPosition)
setIntendedCursorPosition(newCursorPosition)

// Keep the menu open and search for folder contents
// Don't call setShowContextMenu(false)
setSelectedType(null)
setSelectedMenuIndex(0)

// Extract the query for searching folder contents
const query = folderPath
setSearchQuery(query)

// Trigger file search for the folder contents
const reqId = Math.random().toString(36).substring(2, 9)
setSearchRequestId(reqId)
setSearchLoading(true)

// Send message to extension to search files in the folder
vscode.postMessage({
type: "searchFiles",
query: query,
requestId: reqId,
})

// Focus the textarea
setTimeout(() => {
if (textAreaRef.current) {
textAreaRef.current.focus()
}
}, 0)
}
return
}

setShowContextMenu(false)
setSelectedType(null)

Expand Down Expand Up @@ -387,7 +451,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[setInputValue, cursorPosition],
[setInputValue, cursorPosition, setSearchRequestId, setSearchLoading],
)

const handleKeyDown = useCallback(
Expand Down
70 changes: 70 additions & 0 deletions webview-ui/src/utils/__tests__/context-mentions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,43 @@ describe("insertMention", () => {
expect(result.mentionIndex).toBe(6)
})
})

// --- Tests for Folder Drill-Down ---
describe("folder drill-down behavior", () => {
it("should not add trailing space when inserting folder path ending with /", () => {
// This test documents the expected behavior for folder drill-down
// When a folder path ends with /, we don't want a trailing space
const folderPath = "/path/to/folder/"
const result = insertMention("Browse @", 8, folderPath)

// The current implementation adds a space, but for folder drill-down
// we need to handle this specially in the component
expect(result.newValue).toBe("Browse @/path/to/folder/ ")
expect(result.mentionIndex).toBe(7)
})

it("should handle folder paths with spaces correctly", () => {
const folderPath = "/my documents/project folder/"
const expectedEscapedPath = "/my\\ documents/project\\ folder/"
const result = insertMention("Open @", 6, folderPath)

expect(result.newValue).toBe(`Open @${expectedEscapedPath} `)
expect(result.mentionIndex).toBe(5)
})

it("should ensure folder paths end with / for consistency", () => {
// This documents that the component should ensure folder paths end with /
const folderPathWithoutSlash = "/path/to/folder"
const folderPathWithSlash = "/path/to/folder/"

const result1 = insertMention("@", 1, folderPathWithoutSlash)
const result2 = insertMention("@", 1, folderPathWithSlash)

// Both should have the path, but the component should normalize to have trailing /
expect(result1.newValue).toBe("@/path/to/folder ")
expect(result2.newValue).toBe("@/path/to/folder/ ")
})
})
})

describe("removeMention", () => {
Expand Down Expand Up @@ -585,4 +622,37 @@ describe("shouldShowContextMenu", () => {
// This case means the regex wouldn't match anyway, but confirms context menu logic
expect(shouldShowContextMenu("@/path/with space", 13)).toBe(false) // Cursor after unescaped space
})

// --- Tests for Folder Drill-Down ---
describe("folder drill-down behavior", () => {
it("should keep menu open when path ends with / and no trailing space", () => {
// When drilling into a folder, the path ends with / and no space after
expect(shouldShowContextMenu("@/path/to/folder/", 17)).toBe(true)
})

it("should keep menu open for nested folder paths", () => {
expect(shouldShowContextMenu("@/src/components/", 17)).toBe(true)
expect(shouldShowContextMenu("@/src/components/chat/", 22)).toBe(true)
})

it("should close menu when space is added after folder path", () => {
// Normal behavior - space after path closes the menu
expect(shouldShowContextMenu("@/path/to/folder/ ", 18)).toBe(false)
})

it("should handle folder paths with escaped spaces", () => {
expect(shouldShowContextMenu("@/my\\ documents/", 16)).toBe(true)
expect(shouldShowContextMenu("@/my\\ documents/folder/", 23)).toBe(true)
})

it("should return true for slash commands without spaces", () => {
expect(shouldShowContextMenu("/code", 5)).toBe(true)
expect(shouldShowContextMenu("/debug", 6)).toBe(true)
})

it("should return false for slash commands with spaces", () => {
expect(shouldShowContextMenu("/code ", 6)).toBe(false)
expect(shouldShowContextMenu("/debug some text", 7)).toBe(false)
})
})
})
Loading