use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering};
use std::{cell::RefCell, error::Error, result::Result};
use crate::handler::RequestHandler;
use crate::{arguments::Arguments, config::Config};
pub mod arguments;
mod config;
mod convert_to_rng;
pub mod diagnostics;
pub mod document_state;
mod document_symbol;
pub mod errors;
mod formatting;
mod handler;
pub mod parol_ls_grammar;
mod parol_ls_grammar_trait;
mod parol_ls_parser;
mod rng;
mod server;
mod symbol_def;
mod utils;
extern crate clap;
extern crate parol_runtime;
use clap::Parser;
use errors::ServerError;
use lsp_server::{Connection, ExtractError, Message, Request, RequestId};
use lsp_types::notification::DidChangeConfiguration;
use lsp_types::request::RegisterCapability;
use lsp_types::{
CodeActionProviderCapability, HoverProviderCapability, InitializeParams, OneOf, RenameOptions,
ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, WorkDoneProgressOptions,
notification::{
DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, Notification,
},
request::{
CodeActionRequest, DocumentSymbolRequest, Formatting, GotoDefinition, HoverRequest,
PrepareRenameRequest, Rename,
},
};
use lsp_types::{Registration, RegistrationParams};
use parol_runtime::log::debug;
use serde::Serialize;
use server::Server;
static GLOBAL_REQUEST_ID: RequestCounter = RequestCounter::new();
struct RequestCounter(AtomicI32);
impl RequestCounter {
const fn new() -> RequestCounter {
Self(AtomicI32::new(1000))
}
fn next() -> RequestId {
let id = GLOBAL_REQUEST_ID.0.fetch_add(1, Ordering::SeqCst);
RequestId::from(id)
}
}
macro_rules! request_match {
($req_type:ty, $server:expr, $connection:expr, $req:expr) => {
match cast::<$req_type>($req) {
Ok((id, params)) => {
let resp = <$req_type>::handle(&mut $server.borrow_mut(), id, params);
$connection.sender.send(Message::Response(resp))?;
continue;
}
Err(err @ ExtractError::JsonError { .. }) => panic!("{:?}", err),
Err(ExtractError::MethodMismatch(req)) => req,
};
};
}
pub(crate) fn send_request<R>(
conn: Arc<Connection>,
params: R::Params,
_server: &RefCell<Server>,
) -> Result<(), ServerError>
where
R: lsp_types::request::Request,
R::Params: Serialize,
{
let r = Request::new(RequestCounter::next(), R::METHOD.to_string(), params);
conn.sender
.send(r.into())
.map_err(|err| ServerError::ProtocolError { err: Box::new(err) })
}
fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
debug!("env logger started");
let args = Arguments::parse();
eprintln!("Starting parol language server");
let (connection, io_threads) = if args.stdio {
Connection::stdio()
} else {
Connection::connect((args.ip_address, args.port_number))?
};
let connection = Arc::new(connection);
let server_capabilities = serde_json::to_value(ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
definition_provider: Some(OneOf::Left(true)),
hover_provider: Some(HoverProviderCapability::Simple(true)),
document_symbol_provider: Some(OneOf::Left(true)),
rename_provider: Some(OneOf::Right(RenameOptions {
prepare_provider: Some(true),
work_done_progress_options: WorkDoneProgressOptions::default(),
})),
document_formatting_provider: Some(OneOf::Left(true)),
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
..Default::default()
})
.unwrap();
let initialization_params: InitializeParams =
serde_json::from_value(connection.initialize(server_capabilities)?).unwrap();
let config = Config::new(initialization_params, args);
main_loop(connection, config)?;
io_threads.join()?;
eprintln!("shutting down parol language server");
Ok(())
}
fn main_loop(connection: Arc<Connection>, config: Config) -> Result<(), Box<dyn Error>> {
eprintln!(
"Initialization params {:#?}",
config.initialization_params()
);
eprintln!(
"Initialization options {:#?}",
config.initialization_options()
);
let server = RefCell::new(server::Server::new(config.lookahead()));
server
.borrow_mut()
.update_configuration(config.config_properties())?;
if config.supports_dynamic_registration_for_change_config() {
send_request::<RegisterCapability>(
connection.clone(),
RegistrationParams {
registrations: vec![Registration {
id: "workspace/didChangeConfiguration".to_string(),
method: "workspace/didChangeConfiguration".to_string(),
register_options: None,
}],
},
&server,
)?;
}
for msg in &connection.receiver {
match msg {
Message::Request(req) => {
eprintln!("got request: {req:?}");
if connection.handle_shutdown(&req)? {
return Ok(());
}
match req.method.as_str() {
<GotoDefinition as lsp_types::request::Request>::METHOD => {
request_match!(GotoDefinition, server, connection, req);
}
<HoverRequest as lsp_types::request::Request>::METHOD => {
request_match!(HoverRequest, server, connection, req);
}
<DocumentSymbolRequest as lsp_types::request::Request>::METHOD => {
request_match!(DocumentSymbolRequest, server, connection, req);
}
<PrepareRenameRequest as lsp_types::request::Request>::METHOD => {
request_match!(PrepareRenameRequest, server, connection, req);
}
<Rename as lsp_types::request::Request>::METHOD => {
request_match!(Rename, server, connection, req);
}
<Formatting as lsp_types::request::Request>::METHOD => {
request_match!(Formatting, server, connection, req);
}
<CodeActionRequest as lsp_types::request::Request>::METHOD => {
request_match!(CodeActionRequest, server, connection, req);
}
_ => {
eprintln!("Unhandled request {}", req.method);
}
}
}
Message::Response(resp) => {
eprintln!("got response: {resp:?}");
}
Message::Notification(not) => {
process_notification(not, connection.clone(), &server)?;
}
}
}
Ok(())
}
fn process_notification(
not: lsp_server::Notification,
connection: Arc<Connection>,
server: &RefCell<Server>,
) -> Result<(), Box<dyn Error>> {
eprintln!("got notification: {not:?}");
match not.method.as_str() {
DidOpenTextDocument::METHOD => server.borrow_mut().handle_open_document(connection, not)?,
DidChangeTextDocument::METHOD => server
.borrow_mut()
.handle_change_document(connection, not)?,
DidCloseTextDocument::METHOD => server.borrow_mut().handle_close_document(not)?,
DidChangeConfiguration::METHOD => server.borrow_mut().handle_changed_configuration(not)?,
_ => {}
}
Ok(())
}
fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
where
R: lsp_types::request::Request,
R::Params: serde::de::DeserializeOwned,
{
req.extract(R::METHOD)
}