relay-knowledge 1.1.10

Graph-database-based knowledge graph project.
Documentation
use tree_sitter::Node;

use super::super::nodes::{SyntaxRange, node_text, syntax_range};

pub(in crate::code::parser) fn manual_reference(
    content: &str,
    node: Node<'_>,
) -> Option<(String, &'static str, SyntaxRange)> {
    if !c_family_reference_node(node.kind()) {
        return None;
    }
    let name = node_text(content, node);
    if !c_family_reference_name(&name) {
        return None;
    }
    if node.kind() == "type_identifier" && c_family_type_reference_context(node) {
        return Some((name, "type", syntax_range(node)));
    }
    if c_family_value_reference_context(node) {
        return Some((name, "implementation", syntax_range(node)));
    }

    None
}

fn c_family_reference_node(kind: &str) -> bool {
    matches!(
        kind,
        "identifier" | "field_identifier" | "namespace_identifier" | "type_identifier"
    )
}

fn c_family_reference_name(name: &str) -> bool {
    let mut characters = name.chars();
    characters
        .next()
        .is_some_and(|character| character == '_' || character.is_ascii_alphabetic())
        && characters.all(|character| character == '_' || character.is_ascii_alphanumeric())
}

fn c_family_type_reference_context(node: Node<'_>) -> bool {
    has_ancestor_kind(node, "field_declaration")
        || has_ancestor_kind(node, "parameter_declaration")
        || has_ancestor_kind(node, "qualified_type_identifier")
        || has_ancestor_kind(node, "scoped_type_identifier")
}

fn c_family_value_reference_context(node: Node<'_>) -> bool {
    has_ancestor_kind(node, "initializer_list")
        || has_non_argument_subscript_ancestor(node)
        || has_ancestor_kind(node, "qualified_identifier")
        || has_ancestor_kind(node, "scoped_identifier")
}

fn has_non_argument_subscript_ancestor(mut node: Node<'_>) -> bool {
    while let Some(parent) = node.parent() {
        if parent.kind() == "subscript_expression" {
            return !parent
                .child_by_field_name("argument")
                .is_some_and(|argument| node_contains(argument, node));
        }
        node = parent;
    }

    false
}

fn has_ancestor_kind(mut node: Node<'_>, kind: &str) -> bool {
    while let Some(parent) = node.parent() {
        if parent.kind() == kind {
            return true;
        }
        node = parent;
    }

    false
}

fn node_contains(parent: Node<'_>, child: Node<'_>) -> bool {
    parent.start_byte() <= child.start_byte() && parent.end_byte() >= child.end_byte()
}