mod iri;
mod quickfix;
pub(crate) mod same_subject;
mod select;
mod variable;
use crate::server::{
Server,
lsp::{
CodeAction, CodeActionParams, CodeActionRequest, CodeActionResponse,
diagnostic::Diagnostic,
errors::{ErrorCode, LSPError},
},
};
use futures::lock::Mutex;
use ll_sparql_parser::{SyntaxElement, ast::AstNode};
use ll_sparql_parser::{
ast::{Iri, Var},
syntax_kind::SyntaxKind,
};
use quickfix::get_quickfix;
use std::rc::Rc;
pub(crate) use quickfix::declare_prefix;
pub(crate) use quickfix::remove_prefix_declaration;
#[tracing::instrument(skip_all, fields(id = %request.get_id(), uri = %request.params.text_document.uri))]
pub(super) async fn handle_codeaction_request(
server_rc: Rc<Mutex<Server>>,
request: CodeActionRequest,
) -> Result<(), LSPError> {
let mut server = server_rc.lock().await;
let mut code_action_response = CodeActionResponse::new(request.get_id());
code_action_response.add_code_actions(generate_code_actions(&mut server, &request.params)?);
code_action_response.add_code_actions(generate_quickfixes(&mut server, request));
server.send_message(code_action_response)
}
fn generate_quickfixes(server: &mut Server, request: CodeActionRequest) -> Vec<CodeAction> {
request
.params
.context
.diagnostics
.into_iter()
.filter_map(|diagnostic| {
match get_quickfix(server, &request.params.text_document.uri, diagnostic) {
Ok(code_action) => code_action,
Err(err) => {
tracing::error!(
"Encountered Error while computing quickfix:\n{}\nDropping error!",
err.message
);
None
}
}
})
.collect()
}
fn generate_code_actions(
server: &mut Server,
params: &CodeActionParams,
) -> Result<Vec<CodeAction>, LSPError> {
let document_uri = ¶ms.text_document.uri;
let document = server.state.get_document(document_uri)?;
let root = server.state.get_cached_parse_tree(document_uri)?.tree;
let range = params
.range
.to_byte_index_range(&document.text)
.ok_or(LSPError::new(
ErrorCode::InvalidParams,
&format!("Range ({:?}) not inside document range", params.range),
))?;
let selected_element: SyntaxElement = root.covering_element(range);
let mut code_actions = vec![];
if selected_element
.parent()
.and_then(Iri::cast)
.is_some_and(|iri| iri.is_uncompressed())
{
code_actions.extend(iri::code_actions(server, document.uri.clone()));
} else if let Some(var) = selected_element.parent().and_then(Var::cast) {
code_actions.extend(variable::code_actions(var, &server.state, document));
} else if matches!(
selected_element.kind(),
SyntaxKind::SelectQuery | SyntaxKind::SELECT | SyntaxKind::SubSelect
) {
code_actions.extend(select::code_actions(
selected_element,
document,
server.settings.format.tab_size.unwrap_or(2),
));
}
if let Some(code_action) =
same_subject::contract_all_triple_groups(document, root, &server.settings.format)?
{
code_actions.push(code_action);
}
Ok(code_actions)
}