towl 0.3.7

A fast CLI tool to scan codebases for TODO comments and output them in multiple formats
Documentation
use super::types::Parser;

const BACKWARD_SEARCH_LINES: usize = 50;
const FORWARD_SEARCH_LINES: usize = 3;

impl Parser {
    pub(super) fn extract_context(&self, lines: &[&str], current_line: usize) -> Vec<String> {
        let mut context = Vec::new();

        let start = current_line.saturating_sub(self.context_lines);

        let end = std::cmp::min(current_line + self.context_lines + 1, lines.len());

        for (i, line) in lines.iter().enumerate().take(end).skip(start) {
            if i != current_line {
                context.push(format!("{}: {}", i + 1, line));
            }
        }

        context
    }

    pub(super) fn match_function_name<'a>(&self, line: &'a str) -> Option<&'a str> {
        for pattern in &self.function_patterns {
            if let Some(captures) = pattern.captures(line) {
                for j in 1..captures.len() {
                    if let Some(name) = captures.get(j) {
                        let name_str = name.as_str();
                        if !name_str.is_empty()
                            && name_str.chars().all(|c| c.is_alphanumeric() || c == '_')
                        {
                            return Some(name_str);
                        }
                    }
                }
            }
        }
        None
    }

    pub(super) fn find_function_context(
        &self,
        lines: &[&str],
        current_line: usize,
    ) -> Option<String> {
        let search_start = current_line.saturating_sub(BACKWARD_SEARCH_LINES);
        for i in (search_start..=current_line).rev() {
            if let Some(name) = self.match_function_name(lines[i]) {
                return Some(format!("{name}:{}", i + 1));
            }
        }

        let search_end = std::cmp::min(current_line + FORWARD_SEARCH_LINES + 1, lines.len());
        for (i, line) in lines
            .iter()
            .enumerate()
            .take(search_end)
            .skip(current_line + 1)
        {
            if let Some(name) = self.match_function_name(line) {
                return Some(format!("{name}:{} (below)", i + 1));
            }
        }

        None
    }
}