on master help sign in
Init lsp through the registry.
commit955cb81687db139422875d02b534d753dac5603f authorBlaž Hrastnik <blaz@mxxn.io> dateWed 23 Dec 2020, 15:50 (6 years ago) parentc8424c6
4 files changed+198−25
helix-lsp/src/client.rs+20−14 helix-lsp/src/lib.rs+28−5 helix-lsp/src/select_all.rs+143 helix-term/src/application.rs+7−6
helix-lsp/src/client.rs+20−14
@@ -23,13 +23,16 @@ use smol::{
2323 Executor,
2424};
2525
26+fn text_document_identifier(doc: &Document) -> lsp::TextDocumentIdentifier {
27+ lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(doc.path().unwrap()).unwrap())
28+}
29+
2630pub struct Client {
2731 _process: Child,
2832 stderr: BufReader<ChildStderr>,
2933
3034 outgoing: Sender<Payload>,
31− pub incoming: Receiver<Call>,
32−
35+ // pub incoming: Receiver<Call>,
3336 pub request_counter: AtomicU64,
3437
3538 capabilities: Option<lsp::ServerCapabilities>,
@@ -38,7 +41,7 @@ pub struct Client {
3841}
3942
4043impl Client {
41− pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> Self {
44+ pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> (Self, Receiver<Call>) {
4245 let mut process = Command::new(cmd)
4346 .args(args)
4447 .stdin(Stdio::piped())
@@ -55,18 +58,22 @@ impl Client {
5558
5659 let (incoming, outgoing) = Transport::start(ex, reader, writer);
5760
58− Client {
61+ let client = Client {
5962 _process: process,
6063 stderr,
6164
6265 outgoing,
63− incoming,
64−
66+ // incoming,
6567 request_counter: AtomicU64::new(0),
6668
6769 capabilities: None,
6870 // diagnostics: HashMap::new(),
69− }
71+ };
72+
73+ // TODO: async client.initialize()
74+ // maybe use an arc<atomic> flag
75+
76+ (client, incoming)
7077 }
7178
7279 fn next_request_id(&self) -> jsonrpc::Id {
@@ -219,7 +226,7 @@ impl Client {
219226 // Text document
220227 // -------------------------------------------------------------------------------------------
221228
222− pub async fn text_document_did_open(&mut self, doc: &Document) -> Result<()> {
229+ pub async fn text_document_did_open(&self, doc: &Document) -> Result<()> {
223230 self.notify::<lsp::notification::DidOpenTextDocument>(lsp::DidOpenTextDocumentParams {
224231 text_document: lsp::TextDocumentItem {
225232 uri: lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
@@ -295,7 +302,7 @@ impl Client {
295302
296303 // TODO: trigger any time history.commit_revision happens
297304 pub async fn text_document_did_change(
298− &mut self,
305+ &self,
299306 doc: &Document,
300307 transaction: &Transaction,
301308 ) -> Result<()> {
@@ -328,6 +335,7 @@ impl Client {
328335
329336 self.notify::<lsp::notification::DidChangeTextDocument>(lsp::DidChangeTextDocumentParams {
330337 text_document: lsp::VersionedTextDocumentIdentifier::new(
338+ // TODO: doc.into() Url
331339 lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
332340 doc.version,
333341 ),
@@ -338,18 +346,16 @@ impl Client {
338346
339347 // TODO: impl into() TextDocumentIdentifier / VersionedTextDocumentIdentifier for Document.
340348
341− pub async fn text_document_did_close(&mut self, doc: &Document) -> Result<()> {
349+ pub async fn text_document_did_close(&self, doc: &Document) -> Result<()> {
342350 self.notify::<lsp::notification::DidCloseTextDocument>(lsp::DidCloseTextDocumentParams {
343− text_document: lsp::TextDocumentIdentifier::new(
344− lsp::Url::from_file_path(doc.path().unwrap()).unwrap(),
345− ),
351+ text_document: text_document_identifier(doc),
346352 })
347353 .await
348354 }
349355
350356 // will_save / will_save_wait_until
351357
352− pub async fn text_document_did_save(&mut self) -> anyhow::Result<()> {
358+ pub async fn text_document_did_save(&self) -> anyhow::Result<()> {
353359 unimplemented!()
354360 }
355361}
helix-lsp/src/lib.rs+28−5
@@ -1,4 +1,5 @@
11mod client;
2+mod select_all;
23mod transport;
34
45pub use jsonrpc_core as jsonrpc;
@@ -69,16 +70,24 @@ pub use jsonrpc::Call;
6970
7071type LanguageId = String;
7172
72−pub static REGISTRY: Lazy<Registry> = Lazy::new(Registry::init);
73+use crate::select_all::SelectAll;
74+use smol::channel::Receiver;
7375
7476pub struct Registry {
7577 inner: HashMap<LanguageId, OnceCell<Arc<Client>>>,
78+
79+ pub incoming: SelectAll<Receiver<Call>>,
7680}
7781
7882impl Registry {
79− pub fn init() -> Self {
83+ pub fn new() -> Self {
84+ let mut inner = HashMap::new();
85+
86+ inner.insert("rust".to_string(), OnceCell::new());
87+
8088 Self {
81− inner: HashMap::new(),
89+ inner,
90+ incoming: SelectAll::new(),
8291 }
8392 }
8493
@@ -91,8 +100,12 @@ impl Registry {
91100 // TODO: lookup defaults for id (name, args)
92101
93102 // initialize a new client
94− let client = Client::start(&ex, "rust-analyzer", &[]);
95− // TODO: also call initialize().await()
103+ let (mut client, incoming) = Client::start(&ex, "rust-analyzer", &[]);
104+ // TODO: run this async without blocking
105+ smol::block_on(client.initialize()).unwrap();
106+
107+ self.incoming.push(incoming);
108+
96109 Arc::new(client)
97110 })
98111 })
@@ -115,3 +128,13 @@ impl Registry {
115128// -> PROBLEM: how do you trigger an update on the editor side when data updates?
116129//
117130// -> The data updates should pull all events until we run out so we don't frequently re-render
131+//
132+//
133+// v2:
134+//
135+// there should be a registry of lsp clients, one per language type (or workspace).
136+// the clients should lazy init on first access
137+// the client.initialize() should be called async and we buffer any requests until that completes
138+// there needs to be a way to process incoming lsp messages from all clients.
139+// -> notifications need to be dispatched to wherever
140+// -> requests need to generate a reply and travel back to the same lsp!
helix-lsp/src/select_all.rsnew file+143
@@ -0,0 +1,143 @@
1+//! An unbounded set of streams
2+
3+use core::fmt::{self, Debug};
4+use core::iter::FromIterator;
5+use core::pin::Pin;
6+
7+use smol::ready;
8+use smol::stream::Stream;
9+use std::task::{Context, Poll};
10+
11+use futures_util::stream::FusedStream;
12+use futures_util::stream::{FuturesUnordered, StreamExt, StreamFuture};
13+
14+/// An unbounded set of streams
15+///
16+/// This "combinator" provides the ability to maintain a set of streams
17+/// and drive them all to completion.
18+///
19+/// Streams are pushed into this set and their realized values are
20+/// yielded as they become ready. Streams will only be polled when they
21+/// generate notifications. This allows to coordinate a large number of streams.
22+///
23+/// Note that you can create a ready-made `SelectAll` via the
24+/// `select_all` function in the `stream` module, or you can start with an
25+/// empty set with the `SelectAll::new` constructor.
26+#[must_use = "streams do nothing unless polled"]
27+pub struct SelectAll<St> {
28+ inner: FuturesUnordered<StreamFuture<St>>,
29+}
30+
31+impl<St: Debug> Debug for SelectAll<St> {
32+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33+ write!(f, "SelectAll {{ ... }}")
34+ }
35+}
36+
37+impl<St: Stream + Unpin> SelectAll<St> {
38+ /// Constructs a new, empty `SelectAll`
39+ ///
40+ /// The returned `SelectAll` does not contain any streams and, in this
41+ /// state, `SelectAll::poll` will return `Poll::Ready(None)`.
42+ pub fn new() -> Self {
43+ Self {
44+ inner: FuturesUnordered::new(),
45+ }
46+ }
47+
48+ /// Returns the number of streams contained in the set.
49+ ///
50+ /// This represents the total number of in-flight streams.
51+ pub fn len(&self) -> usize {
52+ self.inner.len()
53+ }
54+
55+ /// Returns `true` if the set contains no streams
56+ pub fn is_empty(&self) -> bool {
57+ self.inner.is_empty()
58+ }
59+
60+ /// Push a stream into the set.
61+ ///
62+ /// This function submits the given stream to the set for managing. This
63+ /// function will not call `poll` on the submitted stream. The caller must
64+ /// ensure that `SelectAll::poll` is called in order to receive task
65+ /// notifications.
66+ pub fn push(&self, stream: St) {
67+ self.inner.push(stream.into_future());
68+ }
69+}
70+
71+impl<St: Stream + Unpin> Default for SelectAll<St> {
72+ fn default() -> Self {
73+ Self::new()
74+ }
75+}
76+
77+impl<St: Stream + Unpin> Stream for SelectAll<St> {
78+ type Item = St::Item;
79+
80+ fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
81+ loop {
82+ match ready!(self.inner.poll_next_unpin(cx)) {
83+ Some((Some(item), remaining)) => {
84+ self.push(remaining);
85+ return Poll::Ready(Some(item));
86+ }
87+ Some((None, _)) => {
88+ // `FuturesUnordered` thinks it isn't terminated
89+ // because it yielded a Some.
90+ // We do not return, but poll `FuturesUnordered`
91+ // in the next loop iteration.
92+ }
93+ None => return Poll::Ready(None),
94+ }
95+ }
96+ }
97+}
98+
99+impl<St: Stream + Unpin> FusedStream for SelectAll<St> {
100+ fn is_terminated(&self) -> bool {
101+ self.inner.is_terminated()
102+ }
103+}
104+
105+/// Convert a list of streams into a `Stream` of results from the streams.
106+///
107+/// This essentially takes a list of streams (e.g. a vector, an iterator, etc.)
108+/// and bundles them together into a single stream.
109+/// The stream will yield items as they become available on the underlying
110+/// streams internally, in the order they become available.
111+///
112+/// Note that the returned set can also be used to dynamically push more
113+/// futures into the set as they become available.
114+///
115+/// This function is only available when the `std` or `alloc` feature of this
116+/// library is activated, and it is activated by default.
117+pub fn select_all<I>(streams: I) -> SelectAll<I::Item>
118+where
119+ I: IntoIterator,
120+ I::Item: Stream + Unpin,
121+{
122+ let mut set = SelectAll::new();
123+
124+ for stream in streams {
125+ set.push(stream);
126+ }
127+
128+ set
129+}
130+
131+impl<St: Stream + Unpin> FromIterator<St> for SelectAll<St> {
132+ fn from_iter<T: IntoIterator<Item = St>>(iter: T) -> Self {
133+ select_all(iter)
134+ }
135+}
136+
137+impl<St: Stream + Unpin> Extend<St> for SelectAll<St> {
138+ fn extend<T: IntoIterator<Item = St>>(&mut self, iter: T) {
139+ for st in iter {
140+ self.push(st)
141+ }
142+ }
143+}
helix-term/src/application.rs+7−6
@@ -10,6 +10,7 @@ use log::{debug, info};
1010use std::{
1111 io::{self, stdout, Stdout, Write},
1212 path::PathBuf,
13+ sync::Arc,
1314 time::Duration,
1415};
1516
@@ -32,7 +33,8 @@ pub struct Application {
3233 terminal: Terminal,
3334
3435 executor: &'static smol::Executor<'static>,
35− language_server: helix_lsp::Client,
36+ language_server: Arc<helix_lsp::Client>,
37+ language_servers: helix_lsp::Registry,
3638}
3739
3840impl Application {
@@ -49,7 +51,8 @@ impl Application {
4951 let mut compositor = Compositor::new();
5052 compositor.push(Box::new(ui::EditorView::new()));
5153
52− let language_server = helix_lsp::Client::start(&executor, "rust-analyzer", &[]);
54+ let language_servers = helix_lsp::Registry::new();
55+ let language_server = language_servers.get("rust", &executor).unwrap();
5356
5457 let mut app = Self {
5558 editor,
@@ -58,6 +61,7 @@ impl Application {
5861
5962 executor,
6063 language_server,
64+ language_servers,
6165 };
6266
6367 Ok(app)
@@ -81,9 +85,6 @@ impl Application {
8185 pub async fn event_loop(&mut self) {
8286 let mut reader = EventStream::new();
8387
84− // initialize lsp
85− self.language_server.initialize().await.unwrap();
86−
8788 self.language_server
8889 .text_document_did_open(&self.editor.view().unwrap().doc)
8990 .await
@@ -101,7 +102,7 @@ impl Application {
101102 event = reader.next().fuse() => {
102103 self.handle_terminal_events(event)
103104 }
104− call = self.language_server.incoming.next().fuse() => {
105+ call = self.language_servers.incoming.next().fuse() => {
105106 self.handle_language_server_message(call).await
106107 }
107108 }