Minor code cleanup, make get_cursor_position actually work correctly
This commit is contained in:
parent
f8700c8e93
commit
a94368c965
138
src/editor.rs
138
src/editor.rs
@ -287,11 +287,11 @@ impl Editor {
|
|||||||
'\r' => return Some(Enter),
|
'\r' => return Some(Enter),
|
||||||
ch => {
|
ch => {
|
||||||
if ch.is_ascii_control() {
|
if ch.is_ascii_control() {
|
||||||
return Some(Ctrl(ctrl_to_letter(ch)))
|
return Some(Ctrl(ctrl_to_letter(ch)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(OtherKey(ch))
|
return Some(OtherKey(ch));
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
None => return None,
|
None => return None,
|
||||||
}
|
}
|
||||||
@ -417,67 +417,11 @@ impl Editor {
|
|||||||
return Some(input[0]);
|
return Some(input[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cursor_position(&mut self) -> TermSize {
|
/// Get terminal size in rows and columns
|
||||||
let mut query = String::new();
|
|
||||||
// Move the cursor as far to the bottom right as is practical
|
|
||||||
query.push_str("\x1b[999C\x1b[999B");
|
|
||||||
|
|
||||||
// Ask the shell where the cursor is
|
|
||||||
query.push_str("\x1b[6n");
|
|
||||||
|
|
||||||
let stdout = io::stdout();
|
|
||||||
let mut handle = stdout.lock();
|
|
||||||
|
|
||||||
// If you can't write to stdout, you might as well just panic
|
|
||||||
handle.write_all(query.as_bytes()).unwrap();
|
|
||||||
|
|
||||||
let stdin = io::stdin();
|
|
||||||
let stdin = stdin.lock();
|
|
||||||
let mut handle = stdin.take(32);
|
|
||||||
let mut input = String::new();
|
|
||||||
let read_res = handle.read_to_string(&mut input);
|
|
||||||
clean_unwrap(read_res);
|
|
||||||
|
|
||||||
if input.len() < 6 {
|
|
||||||
panic!(
|
|
||||||
"Invalid or missing response to cursor location query: {:?}",
|
|
||||||
input
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut row_str = String::new();
|
|
||||||
let mut col_str = String::new();
|
|
||||||
|
|
||||||
let mut index = 0;
|
|
||||||
|
|
||||||
for ch in input.chars() {
|
|
||||||
if ch == ';' {
|
|
||||||
index += 1;
|
|
||||||
} else if ch == 'R' {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if index == 0 {
|
|
||||||
row_str.push(ch)
|
|
||||||
} else {
|
|
||||||
col_str.push(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rows = clean_unwrap(row_str.parse());
|
|
||||||
let cols = clean_unwrap(row_str.parse());
|
|
||||||
|
|
||||||
return TermSize { cols, rows };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_window_size(&mut self) -> TermSize {
|
fn get_window_size(&mut self) -> TermSize {
|
||||||
match get_term_size() {
|
match get_term_size() {
|
||||||
Some(size) => size,
|
Some(size) => size,
|
||||||
|
None => get_cursor_position(),
|
||||||
None => {
|
|
||||||
print!("\x1b[999C\x1b[999B");
|
|
||||||
return self.get_cursor_position();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -861,30 +805,27 @@ impl Editor {
|
|||||||
self.cursor_x = self.rows[self.cursor_y].chars.len();
|
self.cursor_x = self.rows[self.cursor_y].chars.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ctrl(c) => {
|
Ctrl(c) => match c {
|
||||||
match c {
|
'f' => self.find(),
|
||||||
'f' => self.find(),
|
's' => {
|
||||||
// 'h' => self._del_or_backspace(Backspace),
|
// Save success/error message handled by save method
|
||||||
's' => {
|
match self.save() {
|
||||||
// Save success/error message handled by save method
|
Ok(_) => (),
|
||||||
match self.save() {
|
Err(_) => (),
|
||||||
Ok(_) => (),
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
'q' => {
|
|
||||||
if self.dirty > 0 && self.quit_times > 0 {
|
|
||||||
self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times));
|
|
||||||
self.quit_times -= 1;
|
|
||||||
return Some(OtherKey('\0'));
|
|
||||||
}
|
|
||||||
print!("\x1b[2J");
|
|
||||||
print!("\x1b[H");
|
|
||||||
// Break out of the input loop
|
|
||||||
return None;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
'q' => {
|
||||||
|
if self.dirty > 0 && self.quit_times > 0 {
|
||||||
|
self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times));
|
||||||
|
self.quit_times -= 1;
|
||||||
|
return Some(OtherKey('\0'));
|
||||||
|
}
|
||||||
|
print!("\x1b[2J");
|
||||||
|
print!("\x1b[H");
|
||||||
|
// Break out of the input loop
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
Function(_) => (),
|
Function(_) => (),
|
||||||
OtherKey(c) => {
|
OtherKey(c) => {
|
||||||
@ -1150,6 +1091,7 @@ impl Editor {
|
|||||||
// Row Operations
|
// Row Operations
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Convert cursor x position to the rendered x position
|
||||||
fn row_cx_to_rx(&mut self, index: usize, cx: usize) -> usize {
|
fn row_cx_to_rx(&mut self, index: usize, cx: usize) -> usize {
|
||||||
let mut rx: usize = 0;
|
let mut rx: usize = 0;
|
||||||
|
|
||||||
@ -1168,6 +1110,7 @@ impl Editor {
|
|||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert rendered x position to cursor x position
|
||||||
fn row_rx_to_cx(&mut self, index: usize, rx: usize) -> usize {
|
fn row_rx_to_cx(&mut self, index: usize, rx: usize) -> usize {
|
||||||
let mut current_rx: usize = 0;
|
let mut current_rx: usize = 0;
|
||||||
let mut cx: usize = 0;
|
let mut cx: usize = 0;
|
||||||
@ -1569,7 +1512,7 @@ fn get_syntax_db() -> Vec<Syntax> {
|
|||||||
SyntaxFlags::HIGHLIGHT_NUMBERS | SyntaxFlags::HIGHLIGHT_STRINGS,
|
SyntaxFlags::HIGHLIGHT_NUMBERS | SyntaxFlags::HIGHLIGHT_STRINGS,
|
||||||
),
|
),
|
||||||
Syntax::new(
|
Syntax::new(
|
||||||
"JavaScript",
|
"JavaScript/TypeScript",
|
||||||
vec![".js", ".mjs", ".jsx", ".ts", ".tsx"],
|
vec![".js", ".mjs", ".jsx", ".ts", ".tsx"],
|
||||||
vec![
|
vec![
|
||||||
"instanceof",
|
"instanceof",
|
||||||
@ -1672,22 +1615,6 @@ fn highlight_range(vec: &mut Vec<Highlight>, range: Range<usize>, value: Highlig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Do the equivalent of a Result::unwrap, but cleanup terminal output
|
|
||||||
/// first, so it doesn't destroy console output afterwards.
|
|
||||||
fn clean_unwrap<O, E>(res: Result<O, E>) -> O
|
|
||||||
where
|
|
||||||
E: std::fmt::Debug,
|
|
||||||
{
|
|
||||||
match res {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(e) => {
|
|
||||||
print!("\x1bc");
|
|
||||||
disable_raw_mode();
|
|
||||||
panic!("{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1727,8 +1654,15 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ctrl_key_functions() {
|
fn ctrl_to_letter_() {
|
||||||
let a = ctrl_to_letter(ctrl_a);
|
let a = ctrl_to_letter('\x01');
|
||||||
assert_eq!(a, 'a', "ctrl_to_letter gives letter from ctrl chord");
|
assert_eq!(a, 'a', "ctrl_to_letter gives letter from ctrl chord");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn ctrl_to_letter_panic() {
|
||||||
|
// Del code doesn't map to Ctrl+letter combo
|
||||||
|
ctrl_to_letter('\x7f');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,10 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
let args: Vec<String> = env::args().collect();
|
// 'Access' the saved termios instance, to make sure it is set
|
||||||
|
// before you enable raw mode.
|
||||||
|
let mutex = Arc::clone(&ORIGINAL_TERMIOS);
|
||||||
|
let _ = mutex.lock().unwrap();
|
||||||
|
|
||||||
// Disable canonical/"cooked" terminal mode
|
// Disable canonical/"cooked" terminal mode
|
||||||
enable_raw_mode();
|
enable_raw_mode();
|
||||||
@ -32,6 +35,7 @@ fn main() -> Result<(), Error> {
|
|||||||
let mut editor = Editor::new();
|
let mut editor = Editor::new();
|
||||||
|
|
||||||
// Open the file if specified, from the command line
|
// Open the file if specified, from the command line
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.len() >= 2 {
|
if args.len() >= 2 {
|
||||||
editor.open(&args[1])?;
|
editor.open(&args[1])?;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ use nix::sys::termios;
|
|||||||
use nix::sys::termios::{
|
use nix::sys::termios::{
|
||||||
ControlFlags, InputFlags, LocalFlags, OutputFlags, SpecialCharacterIndices, Termios,
|
ControlFlags, InputFlags, LocalFlags, OutputFlags, SpecialCharacterIndices, Termios,
|
||||||
};
|
};
|
||||||
|
use std::io;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::io::BufReader;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -25,11 +28,6 @@ pub fn get_termios(fd: RawFd) -> Termios {
|
|||||||
|
|
||||||
/// Put terminal into raw mode so there is full control of terminal output
|
/// Put terminal into raw mode so there is full control of terminal output
|
||||||
pub fn enable_raw_mode() {
|
pub fn enable_raw_mode() {
|
||||||
// 'Access' the saved termios instance, to make sure it is set
|
|
||||||
// before you enable raw mode.
|
|
||||||
let mutex = Arc::clone(&super::ORIGINAL_TERMIOS);
|
|
||||||
mutex.lock().unwrap();
|
|
||||||
|
|
||||||
let mut raw = get_termios(STDOUT_FILENO);
|
let mut raw = get_termios(STDOUT_FILENO);
|
||||||
|
|
||||||
raw.input_flags.remove(
|
raw.input_flags.remove(
|
||||||
@ -99,3 +97,67 @@ pub fn get_term_size() -> Option<TermSize> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the terminal size, the hard way.
|
||||||
|
pub fn get_cursor_position() -> TermSize {
|
||||||
|
// Ask the shell where the cursor is
|
||||||
|
write!(io::stdout(), "\x1b[999C\x1b[999B").unwrap();
|
||||||
|
write!(io::stdout(), "\x1b[6n").unwrap();
|
||||||
|
|
||||||
|
// Explicitly flush, so that next input should be the terminal response
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
let mut buffer = vec![];
|
||||||
|
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let mut br = BufReader::new(stdin.lock());
|
||||||
|
br.read_until('R' as u8, &mut buffer).unwrap();
|
||||||
|
|
||||||
|
let input = String::from_utf8(buffer).unwrap();
|
||||||
|
|
||||||
|
// Parse the escape sequence into a location
|
||||||
|
// The escape sequence looks like so: Esc[y;xR
|
||||||
|
let mut row_str = String::new();
|
||||||
|
let mut col_str = String::new();
|
||||||
|
let mut index = 0;
|
||||||
|
for ch in input.chars() {
|
||||||
|
if ch == '\x1b' || ch == '[' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match ch {
|
||||||
|
';' => {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
'R' => break,
|
||||||
|
_ => {
|
||||||
|
if index == 0 {
|
||||||
|
row_str.push(ch)
|
||||||
|
} else {
|
||||||
|
col_str.push(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rows = clean_unwrap(row_str.parse());
|
||||||
|
let cols = clean_unwrap(col_str.parse());
|
||||||
|
|
||||||
|
return TermSize { cols, rows };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do the equivalent of a Result::unwrap, but cleanup terminal output
|
||||||
|
/// first, so it doesn't destroy console output afterwards.
|
||||||
|
pub fn clean_unwrap<O, E>(res: Result<O, E>) -> O
|
||||||
|
where
|
||||||
|
E: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
match res {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(e) => {
|
||||||
|
print!("\x1bc");
|
||||||
|
disable_raw_mode();
|
||||||
|
panic!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user