forked from mars/breed
ehhhh this kinda works
This commit is contained in:
parent
cc858ac593
commit
b03a5b47ea
275
Cargo.lock
generated
Normal file
275
Cargo.lock
generated
Normal 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"
|
@ -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"
|
||||
|
267
src/main.rs
267
src/main.rs
@ -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(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user