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

Commit

Permalink
Implement a basic wrapping algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
swsnr committed Sep 16, 2021
1 parent 96b8830 commit 1ed141d
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 29 deletions.
22 changes: 21 additions & 1 deletion 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 @@ -39,6 +39,11 @@ version = "^4.5"
default-features = false
features = ["parsing", "assets", "dump-load", "regex-fancy"]

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

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

Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,13 @@ where
(State::default(), StateData::default()),
|(state, data), event| {
let s = Style::new().fg(Colour::Blue).paint(format!("{:?}", state));
let sep = Style::new().fg(Colour::Yellow).paint("|>");
let d = Style::new().fg(Colour::Green).paint(format!("{:?}", data));
let sep_e = Style::new().fg(Colour::Yellow).paint("|>");
let sep_d = Style::new().fg(Colour::Yellow).paint("@");
let e = Style::new()
.fg(Colour::Purple)
.paint(format!("{:?}", event));
writeln!(writer, "{} {} {}", s, sep, e)?;
writeln!(writer, "{} {} {} {} {}", s, sep_d, d, sep_e, e)?;
write_event(&mut sink, settings, environment, theme, state, data, event)
},
)?;
Expand Down
107 changes: 83 additions & 24 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use pulldown_cmark::{Event, LinkType};
use std::io::Error;
use syntect::highlighting::{HighlightIterator, Highlighter, Theme};
use syntect::util::LinesWithEndings;
use textwrap::core::display_width;
use url::Url;

use crate::terminal::*;
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};
pub use data::StateData;
pub use state::State;
Expand Down Expand Up @@ -241,7 +243,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 @@ -278,7 +280,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 @@ -337,7 +339,7 @@ pub fn write_event<'a, W: Write>(
.and_data(data)
}
(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 @@ -399,19 +401,25 @@ 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)
}
(Stacked(stack, Inline(_, _)), End(Emphasis)) => (stack.pop(), data),
(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 @@ -420,58 +428,109 @@ pub fn write_event<'a, W: Write>(
}
(Stacked(stack, Inline(_, _)), End(Strong)) => (stack.pop(), data),
(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 }))
.and_data(data)
}
(Stacked(stack, Inline(_, _)), End(Strikethrough)) => (stack.pop(), data),
(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)), data)
(
stack.current(Inline(state, attrs)),
StateData {
current_line,
..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,
)?;
let length = data.current_line.length + display_width(marker);
stack
.current(Inline(ListItem(kind, state), attrs))
.and_data(data)
.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)), data)
let length = data.current_line.length;
(
stack.current(Inline(state, attrs)),
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)), data)
(
stack.current(Inline(state, attrs)),
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)?;
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,
)?;
stack
.current(Inline(ListItem(kind, ItemText), attrs))
.and_data(data)
.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)), data)
(stack.current(Inline(InlineBlock, attrs)), 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,
)?;
(
stack.current(Inline(state, attrs)),
StateData {
current_line,
..data
},
)
}
// Inline HTML
(Stacked(stack, Inline(ListItem(kind, ItemBlock), attrs)), Html(html)) => {
Expand Down Expand Up @@ -499,11 +558,11 @@ pub fn write_event<'a, W: Write>(
// Ending inline text
(Stacked(stack, Inline(_, _)), End(Paragraph)) => {
writeln!(writer)?;
(stack.pop(), data)
(stack.pop(), data.current_line(CurrentLine::empty()))
}
(Stacked(stack, Inline(_, _)), End(Heading(_))) => {
writeln!(writer)?;
(stack.pop(), data)
(stack.pop(), 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(),
}
}
}
4 changes: 4 additions & 0 deletions src/render/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ pub enum InlineState {
///
/// Regular inline text without any particular implications.
InlineText,
/// Inline text.
///
/// Inline text block which may not be wrapped.
InlineBlock,
/// Inline link.
///
/// This state suppresses link references being written when reading a link
Expand Down
Loading

0 comments on commit 1ed141d

Please sign in to comment.