blues-lsp 0.1.3

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

use arcstr::Substr;
use tracing::trace;

use crate::{
    symbols::{name::SymName, scopes::ScopeWalk},
    syntax::cst::{
        aside::Aside,
        tree::{NodeHandle, NodeRef},
        view::{TypeVar, View},
    },
};

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

pub struct Bindings {
    map: Aside<Option<(PkgHandle, NodeHandle)>>,
}

impl Queryable for Bindings {
    type Input = PkgHandle;
    type Ctx = ();
    type Backref = ();

    fn compute(pkg: Self::Input, _ctx: &Self::Ctx, proj: &Project) -> Arc<Self> {
        trace!("computing bindings for {}", proj.get_pkg(pkg));
        Arc::new(Self::compute(proj, pkg))
    }

    fn invalidate(&self, _input: &Self::Input, _ctx: &Self::Ctx, _proj: &Project) {}

    fn invalidate_backref(&self, _backref: Self::Backref, _proj: &Project) {}
}

impl Bindings {
    fn compute(proj: &Project, pkg: PkgHandle) -> Self {
        let syntax = proj.syntax.get(proj, pkg);

        let mut state = BindState {
            aside: Aside::new(&syntax.cst),
            proj,
            syntax: &syntax,
            globals: syntax.compute_globals(proj),
            locals: HashMap::new(),
            depth: 0,
        };
        state.walk_impl(syntax.cst.root());

        Bindings { map: state.aside }
    }

    pub fn get(&self, node: NodeHandle) -> Option<(PkgHandle, NodeHandle)> {
        *self.map.get(node)
    }

    pub fn iter(&self) -> impl Iterator<Item = (NodeHandle, (PkgHandle, NodeHandle))> {
        self.map.iter().filter_map(|(n, d)| Some((n, (*d)?)))
    }
}

struct BindState<'ctx> {
    aside: Aside<Option<(PkgHandle, NodeHandle)>>,
    proj: &'ctx Project,
    syntax: &'ctx Syntax,
    globals: HashMap<SymName, (PkgHandle, NodeHandle)>,
    locals: HashMap<SymName, NodeHandle>,
    depth: usize,
}

impl BindState<'_> {
    fn resolve_id(&self, pkg: Option<Substr>, name: SymName) -> Option<(PkgHandle, NodeHandle)> {
        if let Some(pkg) = pkg {
            let pkg = self.globals.get(&SymName::Pkg(pkg)).map(|(p, _)| *p)?;
            let exports = self.proj.exports.get(self.proj, pkg);
            exports.defs.get(&name).copied()
        } else {
            if let Some(def) = self.locals.get(&name) {
                return Some((self.syntax.ctx.pkg, *def));
            }
            self.globals.get(&name).copied()
        }
    }
}

impl ScopeWalk for BindState<'_> {
    type R = ();

    fn scope(
        &mut self,
        _node: NodeRef,
        inner: impl FnOnce(&mut Self) -> Option<Self::R>,
    ) -> Option<Self::R> {
        // TODO: Better DSA?
        let prev = self.locals.clone();
        self.depth += 1;
        (inner(self));
        self.locals = prev;
        self.depth -= 1;
        None
    }

    fn def(&mut self, node: NodeRef, name: SymName) -> Option<Self::R> {
        if self.depth > 1 {
            self.locals.insert(name, node.handle());
        }
        None
    }

    fn link(&mut self, node: NodeRef, pkg: Option<Substr>, name: SymName) -> Option<Self::R> {
        if let Some(def) = self.resolve_id(pkg, name) {
            self.aside.set(node.handle(), Some(def))
        }
        None
    }

    fn ty_var(&mut self, node: TypeVar) -> Option<Self::R> {
        let name = node.name()?;
        if let Some(def) = self.locals.get(&SymName::Type(name.str())) {
            self.aside
                .set(name.syntax().handle(), Some((self.syntax.ctx.pkg, *def)));
        } else {
            self.locals
                .insert(SymName::Type(name.str()), node.syntax().handle());
        }
        None
    }
}