mod handle;
mod htmx;
mod text_store;
mod tree_sitter;
mod tree_sitter_querier;
use anyhow::Result;
use htmx::HxCompletion;
use log::{debug, error, info, warn};
use lsp_types::{
CompletionItem, CompletionItemKind, CompletionList, HoverContents, InitializeParams,
LanguageString, MarkedString, ServerCapabilities, TextDocumentSyncCapability,
TextDocumentSyncKind, WorkDoneProgressOptions,
};
use lsp_server::{Connection, Message, Response};
use crate::{
handle::{handle_notification, handle_other, handle_request, HtmxResult},
htmx::init_hx_tags,
text_store::init_text_store,
};
fn to_completion_list(items: Vec<HxCompletion>) -> CompletionList {
return CompletionList {
is_incomplete: true,
items: items
.iter()
.map(|x| CompletionItem {
label: x.name.clone(),
label_details: None,
kind: Some(CompletionItemKind::TEXT),
detail: Some(x.desc.clone()),
documentation: None,
deprecated: Some(false),
preselect: None,
sort_text: None,
filter_text: None,
insert_text: None,
insert_text_format: None,
insert_text_mode: None,
text_edit: None,
additional_text_edits: None,
command: None,
commit_characters: None,
data: None,
tags: None,
})
.collect(),
};
}
fn main_loop(connection: Connection, params: serde_json::Value) -> Result<()> {
let _params: InitializeParams = serde_json::from_value(params).unwrap();
info!("STARTING EXAMPLE MAIN LOOP");
for msg in &connection.receiver {
error!("connection received message: {:?}", msg);
let result = match msg {
Message::Notification(not) => handle_notification(not),
Message::Request(req) => handle_request(req),
_ => handle_other(msg),
};
match match result {
Some(HtmxResult::AttributeCompletion(c)) => {
let str = match serde_json::to_value(&to_completion_list(c.items)) {
Ok(s) => s,
Err(_) => continue,
};
connection.sender.send(Message::Response(Response {
id: c.id,
result: Some(str),
error: None,
}))
}
Some(HtmxResult::AttributeHover(hover_resp)) => {
debug!("main_loop - hover response: {:?}", hover_resp);
let hover_response = lsp_types::Hover {
contents: HoverContents::Scalar(MarkedString::LanguageString(LanguageString {
language: "html".to_string(),
value: hover_resp.value.clone(),
})),
range: None,
};
let str = match serde_json::to_value(&hover_response) {
Ok(s) => s,
Err(err) => {
error!("Fail to parse hover_response: {:?}", err);
return Err(anyhow::anyhow!("Fail to parse hover_response"));
}
};
connection.sender.send(Message::Response(Response {
id: hover_resp.id,
result: Some(str),
error: None,
}))
}
None => continue,
} {
Ok(_) => {}
Err(e) => error!("failed to send response: {:?}", e),
};
}
Ok(())
}
pub fn start_lsp() -> Result<()> {
init_text_store();
init_hx_tags();
info!("starting generic LSP server");
let (connection, io_threads) = Connection::stdio();
let server_capabilities = serde_json::to_value(ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
completion_provider: Some(lsp_types::CompletionOptions {
resolve_provider: Some(false),
trigger_characters: Some(vec!["-".to_string(), "\"".to_string(), " ".to_string()]),
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: None,
},
all_commit_characters: None,
completion_item: None,
}),
hover_provider: Some(lsp_types::HoverProviderCapability::Simple(true)),
..Default::default()
})
.unwrap();
let initialization_params = connection.initialize(server_capabilities)?;
main_loop(connection, initialization_params)?;
io_threads.join()?;
warn!("shutting down server");
Ok(())
}
#[cfg(test)]
mod test {
use anyhow::Result;
#[test]
fn test_byte_col() -> Result<()> {
Ok(())
}
}