Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persistent modes #281

Merged
merged 37 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1dfcdf7
Move application mode enum into modes submodule
jmacdonald Feb 23, 2024
638b00f
Introduce ModeKey and mode storage
jmacdonald Feb 23, 2024
7622395
Implement initial mode construction
jmacdonald Feb 23, 2024
6017c8c
Initialize command mode
jmacdonald Feb 26, 2024
b85de7a
Add confirm mode
jmacdonald Feb 26, 2024
4b90da5
Add jump mode
jmacdonald Feb 26, 2024
57b49b6
Add line jump mode
jmacdonald Feb 27, 2024
08a3b34
Add open mode
jmacdonald Mar 3, 2024
02abce5
Add path mode
jmacdonald Mar 3, 2024
15e8543
Add search mode
jmacdonald Mar 3, 2024
418754f
Add select mode
jmacdonald Mar 3, 2024
de3f111
Add select line mode
jmacdonald Mar 3, 2024
9bbbdd8
Add symbol jump mode
jmacdonald Mar 3, 2024
afb7a09
Add syntax mode
jmacdonald Mar 3, 2024
0ada533
Add theme mode
jmacdonald Mar 3, 2024
5934e33
Implement switch_to method for application type
jmacdonald Mar 3, 2024
ac4b42e
Make current_mode/modes private
jmacdonald Mar 3, 2024
c8e3d56
Update simple mode switch commands to use switch_to
jmacdonald Mar 3, 2024
ad46b60
Add the ability to switch to the previous mode
jmacdonald Mar 4, 2024
f18422b
Update switch_to_jump_mode
jmacdonald Mar 4, 2024
dfcca1b
Update switch_to_line_jump_mode
jmacdonald Mar 4, 2024
3b159a7
Update switch_to_open_mode
jmacdonald Mar 4, 2024
f439a10
Update switch_to_path_mode
jmacdonald Mar 4, 2024
017898e
Update switch_to_search_mode
jmacdonald Mar 5, 2024
dc86b10
Update switch_to_select_mode
jmacdonald Mar 5, 2024
01c7036
Update switch_to_select_line_mode
jmacdonald Mar 5, 2024
6961e69
Update switch_to_symbol_jump_mode
jmacdonald Mar 5, 2024
0eb3728
Update switch_to_syntax_mode
jmacdonald Mar 5, 2024
741814d
Update switch_to_theme_mode
jmacdonald Mar 5, 2024
4586cf9
Update switch_to_command_mode
jmacdonald Mar 5, 2024
c73ec14
Update switch_to to ignore requests for the current mode
jmacdonald Mar 5, 2024
391ee9b
Migrate manual mode changes to switch_to
jmacdonald Mar 6, 2024
e2b398d
Replace single-pattern match with if let
jmacdonald Apr 7, 2024
2d4b288
Use Default trait for PathMode constructor
jmacdonald Apr 7, 2024
9f699d0
Use reset pattern for all modes
jmacdonald Apr 8, 2024
9201aca
Use default pattern for theme mode reset
jmacdonald Apr 10, 2024
0d82d9a
Fix search mode reset behaviour
jmacdonald Jul 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 93 additions & 87 deletions src/commands/application.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::commands::{self, Result};
use crate::errors::*;
use crate::input::KeyMap;
use crate::models::application::modes::*;
use crate::models::application::{Application, Mode};
use crate::models::application::{Application, Mode, ModeKey};
use crate::util;
use scribe::Buffer;
use std::mem;

pub fn handle_input(app: &mut Application) -> Result {
// Listen for and respond to user input.
Expand All @@ -26,15 +24,15 @@ pub fn handle_input(app: &mut Application) -> Result {

pub fn switch_to_normal_mode(app: &mut Application) -> Result {
let _ = commands::buffer::end_command_group(app);
app.mode = Mode::Normal;
app.switch_to(ModeKey::Normal);

Ok(())
}

pub fn switch_to_insert_mode(app: &mut Application) -> Result {
if app.workspace.current_buffer.is_some() {
commands::buffer::start_command_group(app)?;
app.mode = Mode::Insert;
app.switch_to(ModeKey::Insert);
commands::view::scroll_to_cursor(app)?;
} else {
bail!(BUFFER_MISSING);
Expand All @@ -44,34 +42,18 @@ pub fn switch_to_insert_mode(app: &mut Application) -> Result {
}

pub fn switch_to_jump_mode(app: &mut Application) -> Result {
let buffer = app
let line = app
.workspace
.current_buffer
.as_ref()
.ok_or(BUFFER_MISSING)?;

// Initialize a new jump mode and swap
// it with the current application mode.
let jump_mode = Mode::Jump(JumpMode::new(buffer.cursor.line));
let old_mode = mem::replace(&mut app.mode, jump_mode);

// If we were previously in a select mode, store it
// in the current jump mode so that we can return to
// it after we've jumped to a location. This is how
// we compose select and jump modes.
match old_mode {
Mode::Select(select_mode) => {
if let Mode::Jump(ref mut mode) = app.mode {
mode.select_mode = jump::SelectModeOptions::Select(select_mode);
}
}
Mode::SelectLine(select_mode) => {
if let Mode::Jump(ref mut mode) = app.mode {
mode.select_mode = jump::SelectModeOptions::SelectLine(select_mode);
}
}
_ => (),
};
.ok_or(BUFFER_MISSING)?
.cursor
.line;

app.switch_to(ModeKey::Jump);
if let Mode::Jump(ref mut mode) = app.mode {
mode.reset(line)
}

Ok(())
}
Expand All @@ -81,15 +63,18 @@ pub fn switch_to_second_stage_jump_mode(app: &mut Application) -> Result {
if let Mode::Jump(ref mut mode) = app.mode {
mode.first_phase = false;
} else {
bail!("Failed to switch to jump mode.");
bail!("Cannot enter second stage jump mode from other modes.");
};

Ok(())
}

pub fn switch_to_line_jump_mode(app: &mut Application) -> Result {
if app.workspace.current_buffer.is_some() {
app.mode = Mode::LineJump(LineJumpMode::new());
app.switch_to(ModeKey::LineJump);
if let Mode::LineJump(ref mut mode) = app.mode {
mode.reset();
}
} else {
bail!(BUFFER_MISSING);
}
Expand All @@ -100,78 +85,110 @@ 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.switch_to(ModeKey::Open);
if let Mode::Open(ref mut mode) = app.mode {
mode.reset(
app.workspace.path.clone(),
exclusions,
app.event_channel.clone(),
config,
);
}

commands::search_select::search(app)?;

Ok(())
}

pub fn switch_to_command_mode(app: &mut Application) -> Result {
let config = app.preferences.borrow().search_select_config();
app.mode = Mode::Command(CommandMode::new(config));

app.switch_to(ModeKey::Command);
if let Mode::Command(ref mut mode) = app.mode {
mode.reset(config)
}

commands::search_select::search(app)?;

Ok(())
}

pub fn switch_to_symbol_jump_mode(app: &mut Application) -> Result {
app.switch_to(ModeKey::SymbolJump);

let token_set = app
.workspace
.current_buffer_tokens()
.chain_err(|| BUFFER_TOKENS_FAILED)?;
let config = app.preferences.borrow().search_select_config();

app.mode = Mode::SymbolJump(SymbolJumpMode::new(&token_set, config)?);
match app.mode {
Mode::SymbolJump(ref mut mode) => mode.reset(&token_set, config),
_ => Ok(()),
}?;

commands::search_select::search(app)?;

Ok(())
}

pub fn switch_to_theme_mode(app: &mut Application) -> Result {
let themes = app
.view
.theme_set
.themes
.keys()
.map(|k| k.to_string())
.collect();
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.switch_to(ModeKey::Theme);
if let Mode::Theme(ref mut mode) = app.mode {
mode.reset(themes, config)
}

commands::search_select::search(app)?;

Ok(())
}

pub fn switch_to_select_mode(app: &mut Application) -> Result {
if let Some(buffer) = app.workspace.current_buffer.as_ref() {
app.mode = Mode::Select(SelectMode::new(*buffer.cursor.clone()));
} else {
bail!(BUFFER_MISSING);
let position = *app
.workspace
.current_buffer
.as_ref()
.ok_or(BUFFER_MISSING)?
.cursor;

app.switch_to(ModeKey::Select);
if let Mode::Select(ref mut mode) = app.mode {
mode.reset(position);
}

Ok(())
}

pub fn switch_to_select_line_mode(app: &mut Application) -> Result {
if let Some(buffer) = app.workspace.current_buffer.as_ref() {
app.mode = Mode::SelectLine(SelectLineMode::new(buffer.cursor.line));
} else {
bail!(BUFFER_MISSING);
let line = app
.workspace
.current_buffer
.as_ref()
.ok_or(BUFFER_MISSING)?
.cursor
.line;

app.switch_to(ModeKey::SelectLine);
if let Mode::SelectLine(ref mut mode) = app.mode {
mode.reset(line);
}

Ok(())
}

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.switch_to(ModeKey::Search);
} else {
bail!(BUFFER_MISSING);
}
Expand All @@ -193,7 +210,11 @@ pub fn switch_to_path_mode(app: &mut Application) -> Result {
.unwrap_or_else(||
// Default to the workspace directory.
format!("{}/", app.workspace.path.to_string_lossy()));
app.mode = Mode::Path(PathMode::new(path));

app.switch_to(ModeKey::Path);
if let Mode::Path(ref mut mode) = app.mode {
mode.reset(path)
}

Ok(())
}
Expand All @@ -207,16 +228,19 @@ pub fn switch_to_syntax_mode(app: &mut Application) -> Result {
.as_ref()
.ok_or("Switching syntaxes requires an open buffer")?;

app.switch_to(ModeKey::Syntax);
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,
));
let syntaxes = app
.workspace
.syntax_set
.syntaxes()
.iter()
.map(|syntax| syntax.name.clone())
.collect();
if let Mode::Syntax(ref mut mode) = app.mode {
mode.reset(syntaxes, config)
}

commands::search_select::search(app)?;

Ok(())
Expand Down Expand Up @@ -282,7 +306,7 @@ pub fn suspend(app: &mut Application) -> Result {
}

pub fn exit(app: &mut Application) -> Result {
app.mode = Mode::Exit;
app.switch_to(ModeKey::Exit);

Ok(())
}
Expand Down Expand Up @@ -316,24 +340,6 @@ mod tests {
assert_eq!(lines.last(), Some("workspace::next_buffer"));
}

#[test]
fn switch_to_search_mode_sets_initial_search_query() {
let mut app = Application::new(&Vec::new()).unwrap();

// A buffer needs to be open to switch to search mode.
let buffer = Buffer::new();
app.workspace.add_buffer(buffer);

app.search_query = Some(String::from("query"));
super::switch_to_search_mode(&mut app).unwrap();

let mode_query = match app.mode {
Mode::Search(ref mode) => mode.input.clone(),
_ => None,
};
assert_eq!(mode_query, Some(String::from("query")));
}

#[test]
fn switch_to_path_mode_inserts_workspace_directory_as_default() {
let mut app = Application::new(&Vec::new()).unwrap();
Expand Down
16 changes: 10 additions & 6 deletions src/commands/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::commands::{self, Result};
use crate::errors::*;
use crate::input::Key;
use crate::models::application::modes::ConfirmMode;
use crate::models::application::{Application, ClipboardContent, Mode};
use crate::models::application::{Application, ClipboardContent, Mode, ModeKey};
use crate::util;
use crate::util::token::{adjacent_token_position, Direction};
use scribe::buffer::{Buffer, Position, Range, Token};
Expand Down Expand Up @@ -204,8 +203,10 @@ pub fn close(app: &mut Application) -> Result {
app.workspace.close_current_buffer();
} else {
// Display a confirmation prompt before closing a modified buffer.
let confirm_mode = ConfirmMode::new(close);
app.mode = Mode::Confirm(confirm_mode);
app.switch_to(ModeKey::Confirm);
if let Mode::Confirm(ref mut mode) = app.mode {
mode.command = close
}
}

Ok(())
Expand Down Expand Up @@ -249,8 +250,11 @@ pub fn close_others(app: &mut Application) -> Result {

if modified_buffer {
// Display a confirmation prompt before closing a modified buffer.
let confirm_mode = ConfirmMode::new(close_others_confirm);
app.mode = Mode::Confirm(confirm_mode);
app.switch_to(ModeKey::Confirm);
if let Mode::Confirm(ref mut mode) = app.mode {
mode.command = close_others_confirm
}

break;
}

Expand Down
22 changes: 1 addition & 21 deletions src/commands/jump.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::commands::Result;
use crate::errors::*;
use crate::input::Key;
use crate::models::application::modes::jump;
use crate::models::application::modes::JumpMode;
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 {
Expand All @@ -23,7 +21,7 @@ pub fn match_tag(app: &mut Application) -> Result {
} else {
bail!("Can't match jump tags outside of jump mode.");
};
switch_to_previous_mode(app);
app.switch_to_previous_mode();

result
}
Expand All @@ -45,24 +43,6 @@ fn jump_to_tag(jump_mode: &mut JumpMode, workspace: &mut Workspace) -> Result {
Ok(())
}

fn switch_to_previous_mode(app: &mut Application) {
let old_mode = mem::replace(&mut app.mode, Mode::Normal);

// Now that we own the jump mode, switch to
// the previous select mode, if there was one.
if let Mode::Jump(jump_mode) = old_mode {
match jump_mode.select_mode {
jump::SelectModeOptions::None => (),
jump::SelectModeOptions::Select(select_mode) => {
app.mode = Mode::Select(select_mode);
}
jump::SelectModeOptions::SelectLine(select_mode) => {
app.mode = Mode::SelectLine(select_mode);
}
}
}
}

pub fn push_search_char(app: &mut Application) -> Result {
if let Some(ref key) = *app.view.last_key() {
if let Mode::Jump(ref mut mode) = app.mode {
Expand Down
Loading