/// Count the number of decimal digits in `n`. fn count_digits(mut n: usize) -> usize { letmut count = 0; while n != 0 {
count += 1;
n /= 10; // remove last digit
}
count
}
impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> { fn get_or_insert_line(
&mutself,
line_index: usize,
line_range: Range<usize>,
line_number: usize,
) -> &mut Line<'diagnostic> { self.lines.entry(line_index).or_insert_with(|| Line {
range: line_range,
number: line_number,
single_labels: vec![],
multi_labels: vec![], // This has to be false by default so we know if it must be rendered by another condition already.
must_render: false,
})
}
}
struct Line<'diagnostic> {
number: usize,
range: std::ops::Range<usize>, // TODO: How do we reuse these allocations?
single_labels: Vec<SingleLabel<'diagnostic>>,
multi_labels: Vec<(usize, LabelStyle, MultiLabel<'diagnostic>)>,
must_render: bool,
}
// TODO: Make this data structure external, to allow for allocation reuse letmut labeled_files = Vec::<LabeledFile<'_, _>>::new(); // Keep track of the outer padding to use when rendering the // snippets of source code. letmut outer_padding = 0;
// Group labels by file for label in &self.diagnostic.labels { let start_line_index = files.line_index(label.file_id, label.range.start)?; let start_line_number = files.line_number(label.file_id, start_line_index)?; let start_line_range = files.line_range(label.file_id, start_line_index)?; let end_line_index = files.line_index(label.file_id, label.range.end)?; let end_line_number = files.line_number(label.file_id, end_line_index)?; let end_line_range = files.line_range(label.file_id, end_line_index)?;
// NOTE: This could be made more efficient by using an associative // data structure like a hashmap or B-tree, but we use a vector to // preserve the order that unique files appear in the list of labels. let labeled_file = match labeled_files
.iter_mut()
.find(|labeled_file| label.file_id == labeled_file.file_id)
{
Some(labeled_file) => { // another diagnostic also referenced this file if labeled_file.max_label_style > label.style
|| (labeled_file.max_label_style == label.style
&& labeled_file.start > label.range.start)
{ // this label has a higher style or has the same style but starts earlier
labeled_file.start = label.range.start;
labeled_file.location = files.location(label.file_id, label.range.start)?;
labeled_file.max_label_style = label.style;
}
labeled_file
}
None => { // no other diagnostic referenced this file yet
labeled_files.push(LabeledFile {
file_id: label.file_id,
start: label.range.start,
name: files.name(label.file_id)?.to_string(),
location: files.location(label.file_id, label.range.start)?,
num_multi_labels: 0,
lines: BTreeMap::new(),
max_label_style: label.style,
}); // this unwrap should never fail because we just pushed an element
labeled_files
.last_mut()
.expect("just pushed an element that disappeared")
}
};
if start_line_index == end_line_index { // Single line // // ```text // 2 │ (+ test "") // │ ^^ expected `Int` but found `String` // ``` let label_start = label.range.start - start_line_range.start; // Ensure that we print at least one caret, even when we // have a zero-length source range. let label_end =
usize::max(label.range.end - start_line_range.start, label_start + 1);
let line = labeled_file.get_or_insert_line(
start_line_index,
start_line_range,
start_line_number,
);
// Ensure that the single line labels are lexicographically // sorted by the range of source code that they cover. let index = match line.single_labels.binary_search_by(|(_, range, _)| { // `Range<usize>` doesn't implement `Ord`, so convert to `(usize, usize)` // to piggyback off its lexicographic comparison implementation.
(range.start, range.end).cmp(&(label_start, label_end))
}) { // If the ranges are the same, order the labels in reverse // to how they were originally specified in the diagnostic. // This helps with printing in the renderer.
Ok(index) | Err(index) => index,
};
// The line should be rendered to match the configuration of how much context to show.
line.must_render |= // Is this line part of the context after the start of the label?
line_index - start_line_index <= self.config.start_context_lines
|| // Is this line part of the context before the end of the label?
end_line_index - line_index <= self.config.end_context_lines;
}
// Last labeled line // // ```text // 8 │ │ _ _ => num // │ ╰──────────────^ `case` clauses have incompatible types // ``` let label_end = label.range.end - end_line_range.start;
let end_line = labeled_file.get_or_insert_line(
end_line_index,
end_line_range,
end_line_number,
);
// Check to see if we need to render any intermediate stuff // before rendering the next line. iflet Some((next_line_index, _)) = lines.peek() { match next_line_index.checked_sub(*line_index) { // Consecutive lines
Some(1) => {} // One line between the current line and the next line
Some(2) => { // Write a source line let file_id = labeled_file.file_id;
// This line was not intended to be rendered initially. // To render the line right, we have to get back the original labels. let labels = labeled_file
.lines
.get(&(line_index + 1))
.map_or(&[][..], |line| &line.multi_labels[..]);
renderer.render_snippet_source(
outer_padding,
files.line_number(file_id, line_index + 1)?,
&source[files.line_range(file_id, line_index + 1)?], self.diagnostic.severity,
&[],
labeled_file.num_multi_labels,
labels,
)?;
} // More than one line between the current line and the next line.
Some(_) | None => { // Source break // // ```text // · // ```
renderer.render_snippet_break(
outer_padding, self.diagnostic.severity,
labeled_file.num_multi_labels,
&line.multi_labels,
)?;
}
}
}
}
// Check to see if we should render a trailing border after the // final line of the snippet. if labeled_files.peek().is_none() && self.diagnostic.notes.is_empty() { // We don't render a border if we are at the final newline // without trailing notes, because it would end up looking too // spaced-out in combination with the final new line.
} else { // Render the trailing snippet border.
renderer.render_snippet_empty(
outer_padding, self.diagnostic.severity,
labeled_file.num_multi_labels,
&[],
)?;
}
}
// Additional notes // // ```text // = expected type `Int` // found type `String` // ``` for note in &self.diagnostic.notes {
renderer.render_snippet_note(outer_padding, note)?;
}
renderer.render_empty()
}
}
/// Output a short diagnostic, with a line number, severity, and message. pubstruct ShortDiagnostic<'diagnostic, FileId> {
diagnostic: &'diagnostic Diagnostic<FileId>,
show_notes: bool,
}
// Fallback to printing a non-located header if no primary labels were encountered // // ```text // error[E0002]: Bad config found // ``` if primary_labels_encountered == 0 {
renderer.render_header(
None, self.diagnostic.severity, self.diagnostic.code.as_deref(), self.diagnostic.message.as_str(),
)?;
}
ifself.show_notes { // Additional notes // // ```text // = expected type `Int` // found type `String` // ``` for note in &self.diagnostic.notes {
renderer.render_snippet_note(0, note)?;
}
}
Ok(())
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.