Welcome to Vifm Q&A, where you can ask questions about using Vifm. Registration is optional, anonymous posts are moderated. E-mail and GitHub logins are enabled.
0 votes
in vifm by

Hi,

The Context

I'm using git annex, which stores files as symlinks in a git repository. And while browsing such repository works great with VIFM, moving files around is not that perfect because when moving files from one panel to another with dd and p works, and git and git annex are able to detect such move most of the time, ideally moving symlinks in such a repo should be done with git mv command.

If I understand the current behavior of VIFM correctly, dd puts files (including symlinks) into .trash folder + a buffer, and when pasting, it's putting them from the .trash to a target folder. If this understanding is correct, then the goal is:
1. on dd for a symlink do not move it into .trash
2. on p run git mv <source-parent-dir>/* <target-dir> (if all files in the source are selected)
3. if not: on p run git mv <buffer>/* <target-dir>

The Question

So, what I would like to know - is it possible to override the default dd/p behavior? Maybe with a plugin (if there is no an easier way)?

Other Notes

P.S. I've tried to look into the existing plugins implementation, but couldn't find anything similar. I'm also considering forking the repo, "patching" such behavior and building from source. But it's not very sustainable approach. Also, I'm not familiar with the code base, and that's why will appreciate any ideas, hints (entry points, etc) and any other help with the subject.

1 Answer

+1 vote
by

Hello,

There is no way to overwrite how an operation is handled. The closest thing is probably app.fsop event in Lua API. It won't change what Vifm does, but will allow following up its actions with your own, in this case:

  • when a file is moved to trash: record some metadata to decide how to handle pasting it
  • when a file is moved out of trash: run git mv <src> <dst> (if source needs to exist, the metadata can be used to effectively undo file removal first)

I've never used git annex, just looked up that it's similar to git lfs, so I might be misunderstanding the context here, but it seems doable that way. Creating or removing symlinks is cheap, so extra operations shouldn't be a problem, but can mess up undo/redo.

by

Thanks for the directions! The most important in this context about git annex is that it stores files in .git/annex/objects/... and all big files in a repository that it manages are basically symbolic links to files in .git/annex/objects/.... So, when I move a big file in a git annex enabled repository, I'm moving a symbolic link (always from one folder to another in the same repo); and files in .git/annex/objects/... are never touched by this operation. But it's correct, it's purpose is very close to git LFS - manage big files (though they achieve it differently).

And another "why" I want to use git mv - is that it works super fast with git annex (practically, no delays), while when I move a symbolic link with VIFM (or any other file manager) and do git add -A, it takes a couple of seconds (around 5-15) at least in my repository. And it's very inconvenient.

Creating or removing symlinks is cheap, so extra operations shouldn't be a problem, but can mess up undo/redo.

Yep, that should be cheap. Not ideal, but if to do it by the book requires rewriting entire VIFM, then using app.fsop is definitely a way to go.

by
edited by

@xaizek While implementing the suggested approach I've realized that there is no way to detect the end of the "move" operation. When fsop.move event is firing for several files (e.g. "a.txt", "b.txt") when I want to record it, I should know that "b.txt" was the last file during the "move". Because if after recording "{a,b}.txt" I also dd "c.txt" and "d.txt" (into trash) in a separate batch, then how can I know that it's a separate operation?

The only thing that comes to mind is to only filter by files during "fromtrash" operation.

Update 1: manage to achieve it by filtering in "fromtrash" op, but now having another issue : during the event I call vifm.fs.mv(dst, src) and it produces an error attempt to index a nil value (field 'fs') on ViFM 0.13.

I've seen in the docs that vifm.fs.mv also "generate |vifm-lua-event-app.fsop| event" and it's probably the reason why it's disabled in the event itself? Are there any workarounds?

Update 2: managed to solve this problem by just using vifm.run("mv <src> <dst>") and finished the "proof of concept" plugin. Thanks for the help one more time!

by

Here is the draft version of the plugin on github: vifm-git-mv

by

I didn't really understand that part of the question where you talked about multiple files. Specifically, why parent path is needed when separate files are being processed.

during the event I call vifm.fs.mv(dst, src) and it produces an error attempt to index a nil value (field 'fs') on ViFM 0.13.

My bad, I gave the link to the master branch because that file-system event is present in v0.13 as well, but that version of the documentation has some things which will be new in v0.14, like vifm.fs. You can use the local /usr/share/vifm/vim-doc/doc/vifm-lua.txt file instead.

I've seen in the docs that vifm.fs.mv also "generate |vifm-lua-event-app.fsop| event" and it's probably the reason why it's disabled in the event itself?

There is no issue with that, dispatching callbacks is delayed, so such recursion is not possible.

vifm.run

That can cause flickering, better use vifm.startjob(), maybe in a wrapper:

local function run(cmd)
    return vifm.startjob({ cmd = cmd }):exitcode()
end
by

I didn't really understand that part of the question where you talked about multiple files.

Sorry, that might not be clear indeed. When I talked about multiple files, I've just tried to show that there is no way to detect the end of a move (when all files are processed by dd) in the event, which in turn was needed to clear a lua table in my implementation containing previous dd items. At first, it seemed like a temporal coupling to me, but it's probably not that important. That was solved by clearing the lua table in dd after another (non-dd) operation (the update 1).

Specifically, why parent path is needed when separate files are being processed.

I don't remember mentioning that parent path is needed. Though, in the implementation I'm getting the parent path in case of a directory because of the specific of git mv <src-path-plus-dir-name> <dst-path-no-dir-name>/<dst-dir-name> - for moving a folder, it requires the <dst-dir-name> to not be present in <dst-path-no-dir-name>. But ViFMs event.target contains <dst-path-plus-dir-name>, and that's why getParentDir(<dst-path-plus-dir-name>) was needed.

That can cause flickering, better use vifm.startjob(), maybe in a wrapper

Thanks for the suggestion. I've noticed the flickering, but wanted to finish the poc as fast as possible. Will use that later.

...