Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# Author : Gokul Soumya <[email protected]>

"attribute" = { fg = "yellow" }
"comment" = { fg = "light-gray", modifiers = ["italic"] }
"constant" = { fg = "cyan" }
"constant.numeric" = { fg = "gold" }
"constant.builtin" = { fg = "gold" }
"constant.character.escape" = { fg = "gold" }
"constructor" = { fg = "blue" }
"function" = { fg = "blue" }
"function.builtin" = { fg = "blue" }
"function.macro" = { fg = "purple" }
"keyword" = { fg = "red" }
"keyword.control" = { fg = "purple" }
"keyword.control.import" = { fg = "red" }
"keyword.directive" = { fg = "purple" }
"label" = { fg = "purple" }
"namespace" = { fg = "blue" }
"operator" = { fg = "purple" }
"keyword.operator" = { fg = "purple" }
"property" = { fg = "red" }
"special" = { fg = "blue" }
"string" = { fg = "green" }
"type" = { fg = "yellow" }
# "variable" = { fg = "blue" }
"variable.builtin" = { fg = "blue" }
"variable.parameter" = { fg = "red" }
"variable.other.member" = { fg = "red" }

"markup.heading" = { fg = "red" }
"markup.raw.inline" = { fg = "green" }
"markup.bold" = { fg = "gold", modifiers = ["bold"] }
"markup.italic" = { fg = "purple", modifiers = ["italic"] }
"markup.list" = { fg = "red" }
"markup.quote" = { fg = "yellow" }
"markup.link.url" = { fg = "cyan", modifiers = ["underlined"]}
"markup.link.text" = { fg = "purple" }

"diff.plus" = "green"
"diff.delta" = "gold"
"diff.minus" = "red"

diagnostic = { modifiers = ["underlined"] }
"info" = { fg = "blue", modifiers = ["bold"] }
"hint" = { fg = "green", modifiers = ["bold"] }
"warning" = { fg = "yellow", modifiers = ["bold"] }
"error" = { fg = "red", modifiers = ["bold"] }

"ui.background" = { bg = "black" }

"ui.cursor" = { fg = "white", modifiers = ["reversed"] }
"ui.cursor.primary" = { fg = "white", modifiers = ["reversed"] }
"ui.cursor.match" = { fg = "blue", modifiers = ["underlined"]}

"ui.selection" = { bg = "light-gray" }
"ui.selection.primary" = { bg = "gray" }

"ui.linenr" = { fg = "linenr", modifiers = ["dim"] }
"ui.linenr.selected" = { fg = "white" }

"ui.statusline" = { fg = "white", bg = "light-black" }
"ui.statusline.inactive" = { fg = "light-gray", bg = "light-black" }

"ui.text" = { fg = "white" }
"ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] }

"ui.help" = { bg = "gray" }
"ui.popup" = { bg = "gray" }
"ui.window" = { bg = "gray" }
"ui.menu.selected" = { fg = "black", bg = "blue" }

[palette]

yellow = "#E5C07B"
blue = "#61AFEF"
red = "#E06C75"
purple = "#C678DD"
green = "#98C379"
gold = "#D19A66"
cyan = "#56B6C2"
white = "#ABB2BF"
black = "#282C34"
light-black = "#2C323C"
gray = "#3E4452"
light-gray = "#5C6370"
linenr = "#4B5263"
sync::mpsc::{unbounded_channel, Sender, UnboundedReceiver, UnboundedSender}, }; #[derive(Debug)] pub enum Payload { Request { chan: Sender<Result<Value>>, value: jsonrpc::MethodCall, }, Notification(jsonrpc::Notification), Response(jsonrpc::Output), } /// A type representing all possible values sent from the server to the client. #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] #[serde(deny_unknown_fields)] #[serde(untagged)] enum ServerMessage { /// A regular JSON-RPC request output (single response). Output(jsonrpc::Output), /// A JSON-RPC request or notification. Call(jsonrpc::Call), } #[derive(Debug)] pub struct Transport { id: usize, client_tx: UnboundedSender<(usize, jsonrpc::Call)>, client_rx: UnboundedReceiver<Payload>, pending_requests: HashMap<jsonrpc::Id, Sender<Result<Value>>>, server_stdin: BufWriter<ChildStdin>, server_stdout: BufReader<ChildStdout>, server_stderr: BufReader<ChildStderr>, } impl Transport { pub fn start( server_stdout: BufReader<ChildStdout>, server_stdin: BufWriter<ChildStdin>, server_stderr: BufReader<ChildStderr>, id: usize, ) -> ( UnboundedReceiver<(usize, jsonrpc::Call)>, UnboundedSender<Payload>, ) { let (client_tx, rx) = unbounded_channel(); let (tx, client_rx) = unbounded_channel(); let transport = Self { id, server_stdout, server_stdin, server_stderr, client_tx, client_rx, pending_requests: HashMap::default(), }; tokio::spawn(transport.duplex()); (rx, tx) } async fn recv_server_message( reader: &mut (impl AsyncBufRead + Unpin + Send), buffer: &mut String, ) -> Result<ServerMessage> { let mut content_length = None; loop { buffer.truncate(0); reader.read_line(buffer).await?; let header = buffer.trim(); if header.is_empty() { break; } let mut parts = header.split(": "); match (parts.next(), parts.next(), parts.next()) { (Some("Content-Length"), Some(value), None) => { content_length = Some(value.parse().context("invalid content length")?); } (Some(_), Some(_), None) => {} _ => { return Err(std::io::Error::new( std::io::ErrorKind::Other, "Failed to parse header", ) .into()); } } } let content_length = content_length.context("missing content length")?; //TODO: reuse vector let mut content = vec![0; content_length]; reader.read_exact(&mut content).await?; let msg = String::from_utf8(content).context("invalid utf8 from server")?; info!("<- {}", msg); // try parsing as output (server response) or call (server request) let output: serde_json::Result<ServerMessage> = serde_json::from_str(&msg); Ok(output?) } async fn recv_server_error( err: &mut (impl AsyncBufRead + Unpin + Send), buffer: &mut String, ) -> Result<()> { buffer.truncate(0); err.read_line(buffer).await?; error!("err <- {}", buffer); Ok(()) } async fn send_payload_to_server(&mut self, payload: Payload) -> Result<()> { //TODO: reuse string let json = match payload { Payload::Request { chan, value } => { self.pending_requests.insert(value.id.clone(), chan); serde_json::to_string(&value)? } Payload::Notification(value) => serde_json::to_string(&value)?, Payload::Response(error) => serde_json::to_string(&error)?, }; self.send_string_to_server(json).await } async fn send_string_to_server(&mut self, request: String) -> Result<()> { info!("-> {}", request); // send the headers self.server_stdin .write_all(format!("Content-Length: {}\r\n\r\n", request.len()).as_bytes()) .await?; // send the body self.server_stdin.write_all(request.as_bytes()).await?; self.server_stdin.flush().await?; Ok(()) } async fn process_server_message(&mut self, msg: ServerMessage) -> Result<()> { match msg { ServerMessage::Output(output) => self.process_request_response(output).await?, ServerMessage::Call(call) => { self.client_tx .send((self.id, call)) .context("failed to send a message to server")?; // let notification = Notification::parse(&method, params); } }; Ok(()) } async fn process_request_response(&mut self, output: jsonrpc::Output) -> Result<()> { let (id, result) = match output { jsonrpc::Output::Success(jsonrpc::Success { id, result, .. }) => { info!("<- {}", result); (id, Ok(result)) } jsonrpc::Output::Failure(jsonrpc::Failure { id, error, .. }) => { error!("<- {}", error); (id, Err(error.into())) } }; let tx = self .pending_requests .remove(&id) .expect("pending_request with id not found!"); match tx.send(result).await { Ok(_) => (), Err(_) => error!( "Tried sending response into a closed channel (id={:?}), original request likely timed out", id ), }; Ok(()) } async fn duplex(mut self) { let mut recv_buffer = String::new(); let mut err_buffer = String::new(); loop { tokio::select! { // client -> server msg = self.client_rx.recv() => { match msg { Some(msg) => { self.send_payload_to_server(msg).await.unwrap() }, None => break } } // server -> client msg = Self::recv_server_message(&mut self.server_stdout, &mut recv_buffer) => { match msg { Ok(msg) => { self.process_server_message(msg).await.unwrap(); } Err(_) => { error!("err: <- {:?}", msg); break; }, } } _msg = Self::recv_server_error(&mut self.server_stderr, &mut err_buffer) => {} } } } }