blues-lsp 0.1.3

LSP language server for the Bluespec SystemVerilog language
Documentation
use crate::{
    project::{PkgHandle, Project},
    syntax::cst::{
        tree::{NodeHandle, NodeRef},
        view::View,
    },
};

use super::{
    name::SymName,
    node::{node_is_def, node_is_implicit_def, node_is_ref},
};

pub fn find_references_for(
    proj: &Project,
    this_pkg: PkgHandle,
    node: NodeRef<'_>,
    project_wide: bool,
    include_declaration: bool,
) -> Option<Vec<(PkgHandle, NodeHandle)>> {
    if node_is_ref(node) {
        if let Some((pkg, node)) = find_definition(proj, this_pkg, node.handle()) {
            Some(find_references(
                proj,
                pkg,
                node,
                // TODO: better API for fetching referneces for a specific pkg.
                project_wide || this_pkg != pkg,
                include_declaration,
            ))
        } else {
            None
        }
    } else if node_is_def(node) {
        Some(find_references(
            proj,
            this_pkg,
            node.handle(),
            project_wide,
            include_declaration,
        ))
    } else if let Some(name) = node_is_implicit_def(node) {
        let (pkg, node) = find_definition(proj, this_pkg, name.syntax().handle())
            .unwrap_or((this_pkg, node.handle()));
        Some(find_references(proj, pkg, node, false, include_declaration))
    } else {
        return None;
    }
}

pub fn definition_for(
    proj: &Project,
    pkg: PkgHandle,
    node: NodeRef<'_>,
) -> Option<(PkgHandle, NodeHandle)> {
    if node_is_ref(node) {
        find_definition(proj, pkg, node.handle())
    } else if node_is_def(node) {
        Some((pkg, node.handle()))
    } else {
        None
    }
}

pub fn find_definition(
    proj: &Project,
    pkg: PkgHandle,
    node: NodeHandle,
) -> Option<(PkgHandle, NodeHandle)> {
    let bindings = proj.bindings.get(proj, pkg);
    bindings.get(node)
}

pub fn find_references(
    proj: &Project,
    pkg: PkgHandle,
    node: NodeHandle,
    project_wide: bool,
    include_declaration: bool,
) -> Vec<(PkgHandle, NodeHandle)> {
    let bindings = proj.bindings.get(proj, pkg);
    let mut out = Vec::new();

    if include_declaration {
        out.push((pkg, node));
    }

    for (n, def) in bindings.iter() {
        if def == (pkg, node) {
            out.push((pkg, n));
        }
    }

    if !project_wide {
        return out;
    }

    let exports = proj.exports.get(proj, pkg);
    if !exports.defs.iter().any(|(_, v)| *v == (pkg, node)) {
        return out;
    }

    let this_pkg = pkg;
    let Some(this_pkg_name) = proj.get_pkg(this_pkg).name.clone() else {
        // Dummy package, couldn't possibly be referenced elsewhere
        return out;
    };

    for pkg in proj.iter_packages() {
        if pkg == this_pkg {
            continue;
        }

        if exports
            .defs
            .get(&SymName::Pkg(this_pkg_name.clone()))
            .map(|(pkg, _)| *pkg)
            != Some(this_pkg)
        {
            // Target pacakge not visible from here, skip.
            continue;
        }

        let bindings = proj.bindings.get(proj, pkg);

        for (n, def) in bindings.iter() {
            if def == (this_pkg, node) {
                out.push((pkg, n));
            }
        }
    }

    out
}