use lark_actor::{self, Actor, LspResponse, QueryRequest};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::collections::VecDeque;
use std::io;
use std::io::prelude::{Read, Write};
use std::sync::mpsc::Sender;
use url::Url;
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "method")]
#[allow(non_camel_case_types)]
pub enum LSPCommand {
initialize {
id: usize,
params: languageserver_types::InitializeParams,
},
initialized,
#[serde(rename = "textDocument/didOpen")]
didOpen {
params: languageserver_types::DidOpenTextDocumentParams,
},
#[serde(rename = "textDocument/didChange")]
didChange {
params: languageserver_types::DidChangeTextDocumentParams,
},
#[serde(rename = "textDocument/hover")]
hover {
id: usize,
params: languageserver_types::TextDocumentPositionParams,
},
#[serde(rename = "textDocument/completion")]
completion {
id: usize,
params: languageserver_types::CompletionParams,
},
#[serde(rename = "textDocument/definition")]
definition {
id: usize,
params: languageserver_types::TextDocumentPositionParams,
},
#[serde(rename = "textDocument/references")]
references {
id: usize,
params: languageserver_types::TextDocumentPositionParams,
},
#[serde(rename = "textDocument/rename")]
rename {
id: usize,
params: languageserver_types::RenameParams,
},
#[serde(rename = "$/cancelRequest")]
cancelRequest {
params: languageserver_types::CancelParams,
},
#[serde(rename = "completionItem/resolve")]
completionItemResolve {
id: usize,
params: languageserver_types::CompletionItem,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub struct JsonRPCResponse<T> {
jsonrpc: String,
pub id: usize,
pub result: T,
}
impl<T> JsonRPCResponse<T> {
pub fn new(id: usize, result: T) -> Self {
JsonRPCResponse {
jsonrpc: "2.0".into(),
id,
result,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct JsonRPCNotification<T> {
jsonrpc: String,
pub method: String,
pub params: T,
}
impl<T> JsonRPCNotification<T> {
pub fn new(method: String, params: T) -> Self {
JsonRPCNotification {
jsonrpc: "2.0".into(),
method,
params,
}
}
}
fn send_response<T: Serialize>(id: usize, result: T) {
let response = JsonRPCResponse::new(id, result);
let response_raw = serde_json::to_string(&response).unwrap();
print!("Content-Length: {}\r\n\r\n", response_raw.len());
print!("{}", response_raw);
let _ = io::stdout().flush();
}
fn send_notification<T: Serialize>(method: String, notice: T) {
let response = JsonRPCNotification::new(method, notice);
let response_raw = serde_json::to_string(&response).unwrap();
print!("Content-Length: {}\r\n\r\n", response_raw.len());
print!("{}", response_raw);
let _ = io::stdout().flush();
}
pub struct LspResponder;
impl Actor for LspResponder {
type InMessage = LspResponse;
fn receive_messages(&mut self, messages: &mut VecDeque<Self::InMessage>) {
match messages.pop_front().unwrap() {
LspResponse::Type(id, ty) => {
let result = languageserver_types::Hover {
contents: languageserver_types::HoverContents::Scalar(
languageserver_types::MarkedString::from_markdown(ty),
),
range: None,
};
send_response(id, result);
}
LspResponse::Range(id, uri, range) => {
let result = languageserver_types::Location { uri, range };
send_response(id, result);
}
LspResponse::Ranges(id, vec_of_uri_range) => {
let result: Vec<languageserver_types::Location> = vec_of_uri_range
.into_iter()
.map(|x| languageserver_types::Location {
uri: x.0,
range: x.1,
})
.collect();
send_response(id, result);
}
LspResponse::WorkspaceEdits(id, vec_of_edits) => {
let mut map_of_edits: HashMap<Url, Vec<languageserver_types::TextEdit>> =
HashMap::new();
for edit in vec_of_edits.into_iter() {
let entry = map_of_edits.entry(edit.0).or_insert(vec![]);
(*entry).push(languageserver_types::TextEdit {
range: edit.1,
new_text: edit.2,
});
}
let result = languageserver_types::WorkspaceEdit {
changes: Some(map_of_edits),
document_changes: None,
};
send_response(id, result);
}
LspResponse::Nothing(id) => {
send_response(id, ());
}
LspResponse::Completions(id, completions) => {
let mut completion_items = vec![];
for completion in completions {
completion_items.push(languageserver_types::CompletionItem::new_simple(
completion.0,
completion.1,
));
}
let result = languageserver_types::CompletionList {
is_incomplete: false,
items: completion_items,
};
send_response(id, result);
}
LspResponse::Initialized(id) => {
let result = languageserver_types::InitializeResult {
capabilities: languageserver_types::ServerCapabilities {
text_document_sync: Some(
languageserver_types::TextDocumentSyncCapability::Kind(
languageserver_types::TextDocumentSyncKind::Incremental,
),
),
hover_provider: Some(true),
completion_provider: None,
signature_help_provider: None,
definition_provider: Some(true),
type_definition_provider: None,
implementation_provider: None,
references_provider: Some(true),
document_highlight_provider: None,
document_symbol_provider: None,
workspace_symbol_provider: None,
code_action_provider: None,
code_lens_provider: None,
document_formatting_provider: None,
document_range_formatting_provider: None,
document_on_type_formatting_provider: None,
rename_provider: Some(
languageserver_types::RenameProviderCapability::Simple(true),
),
color_provider: None,
folding_range_provider: None,
execute_command_provider: None,
workspace: None,
},
};
send_response(id, result);
}
LspResponse::Diagnostics(url, diagnostics) => {
let lsp_diagnostics: Vec<languageserver_types::Diagnostic> = diagnostics
.iter()
.map(|(range, diag)| {
languageserver_types::Diagnostic::new_simple(*range, diag.clone())
})
.collect();
let notice = languageserver_types::PublishDiagnosticsParams {
uri: url,
diagnostics: lsp_diagnostics,
};
send_notification("textDocument/publishDiagnostics".into(), notice);
}
}
}
}
pub fn lsp_serve(send_to_query_channel: Sender<QueryRequest>) {
loop {
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => {
let content_length_items: Vec<&str> = input.split(' ').collect();
if content_length_items[0] == "Content-Length:" {
let num_bytes: usize = content_length_items[1].trim().parse().unwrap();
let mut buffer = vec![0u8; num_bytes + 2];
let _ = io::stdin().read_exact(&mut buffer);
let buffer_string = String::from_utf8(buffer).unwrap();
let command = serde_json::from_str::<LSPCommand>(&buffer_string);
match command {
Ok(LSPCommand::initialize { id, .. }) => {
let _ = send_to_query_channel.send(QueryRequest::Initialize(id));
}
Ok(LSPCommand::initialized) => {
}
Ok(LSPCommand::didOpen { params }) => {
let _ = send_to_query_channel.send(QueryRequest::OpenFile(
params.text_document.uri.clone(),
params.text_document.text.clone(),
));
}
Ok(LSPCommand::didChange { params }) => {
let changes = params
.content_changes
.iter()
.map(|x| (x.range.unwrap(), x.text.clone()))
.collect();
let _ = send_to_query_channel.send(QueryRequest::EditFile(
params.text_document.uri.clone(),
changes,
));
}
Ok(LSPCommand::hover { id, params }) => {
let _ = send_to_query_channel.send(QueryRequest::TypeAtPosition(
id,
params.text_document.uri.clone(),
params.position.clone(),
));
}
Ok(LSPCommand::definition { id, params }) => {
let _ = send_to_query_channel.send(QueryRequest::DefinitionAtPosition(
id,
params.text_document.uri.clone(),
params.position.clone(),
));
}
Ok(LSPCommand::references { id, params }) => {
let _ = send_to_query_channel.send(QueryRequest::ReferencesAtPosition(
id,
params.text_document.uri.clone(),
params.position.clone(),
true,
));
}
Ok(LSPCommand::rename { id, params }) => {
let _ = send_to_query_channel.send(QueryRequest::RenameAtPosition(
id,
params.text_document.uri.clone(),
params.position.clone(),
params.new_name.clone(),
));
}
Ok(LSPCommand::completion { .. }) => {
}
Ok(LSPCommand::completionItemResolve { .. }) => {
}
Ok(LSPCommand::cancelRequest {
params: languageserver_types::CancelParams { id },
}) => match id {
languageserver_types::NumberOrString::Number(_num) => {
}
_ => unimplemented!(
"Non-number cancellation IDs not currently supported"
),
},
Err(e) => eprintln!("Error handling command: {:?}", e),
}
}
}
Err(error) => eprintln!("error: {}", error),
}
}
}