diff --git a/src/presenters/error.rs b/src/presenters/error.rs index 163c9031..c46a0052 100644 --- a/src/presenters/error.rs +++ b/src/presenters/error.rs @@ -11,11 +11,16 @@ pub fn display(workspace: &mut Workspace, view: &mut View, error: &Error) { let _ = presenter.print_buffer(buffer, &data, None, None); } - presenter.print_status_line(&[StatusLineData { - content: error.description().to_string(), - style: Style::Bold, - colors: Colors::Warning, - }]); + presenter.print_status_line( + &[ + StatusLineData { + content: error.description().to_string(), + style: Style::Bold, + colors: Colors::Warning, + } + ], + &[], + ); presenter.present(); } diff --git a/src/presenters/mod.rs b/src/presenters/mod.rs index f50949af..bfa39034 100644 --- a/src/presenters/mod.rs +++ b/src/presenters/mod.rs @@ -11,19 +11,19 @@ fn path_as_title(path: &Path) -> String { } fn current_buffer_status_line_data(workspace: &mut Workspace) -> StatusLineData { - let modified = workspace.current_buffer().map(|b| b.modified()).unwrap_or(false); + let buffer = workspace.current_buffer(); + let modified = buffer.as_ref().map(|b| b.modified()).unwrap_or(false); let (content, style) = workspace.current_buffer_path().map(|path| { - // Determine buffer title styles based on its modification status. + let mut title = path_as_title(path); + let mut style = Style::Default; + if modified { - // Use an emboldened path with an asterisk. - let mut title = path_as_title(path); title.push('*'); - - (title, Style::Bold) - } else { - (path_as_title(path), Style::Default) + style = Style::Bold; } + + (format!(" {}", title), style) }).unwrap_or((String::new(), Style::Default)); StatusLineData { @@ -33,6 +33,21 @@ fn current_buffer_status_line_data(workspace: &mut Workspace) -> StatusLineData } } +fn percentage_cursor_indicator_line_data(workspace: &mut Workspace) -> StatusLineData { + let content = workspace.current_buffer().map(|b| { + let line_total = b.line_count(); + let line_at = b.cursor.position.line + 1; + let line_perc = (line_at * 100) / line_total; + format!(" [{:2}%]", line_perc) + }).unwrap_or(String::new()); + + StatusLineData { + content, + style: Style::Default, + colors: Colors::Focused, + } +} + fn git_status_line_data(repo: &Option, path: &Option) -> StatusLineData { // Build a display value for the current buffer's git status. let mut content = String::new(); @@ -54,6 +69,7 @@ fn git_status_line_data(repo: &Option, path: &Option) -> St colors: Colors::Focused, } } + fn presentable_status(status: &Status) -> &str { if status.contains(git2::Status::WT_NEW) { if status.contains(git2::Status::INDEX_NEW) { diff --git a/src/presenters/modes/confirm.rs b/src/presenters/modes/confirm.rs index 41eb1744..d1162b30 100644 --- a/src/presenters/modes/confirm.rs +++ b/src/presenters/modes/confirm.rs @@ -12,13 +12,16 @@ pub fn display(workspace: &mut Workspace, view: &mut View) -> Result<()> { // Draw the status line as a search prompt. let confirmation = "Are you sure? (y/n)".to_string(); - presenter.print_status_line(&[ - StatusLineData { - content: confirmation, - style: Style::Bold, - colors: Colors::Warning, - } - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: confirmation, + style: Style::Bold, + colors: Colors::Warning, + } + ], + &[], + ); // Render the changes to the screen. presenter.present(); diff --git a/src/presenters/modes/insert.rs b/src/presenters/modes/insert.rs index 4a8b32da..26e6a1c6 100644 --- a/src/presenters/modes/insert.rs +++ b/src/presenters/modes/insert.rs @@ -12,14 +12,17 @@ pub fn display(workspace: &mut Workspace, view: &mut View) -> Result<()> { // Draw the visible set of tokens to the terminal. presenter.print_buffer(buf, &data, None, None)?; - presenter.print_status_line(&[ - StatusLineData { - content: " INSERT ".to_string(), - style: Style::Default, - colors: Colors::Insert, - }, - buffer_status - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: " INSERT ".to_string(), + style: Style::Default, + colors: Colors::Insert, + }, + buffer_status + ], + &[], + ); // Render the changes to the screen. presenter.present(); diff --git a/src/presenters/modes/jump.rs b/src/presenters/modes/jump.rs index 6aef8cb6..4d6c1f3f 100644 --- a/src/presenters/modes/jump.rs +++ b/src/presenters/modes/jump.rs @@ -15,14 +15,17 @@ pub fn display(workspace: &mut Workspace, mode: &mut JumpMode, view: &mut View) // Draw the visible set of tokens to the terminal. presenter.print_buffer(buf, &data, None, Some(mode))?; - presenter.print_status_line(&[ - StatusLineData { - content: " JUMP ".to_string(), - style: Style::Default, - colors: Colors::Inverted, - }, - buffer_status - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: " JUMP ".to_string(), + style: Style::Default, + colors: Colors::Inverted, + }, + buffer_status + ], + &[], + ); // Don't display a cursor. presenter.set_cursor(None); diff --git a/src/presenters/modes/line_jump.rs b/src/presenters/modes/line_jump.rs index d715c2b3..1c189348 100644 --- a/src/presenters/modes/line_jump.rs +++ b/src/presenters/modes/line_jump.rs @@ -13,13 +13,16 @@ pub fn display(workspace: &mut Workspace, mode: &LineJumpMode, view: &mut View) // Draw the status line as an input prompt. let input_prompt = format!("Go to line: {}", mode.input); let input_prompt_len = input_prompt.len(); - presenter.print_status_line(&[ - StatusLineData { - content: input_prompt, - style: Style::Default, - colors: Colors::Default, - } - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: input_prompt, + style: Style::Default, + colors: Colors::Default, + } + ], + &[], + ); // Move the cursor to the end of the search query input. let cursor_line = presenter.height() - 1; diff --git a/src/presenters/modes/normal.rs b/src/presenters/modes/normal.rs index c8cd0b3a..27448f0e 100644 --- a/src/presenters/modes/normal.rs +++ b/src/presenters/modes/normal.rs @@ -1,15 +1,22 @@ use crate::errors::*; use scribe::Workspace; use scribe::buffer::Position; -use crate::presenters::{current_buffer_status_line_data, git_status_line_data}; +use crate::presenters::{ + current_buffer_status_line_data, + git_status_line_data, + percentage_cursor_indicator_line_data +}; use git2::Repository; use crate::view::{Colors, StatusLineData, Style, View}; pub fn display(workspace: &mut Workspace, view: &mut View, repo: &Option) -> Result<()> { + let height = view.height(); let mut presenter = view.build_presenter()?; let buffer_status = current_buffer_status_line_data(workspace); if let Some(buf) = workspace.current_buffer() { + let line_count = buf.line_count(); + // Draw the visible set of tokens to the terminal. let data = buf.data(); presenter.print_buffer(buf, &data, None, None)?; @@ -21,16 +28,27 @@ pub fn display(workspace: &mut Workspace, view: &mut View, repo: &Option R mode_display.graphemes(true).count() + search_input.graphemes(true).count(); - presenter.print_status_line(&[ - StatusLineData { - content: mode_display, - style: Style::Default, - colors: Colors::PathMode, - }, - StatusLineData { - content: search_input, - style: Style::Default, - colors: Colors::Focused, - }, - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: mode_display, + style: Style::Default, + colors: Colors::PathMode, + }, + StatusLineData { + content: search_input, + style: Style::Default, + colors: Colors::Focused, + }, + ], + &[], + ); // Move the cursor to the end of the search query input. { diff --git a/src/presenters/modes/search.rs b/src/presenters/modes/search.rs index b1945f45..96586d2d 100644 --- a/src/presenters/modes/search.rs +++ b/src/presenters/modes/search.rs @@ -34,23 +34,27 @@ pub fn display(workspace: &mut Workspace, mode: &SearchMode, view: &mut View) -> mode_display.graphemes(true).count() + search_input.graphemes(true).count(); - presenter.print_status_line(&[ - StatusLineData { - content: mode_display, - style: Style::Default, - colors: Colors::SearchMode, - }, - StatusLineData { - content: search_input, - style: Style::Default, - colors: Colors::Focused, - }, - StatusLineData { - content: result_display, - style: Style::Default, - colors: Colors::Focused, - }, - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: mode_display, + style: Style::Default, + colors: Colors::SearchMode, + }, + StatusLineData { + content: search_input, + style: Style::Default, + colors: Colors::Focused, + }, + ], + &[ + StatusLineData { + content: result_display, + style: Style::Default, + colors: Colors::Focused, + }, + ], + ); // Move the cursor to the end of the search query input. if mode.insert { diff --git a/src/presenters/modes/search_select.rs b/src/presenters/modes/search_select.rs index ea3ae8d3..5f7b30f4 100644 --- a/src/presenters/modes/search_select.rs +++ b/src/presenters/modes/search_select.rs @@ -22,14 +22,17 @@ pub fn display(workspace: &mut Workspace, mode: &mut dyn SearchSelec data = buf.data(); presenter.print_buffer(buf, &data, None, None)?; - presenter.print_status_line(&[ - StatusLineData { - content: format!(" {} ", mode), - style: Style::Default, - colors: Colors::Inverted, - }, - buffer_status - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: format!(" {} ", mode), + style: Style::Default, + colors: Colors::Inverted, + }, + buffer_status + ], + &[], + ); } if let Some(message) = mode.message() { diff --git a/src/presenters/modes/select.rs b/src/presenters/modes/select.rs index ba9e1a2d..96c837c7 100644 --- a/src/presenters/modes/select.rs +++ b/src/presenters/modes/select.rs @@ -15,14 +15,17 @@ pub fn display(workspace: &mut Workspace, mode: &SelectMode, view: &mut View) -> // Draw the visible set of tokens to the terminal. presenter.print_buffer(buf, &data, Some(&[selected_range]), None)?; - presenter.print_status_line(&[ - StatusLineData { - content: " SELECT ".to_string(), - style: Style::Default, - colors: Colors::SelectMode, - }, - buffer_status - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: " SELECT ".to_string(), + style: Style::Default, + colors: Colors::SelectMode, + }, + buffer_status + ], + &[], + ); // Render the changes to the screen. presenter.present(); diff --git a/src/presenters/modes/select_line.rs b/src/presenters/modes/select_line.rs index 691955dd..057fc52c 100644 --- a/src/presenters/modes/select_line.rs +++ b/src/presenters/modes/select_line.rs @@ -14,14 +14,17 @@ pub fn display(workspace: &mut Workspace, mode: &SelectLineMode, view: &mut View // Draw the visible set of tokens to the terminal. presenter.print_buffer(buf, &data, Some(&[selected_range]), None)?; - presenter.print_status_line(&[ - StatusLineData { - content: " SELECT LINE ".to_string(), - style: Style::Default, - colors: Colors::SelectMode, - }, - buffer_status - ]); + presenter.print_status_line( + &[ + StatusLineData { + content: " SELECT LINE ".to_string(), + style: Style::Default, + colors: Colors::SelectMode, + }, + buffer_status + ], + &[], + ); // Render the changes to the screen. presenter.present(); diff --git a/src/view/mod.rs b/src/view/mod.rs index 062c3f36..ad3794bb 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -70,6 +70,10 @@ impl View { Presenter::new(self) } + pub fn height(&self) -> usize { + self.terminal.height() + } + /// /// Scrollable region delegation methods. /// diff --git a/src/view/presenter.rs b/src/view/presenter.rs index 8a726b86..30f161dc 100644 --- a/src/view/presenter.rs +++ b/src/view/presenter.rs @@ -86,49 +86,49 @@ impl<'p> Presenter<'p> { Ok(()) } - pub fn print_status_line(&mut self, entries: &[StatusLineData]) { + pub fn print_status_line( + &mut self, left: &[StatusLineData], right: &[StatusLineData] + ) { let line = self.view.terminal.height() - 1; - entries.iter().enumerate().fold(0, |offset, (index, element)| { - let content = match entries.len() { - // There's only one element; have it fill the line. - 1 => format!( - "{:width$}", - element.content, - width = self.view.terminal.width(), - ), - - // Expand the last element to fill the remaining width. - 2 if index == entries.len() - 1 => format!( - "{:width$}", - element.content, - width = self.view.terminal.width().saturating_sub(offset), - ), - 2 => element.content.clone(), - - _ if index == entries.len() - 2 => { - let space = offset + entries[index+1].content.len(); - format!( - "{:width$}", - element.content, - width = self.view.terminal.width().saturating_sub(space), - ) - }, - _ => element.content.clone(), - }; - - // Update the tracked offset. - let updated_offset = offset + content.len(); - + let mut left_end = self.view.terminal.width(); + for entry in right.iter().rev() { + left_end = left_end.saturating_sub(entry.content.len()); self.print( - &Position{ line, offset }, - element.style, - element.colors, - content + &Position { line, offset: left_end }, + entry.style, + entry.colors, + entry.content.clone(), ); + } - updated_offset - }); + let mut left_start = 0; + + for (i, entry) in left.iter().enumerate() { + if i == left.len() - 1 { + // The last element should fill the remaining the width. + let len = left_end.saturating_sub(left_start); + self.print( + &Position { line, offset: left_start }, + entry.style, + entry.colors, + format!( + "{:width$}", + entry.content.clone(), + width = len, + ), + ); + } else { + self.print( + &Position { line, offset: left_start }, + entry.style, + entry.colors, + entry.content.clone(), + ); + + left_start += entry.content.len(); + } + } } pub fn print(&mut self, position: &Position, style: Style, colors: Colors, content: C)