Skip to content

Commit

Permalink
feat(plugins): session manager cwd and new filepicker (#3200)
Browse files Browse the repository at this point in the history
* prototype

* folder selection ui in session manager

* overhaul strider

* scan folder host command

* get strider to work from the cli and some cli pipe fixes

* some ux improvements to strider

* improve strider's ui

* make strider ui responsive

* make session-manager new ui parts responsive

* fix tests

* style(fmt): rustfmt
  • Loading branch information
imsnif committed Mar 18, 2024
1 parent 12daac3 commit ee16a4b
Show file tree
Hide file tree
Showing 32 changed files with 1,390 additions and 284 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions default-plugins/fixture-plugin-for-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl ZellijPlugin for State {
EventType::FileSystemUpdate,
EventType::FileSystemDelete,
]);
watch_filesystem();
}

fn update(&mut self, event: Event) -> bool {
Expand Down
1 change: 1 addition & 0 deletions default-plugins/session-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ chrono = "0.4.0"
fuzzy-matcher = "0.3.7"
unicode-width = "0.1.10"
humantime = "2.1.0"
uuid = { version = "1.7.0", features = ["v4"] }
56 changes: 50 additions & 6 deletions default-plugins/session-manager/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ mod new_session_info;
mod resurrectable_sessions;
mod session_list;
mod ui;
use zellij_tile::prelude::*;

use std::collections::BTreeMap;
use uuid::Uuid;
use zellij_tile::prelude::*;

use new_session_info::NewSessionInfo;
use ui::{
Expand Down Expand Up @@ -45,6 +45,7 @@ struct State {
colors: Colors,
is_welcome_screen: bool,
show_kill_all_sessions_warning: bool,
request_ids: Vec<String>,
}

register_plugin!(State);
Expand All @@ -66,6 +67,28 @@ impl ZellijPlugin for State {
]);
}

fn pipe(&mut self, pipe_message: PipeMessage) -> bool {
if pipe_message.name == "filepicker_result" {
match (pipe_message.payload, pipe_message.args.get("request_id")) {
(Some(payload), Some(request_id)) => {
match self.request_ids.iter().position(|p| p == request_id) {
Some(request_id_position) => {
self.request_ids.remove(request_id_position);
let new_session_folder = std::path::PathBuf::from(payload);
self.new_session_info.new_session_folder = Some(new_session_folder);
},
None => {
eprintln!("request id not found");
},
}
},
_ => {},
}
true
} else {
false
}
}
fn update(&mut self, event: Event) -> bool {
let mut should_render = false;
match event {
Expand Down Expand Up @@ -109,7 +132,7 @@ impl ZellijPlugin for State {
render_new_session_block(
&self.new_session_info,
self.colors,
height,
height.saturating_sub(2),
width,
x,
y + 2,
Expand Down Expand Up @@ -184,12 +207,33 @@ impl State {
} else if let Key::Ctrl('w') = key {
self.active_screen = ActiveScreen::NewSession;
should_render = true;
} else if let Key::Ctrl('c') = key {
self.new_session_info.handle_key(key);
should_render = true;
} else if let Key::BackTab = key {
self.toggle_active_screen();
should_render = true;
} else if let Key::Ctrl('f') = key {
let request_id = Uuid::new_v4();
let mut config = BTreeMap::new();
let mut args = BTreeMap::new();
self.request_ids.push(request_id.to_string());
// we insert this into the config so that a new plugin will be opened (the plugin's
// uniqueness is determined by its name/url as well as its config)
config.insert("request_id".to_owned(), request_id.to_string());
// we also insert this into the args so that the plugin will have an easier access to
// it
args.insert("request_id".to_owned(), request_id.to_string());
pipe_message_to_plugin(
MessageToPlugin::new("filepicker")
.with_plugin_url("filepicker")
.with_plugin_config(config)
.new_plugin_instance_should_have_pane_title(
"Select folder for the new session...",
)
.with_args(args),
);
should_render = true;
} else if let Key::Ctrl('c') = key {
self.new_session_info.new_session_folder = None;
should_render = true;
} else if let Key::Esc = key {
self.new_session_info.handle_key(key);
should_render = true;
Expand Down
5 changes: 4 additions & 1 deletion default-plugins/session-manager/src/new_session_info.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use std::cmp::Ordering;
use std::path::PathBuf;
use zellij_tile::prelude::*;

#[derive(Default)]
pub struct NewSessionInfo {
name: String,
layout_list: LayoutList,
entering_new_session_info: EnteringState,
pub new_session_folder: Option<PathBuf>,
}

#[derive(Eq, PartialEq)]
Expand Down Expand Up @@ -104,7 +106,8 @@ impl NewSessionInfo {
if new_session_name != current_session_name.as_ref().map(|s| s.as_str()) {
match new_session_layout {
Some(new_session_layout) => {
switch_session_with_layout(new_session_name, new_session_layout, None)
let cwd = self.new_session_folder.as_ref().map(|c| PathBuf::from(c));
switch_session_with_layout(new_session_name, new_session_layout, cwd)
},
None => {
switch_session(new_session_name);
Expand Down
178 changes: 169 additions & 9 deletions default-plugins/session-manager/src/ui/components.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::path::PathBuf;
use unicode_width::UnicodeWidthChar;
use unicode_width::UnicodeWidthStr;
use zellij_tile::prelude::*;
Expand Down Expand Up @@ -519,9 +520,6 @@ pub fn render_screen_toggle(active_screen: ActiveScreen, x: usize, y: usize, max
let key_indication_len = key_indication_text.chars().count() + 1;
let first_ribbon_length = new_session_text.chars().count() + 4;
let second_ribbon_length = running_sessions_text.chars().count() + 4;
let third_ribbon_length = exited_sessions_text.chars().count() + 4;
let total_len =
key_indication_len + first_ribbon_length + second_ribbon_length + third_ribbon_length;
let key_indication_x = x;
let first_ribbon_x = key_indication_x + key_indication_len;
let second_ribbon_x = first_ribbon_x + first_ribbon_length;
Expand Down Expand Up @@ -552,6 +550,140 @@ pub fn render_screen_toggle(active_screen: ActiveScreen, x: usize, y: usize, max
print_ribbon_with_coordinates(exited_sessions_text, third_ribbon_x, y, None, None);
}

fn render_new_session_folder_prompt(
new_session_info: &NewSessionInfo,
colors: Colors,
x: usize,
y: usize,
max_cols: usize,
) {
match new_session_info.new_session_folder.as_ref() {
Some(new_session_folder) => {
let folder_prompt = "New session folder:";
let short_folder_prompt = "Folder:";
let new_session_path = new_session_folder.clone();
let new_session_folder = new_session_folder.display().to_string();
let change_folder_shortcut_text = "<Ctrl f>";
let change_folder_shortcut = colors.magenta(&change_folder_shortcut_text);
let to_change = "to change";
let reset_folder_shortcut_text = "<Ctrl c>";
let reset_folder_shortcut = colors.magenta(reset_folder_shortcut_text);
let to_reset = "to reset";
if max_cols
>= folder_prompt.width()
+ new_session_folder.width()
+ change_folder_shortcut_text.width()
+ to_change.width()
+ reset_folder_shortcut_text.width()
+ to_reset.width()
+ 8
{
print!(
"\u{1b}[m{}{} {} ({} {}, {} {})",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(folder_prompt),
colors.orange(&new_session_folder),
change_folder_shortcut,
to_change,
reset_folder_shortcut,
to_reset,
);
} else if max_cols
>= short_folder_prompt.width()
+ new_session_folder.width()
+ change_folder_shortcut_text.width()
+ to_change.width()
+ reset_folder_shortcut_text.width()
+ to_reset.width()
+ 8
{
print!(
"\u{1b}[m{}{} {} ({} {}, {} {})",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(short_folder_prompt),
colors.orange(&new_session_folder),
change_folder_shortcut,
to_change,
reset_folder_shortcut,
to_reset,
);
} else if max_cols
>= short_folder_prompt.width()
+ new_session_folder.width()
+ change_folder_shortcut_text.width()
+ reset_folder_shortcut_text.width()
+ 5
{
print!(
"\u{1b}[m{}{} {} ({}/{})",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(short_folder_prompt),
colors.orange(&new_session_folder),
change_folder_shortcut,
reset_folder_shortcut,
);
} else {
let total_len = short_folder_prompt.width()
+ change_folder_shortcut_text.width()
+ reset_folder_shortcut_text.width()
+ 5;
let max_path_len = max_cols.saturating_sub(total_len);
let truncated_path = truncate_path(
new_session_path,
new_session_folder.width().saturating_sub(max_path_len),
);
print!(
"\u{1b}[m{}{} {} ({}/{})",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(short_folder_prompt),
colors.orange(&truncated_path),
change_folder_shortcut,
reset_folder_shortcut,
);
}
},
None => {
let folder_prompt = "New session folder:";
let short_folder_prompt = "Folder:";
let change_folder_shortcut_text = "<Ctrl f>";
let change_folder_shortcut = colors.magenta(change_folder_shortcut_text);
let to_set = "to set";

if max_cols
>= folder_prompt.width() + change_folder_shortcut_text.width() + to_set.width() + 4
{
print!(
"\u{1b}[m{}{} ({} {})",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(folder_prompt),
change_folder_shortcut,
to_set,
);
} else if max_cols
>= short_folder_prompt.width()
+ change_folder_shortcut_text.width()
+ to_set.width()
+ 4
{
print!(
"\u{1b}[m{}{} ({} {})",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(short_folder_prompt),
change_folder_shortcut,
to_set,
);
} else {
print!(
"\u{1b}[m{}{} {}",
format!("\u{1b}[{};{}H", y + 1, x + 1),
colors.green(short_folder_prompt),
change_folder_shortcut,
);
}
},
}
}

pub fn render_new_session_block(
new_session_info: &NewSessionInfo,
colors: Colors,
Expand Down Expand Up @@ -615,6 +747,7 @@ pub fn render_new_session_block(
}
render_layout_selection_list(
new_session_info,
colors,
max_rows_of_new_session_block.saturating_sub(1),
max_cols_of_new_session_block,
x,
Expand All @@ -625,6 +758,7 @@ pub fn render_new_session_block(

pub fn render_layout_selection_list(
new_session_info: &NewSessionInfo,
colors: Colors,
max_rows_of_new_session_block: usize,
max_cols_of_new_session_block: usize,
x: usize,
Expand Down Expand Up @@ -658,7 +792,7 @@ pub fn render_layout_selection_list(
let layout_name = layout_info.name();
let layout_name_len = layout_name.width();
let is_builtin = layout_info.is_builtin();
if i > max_rows_of_new_session_block {
if i > max_rows_of_new_session_block.saturating_sub(1) {
break;
} else {
let mut layout_cell = if is_builtin {
Expand All @@ -682,7 +816,15 @@ pub fn render_layout_selection_list(
table = table.add_styled_row(vec![arrow_cell, layout_cell]);
}
}
print_table_with_coordinates(table, x, y + 3, None, None);
let table_y = y + 3;
print_table_with_coordinates(table, x, table_y, None, None);
render_new_session_folder_prompt(
new_session_info,
colors,
x,
(y + max_rows_of_new_session_block).saturating_sub(3),
max_cols_of_new_session_block,
);
}

pub fn render_error(error_text: &str, rows: usize, columns: usize, x: usize, y: usize) {
Expand Down Expand Up @@ -733,10 +875,6 @@ pub fn render_controls_line(
}
},
ActiveScreen::AttachToSession => {
let arrows = colors.magenta("<←↓↑→>");
let navigate = colors.bold("Navigate");
let enter = colors.magenta("<ENTER>");
let select = colors.bold("Attach");
let rename = colors.magenta("<Ctrl r>");
let rename_text = colors.bold("Rename");
let disconnect = colors.magenta("<Ctrl x>");
Expand Down Expand Up @@ -817,3 +955,25 @@ impl Colors {
self.color(&self.palette.magenta, text)
}
}

fn truncate_path(path: PathBuf, mut char_count_to_remove: usize) -> String {
let mut truncated = String::new();
let component_count = path.iter().count();
for (i, component) in path.iter().enumerate() {
let mut component_str = component.to_string_lossy().to_string();
if char_count_to_remove > 0 {
truncated.push(component_str.remove(0));
if i != 0 && i + 1 != component_count {
truncated.push('/');
}
char_count_to_remove =
char_count_to_remove.saturating_sub(component_str.width().saturating_sub(1));
} else {
truncated.push_str(&component_str);
if i != 0 && i + 1 != component_count {
truncated.push('/');
}
}
}
truncated
}
2 changes: 1 addition & 1 deletion default-plugins/strider/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[build]
target = "wasm32-wasi"
target = "wasm32-wasi"
Loading

0 comments on commit ee16a4b

Please sign in to comment.