@@ -499,14 +499,9 @@ function ProgressWindow:update_entry(plugin_name, status, message)
499
499
}
500
500
501
501
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()
508
503
self:refresh()
509
- end
504
+ end)
510
505
end
511
506
512
507
return self
@@ -653,89 +648,127 @@ local function load_opts(opt)
653
648
return type(opt) == 'string' and vim.cmd(opt) or opt()
654
649
end
655
650
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
+
656
694
-- Load a plugin and its dependencies
657
- function Plugin:load(opts )
695
+ function Plugin:load()
658
696
if self.loaded then
659
697
return true
660
698
end
661
699
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
667
707
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
671
710
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
675
714
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
680
722
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)
685
733
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
691
734
692
- self:call_setup()
735
+ self:call_setup()
693
736
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 = {}
698
743
end
699
- end
700
744
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)
721
749
end
750
+ end
722
751
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)
726
761
end
727
762
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
+
735
766
if self.config_opts then
736
767
load_opts(self.config_opts)
737
768
end
738
- end
769
+
770
+ return true
771
+ end)()
739
772
740
773
return true
741
774
end
@@ -799,45 +832,6 @@ function Plugin:ft(filetypes)
799
832
return self
800
833
end
801
834
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
-
841
835
-- Set up lazy loading for specific commands
842
836
function Plugin:cmd(commands)
843
837
self.is_lazy = true
@@ -863,16 +857,12 @@ function Plugin:cmd(commands)
863
857
local args = opts.args ~= '' and (' ' .. opts.args) or ''
864
858
local bang = opts.bang
865
859
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)()
876
866
end, {
877
867
nargs = '*',
878
868
bang = true,
@@ -1563,44 +1553,57 @@ function M.clean()
1563
1553
table.insert(to_remove, { name = name, dir = dir })
1564
1554
end
1565
1555
end
1556
+ if #to_remove == 0 then
1557
+ vim.notify('[Strive]: no plugins to remove')
1558
+ end
1566
1559
1567
1560
-- 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
1582
1568
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
1587
1597
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 )
1592
1602
else
1593
- M.log('info', string.format('Successfully removed %s', path) )
1603
+ ui:close( )
1594
1604
end
1595
-
1596
- -- Add a small delay to ensure file system operations complete
1597
- Async.await(Async.delay(100))
1598
1605
end
1599
-
1600
- M.log('info', 'Clean operation complete.')
1601
- else
1602
- M.log('info', 'No unused plugins to clean.')
1603
- end
1606
+ )
1604
1607
end)()
1605
1608
end
1606
1609
0 commit comments