Skip to content
Open
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
1 change: 1 addition & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub struct GeneralConfig {
pub log_author_width: usize,
pub mouse_support: bool,
pub mouse_scroll_lines: usize,
pub wrap_lines: bool,
}

#[derive(Default, Debug, Deserialize)]
Expand Down
3 changes: 3 additions & 0 deletions src/default_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ recent_commits_limit = 10
log_author_width = 15
mouse_support = false
mouse_scroll_lines = 3
# Wrap lines that are wider than the screen. When disabled, they are cut off
# at the right edge instead.
wrap_lines = true

# When to prompt for discard confirmation. Options:
# "line" - always prompt even for individual lines (default),
Expand Down
2 changes: 1 addition & 1 deletion src/screen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ fn layout_item<'a>(layout: &mut UiTree<'a>, screen: &'a Screen, hide_cursor: boo
let line_sel = line_selection_highlight(style, &line, is_line_sel);
let bg = area_sel.patch(line_sel);

layout.row_with(bg, opts().fill_x(), |layout| {
layout.row_with(bg, ui::item::row_opts(&screen.config), |layout| {
let gutter_char = if !hide_cursor && line.highlighted {
gutter_char(style, is_line_sel, bg)
} else {
Expand Down
18 changes: 18 additions & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,24 @@ fn recent_commits_with_limit() {
snapshot!(ctx, "");
}

const LONG_SUMMARY: &str =
"a commit summary that is far too wide to fit within eighty columns of terminal";

#[test]
fn long_lines_wrap_by_default() {
let ctx = setup_clone!();
commit(&ctx.dir, LONG_SUMMARY, "testing\n");
snapshot!(ctx, "");
}

#[test]
fn long_lines_are_cut_off_with_wrap_lines_disabled() {
let mut ctx = setup_clone!();
ctx.config().general.wrap_lines = false;
commit(&ctx.dir, LONG_SUMMARY, "testing\n");
snapshot!(ctx, "");
}

#[test]
fn log() {
let ctx = setup_clone!();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: src/tests/mod.rs
expression: ctx.redact_buffer()
---
▌On branch main |
▌Your branch is ahead of 'origin/main' by 1 commit(s). |
|
Recent commits |
ac66c1a main add a commit summary that is far too wide to fit w Author Name __ |
b66a0bf origin/main add initial-file Author Name __ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
styles_hash: b2801eef61626397
25 changes: 25 additions & 0 deletions src/tests/snapshots/gitu__tests__long_lines_wrap_by_default.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: src/tests/mod.rs
expression: ctx.redact_buffer()
---
▌On branch main |
▌Your branch is ahead of 'origin/main' by 1 commit(s). |
|
Recent commits |
ac66c1a main add a commit summary that is far too wide to fit Author Name __ |
within eighty columns of terminal |
b66a0bf origin/main add initial-file Author Name __ |
|
|
|
|
|
|
|
|
|
|
|
|
|
styles_hash: 70b38ef10efcdd9f
38 changes: 37 additions & 1 deletion src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,13 @@ fn print_spans(
Payload::Leaf(span) => {
blank_until(term, &mut at, pos, size.0, bg, bg_end)?;
term.queue_move_cursor(pos[0], pos[1])?;
print_span(term, span, highlight.within(index), highlight.style)?;
print_span(
term,
span,
item_size[0],
highlight.within(index),
highlight.style,
)?;

at[0] = pos[0].saturating_add(item_size[0]);
}
Expand All @@ -307,15 +313,21 @@ fn print_spans(
Ok(())
}

/// Prints at most `width` columns of the span, which is less than its text
/// when the layout cut it off at an edge.
fn print_span(
term: &mut TermBackend,
Span(text, style): &Span,
width: u16,
matches: impl Iterator<Item = Range<usize>>,
match_style: Style,
) -> Result<(), Error> {
let text = clip(text, width as usize);
let mut at = 0;

for matched in matches {
let matched = matched.start.min(text.len())..matched.end.min(text.len());

if at < matched.start {
term.queue_print(&text[at..matched.start], style)?;
}
Expand All @@ -331,6 +343,21 @@ fn print_span(
Ok(())
}

/// The longest prefix of `text` that fits in `width` columns.
fn clip(text: &str, width: usize) -> &str {
let mut used = 0;

for (i, grapheme) in text.grapheme_indices(true) {
used += UnicodeWidthStr::width(grapheme);

if used > width {
return &text[..i];
}
}

text
}

fn blank_until(
term: &mut TermBackend,
at: &mut [u16; 2],
Expand Down Expand Up @@ -404,6 +431,15 @@ mod tests {
.collect()
}

#[test]
fn clip_stops_before_a_grapheme_that_does_not_fit() {
assert_eq!("abc", clip("abcdef", 3));
assert_eq!("abcdef", clip("abcdef", 10));
// A double-width char that only half fits is left out entirely.
assert_eq!("a", clip("a漢", 2));
assert_eq!("", clip("漢", 1));
}

#[test]
fn a_match_may_run_from_one_span_into_the_next() {
assert_eq!(
Expand Down
13 changes: 11 additions & 2 deletions src/ui/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ use crate::gitu_diff::Status;
use crate::highlight;
use crate::item_data::{ItemData, Ref, SectionHeader};
use crate::items::Item;
use crate::ui::layout::opts;
use crate::ui::layout::{Opts, opts};
use crate::ui::{UiTree, layout_span};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

/// Options for a row holding an item, honouring `general.wrap_lines`.
pub(crate) fn row_opts(config: &Config) -> Opts<u16> {
if config.general.wrap_lines {
opts().fill_x()
} else {
opts().fill_x().no_wrap()
}
}

/// Lays out an [`Item`] as spans in the caller's container, which is expected to
/// be a single row.
///
Expand Down Expand Up @@ -78,7 +87,7 @@ pub(crate) fn layout_item<'a>(
),
);

layout.row(opts().fill_x(), |layout| {
layout.row(row_opts(config), |layout| {
for reference in associated_references {
layout_span(layout, (" ".into(), base));
layout_reference(layout, reference, config, base);
Expand Down
3 changes: 2 additions & 1 deletion src/ui/layout/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ impl<U: Scalar> Opts<U> {
}
}

#[allow(dead_code)]
/// Cuts a child that doesn't fit off at the edge, rather than starting a
/// new line.
pub fn no_wrap(self) -> Self {
Self {
wrap: Some(false),
Expand Down
Loading