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

Sublime-like changes #109

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d202ae3
Update Cargo.lock
msirringhaus Jun 24, 2019
83692fd
First step towards configurable keybindings: TUI can use bindings pro…
msirringhaus Jun 24, 2019
4f9d809
Copy keybindings to editor and all views. Ugly, but I cannot find a n…
msirringhaus Jun 24, 2019
2d2763e
Reshaped Command to have relative and absolute move as variant instea…
msirringhaus Jun 24, 2019
a4e9f93
Editor now holds the keymap alone and translates it for the view. All…
msirringhaus Jun 25, 2019
386f22b
Rename functions to avoid key-name <-> functionality confusion
msirringhaus Jun 25, 2019
dfd0781
Merge branch 'keybindings'
msirringhaus Jun 25, 2019
0699d1c
Prepare to switch to rust edition 2018
msirringhaus Jun 25, 2019
0bc9269
Rust 2018: run cargo fix --edition-idioms
msirringhaus Jun 25, 2019
117299f
Remove duplicate functions in editor, view and client. Now each objec…
msirringhaus Jun 26, 2019
702b140
Make Insert a command as well and let the client handle it
msirringhaus Jun 26, 2019
39995fd
Add command to hide prompt
msirringhaus Jun 26, 2019
1c11099
Add undo/redo (unfortunately, there is a bug in termion that does not…
msirringhaus Jun 26, 2019
db5488b
Add multi-cursor support! Find current selection and add cursors on e…
msirringhaus Jun 26, 2019
1424a88
Implement copy&paste
msirringhaus Jun 27, 2019
2b38760
Implement (almost) all move commands
msirringhaus Jun 27, 2019
decb28f
Embedd sublime keybindings for now. This is stupid but easier for now…
msirringhaus Jun 27, 2019
c276d9b
Restructure Command a bit so that there is a single parsing function …
msirringhaus Jun 27, 2019
b6472f1
Make open-command work with tilde and env-variables.
msirringhaus Jun 27, 2019
fbd71c2
Implement close current view (last view always remains).
msirringhaus Jun 27, 2019
574c9a9
Implement select_all command
msirringhaus Jun 28, 2019
15ce710
Unify and simplify prompt parsing a bit
msirringhaus Jun 28, 2019
495083c
Add (terminal-specific) keys for next/prev buffer.
msirringhaus Jun 28, 2019
e31365d
First somewhat functioning implementation of Find()
msirringhaus Jun 28, 2019
62a601f
Implement configurable search
msirringhaus Jun 28, 2019
6c32c20
Add explanation of how search works above the search prompt.
msirringhaus Jun 28, 2019
0e77c3d
Activate move_to linenumber as prompt command
msirringhaus Jun 28, 2019
ab35a26
Implement function to set multiple cursor with clicking middle mouse …
msirringhaus Jun 28, 2019
f19eb17
First, hacky and incomplete draft of commandprompt with suggestions
msirringhaus Jun 28, 2019
8bfcfa1
Generate nicer names for prompt suggestions. This is really ugly and …
msirringhaus Jun 28, 2019
694b3b1
Move clipboard to Editor. Had a clipboard for each view, which meant …
msirringhaus Jun 29, 2019
88c8b35
Major rework of command structure. Known bug: Keybindings of subcomma…
msirringhaus Jun 30, 2019
3790457
Remove old, left-over comment
msirringhaus Jul 2, 2019
b1f795c
Adjust logging levels to be more sensible.
msirringhaus Jul 2, 2019
e94e3e9
Make enum-names rusty and use serdes rename_all to use it for parsing.
msirringhaus Jul 3, 2019
7e1aea1
Prompt: Fix prompt parsing and close prompt after finalizing.
msirringhaus Jul 3, 2019
75dff11
Fix prompt parsing of select_lines
msirringhaus Jul 3, 2019
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
6 changes: 3 additions & 3 deletions configs/Default (Linux).sublime-keymap
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
{ "keys": ["ctrl+u"], "command": "soft_undo" },
{ "keys": ["ctrl+shift+u"], "command": "soft_redo" },

{ "keys": ["shift+delete"], "command": "cut" },
{ "keys": ["ctrl+insert"], "command": "copy" },
{ "keys": ["shift+insert"], "command": "paste" },
//{ "keys": ["shift+delete"], "command": "cut" },
//{ "keys": ["ctrl+insert"], "command": "copy" },
//{ "keys": ["shift+insert"], "command": "paste" },

// These two key bindings should replace the above three if you'd prefer
// the traditional X11 behavior of shift+insert pasting from the primary
Expand Down
17 changes: 14 additions & 3 deletions src/core/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ pub struct AbsoluteMove {
pub extend: bool
}

#[allow(non_camel_case_types)]
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
pub struct ExpandLinesDirection {
pub forward: bool
}

#[derive(Debug, PartialEq, Clone)]
pub enum Command {
/// Close the CommandPrompt.
Expand Down Expand Up @@ -89,10 +95,14 @@ pub enum Command {
OpenPrompt,
/// Insert a character
Insert(char),
// Undo last action
/// Undo last action
Undo,
// Redo last undone action
Redo
/// Redo last undone action
Redo,
/// Find word and set another cursor there
FindUnderExpand,
/// Set a new cursor below or above current position
CursorExpandLines(ExpandLinesDirection),
}

#[derive(Debug)]
Expand Down Expand Up @@ -120,6 +130,7 @@ impl FromStr for Command {

fn from_str(s: &str) -> Result<Command, Self::Err> {
match &s[..] {
"fue" | "find_under_expand" => Ok(Command::FindUnderExpand),
"hide_overlay" => Ok(Command::Cancel),
"s" | "save" => Ok(Command::Save(None)),
"q" | "quit" | "exit" => Ok(Command::Quit),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should refrain from adding more command aliases. We could discuss modifying the existing ones, but I think it will confusing to have too many commands that do the same thing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I never planned to do a full PR.
I switched to command names used by sublime texts keymap-file, but didn't want to remove the old names just yet.

Expand Down
25 changes: 20 additions & 5 deletions src/core/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::core::{Command, RelativeMove, AbsoluteMove};
use crate::core::{Command, RelativeMove, AbsoluteMove, ExpandLinesDirection};
use termion::event::{Event, Key};

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -48,6 +48,11 @@ impl KeybindingConfig {
let cmd : AbsoluteMove = serde_json::from_value(args)?;
Command::AbsoluteMove(cmd)
},
"select_lines" => {
let args = binding.args.ok_or("select_lines binding incomplete! Missing \"args\"")?;
let cmd : ExpandLinesDirection = serde_json::from_value(args)?;
Command::CursorExpandLines(cmd)
}
x => match Command::from_str(x) {
Ok(cmd) => cmd,
// unimplemented command for now
Expand All @@ -62,7 +67,7 @@ impl KeybindingConfig {
keymap.insert(binding, cmd.clone());
found_cmds.push(cmd);
} else {
warn!("Skipping failed binding");
// warn!("Skipping failed binding");
continue;
}
}
Expand Down Expand Up @@ -91,12 +96,22 @@ impl KeybindingConfig {
"delete" => Some(Event::Key(Key::Delete)),
"insert" => Some(Event::Key(Key::Insert)),
"escape" => Some(Event::Key(Key::Esc)),
"ctrl+right" => Some(Event::Unsupported(vec![27, 91, 49, 59, 53, 67])),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused: why are these named "ctrl+something" instead of an actual command name like "word-left" or "word-right"? Other question: what are ctrl+shift+up and ctrl+shift+down supposed to do? It seems that it's not doing anything visible at the moment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused: why are these named "ctrl+something" instead of an actual command name like "word-left" or "word-right"?

It looks different on HEAD. There its separated. a bit clearer.

But in principle there are two parsers: One is to parse a command name to Command-struct (e.g. select_all).

The other is to parse key-descriptions to Termion::Event, e.g. escape to Key::Esc.
Now a user can edit their JSON file to let pressing escape trigger select_all for example.

ctrl+right and others are not supported by Termion right now, as they differ from Terminal to Terminal, which...sucks. But to have the functionality, I basically used the Unsupported event to make it work with my terminal.
So this is already a "known bug" of this implementation. Another is, that Termion doesn't differentiate between ctrl+a and ctrl+shift+a. I have opened a bugreport there, but I fear its not really actively maintained anymore.

Other question: what are ctrl+shift+up and ctrl+shift+down supposed to do? It seems that it's not doing anything visible at the moment.

Try typing after doing that :-) It adds a cursor below or above the current cursor, which are unfortunately invisible at the moment. I have some screencasts showing of the features, just have to find a place to upload to

Copy link
Author

@msirringhaus msirringhaus Jul 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry. I confused ctrl+shift+up/down with alt+shift+up/down.
ctrl+shift+up/down is supposed to move the current line one line up or down (swap lines). But this is currently not supported by xi-core ( #793 ).

Either way, this part is not there to define functionality! Its there so that users can use ctrl+shift+up/down for whatever they want (They could map the function of alt+shift+up/down to ctrl+shift+up/down. Or they could for whatever reason map ctrl+shift+up to select_all and ctrl+shift+down to next buffer.).
This part of the code is only there to "know a key-combination exists". Not connect it to any functionality.

But for completeness, here are the screencasts I talked about (just "view raw" for each file):
https://gist.github.com/msirringhaus/83fbc1ef10ae93e56fe8d68dea1dd3b3

"switchcase" is what alt+shift+up/down is doing at the moment.
ctrlD obviously is what ctrl+d does.
And Palette is the CommandPrompt with suggestions. There is a crate to have sublime-like fuzzy-search, which I want to implement next. Then finally, the Prompt should handle up/down movements to select suggestions (at the moment it only shows them)

"ctrl+left" => Some(Event::Unsupported(vec![27, 91, 49, 59, 53, 68])),
"ctrl+shift+right" => Some(Event::Unsupported(vec![27, 91, 49, 59, 54, 67])),
"ctrl+shift+left" => Some(Event::Unsupported(vec![27, 91, 49, 59, 54, 68])),
"ctrl+shift+up" => Some(Event::Unsupported(vec![27, 91, 49, 59, 54, 65])),
"ctrl+shift+down" => Some(Event::Unsupported(vec![27, 91, 49, 59, 54, 66])),
"alt+shift+up" => Some(Event::Unsupported(vec![27, 91, 49, 59, 52, 65])),
"alt+shift+down" => Some(Event::Unsupported(vec![27, 91, 49, 59, 52, 66])),
// Not yet released
// "shift+tab" => Some(Event::Key(Key::Backtab)),

x if x.starts_with("f") => {
match x[1..].parse::<u8>() {
Ok(val) => Some(Event::Key(Key::F(val))),
Err(_) => {
warn!("Cannot parse {}", x);
// warn!("Cannot parse {}", x);
None
}
}
Expand All @@ -109,7 +124,7 @@ impl KeybindingConfig {
let character;
// start_length + "shift+x".len() || start_length + "x".len()
if x.len() != start_length + 7 && x.len() != start_length + 1 {
warn!("Cannot parse {}. Length is = {}, which is neither {} nor {} ", x, x.len(), start_length + 1, start_length + 7);
// warn!("Cannot parse {}. Length is = {}, which is neither {} nor {} ", x, x.len(), start_length + 1, start_length + 7);
return None
} else {
if x.len() == start_length + 7 {
Expand All @@ -128,7 +143,7 @@ impl KeybindingConfig {
}

x => {
warn!("Completely unknown argument {}", x);
// warn!("Completely unknown argument {}", x);
None
},
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod tui;
pub use self::tui::{CoreEvent, Tui, TuiService, TuiServiceBuilder};

mod cmd;
pub use self::cmd::{Command, ParseCommandError, RelativeMove, AbsoluteMove, RelativeMoveDistance, AbsoluteMovePoint};
pub use self::cmd::{Command, ParseCommandError, RelativeMove, AbsoluteMove, RelativeMoveDistance, AbsoluteMovePoint, ExpandLinesDirection};

mod config;
pub use self::config::{KeybindingConfig, Keymap};
2 changes: 1 addition & 1 deletion src/core/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl Tui {
}
return; },
Command::Quit => { self.exit = true; return; },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting is a bit off here, although it does not matter too much since we can just rustfmt later.

Command::Cancel => { self.prompt = None; return; },
Command::Cancel if !self.prompt.is_none() => { self.prompt = None; return; },
_ => {/* Somebody else has to deal with these commands */},
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ extern crate clap;
#[macro_use]
extern crate log;

#[macro_use]
extern crate serde_json;

use log4rs;
use tokio;
use xrl;
Expand Down
12 changes: 5 additions & 7 deletions src/widgets/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,20 @@ impl Editor {
pub fn handle_input(&mut self, event: Event) {
// We have to remove and insert again, to beat the borrow-checker
msirringhaus marked this conversation as resolved.
Show resolved Hide resolved
match event {
Event::Key(key) => {
match self.keybindings.keymap.get(&event).cloned() {
Event::Mouse(mouse_event) => self.views.get_mut(&self.current_view).unwrap().handle_mouse_event(mouse_event),
ev => {
match self.keybindings.keymap.get(&ev).cloned() {
Some(cmd) => self.handle_command(cmd),
msirringhaus marked this conversation as resolved.
Show resolved Hide resolved
None => {
if let Some(view) = self.views.get_mut(&self.current_view) {
match key {
Key::Char(c) => view.handle_command(Command::Insert(c)),
match ev {
Event::Key(Key::Char(c)) => view.handle_command(Command::Insert(c)),
k => error!("un-handled key {:?}", k)
}
}
}
}
},
Event::Mouse(mouse_event) => self.views.get_mut(&self.current_view).unwrap().handle_mouse_event(mouse_event),
ev => error!("un-handled event {:?}", ev),
}
}

Expand All @@ -148,7 +147,6 @@ impl Editor {
Command::PrevBuffer => self.prev_buffer(),
Command::Save(view_id) => self.save(view_id),
Command::Open(file) => self.new_view(file),

view_command => {
if let Some(view) = self.views.get_mut(&self.current_view) {
view.handle_command(view_command)
Expand Down
81 changes: 73 additions & 8 deletions src/widgets/view/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use futures::Future;
use tokio::spawn;
use xrl;
use serde_json::Value;

use crate::core::{Command, RelativeMoveDistance, AbsoluteMovePoint};

Expand Down Expand Up @@ -28,20 +29,29 @@ impl Client {
Command::Save(_view_id) => { /* Handled by Editor */ },
Command::Open(_file) => { /* Handled by Editor */ },
Command::ToggleLineNumbers => { /* Handled by View */ },
Command::FindUnderExpand => { /* Handled by View */ },
Command::Back => self.back(),
Command::Delete => self.delete(),
Command::Insert('\n') => self.insert_newline(),
Command::Insert('\t') => self.insert_tab(),
Command::Insert(c) => self.insert(c),
Command::Undo => self.undo(),
Command::Redo => self.redo(),
Command::CursorExpandLines(dir) => self.cursor_expand_line(dir.forward),
Command::RelativeMove(x) => {
match x.by {
RelativeMoveDistance::characters => {
if x.forward {
self.right()
self.right(x.extend)
} else {
self.left()
self.left(x.extend)
}
},
RelativeMoveDistance::words | RelativeMoveDistance::word_ends => {
if x.forward {
self.word_right(x.extend)
} else {
self.word_left(x.extend)
}
},
RelativeMoveDistance::pages => {
Expand Down Expand Up @@ -71,6 +81,19 @@ impl Client {
}
}

pub fn find_under_expand_next(&mut self) {
let f = self.inner
.find_next(self.view_id, true, false, xrl::ModifySelection::Add)
.map_err(|_| ());
spawn(f);
}

pub fn find_under_expand(&mut self) {
let f = self.inner.edit_notify(self.view_id, "selection_for_find", Some(json!({"case_sensitive": true})))
.map_err(|_| ());
spawn(f);
}

pub fn undo(&mut self) {
let f = self.inner.undo(self.view_id).map_err(|_| ());
spawn(f);
Expand Down Expand Up @@ -111,14 +134,44 @@ impl Client {
spawn(f);
}

pub fn right(&mut self) {
let f = self.inner.right(self.view_id).map_err(|_| ());
spawn(f);
pub fn right(&mut self, extend: bool) {
if extend {
let f = self.inner.right_sel(self.view_id).map_err(|_| ());
spawn(f);
} else {
let f = self.inner.right(self.view_id).map_err(|_| ());
spawn(f);
}
}

pub fn left(&mut self) {
let f = self.inner.left(self.view_id).map_err(|_| ());
spawn(f);
pub fn left(&mut self, extend: bool) {
if extend {
let f = self.inner.left_sel(self.view_id).map_err(|_| ());
spawn(f);
} else {
let f = self.inner.left(self.view_id).map_err(|_| ());
spawn(f);
}
}

pub fn word_right(&mut self, extend: bool) {
if extend {
let f = self.inner.move_word_right_sel(self.view_id).map_err(|_| ());
spawn(f);
} else {
let f = self.inner.move_word_right(self.view_id).map_err(|_| ());
spawn(f);
}
}

pub fn word_left(&mut self, extend: bool) {
if extend {
let f = self.inner.move_word_left_sel(self.view_id).map_err(|_| ());
spawn(f);
} else {
let f = self.inner.move_word_left(self.view_id).map_err(|_| ());
spawn(f);
}
}

pub fn page_down(&mut self) {
Expand Down Expand Up @@ -168,4 +221,16 @@ impl Client {
let f = self.inner.drag(self.view_id, line, column).map_err(|_| ());
spawn(f);
}

pub fn collapse_selections(&mut self) {
let f = self.inner.collapse_selections(self.view_id).map_err(|_| ());
spawn(f);
}

pub fn cursor_expand_line(&mut self, forward: bool) {
let command = if forward { "add_selection_below" } else { "add_selection_above" };
let f = self.inner.edit_notify(self.view_id, command, None as Option<Value>)
.map_err(|_| ());
spawn(f);
}
}
14 changes: 14 additions & 0 deletions src/widgets/view/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub struct View {
file: Option<String>,
client: Client,
cfg: ViewConfig,

search_in_progress: bool
}

impl View {
Expand All @@ -39,6 +41,7 @@ impl View {
cfg: ViewConfig::default(),
client,
file,
search_in_progress: false
}
}

Expand Down Expand Up @@ -145,9 +148,20 @@ impl View {
self.client.drag(line, column);
}

fn find_under_expand(&mut self) {
if self.search_in_progress {
self.client.find_under_expand_next()
} else {
self.search_in_progress = true;
self.client.find_under_expand()
}
}

pub fn handle_command(&mut self, cmd: Command) {
match cmd {
Command::ToggleLineNumbers => self.toggle_line_numbers(),
Command::FindUnderExpand => self.find_under_expand(),
Command::Cancel => { self.search_in_progress = false; self.client.collapse_selections() },
client_command => self.client.handle_command(client_command),
}
}
Expand Down