@@ -499,14 +499,9 @@ function ProgressWindow:update_entry(plugin_name, status, message)
499499 }
500500
501501 if self .visible then
502- -- Only schedule if we're not already in main thread
503- if vim .in_fast_event () then
504- vim .schedule (function ()
505- self :refresh ()
506- end )
507- else
502+ Async .safe_schedule (function ()
508503 self :refresh ()
509- end
504+ end )
510505 end
511506
512507 return self
@@ -653,89 +648,127 @@ local function load_opts(opt)
653648 return type (opt ) == ' string' and vim .cmd (opt ) or opt ()
654649end
655650
651+ -- return a Promise
652+ function Plugin :load_scripts ()
653+ return function (callback )
654+ local plugin_path = self :get_path ()
655+ local plugin_dir = vim .fs .joinpath (plugin_path , ' plugin' )
656+
657+ if not isdir (plugin_dir ) then
658+ callback (Result .success (false ))
659+ return
660+ end
661+
662+ Async .scandir (plugin_dir )(function (result )
663+ if not result .success or not result .value then
664+ M .log (' debug' , string.format (' Plugin directory not found: %s' , plugin_dir ))
665+ callback (Result .success (false ))
666+ return
667+ end
668+
669+ local scripts = {}
670+ while true do
671+ local name , type = uv .fs_scandir_next (result .value )
672+ if not name then
673+ break
674+ end
675+ if type == ' file' and (name :match (' %.lua$' ) or name :match (' %.vim$' )) then
676+ scripts [# scripts + 1 ] = vim .fs .joinpath (plugin_dir , name )
677+ end
678+ end
679+
680+ if # scripts > 0 then
681+ Async .safe_schedule (function ()
682+ for _ , file_path in ipairs (scripts ) do
683+ vim .cmd .source (vim .fn .fnameescape (file_path ))
684+ end
685+ callback (Result .success (true ))
686+ end )
687+ else
688+ callback (Result .success (false ))
689+ end
690+ end )
691+ end
692+ end
693+
656694-- Load a plugin and its dependencies
657- function Plugin :load (opts )
695+ function Plugin :load ()
658696 if self .loaded then
659697 return true
660698 end
661699
662- -- Check if plugin exists
663- local stat = uv .fs_stat (self :get_path ())
664- if not stat or stat .type ~= ' directory' then
665- return false
666- end
700+ Async .async (function ()
701+ local plugin_path = self :get_path ()
702+ local stat = uv .fs_stat (plugin_path )
703+ if not stat or stat .type ~= ' directory' then
704+ self .status = STATUS .ERROR
705+ return false
706+ end
667707
668- -- Set loaded flag to prevent recursive loading
669- self .loaded = true
670- vim .g .strive_loaded = vim .g .strive_loaded + 1
708+ self .loaded = true
709+ vim .g .strive_loaded = vim .g .strive_loaded + 1
671710
672- if self .init_opts then
673- load_opts (self .init_opts )
674- end
711+ if self .init_opts then
712+ load_opts (self .init_opts )
713+ end
675714
676- if self .is_local then
677- -- For local plugins, add directly to runtimepath
678- local plugin_path = self :get_path ()
679- vim .opt .rtp :append (plugin_path )
715+ if self .is_local then
716+ vim .opt .rtp :append (plugin_path )
717+
718+ local after_path = vim .fs .joinpath (plugin_path , ' after' )
719+ if isdir (after_path ) then
720+ vim .opt .rtp :append (after_path )
721+ end
680722
681- -- Also check for and add the 'after' directory
682- local after_path = vim .fs .joinpath (plugin_path , ' after' )
683- if isdir (after_path ) then
684- vim .opt .rtp :append (after_path )
723+ local result = Async .try_await (self :load_scripts ())
724+ if result .error then
725+ M .log (
726+ ' error' ,
727+ string.format (' Failed to load scripts for %s: %s' , self .name , tostring (result .error ))
728+ )
729+ return
730+ end
731+ elseif self .is_lazy then
732+ vim .cmd .packadd (self .plugin_name )
685733 end
686- self :load_scripts ((opts and opts .script_cb ) and opts .script_cb or nil )
687- elseif self .is_lazy then
688- -- For non-local lazy plugins, use packadd
689- vim .cmd .packadd (self .plugin_name )
690- end
691734
692- self :call_setup ()
735+ self :call_setup ()
693736
694- self .status = STATUS .LOADED
695- if self .group_ids then
696- for _ , id in ipairs (self .group_ids ) do
697- api .nvim_del_augroup_by_id (id )
737+ self .status = STATUS .LOADED
738+ if self .group_ids and # self .group_ids > 0 then
739+ for _ , id in ipairs (self .group_ids ) do
740+ api .nvim_del_augroup_by_id (id )
741+ end
742+ self .group_ids = {}
698743 end
699- end
700744
701- local deps_count = # self .dependencies
702- -- Load dependencies in parallel
703- if deps_count > 0 then
704- Async .async (function ()
705- -- Pre-allocate the array with exact size
706- local dependency_promises = {}
707- local promise_count = 0
708-
709- -- Avoid creating unnecessary closures
710- for i = 1 , deps_count do
711- local dep = self .dependencies [i ]
712- if not dep .loaded then
713- promise_count = promise_count + 1
714- dependency_promises [promise_count ] = function (cb )
715- -- Reuse the same async function for all dependencies
716- Async .async (function ()
717- cb (Result .success (dep :load ()))
718- end )()
719- end
720- end
745+ local deps_to_load = {}
746+ for _ , dep in ipairs (self .dependencies ) do
747+ if not dep .loaded then
748+ table.insert (deps_to_load , dep )
721749 end
750+ end
722751
723- -- Only await if we have promises
724- if promise_count > 0 then
725- Async .await (Async .all (dependency_promises ))
752+ if # deps_to_load > 0 then
753+ local promises = {}
754+ for _ , dep in ipairs (deps_to_load ) do
755+ table.insert (promises , function (cb )
756+ Async .async (function ()
757+ dep :load ()
758+ cb (Result .success (true ))
759+ end )()
760+ end )
726761 end
727762
728- -- Run config after dependencies are loaded
729- if self .config_opts then
730- load_opts (self .config_opts )
731- end
732- end )()
733- else
734- -- No dependencies, run config immediately
763+ Async .await (Async .all (promises ))
764+ end
765+
735766 if self .config_opts then
736767 load_opts (self .config_opts )
737768 end
738- end
769+
770+ return true
771+ end )()
739772
740773 return true
741774end
@@ -799,45 +832,6 @@ function Plugin:ft(filetypes)
799832 return self
800833end
801834
802- function Plugin :load_scripts (callback )
803- Async .async (function ()
804- local plugin_path = self :get_path ()
805- local plugin_dir = vim .fs .joinpath (plugin_path , ' plugin' )
806- if not isdir (plugin_dir ) then
807- return
808- end
809-
810- local result = Async .try_await (Async .scandir (plugin_dir ))
811- if not result .success or not result .value then
812- M .log (' debug' , string.format (' Plugin directory not found: %s' , plugin_dir ))
813- return
814- end
815-
816- -- Collect all scripts first
817- local scripts = {}
818- while true do
819- local name , type = uv .fs_scandir_next (result .value )
820- if not name then
821- break
822- end
823- if type == ' file' and (name :match (' %.lua$' ) or name :match (' %.vim$' )) then
824- scripts [# scripts + 1 ] = vim .fs .joinpath (plugin_dir , name )
825- end
826- end
827-
828- if # scripts > 0 then
829- Async .safe_schedule (function ()
830- for _ , file_path in ipairs (scripts ) do
831- vim .cmd .source (vim .fn .fnameescape (file_path ))
832- end
833- if callback then
834- callback ()
835- end
836- end )
837- end
838- end )()
839- end
840-
841835-- Set up lazy loading for specific commands
842836function Plugin :cmd (commands )
843837 self .is_lazy = true
@@ -863,16 +857,12 @@ function Plugin:cmd(commands)
863857 local args = opts .args ~= ' ' and (' ' .. opts .args ) or ' '
864858 local bang = opts .bang
865859
866- if self .is_local then
867- self :load ({
868- script_cb = function ()
869- execute (name , bang , args )
870- end ,
871- })
872- return
873- end
874- self :load ()
875- execute (name , bang , args )
860+ Async .async (function ()
861+ self :load ()
862+ Async .safe_schedule (function ()
863+ execute (name , bang , args )
864+ end )
865+ end )()
876866 end , {
877867 nargs = ' *' ,
878868 bang = true ,
@@ -1563,44 +1553,57 @@ function M.clean()
15631553 table.insert (to_remove , { name = name , dir = dir })
15641554 end
15651555 end
1556+ if # to_remove == 0 then
1557+ vim .notify (' [Strive]: no plugins to remove' )
1558+ end
15661559
15671560 -- Show plugins that will be removed
1568- if # to_remove > 0 then
1569- M .log (' info' , string.format (' Found %d unused plugins to clean:' , # to_remove ))
1570- for _ , item in ipairs (to_remove ) do
1571- local path = vim .fs .joinpath (item .dir , item .name )
1572- M .log (' info' , string.format (' Will remove: %s' , path ))
1573- end
1574-
1575- -- Perform the actual deletion
1576- M .log (' info' , ' Starting deletion process...' )
1577-
1578- -- Process deletions one by one to ensure completion
1579- for _ , item in ipairs (to_remove ) do
1580- local path = vim .fs .joinpath (item .dir , item .name )
1581- M .log (' info' , string.format (' Removing %s' , path ))
1561+ M .log (' info' , string.format (' Found %d unused plugins to clean:' , # to_remove ))
1562+ ui :open ()
1563+ for _ , item in ipairs (to_remove ) do
1564+ local path = vim .fs .joinpath (item .dir , item .name )
1565+ M .log (' info' , string.format (' Will remove: %s' , path ))
1566+ ui :update_entry (item .name , ' PENDING' , ' Marked to removal' )
1567+ end
15821568
1583- -- Use vim.fn.delete synchronously to ensure completion
1584- local ok , result_or_err = pcall (function ()
1585- return vim .fn .delete (path , ' rf' )
1586- end )
1569+ -- Perform the actual deletion
1570+ M .log (' info' , ' Starting deletion process...' )
1571+
1572+ vim .ui .select (
1573+ { ' Yes' , ' No' },
1574+ { prompt = string.format (' Remove %d unused plugins?' , # to_remove ) },
1575+ function (choice )
1576+ if choice and choice :lower ():match (' ^y' ) then
1577+ -- Process deletions through TaskQueue
1578+ local task_queue = TaskQueue .new (DEFAULT_SETTINGS .max_concurrent_tasks )
1579+
1580+ for _ , item in ipairs (to_remove ) do
1581+ task_queue :enqueue (function (done )
1582+ Async .async (function ()
1583+ local path = vim .fs .joinpath (item .dir , item .name )
1584+ ui :update_entry (item .name , ' CLEANING' , ' Removing...' )
1585+
1586+ -- Delete and handle errors
1587+ local ok , result = pcall (vim .fn .delete , path , ' rf' )
1588+ if not ok or result ~= 0 then
1589+ ui :update_entry (item .name , ' ERROR' , ' Failed to remove' )
1590+ else
1591+ ui :update_entry (item .name , ' REMOVED' , ' Successfully removed' )
1592+ end
1593+ done ()
1594+ end )()
1595+ end )
1596+ end
15871597
1588- if not ok then
1589- M . log ( ' error ' , string.format ( ' Error deleting %s: %s ' , path , tostring ( result_or_err ) ))
1590- elseif result_or_err ~= 0 then
1591- M . log ( ' error ' , string.format ( ' Failed to delete %s, return code: %d ' , path , result_or_err ) )
1598+ task_queue : on_complete ( function ()
1599+ Async . await ( Async . delay ( 2000 ))
1600+ ui : close ()
1601+ end )
15921602 else
1593- M . log ( ' info ' , string.format ( ' Successfully removed %s ' , path ) )
1603+ ui : close ( )
15941604 end
1595-
1596- -- Add a small delay to ensure file system operations complete
1597- Async .await (Async .delay (100 ))
15981605 end
1599-
1600- M .log (' info' , ' Clean operation complete.' )
1601- else
1602- M .log (' info' , ' No unused plugins to clean.' )
1603- end
1606+ )
16041607 end )()
16051608end
16061609
0 commit comments