zellij-org / zellij

A terminal workspace with batteries included
https://zellij.dev
MIT License
20.69k stars 640 forks source link

Plugin API Requests #280

Open TheLostLambda opened 3 years ago

TheLostLambda commented 3 years ago

This issue is meant to be a thread for suggesting / requesting new features in the plugin API. Early plugins will likely require new API to be added, but we'll do our best to be responsive and help add the new API where needed!

Some current things on the docket:

horasal commented 3 years ago

Request: logging api

Current zellij does not provide any logging method (nor for zellij itself except a function for dumping terminal content). As plugin can not simply use println(or simple_log, etc) for logging, debug may be challenging. Providing a log api will be helpful for debugging plugin as well as reporting issues. Furthermore, this will also be useful for zellij itself.

Rust already have many good logging library, such as log and tracing. Plugin can just import them and use warn!, debug! etc to log.

register_plugin! macro should also generate some codes to catch messages from log, and send them to zellij. For example, it can be

lazy_static! {
    static ref LOGGER: ZellijPluginLogger = ZellijPluginLogger::default();
}

// ... many codes

impl Log for ZellijPluginLogger {
    fn enabled(&self, _: &Metadata) -> bool { 
        check_zellij_availabre() 
    }
    fn log(&self, record: &Record) {
        if self.enabled() { send_log(record) } 
        // .... many codes
    }
}

fn send_log_to_zellij() {
    let _ = log::set_logger(&*LOGGER);
}

zellij-tile should provide an API for send log, e.g. send_log. Furthermore, zellij should also provide a way to check these logs. Here are several approaches:

  1. provide a special tab for logging. This tab can be toggled by a command or option.
  2. write all logs to a file

Remaining Problems

a-kenji commented 3 years ago

Make Plugins Configurable #317.

TheLostLambda commented 3 years ago

@horasal Thanks for the suggestion! I think this is certainly a feature I'd like to have in the API eventually! I do have a currently implemented alternative if you're looking for something simple though :slightly_smiling_face:

This section of the "Writing a Plugin" guide shows off how simple Rust debugging is possible with the dbg!() macro and shell redirection of stderr. Though it's obviously not a robust solution, and I've only coded some simple plugins, cargo make run 2> dbg.log, tail -f dbg.log, and some dbg!() statements in Rust work like a charm (in both plugins and the main program). This is also nice because every language can print to stderr somehow.

Let me know what you think!

horasal commented 3 years ago

@TheLostLambda I tried dbg! during working on https://github.com/zellij-org/zellij/pull/319 , it works and is useful for debugging. However, I think there are still some issues remaining:

TheLostLambda commented 3 years ago

@horasal Those are some quite good points! I would agree that this sort of logging capability would be really nice to have! I do think that Zellij itself will likely need logging added before the same can be done with plugins, but what do you think about changing Zellij to capture output over stderr when Zellij is run with --debug? Using stderr is rather language agnostic and can be totally tossed out when not running in --debug, so plugins can keep debug messages in the code. Any messages from the plugins can then be timestamped and marked with their plugin of origin on the Zellij side.

Let me know what you think about that!

Obviously it would be ideal if plugins could also detect flags, but I think that should come with #317 :)

5c077m4n commented 3 years ago

@TheLostLambda Maybe some sort of message bus/event listener could help (and maybe even remove the need for the timeout and logging)?

TheLostLambda commented 3 years ago

@TheLostLambda Maybe some sort of message bus/event listener could help (and maybe even remove the need for the timeout and logging)?

How exactly do you mean? We currently have a sort of event bus system that allows Zellij to send events to plugins, but I suppose that doesn't go both ways yet? For plugins to talk to Zellij, there are just dedicated functions.

5c077m4n commented 3 years ago

@TheLostLambda I meant something more generic, that can send/receive serializable objects that can be used to pass any data that it may need (other than ModeInfo, Vec<TabInfo> and Key)

TheLostLambda commented 3 years ago

@5c077m4n What new abilities would that give us? Currently things are just sent as JSON over the interface. It's already decently generic I think?

horasal commented 3 years ago

what do you think about changing Zellij to capture output over stderr when Zellij is run with --debug? Any messages from the plugins can then be timestamped and marked with their plugin of origin on the Zellij side.

It sounds great! I really like to see this land to zellij so we can add debug trace for grid/cursor calculation etc.

BTW, I found that json can not serialize things like HashMap<Key, _>. When I change ModeInfo to a hashmap, zellij will lose response because of this failure. Logging will be useful to detect exceptions like this :)

a-kenji commented 3 years ago

Allow plugins to react to mouse events #607.

spacemaison commented 3 years ago

Hey, how do you all feel about metaprogramming Zellij with plugins? Right now the API for plugins seems focused on just getting visual elements painted to the screen to be interacted with. The things I'm interested in doing with plugins involve changing the way Zellij works, rather than presenting information.

For instance, I recently wrote a plugin that keeps the current working directory of the focused pane when splitting into a new one. A lot of additions to the core plugin API were needed to make it work though. Basically, the keybinds API was modified to allow for registering actions to be dispatched into plugins instead of being handled by Zellij. Other changes include adding a "PaneUpdate" event that's supposed to function similarly to the TabUpdate event, a plugins manifest in the core config file was added to help with configuring plugins, and an open_terminal method was also introduced. It's quite a lot of changes, and it's pretty hacky and unfinished, but if someone wants to take a look at my fork I can split it into multiple pull requests.

As an aside though, can I just say thanks to everyone who contributed to Zellij! I used Tmux for years, but tried Zellij a few weeks ago and now I'm never going back.

TheLostLambda commented 3 years ago

@spacemaison I think adding this sort of "metaprogramming" ability to Zellij plugins is essential! It's actually something that's been on our radar for a little while now but that we've not gotten around to. Things like plugin configuration have been particularly tricky UI problems (would plugin configuration be more at home in the layout or config files, for example?)

I'm happy to break things into smaller PRs and merge them in gradually! Is there a particular place that you'd like to start?

I've added "Implement Headless Plugins" to the task list at the top of this issue which I think maps to your concept of a "service" plugin :)

imsnif commented 3 years ago

Just a note @spacemaison - while I would totally welcome these changes, IMO the sticky-cwd needs to be the default behaviour of Zellij. There's an open PR for it here: https://github.com/zellij-org/zellij/pull/277 - I think you can pick it up if you like, or start a new one?

spacemaison commented 3 years ago

Hey thanks for being receptive to the changes @TheLostLambda. I've opened an issue to add a plugins manifest to Zellij as a place to start.

Also thanks for letting me know @imsnif, I'll take a look at that PR and work from there on the issue.

a-kenji commented 3 years ago

Dispatching keybound actions into plugins #662 .

prscoelho commented 3 years ago

Thoughts on plugins being able to send instructions to zellij? There's already subscribe, but I think going the other way around would be important too. Once mouse events are added, tab bar would have to send an instruction to zellij to change the active tab, right?

TheLostLambda commented 3 years ago

@prscoelho

I agree that's a pretty essential feature to have! Currently the way that plugins talk back to Zellij is by calling certain API functions that we expose. Adding one for switching the tab should be relatively simple I think!

spacemaison commented 3 years ago

@TheLostLambda

Has any thought been given to dispatching actions back into Zellij from plugins rather than exposing API methods piecemeal? You could just give plugins a dispatch_action/dispatch_actions method that'd work like this:

impl ZellijPlugin for Whatever {
    fn load(&mut self) {
        // Creates a new tab and renames it
        dispatch_actions(&[
            Action::NewTab(None),
            Action::TabNameInput("Generated tab".as_bytes().to_vec())
        ]);
    }
}

Personally, I don't know if that's kinda ugly or elegant. I already have an open issue for dispatching actions into plugins at #662, and I get the sense @a-kenji thinks it's kinda ugly, so maybe I'm the nutty one here. It'd simplify documentation around what you can do with plugins by quite a bit though, and expand what they can do by a lot.

... A permissions system would absolutely need to be done before this because a plugin author could arbitrarily write whatever they want to the console using Action::Write.

prscoelho commented 3 years ago

It seems like sending actions would scale better than having to add functions for each action. I'm not too familiar with how wasm and rust interoperate, but could update and load have a Sender in their parameters?

TheLostLambda commented 3 years ago

@prscoelho @spacemaison Ah! I misunderstood your original message! I think a function like dispatch_actions is an excellent idea – certainly better than a separate function for every action. I think a permissions system is on the way somewhat soon and it seems (to me) quite reasonable to pass actions through a firewall-style filter in the plugin thread before forwarding them to the rest of Zellij!

I've added this to the official list above!

a-kenji commented 3 years ago

Make PluginPanes copyable #721 .

TheLostLambda commented 2 years ago

As a tentative roadmap:

  1. Move Zellij configuration over from YAML to KDL
  2. Implement plugin manifests in KDL (starting as just a headless vs pane-bound and name thing)
  3. Expand plugin manifests to determine if a pane if globally, per-user, or pane-unique (fixes the 10 tabs, 20 plugins problem)
  4. Add a permission system for Events and Actions
  5. Allow plugins to request permissions via the manifest
  6. Allow plugins to dispatch arbitrary actions and convert most host-functions to actions

How do people feel about this overall? I should get a chance to give these a swing soon (obviously happy to accept help from others as well)!

arefem-project commented 2 years ago

Hey @TheLostLambda, that sounds like a good course of action. How far along are you? I'd be happy to help if you can find a way to divide the work. Every item on that list kinda hangs on the manifest being implemented first though.

How are you planning on implementing the manifest? I vaguely recall that we talked about rolling manifest information into the wasm binary somehow, but I can't remember the specifics of what was discussed...

spacemaison commented 2 years ago

Ah Firefox wants to randomly log me into the wrong account when I'm not paying attention. For clarities sake that was me @TheLostLambda.

TheLostLambda commented 2 years ago

@spacemaison I'm looking to take a swipe at the manifest this week! It's been a while since I've actually done some coding and I'm eager to give it a go. My plan is to kinda incorporate it with the template plugin repo we have so that the manifest can be maintained as a separate text file (YAML, I suppose) and include_bytes! would be used to bake it into the WASM blob. Calling the manifest() function in the WASM would simply return this internal string :)

a-kenji commented 2 years ago

Add action that can send data to a plugin #874

a-kenji commented 2 years ago

Give plugins version information on load #888

a-kenji commented 2 years ago

Feature: add plugin API for File IO #896

ahirner commented 2 years ago

Reading current directory and command: https://github.com/zellij-org/zellij/issues/1133

abhijeetbhagat commented 2 years ago

i want to create a headless plugin that gets details about the layout in a current session. i want to then save this info in a file and load the saved layout the next time zellij is launched if the user wants to.

mdrssv commented 2 months ago

Request:

allow plugins to aid Zellij to resurrect certain panes. By allowing an plugin to generate the command which shall be used to recreate the pane.