blues-lsp 0.1.3

LSP language server for the Bluespec SystemVerilog language
Documentation
use crate::{
    project::syntax::Syntax,
    syntax::{
        cst::{
            kind::TreeKind, tree::{NodeHandle, NodeRef}, view::{self, Ident, View}
        },
        location::{OriginTable, Pos, Span}, parser::token::Tt,
    },
    util::data::either::Either,
};

pub fn node_is_ref(node: NodeRef) -> bool {
    node.is_tt(Tt::Ident)
}

pub fn node_is_def(node: NodeRef) -> bool {
    let Some(kind) = node.open_kind() else {
        return false;
    };
    use TreeKind::*;
    match kind {
        #[rustfmt::skip]
        Package | StmtFunction | FunctionParam | StmtInterface | StmtModule |
        TypedefEnum | TypedefStruct | TypedefUnion | TypedefAlias | ModuleParam |
        ModulePortParam | TypedefParam | StmtMethod | StmtTypeclass => true,
        VarInit => {
            let init = view::VarInit::cast(node);
            init.target().and_then(|t| t.left()).is_some()
        }
        DeclPattern => {
            let pat = view::DeclPattern::cast(node);
            pat.name().and_then(Either::right).is_some()
        }
        _ => false,
    }
}

pub fn node_is_implicit_def(node: NodeRef) -> Option<Ident> {
    use view::*;
    match node.open_kind()? {
        TreeKind::TypeVar => TypeVar::cast(node).name(),
        _ => None,
    }
}

pub fn selection_span(node: NodeRef, ot: &OriginTable) -> Option<Span> {
    use view::*;

    let Some(kind) = node.open_kind() else {
        return node.span(ot);
    };

    match kind {
        TreeKind::Package => Package::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot))
            .or_else(|| node.span(ot).map(|s| s.empty_at_start())),
        TreeKind::FunctionParam => FunctionParam::cast(node)
            .typed_name()
            .and_then(|n| n.name())
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::StmtFunction => StmtFunction::cast(node)
            .typed_name()
            .and_then(|tn| tn.name())
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::StmtMethod => StmtMethod::cast(node)
            .typed_name()
            .and_then(|tn| tn.name())
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::StmtInterface => StmtInterface::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::StmtModule => StmtModule::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::ModuleParam => ModuleParam::cast(node)
            .typed_name()
            .and_then(|n| n.name())
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::ModulePortParam => ModulePortParam::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::StmtTypeclass => StmtTypeclass::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::VarInit => VarInit::cast(node)
            .target()
            .and_then(Either::left)
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::TypedefEnum => TypedefEnum::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::TypedefStruct => TypedefStruct::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::TypedefUnion => TypedefUnion::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::TypedefAlias => TypedefAlias::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        TreeKind::TypedefParam => TypedefParam::cast(node)
            .name()
            .and_then(|n| n.syntax().span(ot)),
        _ => node.span(ot),
    }
}

pub fn node_of_interest(syntax: &Syntax, pos: Pos) -> Option<NodeHandle> {
    fn walk(syntax: &Syntax, pos: &Pos, node: NodeRef) -> Option<NodeHandle> {
        let ot = &syntax.ctx.origins;
        if !node
            .span(ot)
            .and_then(|s| s.contains(pos, ot))
            .unwrap_or(false)
        {
            return None;
        }

        if selection_span(node, ot)
            .and_then(|s| s.contains(pos, ot))
            .unwrap_or(false)
            && (node_is_ref(node) || node_is_def(node) || node_is_implicit_def(node).is_some())
        {
            return Some(node.handle());
        }

        node.children().find_map(|c| walk(syntax, pos, c))
    }

    walk(syntax, &pos, syntax.cst.root())
}