|
1 | 1 | local async = require('plenary.async')
|
2 | 2 | local curl = require('plenary.curl')
|
3 |
| -local scandir = require('plenary.scandir') |
4 | 3 | local log = require('plenary.log')
|
5 | 4 |
|
6 | 5 | local M = {}
|
|
479 | 478 | ---@class CopilotChat.utils.ScanOpts
|
480 | 479 | ---@field max_count number? The maximum number of files to scan
|
481 | 480 | ---@field max_depth number? The maximum depth to scan
|
482 |
| ----@field glob? string The glob pattern to match files |
| 481 | +---@field pattern? string The glob pattern to match files |
483 | 482 | ---@field hidden? boolean Whether to include hidden files
|
484 | 483 | ---@field no_ignore? boolean Whether to respect or ignore .gitignore
|
485 | 484 |
|
@@ -527,19 +526,69 @@ M.glob = async.wrap(function(path, opts, callback)
|
527 | 526 | return
|
528 | 527 | end
|
529 | 528 |
|
530 |
| - -- Fall back to scandir if rg is not available or fails |
531 |
| - scandir.scan_dir_async( |
532 |
| - path, |
533 |
| - vim.tbl_deep_extend('force', opts, { |
534 |
| - depth = opts.max_depth, |
535 |
| - add_dirs = false, |
536 |
| - search_pattern = opts.glob and M.glob_to_pattern(opts.glob) or nil, |
537 |
| - respect_gitignore = not opts.no_ignore, |
538 |
| - on_exit = function(files) |
539 |
| - callback(filter_files(files, opts.max_count)) |
540 |
| - end, |
541 |
| - }) |
542 |
| - ) |
| 529 | + -- Fallback to vim.uv.fs_scandir |
| 530 | + local matchers = {} |
| 531 | + if opts.pattern then |
| 532 | + local file_pattern = vim.glob.to_lpeg(opts.pattern) |
| 533 | + local path_pattern = vim.lpeg.P(path .. '/') * file_pattern |
| 534 | + |
| 535 | + table.insert(matchers, function(name, dir) |
| 536 | + return file_pattern:match(name) or path_pattern:match(dir .. '/' .. name) |
| 537 | + end) |
| 538 | + end |
| 539 | + |
| 540 | + if not opts.hidden then |
| 541 | + table.insert(matchers, function(name) |
| 542 | + return not name:match('^%.') |
| 543 | + end) |
| 544 | + end |
| 545 | + |
| 546 | + local data = {} |
| 547 | + local next_dir = { path } |
| 548 | + local current_depths = { [path] = 1 } |
| 549 | + |
| 550 | + local function read_dir(err, fd) |
| 551 | + local current_dir = table.remove(next_dir, 1) |
| 552 | + local depth = current_depths[current_dir] or 1 |
| 553 | + |
| 554 | + if not err and fd then |
| 555 | + while true do |
| 556 | + local name, typ = vim.uv.fs_scandir_next(fd) |
| 557 | + if name == nil then |
| 558 | + break |
| 559 | + end |
| 560 | + |
| 561 | + local full_path = current_dir .. '/' .. name |
| 562 | + |
| 563 | + if typ == 'directory' and not name:match('^%.git') then |
| 564 | + if not opts.max_depth or depth < opts.max_depth then |
| 565 | + table.insert(next_dir, full_path) |
| 566 | + current_depths[full_path] = depth + 1 |
| 567 | + end |
| 568 | + else |
| 569 | + local match = true |
| 570 | + for _, matcher in ipairs(matchers) do |
| 571 | + if not matcher(name, current_dir) then |
| 572 | + match = false |
| 573 | + break |
| 574 | + end |
| 575 | + end |
| 576 | + |
| 577 | + if match then |
| 578 | + table.insert(data, full_path) |
| 579 | + end |
| 580 | + end |
| 581 | + end |
| 582 | + end |
| 583 | + |
| 584 | + if #next_dir == 0 then |
| 585 | + callback(data) |
| 586 | + else |
| 587 | + vim.uv.fs_scandir(next_dir[1], read_dir) |
| 588 | + end |
| 589 | + end |
| 590 | + |
| 591 | + vim.uv.fs_scandir(path, read_dir) |
543 | 592 | end, 3)
|
544 | 593 |
|
545 | 594 | --- Grep a directory
|
@@ -783,136 +832,4 @@ function M.split_lines(text)
|
783 | 832 | return vim.split(text, '\r?\n', { trimempty = false })
|
784 | 833 | end
|
785 | 834 |
|
786 |
| ---- Convert glob pattern to regex pattern |
787 |
| ---- https://github.com/davidm/lua-glob-pattern/blob/master/lua/globtopattern.lua |
788 |
| ----@param g string The glob pattern |
789 |
| ----@return string |
790 |
| -function M.glob_to_pattern(g) |
791 |
| - local p = '^' -- pattern being built |
792 |
| - local i = 0 -- index in g |
793 |
| - local c -- char at index i in g. |
794 |
| - |
795 |
| - -- unescape glob char |
796 |
| - local function unescape() |
797 |
| - if c == '\\' then |
798 |
| - i = i + 1 |
799 |
| - c = g:sub(i, i) |
800 |
| - if c == '' then |
801 |
| - p = '[^]' |
802 |
| - return false |
803 |
| - end |
804 |
| - end |
805 |
| - return true |
806 |
| - end |
807 |
| - |
808 |
| - -- escape pattern char |
809 |
| - local function escape(c) |
810 |
| - return c:match('^%w$') and c or '%' .. c |
811 |
| - end |
812 |
| - |
813 |
| - -- Convert tokens at end of charset. |
814 |
| - local function charset_end() |
815 |
| - while 1 do |
816 |
| - if c == '' then |
817 |
| - p = '[^]' |
818 |
| - return false |
819 |
| - elseif c == ']' then |
820 |
| - p = p .. ']' |
821 |
| - break |
822 |
| - else |
823 |
| - if not unescape() then |
824 |
| - break |
825 |
| - end |
826 |
| - local c1 = c |
827 |
| - i = i + 1 |
828 |
| - c = g:sub(i, i) |
829 |
| - if c == '' then |
830 |
| - p = '[^]' |
831 |
| - return false |
832 |
| - elseif c == '-' then |
833 |
| - i = i + 1 |
834 |
| - c = g:sub(i, i) |
835 |
| - if c == '' then |
836 |
| - p = '[^]' |
837 |
| - return false |
838 |
| - elseif c == ']' then |
839 |
| - p = p .. escape(c1) .. '%-]' |
840 |
| - break |
841 |
| - else |
842 |
| - if not unescape() then |
843 |
| - break |
844 |
| - end |
845 |
| - p = p .. escape(c1) .. '-' .. escape(c) |
846 |
| - end |
847 |
| - elseif c == ']' then |
848 |
| - p = p .. escape(c1) .. ']' |
849 |
| - break |
850 |
| - else |
851 |
| - p = p .. escape(c1) |
852 |
| - i = i - 1 -- put back |
853 |
| - end |
854 |
| - end |
855 |
| - i = i + 1 |
856 |
| - c = g:sub(i, i) |
857 |
| - end |
858 |
| - return true |
859 |
| - end |
860 |
| - |
861 |
| - -- Convert tokens in charset. |
862 |
| - local function charset() |
863 |
| - i = i + 1 |
864 |
| - c = g:sub(i, i) |
865 |
| - if c == '' or c == ']' then |
866 |
| - p = '[^]' |
867 |
| - return false |
868 |
| - elseif c == '^' or c == '!' then |
869 |
| - i = i + 1 |
870 |
| - c = g:sub(i, i) |
871 |
| - if c == ']' then |
872 |
| - -- ignored |
873 |
| - else |
874 |
| - p = p .. '[^' |
875 |
| - if not charset_end() then |
876 |
| - return false |
877 |
| - end |
878 |
| - end |
879 |
| - else |
880 |
| - p = p .. '[' |
881 |
| - if not charset_end() then |
882 |
| - return false |
883 |
| - end |
884 |
| - end |
885 |
| - return true |
886 |
| - end |
887 |
| - |
888 |
| - -- Convert tokens. |
889 |
| - while 1 do |
890 |
| - i = i + 1 |
891 |
| - c = g:sub(i, i) |
892 |
| - if c == '' then |
893 |
| - p = p .. '$' |
894 |
| - break |
895 |
| - elseif c == '?' then |
896 |
| - p = p .. '.' |
897 |
| - elseif c == '*' then |
898 |
| - p = p .. '.*' |
899 |
| - elseif c == '[' then |
900 |
| - if not charset() then |
901 |
| - break |
902 |
| - end |
903 |
| - elseif c == '\\' then |
904 |
| - i = i + 1 |
905 |
| - c = g:sub(i, i) |
906 |
| - if c == '' then |
907 |
| - p = p .. '\\$' |
908 |
| - break |
909 |
| - end |
910 |
| - p = p .. escape(c) |
911 |
| - else |
912 |
| - p = p .. escape(c) |
913 |
| - end |
914 |
| - end |
915 |
| - return p |
916 |
| -end |
917 |
| - |
918 | 835 | return M
|
0 commit comments