1use std::collections::BTreeMap;
2use std::env;
3
4use anyhow::{bail, Context as _, Result};
5use tokio::io::{self, BufStream};
6
7use crate::config::Config;
8use crate::lsp::ext::{LspMuxOptions, Request};
9use crate::lsp::jsonrpc::Message;
10use crate::lsp::transport::{LspReader, LspWriter};
11use crate::lsp::{InitializationOptions, InitializeParams};
12use crate::socketwrapper::Stream;
13
14pub async fn run(config: &Config, server: String, args: Vec<String>) -> Result<()> {
15 let cwd = env::current_dir()
16 .ok()
17 .and_then(|path| path.to_str().map(String::from));
18
19 let mut env = BTreeMap::new();
20 for key in &config.pass_environment {
21 if let Ok(val) = std::env::var(key) {
22 env.insert(key.clone(), val);
23 }
24 }
25
26 let mut stream = Stream::connect(&config.connect)
27 .await
28 .context("connecting to server")?;
29 let mut stdio = BufStream::new(io::join(io::stdin(), io::stdout()));
30
31 let mut reader = LspReader::new(&mut stdio, "client");
33 let mut req = match reader.read_message().await?.context("stdin closed")? {
34 Message::Request(req) if req.method == "initialize" => req,
35 _ => bail!("first client message was not initialize request"),
36 };
37
38 let mut params = serde_json::from_value::<InitializeParams>(req.params)
40 .context("parse initialize request params")?;
41 params
42 .initialization_options
43 .get_or_insert_with(InitializationOptions::default)
44 .lsp_mux
45 .get_or_insert_with(|| LspMuxOptions {
46 version: LspMuxOptions::PROTOCOL_VERSION.to_owned(),
47 method: Request::Connect {
48 server,
49 args,
50 env,
51 cwd,
52 },
53 });
54 req.params = serde_json::to_value(params).expect("BUG: invalid data");
55
56 let mut writer = LspWriter::new(&mut stream, "lspmux");
58 writer
59 .write_message(&req.into())
60 .await
61 .context("forward initialize request")?;
62
63 io::copy_bidirectional(&mut stream, &mut stdio)
65 .await
66 .context("io error")?;
67 Ok(())
68}