Skip to content

Commit 68f49fd

Browse files
committed
feat(attach): add OrgAttach:delete_one() and delete_all()
These correspond to the following Emacs functions: - `org-attach-delete-one` - `org-attach-delete-all`
1 parent 846d593 commit 68f49fd

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

lua/orgmode/attach/core.lua

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,4 +444,57 @@ function AttachCore:open_in_vim(name, node)
444444
vim.cmd.edit(path)
445445
end
446446

447+
---Delete a single attachment.
448+
---
449+
---@param node OrgAttachNode
450+
---@param name string the name of the attachment to delete
451+
---@return OrgPromise<nil>
452+
function AttachCore:delete_one(node, name)
453+
if name == '' then
454+
utils.echo_warning('No attachment selected')
455+
return Promise.resolve()
456+
end
457+
local attach_dir = self:get_dir(node)
458+
local path = vim.fs.joinpath(attach_dir, name)
459+
return fileops.unlink(path):next(function()
460+
return nil
461+
end)
462+
end
463+
464+
---Delete all attachments from the current outline node.
465+
---
466+
---This actually deletes the entire attachment directory. A safer way is to
467+
---open the directory with `reveal` and delete from there.
468+
---
469+
---@param node OrgAttachNode
470+
---@param recursive fun(): OrgPromise<boolean>
471+
---@return OrgPromise<string> deleted_dir
472+
function AttachCore:delete_all(node, recursive)
473+
local attach_dir = self:get_dir(node)
474+
-- A few synchronous FS operations here, can't really be avoided. The
475+
-- alternative would be to evaluate `recursive` before it's necessary.
476+
local uv = vim.uv or vim.loop
477+
local ok, errmsg, err = uv.fs_unlink(attach_dir)
478+
if ok then
479+
return Promise.resolve()
480+
elseif err ~= 'EISDIR' then
481+
return Promise.reject(errmsg)
482+
end
483+
ok, errmsg, err = uv.fs_rmdir(attach_dir)
484+
if ok then
485+
return Promise.resolve()
486+
elseif err ~= 'ENOTEMPTY' then
487+
return Promise.reject(errmsg)
488+
end
489+
return recursive():next(function(do_recursive)
490+
if not do_recursive then
491+
return Promise.reject(errmsg)
492+
end
493+
return fileops.remove_directory(attach_dir, { recursive = true }):next(function()
494+
node:remove_auto_tag()
495+
return attach_dir
496+
end)
497+
end)
498+
end
499+
447500
return AttachCore

lua/orgmode/attach/init.lua

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ function Attach:prompt()
107107
return self:reveal_nvim()
108108
end,
109109
})
110+
menu:add_separator({ length = #menu.title })
111+
menu:add_option({
112+
label = 'Delete an attachment',
113+
key = 'd',
114+
action = function()
115+
return self:delete_one()
116+
end,
117+
})
118+
menu:add_option({
119+
label = 'Delete all attachments.',
120+
key = 'D',
121+
action = function()
122+
return self:delete_all()
123+
end,
124+
})
110125
menu:add_option({
111126
label = 'Set specific attachment directory for this task.',
112127
key = 's',
@@ -527,4 +542,48 @@ function Attach:open_in_vim(name, node)
527542
:wait(MAX_TIMEOUT)
528543
end
529544

545+
---Delete a single attachment.
546+
---
547+
---@param name? string the name of the attachment to delete
548+
---@param node? OrgAttachNode
549+
---@return nil
550+
function Attach:delete_one(name, node)
551+
node = node or self.core:get_current_node()
552+
local attach_dir = self.core:get_dir(node)
553+
return Promise.resolve(name or ui.select_attachment('Delete', attach_dir))
554+
:next(function(chosen_name)
555+
if not chosen_name then
556+
return
557+
end
558+
return self.core:delete_one(node, chosen_name)
559+
end)
560+
:wait(MAX_TIMEOUT)
561+
end
562+
563+
---Delete all attachments from the current outline node.
564+
---
565+
---This actually deletes the entire attachment directory. A safer way is to
566+
---open the directory with `reveal` and delete from there.
567+
---
568+
---@param force? boolean if true, delete directory will recursively deleted with no prompts.
569+
---@param node? OrgAttachNode
570+
---@return nil
571+
function Attach:delete_all(force, node)
572+
node = node or self.core:get_current_node()
573+
return Promise.resolve(force or ui.yes_or_no_or_cancel_slow('Remove all attachments? '))
574+
:next(function(do_delete)
575+
if not do_delete then
576+
return Promise.reject('Cancelled')
577+
end
578+
return self.core:delete_all(node, function()
579+
return Promise.resolve(force or ui.yes_or_no_or_cancel_slow('Recursive? '))
580+
end)
581+
end)
582+
:next(function()
583+
utils.echo_info('Attachment directory removed')
584+
return nil
585+
end)
586+
:wait(MAX_TIMEOUT)
587+
end
588+
530589
return Attach

0 commit comments

Comments
 (0)