summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2019-07-19 14:40:34 -0230
committerGitHub <noreply@github.com>2019-07-19 14:40:34 -0230
commita38f1b39027c160b597de80395c5f68cc166dde5 (patch)
treeb747370d9daf4d30eab80224f7c12e64a187399f
parentafd268b923b3fde1b749fed0f908f1aade26a1c4 (diff)
parent775603ad2c142ef582de712d1e542953cf9a0cb8 (diff)
Merge pull request #152 from MitMaro/tim/horizontal-scroll
Add horizontal scrolling
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/config.rs4
-rw-r--r--src/git_interactive.rs17
-rw-r--r--src/help/help.rs65
-rw-r--r--src/help/utils.rs12
-rw-r--r--src/input/input_handler.rs4
-rw-r--r--src/list/list.rs95
-rw-r--r--src/scroll/scroll_position.rs55
-rw-r--r--src/show_commit/show_commit.rs149
-rw-r--r--src/view/line_segment.rs17
-rw-r--r--src/view/view.rs22
-rw-r--r--src/view/view_line.rs17
-rw-r--r--test/git-rebase-todo-all-actions.in16
13 files changed, 389 insertions, 85 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea070e9..ab9ae40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Visual mode - change action and reorder with multiple selections
- Configuration option for vertical spacing character
- Configurable key bindings
+- Horizontal scrolling
## [1.0.0] - 2019-04-10
diff --git a/src/config.rs b/src/config.rs
index a1c73af..20918a7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -33,6 +33,8 @@ pub struct Config {
pub input_force_rebase: String,
pub input_help: String,
pub input_move_down: String,
+ pub input_move_left: String,
+ pub input_move_right: String,
pub input_move_selection_down: String,
pub input_move_selection_up: String,
pub input_move_up: String,
@@ -149,6 +151,8 @@ impl Config {
input_force_rebase: get_input(&git_config, "interactive-rebase-tool.inputForceRebase", "W")?,
input_help: get_input(&git_config, "interactive-rebase-tool.inputHelp", "?")?,
input_move_down: get_input(&git_config, "interactive-rebase-tool.inputMoveDown", "Down")?,
+ input_move_left: get_input(&git_config, "interactive-rebase-tool.inputMoveLeft", "Left")?,
+ input_move_right: get_input(&git_config, "interactive-rebase-tool.inputMoveRight", "Right")?,
input_move_selection_down: get_input(&git_config, "interactive-rebase-tool.inputMoveSelectionDown", "j")?,
input_move_selection_up: get_input(&git_config, "interactive-rebase-tool.inputMoveSelectionUp", "k")?,
input_move_up: get_input(&git_config, "interactive-rebase-tool.inputMoveUp", "Up")?,
diff --git a/src/git_interactive.rs b/src/git_interactive.rs
index 3a86501..8ef42c7 100644
--- a/src/git_interactive.rs
+++ b/src/git_interactive.rs
@@ -230,23 +230,6 @@ impl GitInteractive {
&self.selected_commit_stats
}
- pub fn get_commit_stats_length(&self) -> usize {
- match &self.selected_commit_stats {
- Some(s) => {
- let mut len = s.get_file_stats_length();
-
- match s.get_body() {
- Some(b) => {
- len += b.lines().count();
- },
- None => {},
- }
- len + 3 // author + date + commit hash
- },
- None => 0,
- }
- }
-
pub fn get_selected_line_hash(&self) -> &String {
self.lines[self.selected_line_index - 1].get_hash()
}
diff --git a/src/help/help.rs b/src/help/help.rs
index d0bf2a6..227f52d 100644
--- a/src/help/help.rs
+++ b/src/help/help.rs
@@ -1,5 +1,9 @@
use crate::git_interactive::GitInteractive;
-use crate::help::utils::{get_list_normal_mode_help_lines, get_list_visual_mode_help_lines};
+use crate::help::utils::{
+ get_list_normal_mode_help_lines,
+ get_list_visual_mode_help_lines,
+ get_max_help_description_length,
+};
use crate::input::{Input, InputHandler};
use crate::process::{HandleInputResult, HandleInputResultBuilder, ProcessModule, State};
use crate::scroll::ScrollPosition;
@@ -9,9 +13,11 @@ use crate::Config;
pub struct Help<'h> {
normal_mode_help_lines: [(String, &'h str); 21],
+ normal_mode_max_help_line_length: usize,
return_state: State,
scroll_position: ScrollPosition,
visual_mode_help_lines: [(String, &'h str); 13],
+ visual_mode_max_help_line_length: usize,
}
impl<'h> ProcessModule for Help<'h> {
@@ -32,17 +38,24 @@ impl<'h> ProcessModule for Help<'h> {
view: &View,
) -> HandleInputResult
{
- let (_, window_height) = view.get_view_size();
+ let (view_width, view_height) = view.get_view_size();
let input = input_handler.get_input();
let mut result = HandleInputResultBuilder::new(input);
match input {
+ Input::MoveCursorLeft => {
+ self.scroll_position
+ .scroll_left(view_width, self.get_max_help_line_length())
+ },
+ Input::MoveCursorRight => {
+ self.scroll_position
+ .scroll_right(view_width, self.get_max_help_line_length())
+ },
Input::MoveCursorDown => {
self.scroll_position
- .scroll_down(window_height, self.get_help_lines().len());
+ .scroll_down(view_height, self.get_help_lines().len());
},
Input::MoveCursorUp => {
- self.scroll_position
- .scroll_up(window_height, self.get_help_lines().len());
+ self.scroll_position.scroll_up(view_height, self.get_help_lines().len());
},
Input::Resize => {
self.scroll_position.reset();
@@ -60,10 +73,13 @@ impl<'h> ProcessModule for Help<'h> {
let mut view_lines: Vec<ViewLine> = vec![];
for line in self.get_help_lines() {
- view_lines.push(ViewLine::new(vec![
- LineSegment::new_with_color(format!(" {:4} ", line.0).as_str(), WindowColor::IndicatorColor),
- LineSegment::new(line.1),
- ]));
+ view_lines.push(ViewLine::new_with_pinned_segments(
+ vec![
+ LineSegment::new_with_color(format!(" {:4} ", line.0).as_str(), WindowColor::IndicatorColor),
+ LineSegment::new(line.1),
+ ],
+ 1,
+ ));
}
view.draw_title(false);
@@ -76,7 +92,12 @@ impl<'h> ProcessModule for Help<'h> {
view.draw_str(padding.as_str());
}
- view.draw_view_lines(view_lines, self.scroll_position.get_position(), view_height - 3);
+ view.draw_view_lines(
+ view_lines,
+ self.scroll_position.get_top_position(),
+ self.scroll_position.get_left_position(),
+ view_height - 3,
+ );
view.set_color(WindowColor::IndicatorColor);
view.draw_str("Any key to close");
@@ -85,11 +106,17 @@ impl<'h> ProcessModule for Help<'h> {
impl<'h> Help<'h> {
pub fn new(config: &'h Config) -> Self {
+ let normal_mode_help_lines = get_list_normal_mode_help_lines(config);
+ let normal_mode_max_help_line_length = get_max_help_description_length(&normal_mode_help_lines);
+ let visual_mode_help_lines = get_list_visual_mode_help_lines(config);
+ let visual_mode_max_help_line_length = get_max_help_description_length(&visual_mode_help_lines);
Self {
- normal_mode_help_lines: get_list_normal_mode_help_lines(config),
+ normal_mode_help_lines,
+ normal_mode_max_help_line_length,
return_state: State::List(false),
scroll_position: ScrollPosition::new(3, 6, 3),
- visual_mode_help_lines: get_list_visual_mode_help_lines(config),
+ visual_mode_help_lines,
+ visual_mode_max_help_line_length,
}
}
@@ -106,4 +133,18 @@ impl<'h> Help<'h> {
&[]
}
}
+
+ pub fn get_max_help_line_length(&self) -> usize {
+ if let State::List(visual_mode) = self.return_state {
+ if visual_mode {
+ self.visual_mode_max_help_line_length + 6
+ }
+ else {
+ self.normal_mode_max_help_line_length + 6
+ }
+ }
+ else {
+ 4
+ }
+ }
}
diff --git a/src/help/utils.rs b/src/help/utils.rs
index 7a8a97d..00cef87 100644
--- a/src/help/utils.rs
+++ b/src/help/utils.rs
@@ -1,4 +1,5 @@
use crate::Config;
+use unicode_segmentation::UnicodeSegmentation;
pub fn get_list_normal_mode_help_lines(config: &Config) -> [(String, &str); 21] {
[
@@ -88,3 +89,14 @@ pub fn get_list_visual_mode_help_lines(config: &Config) -> [(String, &str); 13]
),
]
}
+
+pub fn get_max_help_description_length(lines: &[(String, &str)]) -> usize {
+ let mut max_length = 0;
+ for (_, desc) in lines {
+ let len = UnicodeSegmentation::graphemes(*desc, true).count();
+ if len > max_length {
+ max_length = len;
+ }
+ }
+ max_length
+}
diff --git a/src/input/input_handler.rs b/src/input/input_handler.rs
index 3de6023..e12b9db 100644
--- a/src/input/input_handler.rs
+++ b/src/input/input_handler.rs
@@ -30,6 +30,8 @@ impl<'i> InputHandler<'i> {
let input = match c {
PancursesInput::Character(c) => c.to_string(),
+ PancursesInput::KeyLeft => String::from("Left"),
+ PancursesInput::KeyRight => String::from("Right"),
PancursesInput::KeyDown => String::from("Down"),
PancursesInput::KeyUp => String::from("Up"),
PancursesInput::KeyPPage => String::from("PageUp"),
@@ -52,6 +54,8 @@ impl<'i> InputHandler<'i> {
i if i == self.config.input_force_abort.as_str() => Input::ForceAbort,
i if i == self.config.input_force_rebase.as_str() => Input::ForceRebase,
i if i == self.config.input_move_down.as_str() => Input::MoveCursorDown,
+ i if i == self.config.input_move_left.as_str() => Input::MoveCursorLeft,
+ i if i == self.config.input_move_right.as_str() => Input::MoveCursorRight,
i if i == self.config.input_move_selection_down.as_str() => Input::SwapSelectedDown,
i if i == self.config.input_move_selection_up.as_str() => Input::SwapSelectedUp,
i if i == self.config.input_move_up.as_str() => Input::MoveCursorUp,
diff --git a/src/list/list.rs b/src/list/list.rs
index 60c3eaa..3c3a391 100644
--- a/src/list/list.rs
+++ b/src/list/list.rs
@@ -16,6 +16,7 @@ use crate::scroll::ScrollPosition;
use crate::view::{LineSegment, View, ViewLine};
use crate::window::WindowColor;
use std::cmp;
+use unicode_segmentation::UnicodeSegmentation;
#[derive(Debug, PartialEq)]
enum ListState {
@@ -33,6 +34,37 @@ pub struct List<'l> {
visual_footer_full: String,
}
+fn get_maximum_line_length(is_full_width: bool, lines: &[Line]) -> usize {
+ let mut length = 0;
+ if is_full_width {
+ for line in lines {
+ let line_length = match *line.get_action() {
+ Action::Exec => UnicodeSegmentation::graphemes(line.get_command().as_str(), true).count(),
+ Action::Break => 0,
+ _ => 9 + UnicodeSegmentation::graphemes(line.get_comment().as_str(), true).count(),
+ } + 10;
+
+ if line_length > length {
+ length = line_length;
+ }
+ }
+ }
+ else {
+ for line in lines {
+ let line_length = match *line.get_action() {
+ Action::Exec => UnicodeSegmentation::graphemes(line.get_command().as_str(), true).count(),
+ Action::Break => 0,
+ _ => 4 + UnicodeSegmentation::graphemes(line.get_comment().as_str(), true).count(),
+ } + 3;
+
+ if line_length > length {
+ length = line_length;
+ }
+ }
+ }
+ length
+}
+
impl<'l> ProcessModule for List<'l> {
fn process(&mut self, git_interactive: &mut GitInteractive, view: &View) -> ProcessResult {
let (_, view_height) = view.get_view_size();
@@ -40,6 +72,7 @@ impl<'l> ProcessModule for List<'l> {
let selected_index = *git_interactive.get_selected_line_index() - 1;
self.scroll_position
.ensure_cursor_visible(selected_index, view_height, lines.len());
+
ProcessResult::new()
}
@@ -47,12 +80,12 @@ impl<'l> ProcessModule for List<'l> {
&mut self,
input_handler: &InputHandler,
git_interactive: &mut GitInteractive,
- _view: &View,
+ view: &View,
) -> HandleInputResult
{
match self.state {
- ListState::Normal => self.handle_normal_mode_input(input_handler, git_interactive),
- ListState::Visual => self.handle_visual_mode_input(input_handler, git_interactive),
+ ListState::Normal => self.handle_normal_mode_input(input_handler, git_interactive, view),
+ ListState::Visual => self.handle_visual_mode_input(input_handler, git_interactive, view),
}
}
@@ -68,19 +101,27 @@ impl<'l> ProcessModule for List<'l> {
let selected_index = *git_interactive.get_selected_line_index() - 1;
for (index, line) in git_interactive.get_lines().iter().enumerate() {
- view_lines.push(ViewLine::new(self.get_todo_line_segments(
- line,
- selected_index == index,
- is_visual_mode
- && ((visual_index <= selected_index && index >= visual_index && index <= selected_index)
- || (visual_index > selected_index && index >= selected_index && index <= visual_index)),
- view_width,
- )));
+ view_lines.push(ViewLine::new_with_pinned_segments(
+ self.get_todo_line_segments(
+ line,
+ selected_index == index,
+ is_visual_mode
+ && ((visual_index <= selected_index && index >= visual_index && index <= selected_index)
+ || (visual_index > selected_index && index >= selected_index && index <= visual_index)),
+ view_width,
+ ),
+ if *line.get_action() == Action::Exec { 2 } else { 3 },
+ ));
}
view.draw_title(true);
- view.draw_view_lines(view_lines, self.scroll_position.get_position(), view_height - 2);
+ view.draw_view_lines(
+ view_lines,
+ self.scroll_position.get_top_position(),
+ self.scroll_position.get_left_position(),
+ view_height - 2,
+ );
view.set_color(WindowColor::Foreground);
view.set_style(true, false, false);
@@ -132,10 +173,12 @@ impl<'l> List<'l> {
&mut self,
input_handler: &InputHandler,
git_interactive: &mut GitInteractive,
+ view: &View,
) -> HandleInputResult
{
let input = input_handler.get_input();
let mut result = HandleInputResultBuilder::new(input);
+ let (view_width, _) = view.get_view_size();
match input {
Input::Help => {
result = result.help(State::List(false));
@@ -172,6 +215,18 @@ impl<'l> List<'l> {
},
Input::SwapSelectedDown => git_interactive.swap_selected_down(),
Input::SwapSelectedUp => git_interactive.swap_selected_up(),
+ Input::MoveCursorLeft => {
+ self.scroll_position.scroll_left(
+ view_width,
+ get_maximum_line_length(view_width >= MINIMUM_FULL_WINDOW_WIDTH, git_interactive.get_lines()),
+ )
+ },
+ Input::MoveCursorRight => {
+ self.scroll_position.scroll_right(
+ view_width,
+ get_maximum_line_length(view_width >= MINIMUM_FULL_WINDOW_WIDTH, git_interactive.get_lines()),
+ )
+ },
Input::MoveCursorDown => git_interactive.move_cursor_down(1),
Input::MoveCursorUp => git_interactive.move_cursor_up(1),
Input::MoveCursorPageDown => git_interactive.move_cursor_down(5),
@@ -191,14 +246,28 @@ impl<'l> List<'l> {
&mut self,
input_handler: &InputHandler,
git_interactive: &mut GitInteractive,
+ view: &View,
) -> HandleInputResult
{
let input = input_handler.get_input();
let mut result = HandleInputResultBuilder::new(input);
+ let (view_width, _) = view.get_view_size();
match input {
Input::Help => {
result = result.help(State::List(true));
},
+ Input::MoveCursorLeft => {
+ self.scroll_position.scroll_left(
+ view_width,
+ get_maximum_line_length(view_width >= MINIMUM_FULL_WINDOW_WIDTH, git_interactive.get_lines()),
+ )
+ },
+ Input::MoveCursorRight => {
+ self.scroll_position.scroll_right(
+ view_width,
+ get_maximum_line_length(view_width >= MINIMUM_FULL_WINDOW_WIDTH, git_interactive.get_lines()),
+ )
+ },
Input::MoveCursorDown => {
git_interactive.move_cursor_down(1);
},
@@ -259,7 +328,7 @@ impl<'l> List<'l> {
line.get_command().clone()
}
else if *action == Action::Break {
- String::from(" ")
+ String::from("")
}
else {
let max_index = cmp::min(line.get_hash().len(), 8);
diff --git a/src/scroll/scroll_position.rs b/src/scroll/scroll_position.rs
index 8de43c7..e75272a 100644
--- a/src/scroll/scroll_position.rs
+++ b/src/scroll/scroll_position.rs
@@ -2,23 +2,26 @@ use std::cell::RefCell;
pub struct ScrollPosition {
big_scroll: usize,
+ left_value: RefCell<usize>,
padding: usize,
small_scroll: usize,
- value: RefCell<usize>,
+ top_value: RefCell<usize>,
}
impl ScrollPosition {
pub fn new(padding: usize, big_scroll: usize, small_scroll: usize) -> Self {
Self {
big_scroll,
+ left_value: RefCell::new(0 as usize),
padding,
small_scroll,
- value: RefCell::new(0 as usize),
+ top_value: RefCell::new(0 as usize),
}
}
pub fn reset(&self) {
- self.value.replace(0);
+ self.left_value.replace(0);
+ self.top_value.replace(0);
}
pub fn scroll_up(&self, window_height: usize, lines_length: usize) {
@@ -29,13 +32,39 @@ impl ScrollPosition {
self.update_top(false, window_height, lines_length);
}
+ pub fn scroll_left(&self, view_width: usize, max_line_width: usize) {
+ let current_value = *self.left_value.borrow();
+ if current_value != 0 {
+ self.set_horizontal_scroll(current_value - 1, view_width, max_line_width);
+ }
+ }
+
+ pub fn scroll_right(&self, view_width: usize, max_line_width: usize) {
+ let current_value = *self.left_value.borrow();
+ self.set_horizontal_scroll(current_value + 1, view_width, max_line_width);
+ }
+
+ fn set_horizontal_scroll(&self, new_value: usize, view_width: usize, max_line_width: usize) {
+ if (new_value + view_width) > max_line_width {
+ if view_width > max_line_width {
+ self.left_value.replace(0);
+ }
+ else {
+ self.left_value.replace(max_line_width - view_width);
+ }
+ }
+ else {
+ self.left_value.replace(new_value);
+ }
+ }
+
pub fn ensure_cursor_visible(&self, cursor: usize, window_height: usize, lines_length: usize) {
let view_height = window_height as usize - self.padding;
- let current_value = *self.value.borrow();
+ let current_value = *self.top_value.borrow();
// TODO I think this can be simplified
- self.value.replace(match cursor {
+ self.top_value.replace(match cursor {
// show all if list is view height is long enough
_ if lines_length <= view_height => 0,
// last item selected, set top to show bottom of lines
@@ -52,8 +81,12 @@ impl ScrollPosition {
});
}
- pub fn get_position(&self) -> usize {
- *self.value.borrow()
+ pub fn get_top_position(&self) -> usize {
+ *self.top_value.borrow()
+ }
+
+ pub fn get_left_position(&self) -> usize {
+ *self.left_value.borrow()
}
fn update_top(&self, scroll_up: bool, window_height: usize, lines_length: usize) {
@@ -70,21 +103,21 @@ impl ScrollPosition {
_ => 1,
};
- let current_value = *self.value.borrow();
+ let current_value = *self.top_value.borrow();
if scroll_up {
if current_value < amount {
self.reset();
}
else {
- self.value.replace(current_value - amount);
+ self.top_value.replace(current_value - amount);
}
}
else if current_value + amount + view_height > lines_length {
- self.value.replace(lines_length - view_height);
+ self.top_value.replace(lines_length - view_height);
}
else {
- self.value.replace(current_value + amount);
+ self.top_value.replace(current_value + amount);
}
}
}
diff --git a/src/show_commit/show_commit.rs b/src/show_commit/show_commit.rs
index f61a0d4..9fdd2c9 100644
--- a/src/show_commit/show_commit.rs
+++ b/src/show_commit/show_commit.rs
@@ -1,3 +1,4 @@
+use crate::commit::Commit;
use crate::constants::MINIMUM_FULL_WINDOW_WIDTH;
use crate::git_interactive::GitInteractive;
use crate::input::{Input, InputHandler};
@@ -14,6 +15,7 @@ use crate::show_commit::util::get_stat_item_segments;
use crate::view::{LineSegment, View, ViewLine};
use crate::window::WindowColor;
use std::cmp;
+use unicode_segmentation::UnicodeSegmentation;
pub struct ShowCommit {
scroll_position: ScrollPosition,
@@ -39,22 +41,46 @@ impl ProcessModule for ShowCommit {
view: &View,
) -> HandleInputResult
{
- let (_, window_height) = view.get_view_size();
+ let (view_width, view_height) = view.get_view_size();
let input = input_handler.get_input();
let mut result = HandleInputResultBuilder::new(input);
match input {
+ Input::MoveCursorLeft => {
+ self.scroll_position.scroll_left(
+ view_width,
+ self.get_max_line_length(
+ git_interactive.get_commit_stats(),
+ view_height >= MINIMUM_FULL_WINDOW_WIDTH,
+ ),
+ )
+ },
+ Input::MoveCursorRight => {
+ self.scroll_position.scroll_right(
+ view_width,
+ self.get_max_line_length(
+ git_interactive.get_commit_stats(),
+ view_height >= MINIMUM_FULL_WINDOW_WIDTH,
+ ),
+ )
+ },
Input::MoveCursorDown => {
- self.scroll_position
- .scroll_down(window_height, git_interactive.get_commit_stats_length())
+ self.scroll_position.scroll_down(
+ view_height,
+ self.get_commit_stats_length(git_interactive.get_commit_stats()),
+ )
},
Input::MoveCursorUp => {
- self.scroll_position
- .scroll_up(window_height, git_interactive.get_commit_stats_length())
+ self.scroll_position.scroll_up(
+ view_height,
+ self.get_commit_stats_length(git_interactive.get_commit_stats()),
+ )
},
Input::Resize => {
- self.scroll_position
- .scroll_up(window_height as usize, git_interactive.get_commit_stats_length());
+ self.scroll_position.scroll_up(
+ view_height as usize,
+ self.get_commit_stats_length(git_interactive.get_commit_stats()),
+ );
},
_ => {
result = result.state(State::List(false));
@@ -160,7 +186,12 @@ impl ProcessModule for ShowCommit {
None => {},
}
- view.draw_view_lines(lines, self.scroll_position.get_position(), view_height);
+ view.draw_view_lines(
+ lines,
+ self.scroll_position.get_top_position(),
+ self.scroll_position.get_left_position(),
+ view_height,
+ );
view.set_color(WindowColor::IndicatorColor);
view.draw_str("Any key to close");
@@ -173,4 +204,106 @@ impl ShowCommit {
scroll_position: ScrollPosition::new(3, 6, 3),
}
}
+
+ fn get_commit_stats_length(&self, commit: &Option<Commit>) -> usize {
+ match commit {
+ Some(c) => {
+ let mut len = c.get_file_stats_length();
+
+ match c.get_body() {
+ Some(b) => {
+ len += b.lines().count();
+ },
+ None => {},
+ }
+ len + 3 // author + date + commit hash
+ },
+ None => 0,
+ }
+ }
+
+ fn get_max_line_length(&self, commit: &Option<Commit>, is_full_width: bool) -> usize {
+ match commit {
+ Some(c) => {
+ let full_hash = c.get_hash();
+ let author = c.get_author();
+ let committer = c.get_committer();
+ let body = c.get_body();
+ let file_stats = c.get_file_stats();
+
+ let mut max_line_length = if is_full_width {
+ full_hash.len() + 8 // 8 = "Commit: "
+ }
+ else {
+ cmp::min(full_hash.len(), 8)
+ };
+
+ max_line_length = cmp::max(
+ if is_full_width {
+ 35 // "Date: Sun Jul 8 00:34:60 2001+09:30"
+ }
+ else {
+ 29 // "Sun Jul 8 00:34:60 2001+09:30"
+ },
+ max_line_length,
+ );
+
+ if let Some(a) = author.to_string() {
+ max_line_length = cmp::max(
+ if is_full_width {
+ UnicodeSegmentation::graphemes(a.as_str(), true).count() + 8 // 8 = "Author: "
+ }
+ else {
+ UnicodeSegmentation::graphemes(a.as_str(), true).count() + 3 // 3 = "A: "
+ },
+ max_line_length,
+ );
+ }
+
+ if let Some(c) = committer.to_string() {
+ max_line_length = cmp::max(
+ if is_full_width {
+ UnicodeSegmentation::graphemes(c.as_str(), true).count() + 11 // 11 = "Committer: "
+ }
+ else {
+ UnicodeSegmentation::graphemes(c.as_str(), true).count() + 3 // 3 = "C: "
+ },
+ max_line_length,
+ );
+ };
+
+ if let Some(b) = body {
+ for line in b.lines() {
+ let line_length = UnicodeSegmentation::graphemes(line, true).count();
+ if line_length > max_line_length {
+ max_line_length = line_length;
+ }
+ }
+ }
+
+ if let Some(stats) = file_stats {
+ let additional_line_length = if is_full_width {
+ 13 // stat name + arrow
+ }
+ else {
+ 3 // stat name + arrow
+ };
+
+ for stat in stats {
+ let stat_line_length =
+ UnicodeSegmentation::graphemes(stat.get_to_name().as_str(), true).count()
+ + UnicodeSegmentation::graphemes(stat.get_from_name().as_str(), true).count()
+ + additional_line_length;
+
+ if stat_line_length > max_line_length {
+ max_line_length = stat_line_length;
+ }
+ }
+ }
+
+ max_line_length
+ },
+ None => 0,
+ }
+ }
}
diff --git a/src/view/line_segment.rs b/src/view/line_segment.rs
index b0e8a72..fcf3d92 100644
--- a/src/view/line_segment.rs
+++ b/src/view/line_segment.rs
@@ -40,20 +40,25 @@ impl LineSegment {
}
}
- pub fn draw(&self, max_width: usize, window: &Window) -> usize {
+ pub fn draw(&self, left: usize, max_width: usize, window: &Window) -> (usize, usize) {
window.color(self.color);
window.set_style(self.dim, self.underline, self.reverse);
let segment_length = UnicodeSegmentation::graphemes(self.text.as_str(), true).count();
- if segment_length >= max_width {
+ if segment_length <= left {
+ (0, segment_length)
+ }
+ else if segment_length - left >= max_width {
let graphemes = UnicodeSegmentation::graphemes(self.text.as_str(), true);
- let partial_line = graphemes.take(max_width).collect::<String>();
+ let partial_line = graphemes.skip(left).take(max_width).collect::<String>();
window.draw_str(partial_line.as_str());
- max_width
+ (max_width, segment_length)
}
else {
- window.draw_str(self.text.as_str());
- segment_length
+ let graphemes = UnicodeSegmentation::graphemes(self.text.as_str(), true);
+ let partial_line = graphemes.skip(left).collect::<String>();
+ window.draw_str(partial_line.as_str());
+ (segment_length - left, segment_length)
}
}
}
diff --git a/src/view/view.rs b/src/view/view.rs
index 48a377d..321bb60 100644
--- a/src/view/view.rs
+++ b/src/view/view.rs
@@ -63,7 +63,7 @@ impl<'v> View<'v> {
self.window.refresh();
}
- pub fn draw_view_lines(&self, lines: Vec<ViewLine>, top: usize, height: usize) {
+ pub fn draw_view_lines(&self, lines: Vec<ViewLine>, top: usize, left: usize, height: usize) {
let number_of_lines = lines.len();
let scroll_indicator_index = get_scroll_position(top, number_of_lines, height);
@@ -71,7 +71,7 @@ impl<'v> View<'v> {
let mut index: usize = 0;
for line in lines.iter().skip(top).take(height) {
- self.draw_view_line(line, show_scroll_bar);
+ self.draw_view_line(line, left, show_scroll_bar);
if show_scroll_bar {
self.window.color(WindowColor::Foreground);
self.window.set_style(scroll_indicator_index != index, false, true);
@@ -85,16 +85,28 @@ impl<'v> View<'v> {
}
}
- pub fn draw_view_line(&self, line: &ViewLine, scrollbar: bool) {
+ pub fn draw_view_line(&self, line: &ViewLine, left: usize, scrollbar: bool) {
let (window_width, _) = self.window.get_window_size();
let window_width = if scrollbar { window_width - 1 } else { window_width } as usize;
let mut start = 0;
- for segment in line.get_segments() {
- start += segment.draw(window_width - start, &self.window);
+ let mut left_start = 0;
+ for (i, segment) in line.get_segments().iter().enumerate() {
+ // set left on first non-pinned segment
+ if i == line.get_number_of_pinned_segment() {
+ left_start = left;
+ }
+ let (amount_drawn, segment_size) = segment.draw(left_start, window_width - start, &self.window);
+ start += amount_drawn;
if start >= window_width {
break;
}
+ if amount_drawn > 0 {
+ left_start = 0;
+ }
+ else {
+ left_start -= segment_size;
+ }
}
if start < window_width {
diff --git a/src/view/view_line.rs b/src/view/view_line.rs
index 818b025..f3e7d50 100644
--- a/src/view/view_line.rs
+++ b/src/view/view_line.rs
@@ -2,14 +2,29 @@ use crate::view::LineSegment;
pub struct ViewLine {
segments: Vec<LineSegment>,
+ pinned_segments: usize,
}
impl ViewLine {
pub fn new(segments: Vec<LineSegment>) -> Self {
- Self { segments }
+ Self {
+ segments,
+ pinned_segments: 0,
+ }
+ }
+
+ pub fn new_with_pinned_segments(segments: Vec<LineSegment>, pinned_segments: usize) -> Self {
+ Self {
+ segments,
+ pinned_segments,
+ }
}
pub fn get_segments(&self) -> &Vec<LineSegment> {
&self.segments
}
+
+ pub fn get_number_of_pinned_segment(&self) -> usize {
+ self.pinned_segments
+ }
}
diff --git a/test/git-rebase-todo-all-actions.in b/test/git-rebase-todo-all-actions.in
index 4caa924..6fd972c 100644
--- a/test/git-rebase-todo-all-actions.in
+++ b/test/git-rebase-todo-all-actions.in
@@ -1,18 +1,10 @@
# A Commment
-pick 166a8bc53895a340625c781bb5dbc2f9794a3a23 Added tests
-reword bbbbbbbbbbbbbbb Added tests
-edit ccccccccccccccc Added tests
-exec git commit --amend -m "Foo bar"
+pick 166a8bc53895a340625c781bb5dbc2f9794a3a23 abcdefghijklmnop
+reword bbbbbbbbbbbbbbb Added tests, this line is longer than it probably needs to be
+edit ccccccccccccccc Lorem ipsum st decourm
+exec git commit --amend -m "Foo bar, with some more text to make this line really lone"
break
-squash ddddddddddddddd Added tests
-fixup eeeeeeeeeeeeeee Added tests
-drop fffffffffffffff Added tests
-p 111111111111111 Added tests
-r 222222222222222 Added tests
-e 333333333333333 Added tests
x git commit --amend -m "Foo bar"
b
-s 444444444444444 Added tests
-f 555555555555555 Added tests
d 666666666666666 Added tests
# A Commment