1use crate::data::{Direction, InputMode, Resize, UnblockCondition};
2use crate::setup::Setup;
3use crate::{
4 consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV},
5 input::{layout::PluginUserConfiguration, options::Options},
6};
7use clap::{ArgEnum, Args, Parser, Subcommand};
8use serde::{Deserialize, Serialize};
9use std::net::IpAddr;
10use std::path::PathBuf;
11use url::Url;
12
13fn validate_session(name: &str) -> Result<String, String> {
14 #[cfg(unix)]
15 {
16 use crate::consts::ZELLIJ_SOCK_MAX_LENGTH;
17
18 let mut socket_path = crate::consts::ZELLIJ_SOCK_DIR.clone();
19 socket_path.push(name);
20
21 if socket_path.as_os_str().len() >= ZELLIJ_SOCK_MAX_LENGTH {
22 let available_length = ZELLIJ_SOCK_MAX_LENGTH
24 .saturating_sub(socket_path.as_os_str().len())
25 .saturating_sub(1);
26
27 return Err(format!(
28 "session name must be less than {} characters",
29 available_length
30 ));
31 };
32 };
33
34 Ok(name.to_owned())
35}
36
37#[derive(Parser, Default, Debug, Clone, Serialize, Deserialize)]
38#[clap(version, name = "zellij")]
39pub struct CliArgs {
40 #[clap(long, value_parser)]
42 pub max_panes: Option<usize>,
43
44 #[clap(long, value_parser, overrides_with = "data_dir")]
46 pub data_dir: Option<PathBuf>,
47
48 #[clap(long, value_parser, hide = true, overrides_with = "server")]
50 pub server: Option<PathBuf>,
51
52 #[clap(long, short, overrides_with = "session", value_parser = validate_session)]
54 pub session: Option<String>,
55
56 #[clap(short, long, value_parser, overrides_with = "layout")]
60 pub layout: Option<PathBuf>,
61
62 #[clap(long, value_parser, conflicts_with_all = &["layout", "new-session-with-layout"])]
66 pub layout_string: Option<String>,
67
68 #[clap(short, long, value_parser, overrides_with = "new_session_with_layout")]
71 pub new_session_with_layout: Option<PathBuf>,
72
73 #[clap(short, long, overrides_with = "config", env = ZELLIJ_CONFIG_FILE_ENV, value_parser)]
75 pub config: Option<PathBuf>,
76
77 #[clap(long, overrides_with = "config_dir", env = ZELLIJ_CONFIG_DIR_ENV, value_parser)]
79 pub config_dir: Option<PathBuf>,
80
81 #[clap(subcommand)]
82 pub command: Option<Command>,
83
84 #[clap(short, long, value_parser)]
86 pub debug: bool,
87}
88
89impl CliArgs {
90 pub fn is_setup_clean(&self) -> bool {
91 if let Some(Command::Setup(ref setup)) = &self.command {
92 if setup.clean {
93 return true;
94 }
95 }
96 false
97 }
98 pub fn options(&self) -> Option<Options> {
99 if let Some(Command::Options(options)) = &self.command {
100 return Some(options.clone());
101 }
102 None
103 }
104}
105
106#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
107pub enum Command {
108 #[clap(name = "options", value_parser)]
110 Options(Options),
111
112 #[clap(name = "setup", value_parser)]
114 Setup(Setup),
115
116 #[clap(name = "web", value_parser)]
118 Web(WebCli),
119
120 #[clap(visible_alias = "ac")]
122 #[clap(subcommand)]
123 Action(Box<CliAction>),
124
125 #[clap(flatten)]
127 Sessions(Sessions),
128
129 #[clap(override_usage(
131 "zellij [--session <OTHER SESSION NAME>] subscribe [OPTIONS] --pane-id..."
132 ))]
133 Subscribe(SubscribeCli),
134}
135
136#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
137pub struct SubscribeCli {
138 #[clap(
140 short,
141 long,
142 required = true,
143 multiple_values = true,
144 multiple_occurrences = true
145 )]
146 pub pane_id: Vec<String>,
147
148 #[clap(
151 short,
152 long,
153 min_values = 0,
154 max_values = 1,
155 default_missing_value = "0"
156 )]
157 pub scrollback: Option<usize>,
158
159 #[clap(short, long, default_value = "raw", arg_enum)]
161 pub format: SubscribeFormat,
162
163 #[clap(long, value_parser, default_value("false"), takes_value(false))]
165 pub ansi: bool,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, ArgEnum)]
169pub enum SubscribeFormat {
170 Raw,
171 Json,
172}
173
174#[derive(Debug, Clone, Args, Serialize, Deserialize)]
175pub struct WebCli {
176 #[clap(long, value_parser, display_order = 1)]
178 pub start: bool,
179
180 #[clap(long, value_parser, exclusive(true), display_order = 2)]
182 pub stop: bool,
183
184 #[clap(long, value_parser, conflicts_with("start"), display_order = 3)]
186 pub status: bool,
187
188 #[clap(long, value_parser, requires = "status", display_order = 4)]
190 pub timeout: Option<u64>,
191
192 #[clap(
194 short,
195 long,
196 value_parser,
197 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
198 display_order = 5
199 )]
200 pub daemonize: bool,
201 #[clap(long, value_parser, display_order = 6)]
205 pub server_startup_timeout: Option<u64>,
206 #[clap(long, value_parser, exclusive(true), display_order = 7)]
209 pub create_token: bool,
210 #[clap(long, value_parser, value_name = "TOKEN_NAME", display_order = 8)]
212 pub token_name: Option<String>,
213 #[clap(long, value_parser, exclusive(true), display_order = 9)]
215 pub create_read_only_token: bool,
216 #[clap(
218 long,
219 value_parser,
220 exclusive(true),
221 value_name = "TOKEN NAME",
222 display_order = 10
223 )]
224 pub revoke_token: Option<String>,
225 #[clap(long, value_parser, exclusive(true), display_order = 11)]
227 pub revoke_all_tokens: bool,
228 #[clap(long, value_parser, exclusive(true), display_order = 12)]
230 pub list_tokens: bool,
231 #[clap(
233 long,
234 value_parser,
235 conflicts_with_all(&["stop", "create-token", "revoke-token", "revoke-all-tokens"]),
236 display_order = 13
237 )]
238 pub ip: Option<IpAddr>,
239 #[clap(
241 long,
242 value_parser,
243 conflicts_with_all(&["stop", "create-token", "revoke-token", "revoke-all-tokens"]),
244 display_order = 14
245 )]
246 pub port: Option<u16>,
247 #[clap(
249 long,
250 value_parser,
251 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
252 display_order = 15
253 )]
254 pub cert: Option<PathBuf>,
255 #[clap(
257 long,
258 value_parser,
259 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
260 display_order = 16
261 )]
262 pub key: Option<PathBuf>,
263}
264
265impl WebCli {
266 pub fn get_start(&self) -> bool {
267 self.start
268 || !(self.stop
269 || self.status
270 || self.create_token
271 || self.create_read_only_token
272 || self.revoke_token.is_some()
273 || self.revoke_all_tokens
274 || self.list_tokens)
275 }
276}
277
278#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
279pub enum SessionCommand {
280 #[clap(name = "options")]
282 Options(Options),
283}
284
285#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
286pub enum Sessions {
287 #[clap(visible_alias = "ls")]
289 ListSessions {
290 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
292 no_formatting: bool,
293
294 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
296 short: bool,
297
298 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
300 reverse: bool,
301 },
302 #[clap(visible_alias = "la")]
304 ListAliases,
305 #[clap(visible_alias = "a")]
307 Attach {
308 #[clap(value_parser)]
310 session_name: Option<String>,
311
312 #[clap(short, long, value_parser)]
314 create: bool,
315
316 #[clap(short('b'), long, value_parser)]
318 create_background: bool,
319
320 #[clap(long, value_parser)]
322 index: Option<usize>,
323
324 #[clap(subcommand, name = "options")]
326 options: Option<Box<SessionCommand>>,
327
328 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
330 force_run_commands: bool,
331
332 #[clap(short('t'), long, value_parser)]
334 token: Option<String>,
335
336 #[clap(short('r'), long, value_parser)]
338 remember: bool,
339
340 #[clap(long, value_parser)]
342 forget: bool,
343
344 #[clap(long, value_name = "FILE", value_parser)]
346 ca_cert: Option<PathBuf>,
347
348 #[clap(long, value_parser)]
350 insecure: bool,
351 },
352
353 #[clap(visible_alias = "w")]
355 Watch {
356 #[clap(value_parser)]
358 session_name: Option<String>,
359 },
360
361 #[clap(visible_alias = "k")]
363 KillSession {
364 #[clap(value_parser)]
366 target_session: Option<String>,
367 },
368
369 #[clap(visible_alias = "d")]
371 DeleteSession {
372 #[clap(value_parser)]
374 target_session: Option<String>,
375 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
377 force: bool,
378 },
379
380 #[clap(visible_alias = "ka")]
382 KillAllSessions {
383 #[clap(short, long, value_parser)]
385 yes: bool,
386 },
387
388 #[clap(visible_alias = "da")]
390 DeleteAllSessions {
391 #[clap(short, long, value_parser)]
393 yes: bool,
394 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
396 force: bool,
397 },
398
399 #[clap(visible_alias = "r")]
402 Run {
403 #[clap(last(true), required(true))]
405 command: Vec<String>,
406
407 #[clap(short, long, value_parser, conflicts_with("floating"))]
409 direction: Option<Direction>,
410
411 #[clap(long, value_parser)]
413 cwd: Option<PathBuf>,
414
415 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
417 floating: bool,
418
419 #[clap(
421 short,
422 long,
423 value_parser,
424 default_value("false"),
425 takes_value(false),
426 conflicts_with("floating"),
427 conflicts_with("direction")
428 )]
429 in_place: bool,
430
431 #[clap(
433 long,
434 value_parser,
435 default_value("false"),
436 takes_value(false),
437 requires("in-place")
438 )]
439 close_replaced_pane: bool,
440
441 #[clap(short, long, value_parser)]
443 name: Option<String>,
444
445 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
447 close_on_exit: bool,
448
449 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
451 start_suspended: bool,
452
453 #[clap(short, long, requires("floating"))]
455 x: Option<String>,
456 #[clap(short, long, requires("floating"))]
458 y: Option<String>,
459 #[clap(long, requires("floating"))]
461 width: Option<String>,
462 #[clap(long, requires("floating"))]
464 height: Option<String>,
465 #[clap(long, requires("floating"))]
467 pinned: Option<bool>,
468 #[clap(
469 long,
470 conflicts_with("floating"),
471 conflicts_with("direction"),
472 value_parser,
473 default_value("false"),
474 takes_value(false)
475 )]
476 stacked: bool,
477 #[clap(long, value_parser, default_value("false"), takes_value(false))]
479 blocking: bool,
480
481 #[clap(
483 long,
484 value_parser,
485 default_value("false"),
486 takes_value(false),
487 conflicts_with("blocking"),
488 conflicts_with("block-until-exit-failure"),
489 conflicts_with("block-until-exit")
490 )]
491 block_until_exit_success: bool,
492
493 #[clap(
496 long,
497 value_parser,
498 default_value("false"),
499 takes_value(false),
500 conflicts_with("blocking"),
501 conflicts_with("block-until-exit-success"),
502 conflicts_with("block-until-exit")
503 )]
504 block_until_exit_failure: bool,
505
506 #[clap(
508 long,
509 value_parser,
510 default_value("false"),
511 takes_value(false),
512 conflicts_with("blocking"),
513 conflicts_with("block-until-exit-success"),
514 conflicts_with("block-until-exit-failure")
515 )]
516 block_until_exit: bool,
517 #[clap(long)]
519 near_current_pane: bool,
520 #[clap(short, long, value_parser)]
523 borderless: Option<bool>,
524 #[clap(
526 long,
527 value_parser,
528 conflicts_with("near-current-pane"),
529 conflicts_with("in-place")
530 )]
531 tab_id: Option<usize>,
532 },
533 #[clap(visible_alias = "p")]
536 Plugin {
537 #[clap(last(true), required(true))]
539 url: String,
540
541 #[clap(short, long, value_parser)]
543 configuration: Option<PluginUserConfiguration>,
544
545 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
547 floating: bool,
548
549 #[clap(
551 short,
552 long,
553 value_parser,
554 default_value("false"),
555 takes_value(false),
556 conflicts_with("floating")
557 )]
558 in_place: bool,
559
560 #[clap(
562 long,
563 value_parser,
564 default_value("false"),
565 takes_value(false),
566 requires("in-place")
567 )]
568 close_replaced_pane: bool,
569
570 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
572 skip_plugin_cache: bool,
573 #[clap(short, long, requires("floating"))]
575 x: Option<String>,
576 #[clap(short, long, requires("floating"))]
578 y: Option<String>,
579 #[clap(long, requires("floating"))]
581 width: Option<String>,
582 #[clap(long, requires("floating"))]
584 height: Option<String>,
585 #[clap(long, requires("floating"))]
587 pinned: Option<bool>,
588 #[clap(short, long, value_parser)]
591 borderless: Option<bool>,
592 #[clap(long, value_parser, conflicts_with("in-place"))]
594 tab_id: Option<usize>,
595 },
596 #[clap(visible_alias = "e")]
599 Edit {
600 file: PathBuf,
601
602 #[clap(short, long, value_parser)]
604 line_number: Option<usize>,
605
606 #[clap(short, long, value_parser, conflicts_with("floating"))]
608 direction: Option<Direction>,
609
610 #[clap(
612 short,
613 long,
614 value_parser,
615 default_value("false"),
616 takes_value(false),
617 conflicts_with("floating"),
618 conflicts_with("direction")
619 )]
620 in_place: bool,
621
622 #[clap(
624 long,
625 value_parser,
626 default_value("false"),
627 takes_value(false),
628 requires("in-place")
629 )]
630 close_replaced_pane: bool,
631
632 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
634 floating: bool,
635
636 #[clap(long, value_parser)]
638 cwd: Option<PathBuf>,
639 #[clap(short, long, requires("floating"))]
641 x: Option<String>,
642 #[clap(short, long, requires("floating"))]
644 y: Option<String>,
645 #[clap(long, requires("floating"))]
647 width: Option<String>,
648 #[clap(long, requires("floating"))]
650 height: Option<String>,
651 #[clap(long, requires("floating"))]
653 pinned: Option<bool>,
654 #[clap(long)]
656 near_current_pane: bool,
657 #[clap(short, long, value_parser)]
660 borderless: Option<bool>,
661 #[clap(
663 long,
664 value_parser,
665 conflicts_with("near-current-pane"),
666 conflicts_with("in-place")
667 )]
668 tab_id: Option<usize>,
669 },
670 ConvertConfig {
671 old_config_file: PathBuf,
672 },
673 ConvertLayout {
674 old_layout_file: PathBuf,
675 },
676 ConvertTheme {
677 old_theme_file: PathBuf,
678 },
679 #[clap(override_usage(
681r#"
682zellij pipe [OPTIONS] [--] <PAYLOAD>
683
684* Send data to a specific plugin:
685
686zellij pipe --plugin file:/path/to/my/plugin.wasm --name my_pipe_name -- my_arbitrary_data
687
688* To all running plugins (that are listening):
689
690zellij pipe --name my_pipe_name -- my_arbitrary_data
691
692* Pipe data into this command's STDIN and get output from the plugin on this command's STDOUT
693
694tail -f /tmp/my-live-logfile | zellij pipe --name logs --plugin https://example.com/my-plugin.wasm | wc -l
695"#))]
696 Pipe {
697 #[clap(short, long, value_parser, display_order(1))]
699 name: Option<String>,
700 payload: Option<String>,
702
703 #[clap(short, long, value_parser, display_order(2))]
704 args: Option<PluginUserConfiguration>, #[clap(short, long, value_parser, display_order(3))]
710 plugin: Option<String>,
711 #[clap(short('c'), long, value_parser, display_order(4))]
714 plugin_configuration: Option<PluginUserConfiguration>,
715 },
716}
717
718#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
719pub enum CliAction {
720 Write {
722 bytes: Vec<u8>,
723 #[clap(short, long, value_parser)]
725 pane_id: Option<String>,
726 },
727 WriteChars {
729 chars: String,
730 #[clap(short, long, value_parser)]
732 pane_id: Option<String>,
733 },
734 Paste {
736 chars: String,
737 #[clap(short, long, value_parser)]
739 pane_id: Option<String>,
740 },
741 SendKeys {
743 #[clap(value_parser, required = true)]
745 keys: Vec<String>,
746
747 #[clap(short, long, value_parser)]
749 pane_id: Option<String>,
750 },
751 Resize {
753 resize: Resize,
754 direction: Option<Direction>,
755 #[clap(short, long, value_parser)]
757 pane_id: Option<String>,
758 },
759 FocusNextPane,
761 FocusPreviousPane,
763 FocusPaneId {
765 pane_id: String,
767 },
768 MoveFocus {
770 direction: Direction,
771 },
772 MoveFocusOrTab {
775 direction: Direction,
776 },
777 MovePane {
780 direction: Option<Direction>,
781 #[clap(short, long, value_parser)]
783 pane_id: Option<String>,
784 },
785 MovePaneBackwards {
787 #[clap(short, long, value_parser)]
789 pane_id: Option<String>,
790 },
791 Clear {
793 #[clap(short, long, value_parser)]
795 pane_id: Option<String>,
796 },
797 DumpScreen {
799 #[clap(long, value_parser)]
801 path: Option<PathBuf>,
802
803 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
805 full: bool,
806
807 #[clap(short, long, value_parser)]
809 pane_id: Option<String>,
810
811 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
813 ansi: bool,
814 },
815 DumpLayout,
817 SaveSession,
819 EditScrollback {
821 #[clap(short, long, value_parser)]
823 pane_id: Option<String>,
824
825 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
827 ansi: bool,
828 },
829 ScrollUp {
831 #[clap(short, long, value_parser)]
833 pane_id: Option<String>,
834 },
835 ScrollDown {
837 #[clap(short, long, value_parser)]
839 pane_id: Option<String>,
840 },
841 ScrollToBottom {
843 #[clap(short, long, value_parser)]
845 pane_id: Option<String>,
846 },
847 ScrollToTop {
849 #[clap(short, long, value_parser)]
851 pane_id: Option<String>,
852 },
853 PageScrollUp {
855 #[clap(short, long, value_parser)]
857 pane_id: Option<String>,
858 },
859 PageScrollDown {
861 #[clap(short, long, value_parser)]
863 pane_id: Option<String>,
864 },
865 HalfPageScrollUp {
867 #[clap(short, long, value_parser)]
869 pane_id: Option<String>,
870 },
871 HalfPageScrollDown {
873 #[clap(short, long, value_parser)]
875 pane_id: Option<String>,
876 },
877 ToggleFullscreen {
879 #[clap(short, long, value_parser)]
881 pane_id: Option<String>,
882 },
883 TogglePaneFrames,
885 ToggleActiveSyncTab {
887 #[clap(short, long, value_parser)]
889 tab_id: Option<usize>,
890 },
891 NewPane {
895 #[clap(short, long, value_parser, conflicts_with("floating"))]
897 direction: Option<Direction>,
898
899 #[clap(last(true))]
900 command: Vec<String>,
901
902 #[clap(short, long, conflicts_with("command"), conflicts_with("direction"))]
903 plugin: Option<String>,
904
905 #[clap(long, value_parser)]
907 cwd: Option<PathBuf>,
908
909 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
911 floating: bool,
912
913 #[clap(
915 short,
916 long,
917 value_parser,
918 default_value("false"),
919 takes_value(false),
920 conflicts_with("floating"),
921 conflicts_with("direction")
922 )]
923 in_place: bool,
924
925 #[clap(
927 long,
928 value_parser,
929 default_value("false"),
930 takes_value(false),
931 requires("in-place")
932 )]
933 close_replaced_pane: bool,
934
935 #[clap(short, long, value_parser)]
937 name: Option<String>,
938
939 #[clap(
941 short,
942 long,
943 value_parser,
944 default_value("false"),
945 takes_value(false),
946 requires("command")
947 )]
948 close_on_exit: bool,
949 #[clap(
951 short,
952 long,
953 value_parser,
954 default_value("false"),
955 takes_value(false),
956 requires("command")
957 )]
958 start_suspended: bool,
959 #[clap(long, value_parser)]
960 configuration: Option<PluginUserConfiguration>,
961 #[clap(long, value_parser)]
962 skip_plugin_cache: bool,
963 #[clap(short, long, requires("floating"))]
965 x: Option<String>,
966 #[clap(short, long, requires("floating"))]
968 y: Option<String>,
969 #[clap(long, requires("floating"))]
971 width: Option<String>,
972 #[clap(long, requires("floating"))]
974 height: Option<String>,
975 #[clap(long, requires("floating"))]
977 pinned: Option<bool>,
978 #[clap(
979 long,
980 conflicts_with("floating"),
981 conflicts_with("direction"),
982 value_parser,
983 default_value("false"),
984 takes_value(false)
985 )]
986 stacked: bool,
987 #[clap(short, long)]
989 blocking: bool,
990
991 #[clap(
993 long,
994 value_parser,
995 default_value("false"),
996 takes_value(false),
997 conflicts_with("blocking"),
998 conflicts_with("block-until-exit-failure"),
999 conflicts_with("block-until-exit")
1000 )]
1001 block_until_exit_success: bool,
1002
1003 #[clap(
1006 long,
1007 value_parser,
1008 default_value("false"),
1009 takes_value(false),
1010 conflicts_with("blocking"),
1011 conflicts_with("block-until-exit-success"),
1012 conflicts_with("block-until-exit")
1013 )]
1014 block_until_exit_failure: bool,
1015
1016 #[clap(
1018 long,
1019 value_parser,
1020 default_value("false"),
1021 takes_value(false),
1022 conflicts_with("blocking"),
1023 conflicts_with("block-until-exit-success"),
1024 conflicts_with("block-until-exit-failure")
1025 )]
1026 block_until_exit: bool,
1027
1028 #[clap(skip)]
1029 unblock_condition: Option<UnblockCondition>,
1030
1031 #[clap(long)]
1033 near_current_pane: bool,
1034 #[clap(long, value_parser)]
1037 borderless: Option<bool>,
1038 #[clap(
1040 long,
1041 value_parser,
1042 conflicts_with("near-current-pane"),
1043 conflicts_with("in-place")
1044 )]
1045 tab_id: Option<usize>,
1046 },
1047 Edit {
1050 file: PathBuf,
1051
1052 #[clap(short, long, value_parser, conflicts_with("floating"))]
1054 direction: Option<Direction>,
1055
1056 #[clap(short, long, value_parser)]
1058 line_number: Option<usize>,
1059
1060 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
1062 floating: bool,
1063
1064 #[clap(
1066 short,
1067 long,
1068 value_parser,
1069 default_value("false"),
1070 takes_value(false),
1071 conflicts_with("floating"),
1072 conflicts_with("direction")
1073 )]
1074 in_place: bool,
1075
1076 #[clap(
1078 long,
1079 value_parser,
1080 default_value("false"),
1081 takes_value(false),
1082 requires("in-place")
1083 )]
1084 close_replaced_pane: bool,
1085
1086 #[clap(long, value_parser)]
1088 cwd: Option<PathBuf>,
1089 #[clap(short, long, requires("floating"))]
1091 x: Option<String>,
1092 #[clap(short, long, requires("floating"))]
1094 y: Option<String>,
1095 #[clap(long, requires("floating"))]
1097 width: Option<String>,
1098 #[clap(long, requires("floating"))]
1100 height: Option<String>,
1101 #[clap(long, requires("floating"))]
1103 pinned: Option<bool>,
1104 #[clap(long)]
1106 near_current_pane: bool,
1107 #[clap(short, long, value_parser)]
1110 borderless: Option<bool>,
1111 #[clap(
1113 long,
1114 value_parser,
1115 conflicts_with("near-current-pane"),
1116 conflicts_with("in-place")
1117 )]
1118 tab_id: Option<usize>,
1119 },
1120 SwitchMode {
1122 input_mode: InputMode,
1123 },
1124 TogglePaneEmbedOrFloating {
1126 #[clap(short, long, value_parser)]
1128 pane_id: Option<String>,
1129 },
1130 ToggleFloatingPanes {
1132 #[clap(short, long, value_parser)]
1134 tab_id: Option<usize>,
1135 },
1136 ShowFloatingPanes {
1140 #[clap(short, long, value_parser)]
1141 tab_id: Option<usize>,
1142 },
1143 HideFloatingPanes {
1147 #[clap(short, long, value_parser)]
1148 tab_id: Option<usize>,
1149 },
1150 AreFloatingPanesVisible {
1155 #[clap(short, long, value_parser)]
1156 tab_id: Option<usize>,
1157 },
1158 ClosePane {
1160 #[clap(short, long, value_parser)]
1162 pane_id: Option<String>,
1163 },
1164 RenamePane {
1166 name: String,
1167 #[clap(short, long, value_parser)]
1169 pane_id: Option<String>,
1170 },
1171 UndoRenamePane {
1173 #[clap(short, long, value_parser)]
1175 pane_id: Option<String>,
1176 },
1177 GoToNextTab,
1179 GoToPreviousTab,
1181 CloseTab {
1183 #[clap(short, long, value_parser)]
1185 tab_id: Option<usize>,
1186 },
1187 GoToTab {
1189 index: u32,
1190 },
1191 GoToTabName {
1195 name: String,
1196 #[clap(short, long, value_parser)]
1198 create: bool,
1199 },
1200 RenameTab {
1202 name: String,
1203 #[clap(short, long, value_parser)]
1205 tab_id: Option<usize>,
1206 },
1207 UndoRenameTab {
1209 #[clap(short, long, value_parser)]
1211 tab_id: Option<usize>,
1212 },
1213 GoToTabById {
1215 id: u64,
1216 },
1217 CloseTabById {
1219 id: u64,
1220 },
1221 RenameTabById {
1223 id: u64,
1224 name: String,
1225 },
1226 NewTab {
1230 #[clap(short, long, value_parser, conflicts_with = "layout-string")]
1232 layout: Option<PathBuf>,
1233
1234 #[clap(long, value_parser, conflicts_with = "layout")]
1236 layout_string: Option<String>,
1237
1238 #[clap(long, value_parser, requires("layout"))]
1240 layout_dir: Option<PathBuf>,
1241
1242 #[clap(short, long, value_parser)]
1244 name: Option<String>,
1245
1246 #[clap(short, long, value_parser)]
1248 cwd: Option<PathBuf>,
1249
1250 #[clap(
1252 value_parser,
1253 conflicts_with("initial-plugin"),
1254 multiple_values(true),
1255 takes_value(true),
1256 last(true)
1257 )]
1258 initial_command: Vec<String>,
1259
1260 #[clap(long, value_parser, conflicts_with("initial-command"))]
1262 initial_plugin: Option<String>,
1263
1264 #[clap(
1266 long,
1267 value_parser,
1268 default_value("false"),
1269 takes_value(false),
1270 requires("initial-command")
1271 )]
1272 close_on_exit: bool,
1273
1274 #[clap(
1276 long,
1277 value_parser,
1278 default_value("false"),
1279 takes_value(false),
1280 requires("initial-command")
1281 )]
1282 start_suspended: bool,
1283
1284 #[clap(
1286 long,
1287 value_parser,
1288 default_value("false"),
1289 takes_value(false),
1290 requires("initial-command"),
1291 conflicts_with("block-until-exit-failure"),
1292 conflicts_with("block-until-exit")
1293 )]
1294 block_until_exit_success: bool,
1295
1296 #[clap(
1298 long,
1299 value_parser,
1300 default_value("false"),
1301 takes_value(false),
1302 requires("initial-command"),
1303 conflicts_with("block-until-exit-success"),
1304 conflicts_with("block-until-exit")
1305 )]
1306 block_until_exit_failure: bool,
1307
1308 #[clap(
1310 long,
1311 value_parser,
1312 default_value("false"),
1313 takes_value(false),
1314 requires("initial-command"),
1315 conflicts_with("block-until-exit-success"),
1316 conflicts_with("block-until-exit-failure")
1317 )]
1318 block_until_exit: bool,
1319 },
1320 MoveTab {
1322 direction: Direction,
1323 #[clap(short, long, value_parser)]
1325 tab_id: Option<usize>,
1326 },
1327 PreviousSwapLayout {
1328 #[clap(short, long, value_parser)]
1330 tab_id: Option<usize>,
1331 },
1332 NextSwapLayout {
1333 #[clap(short, long, value_parser)]
1335 tab_id: Option<usize>,
1336 },
1337 OverrideLayout {
1339 #[clap(
1341 value_parser,
1342 required_unless_present = "layout-string",
1343 conflicts_with = "layout-string"
1344 )]
1345 layout: Option<PathBuf>,
1346
1347 #[clap(long, value_parser, conflicts_with = "layout")]
1349 layout_string: Option<String>,
1350
1351 #[clap(long, value_parser)]
1353 layout_dir: Option<PathBuf>,
1354
1355 #[clap(long, value_parser, takes_value(false), default_value("false"))]
1357 retain_existing_terminal_panes: bool,
1358
1359 #[clap(long, value_parser, takes_value(false), default_value("false"))]
1361 retain_existing_plugin_panes: bool,
1362
1363 #[clap(long, value_parser, takes_value(false), default_value("false"))]
1366 apply_only_to_active_tab: bool,
1367 },
1368 QueryTabNames,
1370 StartOrReloadPlugin {
1371 url: String,
1372 #[clap(short, long, value_parser)]
1373 configuration: Option<PluginUserConfiguration>,
1374 },
1375 LaunchOrFocusPlugin {
1377 #[clap(short, long, value_parser)]
1378 floating: bool,
1379 #[clap(short, long, value_parser)]
1380 in_place: bool,
1381 #[clap(
1383 long,
1384 value_parser,
1385 default_value("false"),
1386 takes_value(false),
1387 requires("in-place")
1388 )]
1389 close_replaced_pane: bool,
1390 #[clap(short, long, value_parser)]
1391 move_to_focused_tab: bool,
1392 url: String,
1393 #[clap(short, long, value_parser)]
1394 configuration: Option<PluginUserConfiguration>,
1395 #[clap(short, long, value_parser)]
1396 skip_plugin_cache: bool,
1397 #[clap(long, value_parser, conflicts_with("in-place"))]
1399 tab_id: Option<usize>,
1400 },
1401 LaunchPlugin {
1403 #[clap(short, long, value_parser)]
1404 floating: bool,
1405 #[clap(short, long, value_parser)]
1406 in_place: bool,
1407 #[clap(
1409 long,
1410 value_parser,
1411 default_value("false"),
1412 takes_value(false),
1413 requires("in-place")
1414 )]
1415 close_replaced_pane: bool,
1416 url: Url,
1417 #[clap(short, long, value_parser)]
1418 configuration: Option<PluginUserConfiguration>,
1419 #[clap(short, long, value_parser)]
1420 skip_plugin_cache: bool,
1421 #[clap(long, value_parser, conflicts_with("in-place"))]
1423 tab_id: Option<usize>,
1424 },
1425 RenameSession {
1426 name: String,
1427 },
1428 #[clap(override_usage(
1430r#"
1431zellij action pipe [OPTIONS] [--] <PAYLOAD>
1432
1433* Send data to a specific plugin:
1434
1435zellij action pipe --plugin file:/path/to/my/plugin.wasm --name my_pipe_name -- my_arbitrary_data
1436
1437* To all running plugins (that are listening):
1438
1439zellij action pipe --name my_pipe_name -- my_arbitrary_data
1440
1441* Pipe data into this command's STDIN and get output from the plugin on this command's STDOUT
1442
1443tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://example.com/my-plugin.wasm | wc -l
1444"#))]
1445 Pipe {
1446 #[clap(short, long, value_parser, display_order(1))]
1448 name: Option<String>,
1449 payload: Option<String>,
1451
1452 #[clap(short, long, value_parser, display_order(2))]
1453 args: Option<PluginUserConfiguration>, #[clap(short, long, value_parser, display_order(3))]
1459 plugin: Option<String>,
1460 #[clap(short('c'), long, value_parser, display_order(4))]
1463 plugin_configuration: Option<PluginUserConfiguration>,
1464 #[clap(
1466 short('l'),
1467 long,
1468 value_parser,
1469 takes_value(false),
1470 default_value("false"),
1471 display_order(5)
1472 )]
1473 force_launch_plugin: bool,
1474 #[clap(
1476 short('s'),
1477 long,
1478 value_parser,
1479 takes_value(false),
1480 default_value("false"),
1481 display_order(6)
1482 )]
1483 skip_plugin_cache: bool,
1484 #[clap(short('f'), long, value_parser, display_order(7))]
1486 floating_plugin: Option<bool>,
1487 #[clap(
1489 short('i'),
1490 long,
1491 value_parser,
1492 conflicts_with("floating-plugin"),
1493 display_order(8)
1494 )]
1495 in_place_plugin: Option<bool>,
1496 #[clap(short('w'), long, value_parser, display_order(9))]
1498 plugin_cwd: Option<PathBuf>,
1499 #[clap(short('t'), long, value_parser, display_order(10))]
1501 plugin_title: Option<String>,
1502 },
1503 ListClients,
1504 ListPanes {
1508 #[clap(short, long, value_parser)]
1510 tab: bool,
1511
1512 #[clap(short, long, value_parser)]
1514 command: bool,
1515
1516 #[clap(short, long, value_parser)]
1518 state: bool,
1519
1520 #[clap(short, long, value_parser)]
1522 geometry: bool,
1523
1524 #[clap(short, long, value_parser)]
1526 all: bool,
1527
1528 #[clap(short, long, value_parser)]
1530 json: bool,
1531 },
1532 ListTabs {
1536 #[clap(short, long, value_parser)]
1538 state: bool,
1539
1540 #[clap(short, long, value_parser)]
1542 dimensions: bool,
1543
1544 #[clap(short, long, value_parser)]
1546 panes: bool,
1547
1548 #[clap(short, long, value_parser)]
1550 layout: bool,
1551
1552 #[clap(short, long, value_parser)]
1554 all: bool,
1555
1556 #[clap(short, long, value_parser)]
1558 json: bool,
1559 },
1560 CurrentTabInfo {
1564 #[clap(short, long, value_parser)]
1566 json: bool,
1567 },
1568 TogglePanePinned {
1569 #[clap(short, long, value_parser)]
1571 pane_id: Option<String>,
1572 },
1573 StackPanes {
1581 #[clap(last(true), required(true))]
1582 pane_ids: Vec<String>,
1583 },
1584 ChangeFloatingPaneCoordinates {
1585 #[clap(short, long, value_parser)]
1588 pane_id: String,
1589 #[clap(short, long)]
1591 x: Option<String>,
1592 #[clap(short, long)]
1594 y: Option<String>,
1595 #[clap(long)]
1597 width: Option<String>,
1598 #[clap(long)]
1600 height: Option<String>,
1601 #[clap(long)]
1603 pinned: Option<bool>,
1604 #[clap(short, long, value_parser)]
1607 borderless: Option<bool>,
1608 },
1609 TogglePaneBorderless {
1610 #[clap(short, long, value_parser)]
1612 pane_id: String,
1613 },
1614 SetPaneBorderless {
1615 #[clap(short, long, value_parser)]
1617 pane_id: String,
1618 #[clap(short, long, value_parser)]
1620 borderless: bool,
1621 },
1622 Detach,
1624 SwitchSession {
1626 name: String,
1628 #[clap(long)]
1630 tab_position: Option<usize>,
1631 #[clap(long)]
1633 pane_id: Option<String>,
1634 #[clap(short, long, value_parser, conflicts_with = "layout-string")]
1636 layout: Option<PathBuf>,
1637 #[clap(long, value_parser, conflicts_with = "layout")]
1639 layout_string: Option<String>,
1640 #[clap(long, value_parser, requires("layout"))]
1642 layout_dir: Option<PathBuf>,
1643 #[clap(short, long, value_parser)]
1645 cwd: Option<PathBuf>,
1646 },
1647 SetPaneColor {
1649 #[clap(short, long, value_parser)]
1652 pane_id: Option<String>,
1653 #[clap(long, value_parser)]
1655 fg: Option<String>,
1656 #[clap(long, value_parser)]
1658 bg: Option<String>,
1659 #[clap(long, value_parser, conflicts_with_all(&["fg", "bg"]))]
1661 reset: bool,
1662 },
1663}
1664
1665#[cfg(test)]
1666mod tests {
1667 use super::*;
1668 use clap::Parser;
1669
1670 fn parse_subscribe(args: &[&str]) -> SubscribeCli {
1671 let mut full_args = vec!["zellij"];
1672 full_args.extend_from_slice(args);
1673 let cli = CliArgs::try_parse_from(full_args).unwrap();
1674 match cli.command {
1675 Some(Command::Subscribe(s)) => s,
1676 other => panic!("Expected Subscribe, got {:?}", other),
1677 }
1678 }
1679
1680 #[test]
1681 fn subscribe_scrollback_bare_flag() {
1682 let s = parse_subscribe(&["subscribe", "--pane-id", "terminal_1", "--scrollback"]);
1683 assert_eq!(s.scrollback, Some(0));
1684 }
1685
1686 #[test]
1687 fn subscribe_scrollback_with_value() {
1688 let s = parse_subscribe(&[
1689 "subscribe",
1690 "--pane-id",
1691 "terminal_1",
1692 "--scrollback",
1693 "100",
1694 ]);
1695 assert_eq!(s.scrollback, Some(100));
1696 }
1697
1698 #[test]
1699 fn subscribe_scrollback_absent() {
1700 let s = parse_subscribe(&["subscribe", "--pane-id", "terminal_1"]);
1701 assert_eq!(s.scrollback, None);
1702 }
1703
1704 #[test]
1705 fn subscribe_format_json() {
1706 let s = parse_subscribe(&["subscribe", "--pane-id", "terminal_1", "--format", "json"]);
1707 assert!(matches!(s.format, SubscribeFormat::Json));
1708 }
1709
1710 #[test]
1711 fn subscribe_format_default_raw() {
1712 let s = parse_subscribe(&["subscribe", "--pane-id", "terminal_1"]);
1713 assert!(matches!(s.format, SubscribeFormat::Raw));
1714 }
1715
1716 #[test]
1717 fn subscribe_multiple_pane_ids() {
1718 let s = parse_subscribe(&[
1719 "subscribe",
1720 "--pane-id",
1721 "terminal_1",
1722 "--pane-id",
1723 "plugin_2",
1724 ]);
1725 assert_eq!(
1726 s.pane_id,
1727 vec!["terminal_1".to_string(), "plugin_2".to_string()]
1728 );
1729 }
1730
1731 #[test]
1732 fn subscribe_requires_pane_id() {
1733 let result = CliArgs::try_parse_from(["zellij", "subscribe"]);
1734 assert!(result.is_err());
1735 }
1736}