Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions crates/config_core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ pub(crate) const DEFAULT_TMUX_BINARY: &str = "tmux";
pub(crate) const DEFAULT_TMUX_PERSISTENCE: bool = true;
pub(crate) const DEFAULT_TMUX_SHOW_ACTIVE_PANE_BORDER: bool = false;
pub(crate) const DEFAULT_MOUSE_SCROLL_MULTIPLIER: f32 = 3.0;
/// Unitless multiplier applied to the font's natural cell height to produce the
/// terminal row height. `1.0` means no extra vertical space; `2.0` doubles it.
pub const DEFAULT_LINE_HEIGHT: f32 = 1.4;
/// Lowest accepted line-height multiplier. Values below this cause rows to
/// visually overlap.
pub const MIN_LINE_HEIGHT: f32 = 0.8;
/// Highest accepted line-height multiplier. Values above this make the grid
/// unusably sparse.
pub const MAX_LINE_HEIGHT: f32 = 2.5;
pub(crate) const DEFAULT_SCROLLBACK_HISTORY: usize = 2000;
pub(crate) const MAX_SCROLLBACK_HISTORY: usize = 100_000;
pub(crate) const DEFAULT_INACTIVE_TAB_SCROLLBACK: Option<usize> = None;
Expand Down
2 changes: 2 additions & 0 deletions crates/config_core/src/default_config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ window_height = 820
font_family = JetBrains Mono
# Terminal font size in pixels
font_size = 14
# Terminal line height multiplier (0.8 to 2.5)
line_height = 1.4
# Shape of the terminal cursor
cursor_style = block
# Enable blinking cursor animation
Expand Down
9 changes: 8 additions & 1 deletion crates/config_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ mod schema;
mod types;

pub use color_keys::{ColorEntryError, apply_color_entry, canonical_color_key};
pub use constants::{SHELL_DECIDE_THEME_ID, VALID_ROOT_KEYS, VALID_SECTIONS};
pub use constants::{
DEFAULT_LINE_HEIGHT, MAX_LINE_HEIGHT, MIN_LINE_HEIGHT, SHELL_DECIDE_THEME_ID, VALID_ROOT_KEYS,
VALID_SECTIONS,
};
pub use diagnostics::{ConfigDiagnostic, ConfigDiagnosticKind, ConfigParseReport};
pub use document::{
ColorSettingUpdate, apply_color_updates, remove_raw_root_key, remove_root_setting,
Expand All @@ -32,5 +35,9 @@ pub use types::{
TerminalScrollbarStyle, TerminalScrollbarVisibility, ThemeId, WorkingDirFallback,
};

pub fn format_line_height(value: f32) -> String {
format!("{value:.2}")
}

#[cfg(test)]
mod parser_tests;
27 changes: 25 additions & 2 deletions crates/config_core/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::collections::{BTreeMap, HashMap};

use crate::color_keys::{ColorEntryError, apply_color_entry};
use crate::constants::{
MAX_MOUSE_SCROLL_MULTIPLIER, MAX_PANE_FOCUS_STRENGTH, MAX_SCROLLBACK_HISTORY,
MIN_MOUSE_SCROLL_MULTIPLIER, SHELL_DECIDE_THEME_ID, VALID_SECTIONS,
MAX_LINE_HEIGHT, MAX_MOUSE_SCROLL_MULTIPLIER, MAX_PANE_FOCUS_STRENGTH, MAX_SCROLLBACK_HISTORY,
MIN_LINE_HEIGHT, MIN_MOUSE_SCROLL_MULTIPLIER, SHELL_DECIDE_THEME_ID, VALID_SECTIONS,
};
use crate::diagnostics::{ConfigDiagnostic, ConfigDiagnosticKind, ConfigParseReport};
use crate::schema::{RootSettingId, root_setting_from_key, root_setting_spec};
Expand Down Expand Up @@ -519,6 +519,29 @@ impl AppConfig {
config.font_size = parsed;
}
}
RootSettingId::LineHeight => {
if let Some(parsed) = parse_f32_field(
&mut diagnostics,
line_number,
key,
value,
"a finite number between 0.8 and 2.5",
) {
if parsed.is_finite()
&& (MIN_LINE_HEIGHT..=MAX_LINE_HEIGHT).contains(&parsed)
{
config.line_height = parsed;
} else {
push_invalid_value(
&mut diagnostics,
line_number,
key,
value,
"a finite number between 0.8 and 2.5",
);
}
}
Comment thread
fcoury marked this conversation as resolved.
}
RootSettingId::CursorStyle => {
if let Some(parsed) = CursorStyle::from_str(value) {
config.cursor_style = parsed;
Expand Down
29 changes: 25 additions & 4 deletions crates/config_core/src/parser_tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
AiProvider, AppConfig, ConfigDiagnosticKind, ConfigParseReport, CursorStyle, PaneFocusEffect,
Rgb8, RootSettingId, RootSettingValueKind, TabCloseVisibility, TabTitleMode, TabTitleSource,
TabWidthMode, TerminalScrollbarStyle, TerminalScrollbarVisibility, WorkingDirFallback,
root_setting_specs,
AiProvider, AppConfig, ConfigDiagnosticKind, ConfigParseReport, CursorStyle,
DEFAULT_LINE_HEIGHT, PaneFocusEffect, Rgb8, RootSettingId, RootSettingValueKind,
TabCloseVisibility, TabTitleMode, TabTitleSource, TabWidthMode, TerminalScrollbarStyle,
TerminalScrollbarVisibility, WorkingDirFallback, root_setting_specs,
};

fn parse(input: &str) -> AppConfig {
Expand All @@ -21,6 +21,7 @@ fn defaults_enable_tmux_persistence_and_raise_pane_focus_strength() {
assert!(!defaults.background_opacity_cells);
assert!(!defaults.chrome_contrast);
assert!((defaults.pane_focus_strength - 0.6).abs() < f32::EPSILON);
assert!((defaults.line_height - DEFAULT_LINE_HEIGHT).abs() < f32::EPSILON);
}

#[test]
Expand Down Expand Up @@ -390,6 +391,26 @@ fn numeric_keys_parse_table_driven() {
assert_eq!(parsed, fallback);
}

assert_eq!(parse("line_height = 0.8\n").line_height, 0.8);
assert_eq!(parse("line_height = 1.25\n").line_height, 1.25);
assert_eq!(parse("line_height = 2.5\n").line_height, 2.5);
assert_eq!(
parse("line_height = 0.79\n").line_height,
defaults.line_height
);
assert_eq!(
parse("line_height = 2.51\n").line_height,
defaults.line_height
);
assert_eq!(
parse("line_height = inf\n").line_height,
defaults.line_height
);
assert_eq!(
parse("line_height = NaN\n").line_height,
defaults.line_height
);

let non_negative_float_cases = [("padding_x", 2.0), ("padding_y", 4.0)];
for (key, expected) in non_negative_float_cases {
let valid = parse(&format!("{} = {}\n", key, expected));
Expand Down
11 changes: 11 additions & 0 deletions crates/config_core/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ define_root_settings! {
(WindowHeight, "window_height", [], Advanced, "WINDOW", "Window Height", "Default startup window height in pixels", ["window", "height", "startup", "size"], RootSettingValueKind::Numeric, false),
(FontFamily, "font_family", [], Appearance, "FONT", "Font Family", "Font family used in terminal UI", ["font", "typeface", "text"], RootSettingValueKind::Special, false),
(FontSize, "font_size", [], Appearance, "FONT", "Font Size", "Terminal font size in pixels", ["font", "size", "text"], RootSettingValueKind::Numeric, false),
(LineHeight, "line_height", [], Appearance, "FONT", "Line Height", "Terminal line height multiplier (0.8 to 2.5)", ["font", "line", "height", "spacing", "rows"], RootSettingValueKind::Numeric, false),
(CursorStyle, "cursor_style", [], Terminal, "CURSOR", "Cursor Style", "Shape of the terminal cursor", ["cursor", "shape", "block", "line"], RootSettingValueKind::Enum, false),
(CursorBlink, "cursor_blink", [], Terminal, "CURSOR", "Cursor Blink", "Enable blinking cursor animation", ["cursor", "blink", "animation"], RootSettingValueKind::Boolean, false),
(BackgroundOpacity, "background_opacity", [], Appearance, "WINDOW", "Background Opacity", "Window background opacity (0.0 to 1.0)", ["background", "opacity", "transparency"], RootSettingValueKind::Numeric, false),
Expand Down Expand Up @@ -478,6 +479,7 @@ pub fn root_setting_default_value(config: &AppConfig, id: RootSettingId) -> Opti
RootSettingId::WindowHeight => Some(config.window_height.to_string()),
RootSettingId::FontFamily => Some(config.font_family.clone()),
RootSettingId::FontSize => Some(config.font_size.to_string()),
RootSettingId::LineHeight => Some(crate::format_line_height(config.line_height)),
RootSettingId::CursorStyle => Some(match config.cursor_style {
CursorStyle::Line => "line".to_string(),
CursorStyle::Block => "block".to_string(),
Expand Down Expand Up @@ -641,4 +643,13 @@ mod tests {
Some("false".to_string())
);
}

#[test]
fn line_height_default_value_matches_app_config() {
let defaults = AppConfig::default();
assert_eq!(
root_setting_default_value(&defaults, RootSettingId::LineHeight),
Some(crate::format_line_height(defaults.line_height))
);
}
}
5 changes: 5 additions & 0 deletions crates/config_core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ pub struct AppConfig {
pub window_height: f32,
pub font_family: String,
pub font_size: f32,
/// Unitless multiplier on the font cell height that controls vertical row
/// spacing. Clamped to [`MIN_LINE_HEIGHT`]..=[`MAX_LINE_HEIGHT`] at the
/// use-site in `TerminalView`.
pub line_height: f32,
pub cursor_style: CursorStyle,
pub cursor_blink: bool,
pub background_opacity: f32,
Expand Down Expand Up @@ -360,6 +364,7 @@ impl Default for AppConfig {
window_height: 820.0,
font_family: "JetBrains Mono".to_string(),
font_size: 14.0,
line_height: crate::constants::DEFAULT_LINE_HEIGHT,
cursor_style: CursorStyle::default(),
cursor_blink: DEFAULT_CURSOR_BLINK,
background_opacity: 1.0,
Expand Down
7 changes: 1 addition & 6 deletions crates/terminal_ui/src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,11 +1058,6 @@ impl TerminalGrid {
self.cells.iter().map(|row| row.len()).sum()
}

#[cfg(test)]
fn iter_cells(&self) -> impl Iterator<Item = &CellRenderInfo> {
self.cells.iter().flat_map(|row| row.iter())
}

fn cell_is_drawable_text(cell: &CellRenderInfo) -> bool {
cell.render_text && cell.char != ' ' && cell.char != '\0' && !cell.char.is_control()
}
Expand Down Expand Up @@ -1107,7 +1102,7 @@ impl TerminalGrid {
let mut ops = Vec::with_capacity(self.cell_count());
let mut current: Option<TextBatch> = None;

for cell in self.iter_cells() {
for cell in self.cells.iter().flat_map(|row| row.iter()) {
if !Self::cell_is_drawable_text(cell) {
Self::push_pending_text_batch(&mut current, &mut ops);
continue;
Expand Down
5 changes: 5 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ Tmux integration is an add-on. Set `tmux_enabled = true` to start in tmux mode b
- Terminal font size in pixels
- Group: `FONT`

`line_height`
- Default: `1.4`
- Terminal line height multiplier (0.8 to 2.5)
- Group: `FONT`

`background_opacity`
- Default: `1`
- Window background opacity (0.0 to 1.0)
Expand Down
2 changes: 1 addition & 1 deletion src/settings_view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::time::{Duration, Instant};
use termy_command_core::CommandId;
use termy_config_core::{
RootSettingId, RootSettingValueKind, SettingsSection as CoreSettingsSection,
color_setting_from_key, color_setting_specs, root_setting_default_value,
color_setting_from_key, color_setting_specs, format_line_height, root_setting_default_value,
root_setting_enum_choices, root_setting_from_key, root_setting_specs, root_setting_value_kind,
};

Expand Down
10 changes: 10 additions & 0 deletions src/settings_view/sections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,14 @@ impl SettingsWindow {
let chrome_contrast = self.config.chrome_contrast;
let font_family = self.config.font_family.clone();
let font_size = self.config.font_size;
let line_height = self.config.line_height;
let padding_x = self.config.padding_x;
let padding_y = self.config.padding_y;
let theme_meta = Self::setting_metadata_or_fallback("theme");
let opacity_meta = Self::setting_metadata_or_fallback("background_opacity");
let font_family_meta = Self::setting_metadata_or_fallback("font_family");
let font_size_meta = Self::setting_metadata_or_fallback("font_size");
let line_height_meta = Self::setting_metadata_or_fallback("line_height");
let padding_x_meta = Self::setting_metadata_or_fallback("padding_x");
let padding_y_meta = Self::setting_metadata_or_fallback("padding_y");

Expand Down Expand Up @@ -161,6 +163,14 @@ impl SettingsWindow {
format!("{}px", font_size as i32),
cx,
),
self.render_editable_row(
"line_height",
EditableField::LineHeight,
line_height_meta.title,
line_height_meta.description,
format!("{line_height:.2}"),
cx,
),
];
let font_group = self.render_settings_group("FONT", font_rows);

Expand Down
35 changes: 35 additions & 0 deletions src/settings_view/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub(super) enum EditableField {
BackgroundOpacity,
FontFamily,
FontSize,
LineHeight,
PaddingX,
PaddingY,
Shell,
Expand Down Expand Up @@ -404,6 +405,7 @@ impl SettingsWindow {
| EditableField::BackgroundOpacity
| EditableField::FontFamily
| EditableField::FontSize
| EditableField::LineHeight
| EditableField::PaddingX
| EditableField::PaddingY => Self::appearance_field_spec(field),
EditableField::Shell
Expand Down Expand Up @@ -474,6 +476,16 @@ impl SettingsWindow {
max: 4096.0,
}),
},
EditableField::LineHeight => FieldSpec {
root_setting: Some(RootSettingId::LineHeight),
codec: FieldCodec::Numeric,
dropdown_click_only: false,
numeric_step: Some(NumericStepSpec {
delta: 0.05,
min: termy_config_core::MIN_LINE_HEIGHT,
max: termy_config_core::MAX_LINE_HEIGHT,
}),
},
EditableField::PaddingX => FieldSpec {
root_setting: Some(RootSettingId::PaddingX),
codec: FieldCodec::Numeric,
Expand Down Expand Up @@ -822,6 +834,7 @@ impl SettingsWindow {
),
EditableField::FontFamily => self.config.font_family.clone(),
EditableField::FontSize => format!("{}", self.config.font_size.round() as i32),
EditableField::LineHeight => format_line_height(self.config.line_height),
EditableField::PaddingX => format!("{}", self.config.padding_x.round() as i32),
EditableField::PaddingY => format!("{}", self.config.padding_y.round() as i32),
EditableField::Shell => self.config.shell.clone().unwrap_or_default(),
Expand Down Expand Up @@ -975,6 +988,18 @@ impl SettingsWindow {
&next.to_string(),
)
}
EditableField::LineHeight => {
let next = (self.config.line_height + (delta as f32 * step.delta))
.clamp(step.min, step.max);
let result = config::set_root_setting(
termy_config_core::RootSettingId::LineHeight,
&format_line_height(next),
);
if result.is_ok() {
self.config.line_height = next;
}
result
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
EditableField::PaddingX => {
let next =
(self.config.padding_x + (delta as f32 * step.delta)).clamp(step.min, step.max);
Expand Down Expand Up @@ -1172,6 +1197,7 @@ mod tests {
EditableField::BackgroundOpacity,
EditableField::FontFamily,
EditableField::FontSize,
EditableField::LineHeight,
EditableField::PaddingX,
EditableField::PaddingY,
EditableField::Shell,
Expand Down Expand Up @@ -1238,6 +1264,7 @@ mod tests {
let numeric_fields = [
EditableField::BackgroundOpacity,
EditableField::FontSize,
EditableField::LineHeight,
EditableField::PaddingX,
EditableField::PaddingY,
EditableField::ScrollbackHistory,
Expand All @@ -1254,6 +1281,14 @@ mod tests {
assert_eq!(spec.codec, FieldCodec::Numeric);
assert!(spec.numeric_step.is_some());
}

let line_height_spec = SettingsWindow::field_spec(EditableField::LineHeight);
let step = line_height_spec
.numeric_step
.expect("missing line-height step");
assert!((step.delta - 0.05).abs() < f32::EPSILON);
assert!((step.min - termy_config_core::MIN_LINE_HEIGHT).abs() < f32::EPSILON);
assert!((step.max - termy_config_core::MAX_LINE_HEIGHT).abs() < f32::EPSILON);
}

#[test]
Expand Down
Loading
Loading