11import { opendir , realpath } from "node:fs/promises" ;
2+ import { homedir } from "node:os" ;
23import { resolve , dirname , basename } from "node:path" ;
34
45const MAX_SUGGESTIONS = 20 ;
@@ -18,25 +19,33 @@ async function resolveDirectory(
1819// Given a path prefix the user has typed (e.g. after @ or in a path field),
1920// return up to MAX_SUGGESTIONS matching filesystem entries. Directories get a
2021// trailing / so the user can drill in. Never throws — returns [] on any fs error.
22+ // Home-relative prefixes (`~`, `~/…`) list the operator's home directory and
23+ // keep the `~/` display form, matching the submit-time expandHome behavior in
24+ // mention-resolution.ts (the completion popup previously offered nothing here).
2125export async function listPathSuggestions (
2226 prefix : string ,
2327 cwd : string ,
2428) : Promise < string [ ] > {
25- if ( prefix === "~" || prefix . startsWith ( "~/" ) ) return [ ] ;
29+ // `~` alone lists home, mirroring submit-time expandHome("~") -> homedir().
30+ const homeRelative = prefix === "~" || prefix . startsWith ( "~/" ) ;
31+ const rest = homeRelative ? ( prefix === "~" ? "" : prefix . slice ( 2 ) ) : prefix ;
32+ const base = homeRelative ? homedir ( ) : cwd ;
2633
2734 try {
28- const endsWithSep = prefix . endsWith ( "/" ) ;
35+ const endsWithSep = homeRelative
36+ ? rest . endsWith ( "/" ) || rest === ""
37+ : prefix . endsWith ( "/" ) ;
2938 // When prefix ends with / the user wants to list that directory.
3039 // When prefix contains a slash but doesn't end with one, split on the last
3140 // slash: the left side is the dir to list, the right side is the filter.
3241 // When prefix has NO slash at all (including the empty string), list cwd
3342 // and use the whole prefix as a filter. This is the `@` alone case which
3443 // should behave like `ls` in the current directory.
35- const lastSlash = prefix . lastIndexOf ( "/" ) ;
44+ const lastSlash = rest . lastIndexOf ( "/" ) ;
3645 const hasSlash = lastSlash !== - 1 ;
37- const dir = endsWithSep ? prefix : hasSlash ? dirname ( prefix ) : "." ;
38- const fragment = endsWithSep ? "" : hasSlash ? basename ( prefix ) : prefix ;
39- const realDir = await resolveDirectory ( dir , cwd ) ;
46+ const dir = endsWithSep ? rest || "." : hasSlash ? dirname ( rest ) : "." ;
47+ const fragment = endsWithSep ? "" : hasSlash ? basename ( rest ) : rest ;
48+ const realDir = await resolveDirectory ( dir , base ) ;
4049 if ( realDir === null ) return [ ] ;
4150
4251 const matched : string [ ] = [ ] ;
@@ -50,11 +59,18 @@ export async function listPathSuggestions(
5059
5160 // Reconstruct the path the user would type. For bare-fragment prefixes
5261 // (no slash) entries are shown relative to cwd so dirPrefix is "".
53- const dirPrefix = endsWithSep
54- ? prefix
62+ // Home-relative results keep the `~/` form so the completion inserts
63+ // text the submit path expands verbatim.
64+ const innerPrefix = endsWithSep
65+ ? rest
5566 : hasSlash
56- ? prefix . slice ( 0 , prefix . length - fragment . length )
67+ ? rest . slice ( 0 , rest . length - fragment . length )
5768 : "" ;
69+ const dirPrefix = homeRelative
70+ ? `~/${ innerPrefix } `
71+ : endsWithSep
72+ ? prefix
73+ : innerPrefix ;
5874 matched . push ( dirPrefix + entry . name + ( entry . isDirectory ( ) ? "/" : "" ) ) ;
5975 }
6076
0 commit comments