summaryrefslogtreecommitdiffstats
path: root/zellij-utils/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-03-18 09:19:58 +0100
committerGitHub <noreply@github.com>2024-03-18 09:19:58 +0100
commitee16a4b8c347a81878389fd4f90144c2c6357f41 (patch)
treebf8675d57499b6278e8c89b84c8141969cc2ce3f /zellij-utils/src
parent12daac3b5445e4281cf5c1810be0ebdb257085c1 (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.rs33
-rw-r--r--zellij-utils/src/errors.rs1
-rw-r--r--zellij-utils/src/input/actions.rs18
-rw-r--r--zellij-utils/src/plugin_api/event.proto9
-rw-r--r--zellij-utils/src/plugin_api/event.rs126
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto3
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs22
-rw-r--r--zellij-utils/src/plugin_api/plugin_ids.proto1
-rw-r--r--zellij-utils/src/plugin_api/plugin_ids.rs3
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(),
})
}
}