Roo-Code/webview-ui/src/utils/path-mentions.ts
John Richmond 34d7dcf8c3
Support mentioning filenames with spaces (#3044)
* Support mentioning filenames with spaces

Allows spaces to be escaped in mentions with \. Does not attempt more
comprehensive escape handling. (IMO a more structured representation or
moving to something like @[something complex] is probably a better long
term approach, but the scope of that seems problematic.

Adapted from 8955d45708.

Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>

* Older array creation to make check-types happier

---------

Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
2025-04-29 22:27:16 -04:00

80 lines
2.5 KiB
TypeScript

/**
* Utilities for handling path-related operations in mentions
*/
/**
* Escapes spaces in a path with backslashes
*
* @param path The path to escape
* @returns A path with spaces escaped
*/
export function escapeSpaces(path: string): string {
return path.replace(/ /g, "\\ ")
}
/**
* Converts an absolute path to a mention-friendly path
* If the provided path starts with the current working directory,
* it's converted to a relative path prefixed with @
* Spaces in the path are escaped with backslashes
*
* @param path The path to convert
* @param cwd The current working directory
* @returns A mention-friendly path
*/
export function convertToMentionPath(path: string, cwd?: string): string {
// Strip file:// or vscode-remote:// protocol if present
let pathWithoutProtocol = path
if (path.startsWith("file://")) {
pathWithoutProtocol = path.substring(7)
} else if (path.startsWith("vscode-remote://")) {
const protocolStripped = path.substring("vscode-remote://".length)
const firstSlashIndex = protocolStripped.indexOf("/")
if (firstSlashIndex !== -1) {
pathWithoutProtocol = protocolStripped.substring(firstSlashIndex + 1)
} else {
pathWithoutProtocol = ""
}
}
try {
pathWithoutProtocol = decodeURIComponent(pathWithoutProtocol)
// Fix: Remove leading slash for Windows paths like /d:/...
if (pathWithoutProtocol.startsWith("/") && pathWithoutProtocol[2] === ":") {
pathWithoutProtocol = pathWithoutProtocol.substring(1)
}
} catch (e) {
// Log error if decoding fails, but continue with the potentially problematic path
console.error("Error decoding URI component in convertToMentionPath:", e, pathWithoutProtocol)
}
const normalizedPath = pathWithoutProtocol.replace(/\\/g, "/")
let normalizedCwd = cwd ? cwd.replace(/\\/g, "/") : ""
if (!normalizedCwd) {
return pathWithoutProtocol
}
// Remove trailing slash from cwd if it exists
if (normalizedCwd.endsWith("/")) {
normalizedCwd = normalizedCwd.slice(0, -1)
}
// Always use case-insensitive comparison for path matching
const lowerPath = normalizedPath.toLowerCase()
const lowerCwd = normalizedCwd.toLowerCase()
if (lowerPath.startsWith(lowerCwd)) {
let relativePath = normalizedPath.substring(normalizedCwd.length)
// Ensure there's a slash after the @ symbol when we create the mention path
relativePath = relativePath.startsWith("/") ? relativePath : "/" + relativePath
// Escape any spaces in the path with backslashes
const escapedRelativePath = escapeSpaces(relativePath)
return "@" + escapedRelativePath
}
return pathWithoutProtocol
}