178 lines
5.9 KiB
Rust
178 lines
5.9 KiB
Rust
use anyhow::Result;
|
|
use log;
|
|
use lsp_server::{ Connection, Message, Response };
|
|
use lsp_server::Notification as ServerNotification;
|
|
use lsp_server::Request as ServerRequest;
|
|
use lsp_types as lsp;
|
|
use lsp_types::notification::{
|
|
DidChangeTextDocument,
|
|
DidCloseTextDocument,
|
|
DidOpenTextDocument,
|
|
LogMessage,
|
|
Notification as LspNotification,
|
|
};
|
|
use lsp_types::request::{
|
|
Completion,
|
|
DocumentSymbolRequest,
|
|
GotoDefinition,
|
|
Request as LspRequest,
|
|
};
|
|
use serde_json::Value;
|
|
|
|
use analysis::{ collect_procs, ProcIndex };
|
|
use features::{ completion, definition, document_symbol };
|
|
use text::DocumentStore;
|
|
|
|
pub struct Server {
|
|
conn: Connection,
|
|
docs: DocumentStore,
|
|
procs: ProcIndex,
|
|
}
|
|
|
|
impl Server {
|
|
pub fn new(conn: Connection) -> Self {
|
|
Self {
|
|
conn,
|
|
docs: DocumentStore::default(),
|
|
procs: ProcIndex::default(),
|
|
}
|
|
}
|
|
|
|
pub fn run(mut self) -> Result<()> {
|
|
// Initialize handshake
|
|
let (id, _params) = self.conn.initialize_start()?;
|
|
let result = lsp::InitializeResult {
|
|
capabilities: server_capabilities(),
|
|
server_info: Some(lsp::ServerInfo {
|
|
name: "tcl-lsp".into(),
|
|
version: Some(env!("CARGO_PKG_VERSION").into()),
|
|
}),
|
|
};
|
|
self.conn.initialize_finish(id, serde_json::to_value(result)?)?;
|
|
|
|
let _ = self.log("tcl-lsp ready");
|
|
|
|
let receiver = self.conn.receiver.clone();
|
|
|
|
// main loop
|
|
for msg in receiver.iter() {
|
|
match msg {
|
|
lsp_server::Message::Request(req) => {
|
|
if self.conn.handle_shutdown(&req)? {
|
|
break;
|
|
}
|
|
if let Err(e) = self.on_request(req) {
|
|
log::error!("request error: {e:#}");
|
|
}
|
|
}
|
|
lsp_server::Message::Notification(n) => {
|
|
if let Err(e) = self.on_notification(n) {
|
|
log::error!("notification error: {e:#}");
|
|
}
|
|
}
|
|
lsp_server::Message::Response(_) => {}
|
|
}
|
|
}
|
|
|
|
let _ = self.log("tcl-lsp stopped");
|
|
Ok(())
|
|
}
|
|
|
|
fn on_notification(&mut self, n: ServerNotification) -> Result<()> {
|
|
match n.method.as_str() {
|
|
DidOpenTextDocument::METHOD => {
|
|
let params: lsp::DidOpenTextDocumentParams = serde_json::from_value(n.params)?;
|
|
let uri = params.text_document.uri.clone();
|
|
self.docs.open(params.text_document)?;
|
|
self.reindex(&uri)?;
|
|
self.log(&format!("Opened {:?}", uri))?;
|
|
}
|
|
DidChangeTextDocument::METHOD => {
|
|
let params: lsp::DidChangeTextDocumentParams = serde_json::from_value(n.params)?;
|
|
let uri = params.text_document.uri.clone();
|
|
// FULL sync: we expect one change with whole text
|
|
self.docs.apply_full_change(&uri, ¶ms.content_changes)?;
|
|
self.reindex(&uri)?;
|
|
}
|
|
DidCloseTextDocument::METHOD => {
|
|
let params: lsp::DidCloseTextDocumentParams = serde_json::from_value(n.params)?;
|
|
self.procs.remove(¶ms.text_document.uri);
|
|
self.docs.close(¶ms.text_document.uri);
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn on_request(&mut self, req: ServerRequest) -> Result<()> {
|
|
match req.method.as_str() {
|
|
DocumentSymbolRequest::METHOD => {
|
|
let params: lsp::DocumentSymbolParams = serde_json::from_value(req.params)?;
|
|
let uri = params.text_document.uri;
|
|
let result = document_symbol(&self.docs, &self.procs, &uri)?;
|
|
send_ok(&self.conn, req.id, result)?;
|
|
}
|
|
GotoDefinition::METHOD => {
|
|
let params: lsp::GotoDefinitionParams = serde_json::from_value(req.params)?;
|
|
let result = definition(&self.docs, &self.procs, ¶ms)?;
|
|
send_ok(&self.conn, req.id, result)?;
|
|
}
|
|
Completion::METHOD => {
|
|
let params: lsp::CompletionParams = serde_json::from_value(req.params)?;
|
|
let _ = self.log("Completion");
|
|
let result = completion(&self.docs, &self.procs, ¶ms)?;
|
|
send_ok(&self.conn, req.id, result)?;
|
|
}
|
|
other => {
|
|
let resp = Response::new_err(
|
|
req.id,
|
|
lsp::error_codes::REQUEST_FAILED as i32,
|
|
format!("Unknown method: {other}")
|
|
);
|
|
self.conn.sender.send(Message::Response(resp))?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn reindex(&mut self, uri: &lsp::Uri) -> Result<()> {
|
|
if let Some(doc) = self.docs.get(uri) {
|
|
if let Some(tree) = doc.tree() {
|
|
let text = doc.text();
|
|
let procs = collect_procs(tree, &text);
|
|
self.procs.insert(uri.clone(), procs);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn log(&self, message: &str) -> Result<()> {
|
|
let notif = LogMessage::METHOD.to_string();
|
|
let params = lsp::LogMessageParams {
|
|
typ: lsp::MessageType::INFO,
|
|
message: message.to_string(),
|
|
};
|
|
self.conn.sender.send(Message::Notification(ServerNotification::new(notif, params)))?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn send_ok<T: serde::Serialize>(
|
|
conn: &Connection,
|
|
id: lsp_server::RequestId,
|
|
result: T
|
|
) -> Result<()> {
|
|
let v: Value = serde_json::to_value(result)?;
|
|
conn.sender.send(Message::Response(Response::new_ok(id, v)))?;
|
|
Ok(())
|
|
}
|
|
|
|
fn server_capabilities() -> lsp::ServerCapabilities {
|
|
lsp::ServerCapabilities {
|
|
completion_provider: Some(lsp::CompletionOptions {
|
|
..Default::default()
|
|
}),
|
|
..Default::default()
|
|
}
|
|
}
|