Skip to content

Commit 216ff28

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 cfe2e74 commit 216ff28

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',
@@ -530,4 +545,48 @@ function Attach:open_in_vim(name, node)
530545
:wait(MAX_TIMEOUT)
531546
end
532547

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

0 commit comments

Comments
 (0)