diff options
author | Aram Drevekenin <aram@poor.dev> | 2024-03-18 09:19:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-18 09:19:58 +0100 |
commit | ee16a4b8c347a81878389fd4f90144c2c6357f41 (patch) | |
tree | bf8675d57499b6278e8c89b84c8141969cc2ce3f /zellij-utils/src | |
parent | 12daac3b5445e4281cf5c1810be0ebdb257085c1 (diff) |
feat(plugins): session manager cwd and new filepicker (#3200)
* 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
Diffstat (limited to 'zellij-utils/src')
-rw-r--r-- | zellij-utils/src/data.rs | 33 | ||||
-rw-r--r-- | zellij-utils/src/errors.rs | 1 | ||||
-rw-r--r-- | zellij-utils/src/input/actions.rs | 18 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/event.proto | 9 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/event.rs | 126 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/plugin_command.proto | 3 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/plugin_command.rs | 22 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/plugin_ids.proto | 1 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/plugin_ids.rs | 3 |
9 files changed, 178 insertions, 38 deletions
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 63ccccced..9bb0d1f96 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -5,6 +5,7 @@ use clap::ArgEnum; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fmt; +use std::fs::Metadata; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::Duration; @@ -458,6 +459,25 @@ pub enum Mouse { Release(isize, usize), // line and column } +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct FileMetadata { + pub is_dir: bool, + pub is_file: bool, + pub is_symlink: bool, + pub len: u64, +} + +impl From<Metadata> for FileMetadata { + fn from(metadata: Metadata) -> Self { + FileMetadata { + is_dir: metadata.is_dir(), + is_file: metadata.is_file(), + is_symlink: metadata.is_symlink(), + len: metadata.len(), + } + } +} + /// These events can be subscribed to with subscribe method exported by `zellij-tile`. /// Once subscribed to, they will trigger the `update` method of the `ZellijPlugin` trait. #[derive(Debug, Clone, PartialEq, EnumDiscriminants, ToString, Serialize, Deserialize)] @@ -488,13 +508,13 @@ pub enum Event { String, // payload ), /// A file was created somewhere in the Zellij CWD folder - FileSystemCreate(Vec<PathBuf>), + FileSystemCreate(Vec<(PathBuf, Option<FileMetadata>)>), /// A file was accessed somewhere in the Zellij CWD folder - FileSystemRead(Vec<PathBuf>), + FileSystemRead(Vec<(PathBuf, Option<FileMetadata>)>), /// A file was modified somewhere in the Zellij CWD folder - FileSystemUpdate(Vec<PathBuf>), + FileSystemUpdate(Vec<(PathBuf, Option<FileMetadata>)>), /// A file was deleted somewhere in the Zellij CWD folder - FileSystemDelete(Vec<PathBuf>), + FileSystemDelete(Vec<(PathBuf, Option<FileMetadata>)>), /// A Result of plugin permission request PermissionRequestResult(PermissionStatus), SessionUpdate( @@ -904,10 +924,11 @@ pub struct PaneInfo { pub is_selectable: bool, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct PluginIds { pub plugin_id: u32, pub zellij_pid: u32, + pub initial_cwd: PathBuf, } /// Tag used to identify the plugin in layout and config kdl files @@ -1350,4 +1371,6 @@ pub enum PluginCommand { MessageToPlugin(MessageToPlugin), DisconnectOtherClients, KillSessions(Vec<String>), // one or more session names + ScanHostFolder(PathBuf), // TODO: rename to ScanHostFolder + WatchFilesystem, } diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 0677f611f..9a100e385 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -400,6 +400,7 @@ pub enum PluginContext { CachePluginEvents, MessageFromPlugin, UnblockCliPipes, + WatchFilesystem, } /// Stack call representations corresponding to the different types of [`ClientInstruction`]s. diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index c71a8ca68..fdf040300 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -353,7 +353,7 @@ impl Action { let alias_cwd = cwd.clone().map(|cwd| current_dir.join(cwd)); let cwd = cwd .map(|cwd| current_dir.join(cwd)) - .or_else(|| Some(current_dir)); + .or_else(|| Some(current_dir.clone())); if let Some(plugin) = plugin { let plugin = match RunPluginLocation::parse(&plugin, cwd.clone()) { Ok(location) => { @@ -365,11 +365,17 @@ impl Action { initial_cwd: cwd.clone(), }) }, - Err(_) => RunPluginOrAlias::Alias(PluginAlias::new( - &plugin, - &configuration.map(|c| c.inner().clone()), - alias_cwd, - )), + Err(_) => { + let mut user_configuration = + configuration.map(|c| c.inner().clone()).unwrap_or_default(); + user_configuration + .insert("caller_cwd".to_owned(), current_dir.display().to_string()); + RunPluginOrAlias::Alias(PluginAlias::new( + &plugin, + &Some(user_configuration), + alias_cwd, + )) + }, }; if floating { Ok(vec![Action::NewFloatingPluginPane( diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index e26a9c23a..abc239c0d 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -103,6 +103,15 @@ message PermissionRequestResultPayload { message FileListPayload { repeated string paths = 1; + repeated FileMetadata paths_metadata = 2; +} + +message FileMetadata { + bool metadata_is_set = 1; // if this is false, the metadata for this file has not been read + bool is_dir = 2; + bool is_file = 3; + bool is_symlink = 4; + uint64 len = 5; } message CustomMessagePayload { diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 21ff1eff1..1b2cbce29 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -3,10 +3,11 @@ pub use super::generated_api::api::{ event::{ event::Payload as ProtobufEventPayload, CopyDestination as ProtobufCopyDestination, Event as ProtobufEvent, EventNameList as ProtobufEventNameList, - EventType as ProtobufEventType, InputModeKeybinds as ProtobufInputModeKeybinds, - KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo, - ModeUpdatePayload as ProtobufModeUpdatePayload, PaneInfo as ProtobufPaneInfo, - PaneManifest as ProtobufPaneManifest, ResurrectableSession as ProtobufResurrectableSession, + EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata, + InputModeKeybinds as ProtobufInputModeKeybinds, KeyBind as ProtobufKeyBind, + LayoutInfo as ProtobufLayoutInfo, ModeUpdatePayload as ProtobufModeUpdatePayload, + PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest, + ResurrectableSession as ProtobufResurrectableSession, SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *, }, input_mode::InputMode as ProtobufInputMode, @@ -14,8 +15,8 @@ pub use super::generated_api::api::{ style::Style as ProtobufStyle, }; use crate::data::{ - CopyDestination, Event, EventType, InputMode, Key, LayoutInfo, ModeInfo, Mouse, PaneInfo, - PaneManifest, PermissionStatus, PluginCapabilities, SessionInfo, Style, TabInfo, + CopyDestination, Event, EventType, FileMetadata, InputMode, Key, LayoutInfo, ModeInfo, Mouse, + PaneInfo, PaneManifest, PermissionStatus, PluginCapabilities, SessionInfo, Style, TabInfo, }; use crate::errors::prelude::*; @@ -124,7 +125,8 @@ impl TryFrom<ProtobufEvent> for Event { let file_paths = file_list_payload .paths .iter() - .map(|p| PathBuf::from(p)) + .zip(file_list_payload.paths_metadata.iter()) + .map(|(p, m)| (PathBuf::from(p), m.into())) .collect(); Ok(Event::FileSystemCreate(file_paths)) }, @@ -135,7 +137,8 @@ impl TryFrom<ProtobufEvent> for Event { let file_paths = file_list_payload .paths .iter() - .map(|p| PathBuf::from(p)) + .zip(file_list_payload.paths_metadata.iter()) + .map(|(p, m)| (PathBuf::from(p), m.into())) .collect(); Ok(Event::FileSystemRead(file_paths)) }, @@ -146,7 +149,8 @@ impl TryFrom<ProtobufEvent> for Event { let file_paths = file_list_payload .paths .iter() - .map(|p| PathBuf::from(p)) + .zip(file_list_payload.paths_metadata.iter()) + .map(|(p, m)| (PathBuf::from(p), m.into())) .collect(); Ok(Event::FileSystemUpdate(file_paths)) }, @@ -157,7 +161,8 @@ impl TryFrom<ProtobufEvent> for Event { let file_paths = file_list_payload .paths .iter() - .map(|p| PathBuf::from(p)) + .zip(file_list_payload.paths_metadata.iter()) + .map(|(p, m)| (PathBuf::from(p), m.into())) .collect(); Ok(Event::FileSystemDelete(file_paths)) }, @@ -322,36 +327,64 @@ impl TryFrom<Event> for ProtobufEvent { payload, })), }), - Event::FileSystemCreate(paths) => { + Event::FileSystemCreate(event_paths) => { + let mut paths = vec![]; + let mut paths_metadata = vec![]; + for (path, path_metadata) in event_paths { + paths.push(path.display().to_string()); + paths_metadata.push(path_metadata.into()); + } let file_list_payload = FileListPayload { - paths: paths.iter().map(|p| p.display().to_string()).collect(), + paths, + paths_metadata, }; Ok(ProtobufEvent { name: ProtobufEventType::FileSystemCreate as i32, payload: Some(event::Payload::FileListPayload(file_list_payload)), }) }, - Event::FileSystemRead(paths) => { + Event::FileSystemRead(event_paths) => { + let mut paths = vec![]; + let mut paths_metadata = vec![]; + for (path, path_metadata) in event_paths { + paths.push(path.display().to_string()); + paths_metadata.push(path_metadata.into()); + } let file_list_payload = FileListPayload { - paths: paths.iter().map(|p| p.display().to_string()).collect(), + paths, + paths_metadata, }; Ok(ProtobufEvent { name: ProtobufEventType::FileSystemRead as i32, payload: Some(event::Payload::FileListPayload(file_list_payload)), }) }, - Event::FileSystemUpdate(paths) => { + Event::FileSystemUpdate(event_paths) => { + let mut paths = vec![]; + let mut paths_metadata = vec![]; + for (path, path_metadata) in event_paths { + paths.push(path.display().to_string()); + paths_metadata.push(path_metadata.into()); + } let file_list_payload = FileListPayload { - paths: paths.iter().map(|p| p.display().to_string()).collect(), + paths, + paths_metadata, }; Ok(ProtobufEvent { name: ProtobufEventType::FileSystemUpdate as i32, payload: Some(event::Payload::FileListPayload(file_list_payload)), }) }, - Event::FileSystemDelete(paths) => { + Event::FileSystemDelete(event_paths) => { + let mut paths = vec![]; + let mut paths_metadata = vec![]; + for (path, path_metadata) in event_paths { + paths.push(path.display().to_string()); + paths_metadata.push(path_metadata.into()); + } let file_list_payload = FileListPayload { - paths: paths.iter().map(|p| p.display().to_string()).collect(), + paths, + paths_metadata, }; Ok(ProtobufEvent { name: ProtobufEventType::FileSystemDelete as i32, @@ -958,6 +991,39 @@ impl From<(String, Duration)> for ProtobufResurrectableSession { } } +impl From<&ProtobufFileMetadata> for Option<FileMetadata> { + fn from(protobuf_file_metadata: &ProtobufFileMetadata) -> Option<FileMetadata> { + if protobuf_file_metadata.metadata_is_set { + Some(FileMetadata { + is_file: protobuf_file_metadata.is_file, + is_dir: protobuf_file_metadata.is_dir, + is_symlink: protobuf_file_metadata.is_symlink, + len: protobuf_file_metadata.len, + }) + } else { + None + } + } +} + +impl From<Option<FileMetadata>> for ProtobufFileMetadata { + fn from(file_metadata: Option<FileMetadata>) -> ProtobufFileMetadata { + match file_metadata { + Some(file_metadata) => ProtobufFileMetadata { + metadata_is_set: true, + is_file: file_metadata.is_file, + is_dir: file_metadata.is_dir, + is_symlink: file_metadata.is_symlink, + len: file_metadata.len, + }, + None => ProtobufFileMetadata { + metadata_is_set: false, + ..Default::default() + }, + } + } +} + #[test] fn serialize_mode_update_event() { use prost::Message; @@ -1256,8 +1322,10 @@ fn serialize_custom_message_event() { #[test] fn serialize_file_system_create_event() { use prost::Message; - let file_system_event = - Event::FileSystemCreate(vec!["/absolute/path".into(), "./relative_path".into()]); + let file_system_event = Event::FileSystemCreate(vec![ + ("/absolute/path".into(), None), + ("./relative_path".into(), Default::default()), + ]); let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap(); let serialized_protobuf_event = protobuf_event.encode_to_vec(); let deserialized_protobuf_event: ProtobufEvent = @@ -1272,8 +1340,10 @@ fn serialize_file_system_create_event() { #[test] fn serialize_file_system_read_event() { use prost::Message; - let file_system_event = - Event::FileSystemRead(vec!["/absolute/path".into(), "./relative_path".into()]); + let file_system_event = Event::FileSystemRead(vec![ + ("/absolute/path".into(), None), + ("./relative_path".into(), Default::default()), + ]); let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap(); let serialized_protobuf_event = protobuf_event.encode_to_vec(); let deserialized_protobuf_event: ProtobufEvent = @@ -1288,8 +1358,10 @@ fn serialize_file_system_read_event() { #[test] fn serialize_file_system_update_event() { use prost::Message; - let file_system_event = - Event::FileSystemUpdate(vec!["/absolute/path".into(), "./relative_path".into()]); + let file_system_event = Event::FileSystemUpdate(vec![ + ("/absolute/path".into(), None), + ("./relative_path".into(), Some(Default::default())), + ]); let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap(); let serialized_protobuf_event = protobuf_event.encode_to_vec(); let deserialized_protobuf_event: ProtobufEvent = @@ -1304,8 +1376,10 @@ fn serialize_file_system_update_event() { #[test] fn serialize_file_system_delete_event() { use prost::Message; - let file_system_event = - Event::FileSystemDelete(vec!["/absolute/path".into(), "./relative_path".into()]); + let file_system_event = Event::FileSystemDelete(vec![ + ("/absolute/path".into(), None), + ("./relative_path".into(), Default::default()), + ]); let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap(); let serialized_protobuf_event = protobuf_event.encode_to_vec(); let deserialized_protobuf_event: ProtobufEvent = diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index eecae43b8..c95111e59 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -93,6 +93,8 @@ enum CommandName { MessageToPlugin = 79; DisconnectOtherClients = 80; KillSessions = 81; + ScanHostFolder = 82; + WatchFilesystem = 83; } message PluginCommand { @@ -148,6 +150,7 @@ message PluginCommand { CliPipeOutputPayload cli_pipe_output_payload = 49; MessageToPluginPayload message_to_plugin_payload = 50; KillSessionsPayload kill_sessions_payload = 60; + string scan_host_folder_payload = 61; } } diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index ab9d52940..e452e53cc 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -855,7 +855,17 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand { Some(Payload::KillSessionsPayload(KillSessionsPayload { session_names })) => { Ok(PluginCommand::KillSessions(session_names)) }, - _ => Err("Mismatched payload for PipeOutput"), + _ => Err("Mismatched payload for KillSessions"), + }, + Some(CommandName::ScanHostFolder) => match protobuf_plugin_command.payload { + Some(Payload::ScanHostFolderPayload(folder_to_scan)) => { + Ok(PluginCommand::ScanHostFolder(PathBuf::from(folder_to_scan))) + }, + _ => Err("Mismatched payload for ScanHostFolder"), + }, + Some(CommandName::WatchFilesystem) => match protobuf_plugin_command.payload { + Some(_) => Err("WatchFilesystem should have no payload, found a payload"), + None => Ok(PluginCommand::WatchFilesystem), }, None => Err("Unrecognized plugin command"), } @@ -1361,6 +1371,16 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand { session_names, })), }), + PluginCommand::ScanHostFolder(folder_to_scan) => Ok(ProtobufPluginCommand { + name: CommandName::ScanHostFolder as i32, + payload: Some(Payload::ScanHostFolderPayload( + folder_to_scan.display().to_string(), + )), + }), + PluginCommand::WatchFilesystem => Ok(ProtobufPluginCommand { + name: CommandName::WatchFilesystem as i32, + payload: None, + }), } } } diff --git a/zellij-utils/src/plugin_api/plugin_ids.proto b/zellij-utils/src/plugin_api/plugin_ids.proto index 2977dbe48..09d30bd07 100644 --- a/zellij-utils/src/plugin_api/plugin_ids.proto +++ b/zellij-utils/src/plugin_api/plugin_ids.proto @@ -5,6 +5,7 @@ package api.plugin_ids; message PluginIds { int32 plugin_id = 1; int32 zellij_pid = 2; + string initial_cwd = 3; } message ZellijVersion { diff --git a/zellij-utils/src/plugin_api/plugin_ids.rs b/zellij-utils/src/plugin_api/plugin_ids.rs index 51f526c66..749ccf22f 100644 --- a/zellij-utils/src/plugin_api/plugin_ids.rs +++ b/zellij-utils/src/plugin_api/plugin_ids.rs @@ -4,6 +4,7 @@ pub use super::generated_api::api::plugin_ids::{ use crate::data::PluginIds; use std::convert::TryFrom; +use std::path::PathBuf; impl TryFrom<ProtobufPluginIds> for PluginIds { type Error = &'static str; @@ -11,6 +12,7 @@ impl TryFrom<ProtobufPluginIds> for PluginIds { Ok(PluginIds { plugin_id: protobuf_plugin_ids.plugin_id as u32, zellij_pid: protobuf_plugin_ids.zellij_pid as u32, + initial_cwd: PathBuf::from(protobuf_plugin_ids.initial_cwd), }) } } @@ -21,6 +23,7 @@ impl TryFrom<PluginIds> for ProtobufPluginIds { Ok(ProtobufPluginIds { plugin_id: plugin_ids.plugin_id as i32, zellij_pid: plugin_ids.zellij_pid as i32, + initial_cwd: plugin_ids.initial_cwd.display().to_string(), }) } } |