Skip to content

Commit 2cac7d6

Browse files
authored
Refactor language server input/output handling (#967)
- Introduced `Input` and `Output` structs for managing stdin and stdout connections. - Updated the `run` function to accept `Input` and `Output` as parameters. - Removed the `stdio` function in favor of direct instantiation of `Input` and `Output` from stdin and stdout. - Enhanced the `Input` and `Output` structs with new constructors for better encapsulation. - Added `modify_lsp_full_range` method to `ServerSource` for modifying the entire file content. - Updated `did_change_text_document` function to handle full range modifications when no specific range is provided.
1 parent c6c58aa commit 2cac7d6

File tree

5 files changed

+69
-37
lines changed

5 files changed

+69
-37
lines changed

crates/rune-languageserver/src/main.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//!
1717
//! A language server for the Rune Language, an embeddable dynamic programming language for Rust.
1818
use anyhow::{bail, Result};
19+
use rune::languageserver::{Input, Output};
1920
use rune::Options;
2021
use std::env;
2122
use std::path::PathBuf;
@@ -80,7 +81,11 @@ fn main() -> Result<()> {
8081
.enable_all()
8182
.build()?;
8283

83-
let result = runtime.block_on(rune::languageserver::run(context, options));
84+
let result = runtime.block_on(rune::languageserver::run(
85+
context,
86+
options,
87+
(Input::from_stdin()?, Output::from_stdout()?),
88+
));
8489

8590
match result {
8691
Ok(()) => {
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
use anyhow::Result;
22

3-
use crate::{Context, Options};
3+
use crate::{
4+
languageserver::{Input, Output},
5+
Context, Options,
6+
};
47

58
pub(super) async fn run(context: Context) -> Result<()> {
69
let options = Options::from_default_env()?;
7-
crate::languageserver::run(context, options).await?;
10+
crate::languageserver::run(
11+
context,
12+
options,
13+
(Input::from_stdin()?, Output::from_stdout()?),
14+
)
15+
.await?;
816
Ok(())
917
}

crates/rune/src/languageserver/connection.rs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
use core::fmt;
22

3-
use rust_alloc::sync::Arc;
4-
53
use anyhow::{anyhow, bail, Result};
6-
use tokio::io;
74
use tokio::io::{
8-
AsyncBufRead, AsyncBufReadExt as _, AsyncReadExt as _, AsyncWriteExt as _, BufReader,
5+
self, AsyncBufRead, AsyncBufReadExt as _, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _,
6+
BufReader,
97
};
108
use tokio::sync::Mutex;
119

@@ -19,15 +17,30 @@ pub(super) struct Frame<'a> {
1917
}
2018

2119
/// Input connection.
22-
pub(super) struct Input {
20+
pub struct Input {
2321
buf: rust_alloc::vec::Vec<u8>,
24-
stdin: BufReader<io::Stdin>,
22+
reader: rust_alloc::boxed::Box<dyn AsyncBufRead + Unpin>,
2523
}
2624

2725
impl Input {
26+
/// Create a new input connection.
27+
pub fn new(reader: rust_alloc::boxed::Box<dyn AsyncBufRead + Unpin>) -> Self {
28+
Self {
29+
buf: rust_alloc::vec::Vec::new(),
30+
reader,
31+
}
32+
}
33+
34+
/// Create a new input connection from stdin.
35+
pub fn from_stdin() -> Result<Self> {
36+
let stdin = io::stdin();
37+
let reader = rust_alloc::boxed::Box::new(BufReader::new(stdin));
38+
Ok(Self::new(reader))
39+
}
40+
2841
/// Get the next input frame.
2942
pub(super) async fn next(&mut self) -> Result<Option<Frame<'_>>> {
30-
let headers = match Headers::read(&mut self.buf, &mut self.stdin).await? {
43+
let headers = match Headers::read(&mut self.buf, &mut self.reader).await? {
3144
Some(headers) => headers,
3245
None => return Ok(None),
3346
};
@@ -40,18 +53,31 @@ impl Input {
4053
};
4154

4255
self.buf.resize(length, 0u8);
43-
self.stdin.read_exact(&mut self.buf[..]).await?;
56+
self.reader.read_exact(&mut self.buf[..]).await?;
4457
Ok(Some(Frame { content: &self.buf }))
4558
}
4659
}
4760

4861
/// Output connection.
49-
#[derive(Clone)]
50-
pub(super) struct Output {
51-
stdout: Arc<Mutex<io::Stdout>>,
62+
pub struct Output {
63+
writer: Mutex<rust_alloc::boxed::Box<dyn AsyncWrite + Unpin>>,
5264
}
5365

5466
impl Output {
67+
/// Create a new output connection.
68+
pub fn new(writer: rust_alloc::boxed::Box<dyn AsyncWrite + Unpin>) -> Self {
69+
Self {
70+
writer: Mutex::new(writer),
71+
}
72+
}
73+
74+
/// Create a new output connection from stdout.
75+
pub fn from_stdout() -> Result<Self> {
76+
let stdout = io::stdout();
77+
let writer = rust_alloc::boxed::Box::new(stdout);
78+
Ok(Self::new(writer))
79+
}
80+
5581
/// Send the given response.
5682
pub(super) async fn response<R>(&self, id: Option<envelope::RequestId>, result: R) -> Result<()>
5783
where
@@ -148,30 +174,13 @@ impl Output {
148174
write!(m, "\r\n")?;
149175
m.append(bytes);
150176

151-
let mut stdout = self.stdout.lock().await;
177+
let mut stdout = self.writer.lock().await;
152178
stdout.write_all(&m).await?;
153179
stdout.flush().await?;
154180
Ok(())
155181
}
156182
}
157183

158-
/// Setup a stdin/stdout connection.
159-
pub(super) fn stdio() -> Result<(Input, Output)> {
160-
let stdin = io::stdin();
161-
let stdout = io::stdout();
162-
163-
let input = Input {
164-
buf: rust_alloc::vec::Vec::new(),
165-
stdin: BufReader::new(stdin),
166-
};
167-
168-
let output = Output {
169-
stdout: Arc::new(Mutex::new(stdout)),
170-
};
171-
172-
Ok((input, output))
173-
}
174-
175184
#[derive(Debug)]
176185
pub(super) enum ContentType {
177186
JsonRPC,

crates/rune/src/languageserver/mod.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use serde::Deserialize;
1818
use tokio::sync::Notify;
1919

2020
use crate::alloc::String;
21-
use crate::languageserver::connection::stdio;
2221
use crate::languageserver::envelope::Code;
2322
use crate::languageserver::state::State;
2423
use crate::support::Result;
@@ -27,15 +26,19 @@ use crate::{Context, Options};
2726

2827
use self::state::StateEncoding;
2928

29+
pub use connection::{Input, Output};
30+
3031
enum Language {
3132
Rune,
3233
Other,
3334
}
3435

3536
/// Run a language server with the given options.
36-
pub async fn run(context: Context, options: Options) -> Result<()> {
37-
let (mut input, output) = stdio()?;
38-
37+
pub async fn run(
38+
context: Context,
39+
options: Options,
40+
(mut input, output): (Input, Output),
41+
) -> Result<()> {
3942
let rebuild_notify = Notify::new();
4043

4144
let rebuild = rebuild_notify.notified();
@@ -322,8 +325,10 @@ async fn did_change_text_document(
322325
for change in params.content_changes {
323326
if let Some(range) = change.range {
324327
source.modify_lsp_range(&s.encoding, range, &change.text)?;
325-
interest = true;
328+
} else {
329+
source.modify_lsp_full_range(&change.text)?;
326330
}
331+
interest = true;
327332
}
328333
} else {
329334
tracing::warn!(

crates/rune/src/languageserver/state.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,11 @@ impl ServerSource {
894894
self.modify_range(start, end, content)
895895
}
896896

897+
/// Modify the full range of the file.
898+
pub(super) fn modify_lsp_full_range(&mut self, content: &str) -> Result<()> {
899+
self.modify_range(0, self.content.len_chars(), content)
900+
}
901+
897902
fn modify_range(&mut self, start: usize, end: usize, content: &str) -> Result<()> {
898903
self.content.try_remove(start..end)?;
899904

0 commit comments

Comments
 (0)