//! Trait for converting `Snippet` to `DisplayList`.
use super::*;
-use crate::snippet;
+use crate::{formatter::get_term_style, snippet};
-fn format_label(label: Option<&str>, style: Option<DisplayTextStyle>) -> Vec<DisplayTextFragment> {
+struct CursorLines<'a>(&'a str);
+
+impl<'a> CursorLines<'a> {
+ fn new(src: &str) -> CursorLines<'_> {
+ CursorLines(src)
+ }
+}
+
+enum EndLine {
+ EOF = 0,
+ CRLF = 1,
+ LF = 2,
+}
+
+impl<'a> Iterator for CursorLines<'a> {
+ type Item = (&'a str, EndLine);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.0.is_empty() {
+ None
+ } else {
+ self.0
+ .find('\n')
+ .map(|x| {
+ let ret = if 0 < x {
+ if self.0.as_bytes()[x - 1] == b'\r' {
+ (&self.0[..x - 1], EndLine::LF)
+ } else {
+ (&self.0[..x], EndLine::CRLF)
+ }
+ } else {
+ ("", EndLine::CRLF)
+ };
+ self.0 = &self.0[x + 1..];
+ ret
+ })
+ .or_else(|| {
+ let ret = Some((&self.0[..], EndLine::EOF));
+ self.0 = "";
+ ret
+ })
+ }
+ }
+}
+
+fn format_label(
+ label: Option<&str>,
+ style: Option<DisplayTextStyle>,
+) -> Vec<DisplayTextFragment<'_>> {
let mut result = vec![];
if let Some(label) = label {
- let elements: Vec<&str> = label.split("__").collect();
- for (idx, element) in elements.iter().enumerate() {
+ for (idx, element) in label.split("__").enumerate() {
let element_style = match style {
Some(s) => s,
None => {
}
};
result.push(DisplayTextFragment {
- content: element.to_string(),
+ content: element,
style: element_style,
});
}
result
}
-fn format_title(annotation: &snippet::Annotation) -> DisplayLine {
- let label = annotation.label.clone().unwrap_or_default();
+fn format_title(annotation: snippet::Annotation<'_>) -> DisplayLine<'_> {
+ let label = annotation.label.unwrap_or_default();
DisplayLine::Raw(DisplayRawLine::Annotation {
annotation: Annotation {
annotation_type: DisplayAnnotationType::from(annotation.annotation_type),
- id: annotation.id.clone(),
+ id: annotation.id,
label: format_label(Some(&label), Some(DisplayTextStyle::Emphasis)),
},
source_aligned: false,
})
}
-fn format_annotation(annotation: &snippet::Annotation) -> Vec<DisplayLine> {
+fn format_annotation(annotation: snippet::Annotation<'_>) -> Vec<DisplayLine<'_>> {
let mut result = vec![];
- let label = annotation.label.clone().unwrap_or_default();
+ let label = annotation.label.unwrap_or_default();
for (i, line) in label.lines().enumerate() {
result.push(DisplayLine::Raw(DisplayRawLine::Annotation {
annotation: Annotation {
result
}
-fn format_slice(slice: &snippet::Slice, is_first: bool, has_footer: bool) -> Vec<DisplayLine> {
+fn format_slice(
+ mut slice: snippet::Slice<'_>,
+ is_first: bool,
+ has_footer: bool,
+) -> Vec<DisplayLine<'_>> {
+ let main_range = slice.annotations.get(0).map(|x| x.range.0);
+ let row = slice.line_start;
+ let origin = slice.origin.take();
let mut body = format_body(slice, has_footer);
+ let header = format_header(origin, main_range, row, &body, is_first);
let mut result = vec![];
- let header = format_header(slice, &body, is_first);
if let Some(header) = header {
result.push(header);
}
result
}
-fn format_header(
- slice: &snippet::Slice,
- body: &[DisplayLine],
+fn format_header<'a>(
+ origin: Option<&'a str>,
+ main_range: Option<usize>,
+ mut row: usize,
+ body: &[DisplayLine<'_>],
is_first: bool,
-) -> Option<DisplayLine> {
- let main_annotation = slice.annotations.get(0);
-
+) -> Option<DisplayLine<'a>> {
let display_header = if is_first {
DisplayHeaderType::Initial
} else {
DisplayHeaderType::Continuation
};
- if let Some(annotation) = main_annotation {
+ if let Some(main_range) = main_range {
let mut col = 1;
- let mut row = slice.line_start;
- for item in body.iter() {
+ for item in body {
if let DisplayLine::Source {
line: DisplaySourceLine::Content { range, .. },
..
} = item
{
- if annotation.range.0 >= range.0 && annotation.range.0 <= range.1 {
- col = annotation.range.0 - range.0;
+ if main_range >= range.0 && main_range <= range.1 {
+ col = main_range - range.0 + 1;
break;
}
row += 1;
}
}
- if let Some(ref path) = slice.origin {
+ if let Some(path) = origin {
return Some(DisplayLine::Raw(DisplayRawLine::Origin {
- path: path.to_string(),
+ path,
pos: Some((row, col)),
header_type: display_header,
}));
}
}
- if let Some(ref path) = slice.origin {
+ if let Some(path) = origin {
return Some(DisplayLine::Raw(DisplayRawLine::Origin {
- path: path.to_string(),
+ path,
pos: None,
header_type: display_header,
}));
None
}
-fn fold_body(body: &[DisplayLine]) -> Vec<DisplayLine> {
- let mut new_body = vec![];
+fn fold_body(mut body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
+ enum Line {
+ Fold(usize),
+ Source(usize),
+ };
+ let mut lines = vec![];
let mut no_annotation_lines_counter = 0;
- let mut idx = 0;
- while idx < body.len() {
- match body[idx] {
+ for (idx, line) in body.iter().enumerate() {
+ match line {
DisplayLine::Source {
line: DisplaySourceLine::Annotation { .. },
- ref inline_marks,
..
} => {
if no_annotation_lines_counter > 2 {
} else {
1
};
- for item in body.iter().take(fold_start + pre_len).skip(fold_start) {
- new_body.push(item.clone());
+ for (i, _) in body
+ .iter()
+ .enumerate()
+ .take(fold_start + pre_len)
+ .skip(fold_start)
+ {
+ lines.push(Line::Source(i));
}
- new_body.push(DisplayLine::Fold {
- inline_marks: inline_marks.clone(),
- });
- for item in body.iter().take(fold_end).skip(fold_end - post_len) {
- new_body.push(item.clone());
+ lines.push(Line::Fold(idx));
+ for (i, _) in body
+ .iter()
+ .enumerate()
+ .take(fold_end)
+ .skip(fold_end - post_len)
+ {
+ lines.push(Line::Source(i));
}
} else {
let start = idx - no_annotation_lines_counter;
- for item in body.iter().take(idx).skip(start) {
- new_body.push(item.clone());
+ for (i, _) in body.iter().enumerate().take(idx).skip(start) {
+ lines.push(Line::Source(i));
}
}
no_annotation_lines_counter = 0;
}
DisplayLine::Source { .. } => {
no_annotation_lines_counter += 1;
- idx += 1;
continue;
}
_ => {
no_annotation_lines_counter += 1;
}
}
- new_body.push(body[idx].clone());
- idx += 1;
+ lines.push(Line::Source(idx));
+ }
+
+ let mut new_body = vec![];
+ let mut removed = 0;
+ for line in lines {
+ match line {
+ Line::Source(i) => {
+ new_body.push(body.remove(i - removed));
+ removed += 1;
+ }
+ Line::Fold(i) => {
+ if let DisplayLine::Source {
+ line: DisplaySourceLine::Annotation { .. },
+ ref inline_marks,
+ ..
+ } = body.get(i - removed).unwrap()
+ {
+ new_body.push(DisplayLine::Fold {
+ inline_marks: inline_marks.clone(),
+ })
+ } else {
+ unreachable!()
+ }
+ }
+ }
}
new_body
}
-fn format_body(slice: &snippet::Slice, has_footer: bool) -> Vec<DisplayLine> {
- let mut body = vec![];
+fn format_body(slice: snippet::Slice<'_>, has_footer: bool) -> Vec<DisplayLine<'_>> {
+ let source_len = slice.source.chars().count();
+ if let Some(bigger) = slice.annotations.iter().find_map(|x| {
+ if source_len < x.range.1 {
+ Some(x.range)
+ } else {
+ None
+ }
+ }) {
+ panic!(
+ "SourceAnnotation range `{:?}` is bigger than source length `{}`",
+ bigger, source_len
+ )
+ }
+ let mut body = vec![];
let mut current_line = slice.line_start;
let mut current_index = 0;
let mut line_index_ranges = vec![];
- for line in slice.source.lines() {
- let line_length = line.chars().count() + 1;
+ for (line, end_line) in CursorLines::new(slice.source) {
+ let line_length = line.chars().count();
let line_range = (current_index, current_index + line_length);
body.push(DisplayLine::Source {
lineno: Some(current_line),
inline_marks: vec![],
line: DisplaySourceLine::Content {
- text: line.to_string(),
+ text: line,
range: line_range,
},
});
line_index_ranges.push(line_range);
current_line += 1;
- current_index += line_length + 1;
+ current_index += line_length + end_line as usize;
}
let mut annotation_line_count = 0;
- let mut annotations = slice.annotations.clone();
- for idx in 0..body.len() {
- let (line_start, line_end) = line_index_ranges[idx];
+ let mut annotations = slice.annotations;
+ for (idx, (line_start, line_end)) in line_index_ranges.into_iter().enumerate() {
// It would be nice to use filter_drain here once it's stable.
annotations = annotations
.into_iter()
};
match annotation.range {
(start, _) if start > line_end => true,
- (start, end) if start >= line_start && end <= line_end + 1 => {
+ (start, end)
+ if start >= line_start && end <= line_end
+ || start == line_end && end - start <= 1 =>
+ {
let range = (start - line_start, end - line_start);
body.insert(
body_idx + 1,
),
});
}
+
let range = (end - line_start, end - line_start + 1);
body.insert(
body_idx + 1,
}
if slice.fold {
- body = fold_body(&body);
+ body = fold_body(body);
}
body.insert(
body
}
-impl From<snippet::Snippet> for DisplayList {
- fn from(snippet: snippet::Snippet) -> Self {
+impl<'a> From<snippet::Snippet<'a>> for DisplayList<'a> {
+ fn from(
+ snippet::Snippet {
+ title,
+ footer,
+ slices,
+ opt,
+ }: snippet::Snippet<'a>,
+ ) -> DisplayList<'a> {
let mut body = vec![];
- if let Some(annotation) = snippet.title {
- body.push(format_title(&annotation));
+ if let Some(annotation) = title {
+ body.push(format_title(annotation));
}
- for (idx, slice) in snippet.slices.iter().enumerate() {
- body.append(&mut format_slice(
- &slice,
- idx == 0,
- !snippet.footer.is_empty(),
- ));
+ for (idx, slice) in slices.into_iter().enumerate() {
+ body.append(&mut format_slice(slice, idx == 0, !footer.is_empty()));
}
- for annotation in snippet.footer {
- body.append(&mut format_annotation(&annotation));
+ for annotation in footer {
+ body.append(&mut format_annotation(annotation));
}
- Self { body }
+ let FormatOptions {
+ color,
+ anonymized_line_numbers,
+ } = opt;
+
+ Self {
+ body,
+ stylesheet: get_term_style(color),
+ anonymized_line_numbers,
+ }
}
}