use crate::proto::{send_msg, Out};
use crate::stryke_runner::resolve_stryke;
use serde_json::json;
use std::io::{BufRead, BufReader, Read, Write};
use std::process::{Child, ChildStdin, Command, Stdio};
pub struct StrykeLsp {
child: Child,
stdin: ChildStdin,
}
impl Drop for StrykeLsp {
fn drop(&mut self) {
let _ = self.child.kill();
let _ = self.child.wait();
}
}
impl StrykeLsp {
pub fn start(out: &Out) -> Result<StrykeLsp, String> {
let stryke =
resolve_stryke().ok_or_else(|| "stryke binary not found on PATH".to_string())?;
let mut child = Command::new(&stryke)
.arg("--lsp")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()
.map_err(|e| format!("spawn stryke --lsp: {e}"))?;
let stdin = child.stdin.take().ok_or("no stdin on stryke --lsp")?;
let stdout = child.stdout.take().ok_or("no stdout on stryke --lsp")?;
let out = out.clone();
std::thread::Builder::new()
.name("stryke-lsp-reader".into())
.spawn(move || {
let mut reader = BufReader::new(stdout);
loop {
let mut content_length: usize = 0;
loop {
let mut line = String::new();
match reader.read_line(&mut line) {
Ok(0) | Err(_) => {
let _ = send_msg(&out, &json!({"ev": "stryke-lsp-exit"}));
return;
}
Ok(_) => {}
}
let trimmed = line.trim_end_matches(['\r', '\n']);
if trimmed.is_empty() {
break;
}
if let Some(v) = trimmed.strip_prefix("Content-Length:") {
content_length = v.trim().parse().unwrap_or(0);
}
}
if content_length == 0 {
continue;
}
let mut buf = vec![0u8; content_length];
if reader.read_exact(&mut buf).is_err() {
let _ = send_msg(&out, &json!({"ev": "stryke-lsp-exit"}));
return;
}
let payload = String::from_utf8_lossy(&buf).into_owned();
let _ = send_msg(&out, &json!({"ev": "stryke-lsp-rx", "message": payload}));
}
})
.map_err(|e| e.to_string())?;
Ok(StrykeLsp { child, stdin })
}
pub fn send(&mut self, message: &str) -> Result<(), String> {
let header = format!("Content-Length: {}\r\n\r\n", message.len());
self.stdin
.write_all(header.as_bytes())
.and_then(|_| self.stdin.write_all(message.as_bytes()))
.and_then(|_| self.stdin.flush())
.map_err(|e| format!("write to stryke --lsp: {e}"))
}
}