Fill the screen with tilde lines

This commit is contained in:
Timothy Warren 2019-08-23 16:46:04 -04:00
parent 40477ec7ca
commit 04369ca796
5 changed files with 131 additions and 12 deletions

1
Cargo.lock generated
View File

@ -36,6 +36,7 @@ dependencies = [
name = "rs-kilo"
version = "0.1.0"
dependencies = [
"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)",
]

View File

@ -7,5 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2"
# Rust wrappers for C/POSIX headers
nix = "0.15.0"

View File

@ -1,23 +1,28 @@
//! Editor functionality
use crate::helpers::*;
use nix::sys::termios::Termios;
use nix::unistd::read;
use std::io;
use std::io::prelude::*;
use std::io::{BufReader, Error, Stdin};
use std::str::Chars;
use std::io::BufReader;
/// Main structure for the editor
///
/// impl blocks are split similarly to the original C implementation
pub struct Editor {}
#[derive(Debug, Default)]
pub struct Editor {
screen_cols: u16,
screen_rows: u16,
}
// init
impl Editor {
pub fn new() -> Self {
Editor {}
let mut instance = Self::default();
let size = instance.get_window_size();
instance.screen_cols = size.cols;
instance.screen_rows = size.rows;
instance
}
}
@ -42,10 +47,41 @@ impl Editor {
return Some(output);
}
fn get_cursor_position(&mut self) -> TermSize {
let stdout = io::stdout();
let mut handle = stdout.lock();
let buffer = String::from("\x1b[6n").into_bytes();
handle.write(&buffer).unwrap();
let stdin = io::stdin();
let stdin = stdin.lock();
let mut in_buf = String::new().into_bytes();
let mut input = BufReader::with_capacity(32, stdin);
input.read_until('R' as u8, &mut in_buf).unwrap();
// @TODO Find a solution to retrieve the cursor coordinates
unimplemented!();
}
fn get_window_size(&mut self) -> TermSize {
match get_term_size() {
Some(size) => size,
None => {
let stdout = io::stdout();
let mut handle = stdout.lock();
let buffer = String::from("\x1b[999C\x1b[999B").into_bytes();
handle.write(&buffer).unwrap();
self.get_cursor_position()
}
}
}
}
// Input
impl Editor {
/// Route user input to the appropriate handler method
pub fn process_keypress(&mut self) -> Option<()> {
match self.read_key() {
// Just continue the input loop on an "empty" keypress
@ -54,6 +90,8 @@ impl Editor {
let first = chars[0];
if first == ctrl_key('q') {
clear_and_reset();
// Break out of the input loop
return None;
}
@ -69,11 +107,29 @@ impl Editor {
// Output
impl Editor {
pub fn refresh_screen() {
fn draw_rows(&mut self) {
let stdout = io::stdout();
let mut handle = stdout.lock();
let buffer = String::from("~\r\n").into_bytes();
for _ in 0..self.screen_rows as usize {
handle.write(&buffer).unwrap();
}
}
pub fn refresh_screen(&mut self) {
let stdout = io::stdout();
let mut handle = stdout.lock();
// Clear screen
let mut buffer = String::from("\x1b[2J").into_bytes();
handle.write_all(&mut buffer).unwrap();
// Reposition cursor
let mut buffer = String::from("\x1b[H").into_bytes();
handle.write_all(&mut buffer).unwrap();
self.draw_rows();
handle.write_all(&mut buffer).unwrap();
}

View File

@ -1,15 +1,37 @@
use libc::ioctl;
/// Helper functions, especially to reproduce C std/posix functions
use libc::{c_ushort, STDOUT_FILENO, TIOCGWINSZ};
use nix::errno::*;
use nix::sys::termios;
use nix::sys::termios::{
ControlFlags, InputFlags, LocalFlags, OutputFlags, SpecialCharacterIndices, Termios,
};
use nix::Error as NixError;
use std::io::Error;
use std::io;
use std::io::prelude::*;
use std::os::unix::io::RawFd;
use std::process::exit;
#[derive(Debug)]
pub struct TermSize {
/// number of rows
pub rows: u16,
/// number of columns
pub cols: u16,
}
#[repr(C)]
#[derive(Debug)]
struct UnixTermSize {
/// number of rows
pub rows: c_ushort,
/// number of columns
pub cols: c_ushort,
x: c_ushort,
y: c_ushort,
}
// Redefine the posix constants for rust land
/// The value of the raw file descriptor for STDIN
@ -33,6 +55,8 @@ pub fn ctrl_key(c: char) -> char {
}
pub fn die(code: &Errno, msg: &str) -> ! {
clear_and_reset();
eprintln!("{:?} ({})", code, msg);
exit(1)
@ -86,3 +110,36 @@ pub fn enable_raw_mode() {
pub fn disable_raw_mode(original: &Termios) {
termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, original).unwrap();
}
pub fn clear_and_reset() {
let stdout = io::stdout();
let mut handle = stdout.lock();
// Clear screen
let mut buffer = String::from("\x1b[2J").into_bytes();
handle.write_all(&mut buffer).unwrap();
// Reposition cursor
let mut buffer = String::from("\x1b[H").into_bytes();
handle.write_all(&mut buffer).unwrap();
}
pub fn get_term_size() -> Option<TermSize> {
let raw = UnixTermSize {
rows: 0,
cols: 0,
x: 0,
y: 0,
};
let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ.into(), &raw) };
if r == 0 {
Some(TermSize {
rows: raw.rows,
cols: raw.cols,
})
} else {
None
}
}

View File

@ -17,8 +17,12 @@ fn main() {
// Main input loop. Editor::process_keypress uses an Option Enum as a sentinel.
// `None` is returned on a quit action, in other cases, `Some(())` is returned,
// continuing the loop
while editor.process_keypress().is_some() {
// loop
loop {
editor.refresh_screen();
if editor.process_keypress().is_none() {
break;
}
}
// Restore previous terminal flags before exit