-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
shepherdma
committed
Nov 29, 2023
1 parent
eddebac
commit 7ca4398
Showing
8 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Compiled files | ||
/target/ | ||
|
||
# These directories store cache and intermediate files | ||
**/*.rs.bk | ||
|
||
# IDE - IntelliJ | ||
.idea/ | ||
|
||
# Dependency directory | ||
/.cargo/ | ||
|
||
# Rustup directory | ||
/.rustup/ | ||
|
||
# Environment variable file | ||
.env | ||
|
||
# Ignore .DS_Store files (Mac OS) | ||
.DS_Store |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "hecto" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
termion = "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use crate::Row; | ||
|
||
#[derive(Default)] | ||
pub struct Document { | ||
rows: Vec<Row>, | ||
} | ||
|
||
impl Document { | ||
pub fn open() -> Self{ | ||
let mut rows = Vec::new(); | ||
rows.push(Row::from("Hello, World!")); | ||
Self { rows } | ||
} | ||
pub fn row(&self, index: usize) -> Option<&Row> { | ||
self.rows.get(index) | ||
} | ||
pub fn is_empty(&self) -> bool { | ||
self.rows.is_empty() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use crate::Row; | ||
use crate::Document; | ||
use crate::Terminal; | ||
use termion::event::Key; | ||
|
||
const VERSION: &str = env!("CARGO_PKG_VERSION"); | ||
#[derive(Default)] | ||
pub struct Position { | ||
pub x: usize, | ||
pub y: usize, | ||
} | ||
pub struct Editor { | ||
should_quit: bool, | ||
terminal: Terminal, | ||
cursor_position: Position, | ||
document: Document, | ||
} | ||
fn die(e: &std::io::Error) { | ||
Terminal::clear_screen(); | ||
panic!("{}", e) | ||
} | ||
|
||
impl Editor { | ||
pub fn run(&mut self) { | ||
loop { | ||
if let Err(error) = self.refresh_screen() { | ||
die(&error); | ||
} | ||
if self.should_quit { | ||
println!("Goodbye.\r"); | ||
break; | ||
} | ||
if let Err(error) = self.process_keypress() { | ||
die(&error); | ||
} | ||
} | ||
} | ||
pub fn default() -> Self{ | ||
Self{ | ||
should_quit: false, | ||
terminal: Terminal::default().expect("Failed to initialize terminal"), | ||
cursor_position: Position::default(), | ||
document: Document::open(), | ||
} | ||
} | ||
fn process_keypress(&mut self) -> Result<(), std::io::Error> { | ||
let pressed_key = Terminal::read_key()?; | ||
match pressed_key { | ||
Key::Ctrl('q') => self.should_quit = true, | ||
Key::Up | Key::Down | Key::Left | Key::Right => self.move_cursor(pressed_key), | ||
_ => (), | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn move_cursor(&mut self, key: Key) { | ||
let Position { mut y, mut x} = self.cursor_position; | ||
match key { | ||
Key::Up => y = y.saturating_sub(1), | ||
Key::Down => y = y.saturating_add(1), | ||
Key::Right => x = x.saturating_add(1), | ||
Key::Left => x = x.saturating_sub(1), | ||
_ => (), | ||
} | ||
self.cursor_position = Position { x, y } | ||
} | ||
|
||
fn refresh_screen(&self) -> Result<(), std::io::Error> { | ||
// print!("\x1b[2J"); | ||
Terminal::cursor_hide(); | ||
Terminal::clear_screen(); | ||
Terminal::cursor_position(&Position::default()); | ||
if self.should_quit { | ||
println!("Goodbye.\r"); | ||
} else { | ||
self.draw_rows(); | ||
Terminal::cursor_position(&self.cursor_position); | ||
} | ||
Terminal::cursor_show(); | ||
Terminal::flush() | ||
} | ||
fn draw_rows(&self) { | ||
let height = self.terminal.size().height; | ||
for terminal_row in 0..height - 1 { | ||
Terminal::clear_current_line(); | ||
if let Some(row) = self.document.row(terminal_row as usize) { | ||
self.draw_row(row) | ||
} else if self.document.is_empty() && terminal_row == height / 3 { | ||
self.draw_welcome_message(); | ||
} else { | ||
println!("~\r"); | ||
} | ||
} | ||
} | ||
fn draw_welcome_message(&self) { | ||
let mut welcome_message = format!("Hecto editor --version{}", VERSION); | ||
let width = self.terminal.size().width as usize; | ||
let len = welcome_message.len(); | ||
let padding = width.saturating_sub(len) / 2; | ||
let spaces = " ".repeat(padding.saturating_sub(1)); | ||
welcome_message = format!("~{}{}", spaces, welcome_message); | ||
welcome_message.truncate(width); | ||
println!("{}\r", welcome_message); | ||
} | ||
pub fn draw_row(&self, row: &Row) { | ||
let start = 0; | ||
let end = self.terminal.size().width as usize; | ||
let row = row.render(start, end); | ||
println!("{}\r", row); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#![warn(clippy::all, clippy::pedantic)] | ||
mod row; | ||
mod editor; | ||
mod document; | ||
mod terminal; | ||
|
||
use editor::Editor; | ||
pub use terminal::Terminal; | ||
pub use document::Document; | ||
pub use row::Row; | ||
|
||
|
||
fn main() { | ||
println!("Welcome to Hecto Text editor"); | ||
let mut editor = Editor::default(); | ||
editor.run(); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use std::cmp; | ||
pub struct Row { | ||
string: String, | ||
} | ||
|
||
impl From<&str> for Row { | ||
fn from(slice: &str) -> Self { | ||
Self { | ||
string: String::from(slice), | ||
} | ||
} | ||
} | ||
|
||
impl Row { | ||
pub fn render(&self, start: usize, end: usize) -> String { | ||
let end = cmp::min(end, self.string.len()); | ||
let start = cmp::min(start, end); | ||
self.string.get(start..end).unwrap_or_default().to_string() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use crate::editor::Position; | ||
use std::io::{self, stdout, Write}; | ||
use termion::event::Key; | ||
use termion::input::TermRead; | ||
use termion::raw::{IntoRawMode, RawTerminal}; | ||
|
||
pub struct Size { | ||
pub width: u16, | ||
pub height: u16, | ||
} | ||
|
||
pub struct Terminal { | ||
size: Size, | ||
_stdout: RawTerminal<std::io::Stdout>, | ||
} | ||
|
||
impl Terminal { | ||
pub fn default() -> Result<Self, std::io::Error> { | ||
let size = termion::terminal_size()?; | ||
Ok(Self { | ||
size: Size { | ||
width: size.0, | ||
height: size.1, | ||
}, | ||
_stdout: stdout().into_raw_mode()?, | ||
}) | ||
} | ||
pub fn size (&self) -> &Size { | ||
&self.size | ||
} | ||
pub fn clear_screen() { | ||
print!("{}", termion::clear::All); | ||
} | ||
pub fn cursor_position(position: &Position) { | ||
let Position{mut x, mut y} = position; | ||
x = x.saturating_add(1); | ||
y = y.saturating_add(1); | ||
let x = x as u16; | ||
let y = y as u16; | ||
print!("{}", termion::cursor::Goto(x, y)); | ||
} | ||
pub fn read_key() -> Result<Key, std::io::Error> { | ||
loop { | ||
if let Some(key) = io::stdin().lock().keys().next() { | ||
return key; | ||
} | ||
} | ||
} | ||
pub fn flush() -> Result<(), std::io::Error> { | ||
io::stdout().flush() | ||
} | ||
pub fn cursor_hide() { | ||
print!("{}", termion::cursor::Hide); | ||
} | ||
pub fn cursor_show() { | ||
print!("{}", termion::cursor::Show); | ||
} | ||
pub fn clear_current_line() { | ||
print!("{}", termion::clear::CurrentLine); | ||
} | ||
} | ||
|