@@ -27,7 +27,7 @@ int os_get_uname(uv_uid_t uid, char *s, size_t len);
2727--- @field shortcuts string
2828--- @field show_hidden boolean
2929--- @field normal_when_fits boolean
30- --- @filed use_trash boolean
30+ --- @field use_trash boolean
3131--- @field file_dir_based boolean
3232--- @field keymaps KeyMapConfig
3333
@@ -230,7 +230,7 @@ local Browser = {}
230230local Actions = {}
231231
232232UI .Entry = {
233- render = function (state , row , entry , file_idx )
233+ render = function (state , row , entry )
234234 local formatted = {
235235 perms = Format .permissions (entry .stat .mode ),
236236 user = Format .username (entry .stat .uid ),
659659
660660local function create_shortcut_manager ()
661661 local pool = vim .split (Config .shortcuts , ' ' )
662- local idx = 1
663662 -- {shortcut -> line_num}
664663 local assigned = {} -- type table<string, int>
665- local existing_keymaps = {}
666664
667665 return {
668666 get = function ()
@@ -728,6 +726,106 @@ local function create_shortcut_manager()
728726 }
729727end
730728
729+ local function create_debounced_search ()
730+ local timer = nil
731+ local last_search = ' '
732+ local last_results = {}
733+ local is_searching = false
734+ local pending_search = nil
735+
736+ -- 清理函数
737+ local function cleanup ()
738+ if timer then
739+ if timer :is_active () then
740+ timer :stop ()
741+ end
742+ timer :close ()
743+ timer = nil
744+ end
745+ end
746+
747+ local function execute_search (state , search_text , callback )
748+ if search_text == last_search and # last_results > 0 then
749+ return vim .schedule (function ()
750+ callback (last_results )
751+ end )
752+ end
753+
754+ is_searching = true
755+ last_search = search_text
756+
757+ vim .schedule (function ()
758+ local filtered_entries = {}
759+
760+ for _ , entry in ipairs (state .entries ) do
761+ local match = vim .fn .matchfuzzypos ({ entry .name }, search_text )
762+ if # match [3 ] > 0 and match [3 ][1 ] > 0 then
763+ entry .match_pos = match [2 ][1 ]
764+ entry .score = match [3 ][1 ]
765+ table.insert (filtered_entries , entry )
766+ end
767+ end
768+
769+ table.sort (filtered_entries , function (a , b )
770+ return a .score > b .score
771+ end )
772+ last_results = filtered_entries
773+ is_searching = false
774+
775+ if pending_search then
776+ local pending = pending_search
777+ pending_search = nil
778+ execute_search (state , pending .text , pending .callback )
779+ return
780+ end
781+
782+ vim .schedule (function ()
783+ callback (filtered_entries )
784+ end )
785+ end )
786+ end
787+
788+ return {
789+ search = function (state , search_text , callback , delay )
790+ delay = delay or 100
791+ if is_searching then
792+ pending_search = { text = search_text , callback = callback }
793+ return
794+ end
795+ if not timer then
796+ timer = assert (vim .uv .new_timer ())
797+ end
798+
799+ if timer :is_active () then
800+ timer :stop ()
801+ end
802+
803+ if # search_text == 0 then
804+ last_search = ' '
805+ last_results = {}
806+ return vim .schedule (function ()
807+ callback (state .entries )
808+ end )
809+ end
810+
811+ timer :start (
812+ delay ,
813+ 0 ,
814+ vim .schedule_wrap (function ()
815+ execute_search (state , search_text , callback )
816+ end )
817+ )
818+ end ,
819+ destroy = cleanup ,
820+ reset = function ()
821+ last_search = ' '
822+ last_results = {}
823+ is_searching = false
824+ pending_search = nil
825+ end ,
826+ }
827+ end
828+
731829Browser .State = {
732830 create = function (path )
733831 local width = math.floor (vim .o .columns * 0.8 )
@@ -740,13 +838,15 @@ Browser.State = {
740838
741839 return F .IO .chain (UI .Window .create (dimensions ), function (state )
742840 return F .IO .chain (UI .Window .event (state ), function (s )
841+ --- @diagnostic disable-next-line : redefined-local
743842 return F .IO .chain (UI .Window .monitor (s ), function (s )
744843 s .current_path = path
745844 s .entries = {}
746845 s .show_hidden = Config .show_hidden
747846 s .original_entries = {}
748847 s .clipboard = {}
749848 s .shortcut_manager = create_shortcut_manager ()
849+ s .search_engine = create_debounced_search ()
750850 s .initialized = false
751851
752852 -- Function to update display with entries
@@ -780,7 +880,7 @@ Browser.State = {
780880
781881 vim .bo [new_state .buf ].modifiable = true
782882 for i , entry in ipairs (entries_to_show ) do
783- UI .Entry .render (new_state , i - 1 , entry , i )
883+ UI .Entry .render (new_state , i - 1 , entry )
784884 end
785885 end
786886
@@ -798,57 +898,25 @@ Browser.State = {
798898 end
799899
800900 if not s .initialized then
801- local timer = assert (vim .uv .new_timer ())
802901 -- Attach buffer for search
803902 api .nvim_buf_attach (state .search_buf , false , {
804- on_lines = function (... )
903+ on_lines = function ()
805904 -- Get search text without prompt path
806- local text =
905+ local query =
807906 api .nvim_get_current_line ():gsub (state .abbr_path or state .current_path , ' ' )
808907
809- if text == ' ' or text :match (SEPARATOR .. ' $' ) then
908+ if query == ' ' or query :match (SEPARATOR .. ' $' ) then
810909 update_display (state , state .entries )
811910 return
812911 end
813-
814- -- Clear previous timer if exists
815- if timer :is_active () then
816- timer :stop ()
817- end
818-
819- -- Set new timer for delayed search
820- timer :start (
821- 50 ,
822- 0 ,
823- vim .schedule_wrap (function ()
824- if
825- api
826- .nvim_get_current_line ()
827- :gsub (state .abbr_path or state .current_path , ' ' )
828- == text
829- then
830- local filtered_entries = {}
831- for _ , entry in ipairs (s .entries ) do
832- local match = vim .fn .matchfuzzypos ({ entry .name }, text )
833- if # match [3 ] > 0 and match [3 ][1 ] > 0 then
834- entry .match_pos = match [2 ][1 ]
835- entry .score = match [3 ][1 ]
836- table.insert (filtered_entries , entry )
837- end
838- end
839- table.sort (filtered_entries , function (a , b )
840- return a .score > b .score
841- end )
842- update_display (state , filtered_entries , false )
843- end
844- end )
845- )
912+ state .search_engine .search (state , query , function (filtered_entries )
913+ update_display (state , filtered_entries , false )
914+ end , 80 )
846915 end ,
847916 on_detach = function ()
848- if timer : is_active () then
849- timer : stop ()
917+ if state . search_engine then
918+ state . search_engine . destroy ()
850919 end
851- timer :close ()
852920 end ,
853921 })
854922 end
0 commit comments