blues-lsp 0.1.0

LSP language server for the Bluespec SystemVerilog language
Documentation
use std::{
    collections::{HashMap, HashSet},
    sync::Arc,
};

use arcstr::Substr;
use parking_lot::Mutex;
use tracing::{trace, warn};

use crate::{
    symbols::name::SymName,
    syntax::cst::{tree::NodeHandle, view::View},
    util::data::either::Either,
};

use super::{PkgHandle, Project, querry::Queryable};

pub struct Exports {
    pub pkg: PkgHandle,
    pub defs: HashMap<SymName, (PkgHandle, NodeHandle)>,
}

impl Queryable for Exports {
    type Input = PkgHandle;
    type Ctx = Mutex<HashSet<PkgHandle>>;
    type Backref = PkgHandle;

    fn compute(pkg: Self::Input, ctx: &Self::Ctx, proj: &Project) -> Arc<Self> {
        trace!("computing exports for {}", proj.get_pkg(pkg));
        if !ctx.lock().insert(pkg) {
            warn!("Circular dependency detected while computing package exports");
            return Arc::new(Exports {
                pkg,
                defs: Default::default(),
            });
        }
        Arc::new(Self::compute_exports(proj, pkg))
    }

    fn invalidate(&self, input: &Self::Input, ctx: &Self::Ctx, _proj: &Project) {
        ctx.lock().remove(input);
    }

    fn invalidate_backref(&self, backref: Self::Backref, proj: &Project) {
        proj.exports.invalidate(proj, &backref);
        proj.bindings.invalidate(proj, &backref);
    }
}

impl Exports {
    fn compute_globals(
        proj: &Project,
        pkg: PkgHandle,
    ) -> HashMap<SymName, (PkgHandle, NodeHandle)> {
        let syntax = proj.syntax.get(proj, pkg);
        let mut defs: HashMap<SymName, (PkgHandle, NodeHandle)> = HashMap::new();

        for imported in &syntax.top.imports {
            let exports = proj.exports.get_as(proj, *imported, pkg);
            defs.extend(exports.defs.iter().map(|(k, v)| (k.clone(), *v)));
        }

        defs.extend(syntax.top.defs.iter().map(|(k, v)| (k.clone(), (pkg, *v))));
        proj.get_pkg(pkg)
            .name
            .clone()
            .map(|name| defs.insert(SymName::Pkg(name), (pkg, syntax.root().syntax().handle())));

        defs
    }

    fn compute_exports(proj: &Project, pkg: PkgHandle) -> Exports {
        let syntax = proj.syntax.get(proj, pkg);
        let defs = Self::compute_globals(proj, pkg);

        let mut exported = HashMap::new();

        exported.extend(
            defs.iter()
                .filter(|(name, _)| matches!(name, SymName::Pkg(_)))
                .map(|(k, v)| (k.clone(), *v)),
        );

        if syntax.top.exports.is_empty() {
            exported.extend(syntax.top.defs.iter().map(|(k, v)| (k.clone(), (pkg, *v))));
            return Exports {
                pkg,
                defs: exported,
            };
        }

        for export in &syntax.top.exports {
            match export {
                Either::Left(exported_name) => {
                    defs.get_key_value(&SymName::Value(exported_name.clone()))
                        .map(|(k, v)| exported.insert(k.clone(), *v));
                    defs.get_key_value(&SymName::Type(exported_name.clone()))
                        .map(|(k, v)| exported.insert(k.clone(), *v));
                }
                Either::Right(exported_pkg) => {
                    exported.extend(
                        defs.iter()
                            .filter(|(_, (def_pkg, _))| def_pkg == exported_pkg)
                            .map(|(k, v)| (k.clone(), *v)),
                    );
                }
            }
        }

        Exports {
            pkg,
            defs: exported,
        }
    }

    pub fn lookup_package(&self, name: Substr) -> Option<PkgHandle> {
        self.defs.get(&SymName::Pkg(name)).map(|(p, _)| *p)
    }
}