summaryrefslogtreecommitdiffstats
path: root/below/view/src/help_menu.rs
blob: c8a4453fa340ba494abf765de2f3be2fcc36ecd0 (plain)
1
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use cursive::event::Event;
use cursive::view::Nameable;
use cursive::view::Scrollable;
use cursive::view::View;
use cursive::views::LinearLayout;
use cursive::views::Panel;
use cursive::views::SelectView;
use cursive::views::TextView;

use crate::controllers::event_to_string;
use crate::controllers::Controllers;
use crate::tab_view::TabView;

pub struct ControllerHelper {
    events: Vec<Event>,
    description: &'static str,
    cmd: &'static str,
    cmd_short: &'static str,
    args: &'static str,
}

impl std::fmt::Display for ControllerHelper {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{:<18} {:<11} {:<24} {:<10} {}",
            self.cmd,
            if self.cmd_short.is_empty() {
                "-"
            } else {
                self.cmd_short
            },
            &gen_hotkey_string(&self.events),
            self.args,
            self.description
        )
    }
}

fn gen_hotkey_string(events: &[Event]) -> String {
    return events
        .iter()
        .map(event_to_string)
        .collect::<Vec<String>>()
        .join(",");
}

fn get_description(controller: &Controllers) -> &'static str {
    match controller {
        Controllers::CmdPalette => "Invoking command palette.",
        Controllers::NextTab => "Cycle topic tabs.",
        Controllers::PrevTab => "Reverse cycle topic tabs.",
        Controllers::NextCol => "Cycle columns.",
        Controllers::PrevCol => "Reverse cycle columns.",
        Controllers::Right => "Scroll right primary display.",
        Controllers::Left => "Scroll left primary display.",
        Controllers::SortCol => {
            "Sort by the selected title, reverse the result by hitting 'S' again(cgroup view and process view only)."
        }
        Controllers::Filter => "Filter by selected column.",
        Controllers::CFilter => "Clear the current filter.",
        Controllers::JForward => {
            "Jump time by a specific amount forward or to a specific timestamp (replay and live-paused mode)."
        }
        Controllers::JBackward => {
            "Jump time by a specific amount backward or to a specific timestamp (replay and live-paused mode)."
        }
        Controllers::NSample => "Show next sample (replay and live-paused mode).",
        Controllers::PSample => "Show previous sample (replay and live-paused mode).",
        Controllers::Pause => {
            "pause/resume the live mode. While pausing, use the above commands to go forwards or backwards in time"
        }
        Controllers::Quit => "Quit.",
        Controllers::Help => "Toggle help menu.",
        Controllers::Process => "Show process view.",
        Controllers::Cgroup => "Show cgroup view.",
        Controllers::System => "Show system core view.",
        Controllers::Gpu => "Show GPU view.",
        Controllers::GpuZoom => "Zoom into process view filtered by selected GPU.",
        Controllers::GpuProcess => "Zoom into process view for all GPU processes.",
        Controllers::Zoom => {
            "If in cgroup view, zoom into process view filtered by cgroup. If in process view, zoom into cgroup view, selected on cgroup of process."
        }
        Controllers::Fold => "Fold processes (post filter) and display aggregated values.",
        Controllers::NextPage => "Scroll down 15 lines primary display.",
        Controllers::PrevPage => "Scroll up 15 lines primary display.",
        _ => "Unknown",
    }
}

fn get_args(controller: &Controllers) -> &'static str {
    match controller {
        Controllers::SortCol => "SortKey",
        Controllers::Filter => "Substring",
        Controllers::JForward => "Time",
        Controllers::JBackward => "Time",
        _ => "-",
    }
}

fn get_title() -> Vec<String> {
    vec![
        format!("{:<18}", "Command"),
        format!("{:<11}", "Short Cmd"),
        format!("{:<24}", "Hot Key"),
        format!("{:<10}", "Args"),
        "Description".into(),
    ]
}

// Grab the user customized keymaps and generate helper message
fn fill_controllers(
    v: &mut SelectView<String>,
    event_controllers: Rc<RefCell<HashMap<Event, Controllers>>>,
) {
    // event_controllers can generate helper messages in completely random order base on
    // user's customization. Instead of using it directly, we will generate a cmd-msg map
    // to ensure the order.
    //
    let mut cmd_map: HashMap<Controllers, ControllerHelper> = HashMap::new();
    for (event, controller) in event_controllers.borrow().iter() {
        match cmd_map.get_mut(controller) {
            Some(ref mut item) => item.events.push(event.clone()),
            None => drop(cmd_map.insert(
                controller.clone(),
                ControllerHelper {
                    events: vec![event.clone()],
                    cmd: controller.command(),
                    cmd_short: controller.cmd_shortcut(),
                    description: get_description(controller),
                    args: get_args(controller),
                },
            )),
        }
    }

    // Unwrap in this vec! must be success, otherwise we may have lost
    // controller(s) and should be detected by unit test.
    let mut controllers = vec![
        cmd_map.get(&Controllers::Help).unwrap().to_string(),
        cmd_map.get(&Controllers::CmdPalette).unwrap().to_string(),
        cmd_map.get(&Controllers::Quit).unwrap().to_string(),
        cmd_map.get(&Controllers::Left).unwrap().to_string(),
        cmd_map.get(&Controllers::Right).unwrap().to_string(),
        cmd_map.get(&Controllers::NextTab).unwrap().to_string(),
        cmd_map.get(&Controllers::PrevTab).unwrap().to_string(),
        cmd_map.get(&Controllers::NextCol).unwrap().to_string(),
        cmd_map.get(&Controllers::PrevCol).unwrap().to_string(),
        cmd_map.get(&Controllers::JForward).unwrap().to_string(),
        cmd_map.get(&Controllers::JBackward).unwrap().to_string(),
        cmd_map.get(&Controllers::NSample).unwrap().to_string(),
        cmd_map.get(&Controllers::PSample).unwrap().to_string(),
        cmd_map.get(&Controllers::Pause).unwrap().to_string(),
        cmd_map.get(&Controllers::SortCol).unwrap().to_string(),
        cmd_map.get(&Controllers::Filter).unwrap().to_string(),
        cmd_map.get(&Controllers::CFilter).unwrap().to_string(),
        cmd_map.get(&Controllers::Zoom).unwrap().to_string(),
        cmd_map.get(&Controllers::Fold).unwrap().to_string(),
        cmd_map.get(&Controllers::Process).unwrap().to_string(),
        cmd_map.get(&Controllers::Cgroup).unwrap().to_string(),
        cmd_map.get(&Controllers::System).unwrap().to_string(),
        cmd_map.get(&Controllers::NextPage).unwrap().to_string(),
        cmd_map.get(&Controllers::PrevPage).unwrap().to_string(),
    ];

    controllers.extend(crate::get_extra_controller_str(&cmd_map));

    v.add_all_str(controllers);
}

fn fill_reserved(v: &mut LinearLayout) {
    let lines = vec![
        " <DOWN>         - scroll down primary display, next command if command palette activated\n",
        " <UP>           - scroll up primary display, last command if command palette activated\n",
        " <PgDn>         - scroll down 15 lines primary display\n",
        " <PgUp>         - scroll up 15 lines primary display\n",
        " <Home>         - scroll to top of primary display\n",
        " <End>          - scroll to end of primary display\n",
        " <Enter>        - collapse/expand cgroup tree, submit command if command palette activated\n",
        " <Ctrl>-r       - refresh the screen",
        " 'P'            - sort by pid (process view only)\n",
        " 'N'            - sort by name (process view only)\n",
        " 'C'            - sort by cpu (cgroup view and process view only)\n",
        " 'M'            - sort by memory (cgroup view and process view only)\n",
        " 'D'            - sort by total disk activity(cgroup view and process view only)\n",
    ];

    for line in lines {
        v.add_child(TextView::new(line));
    }
}

pub fn new(event_controllers: Rc<RefCell<HashMap<Event, Controllers>>>) -> impl View {
    let mut reserved = LinearLayout::vertical();
    fill_reserved(&mut reserved);
    let mut controllers = SelectView::<String>::new();
    fill_controllers(&mut controllers, event_controllers);
    LinearLayout::vertical()
        .child(Panel::new(reserved))
        .child(Panel::new(
            LinearLayout::vertical()
                .child(
                    TabView::new(get_title(), " ", 0 /* pinned titles */)
                        .expect("Failed to construct title tab in help menu"),
                )
                .child(controllers)
                .scrollable()
                .scroll_x(true),
        ))
        .with_name("help_menu")
}