sxyazi / yazi

💥 Blazing fast terminal file manager written in Rust, based on async I/O.
https://yazi-rs.github.io
MIT License
15.6k stars 356 forks source link

[Feature request] server/client architecture #639

Closed oredaze closed 6 months ago

oredaze commented 8 months ago

Is your feature request related to a problem? Please describe

I love the idea of switching everything I use to rust. So next on the line is switching away from Lf.

I appear to be missing a killer feature that is present in Lf, which is the ability to have multiple instances of your file manager communicate with each other (copy/paste from one to the other and such).

The idea is that your window manager is responsible for tabbing/arranging multiple windows, etc., not the file manager.

Will you be willing to contribute this feature?

Describe the solution you'd like

The first run of yazi starts the server/daemon and each subsequent launches of an instance try to connect if server is found.

Describe alternatives you've considered

No response

Additional context

I am not a programmer, so don't know if my request is reasonable... Would totally understand if it is too late to implement such a thing.

og900aero commented 8 months ago

Ohh noo. Dont be the server/client operation. This mode of operation is also one of the biggest disadvantages of lf, and it cannot use tabs feature.

Abebe123000 commented 8 months ago

I want this feature too. It is very useful.

rrveex commented 7 months ago

Ohh noo. Dont be the server/client operation. This mode of operation is also one of the biggest disadvantages of lf, and it cannot use tabs feature.

Why is that a disadvantage?

I think it's a matter of getting used to (workflow). When I switched from ranger to lf, I missed the tabs. Now on yazi I miss the "common" or "global" yanked/cut from lf and almost don't use tabs at all (yet)

My brain thinks in terms of "where am I now?" and somehow correlates say "a project" (like yazi git tree) or "a place" (like ~/.config) with something both visual and "action to go there" (like cd or "switch to workspace 3, look at left side of screen").

Coincidentally, during my ranger times, I didn't use workspaces much, so having several places (== tabs) in the same window was an advantage. The "where" was "tab number" in the single file manager instance, (almost) always on screen.

Now on tiling WMs, the "were" has become a workspace on which I usually have one file manager + some other project related window(s). So I don't need (that many) tabs, but the "global" yanked would be nice.

Doesn't have to be "server/client" though. lf actually just keeps the list in ~/.local/share/files. Something similar is easily doable in yazi with a plugin. Just pipe the yank/cut/paste operations through a plugin which does the "accounting" and then ya.manager_emit(yank/...).

sxyazi commented 7 months ago

Yeah, these features do not necessarily need to be implemented through a client/server architecture imo.

I have been thinking recently about introducing incremental logging or plugin hooks for Yazi. The former would semi-real-time log user actions (like directory changes, hovering file changes, etc.), while the latter could trigger one or several Lua plugins when users perform these tasks, such as cd, allowing these plugins to perform almost any task.

These are two completely different working modes, and I need to think about them carefully. Maybe I can propose an RFC for it in the near future, of course, any ideas are welcome!

og900aero commented 7 months ago

Ohh noo. Dont be the server/client operation. This mode of operation is also one of the biggest disadvantages of lf, and it cannot use tabs feature.

Why is that a disadvantage?

I think it's a matter of getting used to (workflow). When I switched from ranger to lf, I missed the tabs. Now on yazi I miss the "common" or "global" yanked/cut from lf and almost don't use tabs at all (yet)

It is not necessary because:

  1. The server/client solution consumes more memory and processes because it starts more instances
  2. There is no need to start multiple instances, because using tab is like running multiple yazis
  3. To use the server/client, you have to start it in multiple instances, but it also takes up more space on the monitor by opening it in multiple instances. This is precisely why the tab system is suitable, because it uses the screen area more economically.

Personally, I really like lf because it is light, small, fast and easy to program with the bash language, but due to the server/client solution, more professional use has been shot down (for example, system administrator, system engineer, programmer, etc. In their case, tab is more convenient use)

rrveex commented 7 months ago

(for example, system administrator, system engineer, programmer, etc. In their case, tab is more convenient use)

I agree with "sysadmin remote" so that one doesn't need multiple ssh sessions. Otherwise I still think it's a matter of preference - on multiple workspaces, I don't want a single file manager instance, I want one on every workspace.

These are two completely different working modes, and I need to think about them carefully. Maybe I can propose an RFC for it in the near future, of course, any ideas are welcome!

I'd like to start commenting right away :) I find lua plugins even now pretty difficult to write. Portable vs. shell, that's true, but difficult. I spent (wasted) half a day on this:

[...]
for ...
  selected[#selected + 1] = tostring(f.name)
[...]
local cmd = Command("rsync"):args({ "-a", "--info=progress2" })
-- cmd = cmd:args(selected)  -- <------------------------- this doesn't work ?!!
for _, a in ipairs(selected) do
    cmd = cmd:arg(a)
end
cmd = cmd:arg(destination):stdout(Command.PIPED)

I have no idea how to debug the commented out "doesn't work". Changed rsync with echo, logged the :output() with ya.err, copy/pasted the arguments in a shell to rsync and it works. This kind of experience tends to turn me away and, unlike OP, I am a programmer.

The point is: elegant architecture is difficult to use. Keeping stuff in a file is barbaric, but easy. When performance doesn't matter (like this issue), I prefer easy.

Don't know if this helps :/ Pleasing various users is hard (that's why I don't publish what I write, shame on me, praise to @sxyazi )

sxyazi commented 7 months ago

I find lua plugins even now pretty difficult to write. Portable vs. shell, that's true, but difficult. I spent (wasted) half a day on this: I have no idea how to debug the commented out "doesn't work". Changed rsync with echo, logged the :output() with ya.err, copy/pasted the arguments in a shell to rsync and it works. This kind of experience tends to turn me away and, unlike OP, I am a programmer.

I'm sorry to hear about your experience, but I don't see any obvious errors in the code you've provided, this could be a bug, would you like to create a bug report for it? Although the implementation for args() and arg() are almost the same, except that args() is in a loop:

https://github.com/sxyazi/yazi/blob/5169bb90f259b4b5827a1fb153d0ed6871b80e2c/yazi-plugin/src/process/command.rs#L39-L51

Yazi's plugin system is still in its very early stages and has a lot of room for improvement, especially for the functional plugin you're working on, I just removed the "coming soon" from the README 3 days ago. With that said, I just hope you won't be completely disappointed with it, we are creating something entirely new, which is certainly challenging, especially in the early stages :)

The point is: elegant architecture is difficult to use. Keeping stuff in a file is barbaric, but easy. When performance doesn't matter (like this issue), I prefer easy.

This is also what I am currently considering, but there are some differences from your thoughts. I think files will have better performance, while plugins will be easier to write. Since the file contains a large number of different events, Yazi must implement and define a serialization format, and require all programs that need to listen to Yazi events to parse it.

Its content may look like this (but will eventually be much more complex, for example, when there are line breaks in the path, they must be escaped. see https://github.com/sxyazi/yazi/issues/709 and https://github.com/sxyazi/yazi/issues/760):

hover,/path/to/file1
hover,/path/to/file2
hover,/path/to/file3
cd,/path/foo
rename,/path/to/a,/path/to/b
select,/path/to/1,/path/to/2,/path/to/3
cd,/path/bar

(The rename event here can also address https://github.com/sxyazi/yazi/issues/766.)

For incremental updates, we cannot use existing formats like JSON/XML, so we must define our own. With plugins, after the event is triggered, the thing only needed is to call the plugin, and then get these data in the plugin through cx easily without having to parse them.

But plugins may have performance issues because we cannot predict what instructions will be included in the user's plugin, which is an uncontrollable factor, especially for events that can be triggered very frequently like hover.

A file, on the other hand, is like a queue. We just need to flush the content into the file, without considering any other work. Programs that need it will read it and process it.

rrveex commented 7 months ago

I'm not sure I understand. Do you mean to keep a journal, out of which the state (including things like the list of yanked files) can be computed by a newly started instance of yazi?

If so, I find the sheer amount of permutations of "which parts of the state do I want to inherit in the new instance" overwhelming :)

How about another approach: we make poll for a list of use-cases (e.g. "yanked list" would be one of them) and perhaps a separate (trivial) solution for each use-case could be "better" than a generic solution?

gwww commented 7 months ago

@sxyazi This is a follow up to my comment on Discord around being able to save/restore yazi state.

How I was hoping to implement this was as a plugin, where events are fired when things happen in yazi. The specific events I was looking for were a startup and shutdown event. The startup event would load state from a file, and shutdown would save state to a file. Of course state needs to be available through the API. State should probably have a version number in it so that changes in API can be handled gracefully.

If you are familiar with wezterm, it has events that fire. You can see an example in my dotfiles or read the Wezterm docs.

sxyazi commented 6 months ago

Done by https://github.com/sxyazi/yazi/pull/826, I will add plugin API documentation to https://yazi-rs.github.io in the next few days.

github-actions[bot] commented 5 months ago

I'm going to lock this issue because it has been closed for 30 days. ⏳ This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.