diff --git a/Cargo.lock b/Cargo.lock index c7f6218..efb940b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,12 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-trait" version = "0.1.77" @@ -501,6 +507,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_deref" version = "1.1.1" @@ -1177,6 +1194,7 @@ dependencies = [ "log", "pretty_assertions", "ratatui", + "ratatui-explorer", "serde", "serde_json", "signal-hook", @@ -1188,6 +1206,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-subscriber", + "tui-term", ] [[package]] @@ -1344,6 +1363,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "ratatui-explorer" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93dbe2398d7478db2f2ce5cd7ba1566792effda5361fadedd706b3ce325b511" +dependencies = [ + "crossterm", + "derivative", + "ratatui", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -1883,6 +1913,16 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tui-term" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3a054e8a92d4b51f9c28e04712e18ef3721ea25552577e944ee9870cb7445b" +dependencies = [ + "ratatui", + "vt100", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1949,12 +1989,25 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vt100" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de" +dependencies = [ + "itoa", + "log", + "unicode-width", + "vte", +] + [[package]] name = "vte" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ + "arrayvec", "utf8parse", "vte_generate_state_changes", ] diff --git a/crates/papier/.config/config.toml b/crates/papier/.config/config.toml index 6c212d2..927aa89 100644 --- a/crates/papier/.config/config.toml +++ b/crates/papier/.config/config.toml @@ -1,3 +1,4 @@ [keybindings.normal] "ctrl-l" = { action = "Custom", payload = "NextBuffer" } "ctrl-h" = { action = "Custom", payload = "PreviousBuffer" } +"ctrl-e" = { action = "Custom", payload = "ToggleExplorer" } diff --git a/crates/papier/Cargo.toml b/crates/papier/Cargo.toml index ecd488f..c18d9b9 100644 --- a/crates/papier/Cargo.toml +++ b/crates/papier/Cargo.toml @@ -47,3 +47,5 @@ toml = "0.8.10" tracing = "0.1.40" tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde"] } +ratatui-explorer = "0.1.1" +tui-term = "0.1.8" diff --git a/crates/papier/src/components/editor.rs b/crates/papier/src/components/editor.rs index 45ac6bd..1c2e489 100644 --- a/crates/papier/src/components/editor.rs +++ b/crates/papier/src/components/editor.rs @@ -5,13 +5,15 @@ use crate::{ PapierAction, }; use color_eyre::eyre::{eyre, Result}; -use crossterm::event::{KeyCode, KeyEvent}; +use config::File; +use crossterm::event::{Event, KeyCode, KeyEvent}; use edtui::{ actions::Execute, state::command::Command, view::EditorMessage, EditorMode, EditorState, EditorTheme, EditorView, Index2, Input, Lines, StatusLine, }; use log::debug; use ratatui::{prelude::*, style::palette::tailwind::PURPLE, widgets::*}; +use ratatui_explorer::{FileExplorer, Input as ExplorerInput, Theme as FileTheme}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -81,6 +83,12 @@ pub struct Buffer { state: EditorState, input: Input, message: Option, + explorer: FileExplorer, + explorer_state: FileExplorerState, +} + +struct FileExplorerState { + open: bool, } impl Buffer { @@ -170,6 +178,8 @@ Don't hesitate to open issues or submit pull requests to contribute! input, message: None, name: name.or_else(|| path.map(|p| p.file_name().unwrap().to_string_lossy().to_string())), + explorer: FileExplorer::with_theme(FileTheme::default().add_default_title())?, + explorer_state: FileExplorerState { open: false }, }) } @@ -225,6 +235,26 @@ impl Component for Editor { let current_buffer = self.current_buffer().unwrap(); let input = &mut current_buffer.input; let state = &mut current_buffer.state; + let explorer = &mut current_buffer.explorer; + let explorer_state = &mut current_buffer.explorer_state; + + if explorer_state.open { + if key.code == KeyCode::Esc { + explorer_state.open = false; + return Ok(None); + } + explorer.handle(&Event::Key(key))?; + + if !explorer.current().is_dir() && (key.code == KeyCode::Enter || key.code == KeyCode::Char('l')) { + let path = explorer.current().path().to_path_buf(); + let buffer = Buffer::new(Some(path), self.config.keybindings.clone(), None, None)?; + self.buffers.push(buffer); + self.current_buffer = Some(self.buffers.len() - 1); + return Ok(None); + } + return Ok(None); + } + let maybe_custom = input.on_key(key, state); if let Some(custom) = maybe_custom { @@ -278,6 +308,9 @@ impl Component for Editor { self.current_buffer = Some(self.buffers.len() - 1); }, PapierAction::QuitAll => return Ok(Some(Action::Quit)), + PapierAction::ToggleExplorer => { + current_buffer.explorer_state.open = !current_buffer.explorer_state.open; + }, } }; @@ -285,12 +318,23 @@ impl Component for Editor { } fn draw(&mut self, f: &mut Frame<'_>, area: Rect) -> Result<()> { - let [top, bottom] = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]).areas(area); - let buffer_index = self.current_buffer.unwrap(); let buffer_count = self.buffers.len(); let current_buffer = self.current_buffer().unwrap(); let state = &mut current_buffer.state; + + let area = area.inner(&Margin { horizontal: 1, vertical: 1 }); + let [explorer, editor] = Layout::horizontal([ + Constraint::Length(if current_buffer.explorer_state.open { 20 } else { 0 }), + Constraint::Min(0), + ]) + .areas(area); + let [top, bottom] = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]).areas(editor); + + if current_buffer.explorer_state.open { + f.render_widget(¤t_buffer.explorer.widget(), explorer); + } + let theme = EditorTheme::default() .base(EditorTheme::default().base.bold().bg(Color::Reset)) .selection_style(Style::default().bg(Color::LightMagenta).fg(Color::Reset)) diff --git a/crates/papier/src/main.rs b/crates/papier/src/main.rs index e53aae2..4affbb5 100644 --- a/crates/papier/src/main.rs +++ b/crates/papier/src/main.rs @@ -32,6 +32,7 @@ pub enum PapierAction { PreviousBuffer, NextBuffer, Open(String), + ToggleExplorer, } impl Execute for PapierAction {