Skip to content

Commit

Permalink
Merge pull request #7 from hiasr/configurability
Browse files Browse the repository at this point in the history
Add configurable mods and additional commands
  • Loading branch information
hiasr authored May 4, 2024
2 parents 6f46dc6 + 500e2da commit a153d2e
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 41 deletions.
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,59 @@ For version <0.2.0 you also need to install this plugin:
```

## Usage
Available commands:
- `move_focus` with payload `up`, `down`, `left`, `right` to move the focus in the corresponding direction.
- `move_focus_or_tab` with payload `up`, `down`, `left`, `right` to move the focus in the corresponding direction or switch to the next tab if the focus is already at the edge.
- `resize` with payload `up`, `down`, `left`, `right` to resize the pane in the corresponding direction.

If you use configuration for the plugin it must be added to every command in order to function consistently.
This is because the plugin is loaded with the configuration of the first command executed.

Available configuration options:
- `move_mod`: The modifier key passed to Neovim with `move_focus` or `move_focus_or_tab`. Default: `ctrl`. Options: `ctrl`, `alt`.
- `resize_mod`: The modifier key passed to Neovim with the `resize` command. Default: `alt`. Options: `ctrl`, `alt`.

```javascript
keybinds {
shared_except "locked" {
bind "Ctrl h" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.0/vim-zellij-navigator.wasm" {
name "move_focus";
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus_or_tab";
payload "left";

// Plugin Configuration
move_mod "ctrl"; // Optional, should be added on every command if you want to use it
resize_mod "alt"; // Optional, should be added on every command if you want to use it
};
}

bind "Ctrl j" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.0/vim-zellij-navigator.wasm" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus";
payload "down";

move_mod "ctrl";
resize_mod "alt";
};
}

bind "Ctrl k" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.0/vim-zellij-navigator.wasm" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus";
payload "up";

move_mod "ctrl";
resize_mod "alt";
};
}

bind "Ctrl l" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.0/vim-zellij-navigator.wasm" {
name "move_focus";
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus_or_tab";
payload "right";

move_mod "ctrl";
resize_mod "alt";
};
}
}
Expand Down
44 changes: 44 additions & 0 deletions example_config.kdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
keybinds {
shared_except "locked" {
bind "Ctrl h" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus_or_tab";
payload "left";

// Plugin Configuration
move_mod "ctrl"; // Optional, should be added on every command if you want to use it
resize_mod "alt"; // Optional, should be added on every command if you want to use it
};
}

bind "Ctrl j" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus";
payload "down";

move_mod "ctrl";
resize_mod "alt";
};
}

bind "Ctrl k" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus";
payload "up";

move_mod "ctrl";
resize_mod "alt";
};
}

bind "Ctrl l" {
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
name "move_focus_or_tab";
payload "right";

move_mod "ctrl";
resize_mod "alt";
};
}
}
}
151 changes: 116 additions & 35 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,34 @@ use zellij_tile::prelude::*;

use std::collections::{BTreeMap, VecDeque};

#[derive(Default)]
struct State {
userspace_configuration: BTreeMap<String, String>,
permissions_granted: bool,
current_command: Option<String>,
direction_queue: VecDeque<Direction>,
current_term_command: Option<String>,
command_queue: VecDeque<Command>,

// Configuration
move_mod: Mod,
resize_mod: Mod,
}

enum Command {
MoveFocus(Direction),
MoveFocusOrTab(Direction),
Resize(Direction),
}

#[derive(Debug)]
enum Mod {
Ctrl,
Alt,
}

register_plugin!(State);

impl ZellijPlugin for State {
fn load(&mut self, configuration: BTreeMap<String, String>) {
self.userspace_configuration = configuration;
self.parse_configuration(configuration);

request_permission(&[
PermissionType::RunCommands,
PermissionType::WriteToStdin,
Expand All @@ -34,13 +49,13 @@ impl ZellijPlugin for State {
Event::RunCommandResult(_, stdout, _, _) => {
let stdout = String::from_utf8(stdout).unwrap();

self.current_command = command_from_client_list(stdout);
self.current_term_command = term_command_from_client_list(stdout);

if !self.direction_queue.is_empty() {
let direction = self.direction_queue.pop_front().unwrap();
self.move_focus(direction);
if !self.command_queue.is_empty() {
let command = self.command_queue.pop_front().unwrap();
self.execute_command(command);
}
},
}

Event::PermissionRequestResult(permission) => {
self.permissions_granted = match permission {
Expand All @@ -56,56 +71,88 @@ impl ZellijPlugin for State {
true
}

fn render(&mut self, _rows: usize, _cols: usize) {
}
fn render(&mut self, _rows: usize, _cols: usize) {}

fn pipe(&mut self, pipe_message: PipeMessage) -> bool {
match pipe_message.name.as_str() {
"move_focus" => self.handle_move_focus(pipe_message.payload),
_ => {}
if let Some(command) = parse_command(pipe_message) {
self.handle_command(command);
}
true
}
}

impl State {
fn handle_move_focus(&mut self, payload: Option<String>) {
if payload.is_none() {
return;
}
impl Default for State {
fn default() -> Self {
Self {
permissions_granted: false,
current_term_command: None,
command_queue: VecDeque::new(),

let direction = string_to_direction(payload.unwrap().as_str());
if direction.is_none() {
return;
move_mod: Mod::Ctrl,
resize_mod: Mod::Alt,
}
}
}

self.direction_queue.push_back(direction.unwrap());
impl State {
fn handle_command(&mut self, command: Command) {
self.command_queue.push_back(command);
run_command(&["zellij", "action", "list-clients"], BTreeMap::new());
}

fn move_focus(&mut self, direction: Direction) {
fn execute_command(&mut self, command: Command) {
if self.current_pane_is_vim() {
write_chars(direction_to_keybinding(direction));
} else {
match direction {
Direction::Left | Direction::Right => move_focus_or_tab(direction),
_ => move_focus(direction),
write_chars(&self.command_to_keybind(&command));
return;
}

match command {
Command::MoveFocus(direction) => move_focus(direction),
Command::MoveFocusOrTab(direction) => move_focus_or_tab(direction),
Command::Resize(direction) => {
resize_focused_pane_with_direction(Resize::Increase, direction)
}
}
}

fn current_pane_is_vim(&self) -> bool {
if let Some(current_command) = &self.current_command {
if let Some(current_command) = &self.current_term_command {
if current_command == "nvim" || current_command == "vim" {
return true;
}
}
false
}

fn parse_configuration(&mut self, configuration: BTreeMap<String, String>) {
self.move_mod = configuration.get("move_mod").map_or(Mod::Ctrl, |f| {
string_to_mod(f).expect("Illegal modifier for move_mod")
});
self.resize_mod = configuration.get("resize_mod").map_or(Mod::Alt, |f| {
string_to_mod(f).expect("Illegal modifier for resize_mod")
});
}

fn command_to_keybind(&mut self, command: &Command) -> String {
let mod_key = match command {
Command::MoveFocus(_) | Command::MoveFocusOrTab(_) => &self.move_mod,
Command::Resize(_) => &self.resize_mod,
};

let direction = match command {
Command::MoveFocus(direction)
| Command::MoveFocusOrTab(direction)
| Command::Resize(direction) => direction,
};

match mod_key {
Mod::Ctrl => ctrl_keybinding(direction),
Mod::Alt => alt_keybinding(direction),
}
}
}

fn command_from_client_list(cl: String) -> Option<String> {
fn term_command_from_client_list(cl: String) -> Option<String> {
let clients = cl.split('\n').skip(1).collect::<Vec<&str>>();
if clients.is_empty() {
return None;
Expand All @@ -126,13 +173,25 @@ fn command_from_client_list(cl: String) -> Option<String> {
Some(command.to_string())
}

fn direction_to_keybinding(direction: Direction) -> &'static str {
match direction {
fn ctrl_keybinding(direction: &Direction) -> String {
let direction = match direction {
Direction::Left => "\u{0008}",
Direction::Right => "\u{000C}",
Direction::Up => "\u{000B}",
Direction::Down => "\u{000A}",
}
};
direction.to_string()
}

fn alt_keybinding(direction: &Direction) -> String {
let mut char_vec: Vec<char> = vec![0x1b as char];
char_vec.push(match direction {
Direction::Left => 'h',
Direction::Right => 'l',
Direction::Up => 'k',
Direction::Down => 'j',
});
char_vec.iter().collect()
}

fn string_to_direction(s: &str) -> Option<Direction> {
Expand All @@ -144,3 +203,25 @@ fn string_to_direction(s: &str) -> Option<Direction> {
_ => None,
}
}

fn string_to_mod(s: &str) -> Option<Mod> {
match s.to_lowercase().as_str() {
"ctrl" => Some(Mod::Ctrl),
"alt" => Some(Mod::Alt),
_ => None,
}
}

fn parse_command(pipe_message: PipeMessage) -> Option<Command> {
let payload = pipe_message.payload?;
let command = pipe_message.name;

let direction = string_to_direction(payload.as_str())?;

match command.as_str() {
"move_focus" => Some(Command::MoveFocus(direction)),
"move_focus_or_tab" => Some(Command::MoveFocusOrTab(direction)),
"resize" => Some(Command::Resize(direction)),
_ => None,
}
}

0 comments on commit a153d2e

Please sign in to comment.