Fix directory link handling in markdown (#3690)

fix: improve directory link handling in markdown

Enhance the openFile function to better handle directory links in markdown:
- Add support for resolving ./SimpleName paths to home directory if not found in workspace
- Improve path resolution by checking multiple potential locations
- Ensure directories are properly revealed in the Explorer view
- Attempt to expand directories after revealing them

Fixes: #3686

Signed-off-by: Eric Wheeler <roo-code@z.ewheeler.org>
Co-authored-by: Eric Wheeler <roo-code@z.ewheeler.org>
This commit is contained in:
KJ7LNW 2025-06-01 19:59:17 -07:00 committed by GitHub
parent caf54dcc3d
commit d079cb11ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -28,39 +28,104 @@ interface OpenFileOptions {
export async function openFile(filePath: string, options: OpenFileOptions = {}) {
try {
// Get workspace root
const workspaceRoot = getWorkspacePath()
const homeDir = os.homedir()
const originalFilePathForError = filePath // Keep original for error messages
// If path starts with ./, resolve it relative to workspace root if available
// Otherwise, use the path as provided without modification
const fullPath = filePath.startsWith("./")
? workspaceRoot
? path.join(workspaceRoot, filePath.slice(2))
: filePath
: filePath
const attemptPaths: string[] = []
const uri = vscode.Uri.file(fullPath)
// Check if file exists
try {
await vscode.workspace.fs.stat(uri)
} catch {
// File doesn't exist
if (!options.create) {
throw new Error("File does not exist")
if (filePath.startsWith("./")) {
const relativePart = filePath.slice(2)
if (workspaceRoot) {
attemptPaths.push(path.join(workspaceRoot, relativePart))
}
// Create with provided content or empty string
const content = options.content || ""
await vscode.workspace.fs.writeFile(uri, Buffer.from(content, "utf8"))
if (homeDir) {
const homePath = path.join(homeDir, relativePart)
// Add home path if it's different from what might have been added via workspaceRoot
// (e.g. if workspaceRoot itself is the home directory)
if (!attemptPaths.includes(homePath)) {
attemptPaths.push(homePath)
}
}
// If no workspace and no home, or if paths were identical.
if (attemptPaths.length === 0) {
attemptPaths.push(filePath) // Try the original relative path as a last resort
}
} else {
attemptPaths.push(filePath) // Assumed absolute or directly resolvable
}
let fileStat: vscode.FileStat | undefined
let successfulUri: vscode.Uri | undefined
for (const p of attemptPaths) {
try {
const tempUri = vscode.Uri.file(p)
fileStat = await vscode.workspace.fs.stat(tempUri)
successfulUri = tempUri // Path found
break // Exit loop once a path is successfully stated
} catch (e) {
// Stat failed for this path, continue to the next one
}
}
let uriToProcess: vscode.Uri
if (fileStat && successfulUri) {
// Path was found
if (fileStat.type === vscode.FileType.Directory) {
await vscode.commands.executeCommand("revealInExplorer", successfulUri)
// Attempt to expand the revealed directory in the explorer.
// This requires the explorer to have focus and the item to be selected.
// A slight delay might sometimes be needed for focus to shift,
// but often it works immediately after revealInExplorer.
try {
await vscode.commands.executeCommand("list.expand")
} catch (expandError) {
// Log or handle if expansion specifically fails, though often not critical
console.warn("Could not expand directory in explorer:", expandError)
}
return // Done for directories
}
uriToProcess = successfulUri // It's an existing file
} else {
// Path was not found in any attempted locations. Consider creation.
if (
options.create &&
!(originalFilePathForError.endsWith("/") || originalFilePathForError.endsWith("\\"))
) {
let pathToCreateAt: string
if (originalFilePathForError.startsWith("./")) {
const relativePart = originalFilePathForError.slice(2)
if (workspaceRoot) {
pathToCreateAt = path.join(workspaceRoot, relativePart)
} else if (homeDir) {
pathToCreateAt = path.join(homeDir, relativePart)
} else {
pathToCreateAt = originalFilePathForError // Fallback: use original relative path
}
} else {
pathToCreateAt = originalFilePathForError // Absolute path
}
uriToProcess = vscode.Uri.file(pathToCreateAt)
const contentToCreate = options.content || ""
await vscode.workspace.fs.writeFile(uriToProcess, Buffer.from(contentToCreate, "utf8"))
// File is now created, uriToProcess points to it.
} else {
// Not creating, or it's a directory-like path that doesn't exist.
throw new Error(`Path does not exist: ${originalFilePathForError}`)
}
}
// At this point, uriToProcess points to an existing file or a newly created file.
// Check if the document is already open in a tab group that's not in the active editor's column
try {
for (const group of vscode.window.tabGroups.all) {
const existingTab = group.tabs.find(
(tab) =>
tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, uri.fsPath),
tab.input instanceof vscode.TabInputText &&
arePathsEqual(tab.input.uri.fsPath, uriToProcess.fsPath),
)
if (existingTab) {
const activeColumn = vscode.window.activeTextEditor?.viewColumn
@ -75,7 +140,7 @@ export async function openFile(filePath: string, options: OpenFileOptions = {})
}
} catch {} // not essential, sometimes tab operations fail
const document = await vscode.workspace.openTextDocument(uri)
const document = await vscode.workspace.openTextDocument(uriToProcess)
const selection =
options.line !== undefined
? new vscode.Selection(Math.max(options.line - 1, 0), 0, Math.max(options.line - 1, 0), 0)