Hide lines in code block (#1453)
* hide lines * test hide ines * add documentation * fix test * factor shared code with hl_lines
This commit is contained in:
parent
5365efebb3
commit
b244bcdfbb
|
@ -17,6 +17,8 @@ pub struct CodeBlock<'config> {
|
||||||
|
|
||||||
/// List of ranges of lines to highlight.
|
/// List of ranges of lines to highlight.
|
||||||
highlight_lines: Vec<Range>,
|
highlight_lines: Vec<Range>,
|
||||||
|
/// List of ranges of lines to hide.
|
||||||
|
hide_lines: Vec<Range>,
|
||||||
/// The number of lines in the code block being processed.
|
/// The number of lines in the code block being processed.
|
||||||
num_lines: usize,
|
num_lines: usize,
|
||||||
}
|
}
|
||||||
|
@ -52,6 +54,7 @@ impl<'config> CodeBlock<'config> {
|
||||||
theme,
|
theme,
|
||||||
|
|
||||||
highlight_lines: fence_info.highlight_lines,
|
highlight_lines: fence_info.highlight_lines,
|
||||||
|
hide_lines: fence_info.hide_lines,
|
||||||
num_lines: 0,
|
num_lines: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,6 +80,9 @@ impl<'config> CodeBlock<'config> {
|
||||||
let hl_lines = self.get_highlighted_lines();
|
let hl_lines = self.get_highlighted_lines();
|
||||||
color_highlighted_lines(&mut highlighted, &hl_lines, hl_background);
|
color_highlighted_lines(&mut highlighted, &hl_lines, hl_background);
|
||||||
|
|
||||||
|
let hide_lines = self.get_hidden_lines();
|
||||||
|
let highlighted = hide_hidden_lines(highlighted, &hide_lines);
|
||||||
|
|
||||||
styled_line_to_highlighted_html(&highlighted, self.background)
|
styled_line_to_highlighted_html(&highlighted, self.background)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,8 +100,16 @@ impl<'config> CodeBlock<'config> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_highlighted_lines(&self) -> HashSet<usize> {
|
fn get_highlighted_lines(&self) -> HashSet<usize> {
|
||||||
|
self.ranges_to_lines(&self.highlight_lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hidden_lines(&self) -> HashSet<usize> {
|
||||||
|
self.ranges_to_lines(&self.hide_lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ranges_to_lines(&self, range: &Vec<Range>) -> HashSet<usize> {
|
||||||
let mut lines = HashSet::new();
|
let mut lines = HashSet::new();
|
||||||
for range in &self.highlight_lines {
|
for range in range {
|
||||||
for line in range.from..=min(range.to, self.num_lines) {
|
for line in range.from..=min(range.to, self.num_lines) {
|
||||||
// Ranges are one-indexed
|
// Ranges are one-indexed
|
||||||
lines.insert(line.saturating_sub(1));
|
lines.insert(line.saturating_sub(1));
|
||||||
|
@ -196,3 +210,29 @@ fn color_highlighted_lines(data: &mut [(Style, &str)], lines: &HashSet<usize>, b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hide_hidden_lines<'a>(
|
||||||
|
data: Vec<(Style, &'a str)>,
|
||||||
|
lines: &HashSet<usize>,
|
||||||
|
) -> Vec<(Style, &'a str)> {
|
||||||
|
if lines.is_empty() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_line = 0;
|
||||||
|
|
||||||
|
let mut to_keep = Vec::new();
|
||||||
|
|
||||||
|
for item in data {
|
||||||
|
if !lines.contains(¤t_line) {
|
||||||
|
to_keep.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We split the lines such that every newline is at the end of an item.
|
||||||
|
if item.1.ends_with('\n') {
|
||||||
|
current_line += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_keep
|
||||||
|
}
|
||||||
|
|
|
@ -28,16 +28,23 @@ pub struct FenceSettings<'a> {
|
||||||
pub language: Option<&'a str>,
|
pub language: Option<&'a str>,
|
||||||
pub line_numbers: bool,
|
pub line_numbers: bool,
|
||||||
pub highlight_lines: Vec<Range>,
|
pub highlight_lines: Vec<Range>,
|
||||||
|
pub hide_lines: Vec<Range>,
|
||||||
}
|
}
|
||||||
impl<'a> FenceSettings<'a> {
|
impl<'a> FenceSettings<'a> {
|
||||||
pub fn new(fence_info: &'a str) -> Self {
|
pub fn new(fence_info: &'a str) -> Self {
|
||||||
let mut me = Self { language: None, line_numbers: false, highlight_lines: Vec::new() };
|
let mut me = Self {
|
||||||
|
language: None,
|
||||||
|
line_numbers: false,
|
||||||
|
highlight_lines: Vec::new(),
|
||||||
|
hide_lines: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
for token in FenceIter::new(fence_info) {
|
for token in FenceIter::new(fence_info) {
|
||||||
match token {
|
match token {
|
||||||
FenceToken::Language(lang) => me.language = Some(lang),
|
FenceToken::Language(lang) => me.language = Some(lang),
|
||||||
FenceToken::EnableLineNumbers => me.line_numbers = true,
|
FenceToken::EnableLineNumbers => me.line_numbers = true,
|
||||||
FenceToken::HighlightLines(lines) => me.highlight_lines.extend(lines),
|
FenceToken::HighlightLines(lines) => me.highlight_lines.extend(lines),
|
||||||
|
FenceToken::HideLines(lines) => me.hide_lines.extend(lines),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +57,7 @@ enum FenceToken<'a> {
|
||||||
Language(&'a str),
|
Language(&'a str),
|
||||||
EnableLineNumbers,
|
EnableLineNumbers,
|
||||||
HighlightLines(Vec<Range>),
|
HighlightLines(Vec<Range>),
|
||||||
|
HideLines(Vec<Range>),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FenceIter<'a> {
|
struct FenceIter<'a> {
|
||||||
|
@ -59,6 +67,16 @@ impl<'a> FenceIter<'a> {
|
||||||
fn new(fence_info: &'a str) -> Self {
|
fn new(fence_info: &'a str) -> Self {
|
||||||
Self { split: fence_info.split(',') }
|
Self { split: fence_info.split(',') }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_ranges(token: Option<&str>) -> Vec<Range> {
|
||||||
|
let mut ranges = Vec::new();
|
||||||
|
for range in token.unwrap_or("").split(' ') {
|
||||||
|
if let Some(range) = Range::parse(range) {
|
||||||
|
ranges.push(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ranges
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for FenceIter<'a> {
|
impl<'a> Iterator for FenceIter<'a> {
|
||||||
|
@ -73,14 +91,13 @@ impl<'a> Iterator for FenceIter<'a> {
|
||||||
"" => continue,
|
"" => continue,
|
||||||
"linenos" => return Some(FenceToken::EnableLineNumbers),
|
"linenos" => return Some(FenceToken::EnableLineNumbers),
|
||||||
"hl_lines" => {
|
"hl_lines" => {
|
||||||
let mut ranges = Vec::new();
|
let ranges = Self::parse_ranges(tok_split.next());
|
||||||
for range in tok_split.next().unwrap_or("").split(' ') {
|
|
||||||
if let Some(range) = Range::parse(range) {
|
|
||||||
ranges.push(range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Some(FenceToken::HighlightLines(ranges));
|
return Some(FenceToken::HighlightLines(ranges));
|
||||||
}
|
}
|
||||||
|
"hide_lines" => {
|
||||||
|
let ranges = Self::parse_ranges(tok_split.next());
|
||||||
|
return Some(FenceToken::HideLines(ranges));
|
||||||
|
}
|
||||||
lang => {
|
lang => {
|
||||||
return Some(FenceToken::Language(lang));
|
return Some(FenceToken::Language(lang));
|
||||||
}
|
}
|
||||||
|
|
61
components/rendering/tests/codeblock_hide_lines.rs
Normal file
61
components/rendering/tests/codeblock_hide_lines.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use tera::Tera;
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
|
use front_matter::InsertAnchor;
|
||||||
|
use rendering::{render_content, RenderContext};
|
||||||
|
|
||||||
|
macro_rules! colored_html_line {
|
||||||
|
( $s:expr ) => {{
|
||||||
|
let mut result = "<span style=\"color:#c0c5ce;\">".to_string();
|
||||||
|
result.push_str($s);
|
||||||
|
result.push_str("\n</span>");
|
||||||
|
result
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! colored_html {
|
||||||
|
( $($s:expr),* $(,)* ) => {{
|
||||||
|
let mut result = "<pre style=\"background-color:#2b303b;\">\n<code>".to_string();
|
||||||
|
$(
|
||||||
|
result.push_str(colored_html_line!($s).as_str());
|
||||||
|
)*
|
||||||
|
result.push_str("</code></pre>");
|
||||||
|
result
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hide_lines_simple() {
|
||||||
|
let tera_ctx = Tera::default();
|
||||||
|
let permalinks_ctx = HashMap::new();
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.markdown.highlight_code = true;
|
||||||
|
let context = RenderContext::new(
|
||||||
|
&tera_ctx,
|
||||||
|
&config,
|
||||||
|
&config.default_language,
|
||||||
|
"",
|
||||||
|
&permalinks_ctx,
|
||||||
|
InsertAnchor::None,
|
||||||
|
);
|
||||||
|
let res = render_content(
|
||||||
|
r#"
|
||||||
|
```hide_lines=2
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
baz
|
||||||
|
bat
|
||||||
|
```
|
||||||
|
"#,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
res.body,
|
||||||
|
colored_html!(
|
||||||
|
"foo\nbaz\nbat",
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
|
@ -150,7 +150,6 @@ Here is a full list of supported languages and their short names:
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: due to some issues with the JavaScript syntax, the TypeScript syntax will be used instead.
|
Note: due to some issues with the JavaScript syntax, the TypeScript syntax will be used instead.
|
||||||
If
|
|
||||||
|
|
||||||
If you want to highlight a language not on this list, please open an issue or a pull request on the [Zola repo](https://github.com/getzola/zola).
|
If you want to highlight a language not on this list, please open an issue or a pull request on the [Zola repo](https://github.com/getzola/zola).
|
||||||
Alternatively, the `extra_syntaxes` configuration option can be used to add additional syntax files.
|
Alternatively, the `extra_syntaxes` configuration option can be used to add additional syntax files.
|
||||||
|
@ -173,3 +172,40 @@ If your site source is laid out as follows:
|
||||||
```
|
```
|
||||||
|
|
||||||
you would set your `extra_syntaxes` to `["syntaxes", "syntaxes/Sublime-Language1"]` to load `lang1.sublime-syntax` and `lang2.sublime-syntax`.
|
you would set your `extra_syntaxes` to `["syntaxes", "syntaxes/Sublime-Language1"]` to load `lang1.sublime-syntax` and `lang2.sublime-syntax`.
|
||||||
|
|
||||||
|
## Annotations
|
||||||
|
|
||||||
|
You can use additional annotations to customize how code blocks are displayed:
|
||||||
|
|
||||||
|
- `linenos` to enable line numbering.
|
||||||
|
````md
|
||||||
|
|
||||||
|
```rust,linenos
|
||||||
|
use highlighter::highlight;
|
||||||
|
let code = "...";
|
||||||
|
highlight(code);
|
||||||
|
```
|
||||||
|
|
||||||
|
````
|
||||||
|
- `hl_lines` to highlight lines. You must specify a list of ranges of lines to highlight,
|
||||||
|
separated by ` `. Ranges are 1-indexed.
|
||||||
|
````md
|
||||||
|
|
||||||
|
```rust,hl_lines=3
|
||||||
|
use highlighter::highlight;
|
||||||
|
let code = "...";
|
||||||
|
highlight(code);
|
||||||
|
```
|
||||||
|
|
||||||
|
````
|
||||||
|
- `hide_lines` to hide lines. You must specify a list of ranges of lines to hide,
|
||||||
|
separated by ` `. Ranges are 1-indexed.
|
||||||
|
````md
|
||||||
|
|
||||||
|
```rust,hide_lines=1-2
|
||||||
|
use highlighter::highlight;
|
||||||
|
let code = "...";
|
||||||
|
highlight(code);
|
||||||
|
```
|
||||||
|
|
||||||
|
````
|
||||||
|
|
Loading…
Reference in a new issue