tracexec_tui/
help.rs

1use std::borrow::Cow;
2
3use ratatui::{
4  layout::Rect,
5  style::{
6    Styled,
7    Stylize,
8  },
9  text::{
10    Line,
11    Span,
12    Text,
13  },
14  widgets::{
15    Paragraph,
16    Wrap,
17  },
18};
19use tracexec_core::event::EventStatus;
20
21use super::{
22  sized_paragraph::SizedParagraph,
23  theme::THEME,
24};
25
26pub fn cli_flag<'a, T>(f: T) -> Span<'a>
27where
28  T: Into<Cow<'a, str>> + Styled<Item = Span<'a>>,
29{
30  f.set_style(THEME.cli_flag)
31}
32
33pub fn help_key<'a, T>(k: T) -> Span<'a>
34where
35  T: Into<Cow<'a, str>> + Styled<Item = Span<'a>>,
36{
37  let mut key_string = String::from("\u{00a0}");
38  key_string.push_str(&k.into());
39  key_string.push('\u{00a0}');
40  key_string.set_style(THEME.help_key)
41}
42pub fn help_desc<'a, T>(d: T) -> Span<'a>
43where
44  T: Into<Cow<'a, str>> + Styled<Item = Span<'a>>,
45{
46  let mut desc_string = String::from("\u{00a0}");
47  desc_string.push_str(&d.into());
48  desc_string.push('\u{00a0}');
49  desc_string.set_style(THEME.help_desc)
50}
51
52pub fn fancy_help_desc<'a, T>(d: T) -> Span<'a>
53where
54  T: Into<Cow<'a, str>> + Styled<Item = Span<'a>>,
55{
56  let mut desc_string = String::from("\u{00a0}");
57  desc_string.push_str(&d.into());
58  desc_string.push('\u{00a0}');
59  desc_string.set_style(THEME.fancy_help_desc)
60}
61
62macro_rules! help_item {
63  ($key: expr, $desc: expr) => {{
64    [
65      crate::help::help_key($key),
66      crate::help::help_desc($desc),
67      "\u{200b}".into(),
68    ]
69  }};
70}
71
72pub(crate) use help_item;
73
74pub fn help<'a>(area: Rect) -> SizedParagraph<'a> {
75  let line1 = Line::default().spans(vec![
76      "W".bold().black(),
77      "elcome to tracexec! The TUI consists of at most two panes: the event list and optionally the pseudo terminal if ".into(),
78      cli_flag("--tty/-t"),
79      " is enabled. The event list displays the events emitted by the tracer. \
80       The active pane's border is highlighted in cyan. \
81       To switch active pane, press ".into(),
82      help_key("Ctrl+S"),
83      ". To send ".into(),
84      help_key("Ctrl+S"),
85      " to the pseudo terminal, press ".into(),
86      help_key("Alt+S"),
87      " when event list is active. The keybinding list at the bottom of the screen shows the available keys for currently active pane or popup.".into(),
88    ]);
89  let line2 = Line::default().spans(vec![
90    "Y".bold().black(),
91    "ou can navigate the event list using the arrow keys or ".into(),
92    help_key("H/J/K/L"),
93    ". To scroll faster, use ".into(),
94    help_key("Ctrl+↑/↓/←/→/H/J/K/L"),
95    " or ".into(),
96    help_key("PgUp/PgDn"),
97    ". Use ".into(),
98    help_key("(Shift +) Home/End"),
99    " to scroll to the (line start/line end)/top/bottom. Press ".into(),
100    help_key("F"),
101    " to toggle follow mode, which will keep the list scrolled to bottom. ".into(),
102    "To change pane size, press ".into(),
103    help_key("G/S"),
104    " when the active pane is event list. ".into(),
105    "To switch between horizontal and vertical layout, press ".into(),
106    help_key("Alt+L"),
107    ". To view the details of the selected event, press ".into(),
108    help_key("V"),
109    ". To copy the selected event to the clipboard, press ".into(),
110    help_key("C"),
111    " then select what to copy. To jump to the parent exec event of the currently selected event, press ".into(),
112    help_key("U"),
113    ". To show the backtrace of the currently selected event, press ".into(),
114    help_key("T"),
115    ". To quit, press ".into(),
116    help_key("Q"),
117    " while the event list is active.".into(),
118  ]);
119  let line3 = Line::default().spans(vec![
120    "W".bold(),
121    "hen the pseudo terminal is active, you can interact with the terminal using the keyboard."
122      .into(),
123  ]);
124  let line4 =
125    Line::default().spans(vec![
126    "E".bold().black(),
127    "ach exec event in the event list consists of four parts, the pid, the status of the process,\
128    the comm of the process (before exec), and the commandline to reproduce the exec event. \
129    The pid is colored according to the result of the execve{,at} syscall.
130    The status can be one of the following: "
131      .into(),
132    help_key(<&str>::from(EventStatus::ExecENOENT)),
133    help_desc("Exec failed (ENOENT)"),
134    ", ".into(),
135    help_key(<&str>::from(EventStatus::ExecFailure)),
136    help_desc("Exec failed"),
137    ", ".into(),
138    help_key(<&str>::from(EventStatus::ProcessRunning)),
139    help_desc("Running"),
140    ", ".into(),
141    help_key(<&str>::from(EventStatus::ProcessPaused)),
142    help_desc("Paused"),
143    ", ".into(),
144    help_key(<&str>::from(EventStatus::ProcessDetached)),
145    help_desc("Detached"),
146    ", ".into(),
147    help_key(<&str>::from(EventStatus::ProcessExitedNormally)),
148    help_desc("Exited normally"),
149    ", ".into(),
150    help_key(<&str>::from(EventStatus::ProcessExitedAbnormally(1))),
151    help_desc("Exited abnormally"),
152    ", ".into(),
153    help_key(<&str>::from(EventStatus::ProcessTerminated)),
154    help_desc("Terminated"),
155    ", ".into(),
156    help_key(<&str>::from(EventStatus::ProcessKilled)),
157    help_desc("Killed"),
158    ", ".into(),
159    help_key(<&str>::from(EventStatus::ProcessSegfault)),
160    help_desc("Segfault"),
161    ", ".into(),
162    help_key(<&str>::from(EventStatus::ProcessInterrupted)),
163    help_desc("Interrupted"),
164    ", ".into(),
165    help_key(<&str>::from(EventStatus::ProcessIllegalInstruction)),
166    help_desc("Illegal instruction"),
167    ", ".into(),
168    help_key(<&str>::from(EventStatus::ProcessAborted)),
169    help_desc("Aborted"),
170    ", ".into(),
171    help_key(<&str>::from(EventStatus::ProcessSignaled(nix::sys::signal::Signal::SIGURG.into()))),
172    help_desc("Signaled"),
173    ", ".into(),
174    help_key(<&str>::from(EventStatus::InternalError)),
175    help_desc("An internal error occurred"),
176  ]);
177
178  let line5 = Line::default()
179    .spans(vec![
180      "P".bold().black(),
181      "ress ".into(),
182      help_key("Any Key"),
183      " to close this help popup.".into(),
184    ])
185    .centered();
186  let paragraph =
187    Paragraph::new(Text::from_iter([line1, line2, line3, line4, line5])).wrap(Wrap { trim: false });
188  let perhaps_a_suitable_width = area.width.saturating_sub(6) as usize;
189  SizedParagraph::new(paragraph, perhaps_a_suitable_width)
190}