Skip to content

Commit

Permalink
Introduce a TTYDimensions type, and display buffered input in bottom …
Browse files Browse the repository at this point in the history
…right corner.
  • Loading branch information
PaulJuliusMartinez committed Aug 20, 2021
1 parent eb7e116 commit 2d11dee
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 64 deletions.
53 changes: 33 additions & 20 deletions src/jless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ use crate::flatjson;
use crate::input::TuiEvent;
use crate::input::TuiEvent::{KeyEvent, WinChEvent};
use crate::screenwriter::{AnsiTTYWriter, ScreenWriter};
use crate::types::TTYDimensions;
use crate::viewer::{Action, JsonViewer, Mode};

pub struct JLess {
viewer: JsonViewer,
screen_writer: ScreenWriter,

input_buffer: String,
input_buffer: Vec<u8>,
}

const MAX_BUFFER_SIZE: usize = 8;
const BELL: &'static str = "\x07";

pub fn new(json: String, stdout: Box<dyn Write>) -> Result<JLess, String> {
Expand All @@ -33,20 +34,22 @@ pub fn new(json: String, stdout: Box<dyn Write>) -> Result<JLess, String> {
let screen_writer = ScreenWriter {
tty_writer,
command_editor: Editor::<()>::new(),
dimensions: TTYDimensions::default(),
};

Ok(JLess {
viewer,
screen_writer,
input_buffer: String::new(),
input_buffer: vec![],
})
}

impl JLess {
pub fn run(&mut self, input: Box<dyn Iterator<Item = io::Result<TuiEvent>>>) {
let (width, height) = termion::terminal_size().unwrap();
self.viewer.set_window_dimensions(height, width);
self.screen_writer.print_screen(&self.viewer);
let dimensions = TTYDimensions::from_size(termion::terminal_size().unwrap());
self.viewer.dimensions = dimensions.without_status_bar();
self.screen_writer.dimensions = dimensions;
self.screen_writer.print(&self.viewer, &self.input_buffer);

for event in input {
let event = event.unwrap();
Expand All @@ -55,7 +58,7 @@ impl JLess {
KeyEvent(Key::Ctrl('c')) | KeyEvent(Key::Char('q')) => break,
// These inputs may be buffered.
KeyEvent(Key::Char(ch @ '0'..='9')) => {
self.input_buffer.push(ch);
self.buffer_input(ch as u8);
None
}
KeyEvent(Key::Char('z')) => self.handle_z_input(),
Expand Down Expand Up @@ -85,15 +88,15 @@ impl JLess {
}
// These may interpret the input buffer some other way
Key::Char('t') => {
if self.input_buffer == "z" {
if self.input_buffer == "z".as_bytes() {
// Action::MoveFocusedLineToTop
None
} else {
None
}
}
Key::Char('b') => {
if self.input_buffer == "z" {
if self.input_buffer == "z".as_bytes() {
// Action::MoveFocusedLineToBottom
None
} else {
Expand All @@ -105,6 +108,7 @@ impl JLess {
Key::Left | Key::Char('h') => Some(Action::MoveLeft),
Key::Right | Key::Char('l') => Some(Action::MoveRight),
Key::Char('i') => Some(Action::ToggleCollapsed),
// TODO: These should also accept buffered counts
Key::Char('K') => Some(Action::FocusPrevSibling),
Key::Char('J') => Some(Action::FocusNextSibling),
Key::Char('^') => Some(Action::FocusFirstSibling),
Expand All @@ -114,7 +118,7 @@ impl JLess {
Key::Char('%') => Some(Action::FocusMatchingPair),
Key::Char('m') => Some(Action::ToggleMode),
Key::Char(':') => {
let _readline = self.screen_writer.get_command(&self.viewer);
let _readline = self.screen_writer.get_command();
// Something like this?
// Some(Action::Command(parse_command(_readline))
None
Expand All @@ -130,8 +134,11 @@ impl JLess {
action
}
WinChEvent => {
let (width, height) = termion::terminal_size().unwrap();
Some(Action::ResizeWindow(height, width))
let dimensions = TTYDimensions::from_size(termion::terminal_size().unwrap());
self.screen_writer.dimensions = dimensions;
Some(Action::ResizeViewerDimensions(
dimensions.without_status_bar(),
))
}
_ => {
println!("{}Got: {:?}\r", BELL, event);
Expand All @@ -141,30 +148,36 @@ impl JLess {

if let Some(action) = action {
self.viewer.perform_action(action);
// TODO: Change print_screen to print_viewer,
// and uncomment print_status_bar below.
// self.screen_writer.print_viewer(&self.viewer);
self.screen_writer.print_screen(&self.viewer);
self.screen_writer.print_viewer(&self.viewer);
}
// self.screen_writer.print_status_bar(&self.viewer);
self.screen_writer
.print_status_bar(&self.viewer, &self.input_buffer);
}
}

fn buffer_input(&mut self, ch: u8) {
if self.input_buffer.len() >= MAX_BUFFER_SIZE {
self.input_buffer.rotate_left(1);
self.input_buffer.pop();
}
self.input_buffer.push(ch);
}

fn handle_z_input(&mut self) -> Option<Action> {
if self.input_buffer == "z" {
if self.input_buffer == "z".as_bytes() {
self.input_buffer.clear();

// Action::MoveFocusedLineToCenter()
None
} else {
self.input_buffer.clear();
self.input_buffer.push('z');
self.buffer_input('z' as u8);
None
}
}

fn parse_input_buffer_as_number(&mut self) -> usize {
let n = str::parse::<usize>(&self.input_buffer);
let n = str::parse::<usize>(std::str::from_utf8(&self.input_buffer).unwrap());
self.input_buffer.clear();
n.unwrap_or(1)
}
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod flatjson;
mod input;
mod jless;
mod screenwriter;
mod types;
mod viewer;

#[derive(Debug, StructOpt)]
Expand Down
67 changes: 49 additions & 18 deletions src/screenwriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use std::fmt::Write;
use termion::{clear, cursor};
use termion::{color, style};

use super::flatjson::{ContainerType, Index, OptionIndex, Row, Value};
use super::viewer::{JsonViewer, Mode};
use crate::flatjson::{ContainerType, Index, OptionIndex, Row, Value};
use crate::types::TTYDimensions;
use crate::viewer::{JsonViewer, Mode};

#[derive(Copy, Clone)]
#[allow(dead_code)]
Expand Down Expand Up @@ -33,6 +34,7 @@ use Color::*;
pub struct ScreenWriter {
pub tty_writer: AnsiTTYWriter,
pub command_editor: Editor<()>,
pub dimensions: TTYDimensions,
}

const FOCUSED_LINE: &'static str = "▶ ";
Expand All @@ -46,20 +48,34 @@ lazy_static! {
}

impl ScreenWriter {
pub fn print_screen(&mut self, viewer: &JsonViewer) {
match self.print_screen_no_error_handling(viewer) {
pub fn print(&mut self, viewer: &JsonViewer, input_buffer: &[u8]) {
self.print_viewer(viewer);
self.print_status_bar(viewer, input_buffer);
}

pub fn print_viewer(&mut self, viewer: &JsonViewer) {
match self.print_screen_impl(viewer) {
Ok(_) => {}
Err(e) => {
eprintln!("Error while printing to screen: {}", e);
eprintln!("Error while printing viewer: {}", e);
}
}
}

pub fn print_screen_no_error_handling(&mut self, viewer: &JsonViewer) -> std::io::Result<()> {
pub fn print_status_bar(&mut self, viewer: &JsonViewer, input_buffer: &[u8]) {
match self.print_status_bar_impl(viewer, input_buffer) {
Ok(_) => {}
Err(e) => {
eprintln!("Error while printing status bar: {}", e);
}
}
}

fn print_screen_impl(&mut self, viewer: &JsonViewer) -> std::io::Result<()> {
self.tty_writer.clear_screen()?;

let mut line = OptionIndex::Index(viewer.top_row);
for row_index in 0..viewer.height {
for row_index in 0..viewer.dimensions.height {
match line {
OptionIndex::Nil => {
self.tty_writer.position_cursor(1, row_index + 1)?;
Expand All @@ -76,18 +92,16 @@ impl ScreenWriter {
}
}

self.print_status_bar(viewer)?;

self.tty_writer.flush()
}

pub fn get_command(&mut self, viewer: &JsonViewer) -> rustyline::Result<String> {
pub fn get_command(&mut self) -> rustyline::Result<String> {
write!(self.tty_writer, "{}", termion::cursor::Show)?;
self.tty_writer.position_cursor(1, viewer.height + 2)?;
self.tty_writer.position_cursor(1, self.dimensions.height)?;
let result = self.command_editor.readline(":");
write!(self.tty_writer, "{}", termion::cursor::Hide)?;

self.tty_writer.position_cursor(1, viewer.height + 2)?;
self.tty_writer.position_cursor(1, self.dimensions.height)?;
self.tty_writer.clear_line()?;

match &result {
Expand Down Expand Up @@ -292,25 +306,42 @@ impl ScreenWriter {
Ok(())
}

fn print_status_bar(&mut self, viewer: &JsonViewer) -> std::io::Result<()> {
self.tty_writer.position_cursor(1, viewer.height + 1)?;
fn print_status_bar_impl(
&mut self,
viewer: &JsonViewer,
input_buffer: &[u8],
) -> std::io::Result<()> {
self.tty_writer
.position_cursor(1, self.dimensions.height - 1)?;
self.invert_colors(Black)?;
self.tty_writer.clear_line()?;
self.tty_writer.position_cursor(1, viewer.height + 1)?;
self.tty_writer
.position_cursor(1, self.dimensions.height - 1)?;
write!(
self.tty_writer,
"{}",
ScreenWriter::get_path_to_focused_node(viewer)
)?;
self.tty_writer
.position_cursor(viewer.width - 8, viewer.height + 1)?;
.position_cursor(self.dimensions.width - 8, self.dimensions.height - 1)?;
write!(self.tty_writer, "FILE NAME")?;

self.reset_style()?;
self.tty_writer.position_cursor(1, viewer.height + 2)?;
self.tty_writer.position_cursor(1, self.dimensions.height)?;
write!(self.tty_writer, ":")?;

Ok(())
let buffer_len = input_buffer.len();
self.tty_writer.position_cursor(
self.dimensions.width - 4 - (buffer_len as u16),
self.dimensions.height,
)?;
write!(
self.tty_writer,
"{}",
std::str::from_utf8(input_buffer).unwrap()
)?;

self.tty_writer.flush()
}

fn get_path_to_focused_node(viewer: &JsonViewer) -> String {
Expand Down
38 changes: 38 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
pub const DEFAULT_WIDTH: u16 = 80;
pub const DEFAULT_HEIGHT: u16 = 24;
pub const STATUS_BAR_HEIGHT: u16 = 2;

#[derive(Copy, Clone, Debug)]
pub struct TTYDimensions {
pub width: u16,
pub height: u16,
}

impl TTYDimensions {
pub fn from_size(size: (u16, u16)) -> TTYDimensions {
TTYDimensions {
width: size.0,
height: size.1,
}
}

pub fn without_status_bar(&self) -> TTYDimensions {
TTYDimensions {
width: self.width,
height: if self.height < STATUS_BAR_HEIGHT {
0
} else {
self.height - STATUS_BAR_HEIGHT
},
}
}
}

impl Default for TTYDimensions {
fn default() -> TTYDimensions {
TTYDimensions {
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
}
}
}
Loading

0 comments on commit 2d11dee

Please sign in to comment.