Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Commit

Permalink
Wrap text
Browse files Browse the repository at this point in the history
  • Loading branch information
swsnr committed Apr 10, 2023
1 parent 83c3c10 commit 885dfc9
Show file tree
Hide file tree
Showing 292 changed files with 745 additions and 533 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ Use `cargo release` to create a new release.

## [Unreleased]

### Added
- mdcat now fills paragraph text to the column limit, i.e. fills up short lines and wraps long lines (see [GH-4]).

### Changed
- Update all dependencies.
- `mdcat::Settings` now holds a reference to a syntax set, so the syntax set can now be shared among multiple different settings.

### Changed
- Explicitly set minimum rust version in `Cargo.toml`, and document MSRV policy.

[GH-4]: https://github.com/swsnr/mdcat/issues/4

## [1.1.1] – 2023-03-18

### Fixed
Expand Down
41 changes: 41 additions & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ version = "5.0.0"
default-features = false
features = ["parsing", "regex-fancy", "default-themes", "default-syntaxes"]

[dependencies.textwrap]
version = "0.16.0"
default-features = false
features = ["unicode-linebreak", "unicode-width"]

[target.'cfg(unix)'.dependencies]
libc = "0.2.141"

Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ Run `mdcat` with `$MDCAT_LOG=trace` for complete tracing information, or with `$
- [ ] Figure out a better way to show HTML [#3].
- [ ] CommonMark extensions: Footnotes [#1].
- [ ] CommonMark extensions: Tables [#2].
- [ ] Ignore soft wraps and wrap inline text a column limit instead [#4].

[#1]: https://github.com/swsnr/mdcat/issues/1
[#2]: https://github.com/swsnr/mdcat/issues/2
Expand Down
112 changes: 84 additions & 28 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use pulldown_cmark::Tag::*;
use pulldown_cmark::{Event, LinkType};
use syntect::highlighting::{HighlightIterator, Highlighter, Theme};
use syntect::util::LinesWithEndings;
use textwrap::core::display_width;
use tracing::{event, instrument, Level};
use url::Url;

Expand All @@ -30,6 +31,7 @@ use crate::references::*;
use state::*;
use write::*;

use crate::render::data::CurrentLine;
use crate::render::state::MarginControl::{Margin, NoMargin};
use crate::terminal::capabilities::LinkCapability;
pub use data::StateData;
Expand Down Expand Up @@ -259,7 +261,7 @@ pub fn write_event<'a, W: Write>(

// Lists
(Stacked(stack, Inline(ListItem(kind, state), attrs)), Start(Item)) => {
let InlineAttrs { indent, style } = attrs;
let InlineAttrs { indent, style, .. } = attrs;
if state == ItemBlock {
// Add margin
writeln!(writer)?;
Expand Down Expand Up @@ -298,7 +300,7 @@ pub fn write_event<'a, W: Write>(
}
(Stacked(stack, Inline(ListItem(kind, _), attrs)), Start(CodeBlock(ck))) => {
writeln!(writer)?;
let InlineAttrs { indent, style } = attrs;
let InlineAttrs { indent, style, .. } = attrs;
stack
.push(Inline(ListItem(kind, ItemBlock), attrs))
.current(write_start_code_block(
Expand Down Expand Up @@ -362,7 +364,7 @@ pub fn write_event<'a, W: Write>(
.ok()
}
(Stacked(stack, Inline(ListItem(kind, state), attrs)), End(Item)) => {
let InlineAttrs { indent, style } = attrs;
let InlineAttrs { indent, style, .. } = attrs;
if state != ItemBlock {
// End the inline text of this item
writeln!(writer)?;
Expand Down Expand Up @@ -428,20 +430,26 @@ pub fn write_event<'a, W: Write>(

// Inline markup
(Stacked(stack, Inline(state, attrs)), Start(Emphasis)) => {
let indent = attrs.indent;
let style = Style {
is_italic: !attrs.style.is_italic,
..attrs.style
let InlineAttrs { style, indent } = attrs;
let new_style = Style {
is_italic: !style.is_italic,
..style
};
stack
.push(Inline(state, attrs))
.current(Inline(state, InlineAttrs { style, indent }))
.current(Inline(
state,
InlineAttrs {
style: new_style,
indent,
},
))
.and_data(data)
.ok()
}
(Stacked(stack, Inline(_, _)), End(Emphasis)) => stack.pop().and_data(data).ok(),
(Stacked(stack, Inline(state, attrs)), Start(Strong)) => {
let indent = attrs.indent;
let InlineAttrs { indent, .. } = attrs;
let style = attrs.style.bold();
stack
.push(Inline(state, attrs))
Expand All @@ -451,8 +459,8 @@ pub fn write_event<'a, W: Write>(
}
(Stacked(stack, Inline(_, _)), End(Strong)) => stack.pop().and_data(data).ok(),
(Stacked(stack, Inline(state, attrs)), Start(Strikethrough)) => {
let InlineAttrs { indent, .. } = attrs;
let style = attrs.style.strikethrough();
let indent = attrs.indent;
stack
.push(Inline(state, attrs))
.current(Inline(state, InlineAttrs { style, indent }))
Expand All @@ -461,51 +469,95 @@ pub fn write_event<'a, W: Write>(
}
(Stacked(stack, Inline(_, _)), End(Strikethrough)) => stack.pop().and_data(data).ok(),
(Stacked(stack, Inline(state, attrs)), Code(code)) => {
write_styled(
let current_line = write_styled_and_wrapped(
writer,
&settings.terminal_capabilities,
&attrs.style.fg(Colour::Yellow),
settings.terminal_size.columns,
attrs.indent as usize,
data.current_line,
code,
)?;
stack.current(Inline(state, attrs)).and_data(data).ok()
let data = StateData {
current_line,
..data
};
Ok(stack.current(Inline(state, attrs)).and_data(data))
}
(Stacked(stack, Inline(ListItem(kind, state), attrs)), TaskListMarker(checked)) => {
let marker = if checked { "\u{2611} " } else { "\u{2610} " };
let marker = if checked { "\u{2611}" } else { "\u{2610}" };
write_styled(
writer,
&settings.terminal_capabilities,
&attrs.style,
marker,
)?;
stack
let length = data.current_line.length + display_width(marker);
Ok(stack
.current(Inline(ListItem(kind, state), attrs))
.and_data(data)
.ok()
.and_data(data.current_line(CurrentLine {
length,
trailing_space: Some(" ".to_owned()),
})))
}
// Inline line breaks
(Stacked(stack, Inline(state, attrs)), SoftBreak) => {
writeln!(writer)?;
write_indent(writer, attrs.indent)?;
stack.current(Inline(state, attrs)).and_data(data).ok()
let length = data.current_line.length;

Ok(stack
.current(Inline(state, attrs))
.and_data(data.current_line(CurrentLine {
length,
trailing_space: Some(" ".to_owned()),
})))
}
(Stacked(stack, Inline(state, attrs)), HardBreak) => {
writeln!(writer)?;
write_indent(writer, attrs.indent)?;
stack.current(Inline(state, attrs)).and_data(data).ok()

Ok(stack
.current(Inline(state, attrs))
.and_data(data.current_line(CurrentLine::empty())))
}
// Inline text
(Stacked(stack, Inline(ListItem(kind, ItemBlock), attrs)), Text(text)) => {
// Fresh text after a new block, so indent again.
write_indent(writer, attrs.indent)?;
write_styled(writer, &settings.terminal_capabilities, &attrs.style, text)?;
stack
let current_line = write_styled_and_wrapped(
writer,
&settings.terminal_capabilities,
&attrs.style,
settings.terminal_size.columns,
attrs.indent as usize,
data.current_line,
text,
)?;
Ok(stack
.current(Inline(ListItem(kind, ItemText), attrs))
.and_data(data)
.ok()
.and_data(StateData {
current_line,
..data
}))
}
(Stacked(stack, Inline(state, attrs)), Text(text)) => {
// Inline blocks don't wrap
(Stacked(stack, Inline(InlineBlock, attrs)), Text(text)) => {
write_styled(writer, &settings.terminal_capabilities, &attrs.style, text)?;
stack.current(Inline(state, attrs)).and_data(data).ok()
Ok(stack.current(Inline(InlineBlock, attrs)).and_data(data))
}
(Stacked(stack, Inline(state, attrs)), Text(text)) => {
let current_line = write_styled_and_wrapped(
writer,
&settings.terminal_capabilities,
&attrs.style,
settings.terminal_size.columns,
attrs.indent as usize,
data.current_line,
text,
)?;
Ok(stack.current(Inline(state, attrs)).and_data(StateData {
current_line,
..data
}))
}
// Inline HTML
(Stacked(stack, Inline(ListItem(kind, ItemBlock), attrs)), Html(html)) => {
Expand Down Expand Up @@ -534,11 +586,15 @@ pub fn write_event<'a, W: Write>(
// Ending inline text
(Stacked(stack, Inline(_, _)), End(Paragraph)) => {
writeln!(writer)?;
stack.pop().and_data(data).ok()
Ok(stack
.pop()
.and_data(data.current_line(CurrentLine::empty())))
}
(Stacked(stack, Inline(_, _)), End(Heading(_, _, _))) => {
writeln!(writer)?;
stack.pop().and_data(data).ok()
Ok(stack
.pop()
.and_data(data.current_line(CurrentLine::empty())))
}

// Links.
Expand Down
29 changes: 29 additions & 0 deletions src/render/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ pub struct LinkReferenceDefinition<'a> {
pub(crate) colour: Colour,
}

/// The state of the current line for render.md.wrapping.
#[derive(Debug)]
pub struct CurrentLine {
/// The line length
pub(super) length: usize,
/// Trailing space to add before continuing this line.
pub(super) trailing_space: Option<String>,
}

impl CurrentLine {
/// An empty current line
pub(super) fn empty() -> Self {
Self {
length: 0,
trailing_space: None,
}
}
}

/// Data associated with rendering state.
///
/// Unlike state attributes state data represents cross-cutting
Expand All @@ -33,9 +52,18 @@ pub struct StateData<'a> {
pub(super) pending_link_definitions: Vec<LinkReferenceDefinition<'a>>,
/// The reference number for the next link.
pub(super) next_link: u16,
/// The state of the current line for render.md.wrapping.
pub(super) current_line: CurrentLine,
}

impl<'a> StateData<'a> {
pub(crate) fn current_line(self, current_line: CurrentLine) -> Self {
Self {
current_line,
..self
}
}

/// Add a pending link to the state data.
///
/// `target` is the link target, and `title` the link title to show after the URL.
Expand Down Expand Up @@ -75,6 +103,7 @@ impl<'a> Default for StateData<'a> {
StateData {
pending_link_definitions: Vec::new(),
next_link: 1,
current_line: CurrentLine::empty(),
}
}
}
Loading

0 comments on commit 885dfc9

Please sign in to comment.