@@ -6,21 +6,34 @@ local uv = vim.loop
66
77local m = {}
88
9- local get_gitignore = function (basepath )
10- local gitignore = {}
9+ local make_gitignore = function (basepath )
10+ local patterns = {}
1111 local valid = false
1212 for _ , v in ipairs (basepath ) do
1313 local p = Path :new (v .. os_sep .. " .gitignore" )
1414 if p :exists () then
1515 valid = true
16- gitignore [v ] = {}
16+ patterns [v ] = { ignored = {}, negated = {} }
1717 for l in p :iter () do
18- if l ~= " " then
19- local el = l :gsub (" %#.*" , " " )
18+ local prefix = l :sub (1 , 1 )
19+ local negated = prefix == " !"
20+ if negated then
21+ l = l :sub (2 )
22+ prefix = l :sub (1 , 1 )
23+ end
24+ if prefix == " /" then
25+ l = v .. l
26+ end
27+ if not (prefix == " " or prefix == " #" ) then
28+ local el = vim .trim (l )
29+ el = el :gsub (" %-" , " %%-" )
2030 el = el :gsub (" %." , " %%." )
21- el = el :gsub (" %*" , " %.%*" )
31+ el = el :gsub (" /%*%*/" , " /%%w+/" )
32+ el = el :gsub (" %*%*" , " " )
33+ el = el :gsub (" %*" , " %%w+" )
34+ el = el :gsub (" %?" , " %%w" )
2235 if el ~= " " then
23- table.insert (gitignore [v ], el )
36+ table.insert (negated and patterns [v ]. negated or patterns [ v ]. ignored , el )
2437 end
2538 end
2639 end
@@ -29,21 +42,29 @@ local get_gitignore = function(basepath)
2942 if not valid then
3043 return nil
3144 end
32- return gitignore
33- end
34-
35- local interpret_gitignore = function (gitignore , bp , entry )
36- for _ , v in ipairs (bp ) do
37- if entry :find (v , 1 , true ) then
38- for _ , w in ipairs (gitignore [v ]) do
39- if entry :match (w ) then
40- return false
45+ return function (bp , entry )
46+ for _ , v in ipairs (bp ) do
47+ if entry :find (v , 1 , true ) then
48+ local negated = false
49+ for _ , w in ipairs (patterns [v ].ignored ) do
50+ if not negated and entry :match (w ) then
51+ for _ , inverse in ipairs (patterns [v ].negated ) do
52+ if not negated and entry :match (inverse ) then
53+ negated = true
54+ end
55+ end
56+ if not negated then
57+ return false
58+ end
59+ end
4160 end
4261 end
4362 end
63+ return true
4464 end
45- return true
4665end
66+ -- exposed for testing
67+ m .__make_gitignore = make_gitignore
4768
4869local handle_depth = function (base_paths , entry , depth )
4970 for _ , v in ipairs (base_paths ) do
@@ -73,6 +94,8 @@ local gen_search_pat = function(pattern)
7394 end
7495 return false
7596 end
97+ elseif type (pattern ) == " function" then
98+ return pattern
7699 end
77100end
78101
@@ -85,8 +108,8 @@ local process_item = function(opts, name, typ, current_dir, next_dir, bp, data,
85108 else
86109 table.insert (next_dir , entry )
87110 end
88- if opts .add_dirs then
89- if not giti or interpret_gitignore ( giti , bp , entry .. " /" ) then
111+ if opts .add_dirs or opts . only_dirs then
112+ if not giti or giti ( bp , entry .. " /" ) then
90113 if not msp or msp (entry ) then
91114 table.insert (data , entry )
92115 if opts .on_insert then
@@ -95,9 +118,9 @@ local process_item = function(opts, name, typ, current_dir, next_dir, bp, data,
95118 end
96119 end
97120 end
98- else
121+ elseif not opts . only_dirs then
99122 local entry = current_dir .. os_sep .. name
100- if not giti or interpret_gitignore ( giti , bp , entry ) then
123+ if not giti or giti ( bp , entry ) then
101124 if not msp or msp (entry ) then
102125 table.insert (data , entry )
103126 if opts .on_insert then
117140-- @param opts: table to change behavior
118141-- opts.hidden (bool): if true hidden files will be added
119142-- opts.add_dirs (bool): if true dirs will also be added to the results
143+ -- opts.only_dirs (bool): if true only dirs will be added to the results
120144-- opts.respect_gitignore (bool): if true will only add files that are not ignored by the git (uses each gitignore found in path table)
121145-- opts.depth (int): depth on how deep the search should go
122- -- opts.search_pattern (regex): regex for which files will be added, string or table of strings
146+ -- opts.search_pattern (regex): regex for which files will be added, string, table of strings, or callback (should return bool)
123147-- opts.on_insert(entry): Will be called for each element
124148-- opts.silent (bool): if true will not echo messages that are not accessible
125149-- @return array with files
@@ -130,8 +154,8 @@ m.scan_dir = function(path, opts)
130154 local base_paths = vim .tbl_flatten { path }
131155 local next_dir = vim .tbl_flatten { path }
132156
133- local gitignore = opts .respect_gitignore and get_gitignore (base_paths ) or nil
134- local match_seach_pat = opts .search_pattern and gen_search_pat (opts .search_pattern ) or nil
157+ local gitignore = opts .respect_gitignore and make_gitignore (base_paths ) or nil
158+ local match_search_pat = opts .search_pattern and gen_search_pat (opts .search_pattern ) or nil
135159
136160 for i = table .getn (base_paths ), 1 , - 1 do
137161 if uv .fs_access (base_paths [i ], " X" ) == false then
@@ -154,7 +178,7 @@ m.scan_dir = function(path, opts)
154178 if name == nil then
155179 break
156180 end
157- process_item (opts , name , typ , current_dir , next_dir , base_paths , data , gitignore , match_seach_pat )
181+ process_item (opts , name , typ , current_dir , next_dir , base_paths , data , gitignore , match_search_pat )
158182 end
159183 end
160184 until table .getn (next_dir ) == 0
169193-- @param opts: table to change behavior
170194-- opts.hidden (bool): if true hidden files will be added
171195-- opts.add_dirs (bool): if true dirs will also be added to the results
196+ -- opts.only_dirs (bool): if true only dirs will be added to the results
172197-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git
173198-- opts.depth (int): depth on how deep the search should go
174- -- opts.search_pattern (lua regex): depth on how deep the search should go
199+ -- opts.search_pattern (regex): regex for which files will be added, string, table of strings, or callback ( should return bool)
175200-- opts.on_insert function(entry): will be called for each element
176201-- opts.on_exit function(results): will be called at the end
177202-- opts.silent (bool): if true will not echo messages that are not accessible
@@ -184,8 +209,8 @@ m.scan_dir_async = function(path, opts)
184209 local current_dir = table.remove (next_dir , 1 )
185210
186211 -- TODO(conni2461): get gitignore is not async
187- local gitignore = opts .respect_gitignore and get_gitignore ( ) or nil
188- local match_seach_pat = opts .search_pattern and gen_search_pat (opts .search_pattern ) or nil
212+ local gitignore = opts .respect_gitignore and make_gitignore ( base_paths ) or nil
213+ local match_search_pat = opts .search_pattern and gen_search_pat (opts .search_pattern ) or nil
189214
190215 -- TODO(conni2461): is not async. Shouldn't be that big of a problem but still
191216 -- Maybe obers async pr can take me out of callback hell
@@ -209,7 +234,7 @@ m.scan_dir_async = function(path, opts)
209234 if name == nil then
210235 break
211236 end
212- process_item (opts , name , typ , current_dir , next_dir , base_paths , data , gitignore , match_seach_pat )
237+ process_item (opts , name , typ , current_dir , next_dir , base_paths , data , gitignore , match_search_pat )
213238 end
214239 if table .getn (next_dir ) == 0 then
215240 if opts .on_exit then
0 commit comments