@@ -83,6 +83,88 @@ local function copy_file(src, dst)
8383 return vim .fn .writefile (data , dst ) == 0
8484end
8585
86+ local function collect_sources_recursive (root , ignore_set )
87+ local uv = vim .loop
88+ local results = {}
89+ local function scan (dir )
90+ local fs = uv .fs_scandir (dir )
91+ if not fs then return end
92+ while true do
93+ local name , t = uv .fs_scandir_next (fs )
94+ if not name then break end
95+ local path = dir .. ' /' .. name
96+ if t == ' directory' then
97+ if not ignore_set [name ] then
98+ scan (path )
99+ end
100+ else
101+ local ext = (name :match (' %.([%w]+)$' ) or ' ' ):lower ()
102+ if ext == ' c' or ext == ' cpp' or ext == ' cc' or ext == ' cxx' then
103+ table.insert (results , path )
104+ end
105+ end
106+ end
107+ end
108+ scan (root )
109+ return results
110+ end
111+
112+ local function collect_sources_async (root , ignore_set , include_hidden , max_depth , on_progress , on_done )
113+ local uv = vim .loop
114+ local queue = { { root , 0 } }
115+ local results = {}
116+ local scanners = {}
117+ local dir_count , file_count = 0 , 0
118+ local function step ()
119+ local budget = 600
120+ while budget > 0 do
121+ local item = queue [# queue ]
122+ local current , depth = nil , 0
123+ if item then current , depth = item [1 ], item [2 ] or 0 end
124+ if not current then
125+ on_done (results )
126+ return
127+ end
128+ local fs = scanners [current ]
129+ if not fs then
130+ fs = uv .fs_scandir (current )
131+ scanners [current ] = fs or false
132+ if not fs then
133+ queue [# queue ] = nil
134+ end
135+ else
136+ local name , t = uv .fs_scandir_next (fs )
137+ if not name then
138+ queue [# queue ] = nil
139+ else
140+ local path = current .. ' /' .. name
141+ if t == ' directory' then
142+ if not ignore_set [name ] then
143+ if include_hidden or not name :match (' ^%.' ) then
144+ if (not max_depth ) or (depth + 1 <= max_depth ) then
145+ queue [# queue + 1 ] = { path , depth + 1 }
146+ dir_count = dir_count + 1
147+ end
148+ end
149+ end
150+ else
151+ local ext = (name :match (' %.([%w]+)$' ) or ' ' ):lower ()
152+ if ext == ' c' or ext == ' cpp' or ext == ' cc' or ext == ' cxx' then
153+ results [# results + 1 ] = path
154+ file_count = file_count + 1
155+ end
156+ end
157+ budget = budget - 1
158+ end
159+ end
160+ if budget <= 0 then break end
161+ end
162+ if on_progress then on_progress (file_count , dir_count ) end
163+ vim .defer_fn (step , 1 )
164+ end
165+ step ()
166+ end
167+
86168function CC .generate (config , notify )
87169 local ft = vim .bo .filetype
88170 if ft ~= ' c' and ft ~= ' cpp' then
@@ -239,23 +321,37 @@ end
239321-- Non-CMake: scan project for sources and generate
240322function CC .generate_for_project (config , notify )
241323 local cwd = vim .fn .getcwd ()
242- local patterns = { ' **/*.c' , ' **/*.cpp' , ' **/*.cc' , ' **/*.cxx' }
243- local seen , sources = {}, {}
244- for _ , pat in ipairs (patterns ) do
245- local list = vim .fn .glob (pat , true , true )
246- for _ , f in ipairs (list ) do
247- local p = vim .fn .fnamemodify (f , ' :p' )
248- if vim .fn .filereadable (p ) == 1 and not seen [p ] then
249- seen [p ] = true
250- table.insert (sources , p )
324+ local ccfg = config .compile_commands or {}
325+ local ignores = {}
326+ for _ , v in ipairs (ccfg .ignore_dirs or { ' .git' , ' node_modules' , ' .cache' }) do ignores [v ] = true end
327+ local include_hidden = ccfg .include_hidden ~= false
328+ local max_depth = ccfg .max_depth
329+ local throttle = tonumber (ccfg .progress_throttle_ms ) or 600
330+ local last = 0
331+ collect_sources_async (cwd , ignores , include_hidden , max_depth , function (files , dirs )
332+ local uv = vim .loop
333+ if uv and uv .now then
334+ local now = uv .now ()
335+ if now - (last or 0 ) >= throttle then
336+ notify .info (string.format (' Scanning project: %d files, %d dirs' , files or 0 , dirs or 0 ))
337+ last = now
251338 end
252339 end
253- end
254- if # sources == 0 then
255- notify .warn ' No C/C++ sources found under project root'
256- return
257- end
258- CC .generate_for_sources (config , notify , sources )
340+ end , function (list )
341+ local seen , sources = {}, {}
342+ for _ , p in ipairs (list ) do
343+ local abs = vim .fn .fnamemodify (p , ' :p' )
344+ if vim .fn .filereadable (abs ) == 1 and not seen [abs ] then
345+ seen [abs ] = true
346+ sources [# sources + 1 ] = abs
347+ end
348+ end
349+ if # sources == 0 then
350+ notify .warn ' No C/C++ sources found under project root'
351+ return
352+ end
353+ CC .generate_for_sources (config , notify , sources )
354+ end )
259355end
260356
261357-- Non-CMake: generate for all sources under a specified directory
@@ -270,24 +366,42 @@ function CC.generate_for_dir(config, notify, dir)
270366 notify .err (' Not a directory: ' .. tostring (dir ))
271367 return
272368 end
273- local patterns = { ' **/*.c' , ' **/*.cpp' , ' **/*.cc' , ' **/*.cxx' }
274- local seen , sources = {}, {}
275- for _ , pat in ipairs (patterns ) do
276- -- Use globpath to search inside the specified directory recursively
277- local list = vim .fn .globpath (dir , pat , true , true )
278- for _ , f in ipairs (list ) do
279- local p = vim .fn .fnamemodify (f , ' :p' )
280- if vim .fn .filereadable (p ) == 1 and not seen [p ] then
281- seen [p ] = true
282- table.insert (sources , p )
369+ local ccfg = config .compile_commands or {}
370+ local ignores = {}
371+ for _ , v in ipairs (ccfg .ignore_dirs or { ' .git' , ' node_modules' , ' .cache' }) do ignores [v ] = true end
372+ local include_hidden = ccfg .include_hidden ~= false
373+ local max_depth = ccfg .max_depth
374+ local throttle = tonumber (ccfg .progress_throttle_ms ) or 600
375+ local last = 0
376+ collect_sources_async (dir , ignores , include_hidden , max_depth , function (files , dirs )
377+ local uvn = vim .loop
378+ if uvn and uvn .now then
379+ local now = uvn .now ()
380+ if now - (last or 0 ) >= throttle then
381+ notify .info (string.format (' Scanning dir: %d files, %d dirs' , files or 0 , dirs or 0 ))
382+ last = now
283383 end
284384 end
285- end
286- if # sources == 0 then
287- notify .warn (' No C/C++ sources found under: ' .. dir )
288- return
289- end
290- CC .generate_for_sources (config , notify , sources )
385+ end , function (list )
386+ local seen , sources = {}, {}
387+ for _ , p in ipairs (list ) do
388+ local abs = vim .fn .fnamemodify (p , ' :p' )
389+ if vim .fn .filereadable (abs ) == 1 and not seen [abs ] then
390+ seen [abs ] = true
391+ sources [# sources + 1 ] = abs
392+ end
393+ end
394+ if # sources == 0 then
395+ notify .warn (' No C/C++ sources found under: ' .. dir )
396+ return
397+ end
398+ local cfg = vim .tbl_deep_extend (' force' , {}, config )
399+ cfg .compile_commands = cfg .compile_commands or {}
400+ if cfg .compile_commands .outdir == ' source' then
401+ cfg .compile_commands .outdir = dir
402+ end
403+ CC .generate_for_sources (cfg , notify , sources )
404+ end )
291405end
292406
293407return CC
0 commit comments