• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MitMaro / git-interactive-rebase-tool / 6883077488

15 Nov 2023 09:23PM CUT coverage: 93.248% (-0.4%) from 93.64%
6883077488

Pull #873

github

web-flow
Merge 0ab516642 into d7655157f
Pull Request #873: When editing in the middle of a rebase, dont clear on quit

45 of 72 new or added lines in 14 files covered. (62.5%)

1 existing line in 1 file now uncovered.

4792 of 5139 relevant lines covered (93.25%)

3.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.08
/src/core/src/modules/list/utils.rs
1
use std::cmp;
2

3
use bitflags::bitflags;
4
use config::KeyBindings;
5
use display::DisplayColor;
6
use todo_file::{Action, Line, TodoFile};
7
use view::LineSegment;
8

9
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
10
enum HelpLinesSelector {
11
        Normal,
12
        Visual,
13
        Common,
14
}
15

16
fn build_help_lines(key_bindings: &KeyBindings, selector: HelpLinesSelector) -> Vec<(Vec<String>, String)> {
1✔
17
        let lines = vec![
2✔
18
                (&key_bindings.move_up, "Move selection up", HelpLinesSelector::Common),
1✔
19
                (
20
                        &key_bindings.move_down,
1✔
21
                        "Move selection down",
22
                        HelpLinesSelector::Common,
1✔
23
                ),
24
                (
25
                        &key_bindings.move_up_step,
1✔
26
                        "Move selection up half a page",
27
                        HelpLinesSelector::Common,
1✔
28
                ),
29
                (
30
                        &key_bindings.move_down_step,
1✔
31
                        "Move selection down half a page",
32
                        HelpLinesSelector::Common,
1✔
33
                ),
34
                (
35
                        &key_bindings.move_home,
1✔
36
                        "Move selection to top of the list",
37
                        HelpLinesSelector::Common,
1✔
38
                ),
39
                (
40
                        &key_bindings.move_end,
1✔
41
                        "Move selection to end of the list",
42
                        HelpLinesSelector::Common,
1✔
43
                ),
44
                (
45
                        &key_bindings.move_left,
1✔
46
                        "Scroll content to the left",
47
                        HelpLinesSelector::Common,
1✔
48
                ),
49
                (
50
                        &key_bindings.move_right,
1✔
51
                        "Scroll content to the right",
52
                        HelpLinesSelector::Common,
1✔
53
                ),
54
                (
55
                        &key_bindings.abort,
56
                        "Abort interactive rebase",
57
                        HelpLinesSelector::Common,
1✔
58
                ),
59
                (
60
                        &key_bindings.force_abort,
1✔
61
                        "Immediately abort interactive rebase",
62
                        HelpLinesSelector::Common,
1✔
63
                ),
64
                (
65
                        &key_bindings.rebase,
1✔
66
                        "Write interactive rebase file",
67
                        HelpLinesSelector::Common,
1✔
68
                ),
69
                (
70
                        &key_bindings.force_rebase,
1✔
71
                        "Immediately write interactive rebase file",
72
                        HelpLinesSelector::Common,
1✔
73
                ),
74
                (&key_bindings.help, "Show help", HelpLinesSelector::Common),
1✔
75
                (
76
                        &key_bindings.move_selection_down,
1✔
77
                        "Move selected lines down",
78
                        HelpLinesSelector::Common,
1✔
79
                ),
80
                (
81
                        &key_bindings.move_selection_up,
1✔
82
                        "Move selected lines up",
83
                        HelpLinesSelector::Common,
1✔
84
                ),
85
                (
86
                        &key_bindings.show_commit,
1✔
87
                        "Show commit information",
88
                        HelpLinesSelector::Normal,
1✔
89
                ),
90
                (
91
                        &key_bindings.action_break,
1✔
92
                        "Toggle break action",
93
                        HelpLinesSelector::Normal,
1✔
94
                ),
95
                (
96
                        &key_bindings.action_pick,
1✔
97
                        "Set selected commits to be picked",
98
                        HelpLinesSelector::Common,
1✔
99
                ),
100
                (
101
                        &key_bindings.action_reword,
1✔
102
                        "Set selected commits to be reworded",
103
                        HelpLinesSelector::Common,
1✔
104
                ),
105
                (
106
                        &key_bindings.action_edit,
1✔
107
                        "Set selected commits to be edited",
108
                        HelpLinesSelector::Common,
1✔
109
                ),
110
                (
111
                        &key_bindings.action_squash,
1✔
112
                        "Set selected commits to be squashed",
113
                        HelpLinesSelector::Common,
1✔
114
                ),
115
                (
116
                        &key_bindings.action_fixup,
1✔
117
                        "Set selected commits to be fixed-up",
118
                        HelpLinesSelector::Common,
1✔
119
                ),
120
                (
121
                        &key_bindings.action_drop,
1✔
122
                        "Set selected commits to be dropped",
123
                        HelpLinesSelector::Common,
1✔
124
                ),
125
                (
126
                        &key_bindings.action_cut,
1✔
127
                        "Set selected commits to be cut / split (git-revise)",
128
                        HelpLinesSelector::Common,
1✔
129
                ),
130
                (
131
                        &key_bindings.action_index,
1✔
132
                        "Set selected commits to be staged in the index (git-revise)",
133
                        HelpLinesSelector::Common,
1✔
134
                ),
135
                (
136
                        &key_bindings.edit,
1✔
137
                        "Edit an exec, label, reset or merge action's content",
138
                        HelpLinesSelector::Normal,
1✔
139
                ),
140
                (
141
                        &key_bindings.insert_line,
1✔
142
                        "Insert a new line",
143
                        HelpLinesSelector::Normal,
1✔
144
                ),
145
                (
146
                        &key_bindings.remove_line,
1✔
147
                        "Completely remove the selected lines",
148
                        HelpLinesSelector::Common,
1✔
149
                ),
150
                (&key_bindings.undo, "Undo the last change", HelpLinesSelector::Common),
1✔
151
                (
152
                        &key_bindings.redo,
1✔
153
                        "Redo the previous undone change",
154
                        HelpLinesSelector::Common,
1✔
155
                ),
156
                (
157
                        &key_bindings.open_in_external_editor,
1✔
158
                        "Open the todo file in the default editor",
159
                        HelpLinesSelector::Common,
1✔
160
                ),
161
                (
162
                        &key_bindings.toggle_visual_mode,
1✔
163
                        "Enter visual selection mode",
164
                        HelpLinesSelector::Normal,
1✔
165
                ),
166
                (
167
                        &key_bindings.toggle_visual_mode,
1✔
168
                        "Exit visual selection mode",
169
                        HelpLinesSelector::Visual,
1✔
170
                ),
171
        ];
172

173
        lines
3✔
174
                .iter()
175
                .filter_map(|&(binding, help, line_selector)| {
3✔
176
                        let selected = line_selector == selector || line_selector == HelpLinesSelector::Common;
1✔
177
                        selected.then(|| ((*binding).clone(), String::from(help)))
3✔
178
                })
179
                .collect()
180
}
181

182
pub(super) fn get_list_normal_mode_help_lines(key_bindings: &KeyBindings) -> Vec<(Vec<String>, String)> {
1✔
183
        build_help_lines(key_bindings, HelpLinesSelector::Normal)
1✔
184
}
185

186
pub(super) fn get_list_visual_mode_help_lines(key_bindings: &KeyBindings) -> Vec<(Vec<String>, String)> {
1✔
187
        build_help_lines(key_bindings, HelpLinesSelector::Visual)
1✔
188
}
189

190
const fn get_action_color(action: Action) -> DisplayColor {
1✔
191
        match action {
1✔
192
                Action::Break => DisplayColor::ActionBreak,
1✔
NEW
193
                Action::Cut => DisplayColor::ActionCut,
×
194
                Action::Drop => DisplayColor::ActionDrop,
1✔
195
                Action::Edit => DisplayColor::ActionEdit,
1✔
196
                Action::Exec => DisplayColor::ActionExec,
1✔
197
                Action::Fixup => DisplayColor::ActionFixup,
1✔
NEW
198
                Action::Index => DisplayColor::ActionIndex,
×
199
                Action::Pick => DisplayColor::ActionPick,
1✔
200
                Action::Reword => DisplayColor::ActionReword,
1✔
201
                Action::Squash => DisplayColor::ActionSquash,
1✔
202
                Action::Label => DisplayColor::ActionLabel,
1✔
203
                Action::Reset => DisplayColor::ActionReset,
1✔
204
                Action::Merge => DisplayColor::ActionMerge,
1✔
205
                Action::UpdateRef => DisplayColor::ActionUpdateRef,
1✔
206
                // this is technically impossible, since noops should never be rendered
207
                Action::Noop => DisplayColor::Normal,
1✔
208
        }
209
}
210

211
pub(super) fn get_line_action_maximum_width(todo_file: &TodoFile) -> usize {
1✔
212
        let mut max_width = 0;
1✔
213

214
        for line in todo_file.lines_iter() {
2✔
215
                let action_length = match line.get_action() {
1✔
216
                        // allow these to overflow their bounds
217
                        &Action::Exec | &Action::UpdateRef => 0,
1✔
218
                        &Action::Cut | &Action::Drop | &Action::Edit | &Action::Noop | &Action::Pick => 4,
1✔
219
                        &Action::Break | &Action::Label | &Action::Reset | &Action::Merge | &Action::Index => 5,
1✔
220
                        &Action::Fixup => {
221
                                if line.option().is_some() {
2✔
222
                                        8 // "fixup -C" = 8
1✔
223
                                }
224
                                else {
225
                                        5
1✔
226
                                }
227
                        },
228
                        &Action::Reword | &Action::Squash => 6,
1✔
229
                };
230
                if max_width < action_length {
2✔
231
                        max_width = action_length;
1✔
232
                }
233
        }
234

235
        max_width
1✔
236
}
237

238
bitflags! {
8✔
239
        #[derive(Default, PartialEq, Eq, Debug, Clone, Copy)]
×
240
        pub(crate) struct TodoLineSegmentsOptions: u8 {
241
                const CURSOR_LINE = 0b0000_0001;
242
                const SELECTED = 0b0000_0010;
243
                const FULL_WIDTH = 0b0000_0100;
244
                const SEARCH_LINE = 0b0000_1000;
245
        }
246
}
247

248
// safe slice, as it is only on the hash, which is hexadecimal
249
#[allow(clippy::string_slice)]
250
pub(super) fn get_todo_line_segments(
1✔
251
        line: &Line,
252
        search_term: Option<&str>,
253
        options: TodoLineSegmentsOptions,
254
        maximum_action_width: usize,
255
) -> Vec<LineSegment> {
256
        let mut segments: Vec<LineSegment> = vec![];
1✔
257

258
        let is_cursor_line = options.contains(TodoLineSegmentsOptions::CURSOR_LINE);
2✔
259
        let selected = options.contains(TodoLineSegmentsOptions::SELECTED);
1✔
260
        let is_full_width = options.contains(TodoLineSegmentsOptions::FULL_WIDTH);
1✔
261
        let is_search_index = options.contains(TodoLineSegmentsOptions::SEARCH_LINE);
1✔
262

263
        let action = line.get_action();
1✔
264

265
        let indicator = if is_cursor_line || selected {
1✔
266
                if is_full_width { " > " } else { ">" }
2✔
267
        }
268
        else if is_full_width {
5✔
269
                "   "
2✔
270
        }
271
        else {
272
                " "
1✔
273
        };
274

275
        segments.push(LineSegment::new_with_color_and_style(
1✔
276
                indicator,
1✔
277
                DisplayColor::Normal,
1✔
278
                !is_cursor_line && selected,
4✔
279
                false,
280
                false,
281
        ));
282

283
        let action_padding = cmp::max(maximum_action_width, 6);
1✔
284

285
        let action_name = if is_full_width {
1✔
286
                if let Some(opt) = line.option() {
4✔
287
                        format!("{:action_padding$} ", format!("{action} {opt}"))
3✔
288
                }
289
                else {
290
                        format!("{:action_padding$} ", action.to_string())
3✔
291
                }
292
        }
293
        else {
294
                format!(
3✔
295
                        "{:1}{}",
296
                        action.to_abbreviation(),
1✔
297
                        if line.option().is_some() { "*" } else { " " }
1✔
298
                )
299
        };
300

301
        segments.push(LineSegment::new_with_color(
1✔
302
                action_name.as_str(),
1✔
303
                get_action_color(*action),
1✔
304
        ));
305

306
        // render hash
307
        match *action {
1✔
308
                Action::Cut | Action::Drop | Action::Edit | Action::Fixup | Action::Index | Action::Pick | Action::Reword | Action::Squash => {
309
                        let action_width = if is_full_width { 8 } else { 3 };
1✔
310
                        let max_index = cmp::min(line.get_hash().len(), action_width);
1✔
311
                        let search_match = search_term.map_or(false, |term| line.get_hash().starts_with(term));
3✔
312

313
                        segments.push(LineSegment::new_with_color_and_style(
1✔
314
                                format!(
2✔
315
                                        "{:width$}",
316
                                        line.get_hash()[0..max_index].to_string(), // safe slice, ascii only
1✔
317
                                        width = action_width
318
                                )
319
                                .as_str(),
320
                                if search_match {
2✔
321
                                        DisplayColor::IndicatorColor
1✔
322
                                }
323
                                else {
324
                                        DisplayColor::Normal
1✔
325
                                },
326
                                false,
327
                                search_match && is_search_index,
1✔
328
                                false,
329
                        ));
330
                        segments.push(LineSegment::new(" "));
1✔
331
                },
332
                Action::Exec
333
                | Action::Label
334
                | Action::Reset
335
                | Action::Merge
336
                | Action::Break
337
                | Action::Noop
338
                | Action::UpdateRef => {},
339
        }
340

341
        let content = line.get_content();
2✔
342
        if !content.is_empty() {
1✔
343
                if let Some(term) = search_term {
2✔
344
                        let mut split_iter = content.split(term);
1✔
345
                        segments.push(LineSegment::new(split_iter.next().unwrap()));
1✔
346
                        for split in split_iter {
1✔
347
                                segments.push(LineSegment::new_with_color_and_style(
1✔
348
                                        term,
349
                                        DisplayColor::IndicatorColor,
1✔
350
                                        false,
351
                                        is_search_index,
352
                                        false,
353
                                ));
354
                                if !split.is_empty() {
1✔
355
                                        segments.push(LineSegment::new(split));
1✔
356
                                }
357
                        }
358
                }
359
                else {
360
                        segments.push(LineSegment::new(content));
2✔
361
                }
362
        }
363
        segments
1✔
364
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc