diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index a4474f9..1eb915e 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -1189,54 +1189,30 @@ impl GraphicalReportHandler { .read_span(context_span, self.context_lines, self.context_lines) .map_err(|_| fmt::Error)?; let context = std::str::from_utf8(context_data.data()).expect("Bad utf8 detected"); - let mut line = context_data.line(); - let mut column = context_data.column(); - let mut offset = context_data.span().offset(); - let mut line_offset = offset; - let mut iter = context.chars().peekable(); - let mut line_str = String::new(); - let mut lines = Vec::new(); - while let Some(char) = iter.next() { - offset += char.len_utf8(); - let mut at_end_of_file = false; - match char { - '\r' => { - if iter.next_if_eq(&'\n').is_some() { - offset += 1; - line += 1; - column = 0; - } else { - line_str.push(char); - column += 1; - } - at_end_of_file = iter.peek().is_none(); + let lines = context + .split_inclusive('\n') + .enumerate() + .map(|(line_number, line)| { + let length = line.len(); + // Strip the newline chars + let line = line + .strip_suffix('\n') + .and_then(|line| line.strip_suffix('\r').or(Some(line))) + .unwrap_or(line); + // SAFETY: + // - it is safe to use `offset_from` on slices of an array per Rus design (max array size) + // (https://doc.rust-lang.org/stable/reference/types/numeric.html#machine-dependent-integer-types) + // - since `line` is a slice of `context`, the offset cannot be negative either + Line { + line_number: context_data.line() + line_number + 1, + offset: context_data.span().offset() + + unsafe { line.as_ptr().offset_from(context.as_ptr()) } as usize, + length, + text: line.to_string(), } - '\n' => { - at_end_of_file = iter.peek().is_none(); - line += 1; - column = 0; - } - _ => { - line_str.push(char); - column += 1; - } - } + }) + .collect::>(); - if iter.peek().is_none() && !at_end_of_file { - line += 1; - } - - if column == 0 || iter.peek().is_none() { - lines.push(Line { - line_number: line, - offset: line_offset, - length: offset - line_offset, - text: line_str.clone(), - }); - line_str.clear(); - line_offset = offset; - } - } Ok((context_data, lines)) } } diff --git a/src/handlers/narratable.rs b/src/handlers/narratable.rs index c809124..418c899 100644 --- a/src/handlers/narratable.rs +++ b/src/handlers/narratable.rs @@ -291,54 +291,34 @@ impl NarratableReportHandler { .read_span(context_span, self.context_lines, self.context_lines) .map_err(|_| fmt::Error)?; let context = std::str::from_utf8(context_data.data()).expect("Bad utf8 detected"); - let mut line = context_data.line(); - let mut column = context_data.column(); - let mut offset = context_data.span().offset(); - let mut line_offset = offset; - let mut iter = context.chars().peekable(); - let mut line_str = String::new(); - let mut lines = Vec::new(); - while let Some(char) = iter.next() { - offset += char.len_utf8(); - let mut at_end_of_file = false; - match char { - '\r' => { - if iter.next_if_eq(&'\n').is_some() { - offset += 1; - line += 1; - column = 0; - } else { - line_str.push(char); - column += 1; - } - at_end_of_file = iter.peek().is_none(); - } - '\n' => { - at_end_of_file = iter.peek().is_none(); - line += 1; - column = 0; - } - _ => { - line_str.push(char); - column += 1; - } - } - if iter.peek().is_none() && !at_end_of_file { - line += 1; - } - - if column == 0 || iter.peek().is_none() { - lines.push(Line { - line_number: line, - offset: line_offset, - text: line_str.clone(), + let lines = context + .split_inclusive('\n') + .enumerate() + .map(|(line_number, line)| { + // SAFETY: + // - it is safe to use `offset_from` on slices of an array per Rus design (max array size) + // (https://doc.rust-lang.org/stable/reference/types/numeric.html#machine-dependent-integer-types) + // - since `line` is a slice of `context`, the offset cannot be negative either + let offset = unsafe { line.as_ptr().offset_from(context.as_ptr()) } as usize; + let length = line.len(); + // Strip the newline chars + let line = line + .strip_suffix('\n') + .and_then(|line| line.strip_suffix('\r').or(Some(line))) + .unwrap_or(line); + // End of the "file" if the end of the line is also the end of + // the context and we removed some characters (newline) + let at_end_of_file = (offset + length == context.len()) && (length != line.len()); + Line { + line_number: context_data.line() + line_number + 1, + offset: context_data.span().offset() + offset, + text: line.to_string(), at_end_of_file, - }); - line_str.clear(); - line_offset = offset; - } - } + } + }) + .collect::>(); + Ok((context_data, lines)) } }