1use std::env;
2
3use anyhow::{bail, Context, Result};
4use serde::de::{DeserializeOwned, IgnoredAny};
5use tokio::io::BufReader;
6
7use crate::config::Config;
8use crate::lsp::ext::{self, LspMuxOptions, StatusResponse};
9use crate::lsp::jsonrpc::{Message, Request, RequestId, Version};
10use crate::lsp::transport::{LspReader, LspWriter};
11use crate::lsp::{InitializationOptions, InitializeParams};
12use crate::socketwrapper::Stream;
13
14pub async fn ext_request<T>(config: &Config, method: ext::Request) -> Result<T>
15where
16 T: DeserializeOwned,
17{
18 let (reader, writer) = Stream::connect(&config.connect)
19 .await
20 .context("connect")?
21 .into_split();
22 let mut writer = LspWriter::new(writer, "lspmux");
23 let mut reader = LspReader::new(BufReader::new(reader), "lspmux");
24
25 writer
26 .write_message(
27 &Request {
28 jsonrpc: Version,
29 method: "initialize".into(),
30 params: serde_json::to_value(InitializeParams {
31 initialization_options: Some(InitializationOptions {
32 lsp_mux: Some(LspMuxOptions {
33 version: LspMuxOptions::PROTOCOL_VERSION.into(),
34 method,
35 }),
36 other_options: serde_json::Map::default(),
37 }),
38 process_id: None,
39 client_info: None,
40 locale: None,
41 root_path: None,
42 root_uri: None,
43 capabilities: None,
44 trace: None,
45 workspace_folders: Vec::new(),
46 })
47 .unwrap(),
48 id: RequestId::Number(0),
49 }
50 .into(),
51 )
52 .await
53 .context("send lspmux request")?;
54
55 match reader
56 .read_message()
57 .await
58 .context("read lspmux response")?
59 .context("stream ended")?
60 .into_response()
61 .context("received message was not a response")?
62 {
63 Ok(success) => serde_json::from_value(success.result).context("parse response result"),
64 Err(error) => bail!(
65 "received error response: {msg:?}",
66 msg = Message::ResponseError(error),
67 ),
68 }
69}
70
71pub fn config(config: &Config) -> Result<()> {
72 println!("{config:#?}");
73 Ok(())
74}
75
76pub async fn status(config: &Config, json: bool) -> Result<()> {
77 let res = ext_request::<StatusResponse>(config, ext::Request::Status {}).await?;
78
79 if json {
80 let json = serde_json::to_string(&res).unwrap();
81 println!("{json}");
82 return Ok(());
83 }
84
85 for instance in res.instances {
86 println!("- Instance");
87 println!(" pid: {}", instance.pid);
88 println!(" server: {:?} {:?}", instance.server, instance.args);
89 if !instance.env.is_empty() {
90 println!(" server env:");
91 for (key, val) in instance.env {
92 println!(" {key} = {val}");
93 }
94 }
95 println!(" path: {:?}", instance.workspace_root);
96 let now = time::OffsetDateTime::now_utc().unix_timestamp();
97 println!(" last used: {}s ago", now - instance.last_used);
98 println!(" registered dynamic capabilities:");
99 for cap in instance.registered_dyn_capabilities {
100 println!(" - {}", cap);
101 }
102 println!(" clients:");
103 for client in instance.clients {
104 println!(" - Client");
105 println!(" id: {}", client.id);
106 println!(" files:");
107 for file in client.files {
108 println!(" - {}", file);
109 }
110 }
111 }
112 Ok(())
113}
114
115pub async fn reload(config: &Config) -> Result<()> {
116 let cwd = env::current_dir()
117 .context("unable to get current_dir")?
118 .to_str()
119 .context("current_dir is not valid utf-8")?
120 .to_owned();
121 ext_request::<IgnoredAny>(config, ext::Request::Reload { cwd }).await?;
122 Ok(())
123}