fix(html): keyword-in-heading badge + brackets in wikilink descriptions
This commit is contained in:
@@ -81,6 +81,9 @@ pub enum VimwikiTokenKind {
|
||||
PreformattedOpen {
|
||||
language: Option<String>,
|
||||
attrs: HashMap<String, String>,
|
||||
/// Verbatim text after `{{{` (trailing whitespace trimmed), preserved
|
||||
/// so the HTML renderer can reproduce vimwiki's raw `<pre …>` attrs.
|
||||
raw: String,
|
||||
},
|
||||
PreformattedClose,
|
||||
PreformattedLine(String),
|
||||
@@ -421,7 +424,14 @@ impl<'src> LexState<'src> {
|
||||
// pairs separated by spaces. Keep parsing forgiving.
|
||||
let after = trimmed[3..].trim();
|
||||
let (language, attrs) = parse_fence_attrs(after);
|
||||
let kind = VimwikiTokenKind::PreformattedOpen { language, attrs };
|
||||
// vimwiki keeps everything after `{{{` (minus trailing whitespace) and
|
||||
// drops it verbatim into the `<pre>` tag, so capture it unparsed.
|
||||
let raw = trimmed[3..].trim_end().to_string();
|
||||
let kind = VimwikiTokenKind::PreformattedOpen {
|
||||
language,
|
||||
attrs,
|
||||
raw,
|
||||
};
|
||||
let span = Span::new(self.pos_in_line(indent), self.line_end_pos(line));
|
||||
self.push(kind, span);
|
||||
self.mode = BlockMode::Preformatted;
|
||||
@@ -865,8 +875,10 @@ impl<'src> LexState<'src> {
|
||||
flush(self, &mut buf, &mut buf_start, abs_col);
|
||||
self.emit(VimwikiTokenKind::WikiLinkOpen, abs_col, abs_col + 2);
|
||||
i += 2;
|
||||
// Lex until `]]` (absolute index into `slice`).
|
||||
let close_abs_in_slice = after_open.find("]]").map(|c| i + c);
|
||||
// Lex until the closing `]]`. A single `]` inside the body —
|
||||
// e.g. a `[TICKET-1]` in the description — is literal text, so
|
||||
// we balance inner `[ ]` rather than stopping at the first `]]`.
|
||||
let close_abs_in_slice = find_wikilink_close(after_open).map(|c| i + c);
|
||||
let inner_end = close_abs_in_slice.unwrap_or(slice.len());
|
||||
self.lex_link_body(slice, i, line_col_start, inner_end, true);
|
||||
i = inner_end;
|
||||
@@ -1052,6 +1064,27 @@ impl<'src> LexState<'src> {
|
||||
/// Parse the bit after `{{{` on a fence line. Format is forgiving:
|
||||
/// `lang` (single word) followed by optional `key=value` pairs separated
|
||||
/// by spaces.
|
||||
/// Byte offset of the closing `]]` for a wikilink body that begins at the start
|
||||
/// of `s` (just after the opening `[[`). Inner `[ ]` pairs are balanced so a
|
||||
/// bracketed description like `[[page|Task [B-1]]]` keeps the `[B-1]` and closes
|
||||
/// at the final `]]`. A stray single `]` (no matching `[`) is treated as literal
|
||||
/// body text. Returns `None` if no closing `]]` is found.
|
||||
fn find_wikilink_close(s: &str) -> Option<usize> {
|
||||
let b = s.as_bytes();
|
||||
let mut depth: u32 = 0;
|
||||
let mut i = 0;
|
||||
while i < b.len() {
|
||||
match b[i] {
|
||||
b'[' => depth += 1,
|
||||
b']' if depth > 0 => depth -= 1, // closes an inner '['
|
||||
b']' if i + 1 < b.len() && b[i + 1] == b']' => return Some(i),
|
||||
_ => {}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_fence_attrs(s: &str) -> (Option<String>, HashMap<String, String>) {
|
||||
let mut attrs = HashMap::new();
|
||||
let mut language: Option<String> = None;
|
||||
|
||||
Reference in New Issue
Block a user