eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
//! Help system for in-app documentation.
//!
//! Provides context-aware help topics and search functionality
//! to assist users in navigating the application.

use std::collections::HashMap;

/// A help topic.
#[derive(Debug, Clone)]
pub struct HelpTopic {
    /// Unique ID
    pub id: String,
    /// Title of the topic
    pub title: String,
    /// Content of the topic (markdown supported)
    pub content: String,
    /// Related keybindings (optional)
    pub keybinds: Vec<String>,
    /// Tags for search
    pub tags: Vec<String>,
}

/// Help system manager.
pub struct HelpSystem {
    /// Help topics by ID
    topics: HashMap<String, HelpTopic>,
    /// Search index (keyword -> topic IDs)
    index: HashMap<String, Vec<String>>,
}

impl HelpSystem {
    /// Create a new help system.
    pub fn new() -> Self {
        Self {
            topics: HashMap::new(),
            index: HashMap::new(),
        }
    }
    
    /// Register a help topic.
    pub fn register_topic(&mut self, topic: HelpTopic) {
        // Index the topic
        let mut keywords = Vec::new();
        keywords.push(topic.id.to_lowercase());
        keywords.push(topic.title.to_lowercase());
        for tag in &topic.tags {
            keywords.push(tag.to_lowercase());
        }
        
        // Simple tokenization for content
        for word in topic.content.split_whitespace() {
            if word.len() > 3 {
                keywords.push(word.to_lowercase());
            }
        }
        
        for keyword in keywords {
            self.index
                .entry(keyword)
                .or_insert_with(Vec::new)
                .push(topic.id.clone());
        }
        
        self.topics.insert(topic.id.clone(), topic);
    }
    
    /// Get a topic by ID.
    pub fn get_topic(&self, id: &str) -> Option<&HelpTopic> {
        self.topics.get(id)
    }
    
    /// Search for topics.
    pub fn search(&self, query: &str) -> Vec<&HelpTopic> {
        let query = query.to_lowercase();
        let mut results = Vec::new();
        let mut seen = std::collections::HashSet::new();
        
        // Exact matches in index
        if let Some(ids) = self.index.get(&query) {
            for id in ids {
                if seen.insert(id) {
                    if let Some(topic) = self.topics.get(id) {
                        results.push(topic);
                    }
                }
            }
        }
        
        // Partial matches
        for (keyword, ids) in &self.index {
            if keyword.contains(&query) {
                for id in ids {
                    if seen.insert(id) {
                        if let Some(topic) = self.topics.get(id) {
                            results.push(topic);
                        }
                    }
                }
            }
        }
        
        results
    }
    
    /// Initialize with default topics.
    pub fn with_defaults() -> Self {
        let mut system = Self::new();
        
        system.register_topic(HelpTopic {
            id: "basics".to_string(),
            title: "Basic Navigation".to_string(),
            content: "Use Tab to switch panels. Use Arrow keys to navigate lists.".to_string(),
            keybinds: vec!["Tab".to_string(), "Arrows".to_string()],
            tags: vec!["navigation".to_string(), "movement".to_string()],
        });
        
        system.register_topic(HelpTopic {
            id: "staging".to_string(),
            title: "Staging Files".to_string(),
            content: "Press 's' to stage a file in the Status panel. Press 'u' to unstage.".to_string(),
            keybinds: vec!["s".to_string(), "u".to_string()],
            tags: vec!["stage".to_string(), "unstage".to_string(), "index".to_string()],
        });
        
        system.register_topic(HelpTopic {
            id: "commit".to_string(),
            title: "Committing Changes".to_string(),
            content: "Briefly describe your changes. Press Ctrl+S to save the commit message.".to_string(),
            keybinds: vec!["c".to_string(), "Ctrl+S".to_string()],
            tags: vec!["commit".to_string(), "save".to_string()],
        });
        
        system
    }
}

impl Default for HelpSystem {
    fn default() -> Self {
        Self::with_defaults()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_help_search() {
        let system = HelpSystem::default();
        
        let results = system.search("nav");
        assert_eq!(results.len(), 1);
        assert_eq!(results[0].id, "basics");
        
        let results = system.search("stage");
        assert_eq!(results.len(), 1);
        assert_eq!(results[0].id, "staging");
    }
    
    #[test]
    fn test_help_content() {
        let system = HelpSystem::default();
        let topic = system.get_topic("commit").unwrap();
        assert!(topic.content.contains("Ctrl+S"));
    }
}