@@ -3,25 +3,125 @@ local location = require("peekstack.core.location")
33
44local M = {}
55
6+ --- @param path string
7+ --- @return boolean
8+ local function is_absolute (path )
9+ return path :sub (1 , 1 ) == " /" or path :sub (1 , 1 ) == " ~" or path :match (" ^%a:[/\\ ]" ) ~= nil or path :sub (1 , 2 ) == " \\\\ "
10+ end
11+
12+ --- @param target string
13+ --- @return string , integer ?, integer ?
14+ local function parse_target_spec (target )
15+ local path , lnum , col = target :match (" ^(.*):(%d+):(%d+)$" )
16+ if path then
17+ -- Avoid treating Windows drive letter colon as line separator
18+ if not path :match (" ^%a:$" ) then
19+ return path , tonumber (lnum ), tonumber (col )
20+ end
21+ end
22+
23+ path , lnum = target :match (" ^(.*):(%d+)$" )
24+ if path then
25+ if not path :match (" ^%a:$" ) then
26+ return path , tonumber (lnum ), nil
27+ end
28+ end
29+
30+ return target , nil , nil
31+ end
32+
33+ --- @param path string
34+ --- @param source_name string
35+ --- @return string
36+ local function resolve_path (path , source_name )
37+ if is_absolute (path ) then
38+ return vim .fn .fnamemodify (vim .fn .expand (path ), " :p" )
39+ end
40+
41+ local base = vim .fn .fnamemodify (source_name , " :p:h" )
42+ if base == " " then
43+ return vim .fn .fnamemodify (path , " :p" )
44+ end
45+
46+ return vim .fn .fnamemodify (base .. " /" .. path , " :p" )
47+ end
48+
49+ --- @param target string
50+ --- @param source_name string
51+ --- @return string ?, integer ?, integer ?
52+ local function resolve_target (target , source_name )
53+ -- 1) Try the target string as-is (handles absolute paths and env-var expansions)
54+ local exact = vim .fn .expand (target )
55+ local stat = vim .uv .fs_stat (exact )
56+ if stat then
57+ if stat .type ~= " file" then
58+ return nil , nil , nil
59+ end
60+ return vim .fn .fnamemodify (exact , " :p" ), nil , nil
61+ end
62+
63+ -- 2) Resolve relative to the source buffer's directory (the target may be a
64+ -- relative path that doesn't exist from cwd but does from the source file)
65+ local raw_resolved = resolve_path (target , source_name )
66+ local raw_stat = vim .uv .fs_stat (raw_resolved )
67+ if raw_stat then
68+ if raw_stat .type ~= " file" then
69+ return nil , nil , nil
70+ end
71+ return raw_resolved , nil , nil
72+ end
73+
74+ -- 3) Strip :line[:col] suffix and retry – the suffix prevented fs_stat above
75+ local path , lnum , col = parse_target_spec (target )
76+ local resolved = resolve_path (path , source_name )
77+ local resolved_stat = vim .uv .fs_stat (resolved )
78+ if not resolved_stat or resolved_stat .type ~= " file" then
79+ return nil , nil , nil
80+ end
81+
82+ return resolved , lnum , col
83+ end
84+
85+ --- @return string
86+ local function cursor_target ()
87+ local target = vim .fn .expand (" <cfile>" )
88+ local wide_target = vim .fn .expand (" <cWORD>" )
89+ if wide_target ~= " " and wide_target :find (" :%d+" ) and not target :find (" :%d+" ) then
90+ return wide_target
91+ end
92+ return target
93+ end
94+
695--- Get file path under cursor
796--- @param ctx PeekstackProviderContext
897--- @param cb fun ( locations : PeekstackLocation[] )
998function M .under_cursor (ctx , cb )
10- local target = vim . fn . expand ( " <cfile> " )
99+ local target = cursor_target ( )
11100 if not target or target == " " then
12101 cb ({})
13102 return
14103 end
15104 if not target :match (" ^%a+://" ) then
16105 local source_name = vim .api .nvim_buf_get_name (ctx .bufnr )
17- local base = vim .fn .fnamemodify (source_name , " :p:h" )
18- target = vim .fn .fnamemodify (base .. " /" .. target , " :p" )
19-
20- local stat = vim .uv .fs_stat (target )
21- if not stat or stat .type ~= " file" then
106+ local resolved , lnum , col = resolve_target (target , source_name )
107+ if not resolved then
22108 cb ({})
23109 return
24110 end
111+ target = resolved
112+ lnum = lnum or 1
113+ col = col or 1
114+
115+ local uri = fs .fname_to_uri (target )
116+ local loc = location .normalize ({
117+ uri = uri ,
118+ range = {
119+ start = { line = lnum - 1 , character = col - 1 },
120+ [" end" ] = { line = lnum - 1 , character = col - 1 },
121+ },
122+ }, " file.under_cursor" )
123+ cb (loc and { loc } or {})
124+ return
25125 end
26126 local uri = fs .fname_to_uri (target )
27127 local loc = location .normalize (
0 commit comments