#![allow(clippy::pattern_type_mismatch)] mod json_rpc;
mod lsp;
pub use lsp::TranslationError;
use {
core::cell::RefCell,
fehler::{throw, throws},
lsp::{ClientMessage, Event, Tool},
lsp_types::{
DidCloseTextDocumentParams, DidOpenTextDocumentParams, DocumentSymbolParams,
DocumentSymbolResponse, PartialResultParams, TextDocumentIdentifier, Url,
WorkDoneProgressParams,
},
market::{
channel::{create, Crossbeam, CrossbeamConsumer, CrossbeamProducer, Size},
thread::{self, Thread},
Consumer, Producer,
},
std::{
process::{Command, ExitStatus},
rc::Rc,
},
};
#[derive(Clone, Copy, Debug, parse_display::Display, PartialEq)]
#[display(style = "lowercase")]
#[non_exhaustive]
pub enum Language {
Rust,
}
struct TongueThreadParams {
root_dir: Url,
transmission_consumer: CrossbeamConsumer<Transmission>,
reception_producer: CrossbeamProducer<Reception>,
status_producer: CrossbeamProducer<ExitStatus>,
}
#[derive(Debug)]
pub struct Tongue {
thread: Thread<(), TranslationError>,
status_consumer: CrossbeamConsumer<ExitStatus>,
transmitter: CrossbeamProducer<Transmission>,
receiver: CrossbeamConsumer<Reception>,
}
impl Tongue {
#[inline]
#[must_use]
pub fn new(root_dir: &Url) -> Self {
let (transmitter, transmission_consumer) = create::<Crossbeam<Transmission>>(
"Tongue Transmission Channel".to_string(),
Size::Infinite,
);
let (reception_producer, receiver) =
create::<Crossbeam<Reception>>("Tongue Reception Channel".to_string(), Size::Infinite);
let (status_producer, status_consumer) =
create::<Crossbeam<ExitStatus>>("Tongue Status Channel".to_string(), Size::Infinite);
let params = TongueThreadParams {
root_dir: root_dir.clone(),
transmission_consumer,
reception_producer,
status_producer,
};
Self {
thread: Thread::new(
"docuglot tongue".to_string(),
thread::Kind::Single,
params,
Self::thread_fn,
),
status_consumer,
transmitter,
receiver,
}
}
#[throws(TranslationError)]
fn thread_fn(params: &mut TongueThreadParams) {
let default_translator = Rc::new(RefCell::new(Tool::new_finished(Command::new("echo"))?));
let rust_translator = Rc::new(RefCell::new(Tool::new(
Command::new("rust-analyzer"),
params.root_dir.clone(),
)?));
let translators = [Rc::clone(&rust_translator), Rc::clone(&default_translator)];
while !translators
.iter()
.map(|t| t.borrow().is_waiting_exit())
.all(|x| x)
{
for good in params.transmission_consumer.goods() {
let transmission = good?;
transmission
.language()
.map_or(&default_translator, |language| match language {
Language::Rust => &rust_translator,
})
.borrow_mut()
.transmit(vec![transmission.into()])?;
}
for translator in &translators {
let mut receptions = Vec::new();
let mut t = translator.borrow_mut();
for event in t.process_receptions()? {
match event {
Event::SendMessages(messages) => t.transmit(messages)?,
Event::Error(error) => throw!(error),
Event::DocumentSymbol(document_symbol) => {
receptions.push(Reception::DocumentSymbols(document_symbol));
}
}
}
params.reception_producer.produce_all(receptions)?;
t.log_errors();
}
}
for translator in &translators {
params
.status_producer
.produce(translator.borrow().server().demand()?)?;
}
}
#[inline]
#[must_use]
pub const fn thread(&self) -> &Thread<(), TranslationError> {
&self.thread
}
#[inline]
#[must_use]
pub const fn transmitter(&self) -> &CrossbeamProducer<Transmission> {
&self.transmitter
}
#[inline]
#[must_use]
pub const fn receiver(&self) -> &CrossbeamConsumer<Reception> {
&self.receiver
}
}
#[derive(Debug, parse_display::Display)]
#[display("")]
pub enum Transmission {
OpenDoc {
doc: lsp_types::TextDocumentItem,
},
CloseDoc {
doc: TextDocumentIdentifier,
},
GetDocumentSymbol {
doc: TextDocumentIdentifier,
},
Shutdown,
}
impl Transmission {
#[inline]
#[must_use]
pub const fn close_doc(doc: TextDocumentIdentifier) -> Self {
Self::CloseDoc { doc }
}
#[allow(clippy::unused_self)] const fn language(&self) -> Option<Language> {
Some(Language::Rust)
}
}
impl From<Transmission> for ClientMessage {
#[inline]
fn from(transmission: Transmission) -> Self {
match transmission {
Transmission::OpenDoc { doc } => {
Self::OpenDoc(DidOpenTextDocumentParams { text_document: doc })
}
Transmission::CloseDoc { doc } => {
Self::CloseDoc(DidCloseTextDocumentParams { text_document: doc })
}
Transmission::GetDocumentSymbol { doc } => Self::DocumentSymbol(DocumentSymbolParams {
text_document: doc,
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
}),
Transmission::Shutdown => Self::Shutdown,
}
}
}
#[derive(Debug, parse_display::Display)]
#[display(style = "CamelCase")]
pub enum Reception {
#[display("{0:?}")]
DocumentSymbols(DocumentSymbolResponse),
}