nanorust 0.1.0

A terminal-based text editor written in Rust, inspired by GNU nano
Documentation
use crate::buffer::Buffer;

/// Search engine for finding text in the buffer
pub struct SearchEngine {
    /// The search query string
    query: String,
    /// The last match position (row, col)
    last_match: Option<(usize, usize)>,
    /// Whether the search is case-sensitive
    case_sensitive: bool,
}

impl SearchEngine {
    /// Create a new search engine with the given query
    pub fn new(query: String, case_sensitive: bool) -> Self {
        Self {
            query,
            last_match: None,
            case_sensitive,
        }
    }

    /// Find the next occurrence of the query starting from the cursor position
    /// 
    /// Returns Some((row, col)) if a match is found, None otherwise
    pub fn find_next(&mut self, buffer: &Buffer) -> Option<(usize, usize)> {
        let cursor = buffer.cursor();
        let lines = buffer.lines();
        
        if self.query.is_empty() {
            return None;
        }

        // Prepare query for comparison
        let query = if self.case_sensitive {
            self.query.clone()
        } else {
            self.query.to_lowercase()
        };

        // Start searching from current cursor position
        let start_row = cursor.row;
        let start_col = cursor.col;

        // First, search the rest of the current line
        if start_row < lines.len() {
            let line = &lines[start_row];
            let search_from = start_col;
            
            let line_to_search = if self.case_sensitive {
                line[search_from..].to_string()
            } else {
                line[search_from..].to_lowercase()
            };

            if let Some(pos) = line_to_search.find(&query) {
                let match_pos = (start_row, search_from + pos);
                self.last_match = Some(match_pos);
                return Some(match_pos);
            }
        }

        // Search subsequent lines
        for row in (start_row + 1)..lines.len() {
            let line = &lines[row];
            
            let line_to_search = if self.case_sensitive {
                line.clone()
            } else {
                line.to_lowercase()
            };

            if let Some(col) = line_to_search.find(&query) {
                let match_pos = (row, col);
                self.last_match = Some(match_pos);
                return Some(match_pos);
            }
        }

        // Wrap around: search from beginning to cursor position
        for row in 0..=start_row {
            let line = &lines[row];
            let search_until = if row == start_row { start_col } else { line.len() };
            
            let line_to_search = if self.case_sensitive {
                line[..search_until].to_string()
            } else {
                line[..search_until].to_lowercase()
            };

            if let Some(col) = line_to_search.find(&query) {
                let match_pos = (row, col);
                self.last_match = Some(match_pos);
                return Some(match_pos);
            }
        }

        None
    }

    /// Find the previous occurrence of the query starting from the cursor position
    /// 
    /// Returns Some((row, col)) if a match is found, None otherwise
    pub fn find_prev(&mut self, buffer: &Buffer) -> Option<(usize, usize)> {
        let cursor = buffer.cursor();
        let lines = buffer.lines();
        
        if self.query.is_empty() {
            return None;
        }

        // Prepare query for comparison
        let query = if self.case_sensitive {
            self.query.clone()
        } else {
            self.query.to_lowercase()
        };

        let start_row = cursor.row;
        let start_col = cursor.col;

        // First, search backwards in the current line before cursor
        if start_row < lines.len() && start_col > 0 {
            let line = &lines[start_row];
            let search_in = &line[..start_col];
            
            let line_to_search = if self.case_sensitive {
                search_in.to_string()
            } else {
                search_in.to_lowercase()
            };

            if let Some(pos) = line_to_search.rfind(&query) {
                let match_pos = (start_row, pos);
                self.last_match = Some(match_pos);
                return Some(match_pos);
            }
        }

        // Search previous lines backwards
        if start_row > 0 {
            for row in (0..start_row).rev() {
                let line = &lines[row];
                
                let line_to_search = if self.case_sensitive {
                    line.clone()
                } else {
                    line.to_lowercase()
                };

                if let Some(col) = line_to_search.rfind(&query) {
                    let match_pos = (row, col);
                    self.last_match = Some(match_pos);
                    return Some(match_pos);
                }
            }
        }

        // Wrap around: search from end backwards to cursor position
        for row in (start_row..lines.len()).rev() {
            let line = &lines[row];
            let search_from = if row == start_row { start_col } else { 0 };
            
            let line_to_search = if self.case_sensitive {
                line[search_from..].to_string()
            } else {
                line[search_from..].to_lowercase()
            };

            if let Some(pos) = line_to_search.rfind(&query) {
                let match_pos = (row, search_from + pos);
                self.last_match = Some(match_pos);
                return Some(match_pos);
            }
        }

        None
    }

    /// Get the query string
    pub fn query(&self) -> &str {
        &self.query
    }

    /// Get the last match position
    pub fn last_match(&self) -> Option<(usize, usize)> {
        self.last_match
    }
}