blues-lsp 0.1.0

LSP language server for the Bluespec SystemVerilog language
Documentation
use crate::lsp::error::LspResult;
use crate::lsp::server::Server;
use crate::symbols::node::{
    node_is_def, node_is_implicit_def, node_is_ref, node_of_interest, selection_span,
};
use crate::symbols::resolve::{find_definition, find_references, find_references_for};
use crate::syntax::cst::view::View;
use crate::syntax::location::Pos;

pub fn definition_capabilites() -> lsp_types::OneOf<bool, lsp_types::DefinitionOptions> {
    lsp_types::OneOf::Left(true)
}

pub fn goto_definition(
    server: &mut Server,
    params: lsp_types::GotoDefinitionParams,
) -> LspResult<Option<lsp_types::GotoDefinitionResponse>> {
    let pkg = server.uri_to_pkg(&params.text_document_position_params.text_document.uri)?;
    let proj = &server.proj;
    let pos = Pos::from_lsp(params.text_document_position_params.position);

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

    let Some(node) = node_of_interest(&syntax, pos) else {
        return Ok(None);
    };
    let node = node.with_cst(&syntax.cst);

    let dests = if node_is_ref(node) {
        if let Some(def) = find_definition(proj, pkg, node.handle()) {
            vec![def]
        } else {
            return Ok(None);
        }
    } else if node_is_def(node) {
        find_references(proj, pkg, node.handle(), true, true)
    } else if let Some(name) = node_is_implicit_def(node) {
        let (pkg, node) =
            find_definition(proj, pkg, name.syntax().handle()).unwrap_or((pkg, node.handle()));
        let syntax = proj.syntax.get(proj, pkg);
        if node_is_implicit_def(node.with_cst(&syntax.cst)).is_some() {
            find_references(proj, pkg, node, false, true)
        } else {
            vec![(pkg, node)]
        }
    } else {
        return Ok(None);
    };

    // TODO: expand macro expansion steps

    let link_range =
        selection_span(node, &syntax.ctx.origins).map(|s| s.to_lsp(&syntax.ctx.origins));

    let links = dests
        .into_iter()
        .filter_map(|(pkg, node)| {
            let syntax = proj.syntax.get(proj, pkg);
            let file = proj.get_pkg(pkg).file;
            let uri = server.file_to_uri(file).ok()?;
            let range = node
                .with_cst(&syntax.cst)
                .span(&syntax.ctx.origins)
                .map(|s| s.to_lsp(&syntax.ctx.origins))?;
            let sel_range = selection_span(node.with_cst(&syntax.cst), &syntax.ctx.origins)
                .map(|s| s.to_lsp(&syntax.ctx.origins))?;

            Some(Ok(lsp_types::LocationLink {
                origin_selection_range: link_range,
                target_uri: uri,
                target_range: range,
                target_selection_range: sel_range,
            }))
        })
        .collect::<LspResult<Vec<_>>>()?;

    Ok(Some(lsp_types::GotoDefinitionResponse::Link(links)))
}

pub fn references_capabilities() -> lsp_types::OneOf<bool, lsp_types::ReferencesOptions> {
    lsp_types::OneOf::Left(true)
}

pub fn references(
    server: &mut Server,
    params: lsp_types::ReferenceParams,
) -> LspResult<Option<Vec<lsp_types::Location>>> {
    let pkg = server.uri_to_pkg(&params.text_document_position.text_document.uri)?;
    let proj = &server.proj;
    let pos = Pos::from_lsp(params.text_document_position.position);
    let include_declaration = params.context.include_declaration;

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

    let Some(node) = node_of_interest(&syntax, pos) else {
        return Ok(None);
    };
    let node = node.with_cst(&syntax.cst);

    let Some(dests) = find_references_for(proj, pkg, node, true, include_declaration) else {
        return Ok(None);
    };

    // TODO: expand macro expansion steps

    let links = dests
        .into_iter()
        .filter_map(|(pkg, node)| {
            let syntax = proj.syntax.get(proj, pkg);
            let file = proj.get_pkg(pkg).file;
            let uri = server.file_to_uri(file).ok()?;
            let range = selection_span(node.with_cst(&syntax.cst), &syntax.ctx.origins)
                .map(|s| s.to_lsp(&syntax.ctx.origins))?;

            Some(Ok(lsp_types::Location { uri, range }))
        })
        .collect::<LspResult<Vec<_>>>()?;

    Ok(Some(links))
}

pub fn document_highlight_capabilities()
-> lsp_types::OneOf<bool, lsp_types::DocumentHighlightOptions> {
    lsp_types::OneOf::Left(true)
}

pub fn document_highlight(
    server: &mut Server,
    params: lsp_types::DocumentHighlightParams,
) -> LspResult<Option<Vec<lsp_types::DocumentHighlight>>> {
    let pkg = server.uri_to_pkg(&params.text_document_position_params.text_document.uri)?;
    let proj = &server.proj;
    let pos = Pos::from_lsp(params.text_document_position_params.position);

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

    let Some(node) = node_of_interest(&syntax, pos) else {
        return Ok(None);
    };
    let node = node.with_cst(&syntax.cst);

    let Some(dests) = find_references_for(proj, pkg, node, false, true) else {
        return Ok(None);
    };

    // TODO: expand macro expansion steps

    let this_pkg = pkg;

    let links = dests
        .into_iter()
        .filter_map(|(pkg, node)| {
            if pkg != this_pkg {
                return None;
            }
            let range = selection_span(node.with_cst(&syntax.cst), &syntax.ctx.origins)
                .map(|s| s.to_lsp(&syntax.ctx.origins))?;

            Some(Ok(lsp_types::DocumentHighlight { range, kind: None }))
        })
        .collect::<LspResult<Vec<_>>>()?;

    Ok(Some(links))
}