ehhhh this kinda works

This commit is contained in:
mars 2023-04-11 15:56:43 -04:00
parent cc858ac593
commit b03a5b47ea
3 changed files with 542 additions and 4 deletions

275
Cargo.lock generated Normal file
View File

@ -0,0 +1,275 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "breed"
version = "0.1.0"
dependencies = [
"crossterm",
"ropey",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossterm"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
dependencies = [
"winapi",
]
[[package]]
name = "libc"
version = "0.2.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "mio"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "ropey"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53ce7a2c43a32e50d666e33c5a80251b31147bb4b49024bcab11fb6f20c671ed"
dependencies = [
"smallvec",
"str_indices",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "signal-hook"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "str_indices"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f026164926842ec52deb1938fae44f83dfdb82d0a5b0270c5bd5935ab74d6dd"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

View File

@ -3,6 +3,6 @@ name = "breed"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crossterm = "0.26"
ropey = "1.6"

View File

@ -1,3 +1,266 @@
fn main() {
println!("Hello, world!");
use std::io::{stdin, stdout, Read, Write};
use crossterm::{cursor, event, terminal, ExecutableCommand, Result};
use event::{Event, KeyCode, KeyEvent};
use ropey::Rope;
struct Buffer {
pub text: Rope,
}
impl Buffer {
pub fn from_str(text: &str) -> Self {
Self {
text: Rope::from_str(text),
}
}
pub fn draw(&self, out: &mut (impl ExecutableCommand + Write)) -> Result<u32> {
let (cols, rows) = terminal::size()?;
let lr_width = self.text.len_lines().ilog10() + 1;
out.execute(cursor::MoveTo(0, 0))?;
for (row, line) in (0..rows).zip(self.text.lines()) {
write!(out, "{:width$} ", row, width = lr_width as usize)?;
write!(out, "{}", line.as_str().unwrap_or("").trim_end())?;
out.execute(cursor::MoveToNextLine(1))?;
}
Ok(lr_width + 1)
}
pub fn clamped_cursor(&self, cursor: Cursor) -> Cursor {
Cursor {
line: cursor.line,
column: cursor
.column
.min(self.text.line(cursor.line).len_chars() - 1),
}
}
pub fn cursor_to_char(&self, cursor: Cursor) -> usize {
let cursor = self.clamped_cursor(cursor);
self.text.line_to_char(cursor.line) + cursor.column
}
pub fn move_cursor(&self, cursor: &mut Cursor, direction: Direction, enable_linewrap: bool) {
*cursor = self.clamped_cursor(*cursor);
match direction {
Direction::Left => {
if cursor.column > 0 {
cursor.column -= 1;
} else if enable_linewrap && cursor.line > 0 {
cursor.line -= 1;
let line = self.text.line(cursor.line);
cursor.column = line.len_chars() - 1;
}
}
Direction::Down => {
if cursor.line < self.text.len_lines() {
cursor.line += 1;
}
}
Direction::Up => {
if cursor.line > 0 {
cursor.line -= 1;
}
}
Direction::Right => {
let line = self.text.line(cursor.line);
if cursor.column + 2 > line.len_chars() {
if enable_linewrap && cursor.line < self.text.len_lines() {
cursor.line += 1;
cursor.column = 0;
}
} else {
cursor.column += 1;
}
}
}
}
}
#[derive(Copy, Clone, Debug, Default)]
struct Cursor {
pub column: usize,
pub line: usize,
}
#[derive(Copy, Clone, Debug, Default)]
enum Mode {
#[default]
Normal,
Command,
Visual,
Insert,
}
impl Mode {
pub fn cursor_style(&self) -> cursor::SetCursorStyle {
use cursor::SetCursorStyle as Style;
match self {
Mode::Normal => Style::SteadyBlock,
Mode::Visual => Style::BlinkingBlock,
Mode::Insert => Style::BlinkingBar,
Mode::Command => Style::SteadyUnderScore,
}
}
}
#[derive(Copy, Clone, Debug)]
enum Direction {
Left,
Down,
Up,
Right,
}
struct State {
pub buffer: Buffer,
pub cursor: Cursor,
pub mode: Mode,
pub quit: bool,
}
impl State {
pub fn from_str(text: &str) -> Self {
Self {
buffer: Buffer::from_str(text),
cursor: Cursor::default(),
mode: Mode::default(),
quit: false,
}
}
pub fn draw(&self, out: &mut impl Write) -> Result<()> {
out.execute(terminal::BeginSynchronizedUpdate)?;
out.execute(terminal::Clear(terminal::ClearType::All))?;
let lr_width = self.buffer.draw(out)?;
let cursor = self.buffer.clamped_cursor(self.cursor);
let (col, row) = (cursor.column as u16, cursor.line as u16);
let col = col + lr_width as u16;
out.execute(cursor::MoveTo(col, row))?;
out.execute(self.mode.cursor_style())?;
out.execute(terminal::EndSynchronizedUpdate)?;
Ok(())
}
pub fn on_event(&mut self, event: Event) {
match self.mode {
Mode::Normal => self.on_normal_event(event),
Mode::Command => self.on_command_event(event),
Mode::Visual => self.on_visual_event(event),
Mode::Insert => self.on_insert_event(event),
}
}
fn on_normal_event(&mut self, event: Event) {
match event {
Event::Key(KeyEvent { code, .. }) => match code {
KeyCode::Char('i') => {
self.mode = Mode::Insert;
}
KeyCode::Char(':') => {
self.mode = Mode::Command;
}
KeyCode::Char('v') => {
self.mode = Mode::Visual;
}
KeyCode::Esc => {
self.quit = true;
}
code => self.match_any_key(code),
},
_ => {}
}
}
fn on_command_event(&mut self, event: Event) {
match event {
Event::Key(KeyEvent { code, .. }) => match code {
KeyCode::Esc => {
self.mode = Mode::Normal;
}
code => self.match_any_key(code),
},
_ => {}
}
}
fn on_visual_event(&mut self, event: Event) {
match event {
Event::Key(KeyEvent { code, .. }) => match code {
KeyCode::Esc => {
self.mode = Mode::Normal;
}
code => self.match_any_key(code),
},
_ => {}
}
}
fn on_insert_event(&mut self, event: Event) {
match event {
Event::Key(KeyEvent { code, .. }) => match code {
KeyCode::Char(c) => {
let index = self.buffer.cursor_to_char(self.cursor);
self.buffer.text.insert_char(index, c);
self.move_cursor(Direction::Right)
}
KeyCode::Backspace => {
self.move_cursor(Direction::Left);
let index = self.buffer.cursor_to_char(self.cursor);
self.buffer.text.remove(index..=index);
}
KeyCode::Delete => {
let index = self.buffer.cursor_to_char(self.cursor);
self.buffer.text.remove(index..=index);
}
KeyCode::Enter => {
let index = self.buffer.cursor_to_char(self.cursor);
self.buffer.text.insert_char(index, '\n');
self.cursor.line += 1;
self.cursor.column = 0;
}
KeyCode::Esc => {
self.mode = Mode::Normal;
}
code => self.match_any_key(code),
},
_ => {}
}
}
fn match_any_key(&mut self, code: KeyCode) {
match code {
KeyCode::Esc => self.mode = Mode::Normal,
KeyCode::Char('h') | KeyCode::Left => self.move_cursor(Direction::Left),
KeyCode::Char('j') | KeyCode::Down => self.move_cursor(Direction::Down),
KeyCode::Char('k') | KeyCode::Up => self.move_cursor(Direction::Up),
KeyCode::Char('l') | KeyCode::Right => self.move_cursor(Direction::Right),
_ => {}
}
}
fn move_cursor(&mut self, direction: Direction) {
self.buffer.move_cursor(&mut self.cursor, direction, true);
}
}
fn main() -> Result<()> {
let text = include_str!("main.rs");
let mut state = State::from_str(text);
let mut stdout = stdout();
terminal::enable_raw_mode()?;
stdout.execute(terminal::EnterAlternateScreen)?;
while !state.quit {
state.draw(&mut stdout)?;
let event = event::read()?;
state.on_event(event);
}
stdout.execute(terminal::LeaveAlternateScreen)?;
Ok(())
}