Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add org-attach feature #878

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open

Conversation

troiganto
Copy link
Contributor

Hi, apologies for dropping such a large PR in your lap!

I've been working on-and-off on attachments and it's in a state now where I believe it works as intended and very similarly to the Emacs version. 🙂

The feature seemed simple., but ended up ballooning because:

  1. the Emacs version uses a lot of Emacs machinery that nvim doesn't have (recursive copying, downloads);
  2. I ended up having to modify several utils functions (like orgmode.utils.fs.substitute_path) for my use case.

I've split commits up as much as I could to make accepting/rejecting each individual change easier. I've also split the implementation of org-attach into 6-7 commits to make it easier to review them.

To be clear, I don't expect all changes to go into the main repository. Let me know if there are any design decisions that you'd rather not commit to maintaining. 😉

Some of my more dubitable choices:

  1. I wrapped a lot of vim.uv.fs_* functions in a module orgmode.attach.fs. I simply couldn't figure out how to write an async recursive copy any other way.
  2. I wrote a few new dialogs in orgmode.attach.ui. Some look weird because they replicate Emacs dialogs (yes_or_no_or_cancel_slow() is the equivalent of yes-or-no-p) and might not necessarily make sense in nvim.
  3. The Emacs version wildly interleaves logic and user interaction. Elisp has a lot of macros that make this work, unlike Lua. To keep the code clear, I've put the actual logic into orgmode.attach.core and wrapped it in orgmode.attach. This separation required passing around a few callbacks and effectively writing every function twice. It may be more work than it's worth.

Thanks again for keeping this project going, it's been helping me a lot!

@kristijanhusak
Copy link
Member

Hey! Thanks for the PR!
I still didn't look into it completely, just took a brief look.
I noticed that as part of this PR you added properties inheritance.

To make things simpler, can we extract this into its own PR?
The same applies to anything else that I maybe missed, and is not strictly related to attachments.

@troiganto
Copy link
Contributor Author

Sure thing 👍 I'll mark this PR as a draft in the meantime, to keep things organized

@troiganto troiganto marked this pull request as draft January 30, 2025 22:11
@troiganto troiganto force-pushed the feat/attach branch 2 times, most recently from 299f922 to 57ca4e8 Compare January 30, 2025 23:11
@troiganto troiganto force-pushed the feat/attach branch 2 times, most recently from 71802a5 to 2ff76bd Compare February 1, 2025 21:05
@kristijanhusak
Copy link
Member

@troiganto I merged the other PR and rebased this one to resolve conflicts.
If there's nothing else to add, move this to "ready to review" and I'll start looking at it.

@kristijanhusak
Copy link
Member

@troiganto can this be reviewed or there are still pending changes?

@troiganto troiganto marked this pull request as ready for review February 17, 2025 16:21
@troiganto
Copy link
Contributor Author

Hi @kristijanhusak ! Thanks for following up, I've been racking my brain how to split the remaining changes up so that they make sense to a reviewer.

The current set of commits is the best I could do. It would be nice if you could at least look over the very first commit. I consider it an MVP, but it very obviously is not a complete implementation (misses e.g. deleting/opening/linking attachments). Let me know how you would handle this PR going forward, eg whether to split it further up.

I'm traveling the next two weeks, so I've got a bit less time, so response times should be a bit longer, but otherwise can make any changes that are necessary or desirable.

@kristijanhusak
Copy link
Member

I appreciate the effort to split this into smaller chunks for reviewing, it helps tremendously. I also understand that there's probably the point where splitting is really hard to achieve.
If the whole PR is completed, I'll look at it, and leave feedback. If it's mostly good (which looks like it at the first glance), I'll probably merge it and address smaller things myself, and any bigger things we can discuss as an improvement in the next PRs.
If that sounds good, let me know, and I'll look into this in the following days.
Thanks!

@troiganto
Copy link
Contributor Author

Sounds good to me!

I've also gone over the commits once more and added notes to the commit messages, which Emacs functions are ported in which commit. This makes the reasoning behind each addition (hopefully!) a bit clearer.

Copy link
Member

@kristijanhusak kristijanhusak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very impressive work!

I left a few comments, and gave it a quick test.
It seems we need to address some minor things:

  1. We need to wrap something into vim.schedule
  2. Seems like non-absolute links are not working correctly

Here's a video for reference:

attach.mp4

---@return OrgPromise<string> tmpfile
local function netrw_read(url)
return Promise.new(function(resolve, reject)
if not vim.g.loaded_netrwPlugin then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How complex would it be to not depend on netrw? A lot of users nowadays disable netrw and use other solutions like nvim-tree, neo-tree and newly introduced Snacks.explorer().
I didn't look into netrw implementation, but we can start with something simple that should work most of the time, for example using only curl, and then we can expand it later. What do you think?


---Remove the `org_attach_auto_tag`.
function AttachNode:remove_auto_tag()
-- TODO: There is currently no way to set #+FILETAGS programmatically. Do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can create a separate issue for this and link it here, just so we don't miss it.

@@ -0,0 +1,4 @@
---@param event OrgHeadlineArchivedEvent
return function(event)
require('orgmode.attach'):maybe_delete_archived(event.headline)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to reference attach from the orgmode instance.

Suggested change
require('orgmode.attach'):maybe_delete_archived(event.headline)
require('orgmode').attach:maybe_delete_archived(event.headline)

@troiganto troiganto force-pushed the feat/attach branch 3 times, most recently from d83e85f to e8d21ec Compare March 18, 2025 13:08
troiganto added 12 commits March 21, 2025 12:22
The implementation follows the Emacs implementation in spirit, with some
additional separation of concerns:

1. the actual logic is implemented in `attach/core.lua`, with no
   concerns for UI or configuration;
2. `attach/init.lua` combines the core with UI and configuration and
   provides the public API;
3. `attach/ui.lua` contains any dialogs needed by the module;
4. `attach/fileops.lua` contains file operations that are provided to
   the Emacs implementation by the Emacs core (e.g. recursive
   copy/deletion of directories)
5. `attach/node.lua` provides an abstraction over headlines and whole
   files that is absent in the Emacs implementation
6. `attach/translate_id.lua` corresponds to the Emacs implementation's
   functions `org-attach-id-uuid-folder-format`,
   `org-attach-id-ts-folder-format`, and
   `org-attach-id-fallback-folder-format`. They are separated like this
   because referring to pre-defined functions in Lua is more difficult
   than in Emacs.

To reduce complexity, the following functions are left unimplemented in
this commit:

- `org-attach-file-list`
- `org-attach-expand`
- `org-attach-follow`
- `org-attach-complete-link`
- `org-attach-reveal`
- `org-attach-reveal-in-emacs`
- `org-attach-open`
- `org-attach-open-in-emacs`
- `org-attach-delete-one`
- `org-attach-delete-all`
- `org-attach-sync`
- `org-attach-archive-delete-maybe`
- `org-attach-expand-links`
- `org-attach-url`
- `org-attach-dired-to-subtree`
This adds functions that correspond to these Emacs functions:

- `org-attach-reveal`
- `org-attach-reveal-in-emacs`
- `org-attach-open`
- `org-attach-open-in-emacs`
These correspond to the following Emacs functions:
- `org-attach-delete-one`
- `org-attach-delete-all`
This corresponds to the Emacs function `org-attach-sync`.
This provides functionality provided by the following functions in the
Emacs implementation:

- `org-attach-file-list`
- `org-attach-expand`
- `org-attach-follow`
- `org-attach-complete-link`
These are implemented in Emacs as the hooks
`org-attach-after-change-hook` and `org-attach-open-hook`.
This corresponds to the Emacs function
`org-attach-archive-delete-maybe`.
This corresponds to the Emacs function `org-attach-expand-links`.
This brings the feature in line with capture and agenda, which similarly
have a top-level menu prompt.
This corresponds to the Emacs function `org-attach-url`. It also adds
some functionality from the Emacs core that we don't get for free.
This reverts commit 272da8c217455f2fde59411f59e5e51ea44a3125.
This function has no direct Emacs equivalent. However, it tries to
replicate the functionality provided by `org-attach-dired-to-subtree`,
which attaches files selected in a dired window (roughly equivalent to
Vim's NetRW) to the last-seen Orgmode task.

Our implementation tries to be extremely general because the Neovim
ecosystem has a plethora of file browser plugins.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants