1use crate::data::{Direction, InputMode, Resize};
2use crate::setup::Setup;
3use crate::{
4 consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV},
5 input::{layout::PluginUserConfiguration, options::CliOptions},
6};
7use clap::{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(short, long, value_parser, overrides_with = "new_session_with_layout")]
65 pub new_session_with_layout: Option<PathBuf>,
66
67 #[clap(short, long, overrides_with = "config", env = ZELLIJ_CONFIG_FILE_ENV, value_parser)]
69 pub config: Option<PathBuf>,
70
71 #[clap(long, overrides_with = "config_dir", env = ZELLIJ_CONFIG_DIR_ENV, value_parser)]
73 pub config_dir: Option<PathBuf>,
74
75 #[clap(subcommand)]
76 pub command: Option<Command>,
77
78 #[clap(short, long, value_parser)]
80 pub debug: bool,
81}
82
83#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
84pub enum Command {
85 #[clap(name = "options", value_parser)]
87 Options(CliOptions),
88
89 #[clap(name = "setup", value_parser)]
91 Setup(Setup),
92
93 #[clap(name = "web", value_parser)]
95 Web(WebCli),
96
97 #[clap(flatten)]
99 Sessions(Sessions),
100}
101
102#[derive(Debug, Clone, Args, Serialize, Deserialize)]
103pub struct WebCli {
104 #[clap(long, value_parser, display_order = 1)]
106 pub start: bool,
107
108 #[clap(long, value_parser, exclusive(true), display_order = 2)]
110 pub stop: bool,
111
112 #[clap(long, value_parser, exclusive(true), display_order = 3)]
114 pub status: bool,
115
116 #[clap(
118 short,
119 long,
120 value_parser,
121 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
122 display_order = 4
123 )]
124 pub daemonize: bool,
125 #[clap(long, value_parser, exclusive(true), display_order = 5)]
128 pub create_token: bool,
129 #[clap(
131 long,
132 value_parser,
133 exclusive(true),
134 value_name = "TOKEN NAME",
135 display_order = 6
136 )]
137 pub revoke_token: Option<String>,
138 #[clap(long, value_parser, exclusive(true), display_order = 7)]
140 pub revoke_all_tokens: bool,
141 #[clap(long, value_parser, exclusive(true), display_order = 8)]
143 pub list_tokens: bool,
144 #[clap(
146 long,
147 value_parser,
148 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
149 display_order = 9
150 )]
151 pub ip: Option<IpAddr>,
152 #[clap(
154 long,
155 value_parser,
156 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
157 display_order = 10
158 )]
159 pub port: Option<u16>,
160 #[clap(
162 long,
163 value_parser,
164 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
165 display_order = 11
166 )]
167 pub cert: Option<PathBuf>,
168 #[clap(
170 long,
171 value_parser,
172 conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
173 display_order = 12
174 )]
175 pub key: Option<PathBuf>,
176}
177
178impl WebCli {
179 pub fn get_start(&self) -> bool {
180 self.start
181 || !(self.stop
182 || self.status
183 || self.create_token
184 || self.revoke_token.is_some()
185 || self.revoke_all_tokens
186 || self.list_tokens)
187 }
188}
189
190#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
191pub enum SessionCommand {
192 #[clap(name = "options")]
194 Options(CliOptions),
195}
196
197#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
198pub enum Sessions {
199 #[clap(visible_alias = "ls")]
201 ListSessions {
202 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
204 no_formatting: bool,
205
206 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
208 short: bool,
209
210 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
212 reverse: bool,
213 },
214 #[clap(visible_alias = "la")]
216 ListAliases,
217 #[clap(visible_alias = "a")]
219 Attach {
220 #[clap(value_parser)]
222 session_name: Option<String>,
223
224 #[clap(short, long, value_parser)]
226 create: bool,
227
228 #[clap(short('b'), long, value_parser)]
230 create_background: bool,
231
232 #[clap(long, value_parser)]
234 index: Option<usize>,
235
236 #[clap(subcommand, name = "options")]
238 options: Option<Box<SessionCommand>>,
239
240 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
242 force_run_commands: bool,
243 },
244
245 #[clap(visible_alias = "k")]
247 KillSession {
248 #[clap(value_parser)]
250 target_session: Option<String>,
251 },
252
253 #[clap(visible_alias = "d")]
255 DeleteSession {
256 #[clap(value_parser)]
258 target_session: Option<String>,
259 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
261 force: bool,
262 },
263
264 #[clap(visible_alias = "ka")]
266 KillAllSessions {
267 #[clap(short, long, value_parser)]
269 yes: bool,
270 },
271
272 #[clap(visible_alias = "da")]
274 DeleteAllSessions {
275 #[clap(short, long, value_parser)]
277 yes: bool,
278 #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
280 force: bool,
281 },
282
283 #[clap(visible_alias = "ac")]
285 #[clap(subcommand)]
286 Action(CliAction),
287 #[clap(visible_alias = "r")]
289 Run {
290 #[clap(last(true), required(true))]
292 command: Vec<String>,
293
294 #[clap(short, long, value_parser, conflicts_with("floating"))]
296 direction: Option<Direction>,
297
298 #[clap(long, value_parser)]
300 cwd: Option<PathBuf>,
301
302 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
304 floating: bool,
305
306 #[clap(
308 short,
309 long,
310 value_parser,
311 default_value("false"),
312 takes_value(false),
313 conflicts_with("floating"),
314 conflicts_with("direction")
315 )]
316 in_place: bool,
317
318 #[clap(short, long, value_parser)]
320 name: Option<String>,
321
322 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
324 close_on_exit: bool,
325
326 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
328 start_suspended: bool,
329
330 #[clap(short, long, requires("floating"))]
332 x: Option<String>,
333 #[clap(short, long, requires("floating"))]
335 y: Option<String>,
336 #[clap(long, requires("floating"))]
338 width: Option<String>,
339 #[clap(long, requires("floating"))]
341 height: Option<String>,
342 #[clap(long, requires("floating"))]
344 pinned: Option<bool>,
345 #[clap(
346 long,
347 conflicts_with("floating"),
348 conflicts_with("direction"),
349 value_parser,
350 default_value("false"),
351 takes_value(false)
352 )]
353 stacked: bool,
354 },
355 #[clap(visible_alias = "p")]
357 Plugin {
358 #[clap(last(true), required(true))]
360 url: String,
361
362 #[clap(short, long, value_parser)]
364 configuration: Option<PluginUserConfiguration>,
365
366 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
368 floating: bool,
369
370 #[clap(
372 short,
373 long,
374 value_parser,
375 default_value("false"),
376 takes_value(false),
377 conflicts_with("floating")
378 )]
379 in_place: bool,
380
381 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
383 skip_plugin_cache: bool,
384 #[clap(short, long, requires("floating"))]
386 x: Option<String>,
387 #[clap(short, long, requires("floating"))]
389 y: Option<String>,
390 #[clap(long, requires("floating"))]
392 width: Option<String>,
393 #[clap(long, requires("floating"))]
395 height: Option<String>,
396 #[clap(long, requires("floating"))]
398 pinned: Option<bool>,
399 },
400 #[clap(visible_alias = "e")]
402 Edit {
403 file: PathBuf,
404
405 #[clap(short, long, value_parser)]
407 line_number: Option<usize>,
408
409 #[clap(short, long, value_parser, conflicts_with("floating"))]
411 direction: Option<Direction>,
412
413 #[clap(
415 short,
416 long,
417 value_parser,
418 default_value("false"),
419 takes_value(false),
420 conflicts_with("floating"),
421 conflicts_with("direction")
422 )]
423 in_place: bool,
424
425 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
427 floating: bool,
428
429 #[clap(long, value_parser)]
431 cwd: Option<PathBuf>,
432 #[clap(short, long, requires("floating"))]
434 x: Option<String>,
435 #[clap(short, long, requires("floating"))]
437 y: Option<String>,
438 #[clap(long, requires("floating"))]
440 width: Option<String>,
441 #[clap(long, requires("floating"))]
443 height: Option<String>,
444 #[clap(long, requires("floating"))]
446 pinned: Option<bool>,
447 },
448 ConvertConfig {
449 old_config_file: PathBuf,
450 },
451 ConvertLayout {
452 old_layout_file: PathBuf,
453 },
454 ConvertTheme {
455 old_theme_file: PathBuf,
456 },
457 #[clap(override_usage(
459r#"
460zellij pipe [OPTIONS] [--] <PAYLOAD>
461
462* Send data to a specific plugin:
463
464zellij pipe --plugin file:/path/to/my/plugin.wasm --name my_pipe_name -- my_arbitrary_data
465
466* To all running plugins (that are listening):
467
468zellij pipe --name my_pipe_name -- my_arbitrary_data
469
470* Pipe data into this command's STDIN and get output from the plugin on this command's STDOUT
471
472tail -f /tmp/my-live-logfile | zellij pipe --name logs --plugin https://example.com/my-plugin.wasm | wc -l
473"#))]
474 Pipe {
475 #[clap(short, long, value_parser, display_order(1))]
477 name: Option<String>,
478 payload: Option<String>,
480
481 #[clap(short, long, value_parser, display_order(2))]
482 args: Option<PluginUserConfiguration>, #[clap(short, long, value_parser, display_order(3))]
488 plugin: Option<String>,
489 #[clap(short('c'), long, value_parser, display_order(4))]
492 plugin_configuration: Option<PluginUserConfiguration>,
493 },
494}
495
496#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
497pub enum CliAction {
498 Write {
500 bytes: Vec<u8>,
501 },
502 WriteChars {
504 chars: String,
505 },
506 Resize {
508 resize: Resize,
509 direction: Option<Direction>,
510 },
511 FocusNextPane,
513 FocusPreviousPane,
515 MoveFocus {
517 direction: Direction,
518 },
519 MoveFocusOrTab {
522 direction: Direction,
523 },
524 MovePane {
527 direction: Option<Direction>,
528 },
529 MovePaneBackwards,
531 Clear,
533 DumpScreen {
535 path: PathBuf,
536
537 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
539 full: bool,
540 },
541 DumpLayout,
543 EditScrollback,
545 ScrollUp,
547 ScrollDown,
549 ScrollToBottom,
551 ScrollToTop,
553 PageScrollUp,
555 PageScrollDown,
557 HalfPageScrollUp,
559 HalfPageScrollDown,
561 ToggleFullscreen,
563 TogglePaneFrames,
565 ToggleActiveSyncTab,
567 NewPane {
570 #[clap(short, long, value_parser, conflicts_with("floating"))]
572 direction: Option<Direction>,
573
574 #[clap(last(true))]
575 command: Vec<String>,
576
577 #[clap(short, long, conflicts_with("command"), conflicts_with("direction"))]
578 plugin: Option<String>,
579
580 #[clap(long, value_parser)]
582 cwd: Option<PathBuf>,
583
584 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
586 floating: bool,
587
588 #[clap(
590 short,
591 long,
592 value_parser,
593 default_value("false"),
594 takes_value(false),
595 conflicts_with("floating"),
596 conflicts_with("direction")
597 )]
598 in_place: bool,
599
600 #[clap(short, long, value_parser)]
602 name: Option<String>,
603
604 #[clap(
606 short,
607 long,
608 value_parser,
609 default_value("false"),
610 takes_value(false),
611 requires("command")
612 )]
613 close_on_exit: bool,
614 #[clap(
616 short,
617 long,
618 value_parser,
619 default_value("false"),
620 takes_value(false),
621 requires("command")
622 )]
623 start_suspended: bool,
624 #[clap(long, value_parser)]
625 configuration: Option<PluginUserConfiguration>,
626 #[clap(long, value_parser)]
627 skip_plugin_cache: bool,
628 #[clap(short, long, requires("floating"))]
630 x: Option<String>,
631 #[clap(short, long, requires("floating"))]
633 y: Option<String>,
634 #[clap(long, requires("floating"))]
636 width: Option<String>,
637 #[clap(long, requires("floating"))]
639 height: Option<String>,
640 #[clap(long, requires("floating"))]
642 pinned: Option<bool>,
643 #[clap(
644 long,
645 conflicts_with("floating"),
646 conflicts_with("direction"),
647 value_parser,
648 default_value("false"),
649 takes_value(false)
650 )]
651 stacked: bool,
652 },
653 Edit {
655 file: PathBuf,
656
657 #[clap(short, long, value_parser, conflicts_with("floating"))]
659 direction: Option<Direction>,
660
661 #[clap(short, long, value_parser)]
663 line_number: Option<usize>,
664
665 #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
667 floating: bool,
668
669 #[clap(
671 short,
672 long,
673 value_parser,
674 default_value("false"),
675 takes_value(false),
676 conflicts_with("floating"),
677 conflicts_with("direction")
678 )]
679 in_place: bool,
680
681 #[clap(long, value_parser)]
683 cwd: Option<PathBuf>,
684 #[clap(short, long, requires("floating"))]
686 x: Option<String>,
687 #[clap(short, long, requires("floating"))]
689 y: Option<String>,
690 #[clap(long, requires("floating"))]
692 width: Option<String>,
693 #[clap(long, requires("floating"))]
695 height: Option<String>,
696 #[clap(long, requires("floating"))]
698 pinned: Option<bool>,
699 },
700 SwitchMode {
702 input_mode: InputMode,
703 },
704 TogglePaneEmbedOrFloating,
706 ToggleFloatingPanes,
708 ClosePane,
710 RenamePane {
712 name: String,
713 },
714 UndoRenamePane,
716 GoToNextTab,
718 GoToPreviousTab,
720 CloseTab,
722 GoToTab {
724 index: u32,
725 },
726 GoToTabName {
728 name: String,
729 #[clap(short, long, value_parser)]
731 create: bool,
732 },
733 RenameTab {
735 name: String,
736 },
737 UndoRenameTab,
739 NewTab {
741 #[clap(short, long, value_parser)]
743 layout: Option<PathBuf>,
744
745 #[clap(long, value_parser, requires("layout"))]
747 layout_dir: Option<PathBuf>,
748
749 #[clap(short, long, value_parser)]
751 name: Option<String>,
752
753 #[clap(short, long, value_parser)]
755 cwd: Option<PathBuf>,
756 },
757 MoveTab {
759 direction: Direction,
760 },
761 PreviousSwapLayout,
762 NextSwapLayout,
763 QueryTabNames,
765 StartOrReloadPlugin {
766 url: String,
767 #[clap(short, long, value_parser)]
768 configuration: Option<PluginUserConfiguration>,
769 },
770 LaunchOrFocusPlugin {
771 #[clap(short, long, value_parser)]
772 floating: bool,
773 #[clap(short, long, value_parser)]
774 in_place: bool,
775 #[clap(short, long, value_parser)]
776 move_to_focused_tab: bool,
777 url: String,
778 #[clap(short, long, value_parser)]
779 configuration: Option<PluginUserConfiguration>,
780 #[clap(short, long, value_parser)]
781 skip_plugin_cache: bool,
782 },
783 LaunchPlugin {
784 #[clap(short, long, value_parser)]
785 floating: bool,
786 #[clap(short, long, value_parser)]
787 in_place: bool,
788 url: Url,
789 #[clap(short, long, value_parser)]
790 configuration: Option<PluginUserConfiguration>,
791 #[clap(short, long, value_parser)]
792 skip_plugin_cache: bool,
793 },
794 RenameSession {
795 name: String,
796 },
797 #[clap(override_usage(
799r#"
800zellij action pipe [OPTIONS] [--] <PAYLOAD>
801
802* Send data to a specific plugin:
803
804zellij action pipe --plugin file:/path/to/my/plugin.wasm --name my_pipe_name -- my_arbitrary_data
805
806* To all running plugins (that are listening):
807
808zellij action pipe --name my_pipe_name -- my_arbitrary_data
809
810* Pipe data into this command's STDIN and get output from the plugin on this command's STDOUT
811
812tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://example.com/my-plugin.wasm | wc -l
813"#))]
814 Pipe {
815 #[clap(short, long, value_parser, display_order(1))]
817 name: Option<String>,
818 payload: Option<String>,
820
821 #[clap(short, long, value_parser, display_order(2))]
822 args: Option<PluginUserConfiguration>, #[clap(short, long, value_parser, display_order(3))]
828 plugin: Option<String>,
829 #[clap(short('c'), long, value_parser, display_order(4))]
832 plugin_configuration: Option<PluginUserConfiguration>,
833 #[clap(
835 short('l'),
836 long,
837 value_parser,
838 takes_value(false),
839 default_value("false"),
840 display_order(5)
841 )]
842 force_launch_plugin: bool,
843 #[clap(
845 short('s'),
846 long,
847 value_parser,
848 takes_value(false),
849 default_value("false"),
850 display_order(6)
851 )]
852 skip_plugin_cache: bool,
853 #[clap(short('f'), long, value_parser, display_order(7))]
855 floating_plugin: Option<bool>,
856 #[clap(
858 short('i'),
859 long,
860 value_parser,
861 conflicts_with("floating-plugin"),
862 display_order(8)
863 )]
864 in_place_plugin: Option<bool>,
865 #[clap(short('w'), long, value_parser, display_order(9))]
867 plugin_cwd: Option<PathBuf>,
868 #[clap(short('t'), long, value_parser, display_order(10))]
870 plugin_title: Option<String>,
871 },
872 ListClients,
873 TogglePanePinned,
874 StackPanes {
882 #[clap(last(true), required(true))]
883 pane_ids: Vec<String>,
884 },
885 ChangeFloatingPaneCoordinates {
886 #[clap(short, long, value_parser)]
889 pane_id: String,
890 #[clap(short, long)]
892 x: Option<String>,
893 #[clap(short, long)]
895 y: Option<String>,
896 #[clap(long)]
898 width: Option<String>,
899 #[clap(long)]
901 height: Option<String>,
902 #[clap(long)]
904 pinned: Option<bool>,
905 },
906}