Ugly progress commit
This commit is contained in:
parent
389a526f41
commit
e6d3995e4c
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -15,6 +15,11 @@ name = "cfg-if"
|
|||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.62"
|
version = "0.2.62"
|
||||||
@ -37,6 +42,7 @@ name = "rs-kilo"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -50,6 +56,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||||
"checksum cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "b548a4ee81fccb95919d4e22cfea83c7693ebfd78f0495493178db20b3139da7"
|
"checksum cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "b548a4ee81fccb95919d4e22cfea83c7693ebfd78f0495493178db20b3139da7"
|
||||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||||
|
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||||
"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
|
"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
@ -8,6 +8,7 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.1.0"
|
bitflags = "1.1.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
# Rust wrappers for C/POSIX headers
|
# Rust wrappers for C/POSIX headers
|
||||||
nix = "0.15.0"
|
nix = "0.15.0"
|
||||||
|
@ -8,7 +8,8 @@ tutorial with a Rust implementation.
|
|||||||
uses `impl`s on a shared `Editor` struct, the prefix is redundant
|
uses `impl`s on a shared `Editor` struct, the prefix is redundant
|
||||||
* Any C equivalent functionality based on memory allocating/deallocating, or other manual book-keeping is instead
|
* Any C equivalent functionality based on memory allocating/deallocating, or other manual book-keeping is instead
|
||||||
implemented in a more idiomatic Rust fashion.
|
implemented in a more idiomatic Rust fashion.
|
||||||
|
* Row structs are referenced by their index in the Editor struct, rather than as a direct reference in method calls.
|
||||||
|
This generally simplifies dealing with the rules of Rust (borrow checker).
|
||||||
|
|
||||||
### Known issues:
|
### Known issues:
|
||||||
* The cursor is invisible :(
|
* The cursor is invisible :(
|
@ -1,5 +1,5 @@
|
|||||||
//! Editor functionality
|
//! Editor functionality
|
||||||
use crate::helpers::*;
|
use crate::terminal_helpers::*;
|
||||||
|
|
||||||
use std::cmp::PartialEq;
|
use std::cmp::PartialEq;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -208,7 +208,12 @@ impl Default for Editor {
|
|||||||
dirty: 0,
|
dirty: 0,
|
||||||
filename: String::new(),
|
filename: String::new(),
|
||||||
status_message: String::new(),
|
status_message: String::new(),
|
||||||
|
|
||||||
|
// This is the only reason I had to implement this method
|
||||||
|
// manually, instead of it being derived. Apparently an
|
||||||
|
// `Instant` struct has no default
|
||||||
status_message_time: Instant::now(),
|
status_message_time: Instant::now(),
|
||||||
|
|
||||||
syntax: None,
|
syntax: None,
|
||||||
|
|
||||||
output_buffer: String::new(),
|
output_buffer: String::new(),
|
||||||
@ -255,7 +260,8 @@ impl Editor {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if e.kind() != io::ErrorKind::UnexpectedEof {
|
if e.kind() != io::ErrorKind::UnexpectedEof {
|
||||||
panic!(e);
|
let error = format!("{:?}", e);
|
||||||
|
self.set_status_message(&error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,7 +295,8 @@ impl Editor {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if e.kind() != io::ErrorKind::UnexpectedEof {
|
if e.kind() != io::ErrorKind::UnexpectedEof {
|
||||||
panic!(e);
|
let error = format!("{:?}", e);
|
||||||
|
self.set_status_message(&error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,6 +410,10 @@ impl Editor {
|
|||||||
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,
|
||||||
|
|
||||||
|
// I could have implemented this, but I felt that parsing
|
||||||
|
// an escape code from stdin was of minimal value,
|
||||||
|
// when the ioctrl method works on any computer I've tried
|
||||||
None => unimplemented!("The easy way usually works"),
|
None => unimplemented!("The easy way usually works"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,19 +424,19 @@ impl Editor {
|
|||||||
|
|
||||||
fn update_syntax(&mut self, index: usize) {
|
fn update_syntax(&mut self, index: usize) {
|
||||||
let rows = &mut self.rows;
|
let rows = &mut self.rows;
|
||||||
|
|
||||||
let prev_row = if index > 0 {
|
let prev_row = if index > 0 {
|
||||||
// I shouldn't have to clone this, but the lifetime is
|
// I shouldn't have to clone this, but the lifetime is
|
||||||
// different than the `row` variable above, so it
|
// different than the `row` variable above, so it
|
||||||
// can't be a immutable borrow. It also can't be a
|
// can't be a immutable borrow. It also can't be a
|
||||||
// mutable borrow, because it would be considered a
|
// mutable borrow, because it would be considered a
|
||||||
// second mutable borrow
|
// second mutable borrow...so a clone it is
|
||||||
Some((&mut rows[index - 1]).clone())
|
Some((&mut rows[index - 1]).clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let row = &mut rows[index];
|
let row = &mut rows[index];
|
||||||
|
|
||||||
|
// Reset the highlighting of the row
|
||||||
row.highlight = vec![Highlight::Normal; row.render.len()];
|
row.highlight = vec![Highlight::Normal; row.render.len()];
|
||||||
|
|
||||||
if self.syntax.is_none() {
|
if self.syntax.is_none() {
|
||||||
@ -444,7 +455,7 @@ impl Editor {
|
|||||||
let mcs = ¤t_syntax.multiline_comment_start;
|
let mcs = ¤t_syntax.multiline_comment_start;
|
||||||
let mce = ¤t_syntax.multiline_comment_end;
|
let mce = ¤t_syntax.multiline_comment_end;
|
||||||
|
|
||||||
let mut prev_separator = false;
|
let mut prev_separator = true;
|
||||||
let mut in_string = false;
|
let mut in_string = false;
|
||||||
let mut str_start = '\0';
|
let mut str_start = '\0';
|
||||||
let mut in_comment = prev_row.map_or(false, |row| row.highlight_comment_start);
|
let mut in_comment = prev_row.map_or(false, |row| row.highlight_comment_start);
|
||||||
@ -574,7 +585,6 @@ impl Editor {
|
|||||||
if &row.render[search_range.clone()] == keyword && is_separator(next_char) {
|
if &row.render[search_range.clone()] == keyword && is_separator(next_char) {
|
||||||
highlight_range(&mut row.highlight, search_range, Highlight::Keyword1);
|
highlight_range(&mut row.highlight, search_range, Highlight::Keyword1);
|
||||||
i += keyword.len();
|
i += keyword.len();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,7 +602,6 @@ impl Editor {
|
|||||||
if &row.render[search_range.clone()] == keyword && is_separator(next_char) {
|
if &row.render[search_range.clone()] == keyword && is_separator(next_char) {
|
||||||
highlight_range(&mut row.highlight, search_range, Highlight::Keyword2);
|
highlight_range(&mut row.highlight, search_range, Highlight::Keyword2);
|
||||||
i += keyword.len();
|
i += keyword.len();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -927,16 +936,16 @@ impl Editor {
|
|||||||
// Center welcome message
|
// Center welcome message
|
||||||
let mut padding = (self.screen_cols - welcome.len()) / 2;
|
let mut padding = (self.screen_cols - welcome.len()) / 2;
|
||||||
if padding > 0 {
|
if padding > 0 {
|
||||||
self.append_out("~");
|
self.append_out_char('~');
|
||||||
padding -= 1;
|
padding -= 1;
|
||||||
}
|
}
|
||||||
for _ in 0..padding {
|
for _ in 0..padding {
|
||||||
self.append_out(" ");
|
self.append_out_char(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.append_out(&welcome);
|
self.append_out(&welcome);
|
||||||
} else {
|
} else {
|
||||||
self.append_out("~");
|
self.append_out_char('~');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let output = self.rows[file_row].render.clone();
|
let output = self.rows[file_row].render.clone();
|
||||||
@ -1045,8 +1054,8 @@ impl Editor {
|
|||||||
self.draw_message_bar();
|
self.draw_message_bar();
|
||||||
|
|
||||||
// Move cursor to state position
|
// Move cursor to state position
|
||||||
let y = (self.cursor_y - self.row_offset) + 1;
|
let y = self.cursor_y - self.row_offset + 1;
|
||||||
let x = (self.render_x - self.col_offset) + 1;
|
let x = self.render_x - self.col_offset + 1;
|
||||||
let cursor_code = format!("\x1b[{y};{x}H", y = y, x = x);
|
let cursor_code = format!("\x1b[{y};{x}H", y = y, x = x);
|
||||||
self.append_out(&cursor_code);
|
self.append_out(&cursor_code);
|
||||||
|
|
||||||
@ -1265,8 +1274,8 @@ impl Editor {
|
|||||||
for row in &self.rows {
|
for row in &self.rows {
|
||||||
// When the file is opened, newlines are stripped
|
// When the file is opened, newlines are stripped
|
||||||
// make sure to add them back when saving!
|
// make sure to add them back when saving!
|
||||||
let row_chars = row.chars.clone() + "\n";
|
output += &row.chars;
|
||||||
output.push_str(&row_chars)
|
output.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
output
|
||||||
@ -1281,7 +1290,7 @@ impl Editor {
|
|||||||
let file = File::open(&self.filename)?;
|
let file = File::open(&self.filename)?;
|
||||||
let buf_reader = BufReader::new(file);
|
let buf_reader = BufReader::new(file);
|
||||||
|
|
||||||
let lines = buf_reader.lines().map(|l| l.unwrap());
|
let lines = buf_reader.lines().map(|l| clean_unwrap(l));
|
||||||
|
|
||||||
for line in lines {
|
for line in lines {
|
||||||
self.insert_row(self.rows.len(), &line);
|
self.insert_row(self.rows.len(), &line);
|
||||||
@ -1299,6 +1308,7 @@ impl Editor {
|
|||||||
self.set_status_message("Save aborted");
|
self.set_status_message("Save aborted");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.select_syntax_highlight();
|
self.select_syntax_highlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1411,6 +1421,7 @@ impl Editor {
|
|||||||
// Functions
|
// Functions
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Get the language highlighting config
|
||||||
fn get_syntax_db() -> Vec<Syntax> {
|
fn get_syntax_db() -> Vec<Syntax> {
|
||||||
vec![
|
vec![
|
||||||
Syntax::new(
|
Syntax::new(
|
||||||
@ -1421,7 +1432,8 @@ fn get_syntax_db() -> Vec<Syntax> {
|
|||||||
"union", "class", "else", "enum", "for", "case", "if",
|
"union", "class", "else", "enum", "for", "case", "if",
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
"unsigned", "double", "signed", "float", "long", "char", "int", "void",
|
"#include", "unsigned", "#define", "#ifndef", "double", "signed", "#endif",
|
||||||
|
"#ifdef", "float", "#error", "#undef", "long", "char", "int", "void", "#if",
|
||||||
],
|
],
|
||||||
"//",
|
"//",
|
||||||
"/*",
|
"/*",
|
||||||
@ -1504,6 +1516,20 @@ fn get_syntax_db() -> Vec<Syntax> {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert Ctrl+letter chords to their
|
||||||
|
/// ASCII table equivalents
|
||||||
|
pub fn ctrl_key(c: char) -> char {
|
||||||
|
let key = c as u8;
|
||||||
|
|
||||||
|
if !c.is_ascii() {
|
||||||
|
panic!("CTRL_KEY only accepts ASCII characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intentionally "overflow" u8 to wrap around to the
|
||||||
|
// beginning of the ASCII table. Ctrl+a is 1, Ctrl+b is 2, etc.
|
||||||
|
(key & 0x1f) as char
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine whether a character is one which separates tokens
|
/// Determine whether a character is one which separates tokens
|
||||||
/// in the language to highlight
|
/// in the language to highlight
|
||||||
fn is_separator(input_char: char) -> bool {
|
fn is_separator(input_char: char) -> bool {
|
||||||
@ -1522,7 +1548,8 @@ fn is_separator(input_char: char) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a range for a slice of a string or vector.
|
/// Get a range for a slice of a string or vector, checking the length of the
|
||||||
|
/// string or vector to prevent panics on invalid access.
|
||||||
///
|
///
|
||||||
/// If `start` to `start + search_len`, is within the size of the search target (`haystack_len`)
|
/// If `start` to `start + search_len`, is within the size of the search target (`haystack_len`)
|
||||||
/// that range is returned. Otherwise, the range is from `start` to `haystack_len`.
|
/// that range is returned. Otherwise, the range is from `start` to `haystack_len`.
|
||||||
@ -1543,12 +1570,28 @@ 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) => {
|
||||||
|
disable_raw_mode();
|
||||||
|
println!("\r\n");
|
||||||
|
panic!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_syntax_hightlight_selects_language() {
|
fn select_syntax_highlight_selects_language() {
|
||||||
let langs = get_syntax_db();
|
let langs = get_syntax_db();
|
||||||
let mut editor = Editor::new();
|
let mut editor = Editor::new();
|
||||||
editor.filename = String::from("foo.c");
|
editor.filename = String::from("foo.c");
|
||||||
|
35
src/main.rs
35
src/main.rs
@ -1,22 +1,30 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
mod editor;
|
mod editor;
|
||||||
mod helpers;
|
mod terminal_helpers;
|
||||||
|
|
||||||
use crate::editor::Editor;
|
use crate::editor::Editor;
|
||||||
use crate::helpers::*;
|
use crate::terminal_helpers::*;
|
||||||
use libc::STDIN_FILENO;
|
use libc::STDIN_FILENO;
|
||||||
|
use nix::sys::termios::Termios;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
// Much ugliness to get and keep a reference to the original terminal settings
|
||||||
|
lazy_static! {
|
||||||
|
// Save terminal flags on start
|
||||||
|
pub static ref ORIGINAL_TERMIOS: Arc<Mutex<Termios>> = Arc::new(Mutex::new(get_termios(STDIN_FILENO)));
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
// Save original terminal flags
|
|
||||||
let original_termios = get_termios(STDIN_FILENO);
|
|
||||||
|
|
||||||
// Disable canonical/"cooked" terminal mode
|
// Disable canonical/"cooked" terminal mode
|
||||||
enable_raw_mode();
|
enable_raw_mode();
|
||||||
|
|
||||||
@ -36,19 +44,20 @@ fn main() -> Result<(), Error> {
|
|||||||
loop {
|
loop {
|
||||||
editor.refresh_screen();
|
editor.refresh_screen();
|
||||||
|
|
||||||
let key = editor.process_keypress();
|
match editor.process_keypress() {
|
||||||
if key.is_none() {
|
Some(_key) => {
|
||||||
break;
|
/* match key {
|
||||||
}
|
|
||||||
|
|
||||||
/*match key.unwrap() {
|
|
||||||
editor::EditorKey::OtherKey('\0') => (),
|
editor::EditorKey::OtherKey('\0') => (),
|
||||||
_ => println!("{:?}\r\n", key)
|
_ => println!("{:?}\r\n", key)
|
||||||
} */
|
}*/
|
||||||
|
()
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore previous terminal flags before exit
|
// Restore previous terminal flags before exit
|
||||||
disable_raw_mode(&original_termios);
|
disable_raw_mode();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! # Helpers
|
||||||
|
//!
|
||||||
|
//! Various functions calling C wrappers to get/set terminal functionality
|
||||||
use libc::ioctl;
|
use libc::ioctl;
|
||||||
use libc::{c_ushort, STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ};
|
use libc::{c_ushort, STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ};
|
||||||
use nix::sys::termios;
|
use nix::sys::termios;
|
||||||
@ -5,6 +8,7 @@ use nix::sys::termios::{
|
|||||||
ControlFlags, InputFlags, LocalFlags, OutputFlags, SpecialCharacterIndices, Termios,
|
ControlFlags, InputFlags, LocalFlags, OutputFlags, SpecialCharacterIndices, Termios,
|
||||||
};
|
};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TermSize {
|
pub struct TermSize {
|
||||||
@ -25,21 +29,6 @@ struct UnixTermSize {
|
|||||||
y: c_ushort,
|
y: c_ushort,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Ctrl+letter chords to their
|
|
||||||
/// ASCII table equivalents
|
|
||||||
#[inline]
|
|
||||||
pub fn ctrl_key(c: char) -> char {
|
|
||||||
let key = c as u8;
|
|
||||||
|
|
||||||
if !c.is_ascii() {
|
|
||||||
panic!("CTRL_KEY only accepts ASCII characters");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally "overflow" u8 to wrap around to the
|
|
||||||
// beginning of the ASCII table. Ctrl+a is 1, Ctrl+b is 2, etc.
|
|
||||||
(key & 0x1f) as char
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a `Termios` struct, for getting/setting terminal flags
|
/// Get a `Termios` struct, for getting/setting terminal flags
|
||||||
pub fn get_termios(fd: RawFd) -> Termios {
|
pub fn get_termios(fd: RawFd) -> Termios {
|
||||||
termios::tcgetattr(fd).unwrap()
|
termios::tcgetattr(fd).unwrap()
|
||||||
@ -73,8 +62,11 @@ pub fn enable_raw_mode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Restore terminal to "cooked"/canonical mode
|
/// Restore terminal to "cooked"/canonical mode
|
||||||
pub fn disable_raw_mode(original: &Termios) {
|
pub fn disable_raw_mode() {
|
||||||
termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, original).unwrap();
|
let mutex = Arc::clone(&super::ORIGINAL_TERMIOS);
|
||||||
|
let termios = mutex.lock().unwrap();
|
||||||
|
|
||||||
|
termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, &termios).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to get the size of the terminal (in rows and columns) from an `ioctl` call
|
/// Attempt to get the size of the terminal (in rows and columns) from an `ioctl` call
|
Loading…
Reference in New Issue
Block a user