zellij
zellij copied to clipboard
Plugin API Requests
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:
- [x] Add
set_timeout(secs)
, allowing the plugin to reschedule its execution (#394) - [ ] Add a permissions system for plugins, allowing the WASI base directory to be specified (permissions for Events and Plugins)
- [x] System command delegation (call shell commands through Zellij, needs permissions first)
- [ ] Make plugins configurable #317
- [ ] Implement headless plugins (that run without an associated Pane)
- [x] Add a logging bus over
stderr
that is only enabled during--debug
(Zellij needs a logging crate) - [x] Add mouse events to plugins!
- [ ] Allow arbitrary actions to be dispatched from plugins
- [ ] Add a plugin manifest function, specifying plugin name, if it's headless, needed permissions, and if it's unique to the whole session, each client, or each tab
- [ ] Add action that can send data to a plugin #874
- [x] Add a way for plugins to get version information on load #888
- [ ] Add plugin API for File IO #896
Request: logging api
- Background
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.
- Plugin Side Design
Rust already have many good logging library, such as log
and tracing
. Plugin can just import them and use warn!
, debug!
etc to log.
- plugin library side design
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 Side
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:
- provide a special tab for logging. This tab can be toggled by a command or option.
- write all logs to a file
Remaining Problems
- Logging library for other languages
- Performance issues
Make Plugins Configurable #317.
@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!
@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:
- developers have to remove all dbg macros before publish
- there's no way to collect those info for bug report
- it's difficult to handle complex scene (for example, bugs that only appear with another specific plugin)
@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 :)
@TheLostLambda Maybe some sort of message bus/event listener could help (and maybe even remove the need for the timeout and logging)?
@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.
@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
)
@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?
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 :)
Allow plugins to react to mouse events #607.
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.
@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 :)
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?
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.
Dispatching keybound actions into plugins #662 .
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?
@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!
@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
.
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<Action> in their parameters?
@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!
Make PluginPanes copyable #721 .
As a tentative roadmap:
- Move Zellij configuration over from YAML to KDL
- Implement plugin manifests in KDL (starting as just a headless vs pane-bound and name thing)
- Expand plugin manifests to determine if a pane if globally, per-user, or pane-unique (fixes the 10 tabs, 20 plugins problem)
- Add a permission system for Events and Actions
- Allow plugins to request permissions via the manifest
- 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)!
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...
Ah Firefox wants to randomly log me into the wrong account when I'm not paying attention. For clarities sake that was me @TheLostLambda.
@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 :)
Add action that can send data to a plugin #874
Give plugins version information on load #888
Feature: add plugin API for File IO #896
Reading current directory and command: https://github.com/zellij-org/zellij/issues/1133