From 9b3e7c27386676077abaa7e89ec9553b5c44894e Mon Sep 17 00:00:00 2001 From: Jordan MacDonald Date: Mon, 19 Feb 2024 12:08:56 -0500 Subject: [PATCH] Apply and enforce rustfmt --- .github/workflows/rust.yml | 6 + benches/view/draw_buffer.rs | 85 +- build.rs | 69 +- src/commands/application.rs | 110 +- src/commands/buffer.rs | 962 +++++++++++------- src/commands/confirm.rs | 11 +- src/commands/cursor.rs | 175 ++-- src/commands/git.rs | 77 +- src/commands/jump.rs | 44 +- src/commands/line_jump.rs | 50 +- src/commands/mod.rs | 3 +- src/commands/path.rs | 65 +- src/commands/search.rs | 107 +- src/commands/search_select.rs | 41 +- src/commands/selection.rs | 109 +- src/commands/view.rs | 26 +- src/commands/workspace.rs | 2 +- src/input/key_map/mod.rs | 303 ++++-- src/lib.rs | 4 +- src/main.rs | 6 +- src/models/application/clipboard.rs | 8 +- src/models/application/event.rs | 2 +- src/models/application/mod.rs | 136 ++- .../modes/command/displayable_command.rs | 2 +- src/models/application/modes/command/mod.rs | 32 +- src/models/application/modes/jump/mod.rs | 190 ++-- .../jump/single_character_tag_generator.rs | 17 +- src/models/application/modes/mod.rs | 8 +- .../application/modes/open/exclusions.rs | 5 +- src/models/application/modes/open/mod.rs | 41 +- src/models/application/modes/path.rs | 2 +- src/models/application/modes/search.rs | 27 +- src/models/application/modes/search_select.rs | 70 +- src/models/application/modes/symbol_jump.rs | 114 ++- src/models/application/modes/syntax.rs | 18 +- src/models/application/modes/theme.rs | 11 +- src/models/application/preferences/mod.rs | 359 ++++--- src/presenters/error.rs | 2 +- src/presenters/mod.rs | 49 +- src/presenters/modes/confirm.rs | 14 +- src/presenters/modes/insert.rs | 4 +- src/presenters/modes/jump.rs | 6 +- src/presenters/modes/line_jump.rs | 16 +- src/presenters/modes/mod.rs | 2 +- src/presenters/modes/normal.rs | 20 +- src/presenters/modes/path.rs | 17 +- src/presenters/modes/search.rs | 31 +- src/presenters/modes/search_select.rs | 49 +- src/presenters/modes/select.rs | 14 +- src/presenters/modes/select_line.rs | 12 +- src/util/mod.rs | 61 +- src/util/movement_lexer.rs | 180 ++-- src/util/reflow.rs | 166 +-- src/util/token.rs | 27 +- src/view/buffer/lexeme_mapper.rs | 2 +- src/view/buffer/line_numbers.rs | 18 +- src/view/buffer/mod.rs | 12 +- src/view/buffer/render_cache.rs | 2 +- src/view/buffer/render_state.rs | 8 +- src/view/buffer/renderer.rs | 251 +++-- src/view/buffer/scrollable_region.rs | 44 +- src/view/color/colors.rs | 21 +- src/view/color/map.rs | 32 +- src/view/event_listener.rs | 23 +- src/view/mod.rs | 91 +- src/view/presenter.rs | 146 +-- src/view/style.rs | 5 +- src/view/terminal/buffer.rs | 26 +- src/view/terminal/buffer_iterator.rs | 126 ++- src/view/terminal/cell.rs | 4 +- src/view/terminal/mod.rs | 6 +- src/view/terminal/termion_terminal.rs | 130 ++- src/view/terminal/test_terminal.rs | 63 +- src/view/theme_loader.rs | 25 +- 74 files changed, 2905 insertions(+), 2097 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c1edecb3..e8c56128 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,3 +18,9 @@ jobs: run: cargo build --verbose - name: Run tests run: cargo test --verbose + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: rustfmt + run: rustfmt --check src/**/*.rs diff --git a/benches/view/draw_buffer.rs b/benches/view/draw_buffer.rs index e5301d32..2baefb25 100644 --- a/benches/view/draw_buffer.rs +++ b/benches/view/draw_buffer.rs @@ -7,48 +7,67 @@ use std::path::PathBuf; fn buffer_rendering(c: &mut Criterion) { let mut app = Application::new(&Vec::new()).unwrap(); - app.workspace.open_buffer( - &PathBuf::from("src/commands/buffer.rs") - ).unwrap(); - app.view.initialize_buffer(app.workspace.current_buffer.as_mut().unwrap()).unwrap(); + app.workspace + .open_buffer(&PathBuf::from("src/commands/buffer.rs")) + .unwrap(); + app.view + .initialize_buffer(app.workspace.current_buffer.as_mut().unwrap()) + .unwrap(); let buffer_data = app.workspace.current_buffer.as_ref().unwrap().data(); - c.bench_function("buffer rendering", move |b| b.iter(|| { - let mut presenter = app.view.build_presenter().unwrap(); - - presenter.print_buffer( - app.workspace.current_buffer.as_ref().unwrap(), - &buffer_data, - &app.workspace.syntax_set, - None, - None - ).unwrap() - })); + c.bench_function("buffer rendering", move |b| { + b.iter(|| { + let mut presenter = app.view.build_presenter().unwrap(); + + presenter + .print_buffer( + app.workspace.current_buffer.as_ref().unwrap(), + &buffer_data, + &app.workspace.syntax_set, + None, + None, + ) + .unwrap() + }) + }); } fn scrolled_buffer_rendering(c: &mut Criterion) { let mut app = Application::new(&Vec::new()).unwrap(); - app.workspace.open_buffer( - &PathBuf::from("src/commands/buffer.rs") - ).unwrap(); - app.view.initialize_buffer(app.workspace.current_buffer.as_mut().unwrap()).unwrap(); + app.workspace + .open_buffer(&PathBuf::from("src/commands/buffer.rs")) + .unwrap(); + app.view + .initialize_buffer(app.workspace.current_buffer.as_mut().unwrap()) + .unwrap(); let buffer_data = app.workspace.current_buffer.as_ref().unwrap().data(); // Scroll to the bottom of the buffer. - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to_last_line(); - app.view.scroll_to_cursor(app.workspace.current_buffer.as_ref().unwrap()).unwrap(); - - c.bench_function("scrolled buffer rendering", move |b| b.iter(|| { - let mut presenter = app.view.build_presenter().unwrap(); - - presenter.print_buffer( - app.workspace.current_buffer.as_ref().unwrap(), - &buffer_data, - &app.workspace.syntax_set, - None, - None - ).unwrap() - })); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to_last_line(); + app.view + .scroll_to_cursor(app.workspace.current_buffer.as_ref().unwrap()) + .unwrap(); + + c.bench_function("scrolled buffer rendering", move |b| { + b.iter(|| { + let mut presenter = app.view.build_presenter().unwrap(); + + presenter + .print_buffer( + app.workspace.current_buffer.as_ref().unwrap(), + &buffer_data, + &app.workspace.syntax_set, + None, + None, + ) + .unwrap() + }) + }); } criterion_group!(benches, buffer_rendering, scrolled_buffer_rendering); diff --git a/build.rs b/build.rs index bb3f5b0a..601d32fa 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,11 @@ use regex::Regex; use std::env; -use std::fs::{self, File, read_to_string}; +use std::fs::{self, read_to_string, File}; use std::io::Write; use std::path::Path; use std::result::Result; -const COMMAND_REGEX: &str = - r"pub fn (.*)\(app: &mut Application\) -> Result"; +const COMMAND_REGEX: &str = r"pub fn (.*)\(app: &mut Application\) -> Result"; fn main() { generate_commands(); @@ -27,27 +26,29 @@ fn create_output_file() -> Result { let out_dir = env::var("OUT_DIR").expect("The compiler did not provide $OUT_DIR"); let out_file: std::path::PathBuf = [&out_dir, "hash_map"].iter().collect(); let mut file = File::create(&out_file).map_err(|_| { - format!("Couldn't create output file: {}", out_file.to_string_lossy()) + format!( + "Couldn't create output file: {}", + out_file.to_string_lossy() + ) })?; - file - .write("{\n let mut commands: HashMap<&'static str, Command> = HashMap::new();\n" - .as_bytes()) - .map_err(|_| "Failed to write command hash init")?; + file.write( + "{\n let mut commands: HashMap<&'static str, Command> = HashMap::new();\n".as_bytes(), + ) + .map_err(|_| "Failed to write command hash init")?; Ok(file) } fn write_commands(output: &mut File) -> Result<(), &str> { - let expression = Regex::new(COMMAND_REGEX) - .expect("Failed to compile command matching regex"); - let entries = fs::read_dir("./src/commands/") - .map_err(|_| "Failed to read command module directory")?; + let expression = Regex::new(COMMAND_REGEX).expect("Failed to compile command matching regex"); + let entries = + fs::read_dir("./src/commands/").map_err(|_| "Failed to read command module directory")?; for entry in entries { let path = entry - .map_err(|_| "Failed to read command module directory entry")?.path(); + .map_err(|_| "Failed to read command module directory entry")? + .path(); let module_name = module_name(&path).unwrap(); - let content = read_to_string(&path) - .map_err(|_| "Failed to read command module data")?; + let content = read_to_string(&path).map_err(|_| "Failed to read command module data")?; for captures in expression.captures_iter(&content) { let function_name = captures.get(1).unwrap().as_str(); write_command(output, &module_name, function_name)?; @@ -57,25 +58,35 @@ fn write_commands(output: &mut File) -> Result<(), &str> { Ok(()) } -fn write_command(output: &mut File, module_name: &str, function_name: &str) -> Result { - output.write( - format!( - " commands.insert(\"{}::{}\", {}::{});\n", - module_name, - function_name, - module_name, - function_name - ).as_bytes() - ).map_err(|_| "Failed to write command") +fn write_command( + output: &mut File, + module_name: &str, + function_name: &str, +) -> Result { + output + .write( + format!( + " commands.insert(\"{}::{}\", {}::{});\n", + module_name, function_name, module_name, function_name + ) + .as_bytes(), + ) + .map_err(|_| "Failed to write command") } fn finalize_output_file(output: &mut File) -> Result { - output.write(" commands\n}\n".as_bytes()) + output + .write(" commands\n}\n".as_bytes()) .map_err(|_| "Failed to write command hash return") } fn module_name(path: &Path) -> Result { - path.file_name().and_then(|name| { - name.to_string_lossy().split('.').next().map(|n| n.to_string()) - }).ok_or("Unable to parse command module from file name") + path.file_name() + .and_then(|name| { + name.to_string_lossy() + .split('.') + .next() + .map(|n| n.to_string()) + }) + .ok_or("Unable to parse command module from file name") } diff --git a/src/commands/application.rs b/src/commands/application.rs index ce092e3f..a6a22251 100644 --- a/src/commands/application.rs +++ b/src/commands/application.rs @@ -1,18 +1,17 @@ -use crate::errors::*; use crate::commands::{self, Result}; +use crate::errors::*; use crate::input::KeyMap; -use scribe::Buffer; -use std::mem; -use crate::models::application::{Application, Mode}; use crate::models::application::modes::*; +use crate::models::application::{Application, Mode}; use crate::util; +use scribe::Buffer; +use std::mem; pub fn handle_input(app: &mut Application) -> Result { // Listen for and respond to user input. let commands = app.view.last_key().as_ref().and_then(|key| { - app.mode_str().and_then(|mode| { - app.preferences.borrow().keymap().commands_for(mode, key) - }) + app.mode_str() + .and_then(|mode| app.preferences.borrow().keymap().commands_for(mode, key)) }); if let Some(coms) = commands { @@ -45,7 +44,11 @@ pub fn switch_to_insert_mode(app: &mut Application) -> Result { } pub fn switch_to_jump_mode(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; // Initialize a new jump mode and swap // it with the current application mode. @@ -97,7 +100,12 @@ pub fn switch_to_line_jump_mode(app: &mut Application) -> Result { pub fn switch_to_open_mode(app: &mut Application) -> Result { let exclusions = app.preferences.borrow().open_mode_exclusions()?; let config = app.preferences.borrow().search_select_config(); - app.mode = Mode::Open(OpenMode::new(app.workspace.path.clone(), exclusions, app.event_channel.clone(), config)); + app.mode = Mode::Open(OpenMode::new( + app.workspace.path.clone(), + exclusions, + app.event_channel.clone(), + config, + )); commands::search_select::search(app)?; Ok(()) @@ -112,7 +120,9 @@ pub fn switch_to_command_mode(app: &mut Application) -> Result { } pub fn switch_to_symbol_jump_mode(app: &mut Application) -> Result { - let token_set = app.workspace.current_buffer_tokens() + let token_set = app + .workspace + .current_buffer_tokens() .chain_err(|| BUFFER_TOKENS_FAILED)?; let config = app.preferences.borrow().search_select_config(); @@ -125,12 +135,15 @@ pub fn switch_to_symbol_jump_mode(app: &mut Application) -> Result { pub fn switch_to_theme_mode(app: &mut Application) -> Result { let config = app.preferences.borrow().search_select_config(); - app.mode = Mode::Theme( - ThemeMode::new( - app.view.theme_set.themes.keys().map(|k| k.to_string()).collect(), - config - ), - ); + app.mode = Mode::Theme(ThemeMode::new( + app.view + .theme_set + .themes + .keys() + .map(|k| k.to_string()) + .collect(), + config, + )); commands::search_select::search(app)?; Ok(()) @@ -158,9 +171,7 @@ pub fn switch_to_select_line_mode(app: &mut Application) -> Result { pub fn switch_to_search_mode(app: &mut Application) -> Result { if app.workspace.current_buffer.is_some() { - app.mode = Mode::Search( - SearchMode::new(app.search_query.clone()) - ); + app.mode = Mode::Search(SearchMode::new(app.search_query.clone())); } else { bail!(BUFFER_MISSING); } @@ -169,20 +180,20 @@ pub fn switch_to_search_mode(app: &mut Application) -> Result { } pub fn switch_to_path_mode(app: &mut Application) -> Result { - let path = app.workspace + let path = app + .workspace .current_buffer .as_ref() .ok_or(BUFFER_MISSING)? - .path.as_ref().map(|p| + .path + .as_ref() + .map(|p| // The buffer has a path; use it. - p.to_string_lossy().into_owned() - ).unwrap_or_else(|| + p.to_string_lossy().into_owned()) + .unwrap_or_else(|| // Default to the workspace directory. - format!("{}/", app.workspace.path.to_string_lossy()) - ); - app.mode = Mode::Path( - PathMode::new(path) - ); + format!("{}/", app.workspace.path.to_string_lossy())); + app.mode = Mode::Path(PathMode::new(path)); Ok(()) } @@ -190,18 +201,22 @@ pub fn switch_to_path_mode(app: &mut Application) -> Result { pub fn switch_to_syntax_mode(app: &mut Application) -> Result { // We'll need a buffer to apply the syntax, // so check before entering syntax mode. - let _ = app.workspace + let _ = app + .workspace .current_buffer .as_ref() .ok_or("Switching syntaxes requires an open buffer")?; let config = app.preferences.borrow().search_select_config(); - app.mode = Mode::Syntax( - SyntaxMode::new( - app.workspace.syntax_set.syntaxes().iter().map(|syntax| syntax.name.clone()).collect(), - config - ), - ); + app.mode = Mode::Syntax(SyntaxMode::new( + app.workspace + .syntax_set + .syntaxes() + .iter() + .map(|syntax| syntax.name.clone()) + .collect(), + config, + )); commands::search_select::search(app)?; Ok(()) @@ -248,15 +263,11 @@ pub fn display_last_error(app: &mut Application) -> Result { let scope_display_buffer = { let mut error_buffer = Buffer::new(); // Add the proximate/contextual error. - error_buffer.insert( - format!("{}\n", error) - ); + error_buffer.insert(format!("{}\n", error)); // Print the chain of other errors that led to the proximate error. for err in error.iter().skip(1) { - error_buffer.insert( - format!("caused by: {}", err) - ); + error_buffer.insert(format!("caused by: {}", err)); } error_buffer @@ -278,8 +289,8 @@ pub fn exit(app: &mut Application) -> Result { #[cfg(test)] mod tests { - use crate::models::Application; use crate::models::application::Mode; + use crate::models::Application; use scribe::Buffer; use std::path::PathBuf; @@ -298,7 +309,10 @@ mod tests { let buffer_data = app.workspace.current_buffer.as_ref().unwrap().data(); let mut lines = buffer_data.lines(); - assert_eq!(lines.nth(0), Some("application::display_available_commands")); + assert_eq!( + lines.nth(0), + Some("application::display_available_commands") + ); assert_eq!(lines.last(), Some("workspace::next_buffer")); } @@ -317,10 +331,7 @@ mod tests { Mode::Search(ref mode) => mode.input.clone(), _ => None, }; - assert_eq!( - mode_query, - Some(String::from("query")) - ); + assert_eq!(mode_query, Some(String::from("query"))); } #[test] @@ -355,10 +366,7 @@ mod tests { Mode::Path(ref mode) => Some(mode.input.clone()), _ => None, }; - assert_eq!( - mode_input, - Some(absolute_path) - ); + assert_eq!(mode_input, Some(absolute_path)); } #[test] diff --git a/src/commands/buffer.rs b/src/commands/buffer.rs index b6e66e0f..9da031ad 100644 --- a/src/commands/buffer.rs +++ b/src/commands/buffer.rs @@ -1,14 +1,14 @@ -use crate::errors::*; use crate::commands::{self, Result}; -use std::io::Write; -use std::mem; -use std::process::Stdio; +use crate::errors::*; use crate::input::Key; -use crate::util; -use crate::util::token::{Direction, adjacent_token_position}; -use crate::models::application::{Application, ClipboardContent, Mode}; use crate::models::application::modes::ConfirmMode; +use crate::models::application::{Application, ClipboardContent, Mode}; +use crate::util; +use crate::util::token::{adjacent_token_position, Direction}; use scribe::buffer::{Buffer, Position, Range, Token}; +use std::io::Write; +use std::mem; +use std::process::Stdio; pub fn save(app: &mut Application) -> Result { remove_trailing_whitespace(app)?; @@ -19,7 +19,8 @@ pub fn save(app: &mut Application) -> Result { .current_buffer .as_ref() .ok_or(BUFFER_MISSING)? - .path.clone(); // clone instead of borrow as we call another command later + .path + .clone(); // clone instead of borrow as we call another command later if path.is_some() { // Save the buffer. @@ -56,13 +57,20 @@ pub fn save(app: &mut Application) -> Result { } pub fn reload(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.reload().chain_err(|| { - BUFFER_RELOAD_FAILED - }) + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .reload() + .chain_err(|| BUFFER_RELOAD_FAILED) } pub fn delete(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.delete(); + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .delete(); commands::view::scroll_to_cursor(app)?; Ok(()) @@ -113,27 +121,34 @@ pub fn copy_current_line(app: &mut Application) -> Result { } pub fn merge_next_line(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let current_line = buffer.cursor.line; let data = buffer.data(); // Don't bother if there isn't a line below. - data.lines().nth(current_line + 1).ok_or("No line below current line")?; + data.lines() + .nth(current_line + 1) + .ok_or("No line below current line")?; // Join the two lines. - let mut merged_lines: String = buffer.data() - .lines() - .enumerate() - .skip(current_line) - .take(2) - .map(|(index, line)| { - if index == current_line { - format!("{} ", line) - } else { - line.trim_start().to_string() - } - }) - .collect(); + let mut merged_lines: String = buffer + .data() + .lines() + .enumerate() + .skip(current_line) + .take(2) + .map(|(index, line)| { + if index == current_line { + format!("{} ", line) + } else { + line.trim_start().to_string() + } + }) + .collect(); // Append a newline if there is a line below the next. if buffer.data().lines().nth(current_line + 2).is_some() { @@ -148,14 +163,16 @@ pub fn merge_next_line(app: &mut Application) -> Result { line: current_line, offset: data.lines().nth(current_line).unwrap().len(), }; - buffer.delete_range(Range::new(Position { - line: current_line, - offset: 0, - }, - Position { - line: current_line + 2, - offset: 0, - })); + buffer.delete_range(Range::new( + Position { + line: current_line, + offset: 0, + }, + Position { + line: current_line + 2, + offset: 0, + }, + )); buffer.cursor.move_to(Position { line: current_line, offset: 0, @@ -169,18 +186,20 @@ pub fn merge_next_line(app: &mut Application) -> Result { pub fn close(app: &mut Application) -> Result { // Build confirmation check conditions. - let (unmodified, empty) = - if let Some(buf) = app.workspace.current_buffer.as_ref() { - (!buf.modified(), buf.data().is_empty()) - } else { - bail!(BUFFER_MISSING); - }; + let (unmodified, empty) = if let Some(buf) = app.workspace.current_buffer.as_ref() { + (!buf.modified(), buf.data().is_empty()) + } else { + bail!(BUFFER_MISSING); + }; let confirm_mode = matches!(app.mode, Mode::Confirm(_)); if unmodified || empty || confirm_mode { // Clean up view-related data for the buffer. app.view.forget_buffer( - app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)? + app.workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?, )?; app.workspace.close_current_buffer(); } else { @@ -194,7 +213,12 @@ pub fn close(app: &mut Application) -> Result { pub fn close_others(app: &mut Application) -> Result { // Get the current buffer's ID so we know what *not* to close. - let id = app.workspace.current_buffer.as_ref().map(|b| b.id).ok_or(BUFFER_MISSING)?; + let id = app + .workspace + .current_buffer + .as_ref() + .map(|b| b.id) + .ok_or(BUFFER_MISSING)?; let mut modified_buffer = false; loop { @@ -299,10 +323,15 @@ pub fn insert_char(app: &mut Application) -> Result { pub fn display_current_scope(app: &mut Application) -> Result { let scope_display_buffer = { let mut scope_stack = None; - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; - let tokens = app.workspace.current_buffer_tokens().chain_err(|| { - BUFFER_TOKENS_FAILED - })?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; + let tokens = app + .workspace + .current_buffer_tokens() + .chain_err(|| BUFFER_TOKENS_FAILED)?; let mut token_iter = tokens.iter().chain_err(|| BUFFER_PARSE_FAILED)?; // Build the scope up to the cursor location. @@ -324,9 +353,7 @@ pub fn display_current_scope(app: &mut Application) -> Result { // Open a buffer with a displayable version of the scope stack. let mut scope_display_buffer = Buffer::new(); for scope in scope_stack.iter() { - scope_display_buffer.insert( - format!("{}\n", scope) - ); + scope_display_buffer.insert(format!("{}\n", scope)); } scope_display_buffer @@ -380,16 +407,18 @@ pub fn insert_newline(app: &mut Application) -> Result { } pub fn indent_line(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let tab_content = app.preferences.borrow().tab_content(buffer.path.as_ref()); let target_position = match app.mode { - Mode::Insert => { - Position { - line: buffer.cursor.line, - offset: buffer.cursor.offset + tab_content.chars().count(), - } - } + Mode::Insert => Position { + line: buffer.cursor.line, + offset: buffer.cursor.offset + tab_content.chars().count(), + }, _ => *buffer.cursor.clone(), }; @@ -410,10 +439,7 @@ pub fn indent_line(app: &mut Application) -> Result { // insert the content, as a single operation. buffer.start_operation_group(); for line in lines { - buffer.cursor.move_to(Position { - line, - offset: 0, - }); + buffer.cursor.move_to(Position { line, offset: 0 }); buffer.insert(tab_content.clone()); } buffer.end_operation_group(); @@ -425,7 +451,11 @@ pub fn indent_line(app: &mut Application) -> Result { } pub fn outdent_line(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let tab_content = app.preferences.borrow().tab_content(buffer.path.as_ref()); // FIXME: Determine this based on file type and/or user config. @@ -472,19 +502,16 @@ pub fn outdent_line(app: &mut Application) -> Result { // Remove leading whitespace, up to indent size, // if we found any, and adjust cursor accordingly. if space_char_count > 0 { - buffer.delete_range(Range::new(Position { - line, - offset: 0, - }, - Position { - line, - offset: space_char_count, - })); + buffer.delete_range(Range::new( + Position { line, offset: 0 }, + Position { + line, + offset: space_char_count, + }, + )); // Figure out where the cursor should sit, guarding against underflow. - let target_offset = buffer.cursor - .offset - .saturating_sub(space_char_count); + let target_offset = buffer.cursor.offset.saturating_sub(space_char_count); let target_line = buffer.cursor.line; buffer.cursor.move_to(Position { @@ -502,12 +529,19 @@ pub fn outdent_line(app: &mut Application) -> Result { } pub fn toggle_line_comment(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let original_cursor = *buffer.cursor.clone(); let comment_prefix = { let path = buffer.path.as_ref().ok_or(BUFFER_PATH_MISSING)?; - let prefix = app.preferences.borrow().line_comment_prefix(path) + let prefix = app + .preferences + .borrow() + .line_comment_prefix(path) .ok_or("No line comment prefix for the current buffer")?; prefix + " " // implicitly add trailing space @@ -527,8 +561,14 @@ pub fn toggle_line_comment(app: &mut Application) -> Result { }; let buffer_range = Range::new( - Position { line: line_numbers.start, offset: 0 }, - Position { line: line_numbers.end, offset: 0 } + Position { + line: line_numbers.start, + offset: 0, + }, + Position { + line: line_numbers.end, + offset: 0, + }, ); let buffer_range_content = buffer.read(&buffer_range).ok_or(CURRENT_LINE_MISSING)?; @@ -536,25 +576,32 @@ pub fn toggle_line_comment(app: &mut Application) -> Result { // Produce a collection of (, ) tuples, but only for // non-empty lines. let lines: Vec<(usize, &str)> = line_numbers - .zip(buffer_range_content.split('\n')) // produces (, ) + .zip(buffer_range_content.split('\n')) // produces (, ) .filter(|(_, line)| !line.trim().is_empty()) // filter out any empty (non-whitespace-only) lines .collect(); // We look at all lines to see if they start with `comment_prefix` or not. // If even a single line does not, we need to comment all lines out, // otherwise remove `comment_prefix` on the start of each line. - let (toggle, offset) = lines.iter() + let (toggle, offset) = lines + .iter() // Map (, ) to (, ) .map(|(_, line)| { let content = line.trim_start(); - (content.starts_with(&comment_prefix), line.len() - content.len()) + ( + content.starts_with(&comment_prefix), + line.len() - content.len(), + ) }) // Now fold it into a single (, ) tuple. // As soon as is `false` a single time, // will result in `false`. - .fold((true, std::usize::MAX), |(folded_toggle, folded_offset), (has_comment, offset)| { - (folded_toggle & has_comment, folded_offset.min(offset)) - }); + .fold( + (true, std::usize::MAX), + |(folded_toggle, folded_offset), (has_comment, offset)| { + (folded_toggle & has_comment, folded_offset.min(offset)) + }, + ); // Move to the start of each of the line's content and // insert/remove the comments, as a single operation. @@ -574,7 +621,10 @@ pub fn toggle_line_comment(app: &mut Application) -> Result { fn add_line_comment(buffer: &mut Buffer, lines: &[(usize, &str)], offset: usize, prefix: &str) { for (line_number, _) in lines { - let target = Position { line: *line_number, offset }; + let target = Position { + line: *line_number, + offset, + }; buffer.cursor.move_to(target); buffer.insert(prefix); @@ -605,18 +655,24 @@ pub fn change_token(app: &mut Application) -> Result { } pub fn delete_rest_of_line(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; // Create a range extending from the // cursor's current position to the next line. let starting_position = *buffer.cursor; let target_line = buffer.cursor.line + 1; buffer.start_operation_group(); - buffer.delete_range(Range::new(starting_position, - Position { - line: target_line, - offset: 0, - })); + buffer.delete_range(Range::new( + starting_position, + Position { + line: target_line, + offset: 0, + }, + )); // Since we've removed a newline as part of the range, re-add it. buffer.insert("\n"); @@ -652,25 +708,28 @@ pub fn end_command_group(app: &mut Application) -> Result { } pub fn undo(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.undo(); - commands::view::scroll_to_cursor(app).chain_err(|| { - "Couldn't scroll to cursor after undoing." - }) + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .undo(); + commands::view::scroll_to_cursor(app).chain_err(|| "Couldn't scroll to cursor after undoing.") } pub fn redo(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.redo(); - commands::view::scroll_to_cursor(app).chain_err(|| { - "Couldn't scroll to cursor after redoing." - }) + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .redo(); + commands::view::scroll_to_cursor(app).chain_err(|| "Couldn't scroll to cursor after redoing.") } pub fn paste(app: &mut Application) -> Result { let insert_below = match app.mode { Mode::Select(_) | Mode::SelectLine(_) | Mode::Search(_) => { - commands::selection::delete(app).chain_err(|| { - "Couldn't delete selection prior to pasting." - })?; + commands::selection::delete(app) + .chain_err(|| "Couldn't delete selection prior to pasting.")?; false } _ => true, @@ -723,7 +782,11 @@ pub fn paste(app: &mut Application) -> Result { } pub fn paste_above(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; if let ClipboardContent::Block(ref content) = *app.clipboard.get_content() { let mut start_of_line = Position { @@ -742,7 +805,11 @@ pub fn paste_above(app: &mut Application) -> Result { } pub fn remove_trailing_whitespace(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let mut line = 0; let mut offset = 0; let mut space_count = 0; @@ -752,14 +819,13 @@ pub fn remove_trailing_whitespace(app: &mut Application) -> Result { if character == '\n' { if space_count > 0 { // We've found some trailing whitespace; track it. - ranges.push(Range::new(Position { - line, - offset: offset - space_count, - }, - Position { - line, - offset, - })); + ranges.push(Range::new( + Position { + line, + offset: offset - space_count, + }, + Position { line, offset }, + )); } // We've hit a newline, so increase the line @@ -783,14 +849,13 @@ pub fn remove_trailing_whitespace(app: &mut Application) -> Result { // The file may not have a trailing newline. If there is // any trailing whitespace on the last line, track it. if space_count > 0 { - ranges.push(Range::new(Position { - line, - offset: offset - space_count, - }, - Position { - line, - offset, - })); + ranges.push(Range::new( + Position { + line, + offset: offset - space_count, + }, + Position { line, offset }, + )); } // Step through the whitespace ranges in reverse order @@ -805,12 +870,17 @@ pub fn remove_trailing_whitespace(app: &mut Application) -> Result { } pub fn ensure_trailing_newline(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; // Find end of buffer position. let data = buffer.data(); if let Some(c) = data.chars().last() { - if c != '\n' { // There's no pre-existing trailing newline. + if c != '\n' { + // There's no pre-existing trailing newline. let (line_no, line) = data .lines() .enumerate() @@ -837,7 +907,11 @@ pub fn ensure_trailing_newline(app: &mut Application) -> Result { } pub fn insert_tab(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let tab_content = app.preferences.borrow().tab_content(buffer.path.as_ref()); let tab_content_width = tab_content.chars().count(); buffer.insert(tab_content.clone()); @@ -851,13 +925,18 @@ pub fn insert_tab(app: &mut Application) -> Result { } pub fn format(app: &mut Application) -> Result { - let buf = app.workspace + let buf = app + .workspace .current_buffer .as_mut() .ok_or(BUFFER_MISSING)?; let path = buf.path.as_ref().ok_or(BUFFER_PATH_MISSING)?; - let mut format_command = app.preferences.borrow().format_command(path).ok_or(FORMAT_TOOL_MISSING)?; + let mut format_command = app + .preferences + .borrow() + .format_command(path) + .ok_or(FORMAT_TOOL_MISSING)?; let data = buf.data(); // Run the command with the buffer path as an argument. @@ -869,14 +948,19 @@ pub fn format(app: &mut Application) -> Result { let mut format_input = process.stdin.take().chain_err(|| "Failed to open stdin")?; std::thread::spawn(move || { - format_input.write_all(data.as_bytes()).expect("Failed to write to stdin"); + format_input + .write_all(data.as_bytes()) + .expect("Failed to write to stdin"); }); - let output = process.wait_with_output().chain_err(|| "Failed to read stdout")?; + let output = process + .wait_with_output() + .chain_err(|| "Failed to read stdout")?; // Reload buffer or propagate errors. if output.status.success() { - let content = String::from_utf8(output.stdout).chain_err(|| "Failed to parse format tool output as UTF8")?; + let content = String::from_utf8(output.stdout) + .chain_err(|| "Failed to parse format tool output as UTF8")?; buf.replace(content); Ok(()) @@ -884,17 +968,18 @@ pub fn format(app: &mut Application) -> Result { let error = String::from_utf8(output.stderr) .unwrap_or(String::from("Failed to parse stderr output as UTF8")); - Err(Error::from(error)).chain_err(|| format!("Format tool failed with code {}", output.status)) + Err(Error::from(error)) + .chain_err(|| format!("Format tool failed with code {}", output.status)) } } #[cfg(test)] mod tests { use crate::commands; - use crate::models::Application; use crate::models::application::{ClipboardContent, Mode, Preferences}; - use scribe::Buffer; + use crate::models::Application; use scribe::buffer::Position; + use scribe::Buffer; use std::env; use std::fs::File; use std::io::Write; @@ -908,10 +993,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert(" amp"); - let position = Position { - line: 0, - offset: 7, - }; + let position = Position { line: 0, offset: 7 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it @@ -920,18 +1002,21 @@ mod tests { super::insert_newline(&mut app).unwrap(); // Ensure that the whitespace is inserted. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\n "); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\n " + ); // Also ensure that the cursor is moved to the end of the inserted whitespace. - let expected_position = Position { - line: 1, - offset: 4, - }; - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.line, - expected_position.line); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.offset, - expected_position.offset); + let expected_position = Position { line: 1, offset: 4 }; + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().cursor.line, + expected_position.line + ); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().cursor.offset, + expected_position.offset + ); } #[test] @@ -974,10 +1059,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert(" amp\neditor"); - let position = Position { - line: 0, - offset: 4, - }; + let position = Position { line: 0, offset: 4 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it @@ -986,8 +1068,10 @@ mod tests { super::change_rest_of_line(&mut app).unwrap(); // Ensure that the content is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " \neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " \neditor" + ); // Ensure that we're in insert mode. assert!(match app.mode { @@ -998,8 +1082,10 @@ mod tests { // Ensure that sub-commands and subsequent inserts are run in batch. app.workspace.current_buffer.as_mut().unwrap().insert(" "); app.workspace.current_buffer.as_mut().unwrap().undo(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\neditor" + ); } #[test] @@ -1014,7 +1100,10 @@ mod tests { super::delete_token(&mut app).unwrap(); // Ensure that the content is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "editor" + ); } #[test] @@ -1029,7 +1118,10 @@ mod tests { super::delete_token(&mut app).unwrap(); // Ensure that the content is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\neditor" + ); } #[test] @@ -1039,10 +1131,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert(" amp\neditor"); - let position = Position { - line: 0, - offset: 4, - }; + let position = Position { line: 0, offset: 4 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it @@ -1051,7 +1140,10 @@ mod tests { super::delete_current_line(&mut app).unwrap(); // Ensure that the content is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "editor" + ); } #[test] @@ -1059,10 +1151,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor"); - buffer.cursor.move_to(Position { - line: 1, - offset: 2, - }); + buffer.cursor.move_to(Position { line: 1, offset: 2 }); // Now that we've set up the buffer, add it // to the application and call the command. @@ -1070,8 +1159,10 @@ mod tests { super::indent_line(&mut app).unwrap(); // Ensure that the content is inserted correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\n editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\n editor" + ); } #[test] @@ -1088,8 +1179,10 @@ mod tests { super::indent_line(&mut app).unwrap(); // Ensure that the content is inserted correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\n editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\n editor" + ); } #[test] @@ -1097,10 +1190,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor"); - buffer.cursor.move_to(Position { - line: 1, - offset: 2, - }); + buffer.cursor.move_to(Position { line: 1, offset: 2 }); // Now that we've set up the buffer, add it // to the application and call the command. @@ -1109,11 +1199,10 @@ mod tests { super::indent_line(&mut app).unwrap(); // Ensure that the cursor is updated. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 4, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 4 } + ); } #[test] @@ -1121,10 +1210,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor"); - buffer.cursor.move_to(Position { - line: 1, - offset: 2, - }); + buffer.cursor.move_to(Position { line: 1, offset: 2 }); // Now that we've set up the buffer, add it // to the application and call the command. @@ -1132,11 +1218,10 @@ mod tests { super::indent_line(&mut app).unwrap(); // Ensure that the cursor is not updated. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 2, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 2 } + ); } #[test] @@ -1153,13 +1238,17 @@ mod tests { super::indent_line(&mut app).unwrap(); // Ensure that the indentation is applied correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\n editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\n editor" + ); // Undo the indent and check that it's treated as one operation. super::undo(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\n editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\n editor" + ); } #[test] @@ -1177,8 +1266,10 @@ mod tests { super::indent_line(&mut app).unwrap(); // Ensure that the indentation is applied correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\n editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\n editor" + ); } #[test] @@ -1186,10 +1277,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\n editor"); - buffer.cursor.move_to(Position { - line: 1, - offset: 6, - }); + buffer.cursor.move_to(Position { line: 1, offset: 6 }); // Now that we've set up the buffer, add it // to the application and call the command. @@ -1197,27 +1285,24 @@ mod tests { super::outdent_line(&mut app).unwrap(); // Ensure that the content is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor" + ); // Ensure that the cursor is updated. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 4, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 4 } + ); } #[test] - fn outdent_line_removes_as_much_space_as_it_can_from_start_of_line_if_less_than_full_indent - () { + fn outdent_line_removes_as_much_space_as_it_can_from_start_of_line_if_less_than_full_indent() { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\n editor"); - buffer.cursor.move_to(Position { - line: 1, - offset: 2, - }); + buffer.cursor.move_to(Position { line: 1, offset: 2 }); // Now that we've set up the buffer, add it // to the application and call the command. @@ -1225,8 +1310,10 @@ mod tests { super::outdent_line(&mut app).unwrap(); // Ensure that the content is inserted correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor" + ); } #[test] @@ -1243,8 +1330,10 @@ mod tests { super::outdent_line(&mut app).unwrap(); // Ensure that the content is inserted correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor "); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor " + ); } #[test] @@ -1261,8 +1350,10 @@ mod tests { super::outdent_line(&mut app).unwrap(); // Ensure that the content is inserted correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor" + ); } #[test] @@ -1279,13 +1370,17 @@ mod tests { super::outdent_line(&mut app).unwrap(); // Ensure that the indentation is applied correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor" + ); // Undo the outdent and check that it's treated as one operation. super::undo(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\n editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\n editor" + ); } #[test] @@ -1303,8 +1398,10 @@ mod tests { super::outdent_line(&mut app).unwrap(); // Ensure that the indentation is applied correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor" + ); } #[test] @@ -1319,8 +1416,10 @@ mod tests { super::remove_trailing_whitespace(&mut app).unwrap(); // Ensure that trailing whitespace is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - " amp\n\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + " amp\n\neditor" + ); } #[test] @@ -1335,8 +1434,10 @@ mod tests { super::remove_trailing_whitespace(&mut app).unwrap(); // Ensure that trailing whitespace is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\t\tamp\n\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\t\tamp\n\neditor" + ); } #[test] @@ -1351,8 +1452,10 @@ mod tests { super::save(&mut app).ok(); // Ensure that trailing whitespace is removed. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor\n"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor\n" + ); } #[test] @@ -1363,8 +1466,10 @@ mod tests { app.workspace.add_buffer(buffer); super::save(&mut app).ok(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\n∴ editor\n"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\n∴ editor\n" + ); } #[test] @@ -1406,13 +1511,16 @@ mod tests { fn save_does_not_run_format_tool_by_default() { // Set up the application with a format command. let mut app = Application::new(&Vec::new()).unwrap(); - let data = YamlLoader::load_from_str(" + let data = YamlLoader::load_from_str( + " types: rs: format_tool: command: tr options: ['a', 'b'] - ").unwrap(); + ", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); app.preferences.replace(preferences); @@ -1435,14 +1543,17 @@ mod tests { fn save_runs_format_command_when_configured() { // Set up the application with a format command. let mut app = Application::new(&Vec::new()).unwrap(); - let data = YamlLoader::load_from_str(" + let data = YamlLoader::load_from_str( + " types: rs: format_tool: command: tr options: ['a', 'b'] run_on_save: true - ").unwrap(); + ", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); app.preferences.replace(preferences); @@ -1461,7 +1572,12 @@ mod tests { ); // Ensure that format tool output was saved to disk. - app.workspace.current_buffer.as_mut().unwrap().reload().unwrap(); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .reload() + .unwrap(); assert_eq!( app.workspace.current_buffer.as_ref().unwrap().data(), "bmp editor\n" @@ -1484,8 +1600,10 @@ mod tests { commands::buffer::paste(&mut app).unwrap(); // Ensure that the clipboard contents are pasted to the line below. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "aamp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "aamp\neditor" + ); } #[test] @@ -1493,10 +1611,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor"); - buffer.cursor.move_to(Position { - line: 0, - offset: 2, - }); + buffer.cursor.move_to(Position { line: 0, offset: 2 }); // Now that we've set up the buffer, add it // to the application, copy the first line to @@ -1507,8 +1622,10 @@ mod tests { commands::buffer::paste(&mut app).unwrap(); // Ensure that the clipboard contents are pasted to the line below. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\namp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\namp\neditor" + ); } #[test] @@ -1516,10 +1633,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor"); - buffer.cursor.move_to(Position { - line: 0, - offset: 0, - }); + buffer.cursor.move_to(Position { line: 0, offset: 0 }); // Now that we've set up the buffer, add it // to the application, copy the first line to @@ -1531,8 +1645,10 @@ mod tests { commands::buffer::paste(&mut app).unwrap(); // Ensure that the clipboard contents are pasted to the line below. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor\namp\n"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor\namp\n" + ); } #[test] @@ -1540,10 +1656,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 0, - }); + buffer.cursor.move_to(Position { line: 0, offset: 0 }); // Now that we've set up the buffer, add it // to the application, copy the first line to @@ -1556,8 +1669,10 @@ mod tests { commands::buffer::paste(&mut app).unwrap(); // Ensure that the clipboard contents are pasted to the line below. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor\namp\n"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor\namp\n" + ); } #[test] @@ -1565,10 +1680,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor\n "); - buffer.cursor.move_to(Position { - line: 2, - offset: 8, - }); + buffer.cursor.move_to(Position { line: 2, offset: 8 }); // Now that we've set up the buffer, add it // to the application and run the command. @@ -1576,8 +1688,10 @@ mod tests { commands::buffer::backspace(&mut app).unwrap(); // Ensure that the clipboard contents are pasted to the line below. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor\n "); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor\n " + ); } #[test] @@ -1592,14 +1706,16 @@ mod tests { commands::buffer::merge_next_line(&mut app).unwrap(); // Ensure that the lines are merged correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "amp editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp editor" + ); // Ensure that the cursor is moved to the end of the current line. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 0, - offset: 3, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 0, offset: 3 } + ); } #[test] @@ -1614,14 +1730,16 @@ mod tests { commands::buffer::merge_next_line(&mut app).ok(); // Ensure that the lines are merged correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "amp editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp editor" + ); // Ensure that the cursor is moved to the end of the current line. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 0, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 0, offset: 0 } + ); } #[test] @@ -1636,8 +1754,10 @@ mod tests { commands::buffer::merge_next_line(&mut app).unwrap(); // Ensure that the lines are merged correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp editor\ntest"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp editor\ntest" + ); } #[test] @@ -1645,10 +1765,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\n amp\neditor"); - buffer.cursor.move_to(Position { - line: 1, - offset: 0, - }); + buffer.cursor.move_to(Position { line: 1, offset: 0 }); // Now that we've set up the buffer, add it // to the application and run the command. @@ -1656,8 +1773,10 @@ mod tests { commands::buffer::merge_next_line(&mut app).unwrap(); // Ensure that the lines are merged correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\n amp editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\n amp editor" + ); } #[test] @@ -1672,7 +1791,10 @@ mod tests { commands::buffer::merge_next_line(&mut app).unwrap(); // Ensure that the lines are merged correctly. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "amp editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp editor" + ); } #[test] @@ -1687,8 +1809,10 @@ mod tests { commands::buffer::ensure_trailing_newline(&mut app).unwrap(); // Ensure that trailing newline is added. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor\n"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor\n" + ); } #[test] @@ -1703,8 +1827,10 @@ mod tests { commands::buffer::ensure_trailing_newline(&mut app).unwrap(); // Ensure that trailing newline is added. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor\n"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor\n" + ); } #[test] @@ -1712,7 +1838,9 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp"); - app.clipboard.set_content(ClipboardContent::Inline("editor".to_string())).unwrap(); + app.clipboard + .set_content(ClipboardContent::Inline("editor".to_string())) + .unwrap(); // Now that we've set up the buffer, add it to // the application, select its contents, and paste. @@ -1722,7 +1850,10 @@ mod tests { commands::buffer::paste(&mut app).unwrap(); // Ensure that the content is replaced - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "editor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "editor" + ); // TODO: Ensure that the operation is treated atomically. // commands::buffer::undo(&mut app); @@ -1734,7 +1865,9 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor"); - app.clipboard.set_content(ClipboardContent::Block("paste amp\n".to_string())).unwrap(); + app.clipboard + .set_content(ClipboardContent::Block("paste amp\n".to_string())) + .unwrap(); // Now that we've set up the buffer, add it to // the application, select its contents, and paste. @@ -1743,8 +1876,10 @@ mod tests { commands::buffer::paste(&mut app).unwrap(); // Ensure that the content is replaced - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "paste amp\neditor"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "paste amp\neditor" + ); // TODO: Ensure that the operation is treated atomically. // commands::buffer::undo(&mut app); @@ -1755,23 +1890,26 @@ mod tests { fn paste_above_inserts_clipboard_contents_on_a_new_line_above() { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); - let original_position = Position { - line: 0, - offset: 3, - }; + let original_position = Position { line: 0, offset: 3 }; buffer.insert("editor"); buffer.cursor.move_to(original_position.clone()); - app.clipboard.set_content(ClipboardContent::Block("amp\n".to_string())).unwrap(); + app.clipboard + .set_content(ClipboardContent::Block("amp\n".to_string())) + .unwrap(); // Now that we've set up the buffer, // add it to the application and paste. app.workspace.add_buffer(buffer); commands::buffer::paste_above(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "amp\neditor"); - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - original_position); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "amp\neditor" + ); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + original_position + ); } #[test] @@ -1841,9 +1979,15 @@ mod tests { app.workspace.add_buffer(buffer_3); commands::buffer::close_others(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "three"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "three" + ); app.workspace.next_buffer(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "three"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "three" + ); } #[test] @@ -1894,9 +2038,15 @@ mod tests { // Run the command twice, to commands::buffer::close_others(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "three"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "three" + ); app.workspace.next_buffer(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), "three"); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "three" + ); } #[test] @@ -1905,9 +2055,9 @@ mod tests { let mut buffer_1 = Buffer::new(); let mut buffer_2 = Buffer::new(); let mut buffer_3 = Buffer::new(); - buffer_1.insert(""); // Empty to prevent close confirmation. + buffer_1.insert(""); // Empty to prevent close confirmation. buffer_2.insert("two"); - buffer_3.insert(""); // Empty to prevent close confirmation. + buffer_3.insert(""); // Empty to prevent close confirmation. // Now that we've set up the buffers, add // them to the application and run the command. @@ -1927,10 +2077,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\tamp\n\teditor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 1 - }); + buffer.cursor.move_to(Position { line: 0, offset: 1 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it @@ -1938,10 +2085,19 @@ mod tests { app.workspace.add_buffer(buffer); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\t// amp\n\teditor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 0, offset: 1 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\t// amp\n\teditor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 0, offset: 1 } + ); } #[test] @@ -1949,27 +2105,35 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\tamp\n\t\teditor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 1, - }); + buffer.cursor.move_to(Position { line: 0, offset: 1 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it // to the application and call the command. app.workspace.add_buffer(buffer); commands::application::switch_to_select_line_mode(&mut app).unwrap(); - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 1, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 1 }); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\t// amp\n\t// \teditor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 1, offset: 1 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\t// amp\n\t// \teditor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 1, offset: 1 } + ); } #[test] @@ -1977,10 +2141,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\t// amp\n\teditor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 1, - }); + buffer.cursor.move_to(Position { line: 0, offset: 1 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it @@ -1988,10 +2149,19 @@ mod tests { app.workspace.add_buffer(buffer); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\tamp\n\teditor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 0, offset: 1 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\tamp\n\teditor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 0, offset: 1 } + ); } #[test] @@ -1999,27 +2169,35 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\t// amp\n\t// \teditor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 1, - }); + buffer.cursor.move_to(Position { line: 0, offset: 1 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it // to the application and call the command. app.workspace.add_buffer(buffer); commands::application::switch_to_select_line_mode(&mut app).unwrap(); - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 1, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 1 }); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\tamp\n\t\teditor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 1, offset: 1 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\tamp\n\t\teditor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 1, offset: 1 } + ); } #[test] @@ -2027,27 +2205,35 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\t// amp\n\t\t// editor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 1, - }); + buffer.cursor.move_to(Position { line: 0, offset: 1 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it // to the application and call the command. app.workspace.add_buffer(buffer); commands::application::switch_to_select_line_mode(&mut app).unwrap(); - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 1, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 1 }); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\tamp\n\t\teditor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 1, offset: 1 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\tamp\n\t\teditor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 1, offset: 1 } + ); } #[test] @@ -2055,27 +2241,35 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\tamp\n\n\teditor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 0, - }); + buffer.cursor.move_to(Position { line: 0, offset: 0 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it // to the application and call the command. app.workspace.add_buffer(buffer); commands::application::switch_to_select_line_mode(&mut app).unwrap(); - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 2, - offset: 0, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 2, offset: 0 }); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\t// amp\n\n\t// editor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 2, offset: 0 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\t// amp\n\n\t// editor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 2, offset: 0 } + ); } #[test] @@ -2083,26 +2277,34 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("\t// amp\n\n\t// editor\n"); - buffer.cursor.move_to(Position { - line: 0, - offset: 0, - }); + buffer.cursor.move_to(Position { line: 0, offset: 0 }); buffer.path = Some("test.rs".into()); // Now that we've set up the buffer, add it // to the application and call the command. app.workspace.add_buffer(buffer); commands::application::switch_to_select_line_mode(&mut app).unwrap(); - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 2, - offset: 0, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 2, offset: 0 }); super::toggle_line_comment(&mut app).unwrap(); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().data(), - "\tamp\n\n\teditor\n"); - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.position, - Position { line: 2, offset: 0 }); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().data(), + "\tamp\n\n\teditor\n" + ); + assert_eq!( + app.workspace + .current_buffer + .as_ref() + .unwrap() + .cursor + .position, + Position { line: 2, offset: 0 } + ); } } diff --git a/src/commands/confirm.rs b/src/commands/confirm.rs index 791768cd..36f45b94 100644 --- a/src/commands/confirm.rs +++ b/src/commands/confirm.rs @@ -2,12 +2,11 @@ use crate::commands::{self, Result}; use crate::models::application::{Application, Mode}; pub fn confirm_command(app: &mut Application) -> Result { - let command = - if let Mode::Confirm(ref mode) = app.mode { - mode.command - } else { - bail!("Can't confirm command outside of confirm mode"); - }; + let command = if let Mode::Confirm(ref mode) = app.mode { + mode.command + } else { + bail!("Can't confirm command outside of confirm mode"); + }; command(app)?; commands::application::switch_to_normal_mode(app) diff --git a/src/commands/cursor.rs b/src/commands/cursor.rs index 49af7516..6ad18fbb 100644 --- a/src/commands/cursor.rs +++ b/src/commands/cursor.rs @@ -1,27 +1,47 @@ -use crate::errors::*; +use super::{application, buffer}; use crate::commands::{self, Result}; -use crate::util::token::{Direction, adjacent_token_position}; +use crate::errors::*; use crate::models::application::Application; +use crate::util::token::{adjacent_token_position, Direction}; use scribe::buffer::Position; -use super::{application, buffer}; pub fn move_up(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.cursor.move_up(); + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .cursor + .move_up(); commands::view::scroll_to_cursor(app).chain_err(|| SCROLL_TO_CURSOR_FAILED) } pub fn move_down(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.cursor.move_down(); + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .cursor + .move_down(); commands::view::scroll_to_cursor(app).chain_err(|| SCROLL_TO_CURSOR_FAILED) } pub fn move_left(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.cursor.move_left(); + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .cursor + .move_left(); commands::view::scroll_to_cursor(app).chain_err(|| SCROLL_TO_CURSOR_FAILED) } pub fn move_right(app: &mut Application) -> Result { - app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?.cursor.move_right(); + app.workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)? + .cursor + .move_right(); commands::view::scroll_to_cursor(app).chain_err(|| SCROLL_TO_CURSOR_FAILED) } @@ -151,11 +171,8 @@ pub fn insert_with_newline_above(app: &mut Application) -> Result { pub fn move_to_start_of_previous_token(app: &mut Application) -> Result { if let Some(buffer) = app.workspace.current_buffer.as_mut() { - let position = adjacent_token_position( - buffer, - false, - Direction::Backward - ).ok_or("Couldn't find previous token")?; + let position = adjacent_token_position(buffer, false, Direction::Backward) + .ok_or("Couldn't find previous token")?; buffer.cursor.move_to(position); } else { @@ -166,11 +183,8 @@ pub fn move_to_start_of_previous_token(app: &mut Application) -> Result { pub fn move_to_start_of_next_token(app: &mut Application) -> Result { if let Some(buffer) = app.workspace.current_buffer.as_mut() { - let position = adjacent_token_position( - buffer, - false, - Direction::Forward - ).ok_or("Couldn't find next token")?; + let position = adjacent_token_position(buffer, false, Direction::Forward) + .ok_or("Couldn't find next token")?; buffer.cursor.move_to(position); } else { @@ -181,11 +195,8 @@ pub fn move_to_start_of_next_token(app: &mut Application) -> Result { pub fn move_to_end_of_current_token(app: &mut Application) -> Result { if let Some(buffer) = app.workspace.current_buffer.as_mut() { - let position = adjacent_token_position( - buffer, - true, - Direction::Forward - ).ok_or("Couldn't find next token")?; + let position = adjacent_token_position(buffer, true, Direction::Forward) + .ok_or("Couldn't find next token")?; buffer.cursor.move_to(position); } else { @@ -201,9 +212,9 @@ pub fn append_to_current_token(app: &mut Application) -> Result { #[cfg(test)] mod tests { - use scribe::Buffer; - use scribe::buffer::Position; use crate::models::application::Application; + use scribe::buffer::Position; + use scribe::Buffer; #[test] fn move_to_first_word_of_line_works() { @@ -211,21 +222,22 @@ mod tests { let mut app = set_up_application(" amp"); // Move to the end of the line. - let position = Position { - line: 0, - offset: 7, - }; - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(position); + let position = Position { line: 0, offset: 7 }; + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(position); // Call the command. super::move_to_first_word_of_line(&mut app).unwrap(); // Ensure that the cursor is moved to the start of the first word. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 0, - offset: 4, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 0, offset: 4 } + ); } #[test] @@ -234,20 +246,21 @@ mod tests { let mut app = set_up_application("\namp editor"); // Move past the first non-whitespace token. - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 2, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 2 }); // Call the command. super::move_to_start_of_previous_token(&mut app).unwrap(); // Ensure that the cursor is moved to the start of the previous word. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 0 } + ); } #[test] @@ -256,20 +269,21 @@ mod tests { let mut app = set_up_application("\namp editor"); // Move to the start of the second non-whitespace word. - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 4, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 4 }); // Call the command. super::move_to_start_of_previous_token(&mut app).unwrap(); // Ensure that the cursor is moved to the start of the previous word. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 0 } + ); } #[test] @@ -278,20 +292,21 @@ mod tests { let mut app = set_up_application("\namp editor"); // Move to the start of the first non-whitespace word. - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 0, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 0 }); // Call the command. super::move_to_start_of_next_token(&mut app).unwrap(); // Ensure that the cursor is moved to the start of the next word. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 4, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 4 } + ); } #[test] @@ -300,20 +315,21 @@ mod tests { let mut app = set_up_application("\namp editor"); // Move to the start of the first non-whitespace word. - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 0, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 0 }); // Call the command. super::move_to_end_of_current_token(&mut app).unwrap(); // Ensure that the cursor is moved to the end of the current word. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 3, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 3 } + ); } #[test] @@ -322,20 +338,21 @@ mod tests { let mut app = set_up_application("\namp editor"); // Move to the start of the first non-whitespace word. - app.workspace.current_buffer.as_mut().unwrap().cursor.move_to(Position { - line: 1, - offset: 0, - }); + app.workspace + .current_buffer + .as_mut() + .unwrap() + .cursor + .move_to(Position { line: 1, offset: 0 }); // Call the command. super::append_to_current_token(&mut app).unwrap(); // Ensure that the cursor is moved to the end of the current word. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 3, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 3 } + ); // Ensure that we're in insert mode. assert!(match app.mode { diff --git a/src/commands/git.rs b/src/commands/git.rs index 4b09b5c8..d3b400e4 100644 --- a/src/commands/git.rs +++ b/src/commands/git.rs @@ -1,6 +1,6 @@ -use crate::errors::*; -use crate::errors; use crate::commands::{self, Result}; +use crate::errors; +use crate::errors::*; use crate::models::application::{Application, ClipboardContent, Mode}; use git2; use regex::Regex; @@ -8,55 +8,70 @@ use std::cmp::Ordering; pub fn add(app: &mut Application) -> Result { let repo = app.repository.as_ref().ok_or("No repository available")?; - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; - let mut index = repo.index().chain_err(|| "Couldn't get the repository index")?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; + let mut index = repo + .index() + .chain_err(|| "Couldn't get the repository index")?; let buffer_path = buffer.path.as_ref().ok_or(BUFFER_PATH_MISSING)?; let repo_path = repo.workdir().ok_or("No path found for the repository")?; - let relative_path = buffer_path.strip_prefix(repo_path).chain_err(|| { - "Failed to build a relative buffer path" - })?; + let relative_path = buffer_path + .strip_prefix(repo_path) + .chain_err(|| "Failed to build a relative buffer path")?; - index.add_path(relative_path).chain_err(|| "Failed to add path to index.")?; + index + .add_path(relative_path) + .chain_err(|| "Failed to add path to index.")?; index.write().chain_err(|| "Failed to write index.") } pub fn copy_remote_url(app: &mut Application) -> Result { if let Some(ref mut repo) = app.repository { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; let buffer_path = buffer.path.as_ref().ok_or(BUFFER_PATH_MISSING)?; - let remote = repo.find_remote("origin").chain_err(|| { - "Couldn't find a remote \"origin\"" - })?; + let remote = repo + .find_remote("origin") + .chain_err(|| "Couldn't find a remote \"origin\"")?; let url = remote.url().ok_or("No URL for remote/origin")?; let gh_path = get_gh_path(url)?; let repo_path = repo.workdir().ok_or("No path found for the repository")?; - let relative_path = buffer_path.strip_prefix(repo_path).chain_err(|| { - "Failed to build a relative buffer path" - })?; + let relative_path = buffer_path + .strip_prefix(repo_path) + .chain_err(|| "Failed to build a relative buffer path")?; - let status = repo.status_file(relative_path).chain_err(|| { - "Couldn't get status info for the specified path" - })?; + let status = repo + .status_file(relative_path) + .chain_err(|| "Couldn't get status info for the specified path")?; if status.contains(git2::Status::WT_NEW) || status.contains(git2::Status::INDEX_NEW) { bail!("The provided path doesn't exist in the repository"); } // We want to build URLs that point to an object ID, so that they'll // refer to a snapshot of the file as it looks at this very moment. - let mut revisions = repo.revwalk().chain_err(|| { - "Couldn't build a list of revisions for the repository" - })?; + let mut revisions = repo + .revwalk() + .chain_err(|| "Couldn't build a list of revisions for the repository")?; // We need to set a starting point for the commit graph we'll // traverse. We want the most recent commit, so start at HEAD. - revisions.push_head().chain_err(|| "Failed to push HEAD to commit graph.")?; + revisions + .push_head() + .chain_err(|| "Failed to push HEAD to commit graph.")?; // Pull the first revision (HEAD). - let last_oid = revisions.next().and_then(|revision| revision.ok()).ok_or( - "Couldn't find a git object ID for this file" - )?; + let last_oid = revisions + .next() + .and_then(|revision| revision.ok()) + .ok_or("Couldn't find a git object ID for this file")?; let line_range = match app.mode { Mode::SelectLine(ref s) => { @@ -69,7 +84,7 @@ pub fn copy_remote_url(app: &mut Application) -> Result { Ordering::Greater => format!("#L{}-L{}", line_2, line_1), Ordering::Equal => format!("#L{}", line_1), } - }, + } _ => String::new(), }; @@ -81,9 +96,8 @@ pub fn copy_remote_url(app: &mut Application) -> Result { line_range ); - app.clipboard.set_content( - ClipboardContent::Inline(gh_url) - )?; + app.clipboard + .set_content(ClipboardContent::Inline(gh_url))?; } else { bail!("No repository available"); } @@ -98,10 +112,11 @@ fn get_gh_path(url: &str) -> errors::Result<&str> { static ref REGEX: Regex = Regex::new(r"^(?:https://|git@)github.com(?::|/)(.*?)(?:.git)?$").unwrap(); } - REGEX.captures(url) + REGEX + .captures(url) .and_then(|c| c.get(1)) .map(|c| c.as_str()) - .chain_err(|| { "Failed to capture remote repo path" }) + .chain_err(|| "Failed to capture remote repo path") } #[test] diff --git a/src/commands/jump.rs b/src/commands/jump.rs index 5e57cfec..ab4b26d9 100644 --- a/src/commands/jump.rs +++ b/src/commands/jump.rs @@ -1,29 +1,28 @@ +use crate::commands::Result; use crate::errors::*; use crate::input::Key; -use std::mem; -use crate::commands::Result; use crate::models::application::modes::jump; use crate::models::application::modes::JumpMode; -use crate::models::application::{Mode, Application}; +use crate::models::application::{Application, Mode}; use scribe::Workspace; +use std::mem; pub fn match_tag(app: &mut Application) -> Result { - let result = - if let Mode::Jump(ref mut jump_mode) = app.mode { - match jump_mode.input.len() { - 0 => return Ok(()), // Not enough data to match to a position. - 1 => { - if jump_mode.first_phase { - jump_to_tag(jump_mode, &mut app.workspace) - } else { - return Ok(()) // Not enough data to match to a position. - } - }, - _ => jump_to_tag(jump_mode, &mut app.workspace), + let result = if let Mode::Jump(ref mut jump_mode) = app.mode { + match jump_mode.input.len() { + 0 => return Ok(()), // Not enough data to match to a position. + 1 => { + if jump_mode.first_phase { + jump_to_tag(jump_mode, &mut app.workspace) + } else { + return Ok(()); // Not enough data to match to a position. + } } - } else { - bail!("Can't match jump tags outside of jump mode."); - }; + _ => jump_to_tag(jump_mode, &mut app.workspace), + } + } else { + bail!("Can't match jump tags outside of jump mode."); + }; switch_to_previous_mode(app); result @@ -37,7 +36,10 @@ fn jump_to_tag(jump_mode: &mut JumpMode, workspace: &mut Workspace) -> Result { let buffer = workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; if !buffer.cursor.move_to(*position) { - bail!("Couldn't move to the specified tag's position ({:?})", position) + bail!( + "Couldn't move to the specified tag's position ({:?})", + position + ) } Ok(()) @@ -72,9 +74,9 @@ pub fn push_search_char(app: &mut Application) -> Result { // Add the input to whatever we've received in jump mode so far. mode.input.push('f'); } - }, + } Key::Char(c) => mode.input.push(c), - _ => bail!("Last key press wasn't a character") + _ => bail!("Last key press wasn't a character"), } } else { bail!("Can't push jump character outside of jump mode") diff --git a/src/commands/line_jump.rs b/src/commands/line_jump.rs index d0d338d0..c146eebf 100644 --- a/src/commands/line_jump.rs +++ b/src/commands/line_jump.rs @@ -1,6 +1,6 @@ +use crate::commands::{self, Result}; use crate::errors::*; use crate::input::Key; -use crate::commands::{self, Result}; use crate::models::application::{Application, Mode}; use scribe::buffer::Position; @@ -14,7 +14,11 @@ pub fn accept_input(app: &mut Application) -> Result { // Ignore zero-value line numbers. if line_number > 0 { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; // Input values won't be zero-indexed; map the value so // that we can use it for a zero-indexed buffer position. @@ -52,7 +56,11 @@ pub fn accept_input(app: &mut Application) -> Result { } pub fn push_search_char(app: &mut Application) -> Result { - let key = app.view.last_key().as_ref().ok_or("View hasn't tracked a key press")?; + let key = app + .view + .last_key() + .as_ref() + .ok_or("View hasn't tracked a key press")?; if let Key::Char(c) = *key { if let Mode::LineJump(ref mut mode) = app.mode { @@ -80,9 +88,9 @@ pub fn pop_search_char(app: &mut Application) -> Result { #[cfg(test)] mod tests { use crate::commands; - use scribe::Buffer; - use scribe::buffer::Position; use crate::models::application::{Application, Mode}; + use scribe::buffer::Position; + use scribe::Buffer; #[test] fn accept_input_moves_cursor_to_requested_line_and_changes_modes() { @@ -103,11 +111,10 @@ mod tests { // Ensure that the cursor is in the right place. // NOTE: We look for a decremented version of the input line number // because users won't be inputting zero-indexed line numbers. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 2, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 2, offset: 0 } + ); // Ensure that we're in normal mode. assert!(match app.mode { @@ -121,10 +128,7 @@ mod tests { let mut app = Application::new(&Vec::new()).unwrap(); let mut buffer = Buffer::new(); buffer.insert("amp\neditor\namp"); - buffer.cursor.move_to(Position { - line: 1, - offset: 3, - }); + buffer.cursor.move_to(Position { line: 1, offset: 3 }); // Now that we've set up the buffer, add it to the application, // switch to line jump mode, set the line input, and run the command. @@ -139,11 +143,10 @@ mod tests { // Ensure that the cursor is in the right place. // NOTE: We look for a decremented version of the input line number // because users won't be inputting zero-indexed line numbers. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 2, - offset: 3, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 2, offset: 3 } + ); // Ensure that we're in normal mode. assert!(match app.mode { @@ -169,11 +172,10 @@ mod tests { commands::line_jump::accept_input(&mut app).unwrap(); // Ensure that the cursor is in the right place. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 0, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 0, offset: 0 } + ); // Ensure that we're in normal mode. assert!(match app.mode { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index cb9dff20..4db36e2b 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -12,8 +12,8 @@ pub mod line_jump; pub mod path; pub mod preferences; pub mod search; -pub mod selection; pub mod search_select; +pub mod selection; pub mod view; pub mod workspace; @@ -23,4 +23,3 @@ pub type Result = errors::Result<()>; pub fn hash_map() -> HashMap<&'static str, Command> { include!(concat!(env!("OUT_DIR"), "/hash_map")) } - diff --git a/src/commands/path.rs b/src/commands/path.rs index 6aaf9a4a..d72cb4c7 100644 --- a/src/commands/path.rs +++ b/src/commands/path.rs @@ -1,11 +1,15 @@ -use crate::errors::*; use crate::commands::{self, Result}; +use crate::errors::*; use crate::input::Key; use crate::models::application::{Application, Mode}; use std::path::PathBuf; pub fn push_char(app: &mut Application) -> Result { - let last_key = app.view.last_key().as_ref().ok_or("View hasn't tracked a key press")?; + let last_key = app + .view + .last_key() + .as_ref() + .ok_or("View hasn't tracked a key press")?; if let Key::Char(c) = *last_key { if let Mode::Path(ref mut mode) = app.mode { mode.push_char(c); @@ -28,20 +32,25 @@ pub fn pop_char(app: &mut Application) -> Result { } pub fn accept_path(app: &mut Application) -> Result { - let save_on_accept = - if let Mode::Path(ref mut mode) = app.mode { - let current_buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; - let path_name = mode.input.clone(); - if path_name.is_empty() { - bail!("Please provide a non-empty path") - } - current_buffer.path = Some(PathBuf::from(path_name)); - mode.save_on_accept - } else { - bail!("Cannot accept path outside of path mode"); - }; + let save_on_accept = if let Mode::Path(ref mut mode) = app.mode { + let current_buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; + let path_name = mode.input.clone(); + if path_name.is_empty() { + bail!("Please provide a non-empty path") + } + current_buffer.path = Some(PathBuf::from(path_name)); + mode.save_on_accept + } else { + bail!("Cannot accept path outside of path mode"); + }; - app.workspace.update_current_syntax().chain_err(|| BUFFER_SYNTAX_UPDATE_FAILED)?; + app.workspace + .update_current_syntax() + .chain_err(|| BUFFER_SYNTAX_UPDATE_FAILED)?; app.mode = Mode::Normal; if save_on_accept { @@ -54,10 +63,10 @@ pub fn accept_path(app: &mut Application) -> Result { #[cfg(test)] mod tests { use crate::commands; - use crate::models::Application; use crate::models::application::Mode; + use crate::models::Application; use scribe::Buffer; - use std::path::{PathBuf, Path}; + use std::path::{Path, PathBuf}; #[test] fn accept_path_sets_buffer_path_based_on_input_and_switches_to_normal_mode() { @@ -94,7 +103,9 @@ mod tests { // Switch to the mode, add a name, set the flag, and accept it. commands::application::switch_to_path_mode(&mut app).unwrap(); if let Mode::Path(ref mut mode) = app.mode { - mode.input = Path::new(concat!(env!("OUT_DIR"), "new_path")).to_string_lossy().into(); + mode.input = Path::new(concat!(env!("OUT_DIR"), "new_path")) + .to_string_lossy() + .into(); mode.save_on_accept = true; } super::accept_path(&mut app).unwrap(); @@ -116,13 +127,18 @@ mod tests { } let result = super::accept_path(&mut app); assert!(result.is_err()); - assert!(app.workspace.current_buffer.as_ref().unwrap().path.is_none()); + assert!(app + .workspace + .current_buffer + .as_ref() + .unwrap() + .path + .is_none()); if let Mode::Path(_) = app.mode { } else { panic!("Not in path mode"); } - } #[test] @@ -140,7 +156,14 @@ mod tests { super::accept_path(&mut app).unwrap(); assert_eq!( - app.workspace.current_buffer.as_ref().unwrap().syntax_definition.as_ref().unwrap().name, + app.workspace + .current_buffer + .as_ref() + .unwrap() + .syntax_definition + .as_ref() + .unwrap() + .name, "Rust" ); } diff --git a/src/commands/search.rs b/src/commands/search.rs index a787d36b..13f308ec 100644 --- a/src/commands/search.rs +++ b/src/commands/search.rs @@ -1,37 +1,46 @@ +use crate::commands::{self, Result}; use crate::errors::*; use crate::input::Key; -use crate::commands::{self, Result}; use crate::models::application::{Application, Mode}; pub fn move_to_previous_result(app: &mut Application) -> Result { if let Mode::Search(ref mut mode) = app.mode { - mode.results.as_mut().ok_or(NO_SEARCH_RESULTS)?.select_previous(); + mode.results + .as_mut() + .ok_or(NO_SEARCH_RESULTS)? + .select_previous(); } else { bail!("Can't move to search result outside of search mode"); } - commands::view::scroll_cursor_to_center(app) - .chain_err(|| SCROLL_TO_CURSOR_FAILED)?; + commands::view::scroll_cursor_to_center(app).chain_err(|| SCROLL_TO_CURSOR_FAILED)?; move_to_current_result(app) } pub fn move_to_next_result(app: &mut Application) -> Result { if let Mode::Search(ref mut mode) = app.mode { - mode.results.as_mut().ok_or(NO_SEARCH_RESULTS)?.select_next(); + mode.results + .as_mut() + .ok_or(NO_SEARCH_RESULTS)? + .select_next(); } else { bail!("Can't move to search result outside of search mode"); } - commands::view::scroll_cursor_to_center(app) - .chain_err(|| SCROLL_TO_CURSOR_FAILED)?; + commands::view::scroll_cursor_to_center(app).chain_err(|| SCROLL_TO_CURSOR_FAILED)?; move_to_current_result(app) } pub fn move_to_current_result(app: &mut Application) -> Result { if let Mode::Search(ref mut mode) = app.mode { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let query = mode.input.as_ref().ok_or(SEARCH_QUERY_MISSING)?; - let result = mode.results + let result = mode + .results .as_mut() .ok_or(NO_SEARCH_RESULTS)? .selection() @@ -41,8 +50,7 @@ pub fn move_to_current_result(app: &mut Application) -> Result { bail!("Can't move to search result outside of search mode"); } - commands::view::scroll_cursor_to_center(app) - .chain_err(|| SCROLL_TO_CURSOR_FAILED)?; + commands::view::scroll_cursor_to_center(app).chain_err(|| SCROLL_TO_CURSOR_FAILED)?; Ok(()) } @@ -71,7 +79,11 @@ pub fn clear_query(app: &mut Application) -> Result { } pub fn push_search_char(app: &mut Application) -> Result { - let key = app.view.last_key().as_ref().ok_or("View hasn't tracked a key press")?; + let key = app + .view + .last_key() + .as_ref() + .ok_or("View hasn't tracked a key press")?; if let Key::Char(c) = *key { if let Mode::Search(ref mut mode) = app.mode { @@ -104,7 +116,11 @@ pub fn pop_search_char(app: &mut Application) -> Result { pub fn run(app: &mut Application) -> Result { if let Mode::Search(ref mut mode) = app.mode { // Search the buffer. - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; mode.search(buffer)?; } else { bail!("Can't run search outside of search mode"); @@ -116,7 +132,11 @@ pub fn run(app: &mut Application) -> Result { fn select_closest_result(app: &mut Application) -> Result { if let Mode::Search(ref mut mode) = app.mode { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; let results = mode.results.as_mut().ok_or(NO_SEARCH_RESULTS)?; // Skip over previous entries. @@ -134,11 +154,11 @@ fn select_closest_result(app: &mut Application) -> Result { #[cfg(test)] mod tests { - use scribe::Buffer; - use scribe::buffer::Position; - use crate::models::Application; - use crate::models::application::Mode; use crate::commands; + use crate::models::application::Mode; + use crate::models::Application; + use scribe::buffer::Position; + use scribe::Buffer; #[test] fn move_to_previous_result_moves_cursor_to_previous_result() { @@ -149,7 +169,7 @@ mod tests { // Move to a location just before, but not at the // last result before adding it to the workspace. - buffer.cursor.move_to(Position{ line: 1, offset: 3 }); + buffer.cursor.move_to(Position { line: 1, offset: 3 }); app.workspace.add_buffer(buffer); // Enter search mode and accept a query. @@ -163,11 +183,10 @@ mod tests { commands::search::move_to_previous_result(&mut app).unwrap(); // Ensure the buffer cursor is at the expected position. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 0 } + ); } #[test] @@ -189,11 +208,10 @@ mod tests { commands::search::move_to_previous_result(&mut app).unwrap(); // Ensure the buffer cursor is at the expected position. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 2, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 2, offset: 0 } + ); } #[test] @@ -215,11 +233,10 @@ mod tests { commands::search::move_to_next_result(&mut app).unwrap(); // Ensure the buffer cursor is at the expected position. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 0 } + ); } #[test] @@ -231,7 +248,7 @@ mod tests { // Move to a location just before, but not at the // last result before adding it to the workspace. - buffer.cursor.move_to(Position{ line: 1, offset: 3 }); + buffer.cursor.move_to(Position { line: 1, offset: 3 }); app.workspace.add_buffer(buffer); // Enter search mode and accept a query. As we've moved the cursor @@ -246,11 +263,10 @@ mod tests { commands::search::move_to_next_result(&mut app).unwrap(); // Ensure the buffer cursor is at the expected position. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 0, - offset: 4, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 0, offset: 4 } + ); } #[test] @@ -261,7 +277,7 @@ mod tests { // Move to a location just at the first // result before adding it to the workspace. - buffer.cursor.move_to(Position{ line: 0, offset: 4 }); + buffer.cursor.move_to(Position { line: 0, offset: 4 }); app.workspace.add_buffer(buffer); // Add a search query, enter search mode, and accept the query. @@ -279,10 +295,9 @@ mod tests { assert_eq!(app.search_query, Some("ed".to_string())); // Ensure the buffer cursor is at the expected position. - assert_eq!(*app.workspace.current_buffer.as_ref().unwrap().cursor, - Position { - line: 1, - offset: 0, - }); + assert_eq!( + *app.workspace.current_buffer.as_ref().unwrap().cursor, + Position { line: 1, offset: 0 } + ); } } diff --git a/src/commands/search_select.rs b/src/commands/search_select.rs index b3ec0e89..5e6651bf 100644 --- a/src/commands/search_select.rs +++ b/src/commands/search_select.rs @@ -1,10 +1,10 @@ -use crate::errors::*; use crate::commands::{self, application, Result}; +use crate::errors::*; use crate::input::Key; -use std::mem; use crate::models::application::modes::open::DisplayablePath; -use crate::models::application::{Application, Mode}; use crate::models::application::modes::SearchSelectMode; +use crate::models::application::{Application, Mode}; +use std::mem; pub fn accept(app: &mut Application) -> Result { // Consume the application mode. This is necessary because the selection in @@ -18,16 +18,17 @@ pub fn accept(app: &mut Application) -> Result { // Run the selected command. (selection.command)(app)?; - }, + } Mode::Open(ref mut mode) => { let DisplayablePath(path) = mode .selection() .ok_or("Couldn't find a selected path to open")?; - let syntax_definition = - app.preferences.borrow().syntax_definition_name(path).and_then(|name| { - app.workspace.syntax_set.find_syntax_by_name(&name).cloned() - }); + let syntax_definition = app + .preferences + .borrow() + .syntax_definition_name(path) + .and_then(|name| app.workspace.syntax_set.find_syntax_by_name(&name).cloned()); app.workspace .open_buffer(path) @@ -42,14 +43,17 @@ pub fn accept(app: &mut Application) -> Result { } app.view.initialize_buffer(buffer)?; - - }, + } Mode::Theme(ref mut mode) => { let theme_key = mode.selection().ok_or("No theme selected")?; app.preferences.borrow_mut().set_theme(theme_key.as_str()); - }, + } Mode::SymbolJump(ref mut mode) => { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; let position = mode .selection() .ok_or("Couldn't find a position for the selected symbol")? @@ -58,14 +62,17 @@ pub fn accept(app: &mut Application) -> Result { if !buffer.cursor.move_to(position) { bail!("Couldn't move to the selected symbol's position"); } - }, + } Mode::Syntax(ref mut mode) => { let name = mode.selection().ok_or("No syntax selected")?; - let syntax = - app.workspace.syntax_set.find_syntax_by_name(name).cloned(); - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let syntax = app.workspace.syntax_set.find_syntax_by_name(name).cloned(); + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; buffer.syntax_definition = syntax; - }, + } _ => bail!("Can't accept selection outside of search select mode."), } diff --git a/src/commands/selection.rs b/src/commands/selection.rs index a7019a6b..ee2db3ac 100644 --- a/src/commands/selection.rs +++ b/src/commands/selection.rs @@ -1,10 +1,10 @@ -use crate::models::application::{Application, ClipboardContent, Mode}; -use scribe::buffer::{LineRange, Range}; use super::application; -use crate::errors::*; use crate::commands::{self, Result}; +use crate::errors::*; +use crate::models::application::{Application, ClipboardContent, Mode}; use crate::util; use crate::util::reflow::Reflow; +use scribe::buffer::{LineRange, Range}; pub fn delete(app: &mut Application) -> Result { let rng = sel_to_range(app)?; @@ -51,28 +51,28 @@ pub fn select_all(app: &mut Application) -> Result { } fn copy_to_clipboard(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; match app.mode { Mode::Select(ref select_mode) => { let cursor_position = *buffer.cursor.clone(); let selected_range = Range::new(cursor_position, select_mode.anchor); - let data = buffer.read(&selected_range.clone()) + let data = buffer + .read(&selected_range.clone()) .ok_or("Couldn't read selected data from buffer")?; app.clipboard.set_content(ClipboardContent::Inline(data))?; } Mode::SelectLine(ref mode) => { - let selected_range = util::inclusive_range( - &LineRange::new( - mode.anchor, - buffer.cursor - .line - ), - buffer - ); - - let data = buffer.read(&selected_range.clone()) + let selected_range = + util::inclusive_range(&LineRange::new(mode.anchor, buffer.cursor.line), buffer); + + let data = buffer + .read(&selected_range.clone()) .ok_or("Couldn't read selected data from buffer")?; app.clipboard.set_content(ClipboardContent::Block(data))?; } @@ -87,8 +87,8 @@ pub fn justify(app: &mut Application) -> Result { let buffer = app.workspace.current_buffer.as_mut().unwrap(); let limit = match app.preferences.borrow().line_length_guide() { - Some(n) => n, - None => bail!("Justification requires a line_length_guide."), + Some(n) => n, + None => bail!("Justification requires a line_length_guide."), }; buffer.start_operation_group(); @@ -99,32 +99,28 @@ pub fn justify(app: &mut Application) -> Result { } fn sel_to_range(app: &mut Application) -> std::result::Result { - let buf = app.workspace.current_buffer.as_mut().ok_or(BUFFER_MISSING)?; + let buf = app + .workspace + .current_buffer + .as_mut() + .ok_or(BUFFER_MISSING)?; match app.mode { - Mode::Select(ref mode) => { - let cursor_position = *buf.cursor.clone(); - Ok(Range::new(cursor_position, mode.anchor)) - }, - Mode::SelectLine(ref mode) => { - Ok(util::inclusive_range( - &LineRange::new( - mode.anchor, - buf.cursor.line - ), - buf - )) - }, - Mode::Search(ref mode) => { - Ok(mode - .results - .as_ref() - .and_then(|r| r.selection()) - .ok_or("A selection is required.")? - .clone() - ) - } - _ => bail!("A selection is required."), + Mode::Select(ref mode) => { + let cursor_position = *buf.cursor.clone(); + Ok(Range::new(cursor_position, mode.anchor)) + } + Mode::SelectLine(ref mode) => Ok(util::inclusive_range( + &LineRange::new(mode.anchor, buf.cursor.line), + buf, + )), + Mode::Search(ref mode) => Ok(mode + .results + .as_ref() + .and_then(|r| r.selection()) + .ok_or("A selection is required.")? + .clone()), + _ => bail!("A selection is required."), } } @@ -132,8 +128,8 @@ fn sel_to_range(app: &mut Application) -> std::result::Result { mod tests { use crate::commands; use crate::models::application::{Application, Mode}; - use scribe::Buffer; use scribe::buffer::Position; + use scribe::Buffer; #[test] fn select_all_selects_the_entire_buffer() { @@ -142,10 +138,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert("amp\neditor\nbuffer"); - let position = Position { - line: 1, - offset: 3, - }; + let position = Position { line: 1, offset: 3 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it @@ -159,12 +152,15 @@ mod tests { match app.mode { Mode::SelectLine(ref mode) => { assert_eq!(mode.anchor, 0); - }, - _ => panic!("Application isn't in select line mode.") + } + _ => panic!("Application isn't in select line mode."), } // Ensure that the cursor is moved to the last line of the buffer. - assert_eq!(app.workspace.current_buffer.as_ref().unwrap().cursor.line, 2); + assert_eq!( + app.workspace.current_buffer.as_ref().unwrap().cursor.line, + 2 + ); } #[test] @@ -174,10 +170,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert("amp\neditor\nbuffer"); - let position = Position { - line: 1, - offset: 0, - }; + let position = Position { line: 1, offset: 0 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it @@ -201,10 +194,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert("amp\neditor\nbuffer"); - let position = Position { - line: 1, - offset: 0, - }; + let position = Position { line: 1, offset: 0 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it @@ -227,10 +217,7 @@ mod tests { // Insert data with indentation and move to the end of the line. buffer.insert("amp\neditor\nbuffer"); - let position = Position { - line: 1, - offset: 0, - }; + let position = Position { line: 1, offset: 0 }; buffer.cursor.move_to(position); // Now that we've set up the buffer, add it diff --git a/src/commands/view.rs b/src/commands/view.rs index ff7be3c0..c732228f 100644 --- a/src/commands/view.rs +++ b/src/commands/view.rs @@ -1,27 +1,43 @@ -use crate::errors::*; use crate::commands::Result; +use crate::errors::*; use crate::models::application::Application; pub fn scroll_up(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; app.view.scroll_up(buffer, 10)?; Ok(()) } pub fn scroll_down(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; app.view.scroll_down(buffer, 10)?; Ok(()) } pub fn scroll_to_cursor(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; app.view.scroll_to_cursor(buffer)?; Ok(()) } pub fn scroll_cursor_to_center(app: &mut Application) -> Result { - let buffer = app.workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; + let buffer = app + .workspace + .current_buffer + .as_ref() + .ok_or(BUFFER_MISSING)?; app.view.scroll_to_center(buffer)?; Ok(()) } diff --git a/src/commands/workspace.rs b/src/commands/workspace.rs index 2db7574f..8c483250 100644 --- a/src/commands/workspace.rs +++ b/src/commands/workspace.rs @@ -1,7 +1,7 @@ use crate::commands::Result; -use scribe::Buffer; use crate::models::application::Application; use crate::util; +use scribe::Buffer; pub fn next_buffer(app: &mut Application) -> Result { app.workspace.next_buffer(); diff --git a/src/input/key_map/mod.rs b/src/input/key_map/mod.rs index 5eb5a943..138e40f8 100644 --- a/src/input/key_map/mod.rs +++ b/src/input/key_map/mod.rs @@ -3,8 +3,8 @@ use crate::errors::*; use crate::input::Key; use smallvec::SmallVec; use std::collections::HashMap; -use std::ops::{Deref, DerefMut}; use std::convert::Into; +use std::ops::{Deref, DerefMut}; use yaml_rust::yaml::{Hash, Yaml, YamlLoader}; /// Nested HashMap newtype that provides a more ergonomic interface. @@ -27,11 +27,11 @@ impl KeyMap { let commands = commands::hash_map(); for (yaml_mode, yaml_key_bindings) in keymap_data { - let mode = yaml_mode.as_str().ok_or_else(|| - "A mode key couldn't be parsed as a string".to_string() - )?; - let key_bindings = parse_mode_key_bindings(yaml_key_bindings, &commands). - chain_err(|| format!("Failed to parse keymaps for \"{}\" mode", mode))?; + let mode = yaml_mode + .as_str() + .ok_or_else(|| "A mode key couldn't be parsed as a string".to_string())?; + let key_bindings = parse_mode_key_bindings(yaml_key_bindings, &commands) + .chain_err(|| format!("Failed to parse keymaps for \"{}\" mode", mode))?; keymap.insert(mode.to_string(), key_bindings); } @@ -44,15 +44,20 @@ impl KeyMap { /// if the specific character binding cannot be found. /// pub fn commands_for(&self, mode: &str, key: &Key) -> Option> { - self.0.get(mode).and_then(|mode_keymap| { - if let Key::Char(_) = *key { - // Look for a command for this specific character, falling - // back to another search for a wildcard character binding. - mode_keymap.get(key).or_else(|| mode_keymap.get(&Key::AnyChar)) - } else { - mode_keymap.get(key) - } - }).map(|commands| (*commands).clone()) + self.0 + .get(mode) + .and_then(|mode_keymap| { + if let Key::Char(_) = *key { + // Look for a command for this specific character, falling + // back to another search for a wildcard character binding. + mode_keymap + .get(key) + .or_else(|| mode_keymap.get(&Key::AnyChar)) + } else { + mode_keymap.get(key) + } + }) + .map(|commands| (*commands).clone()) } /// Loads the default keymap from a static @@ -117,17 +122,22 @@ impl KeyMap { /// /// Key::Char('k') => [commands::cursor::move_up] /// -fn parse_mode_key_bindings(mode: &Yaml, commands: &HashMap<&str, Command>) -> Result>> { - let mode_key_bindings = mode.as_hash().ok_or( - "Keymap mode config didn't return a hash of key bindings", - )?; +fn parse_mode_key_bindings( + mode: &Yaml, + commands: &HashMap<&str, Command>, +) -> Result>> { + let mode_key_bindings = mode + .as_hash() + .ok_or("Keymap mode config didn't return a hash of key bindings")?; let mut key_bindings = HashMap::new(); for (yaml_key, yaml_command) in mode_key_bindings { // Parse modifier/character from key component. - let key = parse_key(yaml_key.as_str().ok_or_else(|| - "A keymap key couldn't be parsed as a string".to_string() - )?)?; + let key = parse_key( + yaml_key + .as_str() + .ok_or_else(|| "A keymap key couldn't be parsed as a string".to_string())?, + )?; let mut key_commands = SmallVec::new(); @@ -136,27 +146,28 @@ fn parse_mode_key_bindings(mode: &Yaml, commands: &HashMap<&str, Command>) -> Re Yaml::String(ref command) => { let command_string = command.as_str(); - key_commands.push( - *commands.get(&command_string).ok_or_else(|| format!( - "Keymap command \"{}\" doesn't exist", - command_string - ))? - ); - }, + key_commands.push(*commands.get(&command_string).ok_or_else(|| { + format!("Keymap command \"{}\" doesn't exist", command_string) + })?); + } Yaml::Array(ref command_array) => { for command in command_array { - let command_string = command.as_str().ok_or_else(|| - format!("Keymap command \"{:?}\" couldn't be parsed as a string", command) - )?; - - key_commands.push( - *commands.get(command_string).ok_or_else(|| - format!("Keymap command \"{}\" doesn't exist", command_string) - )? - ); + let command_string = command.as_str().ok_or_else(|| { + format!( + "Keymap command \"{:?}\" couldn't be parsed as a string", + command + ) + })?; + + key_commands.push(*commands.get(command_string).ok_or_else(|| { + format!("Keymap command \"{}\" doesn't exist", command_string) + })?); } - }, - _ => bail!(format!("Keymap command \"{:?}\" couldn't be parsed", yaml_command)) + } + _ => bail!(format!( + "Keymap command \"{:?}\" couldn't be parsed", + yaml_command + )), } // Add a key/command entry to the mapping. @@ -174,16 +185,16 @@ fn parse_mode_key_bindings(mode: &Yaml, commands: &HashMap<&str, Command>) -> Re /// fn parse_key(data: &str) -> Result { let mut key_components = data.split('-'); - let component = key_components.next().ok_or( - "A keymap key is an empty string", - )?; + let component = key_components + .next() + .ok_or("A keymap key is an empty string")?; if let Some(key) = key_components.next() { // We have a modifier-qualified key; get the key. - let key_char = key.chars().next().ok_or_else(|| format!( - "Keymap key \"{}\" is invalid", - key - ))?; + let key_char = key + .chars() + .next() + .ok_or_else(|| format!("Keymap key \"{}\" is invalid", key))?; // Find the variant for the specified modifier. match component { @@ -193,27 +204,28 @@ fn parse_key(data: &str) -> Result { } else { // No modifier; just get the key. Ok(match component { - "space" => Key::Char(' '), + "space" => Key::Char(' '), "backspace" => Key::Backspace, - "left" => Key::Left, - "right" => Key::Right, - "up" => Key::Up, - "down" => Key::Down, - "home" => Key::Home, - "end" => Key::End, - "page_up" => Key::PageUp, + "left" => Key::Left, + "right" => Key::Right, + "up" => Key::Up, + "down" => Key::Down, + "home" => Key::Home, + "end" => Key::End, + "page_up" => Key::PageUp, "page_down" => Key::PageDown, - "delete" => Key::Delete, - "insert" => Key::Insert, - "escape" => Key::Esc, - "tab" => Key::Tab, - "enter" => Key::Enter, - "_" => Key::AnyChar, - _ => Key::Char( + "delete" => Key::Delete, + "insert" => Key::Insert, + "escape" => Key::Esc, + "tab" => Key::Tab, + "enter" => Key::Enter, + "_" => Key::AnyChar, + _ => Key::Char( // It's not a keyword; take its first character, if available. - component.chars().next().ok_or_else(|| - format!("Keymap key \"{}\" is invalid", component) - )? + component + .chars() + .next() + .ok_or_else(|| format!("Keymap key \"{}\" is invalid", component))?, ), }) } @@ -241,10 +253,10 @@ impl From for HashMap>> { #[cfg(test)] mod tests { - use yaml_rust::YamlLoader; use super::KeyMap; use crate::commands; use crate::input::Key; + use yaml_rust::YamlLoader; #[test] fn keymap_correctly_parses_yaml_character_keybindings() { @@ -253,9 +265,9 @@ mod tests { let yaml = YamlLoader::load_from_str(yaml_data).unwrap(); let keymap = KeyMap::from(&yaml[0].as_hash().unwrap()).unwrap(); - let command = keymap.commands_for("normal", &Key::Char('k')).expect( - "Keymap doesn't contain command", - ); + let command = keymap + .commands_for("normal", &Key::Char('k')) + .expect("Keymap doesn't contain command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_up as *const usize) @@ -271,9 +283,9 @@ mod tests { let characters = vec!['a', 'b', 'c']; for c in characters.into_iter() { - let command = keymap.commands_for("normal", &Key::Char(c)).expect( - "Keymap doesn't contain command", - ); + let command = keymap + .commands_for("normal", &Key::Char(c)) + .expect("Keymap doesn't contain command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_up as *const usize) @@ -288,16 +300,16 @@ mod tests { let yaml = YamlLoader::load_from_str(yaml_data).unwrap(); let keymap = KeyMap::from(&yaml[0].as_hash().unwrap()).unwrap(); - let char_command = keymap.commands_for("normal", &Key::Char('j')).expect( - "Keymap doesn't contain command", - ); + let char_command = keymap + .commands_for("normal", &Key::Char('j')) + .expect("Keymap doesn't contain command"); assert_eq!( (char_command[0] as *const usize), (commands::cursor::move_down as *const usize) ); - let wildcard_command = keymap.commands_for("normal", &Key::Char('a')).expect( - "Keymap doesn't contain command", - ); + let wildcard_command = keymap + .commands_for("normal", &Key::Char('a')) + .expect("Keymap doesn't contain command"); assert_eq!( (wildcard_command[0] as *const usize), (commands::cursor::move_up as *const usize) @@ -311,9 +323,9 @@ mod tests { let yaml = YamlLoader::load_from_str(yaml_data).unwrap(); let keymap = KeyMap::from(&yaml[0].as_hash().unwrap()).unwrap(); - let command = keymap.commands_for("normal", &Key::Ctrl('r')).expect( - "Keymap doesn't contain command", - ); + let command = keymap + .commands_for("normal", &Key::Ctrl('r')) + .expect("Keymap doesn't contain command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_up as *const usize) @@ -323,21 +335,81 @@ mod tests { #[test] fn keymap_correctly_parses_yaml_keyword_keybindings() { let mappings = vec![ - ("normal:\n space: cursor::move_up", Key::Char(' '), commands::cursor::move_up), - ("normal:\n backspace: cursor::move_up", Key::Backspace, commands::cursor::move_up), - ("normal:\n left: cursor::move_up", Key::Left, commands::cursor::move_up), - ("normal:\n right: cursor::move_up", Key::Right, commands::cursor::move_up), - ("normal:\n up: cursor::move_up", Key::Up, commands::cursor::move_up), - ("normal:\n down: cursor::move_up", Key::Down, commands::cursor::move_up), - ("normal:\n home: cursor::move_up", Key::Home, commands::cursor::move_up), - ("normal:\n end: cursor::move_up", Key::End, commands::cursor::move_up), - ("normal:\n page_up: cursor::move_up", Key::PageUp, commands::cursor::move_up), - ("normal:\n page_down: cursor::move_up", Key::PageDown, commands::cursor::move_up), - ("normal:\n delete: cursor::move_up", Key::Delete, commands::cursor::move_up), - ("normal:\n insert: cursor::move_up", Key::Insert, commands::cursor::move_up), - ("normal:\n escape: cursor::move_up", Key::Esc, commands::cursor::move_up), - ("normal:\n tab: cursor::move_up", Key::Tab, commands::cursor::move_up), - ("normal:\n enter: cursor::move_up", Key::Enter, commands::cursor::move_up) + ( + "normal:\n space: cursor::move_up", + Key::Char(' '), + commands::cursor::move_up, + ), + ( + "normal:\n backspace: cursor::move_up", + Key::Backspace, + commands::cursor::move_up, + ), + ( + "normal:\n left: cursor::move_up", + Key::Left, + commands::cursor::move_up, + ), + ( + "normal:\n right: cursor::move_up", + Key::Right, + commands::cursor::move_up, + ), + ( + "normal:\n up: cursor::move_up", + Key::Up, + commands::cursor::move_up, + ), + ( + "normal:\n down: cursor::move_up", + Key::Down, + commands::cursor::move_up, + ), + ( + "normal:\n home: cursor::move_up", + Key::Home, + commands::cursor::move_up, + ), + ( + "normal:\n end: cursor::move_up", + Key::End, + commands::cursor::move_up, + ), + ( + "normal:\n page_up: cursor::move_up", + Key::PageUp, + commands::cursor::move_up, + ), + ( + "normal:\n page_down: cursor::move_up", + Key::PageDown, + commands::cursor::move_up, + ), + ( + "normal:\n delete: cursor::move_up", + Key::Delete, + commands::cursor::move_up, + ), + ( + "normal:\n insert: cursor::move_up", + Key::Insert, + commands::cursor::move_up, + ), + ( + "normal:\n escape: cursor::move_up", + Key::Esc, + commands::cursor::move_up, + ), + ( + "normal:\n tab: cursor::move_up", + Key::Tab, + commands::cursor::move_up, + ), + ( + "normal:\n enter: cursor::move_up", + Key::Enter, + commands::cursor::move_up, + ), ]; for (binding, key, command) in mappings { @@ -345,8 +417,13 @@ mod tests { let yaml = YamlLoader::load_from_str(binding).unwrap(); let keymap = KeyMap::from(&yaml[0].as_hash().unwrap()).unwrap(); - let parsed_command = keymap.commands_for("normal", &key).expect("Keymap doesn't contain command"); - assert_eq!((parsed_command[0] as *const usize), (command as *const usize)); + let parsed_command = keymap + .commands_for("normal", &key) + .expect("Keymap doesn't contain command"); + assert_eq!( + (parsed_command[0] as *const usize), + (command as *const usize) + ); } } @@ -355,9 +432,9 @@ mod tests { // Build the keymap let keymap = KeyMap::default().unwrap(); - let command = keymap.commands_for("normal", &Key::Char('k')).expect( - "Keymap doesn't contain command", - ); + let command = keymap + .commands_for("normal", &Key::Char('k')) + .expect("Keymap doesn't contain command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_up as *const usize) @@ -376,25 +453,25 @@ mod tests { keymap.merge(other_keymap); - let mut command = keymap.commands_for("normal", &Key::Char('j')).expect( - "Keymap doesn't contain original command", - ); + let mut command = keymap + .commands_for("normal", &Key::Char('j')) + .expect("Keymap doesn't contain original command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_down as *const usize) ); - command = keymap.commands_for("normal", &Key::Char('k')).expect( - "Keymap doesn't contain overlapping command", - ); + command = keymap + .commands_for("normal", &Key::Char('k')) + .expect("Keymap doesn't contain overlapping command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_left as *const usize) ); - command = keymap.commands_for("normal", &Key::Char('l')).expect( - "Keymap doesn't contain other command", - ); + command = keymap + .commands_for("normal", &Key::Char('l')) + .expect("Keymap doesn't contain other command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_right as *const usize) @@ -408,9 +485,9 @@ mod tests { let yaml = YamlLoader::load_from_str(yaml_data).unwrap(); let keymap = KeyMap::from(&yaml[0].as_hash().unwrap()).unwrap(); - let command = keymap.commands_for("normal", &Key::Ctrl('r')).expect( - "Keymap doesn't contain command", - ); + let command = keymap + .commands_for("normal", &Key::Ctrl('r')) + .expect("Keymap doesn't contain command"); assert_eq!( (command[0] as *const usize), (commands::cursor::move_up as *const usize) diff --git a/src/lib.rs b/src/lib.rs index 96eafd08..f4df8285 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,12 +10,12 @@ extern crate lazy_static; // Private modules mod commands; mod errors; -mod util; mod input; mod models; mod presenters; +mod util; mod view; // External application API -pub use crate::models::Application; pub use crate::errors::Error; +pub use crate::models::Application; diff --git a/src/main.rs b/src/main.rs index 7f2bcbbb..720fcab9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ fn main() { let args: Vec = env::args().collect(); // Instantiate, run, and handle errors for the application. - if let Some(e) = Application::new(&args) - .and_then(|mut app| app.run()) - .err() { handle_error(&e) } + if let Some(e) = Application::new(&args).and_then(|mut app| app.run()).err() { + handle_error(&e) + } } fn handle_error(error: &Error) { diff --git a/src/models/application/clipboard.rs b/src/models/application/clipboard.rs index 01a4d8cc..f991d7d4 100644 --- a/src/models/application/clipboard.rs +++ b/src/models/application/clipboard.rs @@ -55,8 +55,8 @@ impl Clipboard { } else { // There is system clipboard content we can use. match self.content { - ClipboardContent::Inline(ref app_content) | - ClipboardContent::Block(ref app_content) => { + ClipboardContent::Inline(ref app_content) + | ClipboardContent::Block(ref app_content) => { // We have in-app clipboard content, too. Prefer // the system clipboard content if they differ. if content != *app_content { @@ -91,8 +91,8 @@ impl Clipboard { // Update the system clipboard. match self.content { - ClipboardContent::Inline(ref app_content) | - ClipboardContent::Block(ref app_content) => { + ClipboardContent::Inline(ref app_content) + | ClipboardContent::Block(ref app_content) => { if let Some(ref mut clipboard) = self.system_clipboard { return clipboard .set_contents(app_content.clone()) diff --git a/src/models/application/event.rs b/src/models/application/event.rs index 4244798d..783b54a1 100644 --- a/src/models/application/event.rs +++ b/src/models/application/event.rs @@ -5,5 +5,5 @@ use crate::models::application::modes::open::Index; pub enum Event { Key(Key), Resize, - OpenModeIndexComplete(Index) + OpenModeIndexComplete(Index), } diff --git a/src/models/application/mod.rs b/src/models/application/mod.rs index 1fe0d1ed..d4500c30 100644 --- a/src/models/application/mod.rs +++ b/src/models/application/mod.rs @@ -12,15 +12,15 @@ use self::clipboard::Clipboard; use self::modes::*; use crate::commands; use crate::errors::*; -use git2::Repository; use crate::presenters; +use crate::view::View; +use git2::Repository; use scribe::{Buffer, Workspace}; use std::cell::RefCell; use std::env; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::mpsc::{self, Receiver, Sender}; -use crate::view::View; pub enum Mode { Confirm(ConfirmMode), @@ -177,31 +177,41 @@ impl Application { pub fn mode_str(&self) -> Option<&'static str> { match self.mode { - Mode::Command(ref mode) => if mode.insert_mode() { - Some("search_select_insert") - } else { - Some("search_select") - }, - Mode::SymbolJump(ref mode) => if mode.insert_mode() { - Some("search_select_insert") - } else { - Some("search_select") - }, - Mode::Open(ref mode) => if mode.insert_mode() { - Some("search_select_insert") - } else { - Some("search_select") - }, - Mode::Theme(ref mode) => if mode.insert_mode() { - Some("search_select_insert") - } else { - Some("search_select") - }, - Mode::Syntax(ref mode) => if mode.insert_mode() { - Some("search_select_insert") - } else { - Some("search_select") - }, + Mode::Command(ref mode) => { + if mode.insert_mode() { + Some("search_select_insert") + } else { + Some("search_select") + } + } + Mode::SymbolJump(ref mode) => { + if mode.insert_mode() { + Some("search_select_insert") + } else { + Some("search_select") + } + } + Mode::Open(ref mode) => { + if mode.insert_mode() { + Some("search_select_insert") + } else { + Some("search_select") + } + } + Mode::Theme(ref mode) => { + if mode.insert_mode() { + Some("search_select_insert") + } else { + Some("search_select") + } + } + Mode::Syntax(ref mode) => { + if mode.insert_mode() { + Some("search_select_insert") + } else { + Some("search_select") + } + } Mode::Normal => Some("normal"), Mode::Path(_) => Some("path"), Mode::Confirm(_) => Some("confirm"), @@ -210,11 +220,13 @@ impl Application { Mode::LineJump(_) => Some("line_jump"), Mode::Select(_) => Some("select"), Mode::SelectLine(_) => Some("select_line"), - Mode::Search(ref mode) => if mode.insert_mode() { - Some("search_insert") - } else { - Some("search") - }, + Mode::Search(ref mode) => { + if mode.insert_mode() { + Some("search_insert") + } else { + Some("search") + } + } Mode::Exit => None, } } @@ -226,7 +238,11 @@ fn initialize_preferences() -> Rc> { )) } -fn create_workspace(view: &mut View, preferences: &Preferences, args: &[String]) -> Result { +fn create_workspace( + view: &mut View, + preferences: &Preferences, + args: &[String], +) -> Result { // Discard the executable portion of the argument list. let mut path_args = args.iter().skip(1).peekable(); @@ -242,28 +258,29 @@ fn create_workspace(view: &mut View, preferences: &Preferences, args: &[String]) let workspace_dir = env::current_dir()?; let syntax_path = user_syntax_path()?; - let mut workspace = Workspace::new( - &workspace_dir, - syntax_path.as_deref() - ).chain_err(|| WORKSPACE_INIT_FAILED)?; + let mut workspace = Workspace::new(&workspace_dir, syntax_path.as_deref()) + .chain_err(|| WORKSPACE_INIT_FAILED)?; // If the first argument was a directory, we've navigated into // it; skip it before evaluating file args, lest we interpret // it again as a non-existent file and create a buffer for it. - if workspace_dir != initial_dir { path_args.next(); } + if workspace_dir != initial_dir { + path_args.next(); + } // Try to open specified files. for path_arg in path_args { let path = Path::new(&path_arg); - if path.is_dir() { continue; } + if path.is_dir() { + continue; + } // Check if the user has provided any syntax preference for this file. // If not, a default one will be applied on calling workspace.add_buffer() - let syntax_definition = - preferences.syntax_definition_name(path).and_then(|name| { - workspace.syntax_set.find_syntax_by_name(&name).cloned() - }); + let syntax_definition = preferences + .syntax_definition_name(path) + .and_then(|name| workspace.syntax_set.find_syntax_by_name(&name).cloned()); // Open the specified path if it exists, or // create a new buffer pointing to it if it doesn't. @@ -308,17 +325,17 @@ fn user_syntax_path() -> Result> { #[cfg(test)] mod tests { + use super::preferences::Preferences; use super::Application; use crate::view::View; - use super::preferences::Preferences; - use yaml_rust::YamlLoader; use scribe::Buffer; use std::cell::RefCell; use std::env; use std::path::Path; use std::rc::Rc; use std::sync::mpsc; + use yaml_rust::YamlLoader; #[test] fn application_uses_file_arguments_to_load_contents_into_buffers_when_files_exist() { @@ -331,7 +348,12 @@ mod tests { buffer.path ); assert_eq!( - application.workspace.current_buffer.as_ref().unwrap().data(), + application + .workspace + .current_buffer + .as_ref() + .unwrap() + .data(), buffer.data() ); } @@ -345,11 +367,20 @@ mod tests { application.workspace.current_buffer.as_ref().unwrap().path, Some(env::current_dir().unwrap().join("non_existent_file")) ); - assert_eq!(application.workspace.current_buffer.as_ref().unwrap().data(), ""); + assert_eq!( + application + .workspace + .current_buffer + .as_ref() + .unwrap() + .data(), + "" + ); } #[test] - fn create_workspace_correctly_applies_user_defined_syntax_when_opening_buffer_from_command_line() { + fn create_workspace_correctly_applies_user_defined_syntax_when_opening_buffer_from_command_line( + ) { let data = YamlLoader::load_from_str("types:\n xyz:\n syntax: Rust").unwrap(); let preferences = Rc::new(RefCell::new(Preferences::new(data.into_iter().nth(0)))); let (event_channel, _) = mpsc::channel(); @@ -359,7 +390,14 @@ mod tests { let workspace = super::create_workspace(&mut view, &preferences.borrow(), &args).unwrap(); assert_eq!( - workspace.current_buffer.as_ref().unwrap().syntax_definition.as_ref().unwrap().name, + workspace + .current_buffer + .as_ref() + .unwrap() + .syntax_definition + .as_ref() + .unwrap() + .name, "Rust" ); } diff --git a/src/models/application/modes/command/displayable_command.rs b/src/models/application/modes/command/displayable_command.rs index d6871096..7a636f64 100644 --- a/src/models/application/modes/command/displayable_command.rs +++ b/src/models/application/modes/command/displayable_command.rs @@ -1,5 +1,5 @@ -use std::fmt; use crate::commands::Command; +use std::fmt; // Utility type to make an Amp command function presentable (via the // Display trait), which is required for any type used in search/select mode. diff --git a/src/models/application/modes/command/mod.rs b/src/models/application/modes/command/mod.rs index 0f0adbe5..5f68f6b4 100644 --- a/src/models/application/modes/command/mod.rs +++ b/src/models/application/modes/command/mod.rs @@ -1,13 +1,13 @@ mod displayable_command; -use fragment; +pub use self::displayable_command::DisplayableCommand; +use crate::commands::{self, Command}; +use crate::models::application::modes::{SearchSelectConfig, SearchSelectMode}; use crate::util::SelectableVec; +use fragment; use std::collections::HashMap; use std::fmt; use std::slice::Iter; -use crate::models::application::modes::{SearchSelectMode, SearchSelectConfig}; -use crate::commands::{self, Command}; -pub use self::displayable_command::DisplayableCommand; pub struct CommandMode { insert: bool, @@ -40,26 +40,22 @@ impl SearchSelectMode for CommandMode { let commands: Vec<&'static str> = self.commands.keys().copied().collect(); // Find the commands we're looking for using the query. - let results = fragment::matching::find( - &self.input, - &commands, - self.config.max_results - ); + let results = fragment::matching::find(&self.input, &commands, self.config.max_results); // We don't care about the result objects; we just want // the underlying commands. Map the collection to get these. self.results = SelectableVec::new( results - .into_iter() - .filter_map(|result| { - self.commands.get(*result).map(|command| { - DisplayableCommand{ - description: *result, - command: *command - } + .into_iter() + .filter_map(|result| { + self.commands + .get(*result) + .map(|command| DisplayableCommand { + description: *result, + command: *command, + }) }) - }) - .collect() + .collect(), ); } diff --git a/src/models/application/modes/jump/mod.rs b/src/models/application/modes/jump/mod.rs index 6098b284..b1d477ac 100644 --- a/src/models/application/modes/jump/mod.rs +++ b/src/models/application/modes/jump/mod.rs @@ -1,15 +1,15 @@ -mod tag_generator; mod single_character_tag_generator; +mod tag_generator; -use luthor::token::Category; -use crate::util::movement_lexer; -use std::collections::HashMap; -use scribe::buffer::{Distance, Position}; +use self::single_character_tag_generator::SingleCharacterTagGenerator; +use self::tag_generator::TagGenerator; use crate::models::application::modes::select::SelectMode; use crate::models::application::modes::select_line::SelectLineMode; -use self::tag_generator::TagGenerator; -use self::single_character_tag_generator::SingleCharacterTagGenerator; +use crate::util::movement_lexer; use crate::view::{LexemeMapper, MappedLexeme}; +use luthor::token::Category; +use scribe::buffer::{Distance, Position}; +use std::collections::HashMap; /// Used to compose select and jump modes, allowing jump mode /// to be used for cursor navigation (to select a range of text). @@ -46,7 +46,7 @@ impl JumpMode { tag_positions: HashMap::new(), tag_generator: TagGenerator::new(), single_characters: SingleCharacterTagGenerator::new(), - current_position: Position{ line: 0, offset: 0 }, + current_position: Position { line: 0, offset: 0 }, mapped_lexeme_values: Vec::new(), } } @@ -80,12 +80,10 @@ impl LexemeMapper for JumpMode { let distance = Distance::of_str(&subtoken.lexeme); // We don't do anything to whitespace tokens. - self.mapped_lexeme_values.push( - MappedLexemeValue::Text(( - subtoken.lexeme, - self.current_position - )) - ); + self.mapped_lexeme_values.push(MappedLexemeValue::Text(( + subtoken.lexeme, + self.current_position, + ))); // Advance beyond this subtoken. self.current_position += distance; @@ -108,43 +106,30 @@ impl LexemeMapper for JumpMode { // Keep a copy of the current tag // that we'll use to loan out a lexeme. - self.mapped_lexeme_values.push( - MappedLexemeValue::Tag(( - tag.clone(), - self.current_position - )) - ); + self.mapped_lexeme_values + .push(MappedLexemeValue::Tag((tag.clone(), self.current_position))); // Track the location of this tag. self.tag_positions.insert(tag, self.current_position); // Advance beyond this tag. - self.current_position += Distance{ + self.current_position += Distance { lines: 0, - offset: tag_len + offset: tag_len, }; - let suffix: String = - subtoken - .lexeme - .chars() - .skip(tag_len) - .collect(); + let suffix: String = subtoken.lexeme.chars().skip(tag_len).collect(); let suffix_len = suffix.len(); if suffix_len > 0 { // Push the suffix into the mapped set. - self.mapped_lexeme_values.push( - MappedLexemeValue::Text(( - suffix, - self.current_position - )) - ); + self.mapped_lexeme_values + .push(MappedLexemeValue::Text((suffix, self.current_position))); // Advance beyond this suffix. - self.current_position += Distance{ + self.current_position += Distance { lines: 0, - offset: suffix_len + offset: suffix_len, }; } } @@ -152,12 +137,10 @@ impl LexemeMapper for JumpMode { let distance = Distance::of_str(&subtoken.lexeme); // We couldn't tag this subtoken; move along. - self.mapped_lexeme_values.push( - MappedLexemeValue::Text(( - subtoken.lexeme, - self.current_position - )) - ); + self.mapped_lexeme_values.push(MappedLexemeValue::Text(( + subtoken.lexeme, + self.current_position, + ))); // Advance beyond this subtoken. self.current_position += distance; @@ -166,43 +149,34 @@ impl LexemeMapper for JumpMode { } } - self.mapped_lexeme_values.iter().map(|mapped_lexeme| { - match *mapped_lexeme { - MappedLexemeValue::Tag((ref lexeme, _)) => { - MappedLexeme::Focused(lexeme.as_str()) - }, - MappedLexemeValue::Text((ref lexeme, _)) => { - MappedLexeme::Blurred(lexeme.as_str()) - }, - } - }).collect() + self.mapped_lexeme_values + .iter() + .map(|mapped_lexeme| match *mapped_lexeme { + MappedLexemeValue::Tag((ref lexeme, _)) => MappedLexeme::Focused(lexeme.as_str()), + MappedLexemeValue::Text((ref lexeme, _)) => MappedLexeme::Blurred(lexeme.as_str()), + }) + .collect() } } #[cfg(test)] mod tests { + use super::JumpMode; use crate::view::{LexemeMapper, MappedLexeme}; use scribe::buffer::Position; - use super::JumpMode; #[test] fn map_returns_the_correct_lexemes_in_first_phase() { let mut jump_mode = JumpMode::new(0); assert_eq!( - jump_mode.map("amp", Position{ line: 0, offset: 0 }), - vec![ - MappedLexeme::Focused("a"), - MappedLexeme::Blurred("mp") - ] + jump_mode.map("amp", Position { line: 0, offset: 0 }), + vec![MappedLexeme::Focused("a"), MappedLexeme::Blurred("mp")] ); assert_eq!( - jump_mode.map("editor", Position{ line: 0, offset: 3 }), - vec![ - MappedLexeme::Focused("b"), - MappedLexeme::Blurred("ditor") - ] + jump_mode.map("editor", Position { line: 0, offset: 3 }), + vec![MappedLexeme::Focused("b"), MappedLexeme::Blurred("ditor")] ); } @@ -212,19 +186,13 @@ mod tests { jump_mode.first_phase = false; assert_eq!( - jump_mode.map("amp", Position{ line: 0, offset: 0 }), - vec![ - MappedLexeme::Focused("aa"), - MappedLexeme::Blurred("p") - ] + jump_mode.map("amp", Position { line: 0, offset: 0 }), + vec![MappedLexeme::Focused("aa"), MappedLexeme::Blurred("p")] ); assert_eq!( - jump_mode.map("editor", Position{ line: 0, offset: 3 }), - vec![ - MappedLexeme::Focused("ab"), - MappedLexeme::Blurred("itor") - ] + jump_mode.map("editor", Position { line: 0, offset: 3 }), + vec![MappedLexeme::Focused("ab"), MappedLexeme::Blurred("itor")] ); } @@ -234,7 +202,7 @@ mod tests { jump_mode.first_phase = false; assert_eq!( - jump_mode.map("do a test", Position{ line: 0, offset: 0 }), + jump_mode.map("do a test", Position { line: 0, offset: 0 }), vec![ MappedLexeme::Focused("aa"), MappedLexeme::Blurred(" "), @@ -254,19 +222,17 @@ mod tests { // Adding space to a lexeme invokes sublexeme handling, since we split // based on whitespace. It's important to ensure the tracked positions // take this into account, too, which is why there's leading whitespace. - jump_mode.map(" amp", Position{ line: 0, offset: 0 }); - jump_mode.map("editor", Position{ line: 0, offset: 5 }); - - assert_eq!(*jump_mode.tag_positions.get("aa").unwrap(), - Position { - line: 0, - offset: 2, - }); - assert_eq!(*jump_mode.tag_positions.get("ab").unwrap(), - Position { - line: 0, - offset: 5, - }); + jump_mode.map(" amp", Position { line: 0, offset: 0 }); + jump_mode.map("editor", Position { line: 0, offset: 5 }); + + assert_eq!( + *jump_mode.tag_positions.get("aa").unwrap(), + Position { line: 0, offset: 2 } + ); + assert_eq!( + *jump_mode.tag_positions.get("ab").unwrap(), + Position { line: 0, offset: 5 } + ); } #[test] @@ -274,20 +240,14 @@ mod tests { let mut jump_mode = JumpMode::new(0); assert_eq!( - jump_mode.map("amp", Position{ line: 0, offset: 0 }), - vec![ - MappedLexeme::Focused("a"), - MappedLexeme::Blurred("mp") - ] + jump_mode.map("amp", Position { line: 0, offset: 0 }), + vec![MappedLexeme::Focused("a"), MappedLexeme::Blurred("mp")] ); jump_mode.reset_display(); assert_eq!( - jump_mode.map("editor", Position{ line: 0, offset: 3 }), - vec![ - MappedLexeme::Focused("a"), - MappedLexeme::Blurred("ditor") - ] + jump_mode.map("editor", Position { line: 0, offset: 3 }), + vec![MappedLexeme::Focused("a"), MappedLexeme::Blurred("ditor")] ); } @@ -297,20 +257,14 @@ mod tests { jump_mode.first_phase = false; assert_eq!( - jump_mode.map("amp", Position{ line: 0, offset: 0 }), - vec![ - MappedLexeme::Focused("aa"), - MappedLexeme::Blurred("p") - ] + jump_mode.map("amp", Position { line: 0, offset: 0 }), + vec![MappedLexeme::Focused("aa"), MappedLexeme::Blurred("p")] ); jump_mode.reset_display(); assert_eq!( - jump_mode.map("editor", Position{ line: 0, offset: 3 }), - vec![ - MappedLexeme::Focused("aa"), - MappedLexeme::Blurred("itor") - ] + jump_mode.map("editor", Position { line: 0, offset: 3 }), + vec![MappedLexeme::Focused("aa"), MappedLexeme::Blurred("itor")] ); } @@ -323,11 +277,8 @@ mod tests { // second character to ensure splitting off the first // two characters would cause a panic. assert_eq!( - jump_mode.map("eéditor", Position{ line: 0, offset: 0 }), - vec![ - MappedLexeme::Focused("aa"), - MappedLexeme::Blurred("ditor") - ] + jump_mode.map("eéditor", Position { line: 0, offset: 0 }), + vec![MappedLexeme::Focused("aa"), MappedLexeme::Blurred("ditor")] ); } @@ -336,13 +287,12 @@ mod tests { let mut jump_mode = JumpMode::new(0); jump_mode.first_phase = false; - jump_mode.map("amp", Position{ line: 0, offset: 0 }); - jump_mode.map("editor", Position{ line: 1, offset: 3 }); - assert_eq!(jump_mode.map_tag("ab"), - Some(&Position { - line: 1, - offset: 3, - })); + jump_mode.map("amp", Position { line: 0, offset: 0 }); + jump_mode.map("editor", Position { line: 1, offset: 3 }); + assert_eq!( + jump_mode.map_tag("ab"), + Some(&Position { line: 1, offset: 3 }) + ); assert_eq!(jump_mode.map_tag("none"), None); } @@ -352,7 +302,7 @@ mod tests { jump_mode.first_phase = false; assert_eq!( - jump_mode.map("amp_editor", Position{ line: 0, offset: 0}), + jump_mode.map("amp_editor", Position { line: 0, offset: 0 }), vec![ MappedLexeme::Focused("aa"), MappedLexeme::Blurred("p"), diff --git a/src/models/application/modes/jump/single_character_tag_generator.rs b/src/models/application/modes/jump/single_character_tag_generator.rs index 0b02245e..618269c3 100644 --- a/src/models/application/modes/jump/single_character_tag_generator.rs +++ b/src/models/application/modes/jump/single_character_tag_generator.rs @@ -7,7 +7,7 @@ pub struct SingleCharacterTagGenerator { impl SingleCharacterTagGenerator { pub fn new() -> SingleCharacterTagGenerator { - SingleCharacterTagGenerator{ index: 96 } + SingleCharacterTagGenerator { index: 96 } } /// Restarts the tag generator sequence. @@ -42,16 +42,13 @@ mod tests { #[test] fn it_returns_a_lowercase_set_of_alphabetical_characters_excluding_f() { let generator = SingleCharacterTagGenerator::new(); - let expected_result = (97..123).fold( - String::new(), - |mut acc, i| { - // Skip f - if i != 102 { - acc.push((i as u8) as char); - } - acc + let expected_result = (97..123).fold(String::new(), |mut acc, i| { + // Skip f + if i != 102 { + acc.push((i as u8) as char); } - ); + acc + }); let result: String = generator.collect(); assert_eq!(result, expected_result); diff --git a/src/models/application/modes/mod.rs b/src/models/application/modes/mod.rs index 015998f5..12c55a76 100644 --- a/src/models/application/modes/mod.rs +++ b/src/models/application/modes/mod.rs @@ -1,5 +1,5 @@ -mod confirm; mod command; +mod confirm; pub mod jump; mod line_jump; pub mod open; @@ -12,14 +12,14 @@ mod symbol_jump; mod syntax; mod theme; -pub use self::confirm::ConfirmMode; pub use self::command::CommandMode; +pub use self::confirm::ConfirmMode; pub use self::jump::JumpMode; pub use self::line_jump::LineJumpMode; -pub use self::path::PathMode; pub use self::open::OpenMode; +pub use self::path::PathMode; pub use self::search::SearchMode; -pub use self::search_select::{SearchSelectMode, SearchSelectConfig}; +pub use self::search_select::{SearchSelectConfig, SearchSelectMode}; pub use self::select::SelectMode; pub use self::select_line::SelectLineMode; pub use self::symbol_jump::SymbolJumpMode; diff --git a/src/models/application/modes/open/exclusions.rs b/src/models/application/modes/open/exclusions.rs index 9ed8b993..ff2a1222 100644 --- a/src/models/application/modes/open/exclusions.rs +++ b/src/models/application/modes/open/exclusions.rs @@ -8,9 +8,8 @@ pub fn parse(exclusion_data: &[Yaml]) -> Result> { for exclusion in exclusion_data.iter() { if let Yaml::String(ref pattern) = *exclusion { mapped_exclusions.push( - ExclusionPattern::new(pattern).chain_err(|| { - format!("Failed to parse exclusion pattern: {}", pattern) - })? + ExclusionPattern::new(pattern) + .chain_err(|| format!("Failed to parse exclusion pattern: {}", pattern))?, ); } else { bail!("Found a non-string exclusion that can't be parsed."); diff --git a/src/models/application/modes/open/mod.rs b/src/models/application/modes/open/mod.rs index 0ac47581..841e731b 100644 --- a/src/models/application/modes/open/mod.rs +++ b/src/models/application/modes/open/mod.rs @@ -1,22 +1,22 @@ mod displayable_path; pub mod exclusions; +pub use self::displayable_path::DisplayablePath; +use crate::models::application::modes::{SearchSelectConfig, SearchSelectMode}; +use crate::models::application::Event; +use crate::util::SelectableVec; +use bloodhound::ExclusionPattern; +pub use bloodhound::Index; use std::fmt; use std::path::PathBuf; use std::slice::Iter; -use bloodhound::ExclusionPattern; -use crate::util::SelectableVec; -use crate::models::application::modes::{SearchSelectMode, SearchSelectConfig}; -use crate::models::application::Event; use std::sync::mpsc::Sender; use std::thread; -pub use bloodhound::Index; -pub use self::displayable_path::DisplayablePath; #[derive(PartialEq)] pub enum OpenModeIndex { Complete(Index), - Indexing(PathBuf) + Indexing(PathBuf), } pub struct OpenMode { @@ -28,15 +28,18 @@ pub struct OpenMode { } impl OpenMode { - pub fn new(path: PathBuf, exclusions: Option>, events: Sender, config: SearchSelectConfig) -> OpenMode { + pub fn new( + path: PathBuf, + exclusions: Option>, + events: Sender, + config: SearchSelectConfig, + ) -> OpenMode { // Build and populate the index in a separate thread. let index_path = path.clone(); thread::spawn(move || { let mut index = Index::new(index_path); index.populate(exclusions, false); - let _ = events.send( - Event::OpenModeIndexComplete(index) - ); + let _ = events.send(Event::OpenModeIndexComplete(index)); }); OpenMode { @@ -61,17 +64,15 @@ impl fmt::Display for OpenMode { impl SearchSelectMode for OpenMode { fn search(&mut self) { - let results = - if let OpenModeIndex::Complete(ref index) = self.index { - index.find( - &self.input.to_lowercase(), - self.config.max_results - ).into_iter() + let results = if let OpenModeIndex::Complete(ref index) = self.index { + index + .find(&self.input.to_lowercase(), self.config.max_results) + .into_iter() .map(|path| DisplayablePath(path.to_path_buf())) .collect() - } else { - vec![] - }; + } else { + vec![] + }; self.results = SelectableVec::new(results); } diff --git a/src/models/application/modes/path.rs b/src/models/application/modes/path.rs index c73a6132..af4378d4 100644 --- a/src/models/application/modes/path.rs +++ b/src/models/application/modes/path.rs @@ -9,7 +9,7 @@ impl PathMode { pub fn new(initial_path: String) -> PathMode { PathMode { input: initial_path, - save_on_accept: false + save_on_accept: false, } } pub fn push_char(&mut self, c: char) { diff --git a/src/models/application/modes/search.rs b/src/models/application/modes/search.rs index 44f97592..d21de0e1 100644 --- a/src/models/application/modes/search.rs +++ b/src/models/application/modes/search.rs @@ -1,7 +1,7 @@ use crate::errors::*; use crate::util::SelectableVec; -use std::fmt; use scribe::buffer::{Buffer, Distance, Range}; +use std::fmt; pub struct SearchMode { pub insert: bool, @@ -31,14 +31,13 @@ impl SearchMode { // Buffer search returns match starting positions, but we'd like ranges. // This maps the positions to ranges using the search query distance // before storing them. - self.results = Some( - SelectableVec::new( - buffer.search(query) - .into_iter() - .map(|start| Range::new(start, start + distance)) - .collect() - ) - ); + self.results = Some(SelectableVec::new( + buffer + .search(query) + .into_iter() + .map(|start| Range::new(start, start + distance)) + .collect(), + )); Ok(()) } @@ -52,8 +51,8 @@ impl fmt::Display for SearchMode { #[cfg(test)] mod tests { - use scribe::buffer::{Buffer, Position, Range}; use super::SearchMode; + use scribe::buffer::{Buffer, Position, Range}; #[test] fn search_populates_results_with_correct_ranges() { @@ -67,12 +66,12 @@ mod tests { *mode.results.unwrap(), vec![ Range::new( - Position{ line: 0, offset: 0 }, - Position{ line: 0, offset: 4 }, + Position { line: 0, offset: 0 }, + Position { line: 0, offset: 4 }, ), Range::new( - Position{ line: 1, offset: 0 }, - Position{ line: 1, offset: 4 }, + Position { line: 1, offset: 0 }, + Position { line: 1, offset: 4 }, ), ] ); diff --git a/src/models/application/modes/search_select.rs b/src/models/application/modes/search_select.rs index 3353b5f6..0cc3bda9 100644 --- a/src/models/application/modes/search_select.rs +++ b/src/models/application/modes/search_select.rs @@ -8,9 +8,7 @@ pub struct SearchSelectConfig { impl Default for SearchSelectConfig { fn default() -> SearchSelectConfig { - SearchSelectConfig { - max_results: 5, - } + SearchSelectConfig { max_results: 5 } } } @@ -47,13 +45,15 @@ pub trait SearchSelectMode: Display { // Find the last word boundary (transition to/from whitespace), using // using fold to carry the previous character's type forward. let mut boundary_index = 0; - query.char_indices().fold(true, |was_whitespace, (index, c)| { - if c.is_whitespace() != was_whitespace { - boundary_index = index; - } + query + .char_indices() + .fold(true, |was_whitespace, (index, c)| { + if c.is_whitespace() != was_whitespace { + boundary_index = index; + } - c.is_whitespace() - }); + c.is_whitespace() + }); query.truncate(boundary_index); } @@ -61,9 +61,9 @@ pub trait SearchSelectMode: Display { #[cfg(test)] mod tests { + use super::{SearchSelectConfig, SearchSelectMode}; use std::fmt; use std::slice::Iter; - use super::{SearchSelectMode, SearchSelectConfig}; #[derive(Default)] struct TestMode { @@ -84,41 +84,63 @@ mod tests { &mut self.input } - fn search(&mut self) { } - fn insert_mode(&self) -> bool { false } - fn set_insert_mode(&mut self, _: bool) { } - fn results(&self) -> Iter { self.results.iter() } - fn selection(&self) -> Option<&String> { Some(&self.selection) } - fn selected_index(&self) -> usize { 0 } - fn select_previous(&mut self) { } - fn select_next(&mut self) { } - fn config(&self) -> &SearchSelectConfig { &self.config } + fn search(&mut self) {} + fn insert_mode(&self) -> bool { + false + } + fn set_insert_mode(&mut self, _: bool) {} + fn results(&self) -> Iter { + self.results.iter() + } + fn selection(&self) -> Option<&String> { + Some(&self.selection) + } + fn selected_index(&self) -> usize { + 0 + } + fn select_previous(&mut self) {} + fn select_next(&mut self) {} + fn config(&self) -> &SearchSelectConfig { + &self.config + } } #[test] fn push_search_char_updates_query() { - let mut mode = TestMode{ .. Default::default() }; + let mut mode = TestMode { + ..Default::default() + }; mode.push_search_char('a'); assert_eq!(mode.query(), "a"); } #[test] fn pop_search_token_pops_all_characters_when_on_only_token() { - let mut mode = TestMode{ input: String::from("amp"), .. Default::default() }; + let mut mode = TestMode { + input: String::from("amp"), + ..Default::default() + }; mode.pop_search_token(); assert_eq!(mode.query(), ""); } #[test] - fn pop_search_token_pops_all_adjacent_non_whitespace_characters_when_on_non_whitespace_character() { - let mut mode = TestMode{ input: String::from("amp editor"), .. Default::default() }; + fn pop_search_token_pops_all_adjacent_non_whitespace_characters_when_on_non_whitespace_character( + ) { + let mut mode = TestMode { + input: String::from("amp editor"), + ..Default::default() + }; mode.pop_search_token(); assert_eq!(mode.query(), "amp "); } #[test] fn pop_search_token_pops_all_whitespace_characters_when_on_whitespace_character() { - let mut mode = TestMode{ input: String::from("amp "), .. Default::default() }; + let mut mode = TestMode { + input: String::from("amp "), + ..Default::default() + }; mode.pop_search_token(); assert_eq!(mode.query(), "amp"); } diff --git a/src/models/application/modes/symbol_jump.rs b/src/models/application/modes/symbol_jump.rs index f508b8d2..a421f08f 100644 --- a/src/models/application/modes/symbol_jump.rs +++ b/src/models/application/modes/symbol_jump.rs @@ -1,15 +1,15 @@ +use crate::errors::*; +use crate::models::application::modes::{SearchSelectConfig, SearchSelectMode}; +use crate::util::SelectableVec; use fragment; use fragment::matching::AsStr; use scribe::buffer::{Position, Token, TokenSet}; -use syntect::highlighting::ScopeSelectors; -use crate::errors::*; -use crate::util::SelectableVec; +use std::clone::Clone; use std::fmt; use std::iter::Iterator; -use std::clone::Clone; -use std::str::FromStr; use std::slice::Iter; -use crate::models::application::modes::{SearchSelectMode, SearchSelectConfig}; +use std::str::FromStr; +use syntect::highlighting::ScopeSelectors; pub struct SymbolJumpMode { insert: bool, @@ -33,7 +33,10 @@ impl fmt::Display for Symbol { impl Clone for Symbol { fn clone(&self) -> Symbol { - Symbol{ token: self.token.clone(), position: self.position } + Symbol { + token: self.token.clone(), + position: self.position, + } } fn clone_from(&mut self, source: &Self) { @@ -115,68 +118,67 @@ impl SearchSelectMode for SymbolJumpMode { } } -fn symbols<'a, T>(tokens: T) -> Vec where T: Iterator> { - let eligible_scopes = ScopeSelectors::from_str( - "entity.name.function, entity.name.class, entity.name.struct" - ).unwrap(); - tokens.filter_map(|token| { - if let Token::Lexeme(lexeme) = token { - // Build a symbol, provided it's of the right type. - if eligible_scopes.does_match(lexeme.scope.as_slice()).is_some() { - return Some(Symbol { - token: lexeme.value.to_string(), - position: lexeme.position, - }) - } - } - - None - }).collect() +fn symbols<'a, T>(tokens: T) -> Vec +where + T: Iterator>, +{ + let eligible_scopes = + ScopeSelectors::from_str("entity.name.function, entity.name.class, entity.name.struct") + .unwrap(); + tokens + .filter_map(|token| { + if let Token::Lexeme(lexeme) = token { + // Build a symbol, provided it's of the right type. + if eligible_scopes + .does_match(lexeme.scope.as_slice()) + .is_some() + { + return Some(Symbol { + token: lexeme.value.to_string(), + position: lexeme.position, + }); + } + } + + None + }) + .collect() } #[cfg(test)] mod tests { + use super::{symbols, Symbol}; use scribe::buffer::{Lexeme, Position, ScopeStack, Token}; use std::str::FromStr; - use super::{Symbol, symbols}; #[test] fn symbols_are_limited_to_functions() { let tokens = vec![ - Token::Lexeme( - Lexeme{ - value: "text", - position: Position{ - line: 0, - offset: 0 - }, - scope: ScopeStack::from_str("meta.block.rust").unwrap() - } - ), - Token::Lexeme( - Lexeme{ - value: "function", - position: Position{ - line: 1, - offset: 0 - }, - scope: ScopeStack::from_str("entity.name.function").unwrap() - } - ), - Token::Lexeme( - Lexeme{ - value: "non-function", - position: Position{ - line: 2, - offset: 0 - }, - scope: ScopeStack::from_str("meta.entity.name.function").unwrap() - } - ) + Token::Lexeme(Lexeme { + value: "text", + position: Position { line: 0, offset: 0 }, + scope: ScopeStack::from_str("meta.block.rust").unwrap(), + }), + Token::Lexeme(Lexeme { + value: "function", + position: Position { line: 1, offset: 0 }, + scope: ScopeStack::from_str("entity.name.function").unwrap(), + }), + Token::Lexeme(Lexeme { + value: "non-function", + position: Position { line: 2, offset: 0 }, + scope: ScopeStack::from_str("meta.entity.name.function").unwrap(), + }), ]; let results = symbols(tokens.into_iter()); assert_eq!(results.len(), 1); - assert_eq!(results.first().unwrap(), &Symbol{ token: "function".to_string(), position: Position{ line: 1, offset: 0 }}); + assert_eq!( + results.first().unwrap(), + &Symbol { + token: "function".to_string(), + position: Position { line: 1, offset: 0 } + } + ); } } diff --git a/src/models/application/modes/syntax.rs b/src/models/application/modes/syntax.rs index 5cdecfaa..50b94e5e 100644 --- a/src/models/application/modes/syntax.rs +++ b/src/models/application/modes/syntax.rs @@ -1,8 +1,8 @@ -use fragment; +use crate::models::application::modes::{SearchSelectConfig, SearchSelectMode}; use crate::util::SelectableVec; +use fragment; use std::fmt; use std::slice::Iter; -use crate::models::application::modes::{SearchSelectMode, SearchSelectConfig}; pub struct SyntaxMode { insert: bool, @@ -33,20 +33,12 @@ impl fmt::Display for SyntaxMode { impl SearchSelectMode for SyntaxMode { fn search(&mut self) { // Find the themes we're looking for using the query. - let results = fragment::matching::find( - &self.input, - &self.syntaxes, - self.config.max_results - ); + let results = + fragment::matching::find(&self.input, &self.syntaxes, self.config.max_results); // We don't care about the result objects; we just want // the underlying symbols. Map the collection to get these. - self.results = SelectableVec::new( - results - .into_iter() - .map(|r| r.clone()) - .collect() - ); + self.results = SelectableVec::new(results.into_iter().map(|r| r.clone()).collect()); } fn query(&mut self) -> &mut String { diff --git a/src/models/application/modes/theme.rs b/src/models/application/modes/theme.rs index 3c3d311e..d093fd36 100644 --- a/src/models/application/modes/theme.rs +++ b/src/models/application/modes/theme.rs @@ -1,8 +1,8 @@ -use fragment; +use crate::models::application::modes::{SearchSelectConfig, SearchSelectMode}; use crate::util::SelectableVec; +use fragment; use std::fmt; use std::slice::Iter; -use crate::models::application::modes::{SearchSelectMode, SearchSelectConfig}; pub struct ThemeMode { insert: bool, @@ -37,12 +37,7 @@ impl SearchSelectMode for ThemeMode { // We don't care about the result objects; we just want // the underlying symbols. Map the collection to get these. - self.results = SelectableVec::new( - results - .into_iter() - .map(|r| r.clone()) - .collect() - ); + self.results = SelectableVec::new(results.into_iter().map(|r| r.clone()).collect()); } fn query(&mut self) -> &mut String { diff --git a/src/models/application/preferences/mod.rs b/src/models/application/preferences/mod.rs index d9b8cdbe..abf9c3a2 100644 --- a/src/models/application/preferences/mod.rs +++ b/src/models/application/preferences/mod.rs @@ -1,15 +1,15 @@ -use app_dirs2::{app_dir, app_root, get_app_root, AppDataType, AppInfo}; -use bloodhound::ExclusionPattern; use crate::errors::*; use crate::input::KeyMap; use crate::models::application::modes::open; +use crate::models::application::modes::SearchSelectConfig; +use app_dirs2::{app_dir, app_root, get_app_root, AppDataType, AppInfo}; +use bloodhound::ExclusionPattern; use scribe::Buffer; use std::fs::OpenOptions; use std::io::Read; use std::path::{Path, PathBuf}; use std::process; use yaml_rust::yaml::{Hash, Yaml, YamlLoader}; -use crate::models::application::modes::SearchSelectConfig; const APP_INFO: AppInfo = AppInfo { name: "amp", @@ -48,7 +48,7 @@ impl Preferences { default: load_default_document().expect("Failed to load default preferences!"), data, keymap: KeyMap::default().expect("Failed to load default keymap!"), - theme: None + theme: None, } } @@ -56,20 +56,21 @@ impl Preferences { pub fn load() -> Result { let default = load_default_document()?; let data = load_document()?; - let keymap = load_keymap( - data.as_ref().and_then(|data| data["keymap"].as_hash()) - )?; + let keymap = load_keymap(data.as_ref().and_then(|data| data["keymap"].as_hash()))?; - Ok(Preferences { default, data, keymap, theme: None }) + Ok(Preferences { + default, + data, + keymap, + theme: None, + }) } /// Reloads all user preferences from disk and merges them with defaults. pub fn reload(&mut self) -> Result<()> { let default = load_default_document()?; let data = load_document()?; - let keymap = load_keymap( - data.as_ref().and_then(|data| data["keymap"].as_hash()) - )?; + let keymap = load_keymap(data.as_ref().and_then(|data| data["keymap"].as_hash()))?; self.default = default; self.data = data; @@ -102,9 +103,8 @@ impl Preferences { /// if they don't already exist. pub fn edit() -> Result { // Build the path, creating parent directories, if required. - let mut config_path = - app_root(AppDataType::UserConfig, &APP_INFO) - .chain_err(|| "Couldn't create or open application config directory")?; + let mut config_path = app_root(AppDataType::UserConfig, &APP_INFO) + .chain_err(|| "Couldn't create or open application config directory")?; config_path.push(FILE_NAME); // Load the buffer, falling back to a @@ -120,17 +120,23 @@ impl Preferences { /// the configuration file, and then the default value. pub fn theme(&self) -> &str { // Return the mutable in-memory value, if set. - if let Some(ref theme) = self.theme { return theme; } + if let Some(ref theme) = self.theme { + return theme; + } self.data .as_ref() - .and_then(|data| if let Yaml::String(ref theme) = data[THEME_KEY] { - Some(theme.as_str()) - } else { - None - }) + .and_then(|data| { + if let Yaml::String(ref theme) = data[THEME_KEY] { + Some(theme.as_str()) + } else { + None + } + }) .unwrap_or_else(|| { - self.default[THEME_KEY].as_str().expect("Couldn't find default theme name!") + self.default[THEME_KEY] + .as_str() + .expect("Couldn't find default theme name!") }) } @@ -162,7 +168,8 @@ impl Preferences { None }) .unwrap_or_else(|| { - self.default[TAB_WIDTH_KEY].as_i64() + self.default[TAB_WIDTH_KEY] + .as_i64() .expect("Couldn't find default tab width setting!") as usize }) } @@ -194,7 +201,8 @@ impl Preferences { None }) .unwrap_or_else(|| { - self.default[SOFT_TABS_KEY].as_bool() + self.default[SOFT_TABS_KEY] + .as_bool() .expect("Couldn't find default soft tabs setting!") }) } @@ -203,32 +211,35 @@ impl Preferences { self.data .as_ref() .and_then(|data| match data[LINE_LENGTH_GUIDE_KEY] { - Yaml::Integer(line_length) => Some(line_length as usize), - Yaml::Boolean(line_length_guide) => { - let default = self.default[LINE_LENGTH_GUIDE_KEY].as_i64() - .expect("Couldn't find default line length guide setting!"); - - if line_length_guide { - Some(default as usize) - } else { - None - } - } - _ => None, - }) - + Yaml::Integer(line_length) => Some(line_length as usize), + Yaml::Boolean(line_length_guide) => { + let default = self.default[LINE_LENGTH_GUIDE_KEY] + .as_i64() + .expect("Couldn't find default line length guide setting!"); + + if line_length_guide { + Some(default as usize) + } else { + None + } + } + _ => None, + }) } pub fn line_wrapping(&self) -> bool { self.data .as_ref() - .and_then(|data| if let Yaml::Boolean(wrapping) = data[LINE_WRAPPING_KEY] { - Some(wrapping) - } else { - None - }) + .and_then(|data| { + if let Yaml::Boolean(wrapping) = data[LINE_WRAPPING_KEY] { + Some(wrapping) + } else { + None + } + }) .unwrap_or_else(|| { - self.default[LINE_WRAPPING_KEY].as_bool() + self.default[LINE_WRAPPING_KEY] + .as_bool() .expect("Couldn't find default line wrapping setting!") }) } @@ -242,17 +253,16 @@ impl Preferences { } pub fn open_mode_exclusions(&self) -> Result>> { - let exclusion_data = self.data + let exclusion_data = self + .data .as_ref() .map(|data| &data[OPEN_MODE_KEY][OPEN_MODE_EXCLUSIONS_KEY]); if let Some(exclusion_data) = exclusion_data { match *exclusion_data { - Yaml::Array(ref exclusions) => { - open::exclusions::parse(exclusions) - .chain_err(|| "Failed to parse user-defined open mode exclusions") - .map(Some) - }, + Yaml::Array(ref exclusions) => open::exclusions::parse(exclusions) + .chain_err(|| "Failed to parse user-defined open mode exclusions") + .map(Some), Yaml::Boolean(_) => Ok(None), _ => self.default_open_mode_exclusions(), } @@ -272,29 +282,29 @@ impl Preferences { } pub fn syntax_definition_name(&self, path: &Path) -> Option { - self.data - .as_ref() - .and_then(|data| { - // First try to match the file extension - if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) { - if let Some(syntax) = data[TYPES_KEY][extension][TYPES_SYNTAX_KEY].as_str() { - return Some(syntax.to_owned()); - } + self.data.as_ref().and_then(|data| { + // First try to match the file extension + if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) { + if let Some(syntax) = data[TYPES_KEY][extension][TYPES_SYNTAX_KEY].as_str() { + return Some(syntax.to_owned()); } + } - // If matching the file extension fails, try matching the whole filename - if let Some(path) = path.file_name().and_then(|name| name.to_str()) { - if let Some(syntax) = data[TYPES_KEY][path][TYPES_SYNTAX_KEY].as_str() { - return Some(syntax.to_owned()); - } + // If matching the file extension fails, try matching the whole filename + if let Some(path) = path.file_name().and_then(|name| name.to_str()) { + if let Some(syntax) = data[TYPES_KEY][path][TYPES_SYNTAX_KEY].as_str() { + return Some(syntax.to_owned()); } + } - None - }) + None + }) } pub fn format_on_save(&self, path: &PathBuf) -> bool { - let Some(extension) = path_extension(Some(path)) else { return false; }; + let Some(extension) = path_extension(Some(path)) else { + return false; + }; self.data .as_ref() @@ -306,15 +316,18 @@ impl Preferences { let extension = path_extension(Some(path))?; // Build a command using the command sub-key. - let Some(program) = self.data + let Some(program) = self + .data .as_ref() - .and_then(|data| data[TYPES_KEY][extension][FORMAT_TOOL_KEY]["command"].as_str()) else { - return None; - }; + .and_then(|data| data[TYPES_KEY][extension][FORMAT_TOOL_KEY]["command"].as_str()) + else { + return None; + }; let mut command = process::Command::new(program); // Parse and add options to command, if present. - let option_data = self.data + let option_data = self + .data .as_ref() .and_then(|data| data[TYPES_KEY][extension][FORMAT_TOOL_KEY]["options"].as_vec()); if let Some(options) = option_data { @@ -342,9 +355,8 @@ impl Preferences { /// Loads the first YAML document in the user's config file. fn load_document() -> Result> { // Build a path to the config file. - let mut config_path = - get_app_root(AppDataType::UserConfig, &APP_INFO) - .chain_err(|| "Couldn't open application config directory")?; + let mut config_path = get_app_root(AppDataType::UserConfig, &APP_INFO) + .chain_err(|| "Couldn't open application config directory")?; config_path.push(FILE_NAME); // Open (or create) the config file. @@ -360,15 +372,16 @@ fn load_document() -> Result> { .chain_err(|| "Couldn't read config file")?; // Parse the config file's contents and get the first YAML document inside. - let parsed_data = YamlLoader::load_from_str(&data) - .chain_err(|| "Couldn't parse config file")?; + let parsed_data = + YamlLoader::load_from_str(&data).chain_err(|| "Couldn't parse config file")?; Ok(parsed_data.into_iter().next()) } fn load_default_document() -> Result { YamlLoader::load_from_str(include_str!("default.yml")) .chain_err(|| "Couldn't parse default config file")? - .into_iter().next() + .into_iter() + .next() .chain_err(|| "No default preferences document found") } @@ -386,16 +399,15 @@ fn load_keymap(keymap_overrides: Option<&Hash>) -> Result { /// Maps a path to its file extension. fn path_extension(path: Option<&PathBuf>) -> Option<&str> { - path - .and_then(|p| p.extension().or_else(|| p.as_path().file_name())) + path.and_then(|p| p.extension().or_else(|| p.as_path().file_name())) .and_then(|e| e.to_str()) } #[cfg(test)] mod tests { use super::{ExclusionPattern, Preferences, YamlLoader}; - use std::path::{Path, PathBuf}; use crate::input::KeyMap; + use std::path::{Path, PathBuf}; use yaml_rust::yaml::{Hash, Yaml}; #[test] @@ -431,12 +443,14 @@ mod tests { #[test] fn tab_width_returns_user_defined_type_specific_data() { - let data = YamlLoader::load_from_str("tab_width: 12\ntypes:\n rs:\n tab_width: 24") - .unwrap(); + let data = + YamlLoader::load_from_str("tab_width: 12\ntypes:\n rs:\n tab_width: 24").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.tab_width(Some(PathBuf::from("preferences.rs")).as_ref()), - 24); + assert_eq!( + preferences.tab_width(Some(PathBuf::from("preferences.rs")).as_ref()), + 24 + ); } #[test] @@ -444,8 +458,10 @@ mod tests { let data = YamlLoader::load_from_str("tab_width: 12").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.tab_width(Some(PathBuf::from("preferences.rs")).as_ref()), - 12); + assert_eq!( + preferences.tab_width(Some(PathBuf::from("preferences.rs")).as_ref()), + 12 + ); } #[test] @@ -465,10 +481,15 @@ mod tests { #[test] fn soft_tabs_returns_user_defined_type_specific_data() { - let data = YamlLoader::load_from_str("soft_tabs: true\ntypes:\n rs:\n soft_tabs: false").unwrap(); + let data = + YamlLoader::load_from_str("soft_tabs: true\ntypes:\n rs:\n soft_tabs: false") + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.soft_tabs(Some(PathBuf::from("preferences.rs")).as_ref()), false); + assert_eq!( + preferences.soft_tabs(Some(PathBuf::from("preferences.rs")).as_ref()), + false + ); } #[test] @@ -476,7 +497,10 @@ mod tests { let data = YamlLoader::load_from_str("soft_tabs: false").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.soft_tabs(Some(PathBuf::from("preferences.rs")).as_ref()), false); + assert_eq!( + preferences.soft_tabs(Some(PathBuf::from("preferences.rs")).as_ref()), + false + ); } #[test] @@ -488,10 +512,15 @@ mod tests { #[test] fn non_extension_types_are_supported_for_type_specific_data() { - let data = YamlLoader::load_from_str("soft_tabs: true\ntypes:\n Makefile:\n soft_tabs: false").unwrap(); + let data = + YamlLoader::load_from_str("soft_tabs: true\ntypes:\n Makefile:\n soft_tabs: false") + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.soft_tabs(Some(PathBuf::from("Makefile")).as_ref()), false); + assert_eq!( + preferences.soft_tabs(Some(PathBuf::from("Makefile")).as_ref()), + false + ); } #[test] @@ -499,8 +528,10 @@ mod tests { let data = YamlLoader::load_from_str("types:\n xyz:\n syntax: Rust").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.syntax_definition_name(&Path::new("test.xyz")), - Some("Rust".to_owned())); + assert_eq!( + preferences.syntax_definition_name(&Path::new("test.xyz")), + Some("Rust".to_owned()) + ); } #[test] @@ -508,8 +539,10 @@ mod tests { let data = YamlLoader::load_from_str("types:\n xyz:\n syntax: Rust").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.syntax_definition_name(&Path::new("src/test.xyz")), - Some("Rust".to_owned())); + assert_eq!( + preferences.syntax_definition_name(&Path::new("src/test.xyz")), + Some("Rust".to_owned()) + ); } #[test] @@ -517,8 +550,10 @@ mod tests { let data = YamlLoader::load_from_str("types:\n Makefile:\n syntax: Makefile").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.syntax_definition_name(&Path::new("Makefile")), - Some("Makefile".to_owned())); + assert_eq!( + preferences.syntax_definition_name(&Path::new("Makefile")), + Some("Makefile".to_owned()) + ); } #[test] @@ -526,17 +561,22 @@ mod tests { let data = YamlLoader::load_from_str("types:\n Makefile:\n syntax: Makefile").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.syntax_definition_name(&Path::new("src/Makefile")), - Some("Makefile".to_owned())); + assert_eq!( + preferences.syntax_definition_name(&Path::new("src/Makefile")), + Some("Makefile".to_owned()) + ); } #[test] fn syntax_definition_name_returns_user_defined_syntax_for_full_filename_with_extension() { - let data = YamlLoader::load_from_str("types:\n Makefile.lib:\n syntax: Makefile").unwrap(); + let data = + YamlLoader::load_from_str("types:\n Makefile.lib:\n syntax: Makefile").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.syntax_definition_name(&Path::new("Makefile.lib")), - Some("Makefile".to_owned())); + assert_eq!( + preferences.syntax_definition_name(&Path::new("Makefile.lib")), + Some("Makefile".to_owned()) + ); } #[test] @@ -597,28 +637,39 @@ mod tests { #[test] fn tab_content_uses_tab_width_spaces_when_type_specific_soft_tabs_are_enabled() { let data = YamlLoader::load_from_str( - "soft_tabs: false\ntypes:\n rs:\n soft_tabs: true\n tab_width: 5").unwrap(); + "soft_tabs: false\ntypes:\n rs:\n soft_tabs: true\n tab_width: 5", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.tab_content(Some(PathBuf::from("preferences.rs")).as_ref()), - " "); + assert_eq!( + preferences.tab_content(Some(PathBuf::from("preferences.rs")).as_ref()), + " " + ); } #[test] fn tab_content_returns_tab_character_when_type_specific_soft_tabs_are_disabled() { let data = YamlLoader::load_from_str( - "soft_tabs: true\ntab_width: 5\ntypes:\n rs:\n soft_tabs: false\n").unwrap(); + "soft_tabs: true\ntab_width: 5\ntypes:\n rs:\n soft_tabs: false\n", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.tab_content(Some(PathBuf::from("preferences.rs")).as_ref()), - "\t"); + assert_eq!( + preferences.tab_content(Some(PathBuf::from("preferences.rs")).as_ref()), + "\t" + ); } #[test] fn open_mode_exclusions_returns_correct_defaults_when_no_data_provided() { let preferences = Preferences::new(None); - assert_eq!(preferences.open_mode_exclusions().unwrap(), Some(vec![ExclusionPattern::new("**/.git").unwrap()])); + assert_eq!( + preferences.open_mode_exclusions().unwrap(), + Some(vec![ExclusionPattern::new("**/.git").unwrap()]) + ); } #[test] @@ -626,7 +677,10 @@ mod tests { let data = YamlLoader::load_from_str("tab_width: 12").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.open_mode_exclusions().unwrap(), Some(vec![ExclusionPattern::new("**/.git").unwrap()])); + assert_eq!( + preferences.open_mode_exclusions().unwrap(), + Some(vec![ExclusionPattern::new("**/.git").unwrap()]) + ); } #[test] @@ -634,7 +688,10 @@ mod tests { let data = YamlLoader::load_from_str("open_mode:\n exclusions:\n - \".svn\"").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.open_mode_exclusions().unwrap(), Some(vec![ExclusionPattern::new(".svn").unwrap()])); + assert_eq!( + preferences.open_mode_exclusions().unwrap(), + Some(vec![ExclusionPattern::new(".svn").unwrap()]) + ); } #[test] @@ -649,8 +706,10 @@ mod tests { fn line_comment_prefix_returns_correct_default_type_specific_data() { let preferences = Preferences::new(None); - assert_eq!(preferences.line_comment_prefix(&PathBuf::from("preferences.rs")), - Some("//".into())); + assert_eq!( + preferences.line_comment_prefix(&PathBuf::from("preferences.rs")), + Some("//".into()) + ); } #[test] @@ -658,25 +717,32 @@ mod tests { let data = YamlLoader::load_from_str("types:\n rs:\n line_comment_prefix: $$").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.line_comment_prefix(&PathBuf::from("preferences.rs")), - Some("$$".into())); + assert_eq!( + preferences.line_comment_prefix(&PathBuf::from("preferences.rs")), + Some("$$".into()) + ); } #[test] fn line_comment_prefix_returns_correct_user_defined_type_specific_data_with_no_default() { - let data = YamlLoader::load_from_str("types:\n abc:\n line_comment_prefix: $$").unwrap(); + let data = + YamlLoader::load_from_str("types:\n abc:\n line_comment_prefix: $$").unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert_eq!(preferences.line_comment_prefix(&PathBuf::from("preferences.abc")), - Some("$$".into())); + assert_eq!( + preferences.line_comment_prefix(&PathBuf::from("preferences.abc")), + Some("$$".into()) + ); } #[test] fn line_comment_prefix_returns_none_for_non_existing_type() { let preferences = Preferences::new(None); - assert_eq!(preferences.line_comment_prefix(&PathBuf::from("preferences.abc")), - None); + assert_eq!( + preferences.line_comment_prefix(&PathBuf::from("preferences.abc")), + None + ); } #[test] @@ -707,7 +773,7 @@ mod tests { default: Yaml::Null, data: None, keymap: KeyMap::from(&Hash::new()).unwrap(), - theme: None + theme: None, }; // Reload the preferences, ensuring that it refreshes the keymap. @@ -719,67 +785,76 @@ mod tests { fn format_on_save_defaults_to_false() { let preferences = Preferences::new(None); - assert!( - !preferences.format_on_save(&PathBuf::from("preferences.rs")) - ); + assert!(!preferences.format_on_save(&PathBuf::from("preferences.rs"))); } #[test] fn format_on_save_returns_type_specific_value() { - let data = YamlLoader::load_from_str(" + let data = YamlLoader::load_from_str( + " types: rs: format_tool: run_on_save: true - ").unwrap(); + ", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - assert!( - preferences.format_on_save(&PathBuf::from("preferences.rs")) - ); + assert!(preferences.format_on_save(&PathBuf::from("preferences.rs"))); } #[test] fn format_command_correctly_handles_missing_type_specific_command() { let preferences = Preferences::new(None); - assert!( - preferences.format_command(&PathBuf::from("preferences.rs")).is_none() - ); + assert!(preferences + .format_command(&PathBuf::from("preferences.rs")) + .is_none()); } #[test] fn format_command_returns_user_defined_type_specific_command_without_args() { - let data = YamlLoader::load_from_str(" + let data = YamlLoader::load_from_str( + " types: rs: format_tool: command: rustfmt - ").unwrap(); + ", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - let command = preferences.format_command(&PathBuf::from("preferences.rs")).unwrap(); - assert_eq!( - command.get_program(), - "rustfmt" - ); + let command = preferences + .format_command(&PathBuf::from("preferences.rs")) + .unwrap(); + assert_eq!(command.get_program(), "rustfmt"); assert_eq!(command.get_args().count(), 0); } #[test] fn format_command_returns_user_defined_type_specific_command_with_args() { - let data = YamlLoader::load_from_str(" + let data = YamlLoader::load_from_str( + " types: rs: format_tool: command: rustfmt options: [--check] - ").unwrap(); + ", + ) + .unwrap(); let preferences = Preferences::new(data.into_iter().nth(0)); - let command = preferences.format_command(&PathBuf::from("preferences.rs")).unwrap(); + let command = preferences + .format_command(&PathBuf::from("preferences.rs")) + .unwrap(); assert_eq!(command.get_program(), "rustfmt"); assert_eq!(command.get_args().count(), 1); - assert_eq!(command.get_args().next().and_then(|a| a.to_str()), Some("--check")); + assert_eq!( + command.get_args().next().and_then(|a| a.to_str()), + Some("--check") + ); } } diff --git a/src/presenters/error.rs b/src/presenters/error.rs index 8e139c59..b00f5ae2 100644 --- a/src/presenters/error.rs +++ b/src/presenters/error.rs @@ -1,6 +1,6 @@ use crate::errors::*; -use scribe::Workspace; use crate::view::{Colors, StatusLineData, Style, View}; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, view: &mut View, error: &Error) -> Result<()> { let data; diff --git a/src/presenters/mod.rs b/src/presenters/mod.rs index 765d3db4..32766ebd 100644 --- a/src/presenters/mod.rs +++ b/src/presenters/mod.rs @@ -1,30 +1,37 @@ pub mod error; pub mod modes; -use std::path::{Path, PathBuf}; -use scribe::Workspace; use crate::view::{Colors, StatusLineData, Style}; use git2::{self, Repository, Status}; +use scribe::Workspace; +use std::path::{Path, PathBuf}; fn path_as_title(path: &Path) -> String { format!(" {}", path.to_string_lossy()) } fn current_buffer_status_line_data(workspace: &mut Workspace) -> StatusLineData { - let modified = workspace.current_buffer.as_ref().map(|b| b.modified()).unwrap_or(false); + let modified = workspace + .current_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. - if modified { - // Use an emboldened path with an asterisk. - let mut title = path_as_title(path); - title.push('*'); + let (content, style) = workspace + .current_buffer_path() + .map(|path| { + // Determine buffer title styles based on its modification status. + 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) - } - }).unwrap_or((String::new(), Style::Default)); + (title, Style::Bold) + } else { + (path_as_title(path), Style::Default) + } + }) + .unwrap_or((String::new(), Style::Default)); StatusLineData { content, @@ -85,8 +92,8 @@ fn presentable_status(status: &Status) -> &str { #[cfg(test)] mod tests { - use git2; use super::presentable_status; + use git2; #[test] pub fn presentable_status_returns_untracked_when_status_is_locally_new() { @@ -115,14 +122,18 @@ mod tests { #[test] pub fn presentable_status_returns_partially_staged_when_modified_locally_and_in_index() { let status = git2::Status::WT_MODIFIED | git2::Status::INDEX_MODIFIED; - assert_eq!(presentable_status(&status), - "[partially staged]".to_string()); + assert_eq!( + presentable_status(&status), + "[partially staged]".to_string() + ); } #[test] pub fn presentable_status_returns_partially_staged_when_new_locally_and_in_index() { let status = git2::Status::WT_NEW | git2::Status::INDEX_NEW; - assert_eq!(presentable_status(&status), - "[partially staged]".to_string()); + assert_eq!( + presentable_status(&status), + "[partially staged]".to_string() + ); } } diff --git a/src/presenters/modes/confirm.rs b/src/presenters/modes/confirm.rs index 9b2176dc..841ac838 100644 --- a/src/presenters/modes/confirm.rs +++ b/src/presenters/modes/confirm.rs @@ -1,6 +1,6 @@ use crate::errors::*; -use scribe::Workspace; use crate::view::{Colors, StatusLineData, Style, View}; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -12,13 +12,11 @@ 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 49c5a717..be2c86ca 100644 --- a/src/presenters/modes/insert.rs +++ b/src/presenters/modes/insert.rs @@ -1,7 +1,7 @@ use crate::errors::*; use crate::presenters::current_buffer_status_line_data; -use scribe::Workspace; use crate::view::{Colors, CursorType, StatusLineData, Style, View}; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -18,7 +18,7 @@ pub fn display(workspace: &mut Workspace, view: &mut View) -> Result<()> { style: Style::Default, colors: Colors::Insert, }, - buffer_status + buffer_status, ]); // Show a blinking, vertical bar indicating input. diff --git a/src/presenters/modes/jump.rs b/src/presenters/modes/jump.rs index e476cc61..f5ac84aa 100644 --- a/src/presenters/modes/jump.rs +++ b/src/presenters/modes/jump.rs @@ -1,8 +1,8 @@ use crate::errors::*; -use crate::presenters::current_buffer_status_line_data; -use scribe::Workspace; use crate::models::application::modes::JumpMode; +use crate::presenters::current_buffer_status_line_data; use crate::view::{Colors, StatusLineData, Style, View}; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, mode: &mut JumpMode, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -21,7 +21,7 @@ pub fn display(workspace: &mut Workspace, mode: &mut JumpMode, view: &mut View) style: Style::Default, colors: Colors::Inverted, }, - buffer_status + buffer_status, ]); // Don't display a cursor. diff --git a/src/presenters/modes/line_jump.rs b/src/presenters/modes/line_jump.rs index 98755280..956aae8a 100644 --- a/src/presenters/modes/line_jump.rs +++ b/src/presenters/modes/line_jump.rs @@ -1,8 +1,8 @@ use crate::errors::*; -use scribe::Workspace; -use scribe::buffer::Position; use crate::models::application::modes::LineJumpMode; use crate::view::{Colors, CursorType, StatusLineData, Style, View}; +use scribe::buffer::Position; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, mode: &LineJumpMode, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -13,13 +13,11 @@ 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/mod.rs b/src/presenters/modes/mod.rs index ebdd33e4..c9ff237c 100644 --- a/src/presenters/modes/mod.rs +++ b/src/presenters/modes/mod.rs @@ -2,8 +2,8 @@ pub mod confirm; pub mod insert; pub mod jump; pub mod line_jump; -pub mod path; pub mod normal; +pub mod path; pub mod search; pub mod search_select; pub mod select; diff --git a/src/presenters/modes/normal.rs b/src/presenters/modes/normal.rs index b730ab4d..d4264e60 100644 --- a/src/presenters/modes/normal.rs +++ b/src/presenters/modes/normal.rs @@ -1,11 +1,15 @@ use crate::errors::*; -use scribe::Workspace; -use scribe::buffer::Position; use crate::presenters::{current_buffer_status_line_data, git_status_line_data}; -use git2::Repository; use crate::view::{Colors, CursorType, StatusLineData, Style, View}; +use git2::Repository; +use scribe::buffer::Position; +use scribe::Workspace; -pub fn display(workspace: &mut Workspace, view: &mut View, repo: &Option) -> Result<()> { +pub fn display( + workspace: &mut Workspace, + view: &mut View, + repo: &Option, +) -> Result<()> { let mut presenter = view.build_presenter()?; let buffer_status = current_buffer_status_line_data(workspace); @@ -29,7 +33,7 @@ pub fn display(workspace: &mut Workspace, view: &mut View, repo: &Option Result<()> { let mut presenter = view.build_presenter()?; @@ -14,14 +14,9 @@ pub fn display(workspace: &mut Workspace, mode: &PathMode, view: &mut View) -> R presenter.print_buffer(buffer, &data, &workspace.syntax_set, None, None)?; let mode_display = format!(" {} ", mode); - let search_input = format!( - " {}", - mode.input - ); + let search_input = format!(" {}", mode.input); - let cursor_offset = - mode_display.graphemes(true).count() + - search_input.graphemes(true).count(); + let cursor_offset = mode_display.graphemes(true).count() + search_input.graphemes(true).count(); presenter.print_status_line(&[ StatusLineData { @@ -41,7 +36,7 @@ pub fn display(workspace: &mut Workspace, mode: &PathMode, view: &mut View) -> R let cursor_line = presenter.height() - 1; presenter.set_cursor(Some(Position { line: cursor_line, - offset: cursor_offset + offset: cursor_offset, })); } diff --git a/src/presenters/modes/search.rs b/src/presenters/modes/search.rs index c9a87895..1a27ebd2 100644 --- a/src/presenters/modes/search.rs +++ b/src/presenters/modes/search.rs @@ -1,9 +1,9 @@ use crate::errors::*; -use scribe::Workspace; -use scribe::buffer::Position; use crate::models::application::modes::SearchMode; -use unicode_segmentation::UnicodeSegmentation; use crate::view::{Colors, CursorType, StatusLineData, Style, View}; +use scribe::buffer::Position; +use scribe::Workspace; +use unicode_segmentation::UnicodeSegmentation; pub fn display(workspace: &mut Workspace, mode: &SearchMode, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -11,28 +11,33 @@ pub fn display(workspace: &mut Workspace, mode: &SearchMode, view: &mut View) -> // Draw the visible set of tokens to the terminal. let buffer = workspace.current_buffer.as_ref().ok_or(BUFFER_MISSING)?; let data = buffer.data(); - presenter.print_buffer(buffer, &data, &workspace.syntax_set, mode.results.as_ref().map(|r| r.as_slice()), None)?; + presenter.print_buffer( + buffer, + &data, + &workspace.syntax_set, + mode.results.as_ref().map(|r| r.as_slice()), + None, + )?; let mode_display = format!(" {} ", mode); - let search_input = format!( - " {}", - mode.input.as_ref().unwrap_or(&String::new()) - ); + let search_input = format!(" {}", mode.input.as_ref().unwrap_or(&String::new())); let result_display = if mode.insert { String::new() } else if let Some(ref results) = mode.results { if results.len() == 1 { String::from("1 match") } else { - format!("{} of {} matches", results.selected_index() + 1, results.len()) + format!( + "{} of {} matches", + results.selected_index() + 1, + results.len() + ) } } else { String::new() }; - let cursor_offset = - mode_display.graphemes(true).count() + - search_input.graphemes(true).count(); + let cursor_offset = mode_display.graphemes(true).count() + search_input.graphemes(true).count(); presenter.print_status_line(&[ StatusLineData { @@ -57,7 +62,7 @@ pub fn display(workspace: &mut Workspace, mode: &SearchMode, view: &mut View) -> let cursor_line = presenter.height() - 1; presenter.set_cursor(Some(Position { line: cursor_line, - offset: cursor_offset + offset: cursor_offset, })); } diff --git a/src/presenters/modes/search_select.rs b/src/presenters/modes/search_select.rs index 94e6fe09..9d2a1122 100644 --- a/src/presenters/modes/search_select.rs +++ b/src/presenters/modes/search_select.rs @@ -1,14 +1,18 @@ use crate::errors::*; -use std::cmp; -use std::fmt::Display; -use crate::models::application::modes::{SearchSelectMode}; +use crate::models::application::modes::SearchSelectMode; use crate::presenters::current_buffer_status_line_data; -use scribe::Workspace; -use scribe::buffer::Position; use crate::view::{Colors, CursorType, StatusLineData, Style, View}; +use scribe::buffer::Position; +use scribe::Workspace; +use std::cmp; +use std::fmt::Display; use unicode_segmentation::UnicodeSegmentation; -pub fn display(workspace: &mut Workspace, mode: &mut dyn SearchSelectMode, view: &mut View) -> Result<()> { +pub fn display( + workspace: &mut Workspace, + mode: &mut dyn SearchSelectMode, + view: &mut View, +) -> Result<()> { let data; let padded_message; let mut presenter = view.build_presenter()?; @@ -28,16 +32,18 @@ pub fn display(workspace: &mut Workspace, mode: &mut dyn SearchSelec style: Style::Default, colors: Colors::Inverted, }, - buffer_status + buffer_status, ]); } if let Some(message) = mode.message() { padded_message = format!("{:width$}", message, width = presenter.width()); - presenter.print(&Position{ line: 0, offset: 0 }, - Style::Default, - Colors::Default, - &padded_message); + presenter.print( + &Position { line: 0, offset: 0 }, + Style::Default, + Colors::Default, + &padded_message, + ); } else { // Draw the list of search results. for (line, result) in mode.results().enumerate() { @@ -56,7 +62,7 @@ pub fn display(workspace: &mut Workspace, mode: &mut dyn SearchSelec .collect(); padded_content.push(( - Position{ line, offset: 0 }, + Position { line, offset: 0 }, style, colors, format!("{:width$}", trimmed_content, width = presenter.width()), @@ -71,10 +77,10 @@ pub fn display(workspace: &mut Workspace, mode: &mut dyn SearchSelec // Clear any remaining lines in the result display area. for line in cmp::max(mode.results().len(), 1)..mode_config.max_results { remaining_lines.push(( - Position{ line, offset: 0 }, - Style::Default, - Colors::Default, - format!("{:width$}", ' ', width = presenter.width()), + Position { line, offset: 0 }, + Style::Default, + Colors::Default, + format!("{:width$}", ' ', width = presenter.width()), )); } @@ -92,11 +98,12 @@ pub fn display(workspace: &mut Workspace, mode: &mut dyn SearchSelec let padded_content = format!("{:width$}", mode.query(), width = presenter.width()); - presenter.print(&Position{ line, offset: 0 }, - Style::Bold, - colors, - &padded_content); - + presenter.print( + &Position { line, offset: 0 }, + Style::Bold, + colors, + &padded_content, + ); if mode.insert_mode() { // Place the cursor on the search input line, right after its contents. diff --git a/src/presenters/modes/select.rs b/src/presenters/modes/select.rs index 1f99a611..5c6d2006 100644 --- a/src/presenters/modes/select.rs +++ b/src/presenters/modes/select.rs @@ -1,9 +1,9 @@ use crate::errors::*; use crate::models::application::modes::SelectMode; -use scribe::Workspace; -use scribe::buffer::Range; use crate::presenters::current_buffer_status_line_data; use crate::view::{Colors, CursorType, StatusLineData, Style, View}; +use scribe::buffer::Range; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, mode: &SelectMode, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -13,7 +13,13 @@ pub fn display(workspace: &mut Workspace, mode: &SelectMode, view: &mut View) -> let data = buf.data(); // Draw the visible set of tokens to the terminal. - presenter.print_buffer(buf, &data, &workspace.syntax_set, Some(&[selected_range]), None)?; + presenter.print_buffer( + buf, + &data, + &workspace.syntax_set, + Some(&[selected_range]), + None, + )?; presenter.print_status_line(&[ StatusLineData { @@ -21,7 +27,7 @@ pub fn display(workspace: &mut Workspace, mode: &SelectMode, view: &mut View) -> style: Style::Default, colors: Colors::SelectMode, }, - buffer_status + buffer_status, ]); // Show a vertical bar to allow unambiguous/precise selection. diff --git a/src/presenters/modes/select_line.rs b/src/presenters/modes/select_line.rs index 55c1ccfe..754f74dc 100644 --- a/src/presenters/modes/select_line.rs +++ b/src/presenters/modes/select_line.rs @@ -1,8 +1,8 @@ use crate::errors::*; use crate::models::application::modes::SelectLineMode; -use scribe::Workspace; use crate::presenters::current_buffer_status_line_data; use crate::view::{Colors, StatusLineData, Style, View}; +use scribe::Workspace; pub fn display(workspace: &mut Workspace, mode: &SelectLineMode, view: &mut View) -> Result<()> { let mut presenter = view.build_presenter()?; @@ -12,7 +12,13 @@ pub fn display(workspace: &mut Workspace, mode: &SelectLineMode, view: &mut View let data = buf.data(); // Draw the visible set of tokens to the terminal. - presenter.print_buffer(buf, &data, &workspace.syntax_set, Some(&[selected_range]), None)?; + presenter.print_buffer( + buf, + &data, + &workspace.syntax_set, + Some(&[selected_range]), + None, + )?; presenter.print_status_line(&[ StatusLineData { @@ -20,7 +26,7 @@ pub fn display(workspace: &mut Workspace, mode: &SelectLineMode, view: &mut View style: Style::Default, colors: Colors::SelectMode, }, - buffer_status + buffer_status, ]); // Render the changes to the screen. diff --git a/src/util/mod.rs b/src/util/mod.rs index 25de5527..2086d845 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,8 +1,8 @@ pub use self::selectable_vec::SelectableVec; pub mod movement_lexer; -mod selectable_vec; pub mod reflow; +mod selectable_vec; pub mod token; use crate::errors::*; @@ -34,34 +34,35 @@ pub fn inclusive_range(line_range: &LineRange, buffer: &mut Buffer) -> Range { } } // Couldn't find any content for the last line; use a zero offset. - None => { - Position { - line: line_range.end(), - offset: 0, - } - } + None => Position { + line: line_range.end(), + offset: 0, + }, } }; - Range::new(Position { - line: line_range.start(), - offset: 0, - }, - end_position) + Range::new( + Position { + line: line_range.start(), + offset: 0, + }, + end_position, + ) } /// Convenience method to initialize and add a buffer to the workspace. pub fn add_buffer(buffer: Buffer, app: &mut Application) -> Result<()> { app.workspace.add_buffer(buffer); - app.view.initialize_buffer(app.workspace.current_buffer.as_mut().unwrap())?; + app.view + .initialize_buffer(app.workspace.current_buffer.as_mut().unwrap())?; Ok(()) } #[cfg(test)] mod tests { - use scribe::Buffer; use scribe::buffer::{LineRange, Position, Range}; + use scribe::Buffer; #[test] fn inclusive_range_works_correctly_without_trailing_newline() { @@ -69,15 +70,13 @@ mod tests { buffer.insert("amp\neditor"); let range = LineRange::new(1, 1); - assert_eq!(super::inclusive_range(&range, &mut buffer), - Range::new(Position { - line: 1, - offset: 0, - }, - Position { - line: 1, - offset: 6, - })); + assert_eq!( + super::inclusive_range(&range, &mut buffer), + Range::new( + Position { line: 1, offset: 0 }, + Position { line: 1, offset: 6 } + ) + ); } #[test] @@ -86,14 +85,12 @@ mod tests { buffer.insert("amp\neditor\n"); let range = LineRange::new(1, 1); - assert_eq!(super::inclusive_range(&range, &mut buffer), - Range::new(Position { - line: 1, - offset: 0, - }, - Position { - line: 2, - offset: 0, - })); + assert_eq!( + super::inclusive_range(&range, &mut buffer), + Range::new( + Position { line: 1, offset: 0 }, + Position { line: 2, offset: 0 } + ) + ); } } diff --git a/src/util/movement_lexer.rs b/src/util/movement_lexer.rs index cb14bb12..eb7c6379 100644 --- a/src/util/movement_lexer.rs +++ b/src/util/movement_lexer.rs @@ -1,5 +1,5 @@ -use luthor::{Tokenizer, StateFunction}; -use luthor::token::{Token, Category}; +use luthor::token::{Category, Token}; +use luthor::{StateFunction, Tokenizer}; fn initial_state(lexer: &mut Tokenizer) -> Option { if lexer.has_prefix("::") { @@ -15,30 +15,8 @@ fn initial_state(lexer: &mut Tokenizer) -> Option { lexer.advance(); return Some(StateFunction(whitespace)); } - '`' | - '=' | - '_' | - '-' | - '.' | - '(' | - ')' | - '{' | - '}' | - ';' | - '|' | - ',' | - ':' | - '<' | - '>' | - '\'' | - '"' | - '?' | - '@' | - '#' | - '/' | - '\\' | - '[' | - ']' => { + '`' | '=' | '_' | '-' | '.' | '(' | ')' | '{' | '}' | ';' | '|' | ',' | ':' + | '<' | '>' | '\'' | '"' | '?' | '@' | '#' | '/' | '\\' | '[' | ']' => { lexer.tokenize(Category::Text); lexer.tokenize_next(1, Category::Text); return Some(StateFunction(whitespace)); @@ -66,18 +44,16 @@ fn initial_state(lexer: &mut Tokenizer) -> Option { fn whitespace(lexer: &mut Tokenizer) -> Option { match lexer.current_char() { - Some(c) => { - match c { - ' ' | '\n' | '\t' => { - lexer.advance(); - Some(StateFunction(whitespace)) - } - _ => { - lexer.tokenize(Category::Whitespace); - Some(StateFunction(initial_state)) - } + Some(c) => match c { + ' ' | '\n' | '\t' => { + lexer.advance(); + Some(StateFunction(whitespace)) } - } + _ => { + lexer.tokenize(Category::Whitespace); + Some(StateFunction(initial_state)) + } + }, None => { lexer.tokenize(Category::Whitespace); @@ -124,38 +100,114 @@ pub fn lex(data: &str) -> Vec { #[cfg(test)] mod tests { use super::*; - use luthor::token::{Token, Category}; + use luthor::token::{Category, Token}; #[test] fn it_works() { - let data = "local_variable = camelCase.method(param)\n CamelCaseClass something-else CONSTANT val"; + let data = + "local_variable = camelCase.method(param)\n CamelCaseClass something-else CONSTANT val"; let tokens = lex(data); let expected_tokens = vec![ - Token{ lexeme: "local".to_string(), category: Category::Text }, - Token{ lexeme: "_".to_string(), category: Category::Text }, - Token{ lexeme: "variable".to_string(), category: Category::Text }, - Token{ lexeme: " ".to_string(), category: Category::Whitespace }, - Token{ lexeme: "=".to_string(), category: Category::Text }, - Token{ lexeme: " ".to_string(), category: Category::Whitespace }, - Token{ lexeme: "camel".to_string(), category: Category::Text }, - Token{ lexeme: "Case".to_string(), category: Category::Text }, - Token{ lexeme: ".".to_string(), category: Category::Text }, - Token{ lexeme: "method".to_string(), category: Category::Text }, - Token{ lexeme: "(".to_string(), category: Category::Text }, - Token{ lexeme: "param".to_string(), category: Category::Text }, - Token{ lexeme: ")".to_string(), category: Category::Text }, - Token{ lexeme: "\n ".to_string(), category: Category::Whitespace }, - Token{ lexeme: "Camel".to_string(), category: Category::Text }, - Token{ lexeme: "Case".to_string(), category: Category::Text }, - Token{ lexeme: "Class".to_string(), category: Category::Text }, - Token{ lexeme: " ".to_string(), category: Category::Whitespace }, - Token{ lexeme: "something".to_string(), category: Category::Text }, - Token{ lexeme: "-".to_string(), category: Category::Text }, - Token{ lexeme: "else".to_string(), category: Category::Text }, - Token{ lexeme: " ".to_string(), category: Category::Whitespace }, - Token{ lexeme: "CONSTANT".to_string(), category: Category::Text }, - Token{ lexeme: " ".to_string(), category: Category::Whitespace }, - Token{ lexeme: "val".to_string(), category: Category::Text }, + Token { + lexeme: "local".to_string(), + category: Category::Text, + }, + Token { + lexeme: "_".to_string(), + category: Category::Text, + }, + Token { + lexeme: "variable".to_string(), + category: Category::Text, + }, + Token { + lexeme: " ".to_string(), + category: Category::Whitespace, + }, + Token { + lexeme: "=".to_string(), + category: Category::Text, + }, + Token { + lexeme: " ".to_string(), + category: Category::Whitespace, + }, + Token { + lexeme: "camel".to_string(), + category: Category::Text, + }, + Token { + lexeme: "Case".to_string(), + category: Category::Text, + }, + Token { + lexeme: ".".to_string(), + category: Category::Text, + }, + Token { + lexeme: "method".to_string(), + category: Category::Text, + }, + Token { + lexeme: "(".to_string(), + category: Category::Text, + }, + Token { + lexeme: "param".to_string(), + category: Category::Text, + }, + Token { + lexeme: ")".to_string(), + category: Category::Text, + }, + Token { + lexeme: "\n ".to_string(), + category: Category::Whitespace, + }, + Token { + lexeme: "Camel".to_string(), + category: Category::Text, + }, + Token { + lexeme: "Case".to_string(), + category: Category::Text, + }, + Token { + lexeme: "Class".to_string(), + category: Category::Text, + }, + Token { + lexeme: " ".to_string(), + category: Category::Whitespace, + }, + Token { + lexeme: "something".to_string(), + category: Category::Text, + }, + Token { + lexeme: "-".to_string(), + category: Category::Text, + }, + Token { + lexeme: "else".to_string(), + category: Category::Text, + }, + Token { + lexeme: " ".to_string(), + category: Category::Whitespace, + }, + Token { + lexeme: "CONSTANT".to_string(), + category: Category::Text, + }, + Token { + lexeme: " ".to_string(), + category: Category::Whitespace, + }, + Token { + lexeme: "val".to_string(), + category: Category::Text, + }, ]; for (index, token) in tokens.iter().enumerate() { diff --git a/src/util/reflow.rs b/src/util/reflow.rs index 3d7d897a..52648e33 100644 --- a/src/util/reflow.rs +++ b/src/util/reflow.rs @@ -12,10 +12,17 @@ impl<'a> Reflow<'a> { /// Create a reflow instance, where buffer and range determine the target, /// and the limit is the maximum length of a line, regardless of prefixes. pub fn new( - buf: &'a mut Buffer, range: Range, limit: usize + buf: &'a mut Buffer, + range: Range, + limit: usize, ) -> std::result::Result { let text = buf.read(&range).ok_or("Selection is invalid.")?; - Ok(Self { buf, range, text, limit }) + Ok(Self { + buf, + range, + text, + limit, + }) } pub fn apply(mut self) -> std::result::Result<(), Error> { @@ -30,16 +37,17 @@ impl<'a> Reflow<'a> { fn infer_prefix(&self) -> std::result::Result { match self.text.split_whitespace().next() { - Some(n) => if n.chars().next().unwrap().is_alphanumeric() { - Ok("".to_string()) - } else { - Ok(n.to_string()) - }, - None => bail!("Selection is empty."), + Some(n) => { + if n.chars().next().unwrap().is_alphanumeric() { + Ok("".to_string()) + } else { + Ok(n.to_string()) + } + } + None => bail!("Selection is empty."), } } - fn justify_str(&mut self, prefix: &str) -> String { let text = self.buf.read(&self.range).unwrap(); let mut limit = self.limit; @@ -48,42 +56,42 @@ impl<'a> Reflow<'a> { let mut space_delims = ["".to_string(), " ".to_string(), "\n".to_string()]; if !prefix.is_empty() { - space_delims[0] += prefix; - space_delims[0] += " "; - space_delims[2] += prefix; - space_delims[2] += " "; - limit -= prefix.len() + 1; + space_delims[0] += prefix; + space_delims[0] += " "; + space_delims[2] += prefix; + space_delims[2] += " "; + limit -= prefix.len() + 1; } while let Some(par) = pars.next() { - let words = par.split_whitespace(); - let mut len = 0; - let mut first = true; - - for word in words { - if word == prefix { - continue; - } - - len += word.len(); - - let over = len > limit; - let u_over = over as usize; - let idx = (!first as usize) * u_over + !first as usize; - - justified += &space_delims[idx]; - justified += word; - - // if we're over, set the length to 0, otherwise increment it - // properly. This just does that mith multiplication by 0 instead of - // branching. - len = (len + 1) * (1 - u_over) + (word.len() + 1) * u_over; - first = false; - } - - if pars.peek().is_some() { - justified += "\n\n"; // add back the paragraph break. - } + let words = par.split_whitespace(); + let mut len = 0; + let mut first = true; + + for word in words { + if word == prefix { + continue; + } + + len += word.len(); + + let over = len > limit; + let u_over = over as usize; + let idx = (!first as usize) * u_over + !first as usize; + + justified += &space_delims[idx]; + justified += word; + + // if we're over, set the length to 0, otherwise increment it + // properly. This just does that mith multiplication by 0 instead of + // branching. + len = (len + 1) * (1 - u_over) + (word.len() + 1) * u_over; + first = false; + } + + if pars.peek().is_some() { + justified += "\n\n"; // add back the paragraph break. + } } justified @@ -108,23 +116,27 @@ a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a scribe::buffer::Position { line: 1, offset: 0 }, ), 80, - ).unwrap().apply().unwrap(); + ) + .unwrap() + .apply() + .unwrap(); assert_eq!( buf.data(), - "\ + "\ a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a" - ); + ); } #[test] fn justify_paragraph() { let mut buf = Buffer::new(); - buf.insert("\ + buf.insert( + "\ these are words to be used as demos for the thing that this is. this is text \ reflowing and justification over a few lines. this is just filler text in case \ -it wasn't obvious.\n" +it wasn't obvious.\n", ); Reflow::new( @@ -134,19 +146,24 @@ it wasn't obvious.\n" scribe::buffer::Position { line: 1, offset: 0 }, ), 80, - ).unwrap().apply().unwrap(); - assert_eq!( - buf.data(), "\ + ) + .unwrap() + .apply() + .unwrap(); + assert_eq!( + buf.data(), + "\ these are words to be used as demos for the thing that this is. this is text reflowing and justification over a few lines. this is just filler text in case it wasn't obvious." - ); + ); } #[test] fn justify_multiple_pars() { let mut buf = Buffer::new(); - buf.insert("\ + buf.insert( + "\ Here's more filler text! So fun fact of the day, I was trying to just copy paste \ some lorem ipsum to annoy my latin student friends, but honestly it broke the \ M-q 'justify' function in emacs, which makes it a bit difficult to work with. \ @@ -158,7 +175,7 @@ of sanity and coherence here! Fun fact of the day number three is that I spent three hours getting this to not \ branch. There is no way that that micro-optimization will actually save three \ -hours worth of time, but I did it anyway for no good reason!\n" +hours worth of time, but I did it anyway for no good reason!\n", ); Reflow::new( @@ -168,10 +185,14 @@ hours worth of time, but I did it anyway for no good reason!\n" scribe::buffer::Position { line: 5, offset: 0 }, ), 80, - ).unwrap().apply().unwrap(); + ) + .unwrap() + .apply() + .unwrap(); - assert_eq!( - buf.data(), "\ + assert_eq!( + buf.data(), + "\ Here's more filler text! So fun fact of the day, I was trying to just copy paste some lorem ipsum to annoy my latin student friends, but honestly it broke the M-q 'justify' function in emacs, which makes it a bit difficult to work with. @@ -184,13 +205,13 @@ of sanity and coherence here! Fun fact of the day number three is that I spent three hours getting this to not branch. There is no way that that micro-optimization will actually save three hours worth of time, but I did it anyway for no good reason!" - ); + ); } #[test] fn justify_simple_prefix() { let mut buf = Buffer::new(); - buf.insert("\ + buf.insert("\ # a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a\n" ); Reflow::new( @@ -200,23 +221,28 @@ hours worth of time, but I did it anyway for no good reason!" scribe::buffer::Position { line: 1, offset: 0 }, ), 80, - ).unwrap().apply().unwrap(); + ) + .unwrap() + .apply() + .unwrap(); - assert_eq!( - buf.data(), "\ + assert_eq!( + buf.data(), + "\ # a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a # a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a" - ); + ); } #[test] fn justify_paragraph_prefix() { let mut buf = Buffer::new(); - buf.insert("\ + buf.insert( + "\ // filler text meant // to do stuff and things that end up with text nicely \ wrappped around a comment delimiter such as the double slashes in c-style \ -languages.\n" +languages.\n", ); Reflow::new( @@ -226,13 +252,17 @@ languages.\n" scribe::buffer::Position { line: 2, offset: 0 }, ), 80, - ).unwrap().apply().unwrap(); + ) + .unwrap() + .apply() + .unwrap(); - assert_eq!( - buf.data(), "\ + assert_eq!( + buf.data(), + "\ // filler text meant to do stuff and things that end up with text nicely // wrappped around a comment delimiter such as the double slashes in c-style // languages.", - ); + ); } } diff --git a/src/util/token.rs b/src/util/token.rs index 4f2e36f0..b2365650 100644 --- a/src/util/token.rs +++ b/src/util/token.rs @@ -1,6 +1,6 @@ use crate::util::movement_lexer; -use scribe::buffer::{Buffer, Position}; use luthor::token::Category; +use scribe::buffer::{Buffer, Position}; #[derive(Clone, Copy, PartialEq)] pub enum Direction { @@ -8,22 +8,17 @@ pub enum Direction { Backward, } -pub fn adjacent_token_position(buffer: &Buffer, - whitespace: bool, - direction: Direction) - -> Option { +pub fn adjacent_token_position( + buffer: &Buffer, + whitespace: bool, + direction: Direction, +) -> Option { let mut line = 0; let mut offset = 0; - let mut previous_position = Position { - line: 0, - offset: 0, - }; + let mut previous_position = Position { line: 0, offset: 0 }; let tokens = movement_lexer::lex(&buffer.data()); for token in tokens { - let position = Position { - line, - offset, - }; + let position = Position { line, offset }; if position > *buffer.cursor && direction == Direction::Forward { // We've found the next token! if whitespace { @@ -58,10 +53,7 @@ pub fn adjacent_token_position(buffer: &Buffer, // If we're looking backwards and the next iteration will pass the // cursor, return the current position, or the previous if it's whitespace. - let next_position = Position { - line, - offset, - }; + let next_position = Position { line, offset }; if next_position >= *buffer.cursor && direction == Direction::Backward { match token.category { Category::Whitespace => { @@ -80,4 +72,3 @@ pub fn adjacent_token_position(buffer: &Buffer, None } - diff --git a/src/view/buffer/lexeme_mapper.rs b/src/view/buffer/lexeme_mapper.rs index 14880f12..807b3298 100644 --- a/src/view/buffer/lexeme_mapper.rs +++ b/src/view/buffer/lexeme_mapper.rs @@ -3,7 +3,7 @@ use scribe::buffer::Position; #[derive(Debug, PartialEq)] pub enum MappedLexeme<'a> { Focused(&'a str), - Blurred(&'a str) + Blurred(&'a str), } pub trait LexemeMapper { diff --git a/src/view/buffer/line_numbers.rs b/src/view/buffer/line_numbers.rs index cd8aa377..dc88dd2c 100644 --- a/src/view/buffer/line_numbers.rs +++ b/src/view/buffer/line_numbers.rs @@ -10,9 +10,9 @@ pub struct LineNumbers { impl LineNumbers { pub fn new(buffer: &Buffer, offset: Option) -> LineNumbers { - LineNumbers{ + LineNumbers { current_number: offset.unwrap_or(0), - buffer_line_count_width: buffer.line_count().to_string().len() + buffer_line_count_width: buffer.line_count().to_string().len(), } } @@ -26,20 +26,18 @@ impl Iterator for LineNumbers { fn next(&mut self) -> Option { self.current_number += 1; - Some( - format!( - " {:>width$} ", - self.current_number, - width = self.buffer_line_count_width - ) - ) + Some(format!( + " {:>width$} ", + self.current_number, + width = self.buffer_line_count_width + )) } } #[cfg(test)] mod tests { - use scribe::Buffer; use super::*; + use scribe::Buffer; #[test] fn width_considers_buffer_line_count_and_padding() { diff --git a/src/view/buffer/mod.rs b/src/view/buffer/mod.rs index cb1a025c..94461375 100644 --- a/src/view/buffer/mod.rs +++ b/src/view/buffer/mod.rs @@ -1,13 +1,13 @@ -mod renderer; -mod render_cache; -mod render_state; mod lexeme_mapper; mod line_numbers; +mod render_cache; +mod render_state; +mod renderer; mod scrollable_region; -pub use self::renderer::BufferRenderer; -pub use self::render_cache::RenderCache; -pub use self::render_state::RenderState; pub use self::lexeme_mapper::{LexemeMapper, MappedLexeme}; pub use self::line_numbers::LineNumbers; +pub use self::render_cache::RenderCache; +pub use self::render_state::RenderState; +pub use self::renderer::BufferRenderer; pub use self::scrollable_region::ScrollableRegion; diff --git a/src/view/buffer/render_cache.rs b/src/view/buffer/render_cache.rs index 044806b8..390a9242 100644 --- a/src/view/buffer/render_cache.rs +++ b/src/view/buffer/render_cache.rs @@ -13,8 +13,8 @@ impl RenderCache for HashMap { #[cfg(test)] mod tests { - use std::collections::HashMap; use super::RenderCache; + use std::collections::HashMap; #[test] fn invalidate_from_clears_entries_starting_from_specified_index() { diff --git a/src/view/buffer/render_state.rs b/src/view/buffer/render_state.rs index 0815c136..d17420a6 100644 --- a/src/view/buffer/render_state.rs +++ b/src/view/buffer/render_state.rs @@ -1,17 +1,17 @@ -use syntect::highlighting::{Highlighter, HighlightState}; +use syntect::highlighting::{HighlightState, Highlighter}; use syntect::parsing::{ParseState, ScopeStack, SyntaxReference}; #[derive(Clone, Debug, PartialEq)] pub struct RenderState { pub highlight: HighlightState, - pub parse: ParseState + pub parse: ParseState, } impl RenderState { pub fn new(highlighter: &Highlighter, syntax: &SyntaxReference) -> RenderState { - RenderState{ + RenderState { highlight: HighlightState::new(highlighter, ScopeStack::new()), - parse: ParseState::new(syntax) + parse: ParseState::new(syntax), } } } diff --git a/src/view/buffer/renderer.rs b/src/view/buffer/renderer.rs index 1b5e4d23..92030e2d 100644 --- a/src/view/buffer/renderer.rs +++ b/src/view/buffer/renderer.rs @@ -1,21 +1,21 @@ +use crate::errors::*; use crate::models::application::Preferences; -use scribe::buffer::{Buffer, Position, Range}; -use scribe::util::LineIterator; -use crate::view::buffer::{LexemeMapper, MappedLexeme, RenderState}; use crate::view::buffer::line_numbers::*; -use crate::view::{Colors, RENDER_CACHE_FREQUENCY, RGBColor, Style}; +use crate::view::buffer::{LexemeMapper, MappedLexeme, RenderState}; use crate::view::color::to_rgb_color; use crate::view::terminal::{Cell, Terminal, TerminalBuffer}; +use crate::view::{Colors, RGBColor, Style, RENDER_CACHE_FREQUENCY}; +use scribe::buffer::{Buffer, Position, Range}; +use scribe::util::LineIterator; use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::str::FromStr; -use syntect::highlighting::{Highlighter, HighlightIterator, Theme}; use syntect::highlighting::Style as ThemeStyle; +use syntect::highlighting::{HighlightIterator, Highlighter, Theme}; use syntect::parsing::{ScopeStack, SyntaxSet}; use unicode_segmentation::UnicodeSegmentation; -use crate::errors::*; /// A one-time-use type that encapsulates all of the /// details involved in rendering a buffer to the screen. @@ -40,12 +40,17 @@ pub struct BufferRenderer<'a, 'p> { impl<'a, 'p> BufferRenderer<'a, 'p> { #[allow(clippy::too_many_arguments)] - pub fn new(buffer: &'a Buffer, highlights: Option<&'a [Range]>, - scroll_offset: usize, terminal: &'a dyn Terminal, theme: &'a Theme, - preferences: &'a Preferences, - render_cache: &'a Rc>>, - syntax_set: &'a SyntaxSet, - terminal_buffer: &'a mut TerminalBuffer<'p>) -> BufferRenderer<'a, 'p> { + pub fn new( + buffer: &'a Buffer, + highlights: Option<&'a [Range]>, + scroll_offset: usize, + terminal: &'a dyn Terminal, + theme: &'a Theme, + preferences: &'a Preferences, + render_cache: &'a Rc>>, + syntax_set: &'a SyntaxSet, + terminal_buffer: &'a mut TerminalBuffer<'p>, + ) -> BufferRenderer<'a, 'p> { let line_numbers = LineNumbers::new(buffer, Some(scroll_offset)); let gutter_width = line_numbers.width() + 1; @@ -54,7 +59,7 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { let stylist = Highlighter::new(theme); let current_style = stylist.get_default(); - BufferRenderer{ + BufferRenderer { buffer, cursor_position: None, gutter_width, @@ -62,10 +67,10 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { stylist, current_style, line_numbers, - buffer_position: Position{ line: 0, offset: 0 }, + buffer_position: Position { line: 0, offset: 0 }, preferences, render_cache, - screen_position: Position{ line: 0, offset: 0 }, + screen_position: Position { line: 0, offset: 0 }, scroll_offset, syntax_set, terminal, @@ -89,15 +94,22 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { Colors::Default }; - self.print(Position{ line: self.screen_position.line, offset }, - Style::Default, - colors, - " "); + self.print( + Position { + line: self.screen_position.line, + offset, + }, + Style::Default, + colors, + " ", + ); } } fn length_guide_offset(&self) -> Option { - self.preferences.line_length_guide().map(|offset| self.gutter_width + offset) + self.preferences + .line_length_guide() + .map(|offset| self.gutter_width + offset) } fn advance_to_next_line(&mut self) { @@ -136,9 +148,9 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { // We're inside of one of the highlighted areas. // Return early with highlight colors. if range.includes(&self.buffer.cursor) { - return (Style::Bold, Colors::SelectMode) + return (Style::Bold, Colors::SelectMode); } else { - return (Style::Inverted, Colors::Default) + return (Style::Inverted, Colors::Default); } } } @@ -157,7 +169,7 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { } else { (Style::Default, Colors::CustomForeground(token_color)) } - }, + } }; (style, colors) @@ -166,7 +178,9 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { fn print_lexeme>>(&mut self, lexeme: L) { for character in lexeme.into().graphemes(true) { // Ignore newline characters. - if character == "\n" { continue; } + if character == "\n" { + continue; + } self.set_cursor(); @@ -174,7 +188,9 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { let token_color = to_rgb_color(self.current_style.foreground); let (style, color) = self.current_char_style(token_color); - if self.preferences.line_wrapping() && self.screen_position.offset == self.terminal.width() { + if self.preferences.line_wrapping() + && self.screen_position.offset == self.terminal.width() + { self.screen_position.line += 1; self.screen_position.offset = self.gutter_width; self.print(self.screen_position, style, color, character.to_string()); @@ -184,7 +200,8 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { // Calculate the next tab stop using the tab-aware offset, // *without considering the line number gutter*, and then // re-add the gutter width to get the actual/screen offset. - let buffer_tab_stop = self.next_tab_stop(self.screen_position.offset - self.gutter_width); + let buffer_tab_stop = + self.next_tab_stop(self.screen_position.offset - self.gutter_width); let mut screen_tab_stop = buffer_tab_stop + self.gutter_width; // Now that we know where we'd like to go, prevent it from being off-screen. @@ -220,14 +237,22 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { !self.before_visible_content() && !self.after_visible_content() } - pub fn render(&mut self, lines: LineIterator<'p>, mut lexeme_mapper: Option<&mut dyn LexemeMapper>) -> Result> { + pub fn render( + &mut self, + lines: LineIterator<'p>, + mut lexeme_mapper: Option<&mut dyn LexemeMapper>, + ) -> Result> { self.terminal.set_cursor(None); // Print the first line number. Others will // be handled as newlines are encountered. self.print_line_number(); let highlighter = Highlighter::new(self.theme); - let syntax_definition = self.buffer.syntax_definition.as_ref().ok_or("Buffer has no syntax definition")?; + let syntax_definition = self + .buffer + .syntax_definition + .as_ref() + .ok_or("Buffer has no syntax definition")?; // Start or resume state from a previous cache point, if available. let (cached_line_no, mut state) = self @@ -239,16 +264,17 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { // Skip past lines that precede the cached render state. if line_no >= cached_line_no { if line_no % RENDER_CACHE_FREQUENCY == 0 && line_no > 0 { - self.render_cache.borrow_mut().insert(line_no, state.clone()); + self.render_cache + .borrow_mut() + .insert(line_no, state.clone()); } - let events = state.parse.parse_line(line, self.syntax_set).chain_err(|| BUFFER_PARSE_FAILED)?; - let styled_lexemes = HighlightIterator::new( - &mut state.highlight, - &events, - line, - &highlighter - ); + let events = state + .parse + .parse_line(line, self.syntax_set) + .chain_err(|| BUFFER_PARSE_FAILED)?; + let styled_lexemes = + HighlightIterator::new(&mut state.highlight, &events, line, &highlighter); for (style, lexeme) in styled_lexemes { // Move along until we've hit visible content. @@ -269,13 +295,12 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { MappedLexeme::Focused(value) => { self.current_style = focused_style; self.print_lexeme(value.to_string()); - }, + } MappedLexeme::Blurred(value) => { self.current_style = blurred_style; self.print_lexeme(value.to_string()); } } - } } else { self.current_style = style; @@ -301,7 +326,9 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { } fn print_line_number(&mut self) { - if !self.inside_visible_content() { return }; + if !self.inside_visible_content() { + return; + }; let line_number = self.line_numbers.next().unwrap(); @@ -313,10 +340,13 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { }; self.print( - Position{ line: self.screen_position.line, offset: 0 }, + Position { + line: self.screen_position.line, + offset: 0, + }, weight, Colors::Focused, - line_number + line_number, ); // Leave a one-column gap between line numbers and buffer content. @@ -326,34 +356,34 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { Colors::Default }; self.print( - Position{ line: self.screen_position.line, offset: self.line_numbers.width() }, + Position { + line: self.screen_position.line, + offset: self.line_numbers.width(), + }, weight, gap_color, - " " + " ", ); self.screen_position.offset = self.line_numbers.width() + 1; } fn next_tab_stop(&self, offset: usize) -> usize { - (offset / self.preferences.tab_width(self.buffer.path.as_ref()) + 1) * self.preferences.tab_width(self.buffer.path.as_ref()) + (offset / self.preferences.tab_width(self.buffer.path.as_ref()) + 1) + * self.preferences.tab_width(self.buffer.path.as_ref()) } fn mapper_styles(&self) -> (ThemeStyle, ThemeStyle) { - let focused_style = self - .stylist - .style_for_stack( - ScopeStack::from_str("keyword") + let focused_style = self.stylist.style_for_stack( + ScopeStack::from_str("keyword") .unwrap_or_default() - .as_slice() - ); - let blurred_style = self - .stylist - .style_for_stack( - ScopeStack::from_str("comment") + .as_slice(), + ); + let blurred_style = self.stylist.style_for_stack( + ScopeStack::from_str("comment") .unwrap_or_default() - .as_slice() - ); + .as_slice(), + ); (focused_style, blurred_style) } @@ -371,35 +401,37 @@ impl<'a, 'p> BufferRenderer<'a, 'p> { } fn print(&mut self, position: Position, style: Style, colors: Colors, content: C) - where C: Into> + where + C: Into>, { self.terminal_buffer.set_cell( position, - Cell{ content: content.into(), style, colors } + Cell { + content: content.into(), + style, + colors, + }, ); } } fn has_trailing_newline(line: &str) -> bool { - line.chars() - .last() - .map(|c| c == '\n') - .unwrap_or(false) + line.chars().last().map(|c| c == '\n').unwrap_or(false) } #[cfg(test)] mod tests { + use super::{BufferRenderer, LexemeMapper, MappedLexeme}; use crate::models::application::Preferences; - use scribe::{Buffer, Workspace}; + use crate::view::terminal::*; use scribe::buffer::Position; use scribe::util::LineIterator; + use scribe::{Buffer, Workspace}; use std::cell::RefCell; use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::rc::Rc; - use super::{BufferRenderer, LexemeMapper, MappedLexeme}; use syntect::highlighting::ThemeSet; - use crate::view::terminal::*; use yaml_rust::yaml::YamlLoader; #[test] @@ -416,7 +448,11 @@ mod tests { let terminal = build_terminal().unwrap(); let mut terminal_buffer = TerminalBuffer::new(terminal.width(), terminal.height()); let theme_set = ThemeSet::load_defaults(); - let data = YamlLoader::load_from_str("tab_width: 100").unwrap().into_iter().nth(0).unwrap(); + let data = YamlLoader::load_from_str("tab_width: 100") + .unwrap() + .into_iter() + .nth(0) + .unwrap(); let preferences = Preferences::new(Some(data)); BufferRenderer::new( @@ -428,8 +464,10 @@ mod tests { &preferences, &Rc::new(RefCell::new(HashMap::new())), &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); } #[test] @@ -448,7 +486,11 @@ mod tests { let terminal = build_terminal().unwrap(); let mut terminal_buffer = TerminalBuffer::new(terminal.width(), terminal.height()); let theme_set = ThemeSet::load_defaults(); - let data = YamlLoader::load_from_str("tab_width: 2").unwrap().into_iter().nth(0).unwrap(); + let data = YamlLoader::load_from_str("tab_width: 2") + .unwrap() + .into_iter() + .nth(0) + .unwrap(); let preferences = Preferences::new(Some(data)); BufferRenderer::new( @@ -460,8 +502,10 @@ mod tests { &preferences, &Rc::new(RefCell::new(HashMap::new())), &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); // Both tabs should fully expand. let expected_content = " 1 xy"; @@ -487,7 +531,11 @@ mod tests { let terminal = build_terminal().unwrap(); let mut terminal_buffer = TerminalBuffer::new(terminal.width(), terminal.height()); let theme_set = ThemeSet::load_defaults(); - let data = YamlLoader::load_from_str("tab_width: 2").unwrap().into_iter().nth(0).unwrap(); + let data = YamlLoader::load_from_str("tab_width: 2") + .unwrap() + .into_iter() + .nth(0) + .unwrap(); let preferences = Preferences::new(Some(data)); BufferRenderer::new( @@ -499,8 +547,10 @@ mod tests { &preferences, &Rc::new(RefCell::new(HashMap::new())), &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); // The space between the tabs should just eat into the second tab's width. let expected_content = " 1 xy"; @@ -535,8 +585,10 @@ mod tests { &preferences, &Rc::new(RefCell::new(HashMap::new())), &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); let expected_content = " 1 amp ed\n itor \n 2 second\n line \n 3 "; assert_eq!( @@ -578,8 +630,10 @@ mod tests { &preferences, &Rc::new(RefCell::new(HashMap::new())), &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, Some(&mut TestMapper{})).unwrap(); + &mut terminal_buffer, + ) + .render(lines, Some(&mut TestMapper {})) + .unwrap(); let expected_content = " 1 mapped"; assert_eq!( @@ -613,10 +667,12 @@ mod tests { &preferences, &Rc::new(RefCell::new(HashMap::new())), &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); - assert_eq!(cursor_position, Some(Position{ line: 0, offset: 4 })); + assert_eq!(cursor_position, Some(Position { line: 0, offset: 4 })); } #[test] @@ -648,8 +704,10 @@ mod tests { &preferences, &render_cache, &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); assert_eq!(render_cache.borrow().keys().count(), 5); } @@ -684,8 +742,10 @@ mod tests { &preferences, &render_cache, &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); assert_eq!(render_cache.borrow().keys().count(), 1); let initial_cache = render_cache.borrow().values().nth(0).unwrap().clone(); @@ -707,8 +767,10 @@ mod tests { &preferences, &render_cache, &workspace.syntax_set, - &mut terminal_buffer - ).render(lines2, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines2, None) + .unwrap(); assert_eq!(render_cache.borrow().keys().count(), 5); for value in render_cache.borrow().values() { @@ -746,8 +808,10 @@ mod tests { &preferences, &render_cache, &workspace.syntax_set, - &mut terminal_buffer - ).render(lines, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines, None) + .unwrap(); assert_eq!(render_cache.borrow().keys().count(), 1); terminal.clear(); @@ -768,12 +832,15 @@ mod tests { &preferences, &render_cache, &workspace.syntax_set, - &mut terminal_buffer - ).render(lines2, None).unwrap(); + &mut terminal_buffer, + ) + .render(lines2, None) + .unwrap(); let expected_content = " 201 line\n 202 line\n 203 line\n 204 "; assert_eq!( &terminal_buffer.content()[0..expected_content.len()], - expected_content); + expected_content + ); } } diff --git a/src/view/buffer/scrollable_region.rs b/src/view/buffer/scrollable_region.rs index 734662f4..d132fd50 100644 --- a/src/view/buffer/scrollable_region.rs +++ b/src/view/buffer/scrollable_region.rs @@ -1,8 +1,8 @@ -use std::sync::Arc; -use scribe::buffer::Buffer; -use unicode_segmentation::UnicodeSegmentation; use crate::view::buffer::LineNumbers; use crate::view::terminal::Terminal; +use scribe::buffer::Buffer; +use std::sync::Arc; +use unicode_segmentation::UnicodeSegmentation; /// Abstract representation of a fixed-height section of the screen. /// Used to determine visible ranges of lines based on previous state, @@ -30,9 +30,8 @@ impl ScrollableRegion { } else { // Calculate and apply the absolute line // offset based on the cursor location. - let starting_line = (buffer.cursor.line).saturating_sub( - self.preceding_line_count(buffer, self.height()) - ); + let starting_line = (buffer.cursor.line) + .saturating_sub(self.preceding_line_count(buffer, self.height())); if starting_line > self.line_offset { self.line_offset = starting_line; @@ -44,9 +43,10 @@ impl ScrollableRegion { pub fn scroll_to_center(&mut self, buffer: &Buffer) { let limit = (self.height() as f32 / 2.0).ceil() as usize; - self.line_offset = buffer.cursor.line.saturating_sub( - self.preceding_line_count(buffer, limit) - ); + self.line_offset = buffer + .cursor + .line + .saturating_sub(self.preceding_line_count(buffer, limit)); } /// The number of lines the region has scrolled over. @@ -133,7 +133,7 @@ mod tests { let mut buffer = Buffer::new(); let mut region = ScrollableRegion::new(terminal); buffer.insert("\n\n"); - buffer.cursor.move_to(Position{ line: 2, offset: 0 }); + buffer.cursor.move_to(Position { line: 2, offset: 0 }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 0); } @@ -146,7 +146,10 @@ mod tests { for _ in 0..10 { buffer.insert("\n"); } - buffer.cursor.move_to(Position{ line: 10, offset: 0 }); + buffer.cursor.move_to(Position { + line: 10, + offset: 0, + }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 2); } @@ -159,7 +162,7 @@ mod tests { for _ in 0..10 { buffer.insert("word \n"); } - buffer.cursor.move_to(Position{ line: 9, offset: 0 }); + buffer.cursor.move_to(Position { line: 9, offset: 0 }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 1); } @@ -173,7 +176,7 @@ mod tests { for _ in 0..5 { buffer.insert("\n"); } - buffer.cursor.move_to(Position{ line: 5, offset: 0 }); + buffer.cursor.move_to(Position { line: 5, offset: 0 }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 5); } @@ -186,7 +189,7 @@ mod tests { for _ in 0..10 { buffer.insert("\n"); } - buffer.cursor.move_to(Position{ line: 9, offset: 0 }); + buffer.cursor.move_to(Position { line: 9, offset: 0 }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 1); } @@ -205,7 +208,7 @@ mod tests { buffer.insert(" \n"); } - buffer.cursor.move_to(Position{ line: 5, offset: 0 }); + buffer.cursor.move_to(Position { line: 5, offset: 0 }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 1); } @@ -223,7 +226,7 @@ mod tests { // are considered, which eat into terminal space. buffer.insert(" \n"); } - buffer.cursor.move_to(Position{ line: 5, offset: 0 }); + buffer.cursor.move_to(Position { line: 5, offset: 0 }); region.scroll_into_view(&buffer); assert_eq!(region.line_offset(), 2); } @@ -236,7 +239,10 @@ mod tests { for _ in 0..20 { buffer.insert("\n"); } - buffer.cursor.move_to(Position{ line: 20, offset: 0 }); + buffer.cursor.move_to(Position { + line: 20, + offset: 0, + }); region.scroll_to_center(&buffer); assert_eq!(region.line_offset(), 16); } @@ -262,7 +268,7 @@ mod tests { buffer.insert(" \n"); } // Insert non-wrapped lines below. - buffer.cursor.move_to(Position{ line: 4, offset: 0 }); + buffer.cursor.move_to(Position { line: 4, offset: 0 }); for _ in 0..6 { buffer.insert("\n"); } @@ -277,7 +283,7 @@ mod tests { for _ in 0..6 { buffer.insert("\n"); } - buffer.cursor.move_to(Position{ line: 5, offset: 0 }); + buffer.cursor.move_to(Position { line: 5, offset: 0 }); let mut region = ScrollableRegion::new(terminal); region.scroll_to_center(&buffer); assert_eq!(region.line_offset(), 1); diff --git a/src/view/color/colors.rs b/src/view/color/colors.rs index a0b71fc7..f0a5897a 100644 --- a/src/view/color/colors.rs +++ b/src/view/color/colors.rs @@ -3,21 +3,18 @@ use crate::view::color::RGBColor; /// A convenience type used to represent a foreground/background /// color combination. Provides generic/convenience variants to /// discourage color selection outside of the theme, whenever possible. -#[derive(Clone, Copy, Debug, PartialEq)] -#[derive(Default)] +#[derive(Clone, Copy, Debug, PartialEq, Default)] pub enum Colors { #[default] - Default, // default/background - Focused, // default/alt background - Inverted, // background/default - Insert, // white/green - Warning, // white/yellow - PathMode, // white/pink - SearchMode, // white/purple - SelectMode, // white/blue + Default, // default/background + Focused, // default/alt background + Inverted, // background/default + Insert, // white/green + Warning, // white/yellow + PathMode, // white/pink + SearchMode, // white/purple + SelectMode, // white/blue CustomForeground(RGBColor), CustomFocusedForeground(RGBColor), Custom(RGBColor, RGBColor), } - - diff --git a/src/view/color/map.rs b/src/view/color/map.rs index b19eed04..0b9a5f29 100644 --- a/src/view/color/map.rs +++ b/src/view/color/map.rs @@ -1,6 +1,6 @@ -use syntect::highlighting::Theme; use crate::view::color::to_rgb_color; use crate::view::color::{Colors, RGBColor}; +use syntect::highlighting::Theme; pub trait ColorMap { fn map_colors(&self, colors: Colors) -> Colors; @@ -8,23 +8,23 @@ pub trait ColorMap { impl ColorMap for Theme { fn map_colors(&self, colors: Colors) -> Colors { - let fg = self. - settings. - foreground. - map(to_rgb_color). - unwrap_or(RGBColor(255, 255, 255)); + let fg = self + .settings + .foreground + .map(to_rgb_color) + .unwrap_or(RGBColor(255, 255, 255)); - let bg = self. - settings. - background. - map(to_rgb_color). - unwrap_or(RGBColor(0, 0, 0)); + let bg = self + .settings + .background + .map(to_rgb_color) + .unwrap_or(RGBColor(0, 0, 0)); - let alt_bg = self. - settings. - line_highlight. - map(to_rgb_color). - unwrap_or(RGBColor(55, 55, 55)); + let alt_bg = self + .settings + .line_highlight + .map(to_rgb_color) + .unwrap_or(RGBColor(55, 55, 55)); match colors { Colors::Default => Colors::CustomForeground(fg), diff --git a/src/view/event_listener.rs b/src/view/event_listener.rs index 3a0b00c7..3758641d 100644 --- a/src/view/event_listener.rs +++ b/src/view/event_listener.rs @@ -1,21 +1,30 @@ use crate::models::application::Event; -use std::sync::Arc; +use crate::view::Terminal; use std::sync::mpsc::{Receiver, Sender}; +use std::sync::Arc; use std::thread; -use crate::view::Terminal; pub struct EventListener { terminal: Arc>, events: Sender, - killswitch: Receiver<()> + killswitch: Receiver<()>, } impl EventListener { /// Spins up a thread that loops forever, waiting on terminal events /// and forwarding those to the application event channel. - pub fn start(terminal: Arc>, events: Sender, killswitch: Receiver<()>) { + pub fn start( + terminal: Arc>, + events: Sender, + killswitch: Receiver<()>, + ) { thread::spawn(move || { - EventListener { terminal, events, killswitch }.listen(); + EventListener { + terminal, + events, + killswitch, + } + .listen(); }); } @@ -32,11 +41,11 @@ impl EventListener { #[cfg(test)] mod tests { + use super::EventListener; use crate::input::Key; use crate::models::application::Event; - use std::sync::mpsc; - use super::EventListener; use crate::view::terminal::*; + use std::sync::mpsc; #[test] fn start_listens_for_and_sends_key_events_from_terminal() { diff --git a/src/view/mod.rs b/src/view/mod.rs index 6a548750..b8958756 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -1,35 +1,35 @@ -pub mod color; -pub mod terminal; mod buffer; +pub mod color; mod data; mod event_listener; mod presenter; mod style; +pub mod terminal; mod theme_loader; // Published API -pub use self::data::StatusLineData; pub use self::buffer::{LexemeMapper, MappedLexeme}; -pub use self::style::Style; pub use self::color::{Colors, RGBColor}; +pub use self::data::StatusLineData; pub use self::presenter::Presenter; +pub use self::style::Style; pub use self::terminal::*; +use self::buffer::ScrollableRegion; +use self::buffer::{RenderCache, RenderState}; +use self::event_listener::EventListener; +use self::theme_loader::ThemeLoader; use crate::errors::*; use crate::input::Key; use crate::models::application::{Event, Preferences}; -use self::buffer::{RenderCache, RenderState}; -use self::buffer::ScrollableRegion; -use self::event_listener::EventListener; use scribe::buffer::Buffer; +use std::cell::RefCell; use std::cmp; use std::collections::HashMap; -use std::rc::Rc; -use std::cell::RefCell; use std::ops::Drop; +use std::rc::Rc; use std::sync::mpsc::{self, Sender, SyncSender}; use std::sync::Arc; -use self::theme_loader::ThemeLoader; use syntect::highlighting::ThemeSet; const RENDER_CACHE_FREQUENCY: usize = 100; @@ -42,11 +42,14 @@ pub struct View { preferences: Rc>, pub last_key: Option, event_channel: Sender, - event_listener_killswitch: SyncSender<()> + event_listener_killswitch: SyncSender<()>, } impl View { - pub fn new(preferences: Rc>, event_channel: Sender) -> Result { + pub fn new( + preferences: Rc>, + event_channel: Sender, + ) -> Result { let terminal = build_terminal().chain_err(|| "Failed to initialize terminal")?; let theme_path = preferences.borrow().theme_path()?; let theme_set = ThemeLoader::new(theme_path).load()?; @@ -62,7 +65,7 @@ impl View { render_caches: HashMap::new(), theme_set, event_channel, - event_listener_killswitch: killswitch_tx + event_listener_killswitch: killswitch_tx, }) } @@ -99,8 +102,7 @@ impl View { // Limit scrolling to 50% of the screen beyond the end of the buffer. let max = if line_count > half_screen_height { - let visible_line_count = - line_count.saturating_sub(current_offset); + let visible_line_count = line_count.saturating_sub(current_offset); // Of the visible lines, allow scrolling down by however // many lines are beyond the halfway point of the screen. @@ -109,9 +111,7 @@ impl View { 0 }; - self.get_region(buffer)?.scroll_down( - cmp::min(amount, max) - ); + self.get_region(buffer)?.scroll_down(cmp::min(amount, max)); Ok(()) } @@ -128,16 +128,18 @@ impl View { // Tries to fetch a scrollable region for the specified buffer, // inserting (and returning a reference to) a new one if not. fn get_region(&mut self, buffer: &Buffer) -> Result<&mut ScrollableRegion> { - Ok(self.scrollable_regions + Ok(self + .scrollable_regions .entry(buffer_key(buffer)?) - .or_insert( - ScrollableRegion::new(self.terminal.clone()) - ) - ) + .or_insert(ScrollableRegion::new(self.terminal.clone()))) } - fn get_render_cache(&self, buffer: &Buffer) -> Result<&Rc>>> { - let cache = self.render_caches + fn get_render_cache( + &self, + buffer: &Buffer, + ) -> Result<&Rc>>> { + let cache = self + .render_caches .get(&buffer_key(buffer)?) .ok_or("Buffer not properly initialized (render cache not present).")?; @@ -148,7 +150,11 @@ impl View { let _ = self.event_listener_killswitch.send(()); self.terminal.suspend(); let (killswitch_tx, killswitch_rx) = mpsc::sync_channel(0); - EventListener::start(self.terminal.clone(), self.event_channel.clone(), killswitch_rx); + EventListener::start( + self.terminal.clone(), + self.event_channel.clone(), + killswitch_rx, + ); self.event_listener_killswitch = killswitch_tx; } @@ -160,17 +166,15 @@ impl View { pub fn initialize_buffer(&mut self, buffer: &mut Buffer) -> Result<()> { // Build and store a new render cache for the buffer. let render_cache = Rc::new(RefCell::new(HashMap::new())); - self.render_caches.insert( - buffer_key(buffer)?, - render_cache.clone() - ); + self.render_caches + .insert(buffer_key(buffer)?, render_cache.clone()); // Wire up the buffer's change callback to invalidate the render cache. - buffer.change_callback = Some( - Box::new(move |change_position| { - render_cache.borrow_mut().invalidate_from(change_position.line); - }) - ); + buffer.change_callback = Some(Box::new(move |change_position| { + render_cache + .borrow_mut() + .invalidate_from(change_position.line); + })); Ok(()) } @@ -183,21 +187,23 @@ impl Drop for View { } fn buffer_key(buffer: &Buffer) -> Result { - buffer.id.ok_or_else(|| Error::from("Buffer ID doesn't exist")) + buffer + .id + .ok_or_else(|| Error::from("Buffer ID doesn't exist")) } #[cfg(test)] mod tests { - use scribe::{Buffer, Workspace}; use super::View; use crate::models::application::Preferences; + use crate::view::buffer::RenderState; use scribe::buffer::Position; + use scribe::{Buffer, Workspace}; use std::cell::RefCell; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::mpsc; use syntect::highlighting::{Highlighter, ThemeSet}; - use crate::view::buffer::RenderState; #[test] fn scroll_down_prevents_scrolling_completely_beyond_buffer() { @@ -274,7 +280,8 @@ mod tests { // Build a render state. let theme_set = ThemeSet::load_defaults(); let highlighter = Highlighter::new(&theme_set.themes["base16-ocean.dark"]); - let render_state = RenderState::new(&highlighter, buffer.syntax_definition.as_ref().unwrap()); + let render_state = + RenderState::new(&highlighter, buffer.syntax_definition.as_ref().unwrap()); // Populate the render cache with some values. view.render_caches @@ -294,7 +301,10 @@ mod tests { .insert(200, render_state.clone()); // Make a change that will invalidate all lines beyond 100. - buffer.cursor.move_to(Position{ line: 99, offset: 0 }); + buffer.cursor.move_to(Position { + line: 99, + offset: 0, + }); buffer.insert("\n"); assert_eq!( @@ -308,4 +318,3 @@ mod tests { ); } } - diff --git a/src/view/presenter.rs b/src/view/presenter.rs index c4705125..3fb2c96f 100644 --- a/src/view/presenter.rs +++ b/src/view/presenter.rs @@ -1,9 +1,9 @@ use crate::errors::*; use crate::view::buffer::{BufferRenderer, LexemeMapper}; use crate::view::color::{ColorMap, Colors}; -use crate::view::StatusLineData; use crate::view::style::Style; use crate::view::terminal::{Cell, CursorType, TerminalBuffer}; +use crate::view::StatusLineData; use crate::view::View; use scribe::buffer::{Buffer, Position, Range}; use scribe::util::LineIterator; @@ -27,20 +27,19 @@ impl<'p> Presenter<'p> { let theme = { let preferences = view.preferences.borrow(); let theme_name = preferences.theme(); - let theme = view.theme_set.themes + let theme = view + .theme_set + .themes .get(theme_name) .ok_or_else(|| format!("Couldn't find \"{}\" theme", theme_name))?; theme.clone() }; - Ok(Presenter{ + Ok(Presenter { cursor_position: None, - terminal_buffer: TerminalBuffer::new( - view.terminal.width(), - view.terminal.height(), - ), + terminal_buffer: TerminalBuffer::new(view.terminal.width(), view.terminal.height()), theme, - view + view, }) } @@ -79,9 +78,14 @@ impl<'p> Presenter<'p> { Ok(()) } - pub fn print_buffer(&mut self, buffer: &Buffer, buffer_data: &'p str, - syntax_set: &'p SyntaxSet, highlights: Option<&[Range]>, - lexeme_mapper: Option<&'p mut dyn LexemeMapper>) -> Result<()> { + pub fn print_buffer( + &mut self, + buffer: &Buffer, + buffer_data: &'p str, + syntax_set: &'p SyntaxSet, + highlights: Option<&[Range]>, + lexeme_mapper: Option<&'p mut dyn LexemeMapper>, + ) -> Result<()> { let scroll_offset = self.view.get_region(buffer)?.line_offset(); let lines = LineIterator::new(buffer_data); @@ -94,8 +98,9 @@ impl<'p> Presenter<'p> { &self.view.preferences.borrow(), self.view.get_render_cache(buffer)?, syntax_set, - &mut self.terminal_buffer - ).render(lines, lexeme_mapper)?; + &mut self.terminal_buffer, + ) + .render(lines, lexeme_mapper)?; Ok(()) } @@ -103,54 +108,62 @@ impl<'p> Presenter<'p> { pub fn print_status_line(&mut self, entries: &[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!( + 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(space), - ) - }, - _ => element.content.clone(), - }; - - // Update the tracked offset. - let updated_offset = offset + content.len(); - - self.print( - &Position{ line, offset }, - element.style, - element.colors, - content - ); - - updated_offset - }); + 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(); + + self.print( + &Position { line, offset }, + element.style, + element.colors, + content, + ); + + updated_offset + }); } pub fn print(&mut self, position: &Position, style: Style, colors: Colors, content: C) - where C: Into> + where + C: Into>, { self.terminal_buffer.set_cell( *position, - Cell{ content: content.into(), style, colors } + Cell { + content: content.into(), + style, + colors, + }, ); } } @@ -187,25 +200,32 @@ mod tests { workspace.add_buffer(buffer); // Scroll down enough to trigger caching during the render process. - view.scroll_down(workspace.current_buffer.as_ref().unwrap(), 105).unwrap(); + view.scroll_down(workspace.current_buffer.as_ref().unwrap(), 105) + .unwrap(); // Ensure there is nothing in the render cache for this buffer. - let mut cache = view.get_render_cache(workspace.current_buffer.as_ref().unwrap()).unwrap(); + let mut cache = view + .get_render_cache(workspace.current_buffer.as_ref().unwrap()) + .unwrap(); assert_eq!(cache.borrow().iter().count(), 0); // Draw the buffer. let mut presenter = view.build_presenter().unwrap(); let data = workspace.current_buffer.as_ref().unwrap().data(); - presenter.print_buffer( - workspace.current_buffer.as_ref().unwrap(), - &data, - &workspace.syntax_set, - None, - None - ).unwrap(); + presenter + .print_buffer( + workspace.current_buffer.as_ref().unwrap(), + &data, + &workspace.syntax_set, + None, + None, + ) + .unwrap(); // Ensure there is something in the render cache for this buffer. - cache = view.get_render_cache(workspace.current_buffer.as_ref().unwrap()).unwrap(); + cache = view + .get_render_cache(workspace.current_buffer.as_ref().unwrap()) + .unwrap(); assert_ne!(cache.borrow().iter().count(), 0); } } diff --git a/src/view/style.rs b/src/view/style.rs index f792d56b..b6bb4dff 100644 --- a/src/view/style.rs +++ b/src/view/style.rs @@ -1,5 +1,4 @@ -#[derive(Copy, Clone, Debug, PartialEq)] -#[derive(Default)] +#[derive(Copy, Clone, Debug, PartialEq, Default)] pub enum Style { #[default] Default, @@ -7,5 +6,3 @@ pub enum Style { Inverted, Italic, } - - diff --git a/src/view/terminal/buffer.rs b/src/view/terminal/buffer.rs index f0fda614..3dc75699 100644 --- a/src/view/terminal/buffer.rs +++ b/src/view/terminal/buffer.rs @@ -9,10 +9,10 @@ pub struct TerminalBuffer<'c> { impl<'c> TerminalBuffer<'c> { pub fn new(width: usize, height: usize) -> TerminalBuffer<'c> { - TerminalBuffer{ + TerminalBuffer { width, height, - cells: vec![Cell::default(); width*height], + cells: vec![Cell::default(); width * height], } } @@ -25,7 +25,7 @@ impl<'c> TerminalBuffer<'c> { } pub fn clear(&mut self) { - self.cells = vec![Cell::default(); self.width*self.height]; + self.cells = vec![Cell::default(); self.width * self.height]; } pub fn iter(&self) -> TerminalBufferIterator { @@ -53,11 +53,11 @@ impl<'c> TerminalBuffer<'c> { #[cfg(test)] mod tests { - use crate::view::{Colors, Style}; + use super::TerminalBuffer; use crate::view::terminal::Cell; + use crate::view::{Colors, Style}; use scribe::buffer::Position; use std::borrow::Cow; - use super::TerminalBuffer; #[test] fn new_sets_cell_capacity() { @@ -80,8 +80,12 @@ mod tests { #[test] fn set_cell_sets_correct_cell() { let mut buffer = TerminalBuffer::new(5, 10); - let cell = Cell{ content: Cow::from("a"), colors: Colors::Default, style: Style::Default }; - buffer.set_cell(Position{ line: 2, offset: 1 }, cell.clone()); + let cell = Cell { + content: Cow::from("a"), + colors: Colors::Default, + style: Style::Default, + }; + buffer.set_cell(Position { line: 2, offset: 1 }, cell.clone()); assert_eq!(buffer.cells[11], cell); } @@ -89,8 +93,12 @@ mod tests { #[test] fn clear_resets_cells_to_default() { let mut buffer = TerminalBuffer::new(5, 10); - let cell = Cell{ content: Cow::from(" "), colors: Colors::Default, style: Style::Default }; - buffer.set_cell(Position{ line: 2, offset: 1 }, cell.clone()); + let cell = Cell { + content: Cow::from(" "), + colors: Colors::Default, + style: Style::Default, + }; + buffer.set_cell(Position { line: 2, offset: 1 }, cell.clone()); assert_eq!(buffer.cells[11], cell); buffer.clear(); diff --git a/src/view/terminal/buffer_iterator.rs b/src/view/terminal/buffer_iterator.rs index facd1ec3..12391e54 100644 --- a/src/view/terminal/buffer_iterator.rs +++ b/src/view/terminal/buffer_iterator.rs @@ -11,7 +11,11 @@ pub struct TerminalBufferIterator<'c> { impl<'c> TerminalBufferIterator<'c> { pub fn new(width: usize, cells: &'c Vec>) -> TerminalBufferIterator { - TerminalBufferIterator{ index: 0, width, cells } + TerminalBufferIterator { + index: 0, + width, + cells, + } } } @@ -21,9 +25,9 @@ impl<'c> Iterator for TerminalBufferIterator<'c> { /// Iterates over lines of cells. fn next(&mut self) -> Option { if self.index < self.cells.len() { - let position = Position{ + let position = Position { line: self.index / self.width, - offset: self.index % self.width + offset: self.index % self.width, }; let cell = &self.cells[self.index]; self.index += cell.content.graphemes(true).count().max(1); @@ -37,25 +41,46 @@ impl<'c> Iterator for TerminalBufferIterator<'c> { #[cfg(test)] mod tests { - use scribe::buffer::Position; - use std::borrow::Cow; use super::TerminalBufferIterator; use crate::view::terminal::Cell; use crate::view::{Colors, Style}; + use scribe::buffer::Position; + use std::borrow::Cow; #[test] fn terminal_buffer_iterator_yields_cells_and_their_positions() { let width = 3; let cells = vec![ - Cell{ content: Cow::from("a"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("m"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("p"), colors: Colors::Default, style: Style::Default } + Cell { + content: Cow::from("a"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("m"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("p"), + colors: Colors::Default, + style: Style::Default, + }, ]; let mut iterator = TerminalBufferIterator::new(width, &cells); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 0 }, &cells[0]))); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 1 }, &cells[1]))); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 2 }, &cells[2]))); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 0 }, &cells[0])) + ); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 1 }, &cells[1])) + ); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 2 }, &cells[2])) + ); assert_eq!(iterator.next(), None); } @@ -63,28 +88,65 @@ mod tests { fn terminal_buffer_iterator_considers_width_when_calculating_positions() { let width = 2; let cells = vec![ - Cell{ content: Cow::from("a"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("m"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("p"), colors: Colors::Default, style: Style::Default } + Cell { + content: Cow::from("a"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("m"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("p"), + colors: Colors::Default, + style: Style::Default, + }, ]; let mut iterator = TerminalBufferIterator::new(width, &cells); - assert_eq!(iterator.nth(2), Some((Position{ line: 1, offset: 0 }, &cells[2]))); + assert_eq!( + iterator.nth(2), + Some((Position { line: 1, offset: 0 }, &cells[2])) + ); } #[test] fn terminal_buffer_iterator_handles_overlapping_cells_correctly() { let width = 4; let cells = vec![ - Cell{ content: Cow::from("amp"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("b"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("c"), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("d"), colors: Colors::Default, style: Style::Default } + Cell { + content: Cow::from("amp"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("b"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("c"), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("d"), + colors: Colors::Default, + style: Style::Default, + }, ]; let mut iterator = TerminalBufferIterator::new(width, &cells); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 0 }, &cells[0]))); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 3 }, &cells[3]))); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 0 }, &cells[0])) + ); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 3 }, &cells[3])) + ); assert_eq!(iterator.next(), None); } @@ -92,13 +154,27 @@ mod tests { fn terminal_buffer_iterator_handles_empty_cells_correctly() { let width = 4; let cells = vec![ - Cell{ content: Cow::from(""), colors: Colors::Default, style: Style::Default }, - Cell{ content: Cow::from("a"), colors: Colors::Default, style: Style::Default }, + Cell { + content: Cow::from(""), + colors: Colors::Default, + style: Style::Default, + }, + Cell { + content: Cow::from("a"), + colors: Colors::Default, + style: Style::Default, + }, ]; let mut iterator = TerminalBufferIterator::new(width, &cells); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 0 }, &cells[0]))); - assert_eq!(iterator.next(), Some((Position{ line: 0, offset: 1 }, &cells[1]))); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 0 }, &cells[0])) + ); + assert_eq!( + iterator.next(), + Some((Position { line: 0, offset: 1 }, &cells[1])) + ); assert_eq!(iterator.next(), None); } } diff --git a/src/view/terminal/cell.rs b/src/view/terminal/cell.rs index f680a9c5..43a719c3 100644 --- a/src/view/terminal/cell.rs +++ b/src/view/terminal/cell.rs @@ -10,10 +10,10 @@ pub struct Cell<'c> { impl<'c> Default for Cell<'c> { fn default() -> Self { - Cell{ + Cell { content: " ".into(), colors: Colors::default(), - style: Style::default() + style: Style::default(), } } } diff --git a/src/view/terminal/mod.rs b/src/view/terminal/mod.rs index 54ed31e9..d795ad1e 100644 --- a/src/view/terminal/mod.rs +++ b/src/view/terminal/mod.rs @@ -9,8 +9,8 @@ mod test_terminal; use crate::errors::*; use crate::models::application::Event; -use scribe::buffer::Position; use crate::view::{Colors, Style}; +use scribe::buffer::Position; use std::sync::Arc; pub use self::buffer::TerminalBuffer; @@ -38,7 +38,9 @@ pub trait Terminal { #[cfg(not(any(test, feature = "bench")))] pub fn build_terminal() -> Result>> { - Ok(Arc::new(Box::new(termion_terminal::TermionTerminal::new()?))) + Ok(Arc::new( + Box::new(termion_terminal::TermionTerminal::new()?), + )) } #[cfg(any(test, feature = "bench"))] diff --git a/src/view/terminal/termion_terminal.rs b/src/view/terminal/termion_terminal.rs index 664750bd..72db3524 100644 --- a/src/view/terminal/termion_terminal.rs +++ b/src/view/terminal/termion_terminal.rs @@ -1,28 +1,28 @@ extern crate libc; extern crate termion; -use crate::errors::*; -use mio::{Events, Poll, PollOpt, Ready, Token}; -use mio::unix::EventedFd; -use super::Terminal; -use std::io::Stdout; -use std::os::unix::io::AsRawFd; -use scribe::buffer::{Distance, Position}; use self::termion::color::{Bg, Fg}; -use self::termion::{color, cursor}; use self::termion::input::{Keys, TermRead}; use self::termion::raw::{IntoRawMode, RawTerminal}; use self::termion::screen::{AlternateScreen, IntoAlternateScreen}; use self::termion::style; +use self::termion::{color, cursor}; +use super::Terminal; +use crate::errors::*; +use crate::view::{Colors, CursorType, Style}; +use mio::unix::EventedFd; +use mio::{Events, Poll, PollOpt, Ready, Token}; +use scribe::buffer::{Distance, Position}; +use signal_hook::iterator::Signals; use std::borrow::{Borrow, BorrowMut}; -use std::io::{BufWriter, Stdin, stdin, stdout, Write}; use std::fmt::Display; +use std::io::Stdout; +use std::io::{stdin, stdout, BufWriter, Stdin, Write}; use std::ops::Drop; +use std::os::unix::io::AsRawFd; use std::sync::Mutex; use std::time::Duration; -use crate::view::{Colors, CursorType, Style}; use unicode_segmentation::UnicodeSegmentation; -use signal_hook::iterator::Signals; use self::termion::event::Key as TermionKey; use crate::input::Key; @@ -72,7 +72,7 @@ impl TermionTerminal { if let Some(mapped_style) = map_style(new_style) { let _ = write!(output, "{}", mapped_style); - return Ok(()) + return Ok(()); } // Current text has no style; send a reset to the terminal. @@ -82,9 +82,15 @@ impl TermionTerminal { let color_guard = self.current_colors.lock().map_err(|_| LOCK_POISONED)?; if let Some(current_colors) = color_guard.borrow().as_ref() { match *current_colors { - Colors::Default => { let _ = write!(output, "{}{}", Fg(color::Reset), Bg(color::Reset)); } - Colors::Custom(fg, bg) => { let _ = write!(output, "{}{}", Fg(fg), Bg(bg)); } - Colors::CustomForeground(fg) => { let _ = write!(output, "{}{}", Fg(fg), Bg(color::Reset)); } + Colors::Default => { + let _ = write!(output, "{}{}", Fg(color::Reset), Bg(color::Reset)); + } + Colors::Custom(fg, bg) => { + let _ = write!(output, "{}{}", Fg(fg), Bg(bg)); + } + Colors::CustomForeground(fg) => { + let _ = write!(output, "{}{}", Fg(fg), Bg(color::Reset)); + } _ => (), }; } @@ -106,9 +112,15 @@ impl TermionTerminal { current_colors.replace(new_colors); match new_colors { - Colors::Default => { let _ = write!(output, "{}{}", Fg(color::Reset), Bg(color::Reset)); } - Colors::Custom(fg, bg) => { let _ = write!(output, "{}{}", Fg(fg), Bg(bg)); } - Colors::CustomForeground(fg) => { let _ = write!(output, "{}{}", Fg(fg), Bg(color::Reset)); } + Colors::Default => { + let _ = write!(output, "{}{}", Fg(color::Reset), Bg(color::Reset)); + } + Colors::Custom(fg, bg) => { + let _ = write!(output, "{}{}", Fg(fg), Bg(bg)); + } + Colors::CustomForeground(fg) => { + let _ = write!(output, "{}{}", Fg(fg), Bg(color::Reset)); + } _ => (), }; } @@ -136,7 +148,9 @@ impl Terminal for TermionTerminal { fn listen(&self) -> Option { // Check for events on stdin. let mut events = Events::with_capacity(1); - self.event_listener.poll(&mut events, Some(Duration::from_millis(100))).ok()?; + self.event_listener + .poll(&mut events, Some(Duration::from_millis(100))) + .ok()?; if let Some(event) = events.iter().next() { match event.token() { STDIN_INPUT => { @@ -164,7 +178,7 @@ impl Terminal for TermionTerminal { TermionKey::Ctrl(c) => Some(Event::Key(Key::Ctrl(c))), _ => None, } - }, + } RESIZE => { // Consume the resize signal so it doesn't trigger again. self.signals.into_iter().next(); @@ -220,14 +234,11 @@ impl Terminal for TermionTerminal { if let Some(t) = output.as_mut() { match position { Some(ref pos) => { - let _ = write!( - t, - "{}{}", - cursor::Show, - cursor_position(pos) - ); - }, - None => { let _ = write!(t, "{}", cursor::Hide); }, + let _ = write!(t, "{}{}", cursor::Show, cursor_position(pos)); + } + None => { + let _ = write!(t, "{}", cursor::Hide); + } } } } @@ -237,15 +248,27 @@ impl Terminal for TermionTerminal { if let Ok(mut output) = self.output.lock() { if let Some(t) = output.as_mut() { match cursor_type { - CursorType::Bar => { let _ = write!(t, "{}", cursor::SteadyBar); }, - CursorType::BlinkingBar => { let _ = write!(t, "{}", cursor::BlinkingBar); }, - CursorType::Block => { let _ = write!(t, "{}", cursor::SteadyBlock); }, + CursorType::Bar => { + let _ = write!(t, "{}", cursor::SteadyBar); + } + CursorType::BlinkingBar => { + let _ = write!(t, "{}", cursor::BlinkingBar); + } + CursorType::Block => { + let _ = write!(t, "{}", cursor::SteadyBlock); + } } } } } - fn print<'a>(&self, target_position: &Position, style: Style, colors: Colors, content: &str) -> Result<()> { + fn print<'a>( + &self, + target_position: &Position, + style: Style, + colors: Colors, + content: &str, + ) -> Result<()> { self.update_style(style)?; self.update_colors(colors)?; @@ -260,10 +283,11 @@ impl Terminal for TermionTerminal { // Track where the cursor is after printing. *current_position = Some( - *target_position + Distance{ - lines: 0, - offset: content.graphemes(true).count() - } + *target_position + + Distance { + lines: 0, + offset: content.graphemes(true).count(), + }, ); } @@ -278,7 +302,7 @@ impl Terminal for TermionTerminal { fn suspend(&self) { self.restore_cursor(); - self.set_cursor(Some(Position{ line: 0, offset: 0 })); + self.set_cursor(Some(Position { line: 0, offset: 0 })); self.present(); // Clear the current position so we're forced @@ -314,20 +338,17 @@ impl Terminal for TermionTerminal { impl Drop for TermionTerminal { fn drop(&mut self) { self.restore_cursor(); - self.set_cursor(Some(Position{ line: 0, offset: 0 })); + self.set_cursor(Some(Position { line: 0, offset: 0 })); } } fn cursor_position(position: &Position) -> cursor::Goto { - cursor::Goto( - (position.offset + 1) as u16, - (position.line + 1) as u16 - ) + cursor::Goto((position.offset + 1) as u16, (position.line + 1) as u16) } fn terminal_size() -> (usize, usize) { termion::terminal_size() - .map(|(x,y)| (x as usize, y as usize)) + .map(|(x, y)| (x as usize, y as usize)) .unwrap_or((0, 0)) } @@ -335,18 +356,17 @@ fn create_event_listener() -> Result<(Poll, Signals)> { let signals = Signals::new([signal_hook::SIGWINCH]) .chain_err(|| "Failed to initialize event listener signal")?; let event_listener = Poll::new().chain_err(|| "Failed to establish polling")?; - event_listener.register( - &EventedFd(&stdin().as_raw_fd()), - STDIN_INPUT, - Ready::readable(), - PollOpt::level() - ).chain_err(|| "Failed to register stdin to event listener")?; - event_listener.register( - &signals, - RESIZE, - Ready::readable(), - PollOpt::level() - ).chain_err(|| "Failed to register resize signal to event listener")?; + event_listener + .register( + &EventedFd(&stdin().as_raw_fd()), + STDIN_INPUT, + Ready::readable(), + PollOpt::level(), + ) + .chain_err(|| "Failed to register stdin to event listener")?; + event_listener + .register(&signals, RESIZE, Ready::readable(), PollOpt::level()) + .chain_err(|| "Failed to register resize signal to event listener")?; Ok((event_listener, signals)) } diff --git a/src/view/terminal/test_terminal.rs b/src/view/terminal/test_terminal.rs index 3d45b33d..3556fed8 100644 --- a/src/view/terminal/test_terminal.rs +++ b/src/view/terminal/test_terminal.rs @@ -1,10 +1,10 @@ +use super::Terminal; use crate::errors::*; use crate::input::Key; use crate::models::application::Event; +use crate::view::{Colors, CursorType, Style}; use scribe::buffer::Position; use std::sync::Mutex; -use super::Terminal; -use crate::view::{Colors, CursorType, Style}; const WIDTH: usize = 10; const HEIGHT: usize = 10; @@ -14,7 +14,7 @@ const HEIGHT: usize = 10; pub struct TestTerminal { data: Mutex<[[Option<(char, Colors)>; WIDTH]; HEIGHT]>, // 2D array of chars to represent screen cursor: Mutex>, - key_sent: Mutex + key_sent: Mutex, } impl TestTerminal { @@ -22,7 +22,7 @@ impl TestTerminal { TestTerminal { data: Mutex::new([[None; WIDTH]; HEIGHT]), cursor: Mutex::new(None), - key_sent: Mutex::new(false) + key_sent: Mutex::new(false), } } @@ -52,7 +52,7 @@ impl TestTerminal { // want to print a space in between every character, we // set it ahead when we've run into a character to // differentiate from leading spaces. - last_column_with_data = x+1; + last_column_with_data = x + 1; } } } @@ -80,27 +80,35 @@ impl Terminal for TestTerminal { *row = [None; WIDTH]; } } - fn present(&self) { } - fn width(&self) -> usize { WIDTH } - fn height(&self) -> usize { HEIGHT } + fn present(&self) {} + fn width(&self) -> usize { + WIDTH + } + fn height(&self) -> usize { + HEIGHT + } fn set_cursor(&self, position: Option) { let mut cursor = self.cursor.lock().unwrap(); *cursor = position; } - fn set_cursor_type(&self, _: CursorType) { } - fn suspend(&self) { } + fn set_cursor_type(&self, _: CursorType) {} + fn suspend(&self) {} fn print(&self, position: &Position, _: Style, colors: Colors, content: &str) -> Result<()> { // Ignore lines beyond visible height. - if position.line >= self.height() { return Ok(()); } + if position.line >= self.height() { + return Ok(()); + } let mut data = self.data.lock().unwrap(); let string_content = format!("{}", content); for (i, c) in string_content.chars().enumerate() { // Ignore characters beyond visible width. - if i+position.offset >= WIDTH { break; } + if i + position.offset >= WIDTH { + break; + } - data[position.line][i+position.offset] = Some((c, colors)); + data[position.line][i + position.offset] = Some((c, colors)); } Ok(()) @@ -109,15 +117,22 @@ impl Terminal for TestTerminal { #[cfg(test)] mod tests { - use crate::view::terminal::Terminal; use super::TestTerminal; + use crate::view::terminal::Terminal; use crate::view::{Colors, Style}; use scribe::buffer::Position; #[test] fn print_sets_terminal_data_correctly() { let terminal = Box::new(TestTerminal::new()); - terminal.print(&Position{ line: 0, offset: 0 }, Style::Default, Colors::Default, &"data").unwrap(); + terminal + .print( + &Position { line: 0, offset: 0 }, + Style::Default, + Colors::Default, + &"data", + ) + .unwrap(); assert_eq!(terminal.content(), "data"); } @@ -127,8 +142,22 @@ mod tests { let terminal = Box::new(TestTerminal::new()); // Setting a non-zero x coordinate on a previous line exercises column resetting. - terminal.print(&Position{ line: 0, offset: 2 }, Style::Default, Colors::Default, &"some").unwrap(); - terminal.print(&Position{ line: 2, offset: 5 }, Style::Default, Colors::Default, &"data").unwrap(); + terminal + .print( + &Position { line: 0, offset: 2 }, + Style::Default, + Colors::Default, + &"some", + ) + .unwrap(); + terminal + .print( + &Position { line: 2, offset: 5 }, + Style::Default, + Colors::Default, + &"data", + ) + .unwrap(); assert_eq!(terminal.content(), " some\n\n data"); } diff --git a/src/view/theme_loader.rs b/src/view/theme_loader.rs index 8135ca23..5bd0fd55 100644 --- a/src/view/theme_loader.rs +++ b/src/view/theme_loader.rs @@ -8,16 +8,15 @@ use syntect::highlighting::{Theme, ThemeSet}; pub struct ThemeLoader { path: PathBuf, - themes: BTreeMap + themes: BTreeMap, } impl ThemeLoader { pub fn new(path: PathBuf) -> ThemeLoader { - ThemeLoader{ + ThemeLoader { path, - themes: BTreeMap::new() + themes: BTreeMap::new(), } - } /// Consumes the ThemeLoader to produce a ThemeSet. @@ -25,12 +24,16 @@ impl ThemeLoader { self.load_defaults()?; self.load_user()?; - Ok(ThemeSet { themes: self.themes }) + Ok(ThemeSet { + themes: self.themes, + }) } fn load_user(&mut self) -> Result<()> { - let theme_dir_entries = - self.path.read_dir().chain_err(|| "Failed to read themes directory")?; + let theme_dir_entries = self + .path + .read_dir() + .chain_err(|| "Failed to read themes directory")?; let theme_paths = theme_dir_entries .filter_map(|dir| dir.ok()) @@ -54,15 +57,11 @@ impl ThemeLoader { fn load_defaults(&mut self) -> Result<()> { self.insert_theme( "solarized_dark", - Cursor::new( - include_str!("../themes/solarized_dark.tmTheme") - ) + Cursor::new(include_str!("../themes/solarized_dark.tmTheme")), )?; self.insert_theme( "solarized_light", - Cursor::new( - include_str!("../themes/solarized_light.tmTheme") - ) + Cursor::new(include_str!("../themes/solarized_light.tmTheme")), )?; Ok(())