Skip to content

Commit

Permalink
add vertical margin (#48)
Browse files Browse the repository at this point in the history
this PR adds a vertical margin
- can be configured
- compute and keep track of the top index of each depth of the data
  • Loading branch information
amtoine authored Apr 15, 2024
1 parent 8127632 commit 763aba0
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 8 deletions.
1 change: 1 addition & 0 deletions examples/config/default.nuon
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
show_cell_path: true, # whether or not to show the current cell path above the status bar
show_table_header: true, # whether or not to show the table header in "table" layout
layout: "table", # the layout of the data, either "table" or "compact"
margin: 10, # the number of lines to keep between the cursor and the top / bottom

# "reset" is used instead of "black" in a dark terminal because, when the terminal is actually
# black, "black" is not really black which is ugly, whereas "reset" is really black.
Expand Down
3 changes: 3 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ impl std::fmt::Display for Mode {
pub struct App {
/// the full current path in the data
pub position: CellPath,
/// used for rendering
pub rendering_tops: Vec<i32>,
/// the current [`Mode`]
pub mode: Mode,
/// the editor to modify the cells of the data
Expand All @@ -54,6 +56,7 @@ impl Default for App {
fn default() -> Self {
Self {
position: CellPath { members: vec![] },
rendering_tops: vec![],
mode: Mode::default(),
editor: Editor::default(),
value: Value::default(),
Expand Down
14 changes: 13 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::style::{Color, Modifier};

use nu_protocol::{LabeledError, Value};
use nu_protocol::{LabeledError, Span, Value};

mod parsing;
use parsing::{
follow_cell_path, invalid_field, invalid_type, try_bool, try_fg_bg_colors, try_key, try_layout,
try_modifier, try_string,
};

use self::parsing::{positive_integer, try_int};

/// the configuration for the status bar colors in all [`crate::app::Mode`]s
#[derive(Clone, PartialEq, Debug)]
pub struct StatusBarColorConfig {
Expand Down Expand Up @@ -135,6 +137,7 @@ pub struct Config {
pub show_cell_path: bool,
pub layout: Layout,
pub show_table_header: bool,
pub margin: usize,
}

impl Default for Config {
Expand All @@ -145,6 +148,7 @@ impl Default for Config {
show_cell_path: true,
show_table_header: true,
layout: Layout::Table,
margin: 10,
colors: ColorConfig {
normal: TableRowColorConfig {
name: BgFgColorConfig {
Expand Down Expand Up @@ -248,6 +252,14 @@ impl Config {
config.layout = val
}
}
"margin" => {
if let Some(val) = try_int(&value, &["margin"])? {
if val < 0 {
return Err(positive_integer(val, &["margin"], Span::unknown()));
}
config.margin = val as usize
}
}
"colors" => {
let cell = follow_cell_path(&value, &["colors"]).unwrap();
let columns = match &cell {
Expand Down
42 changes: 40 additions & 2 deletions src/config/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ fn u8_out_of_range(value: i64, cell_path: &[&str], span: Span) -> LabeledError {
)
}

pub fn positive_integer(value: i64, cell_path: &[&str], span: Span) -> LabeledError {
LabeledError::new("invalid config").with_label(
format!(
"`$.{}` should be a positive integer, found {}",
cell_path.join("."),
value
),
span,
)
}

/// try to parse a bool in the *value* at the given *cell path*
pub fn try_bool(value: &Value, cell_path: &[&str]) -> Result<Option<bool>, LabeledError> {
match follow_cell_path(value, cell_path) {
Expand All @@ -85,6 +96,15 @@ pub fn try_string(value: &Value, cell_path: &[&str]) -> Result<Option<String>, L
}
}

/// try to parse an integer in the *value* at the given *cell path*
pub fn try_int(value: &Value, cell_path: &[&str]) -> Result<Option<i64>, LabeledError> {
match follow_cell_path(value, cell_path) {
Some(Value::Int { val, .. }) => Ok(Some(val)),
Some(x) => Err(invalid_type(&x, cell_path, "int")),
_ => Ok(None),
}
}

/// try to parse an ANSI modifier in the *value* at the given *cell path*
pub fn try_modifier(value: &Value, cell_path: &[&str]) -> Result<Option<Modifier>, LabeledError> {
match follow_cell_path(value, cell_path) {
Expand Down Expand Up @@ -332,8 +352,8 @@ mod tests {
use ratatui::style::{Color, Modifier};

use super::{
follow_cell_path, try_bool, try_color, try_fg_bg_colors, try_key, try_layout, try_modifier,
try_string,
follow_cell_path, try_bool, try_color, try_fg_bg_colors, try_int, try_key, try_layout,
try_modifier, try_string,
};
use crate::config::{BgFgColorConfig, Layout};

Expand Down Expand Up @@ -420,6 +440,24 @@ mod tests {
);
}

#[test]
fn trying_int() {
test_tried_error(
try_int(&Value::test_bool(true), &[]),
"",
"should be a int, found bool",
);
test_tried_error(
try_int(&Value::test_string("my string"), &[]),
"",
"should be a int, found string",
);

assert_eq!(try_int(&Value::test_int(123), &[]), Ok(Some(123)));
assert_eq!(try_int(&Value::test_int(-123), &[]), Ok(Some(-123)));
assert_eq!(try_int(&Value::test_int(123), &["x"]), Ok(None));
}

#[test]
fn trying_key() {
test_tried_error(
Expand Down
3 changes: 3 additions & 0 deletions src/navigation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ pub(super) fn go_deeper_in_data(app: &mut App) {
}
_ => app.hit_bottom(),
}

app.rendering_tops.push(0);
}

/// pop one level of depth from the data
Expand All @@ -166,6 +168,7 @@ pub(super) fn go_back_in_data(app: &mut App) {
app.position.members.pop();
}
app.mode = Mode::Normal;
app.rendering_tops.pop();
}

// TODO: add proper assert error messages
Expand Down
32 changes: 27 additions & 5 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use ratatui::{
};

/// render the whole ui
pub(super) fn render_ui(frame: &mut Frame, app: &App, config: &Config, error: Option<&str>) {
pub(super) fn render_ui(frame: &mut Frame, app: &mut App, config: &Config, error: Option<&str>) {
render_data(frame, app, config);
if config.show_cell_path {
render_cell_path(frame, app);
Expand Down Expand Up @@ -223,7 +223,7 @@ fn repr_table(table: &[Record]) -> (Vec<String>, Vec<String>, Vec<Vec<String>>)
///
/// the data will be rendered on top of the bar, and on top of the cell path in case
/// [`crate::config::Config::show_cell_path`] is set to `true`.
fn render_data(frame: &mut Frame, app: &App, config: &Config) {
fn render_data(frame: &mut Frame, app: &mut App, config: &Config) {
let mut data_path = app.position.members.clone();
let current = if !app.is_at_bottom() {
data_path.pop()
Expand Down Expand Up @@ -310,6 +310,22 @@ fn render_data(frame: &mut Frame, app: &App, config: &Config) {
None => 0,
};

let height = data_frame_height as i32 - 3; // 3: border x 2 + header
let cursor = selected as i32;
let top = *app.rendering_tops.last().unwrap_or(&0);
let margin = config.margin as i32;

if cursor >= top + height - margin {
app.rendering_tops.pop();
app.rendering_tops
.push((cursor - height + margin + 1).max(0));
} else if cursor <= top + margin {
app.rendering_tops.pop();
app.rendering_tops.push((cursor - margin).max(0));
}

let margin_offset = *app.rendering_tops.last().unwrap_or(&0) as usize;

if is_a_table {
let (columns, shapes, cells) = match value {
Value::List { vals, .. } => {
Expand Down Expand Up @@ -359,7 +375,9 @@ fn render_data(frame: &mut Frame, app: &App, config: &Config) {
frame.render_stateful_widget(
table,
rect_without_bottom_bar,
&mut TableState::default().with_selected(Some(selected)),
&mut TableState::default()
.with_selected(Some(selected))
.with_offset(margin_offset),
);

return;
Expand Down Expand Up @@ -392,7 +410,9 @@ fn render_data(frame: &mut Frame, app: &App, config: &Config) {
frame.render_stateful_widget(
items,
rect_without_bottom_bar,
&mut ListState::default().with_selected(Some(selected)),
&mut ListState::default()
.with_selected(Some(selected))
.with_offset(margin_offset),
)
}
Layout::Table => {
Expand Down Expand Up @@ -488,7 +508,9 @@ fn render_data(frame: &mut Frame, app: &App, config: &Config) {
frame.render_stateful_widget(
table,
rect_without_bottom_bar,
&mut TableState::default().with_selected(Some(selected)),
&mut TableState::default()
.with_selected(Some(selected))
.with_offset(margin_offset),
)
}
}
Expand Down

0 comments on commit 763aba0

Please sign in to comment.