Skip to content

Commit acb9755

Browse files
committed
Add action to paste output of a command to a pane.
This is useful for quickly inserting dynamic snippets e.g. current date. Also useful for people who use a terminal without osc52 over ssh, where an external program can act as a "paste buffer" similar to functionality of screen/tmux.
1 parent 50b451a commit acb9755

17 files changed

+76
-4
lines changed

zellij-server/src/route.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use std::collections::{BTreeMap, HashSet, VecDeque};
2+
use std::fs;
3+
use std::process::Command;
24
use std::sync::{Arc, RwLock};
35

46
use crate::thread_bus::ThreadSenders;
@@ -89,6 +91,24 @@ pub(crate) fn route_action(
8991
))
9092
.with_context(err_context)?;
9193
},
94+
Action::WriteCommandOutput(filename) => {
95+
senders
96+
.send_to_screen(ScreenInstruction::ClearScroll(client_id))
97+
.with_context(err_context)?;
98+
let mut cmd_with_args = filename.split(' ').map(String::from);
99+
let cmd: String = cmd_with_args.next()
100+
.with_context(|| format!("need to specify a command with WriteCommandOutput"))?;
101+
let args: Vec<String> = cmd_with_args.collect();
102+
let output = Command::new(cmd.clone())
103+
.args(args.clone())
104+
.output()
105+
.with_context(err_context)?;
106+
senders
107+
.send_to_screen(ScreenInstruction::WriteCharacter(
108+
None, output.stdout, false, client_id,
109+
))
110+
.with_context(err_context)?;
111+
},
92112
Action::SwitchToMode(mode) => {
93113
let attrs = &client_attributes;
94114
senders
19.4 KB
Binary file not shown.
24.4 KB
Binary file not shown.
15.4 KB
Binary file not shown.
Binary file not shown.
17.1 KB
Binary file not shown.
18.5 KB
Binary file not shown.
13.7 KB
Binary file not shown.
19.3 KB
Binary file not shown.
11 KB
Binary file not shown.

zellij-utils/assets/prost/api.action.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub struct Action {
55
pub name: i32,
66
#[prost(
77
oneof = "action::OptionalPayload",
8-
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49"
8+
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50"
99
)]
1010
pub optional_payload: ::core::option::Option<action::OptionalPayload>,
1111
}
@@ -104,6 +104,8 @@ pub mod action {
104104
MoveTabPayload(i32),
105105
#[prost(message, tag = "49")]
106106
MouseEventPayload(super::MouseEventPayload),
107+
#[prost(message, tag = "50")]
108+
WriteCommandOutputPayload(super::WriteCommandOutputPayload),
107109
}
108110
}
109111
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -236,6 +238,12 @@ pub struct WriteCharsPayload {
236238
}
237239
#[allow(clippy::derive_partial_eq_without_eq)]
238240
#[derive(Clone, PartialEq, ::prost::Message)]
241+
pub struct WriteCommandOutputPayload {
242+
#[prost(string, tag = "1")]
243+
pub filename: ::prost::alloc::string::String,
244+
}
245+
#[allow(clippy::derive_partial_eq_without_eq)]
246+
#[derive(Clone, PartialEq, ::prost::Message)]
239247
pub struct DumpScreenPayload {
240248
#[prost(string, tag = "1")]
241249
pub file_path: ::prost::alloc::string::String,
@@ -476,6 +484,7 @@ pub enum ActionName {
476484
KeybindPipe = 84,
477485
TogglePanePinned = 85,
478486
MouseEvent = 86,
487+
WriteCommandOutput = 87,
479488
}
480489
impl ActionName {
481490
/// String value of the enum field names used in the ProtoBuf definition.
@@ -568,6 +577,7 @@ impl ActionName {
568577
ActionName::KeybindPipe => "KeybindPipe",
569578
ActionName::TogglePanePinned => "TogglePanePinned",
570579
ActionName::MouseEvent => "MouseEvent",
580+
ActionName::WriteCommandOutput => "WriteCommandOutput",
571581
}
572582
}
573583
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -657,6 +667,7 @@ impl ActionName {
657667
"KeybindPipe" => Some(Self::KeybindPipe),
658668
"TogglePanePinned" => Some(Self::TogglePanePinned),
659669
"MouseEvent" => Some(Self::MouseEvent),
670+
"WriteCommandOutput" => Some(Self::WriteCommandOutput),
660671
_ => None,
661672
}
662673
}

zellij-utils/src/cli.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ pub enum CliAction {
401401
WriteChars {
402402
chars: String,
403403
},
404+
/// Run the given command and paste its stdout to the terminal.
405+
WriteCommandOutput {
406+
filename: String,
407+
},
404408
/// [increase|decrease] the focused panes area at the [left|down|up|right] border.
405409
Resize {
406410
resize: Resize,

zellij-utils/src/input/actions.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ pub enum Action {
106106
Write(Option<KeyWithModifier>, Vec<u8>, bool), // bool -> is_kitty_keyboard_protocol
107107
/// Write Characters to the terminal.
108108
WriteChars(String),
109+
/// Write content of given file to the terminal.
110+
WriteCommandOutput(String),
109111
/// Switch to the specified input mode.
110112
SwitchToMode(InputMode),
111113
/// Switch all connected clients to the specified input mode.
@@ -317,6 +319,9 @@ impl Action {
317319
match cli_action {
318320
CliAction::Write { bytes } => Ok(vec![Action::Write(None, bytes, false)]),
319321
CliAction::WriteChars { chars } => Ok(vec![Action::WriteChars(chars)]),
322+
CliAction::WriteCommandOutput { filename } => {
323+
Ok(vec![Action::WriteCommandOutput(filename)])
324+
},
320325
CliAction::Resize { resize, direction } => Ok(vec![Action::Resize(resize, direction)]),
321326
CliAction::FocusNextPane => Ok(vec![Action::FocusNextPane]),
322327
CliAction::FocusPreviousPane => Ok(vec![Action::FocusPreviousPane]),

zellij-utils/src/kdl/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ impl Action {
440440
) -> Result<Self, ConfigError> {
441441
match action_name {
442442
"WriteChars" => Ok(Action::WriteChars(string)),
443+
"WriteCommandOutput" => Ok(Action::WriteCommandOutput(string)),
443444
"SwitchToMode" => match InputMode::from_str(string.as_str()) {
444445
Ok(input_mode) => Ok(Action::SwitchToMode(input_mode)),
445446
Err(_e) => {
@@ -587,6 +588,11 @@ impl Action {
587588
node.push(string.clone());
588589
Some(node)
589590
},
591+
Action::WriteCommandOutput(string) => {
592+
let mut node = KdlNode::new("WriteCommandOutput");
593+
node.push(string.clone());
594+
Some(node)
595+
},
590596
Action::SwitchToMode(input_mode) => {
591597
let mut node = KdlNode::new("SwitchToMode");
592598
node.push(format!("{:?}", input_mode).to_lowercase());
@@ -1358,6 +1364,11 @@ impl TryFrom<(&KdlNode, &Options)> for Action {
13581364
action_arguments,
13591365
kdl_action
13601366
),
1367+
"WriteCommandOutput" => parse_kdl_action_char_or_string_arguments!(
1368+
action_name,
1369+
action_arguments,
1370+
kdl_action
1371+
),
13611372
"SwitchToMode" => parse_kdl_action_char_or_string_arguments!(
13621373
action_name,
13631374
action_arguments,
@@ -5355,6 +5366,7 @@ fn keybinds_to_string_with_all_actions() {
53555366
config_key_2 "config_value_2";
53565367
};
53575368
}
5369+
bind "Ctrl Alt k" { WriteCommandOutput "/dev/null"; }
53585370
}
53595371
}"#;
53605372
let document: KdlDocument = fake_config.parse().unwrap();

zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: zellij-utils/src/kdl/mod.rs
3-
assertion_line: 2731
3+
assertion_line: 5408
44
expression: serialized.to_string()
55
---
66
keybinds clear-defaults=true {
@@ -47,6 +47,7 @@ keybinds clear-defaults=true {
4747
}
4848
bind "Alt j" { GoToPreviousTab; }
4949
bind "Ctrl k" { MovePane "right"; }
50+
bind "Ctrl Alt k" { WriteCommandOutput "/dev/null"; }
5051
bind "Alt k" { CloseTab; }
5152
bind "Ctrl l" { MovePaneBackwards; }
5253
bind "Alt l" { GoToTab 1; }
@@ -127,4 +128,3 @@ keybinds clear-defaults=true {
127128
bind "Alt z" { SearchInput 0; }
128129
}
129130
}
130-

zellij-utils/src/plugin_api/action.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ message Action {
5353
CliPipePayload message_payload = 47;
5454
MoveTabDirection move_tab_payload = 48;
5555
MouseEventPayload mouse_event_payload = 49;
56+
WriteCommandOutputPayload write_command_output_payload = 50;
5657
}
5758
}
5859

@@ -151,6 +152,10 @@ message WriteCharsPayload {
151152
string chars = 1;
152153
}
153154

155+
message WriteCommandOutputPayload {
156+
string filename = 1;
157+
}
158+
154159
message DumpScreenPayload {
155160
string file_path = 1;
156161
bool include_scrollback = 2;
@@ -241,6 +246,7 @@ enum ActionName {
241246
KeybindPipe = 84;
242247
TogglePanePinned = 85;
243248
MouseEvent = 86;
249+
WriteCommandOutput = 87;
244250
}
245251

246252
message Position {

zellij-utils/src/plugin_api/action.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub use super::generated_api::api::{
99
PluginConfiguration as ProtobufPluginConfiguration, Position as ProtobufPosition,
1010
RunCommandAction as ProtobufRunCommandAction, ScrollAtPayload,
1111
SearchDirection as ProtobufSearchDirection, SearchOption as ProtobufSearchOption,
12-
SwitchToModePayload, WriteCharsPayload, WritePayload,
12+
SwitchToModePayload, WriteCommandOutputPayload, WriteCharsPayload, WritePayload,
1313
},
1414
input_mode::InputMode as ProtobufInputMode,
1515
resize::{Resize as ProtobufResize, ResizeDirection as ProtobufResizeDirection},
@@ -49,6 +49,14 @@ impl TryFrom<ProtobufAction> for Action {
4949
},
5050
_ => Err("Wrong payload for Action::WriteChars"),
5151
},
52+
Some(ProtobufActionName::WriteCommandOutput) => {
53+
match protobuf_action.optional_payload {
54+
Some(OptionalPayload::WriteCommandOutputPayload(paste_from_file_payload)) => {
55+
Ok(Action::WriteCommandOutput(paste_from_file_payload.filename))
56+
},
57+
_ => Err("Wrong payload for Action::WriteCommandOutput"),
58+
}
59+
},
5260
Some(ProtobufActionName::SwitchToMode) => match protobuf_action.optional_payload {
5361
Some(OptionalPayload::SwitchToModePayload(switch_to_mode_payload)) => {
5462
let input_mode: InputMode =
@@ -735,6 +743,12 @@ impl TryFrom<Action> for ProtobufAction {
735743
chars: chars_to_write,
736744
})),
737745
}),
746+
Action::WriteCommandOutput(filename) => Ok(ProtobufAction {
747+
name: ProtobufActionName::WriteCommandOutput as i32,
748+
optional_payload: Some(OptionalPayload::WriteCommandOutputPayload(
749+
WriteCommandOutputPayload { filename: filename },
750+
)),
751+
}),
738752
Action::SwitchToMode(input_mode) => {
739753
let input_mode: ProtobufInputMode = input_mode.try_into()?;
740754
Ok(ProtobufAction {

0 commit comments

Comments
 (0)