mirror of
https://github.com/tealdeer-rs/tealdeer.git
synced 2026-08-22 16:14:18 +02:00
parent
3f600b6e76
commit
4e8f6661d3
2 changed files with 22 additions and 78 deletions
|
|
@ -31,53 +31,3 @@ impl FindFrom for str {
|
|||
.map(|i| i + start)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ReplaceInplace {
|
||||
fn replace_inplace(&mut self, pattern: &Self, replacement: &Self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Copy> ReplaceInplace for [T] {
|
||||
fn replace_inplace(&mut self, pattern: &Self, replacement: &Self) -> &mut Self {
|
||||
assert!(replacement.len() <= pattern.len());
|
||||
let mut read_index = 0;
|
||||
let mut write_index = 0;
|
||||
loop {
|
||||
if let Some(match_dist) = self[read_index..]
|
||||
.windows(pattern.len())
|
||||
.position(|win| win == pattern)
|
||||
{
|
||||
self.copy_within(read_index..read_index + match_dist, write_index);
|
||||
read_index += match_dist + pattern.len();
|
||||
write_index += match_dist;
|
||||
|
||||
self[write_index..write_index + replacement.len()].copy_from_slice(replacement);
|
||||
write_index += replacement.len();
|
||||
} else {
|
||||
self.copy_within(read_index.., write_index);
|
||||
write_index += self.len() - read_index;
|
||||
|
||||
return &mut self[..write_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplaceInplace for str {
|
||||
fn replace_inplace(&mut self, pattern: &Self, replacement: &Self) -> &mut Self {
|
||||
let end = {
|
||||
// SAFETY: At the end of the lifetime of `self_bytes`, we have written to all bytes.
|
||||
// The bytes until `end` are valid UTF-8, because UTF-8 substrings matching `pattern`
|
||||
// are replaced by the UTF-8 string `replacement`. From `end` ongoing, we overwrite
|
||||
// everything with ascii letters.
|
||||
let self_bytes = unsafe { self.as_bytes_mut() };
|
||||
let end = self_bytes
|
||||
.replace_inplace(pattern.as_bytes(), replacement.as_bytes())
|
||||
.len();
|
||||
// Note that if we wouldn't do this, we could have the end of a multi-byte sequence
|
||||
// left at the end of `self_bytes` which doesn't have the, say, first byte anymore.
|
||||
self_bytes[end..].fill(b'a');
|
||||
end
|
||||
};
|
||||
&mut self[..end]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
use log::debug;
|
||||
|
||||
use crate::{
|
||||
extensions::{FindFrom, ReplaceInplace},
|
||||
types::LineType,
|
||||
};
|
||||
use crate::{extensions::FindFrom, types::LineType};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq)]
|
||||
/// Represents a snippet from a page of a specific highlighting class.
|
||||
|
|
@ -98,9 +95,9 @@ where
|
|||
}
|
||||
LineType::Description(text) => process_snippet(PageSnippet::Description(&text))?,
|
||||
LineType::ExampleText(text) => process_snippet(PageSnippet::Text(&text))?,
|
||||
LineType::ExampleCode(mut text) => {
|
||||
LineType::ExampleCode(text) => {
|
||||
process_snippet(PageSnippet::NormalCode(" "))?;
|
||||
highlight_code(&command, &mut text, process_snippet)?;
|
||||
highlight_code(&command, text, process_snippet)?;
|
||||
process_snippet(PageSnippet::Linebreak)?;
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +111,7 @@ where
|
|||
/// Highlight code examples including user variables in {{ curly braces }}.
|
||||
fn highlight_code<E>(
|
||||
command: &str,
|
||||
mut text: &mut str,
|
||||
mut text: String,
|
||||
process_snippet: &mut impl FnMut(PageSnippet<&str>) -> Result<(), E>,
|
||||
) -> Result<(), E> {
|
||||
fn find_marker(s: &str, marker: &str, forbidden_prefix: &str) -> Option<usize> {
|
||||
|
|
@ -142,39 +139,36 @@ fn highlight_code<E>(
|
|||
}
|
||||
}
|
||||
|
||||
fn replace_escaped(s: &mut str) -> &str {
|
||||
s.replace_inplace(r"\{\{", "{{")
|
||||
.replace_inplace(r"\}\}", "}}")
|
||||
}
|
||||
// NOTE: This is not optimal, as it allocates one String for each `replace`
|
||||
let replace_escaped = |s: &str| s.replace(r"\{\{", "{{").replace(r"\}\}", "}}");
|
||||
|
||||
while !text.is_empty() {
|
||||
let Some(placeholder_start) = find_marker(&text, "{{", r"\{\{") else {
|
||||
return highlight_code_segment(command, replace_escaped(text), process_snippet);
|
||||
return highlight_code_segment(command, &replace_escaped(&text), process_snippet);
|
||||
};
|
||||
let Some(mut placeholder_len) = find_marker(&text[placeholder_start..], "}}", r"\}\}")
|
||||
let Some(mut placeholder_end) = find_marker(&text[placeholder_start + 2..], "}}", r"\}\}")
|
||||
else {
|
||||
return highlight_code_segment(command, replace_escaped(text), process_snippet);
|
||||
return highlight_code_segment(command, &replace_escaped(&text), process_snippet);
|
||||
};
|
||||
placeholder_len += 2;
|
||||
placeholder_end += placeholder_start + 2;
|
||||
|
||||
// Greedily extend matched range
|
||||
while text.as_bytes().get(placeholder_start + placeholder_len) == Some(&b'}') {
|
||||
placeholder_len += 1;
|
||||
while placeholder_end + 2 < text.len() && text.as_bytes()[placeholder_end + 2] == b'}' {
|
||||
placeholder_end += 1;
|
||||
}
|
||||
|
||||
let (segment, placeholder_and_rest) = text.split_at_mut(placeholder_start);
|
||||
let (placeholder, rest) = placeholder_and_rest.split_at_mut(placeholder_len);
|
||||
let placeholder_without_markers = &mut placeholder[2..placeholder_len - 2];
|
||||
let placeholder_content = &text[placeholder_start + 2..placeholder_end];
|
||||
|
||||
if !segment.is_empty() {
|
||||
highlight_code_segment(command, replace_escaped(segment), process_snippet)?;
|
||||
if placeholder_start > 0 {
|
||||
highlight_code_segment(
|
||||
command,
|
||||
&replace_escaped(&text[..placeholder_start]),
|
||||
process_snippet,
|
||||
)?;
|
||||
}
|
||||
process_snippet(PageSnippet::Variable(&replace_escaped(placeholder_content)))?;
|
||||
|
||||
process_snippet(PageSnippet::Variable(replace_escaped(
|
||||
placeholder_without_markers,
|
||||
)))?;
|
||||
|
||||
text = rest;
|
||||
text.replace_range(..placeholder_end + 2, "");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -264,7 +258,7 @@ mod tests {
|
|||
Ok::<(), ()>(())
|
||||
};
|
||||
|
||||
highlight_code(cmd, &mut segment.to_string(), &mut process_snippet)
|
||||
highlight_code(cmd, segment.to_string(), &mut process_snippet)
|
||||
.expect("highlight code segment failed");
|
||||
yielded
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue