diff --git a/src/chunk.rs b/src/chunk.rs index d23b709..69e9d14 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,7 +1,7 @@ use crate::grep::GrepMatch; use anyhow::Result; use encoding_rs::{Encoding, UTF_8}; -use memchr::memchr2; +use memchr::{memchr2, memchr_iter, Memchr}; use pathdiff::diff_paths; use std::cmp; use std::env; @@ -144,6 +144,62 @@ impl Files { } } +pub struct LinesInclusive<'a> { + lnum: usize, + prev: usize, + buf: &'a str, + iter: Memchr<'a>, +} + +impl<'a> LinesInclusive<'a> { + pub fn new(buf: &'a str) -> Self { + Self { + lnum: 1, + prev: 0, + buf, + iter: memchr_iter(b'\n', buf.as_bytes()), + } + } +} + +impl<'a> Iterator for LinesInclusive<'a> { + type Item = (&'a str, u64); + fn next(&mut self) -> Option { + if let Some(idx) = self.iter.next() { + let lnum = self.lnum; + let end = idx + 1; + let line = &self.buf[self.prev..end]; + self.prev = end; + self.lnum += 1; + Some((line, lnum as u64)) + } else if self.prev == self.buf.len() { + None + } else { + let line = &self.buf[self.prev..]; + self.prev = self.buf.len(); + Some((line, self.lnum as u64)) + } + } +} + +struct Lines<'a>(LinesInclusive<'a>); + +impl<'a> Lines<'a> { + pub fn new(buf: &'a str) -> Self { + Self(LinesInclusive::new(buf)) + } +} + +impl<'a> Iterator for Lines<'a> { + type Item = (&'a str, u64); + fn next(&mut self) -> Option { + let (line, lnum) = self.0.next()?; + let line = line.strip_suffix('\n').unwrap_or(line); + let line = line.strip_suffix('\r').unwrap_or(line); + Some((line, lnum)) + } +} + impl>> Files { fn calculate_chunk_range<'contents>( &self, @@ -225,7 +281,7 @@ impl>> Iterator for Files { Err(err) => return self.error_item(err.into()), }; // Assumes that matched lines are sorted by source location - let mut lines = contents.lines().enumerate().map(|(i, l)| (l, i as u64 + 1)); + let mut lines = Lines::new(&contents); let mut lmats = vec![LineMatch { line_number, ranges, diff --git a/src/syntect.rs b/src/syntect.rs index 382d10f..ecbe949 100644 --- a/src/syntect.rs +++ b/src/syntect.rs @@ -1,10 +1,9 @@ use crate::broken_pipe::IgnoreBrokenPipe as _; -use crate::chunk::File; +use crate::chunk::{File, LinesInclusive}; use crate::printer::{Printer, PrinterOptions, TermColorSupport, TextWrapMode}; use ansi_colours::ansi256_from_rgb; use anyhow::Result; use flate2::read::ZlibDecoder; -use memchr::{memchr_iter, Memchr}; use std::cmp; use std::io::{self, Stdout, StdoutLock, Write}; use std::ops::{Deref, DerefMut}; @@ -607,43 +606,6 @@ impl<'a> LineHighlighter<'a> { } } -// Like chunk::Lines, but includes newlines -struct LinesInclusive<'a> { - lnum: usize, - prev: usize, - buf: &'a str, - iter: Memchr<'a>, -} -impl<'a> LinesInclusive<'a> { - pub fn new(buf: &'a str) -> Self { - Self { - lnum: 1, - prev: 0, - buf, - iter: memchr_iter(b'\n', buf.as_bytes()), - } - } -} -impl<'a> Iterator for LinesInclusive<'a> { - type Item = (&'a str, u64); - fn next(&mut self) -> Option { - if let Some(idx) = self.iter.next() { - let lnum = self.lnum; - let end = idx + 1; - let line = &self.buf[self.prev..end]; - self.prev = end; - self.lnum += 1; - Some((line, lnum as u64)) - } else if self.prev == self.buf.len() { - None - } else { - let line = &self.buf[self.prev..]; - self.prev = self.buf.len(); - Some((line, self.lnum as u64)) - } - } -} - // Drawer is responsible for one-time screen drawing struct Drawer<'file, W: Write> { grid: bool,