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
12 changes: 8 additions & 4 deletions src/app/code_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,14 @@ impl App {
}
}

pub(crate) fn code_block_at_line(&self, line_idx: usize) -> Option<usize> {
self.code_blocks
.iter()
.position(|b| line_idx >= b.rendered_start && line_idx <= b.rendered_end)
pub(crate) fn code_block_at(&self, line_idx: usize, inner_col: u16) -> Option<usize> {
let col = inner_col as usize;
self.code_blocks.iter().position(|b| {
line_idx >= b.rendered_start
&& line_idx <= b.rendered_end
&& col >= b.rendered_offset
&& col < b.rendered_offset + b.rendered_width
})
}

pub(crate) fn highlighted_code_block(&self) -> Option<usize> {
Expand Down
27 changes: 19 additions & 8 deletions src/markdown/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ use super::width::display_width;
use super::wrapping::{push_wrapped_code_lines, push_wrapped_prefixed_lines};
use super::LastBlock;

pub(super) struct BlockLayout {
pub(super) prefix_width: usize,
pub(super) rendered_width: usize,
}

pub(super) fn block_prefix(
in_bq: bool,
theme: &MarkdownTheme,
Expand Down Expand Up @@ -169,7 +174,7 @@ pub(super) fn push_code_block_lines(
code_lang: &mut String,
ctx: CodeBlockRenderContext<'_>,
item_stack: &mut [ItemState],
) {
) -> BlockLayout {
let prefix = if !item_stack.is_empty() {
list_item_prefix(
ctx.blockquote_depth > 0,
Expand Down Expand Up @@ -275,9 +280,12 @@ pub(super) fn push_code_block_lines(
Style::default().fg(ctx.theme_colors.code_frame),
));
lines.push(Line::from(footer));
lines.push(Line::from(""));
code_lang.clear();
code_buf.clear();
BlockLayout {
prefix_width,
rendered_width: inner_width + 2,
}
}

pub(super) struct SpecialBlockCtx<'a, F: Fn(&str) -> Vec<Span<'static>>> {
Expand All @@ -296,7 +304,7 @@ pub(super) fn push_special_block_lines<F: Fn(&str) -> Vec<Span<'static>>>(
list_stack: &[ListKind],
item_stack: &mut [ItemState],
ctx: SpecialBlockCtx<'_, F>,
) {
) -> BlockLayout {
let label = ctx.label;
let content_lines = ctx.content_lines;
let show_line_numbers = ctx.show_line_numbers;
Expand Down Expand Up @@ -409,7 +417,10 @@ pub(super) fn push_special_block_lines<F: Fn(&str) -> Vec<Span<'static>>>(
));
}
lines.push(Line::from(footer));
lines.push(Line::from(""));
BlockLayout {
prefix_width,
rendered_width: inner_width + 2,
}
}

pub(super) struct EmbeddedBlockCtx<'a> {
Expand All @@ -425,7 +436,7 @@ pub(super) fn push_latex_block_lines(
content: &str,
ctx: EmbeddedBlockCtx<'_>,
item_stack: &mut [ItemState],
) {
) -> BlockLayout {
let rendered = latex::to_unicode(content);
let all_lines: Vec<&str> = rendered.lines().collect();
let start = all_lines
Expand All @@ -451,15 +462,15 @@ pub(super) fn push_latex_block_lines(
center: false,
make_spans: |line| vec![Span::styled(line.to_string(), content_style)],
},
);
)
}

pub(super) fn push_mermaid_block_lines(
lines: &mut Vec<Line<'static>>,
content: &str,
ctx: EmbeddedBlockCtx<'_>,
item_stack: &mut [ItemState],
) {
) -> BlockLayout {
let rendered = mermaid::render(content);
let use_rendered = rendered.is_some();
let content_lines: Vec<&str> = if let Some(ref r) = rendered {
Expand Down Expand Up @@ -488,7 +499,7 @@ pub(super) fn push_mermaid_block_lines(
}
},
},
);
)
}

pub(super) fn push_rule_line(
Expand Down
56 changes: 39 additions & 17 deletions src/markdown/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use toc::{normalize_toc, TocEntry};

use blocks::{
flush_wrapped_spans, push_code_block_lines, push_heading_lines, push_latex_block_lines,
push_mermaid_block_lines, push_rule_line, trim_paragraph_gap_before_block,
push_mermaid_block_lines, push_rule_line, trim_paragraph_gap_before_block, BlockLayout,
CodeBlockRenderContext, EmbeddedBlockCtx, CODE_BLOCK_GUTTER,
};
use fences::normalize_code_fences;
Expand Down Expand Up @@ -224,23 +224,28 @@ fn push_text_event(
pub(crate) struct CodeBlockInfo {
pub(crate) rendered_start: usize,
pub(crate) rendered_end: usize,
pub(crate) rendered_offset: usize,
pub(crate) rendered_width: usize,
pub(crate) raw_content: String,
}

fn record_code_block(
code_blocks: &mut Vec<CodeBlockInfo>,
raw_content: String,
rendered_start: usize,
lines_len_after: usize,
rendered_end: usize,
layout: BlockLayout,
) {
let rendered_end = lines_len_after.saturating_sub(1);
if rendered_end >= rendered_start {
code_blocks.push(CodeBlockInfo {
rendered_start,
rendered_end,
raw_content,
});
if rendered_end < rendered_start {
return;
}
code_blocks.push(CodeBlockInfo {
rendered_start,
rendered_end,
rendered_offset: layout.prefix_width,
rendered_width: layout.rendered_width,
raw_content,
});
}

pub(crate) struct ParseResult {
Expand Down Expand Up @@ -411,8 +416,8 @@ pub(crate) fn parse_markdown_with_width(
in_code = false;
let raw_content = code_buf.clone();
let rendered_start = lines.len();
if code_lang == "latex" || code_lang == "tex" {
push_latex_block_lines(
let layout = if code_lang == "latex" || code_lang == "tex" {
let layout = push_latex_block_lines(
&mut lines,
&code_buf,
EmbeddedBlockCtx {
Expand All @@ -427,8 +432,9 @@ pub(crate) fn parse_markdown_with_width(
code_buf.clear();
code_lang.clear();
wraps = true;
layout
} else if code_lang == "mermaid" {
push_mermaid_block_lines(
let layout = push_mermaid_block_lines(
&mut lines,
&code_buf,
EmbeddedBlockCtx {
Expand All @@ -442,8 +448,9 @@ pub(crate) fn parse_markdown_with_width(
);
code_buf.clear();
code_lang.clear();
layout
} else {
push_code_block_lines(
let layout = push_code_block_lines(
&mut lines,
&mut code_buf,
&mut code_lang,
Expand All @@ -460,8 +467,16 @@ pub(crate) fn parse_markdown_with_width(
&mut item_stack,
);
wraps = true;
}
record_code_block(&mut code_blocks, raw_content, rendered_start, lines.len());
layout
};
record_code_block(
&mut code_blocks,
raw_content,
rendered_start,
lines.len().saturating_sub(1),
layout,
);
lines.push(Line::from(""));
last_block = LastBlock::Other;
}
MdEvent::Code(text) => {
Expand Down Expand Up @@ -570,7 +585,7 @@ pub(crate) fn parse_markdown_with_width(
trim_paragraph_gap_before_block(&mut lines, last_block);
let raw_content = text.as_ref().trim().to_string();
let rendered_start = lines.len();
push_latex_block_lines(
let layout = push_latex_block_lines(
&mut lines,
text.as_ref(),
EmbeddedBlockCtx {
Expand All @@ -582,7 +597,14 @@ pub(crate) fn parse_markdown_with_width(
},
&mut item_stack,
);
record_code_block(&mut code_blocks, raw_content, rendered_start, lines.len());
record_code_block(
&mut code_blocks,
raw_content,
rendered_start,
lines.len().saturating_sub(1),
layout,
);
lines.push(Line::from(""));
wraps = true;
last_block = LastBlock::Other;
}
Expand Down
16 changes: 13 additions & 3 deletions src/runtime/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,14 @@ pub(super) fn handle_mouse_event(app: &mut App, mouse: MouseEvent) -> bool {
scrollbar_scroll_to(app, mouse.row);
true
} else if is_double_click {
let block_hit = line_idx_at(app, mouse.column, mouse.row)
.and_then(|line_idx| app.code_block_at_line(line_idx));
let inner_x = content_inner_x(app.content_area, gutter);
let block_hit = if mouse.column >= inner_x {
let inner_col = mouse.column - inner_x;
line_idx_at(app, mouse.column, mouse.row)
.and_then(|line_idx| app.code_block_at(line_idx, inner_col))
} else {
None
};
if let Some(block_idx) = block_hit {
app.code_select = Some(block_idx);
app.copy_code_block_at(block_idx);
Expand Down Expand Up @@ -354,10 +360,14 @@ fn is_in_rect(rect: Rect, col: u16, row: u16) -> bool {
col >= rect.x && col < rect.x + rect.width && row >= rect.y && row < rect.y + rect.height
}

fn content_inner_x(area: Rect, gutter: u16) -> u16 {
area.x + CONTENT_HORIZONTAL_PADDING + gutter
}

fn line_idx_at(app: &App, col: u16, row: u16) -> Option<usize> {
let area = app.content_area;
let gutter = app.line_number_gutter_width() as u16;
let inner_x = area.x + CONTENT_HORIZONTAL_PADDING + gutter;
let inner_x = content_inner_x(area, gutter);
let inner_w = area
.width
.saturating_sub(CONTENT_HORIZONTAL_PADDING * 2)
Expand Down
8 changes: 4 additions & 4 deletions src/tests/app_code_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ fn copy_selected_code_block_works_for_mermaid_latex_and_display_math() {
}

#[test]
fn code_block_at_line_resolves_mermaid_latex_and_display_math() {
fn code_block_at_resolves_mermaid_latex_and_display_math() {
let app = build_app_with_mixed_blocks();
let mermaid = &app.code_blocks[1];
let latex = &app.code_blocks[2];
let display_math = &app.code_blocks[3];
let mermaid_mid = (mermaid.rendered_start + mermaid.rendered_end) / 2;
let latex_mid = (latex.rendered_start + latex.rendered_end) / 2;
let display_math_mid = (display_math.rendered_start + display_math.rendered_end) / 2;
assert_eq!(app.code_block_at_line(mermaid_mid), Some(1));
assert_eq!(app.code_block_at_line(latex_mid), Some(2));
assert_eq!(app.code_block_at_line(display_math_mid), Some(3));
assert_eq!(app.code_block_at(mermaid_mid, 0), Some(1));
assert_eq!(app.code_block_at(latex_mid, 0), Some(2));
assert_eq!(app.code_block_at(display_math_mid, 0), Some(3));
}
Loading