Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): list clients, their focused pane_id and the running command #3314

Merged
merged 2 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions zellij-server/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub enum PluginInstruction {
Option<PathBuf>,
),
DumpLayout(SessionLayoutMetadata, ClientId),
ListClientsMetadata(SessionLayoutMetadata, ClientId),
DumpLayoutToPlugin(SessionLayoutMetadata, PluginId),
LogLayoutToHd(SessionLayoutMetadata),
CliPipe {
Expand Down Expand Up @@ -173,6 +174,7 @@ impl From<&PluginInstruction> for PluginContext {
PluginContext::PermissionRequestResult
},
PluginInstruction::DumpLayout(..) => PluginContext::DumpLayout,
PluginInstruction::ListClientsMetadata(..) => PluginContext::ListClientsMetadata,
PluginInstruction::LogLayoutToHd(..) => PluginContext::LogLayoutToHd,
PluginInstruction::CliPipe { .. } => PluginContext::CliPipe,
PluginInstruction::CachePluginEvents { .. } => PluginContext::CachePluginEvents,
Expand Down Expand Up @@ -489,6 +491,13 @@ pub(crate) fn plugin_thread_main(
client_id,
)));
},
PluginInstruction::ListClientsMetadata(mut session_layout_metadata, client_id) => {
populate_session_layout_metadata(&mut session_layout_metadata, &wasm_bridge);
drop(bus.senders.send_to_pty(PtyInstruction::ListClientsMetadata(
session_layout_metadata,
client_id,
)));
},
PluginInstruction::DumpLayoutToPlugin(mut session_layout_metadata, plugin_id) => {
populate_session_layout_metadata(&mut session_layout_metadata, &wasm_bridge);
match session_serialization::serialize_session_layout(
Expand Down
18 changes: 18 additions & 0 deletions zellij-server/src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub enum PtyInstruction {
Option<PathBuf>, // if Some, will not fill cwd but just forward the message
Option<FloatingPaneCoordinates>,
),
ListClientsMetadata(SessionLayoutMetadata, ClientId),
Exit,
}

Expand All @@ -114,6 +115,7 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::DumpLayoutToPlugin(..) => PtyContext::DumpLayoutToPlugin,
PtyInstruction::LogLayoutToHd(..) => PtyContext::LogLayoutToHd,
PtyInstruction::FillPluginCwd(..) => PtyContext::FillPluginCwd,
PtyInstruction::ListClientsMetadata(..) => PtyContext::ListClientsMetadata,
PtyInstruction::Exit => PtyContext::Exit,
}
}
Expand Down Expand Up @@ -632,6 +634,21 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
},
}
},
PtyInstruction::ListClientsMetadata(mut session_layout_metadata, client_id) => {
let err_context = || format!("Failed to dump layout");
pty.populate_session_layout_metadata(&mut session_layout_metadata);
pty.bus
.senders
.send_to_server(ServerInstruction::Log(
vec![format!(
"{}",
session_layout_metadata.list_clients_metadata()
)],
client_id,
))
.with_context(err_context)
.non_fatal();
},
PtyInstruction::DumpLayoutToPlugin(mut session_layout_metadata, plugin_id) => {
let err_context = || format!("Failed to dump layout");
pty.populate_session_layout_metadata(&mut session_layout_metadata);
Expand Down Expand Up @@ -1342,6 +1359,7 @@ impl Pty {
session_layout_metadata.update_default_shell(get_default_shell());
session_layout_metadata.update_terminal_commands(terminal_ids_to_commands);
session_layout_metadata.update_terminal_cwds(terminal_ids_to_cwds);
session_layout_metadata.update_default_editor(&self.default_editor)
}
pub fn fill_plugin_cwd(
&self,
Expand Down
12 changes: 12 additions & 0 deletions zellij-server/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,18 @@ pub(crate) fn route_action(
log::error!("Message must have a name");
}
},
Action::ListClients => {
let default_shell = match default_shell {
Some(TerminalAction::RunCommand(run_command)) => Some(run_command.command),
_ => None,
};
senders
.send_to_screen(ScreenInstruction::ListClientsMetadata(
default_shell,
client_id,
))
.with_context(err_context)?;
},
}
Ok(should_break)
}
Expand Down
51 changes: 47 additions & 4 deletions zellij-server/src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ pub enum ScreenInstruction {
),
DumpLayoutToHd,
RenameSession(String, ClientId), // String -> new name
ListClientsMetadata(Option<PathBuf>, ClientId), // Option<PathBuf> - default shell
}

impl From<&ScreenInstruction> for ScreenContext {
Expand Down Expand Up @@ -541,6 +542,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::NewInPlacePluginPane(..) => ScreenContext::NewInPlacePluginPane,
ScreenInstruction::DumpLayoutToHd => ScreenContext::DumpLayoutToHd,
ScreenInstruction::RenameSession(..) => ScreenContext::RenameSession,
ScreenInstruction::ListClientsMetadata(..) => ScreenContext::ListClientsMetadata,
}
}
}
Expand Down Expand Up @@ -2165,8 +2167,23 @@ impl Screen {
for (triggering_pane_id, p) in tab.get_suppressed_panes() {
suppressed_panes.insert(*triggering_pane_id, p);
}
let active_pane_id =
first_client_id.and_then(|client_id| tab.get_active_pane_id(client_id));

let all_connected_clients: Vec<ClientId> = self
.connected_clients
.borrow()
.iter()
.copied()
.filter(|c| self.active_tab_indices.get(&c) == Some(&tab_index))
.collect();

let mut active_pane_ids: HashMap<ClientId, Option<PaneId>> = HashMap::new();
for connected_client_id in &all_connected_clients {
active_pane_ids.insert(
*connected_client_id,
tab.get_active_pane_id(*connected_client_id),
);
}

let tiled_panes: Vec<PaneLayoutMetadata> = tab
.get_tiled_panes()
.map(|(pane_id, p)| {
Expand All @@ -2183,18 +2200,25 @@ impl Screen {
}
})
.map(|(pane_id, p)| {
let focused_clients: Vec<ClientId> = active_pane_ids
.iter()
.filter_map(|(c_id, p_id)| {
p_id.and_then(|p_id| if p_id == pane_id { Some(*c_id) } else { None })
})
.collect();
PaneLayoutMetadata::new(
pane_id,
p.position_and_size(),
p.borderless(),
p.invoked_with().clone(),
p.custom_title(),
active_pane_id == Some(pane_id),
!focused_clients.is_empty(),
if self.serialize_pane_viewport {
p.serialize(self.scrollback_lines_to_serialize)
} else {
None
},
focused_clients,
)
})
.collect();
Expand All @@ -2214,18 +2238,25 @@ impl Screen {
}
})
.map(|(pane_id, p)| {
let focused_clients: Vec<ClientId> = active_pane_ids
.iter()
.filter_map(|(c_id, p_id)| {
p_id.and_then(|p_id| if p_id == pane_id { Some(*c_id) } else { None })
})
.collect();
PaneLayoutMetadata::new(
pane_id,
p.position_and_size(),
false, // floating panes are never borderless
p.invoked_with().clone(),
p.custom_title(),
active_pane_id == Some(pane_id),
!focused_clients.is_empty(),
if self.serialize_pane_viewport {
p.serialize(self.scrollback_lines_to_serialize)
} else {
None
},
focused_clients,
)
})
.collect();
Expand Down Expand Up @@ -2660,6 +2691,18 @@ pub(crate) fn screen_thread_main(
))
.with_context(err_context)?;
},
ScreenInstruction::ListClientsMetadata(default_shell, client_id) => {
let err_context = || format!("Failed to dump layout");
let session_layout_metadata = screen.get_layout_metadata(default_shell);
screen
.bus
.senders
.send_to_plugin(PluginInstruction::ListClientsMetadata(
session_layout_metadata,
client_id,
))
.with_context(err_context)?;
},
ScreenInstruction::DumpLayoutToPlugin(plugin_id) => {
let err_context = || format!("Failed to dump layout");
let session_layout_metadata =
Expand Down
99 changes: 97 additions & 2 deletions zellij-server/src/session_layout_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
use crate::panes::PaneId;
use std::collections::HashMap;
use crate::ClientId;
use std::collections::{BTreeMap, HashMap};
use std::path::PathBuf;
use zellij_utils::common_path::common_path_all;
use zellij_utils::pane_size::PaneGeom;
use zellij_utils::{
input::command::RunCommand,
input::layout::{Layout, Run, RunPlugin, RunPluginOrAlias},
session_serialization::{GlobalLayoutManifest, PaneLayoutManifest, TabLayoutManifest},
session_serialization::{
extract_command_and_args, extract_edit_and_line_number, extract_plugin_and_config,
GlobalLayoutManifest, PaneLayoutManifest, TabLayoutManifest,
},
};

#[derive(Default, Debug, Clone)]
pub struct SessionLayoutMetadata {
default_layout: Box<Layout>,
global_cwd: Option<PathBuf>,
pub default_shell: Option<PathBuf>,
pub default_editor: Option<PathBuf>,
tabs: Vec<TabLayoutMetadata>,
}

Expand Down Expand Up @@ -53,6 +58,29 @@ impl SessionLayoutMetadata {
}
}
}
pub fn list_clients_metadata(&self) -> String {
let mut clients_metadata: BTreeMap<ClientId, ClientMetadata> = BTreeMap::new();
for tab in &self.tabs {
let panes = if tab.hide_floating_panes {
&tab.tiled_panes
} else {
&tab.floating_panes
};
for pane in panes {
for focused_client in &pane.focused_clients {
clients_metadata.insert(
*focused_client,
ClientMetadata {
pane_id: pane.id.clone(),
command: pane.run.clone(),
},
);
}
}
}

ClientMetadata::render_many(clients_metadata, &self.default_editor)
}
fn is_default_shell(
default_shell: Option<&PathBuf>,
command_name: &String,
Expand Down Expand Up @@ -192,6 +220,15 @@ impl SessionLayoutMetadata {
}
}
}
pub fn update_default_editor(&mut self, default_editor: &Option<PathBuf>) {
let default_editor = default_editor.clone().unwrap_or_else(|| {
PathBuf::from(
std::env::var("EDITOR")
.unwrap_or_else(|_| std::env::var("VISUAL").unwrap_or_else(|_| "vi".into())),
)
});
self.default_editor = Some(default_editor);
}
}

impl Into<GlobalLayoutManifest> for SessionLayoutMetadata {
Expand Down Expand Up @@ -253,6 +290,7 @@ pub struct PaneLayoutMetadata {
title: Option<String>,
is_focused: bool,
pane_contents: Option<String>,
focused_clients: Vec<ClientId>,
}

impl PaneLayoutMetadata {
Expand All @@ -264,6 +302,7 @@ impl PaneLayoutMetadata {
title: Option<String>,
is_focused: bool,
pane_contents: Option<String>,
focused_clients: Vec<ClientId>,
) -> Self {
PaneLayoutMetadata {
id,
Expand All @@ -274,6 +313,62 @@ impl PaneLayoutMetadata {
title,
is_focused,
pane_contents,
focused_clients,
}
}
}

struct ClientMetadata {
pane_id: PaneId,
command: Option<Run>,
}
impl ClientMetadata {
pub fn stringify_pane_id(&self) -> String {
match self.pane_id {
PaneId::Terminal(terminal_id) => format!("terminal_{}", terminal_id),
PaneId::Plugin(plugin_id) => format!("plugin_{}", plugin_id),
}
}
pub fn stringify_command(&self, editor: &Option<PathBuf>) -> String {
let stringified = match &self.command {
Some(Run::Command(..)) => {
let (command, args) = extract_command_and_args(&self.command);
command.map(|c| format!("{} {}", c, args.join(" ")))
},
Some(Run::EditFile(..)) => {
let (file_to_edit, _line_number) = extract_edit_and_line_number(&self.command);
editor.as_ref().and_then(|editor| {
file_to_edit
.map(|file_to_edit| format!("{} {}", editor.display(), file_to_edit))
})
},
Some(Run::Plugin(..)) => {
let (plugin, _plugin_config) = extract_plugin_and_config(&self.command);
plugin.map(|p| format!("{}", p))
},
_ => None,
};
stringified.unwrap_or("N/A".to_owned())
}
pub fn render_many(
clients_metadata: BTreeMap<ClientId, ClientMetadata>,
default_editor: &Option<PathBuf>,
) -> String {
let mut lines = vec![];
lines.push(String::from("CLIENT_ID ZELLIJ_PANE_ID RUNNING_COMMAND"));

for (client_id, client_metadata) in clients_metadata.iter() {
// 9 - CLIENT_ID, 14 - ZELLIJ_PANE_ID, 15 - RUNNING_COMMAND
lines.push(format!(
"{} {} {}",
format!("{0: <9}", client_id),
format!("{0: <14}", client_metadata.stringify_pane_id()),
format!(
"{0: <15}",
client_metadata.stringify_command(default_editor)
)
));
}
lines.join("\n")
}
}
1 change: 1 addition & 0 deletions zellij-utils/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,4 +736,5 @@ tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://e
#[clap(short('t'), long, value_parser, display_order(10))]
plugin_title: Option<String>,
},
ListClients,
}
3 changes: 3 additions & 0 deletions zellij-utils/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ pub enum ScreenContext {
DumpLayoutToHd,
RenameSession,
DumpLayoutToPlugin,
ListClientsMetadata,
}

/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
Expand All @@ -373,6 +374,7 @@ pub enum PtyContext {
LogLayoutToHd,
FillPluginCwd,
DumpLayoutToPlugin,
ListClientsMetadata,
Exit,
}

Expand Down Expand Up @@ -405,6 +407,7 @@ pub enum PluginContext {
WatchFilesystem,
KeybindPipe,
DumpLayoutToPlugin,
ListClientsMetadata,
}

/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
Expand Down
Loading
Loading