use std::collections::HashSet;
use ll_sparql_parser::ast::AstNode;
use crate::server::{
Server,
analysis::{find_all_prefix_declarations, find_all_uncompacted_iris},
lsp::{
CodeAction, CodeActionKind,
textdocument::{Range, TextEdit},
},
};
pub(super) fn code_actions(server: &Server, document_uri: String) -> Vec<CodeAction> {
shorten_all_uris(server, &document_uri)
.into_iter()
.collect()
}
fn shorten_all_uris(server: &Server, document_uri: &String) -> Option<CodeAction> {
let mut code_action = CodeAction::new("Shorten all URIs", Some(CodeActionKind::Refactor));
let uncompacted_uris = find_all_uncompacted_iris(server, document_uri).ok()?;
let mut declared_uri_prefix_set: HashSet<String> =
find_all_prefix_declarations(&server.state, document_uri)
.ok()?
.into_iter()
.filter_map(|prefix_declaration| prefix_declaration.raw_uri_prefix())
.collect();
let document = server.state.get_document(document_uri).ok()?;
uncompacted_uris.iter().for_each(|iri| {
if let Some((prefix, uri_prefix, curie)) =
server.shorten_uri(&iri.raw_iri().expect("iri should be uncompacted"), None)
{
code_action.add_edit(
document_uri,
TextEdit::new(
Range::from_byte_offset_range(iri.syntax().text_range(), &document.text)
.unwrap(),
&curie,
),
);
if !declared_uri_prefix_set.contains(&uri_prefix) {
code_action.add_edit(
document_uri,
TextEdit::new(
Range::new(0, 0, 0, 0),
&format!("PREFIX {}: <{}>\n", prefix, uri_prefix),
),
);
declared_uri_prefix_set.insert(uri_prefix);
}
}
});
if !uncompacted_uris.is_empty() {
return Some(code_action);
}
None
}
#[cfg(test)]
mod test {
use std::collections::HashMap;
use crate::server::{
Server,
configuration::BackendConfiguration,
lsp::textdocument::{Range, TextDocumentItem, TextEdit},
message_handler::code_action::iri::shorten_all_uris,
state::ServerState,
};
use indoc::indoc;
fn setup_state(text: &str) -> ServerState {
let mut state = ServerState::new();
state.add_backend(BackendConfiguration {
name: "test".to_string(),
url: "".to_string(),
health_check_url: None,
engine: None,
request_method: None,
prefix_map: HashMap::new(),
default: true,
queries: HashMap::new(),
additional_data: None,
});
state.set_default_backend("test".to_string());
state
.load_prefix_map(
"test".to_string(),
&HashMap::from_iter([("schema".to_string(), "https://schema.org/".to_string())]),
)
.unwrap();
let document = TextDocumentItem::new("uri", text);
state.add_document(document);
state
}
#[test]
fn shorten_all_uris_undeclared() {
let mut server = Server::new(|_message| {});
let state = setup_state(indoc!(
"SELECT * {
?a <https://schema.org/name> ?b .
?c <https://schema.org/name> ?d
}"
));
server.state = state;
let code_action = shorten_all_uris(&server, &"uri".to_string()).unwrap();
assert_eq!(
code_action.edit.changes.unwrap().get("uri").unwrap(),
&vec![
TextEdit::new(Range::new(1, 5, 1, 30), "schema:name"),
TextEdit::new(
Range::new(0, 0, 0, 0),
"PREFIX schema: <https://schema.org/>\n"
),
TextEdit::new(Range::new(2, 5, 2, 30), "schema:name"),
]
);
}
#[test]
fn shorten_all_uris_declared() {
let mut server = Server::new(|_message| {});
let state = setup_state(indoc!(
"PREFIX schema: <https://schema.org/>
SELECT * {
?a <https://schema.org/name> ?b .
?c <https://schema.org/name> ?d
}"
));
server.state = state;
let code_action = shorten_all_uris(&server, &"uri".to_string()).unwrap();
assert_eq!(
code_action.edit.changes.unwrap().get("uri").unwrap(),
&vec![
TextEdit::new(Range::new(2, 5, 2, 30), "schema:name"),
TextEdit::new(Range::new(3, 5, 3, 30), "schema:name"),
]
);
}
}