frigg 0.3.2

Local-first MCP server for code understanding.
Documentation
use super::*;

impl SymbolGraph {
    pub fn precise_counts(&self) -> PreciseGraphCounts {
        PreciseGraphCounts {
            symbols: self.precise_symbols.len(),
            occurrences: self.precise_occurrences.len(),
            relationships: self.precise_relationships.len(),
        }
    }

    pub fn clear_precise_data(&mut self) {
        self.precise_symbols.clear();
        self.precise_symbol_keys_by_repository.clear();
        self.precise_symbols_by_file.clear();
        self.precise_symbol_ref_counts.clear();
        self.precise_occurrences.clear();
        self.precise_occurrence_keys_by_file.clear();
        self.precise_occurrence_keys_by_symbol.clear();
        self.precise_relationships.clear();
        self.precise_relationship_keys_by_from_symbol.clear();
        self.precise_relationship_keys_by_to_symbol.clear();
        self.precise_relationships_by_file.clear();
        self.precise_relationship_ref_counts.clear();
    }

    pub fn precise_symbol(
        &self,
        repository_id: &str,
        symbol: &str,
    ) -> Option<&PreciseSymbolRecord> {
        self.precise_symbols
            .get(&(repository_id.to_owned(), symbol.to_owned()))
    }

    pub fn precise_symbols_for_repository(&self, repository_id: &str) -> Vec<PreciseSymbolRecord> {
        let mut symbols = self
            .precise_symbol_keys_by_repository
            .get(repository_id)
            .into_iter()
            .flat_map(|symbol_ids| symbol_ids.iter())
            .filter_map(|symbol_id| {
                self.precise_symbols
                    .get(&(repository_id.to_owned(), symbol_id.clone()))
                    .cloned()
            })
            .collect::<Vec<_>>();
        symbols.sort_by(precise_symbol_order);
        symbols
    }

    pub fn precise_occurrences_for_symbol(
        &self,
        repository_id: &str,
        symbol: &str,
    ) -> Vec<PreciseOccurrenceRecord> {
        let mut occurrences = self
            .precise_occurrence_keys_by_symbol
            .get(&precise_symbol_key(repository_id, symbol))
            .into_iter()
            .flat_map(|keys| keys.iter())
            .filter_map(|key| self.precise_occurrences.get(key).cloned())
            .collect::<Vec<_>>();
        occurrences.sort_by(precise_occurrence_order);
        occurrences
    }

    pub fn precise_definition_occurrence_for_symbol(
        &self,
        repository_id: &str,
        symbol: &str,
    ) -> Option<PreciseOccurrenceRecord> {
        self.precise_occurrence_keys_by_symbol
            .get(&precise_symbol_key(repository_id, symbol))
            .into_iter()
            .flat_map(|keys| keys.iter())
            .filter_map(|key| self.precise_occurrences.get(key))
            .find(|occurrence| occurrence.is_definition())
            .cloned()
    }

    pub fn precise_references_for_symbol(
        &self,
        repository_id: &str,
        symbol: &str,
    ) -> Vec<PreciseOccurrenceRecord> {
        self.precise_occurrences_for_symbol(repository_id, symbol)
            .into_iter()
            .filter(|occurrence| !occurrence.is_definition())
            .collect()
    }

    pub fn precise_occurrences_for_file(
        &self,
        repository_id: &str,
        path: &str,
    ) -> Vec<PreciseOccurrenceRecord> {
        let mut occurrences = self
            .precise_occurrence_keys_by_file
            .get(&precise_file_key(repository_id, path))
            .into_iter()
            .flat_map(|keys| keys.iter())
            .filter_map(|key| self.precise_occurrences.get(key).cloned())
            .collect::<Vec<_>>();
        occurrences.sort_by(precise_occurrence_order);
        occurrences
    }

    pub fn select_precise_symbol_for_location(
        &self,
        repository_id: &str,
        path: &str,
        line: usize,
        column: Option<usize>,
    ) -> Option<PreciseSymbolRecord> {
        let mut ranked = self
            .precise_occurrences_for_file(repository_id, path)
            .into_iter()
            .filter(|occurrence| occurrence.range.start_line <= line)
            .filter(|occurrence| {
                column.is_none_or(|value| {
                    occurrence.range.start_line < line || occurrence.range.start_column <= value
                })
            })
            .filter_map(|occurrence| {
                let symbol = self
                    .precise_symbol(repository_id, &occurrence.symbol)?
                    .clone();
                let line_distance = line.saturating_sub(occurrence.range.start_line);
                let column_distance = if line_distance == 0 {
                    column
                        .map(|value| value.saturating_sub(occurrence.range.start_column))
                        .unwrap_or(0)
                } else {
                    0
                };
                let containment_rank = if occurrence.contains_location(line, column) {
                    0u8
                } else {
                    1u8
                };
                Some((
                    containment_rank,
                    line_distance,
                    column_distance,
                    occurrence.range.start_line,
                    occurrence.range.start_column,
                    occurrence.symbol.clone(),
                    symbol,
                ))
            })
            .collect::<Vec<_>>();
        ranked.sort_by(|left, right| {
            left.0
                .cmp(&right.0)
                .then(left.1.cmp(&right.1))
                .then(left.2.cmp(&right.2))
                .then(right.3.cmp(&left.3))
                .then(right.4.cmp(&left.4))
                .then(left.5.cmp(&right.5))
        });
        ranked
            .into_iter()
            .next()
            .map(|(_, _, _, _, _, _, symbol)| symbol)
    }

    pub fn precise_relationships_from_symbol(
        &self,
        repository_id: &str,
        from_symbol: &str,
    ) -> Vec<PreciseRelationshipRecord> {
        let mut relationships = self
            .precise_relationship_keys_by_from_symbol
            .get(&precise_symbol_key(repository_id, from_symbol))
            .into_iter()
            .flat_map(|keys| keys.iter())
            .filter_map(|key| self.precise_relationships.get(key).cloned())
            .collect::<Vec<_>>();
        relationships.sort_by(precise_relationship_order);
        relationships
    }

    pub fn precise_relationships_to_symbol_by_kinds(
        &self,
        repository_id: &str,
        to_symbol: &str,
        kinds: &[PreciseRelationshipKind],
    ) -> Vec<PreciseRelationshipRecord> {
        let mut relationships = self
            .precise_relationship_keys_by_to_symbol
            .get(&precise_symbol_key(repository_id, to_symbol))
            .into_iter()
            .flat_map(|keys| keys.iter())
            .filter_map(|key| self.precise_relationships.get(key))
            .filter(|relationship| kinds.contains(&relationship.kind))
            .cloned()
            .collect::<Vec<_>>();
        relationships.sort_by(precise_relationship_order);
        relationships
    }

    pub fn select_precise_symbol_for_navigation(
        &self,
        repository_id: &str,
        symbol_query: &str,
        fallback_symbol_name: &str,
    ) -> Option<PreciseSymbolRecord> {
        self.matching_precise_symbols_for_navigation(
            repository_id,
            symbol_query,
            fallback_symbol_name,
        )
        .into_iter()
        .next()
    }

    pub fn matching_precise_symbols_for_navigation(
        &self,
        repository_id: &str,
        symbol_query: &str,
        fallback_symbol_name: &str,
    ) -> Vec<PreciseSymbolRecord> {
        let mut ranked = self
            .precise_symbols_for_repository(repository_id)
            .into_iter()
            .filter_map(|precise_symbol| {
                precise_navigation_symbol_rank(&precise_symbol, symbol_query, fallback_symbol_name)
                    .map(|rank| (rank, precise_symbol))
            })
            .collect::<Vec<_>>();
        ranked.sort_by(|left, right| {
            left.0
                .cmp(&right.0)
                .then(left.1.symbol.cmp(&right.1.symbol))
                .then(left.1.display_name.cmp(&right.1.display_name))
                .then(left.1.kind.cmp(&right.1.kind))
        });
        ranked
            .into_iter()
            .map(|(_, precise_symbol)| precise_symbol)
            .collect()
    }
}