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(¶ms.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);
};
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(¶ms.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);
};
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(¶ms.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);
};
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))
}