From 355ad3cb8289611b06cd42fa62ddfe0a5c716e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 6 May 2021 13:56:34 +0900 Subject: [PATCH] Tokio migration. --- Cargo.lock | 308 ++++++---------------------------- helix-lsp/Cargo.toml | 4 +- helix-lsp/src/client.rs | 79 +++++---- helix-lsp/src/lib.rs | 18 +- helix-lsp/src/select_all.rs | 2 +- helix-lsp/src/transport.rs | 34 ++-- helix-term/Cargo.toml | 3 +- helix-term/src/application.rs | 14 +- helix-term/src/commands.rs | 42 +++-- helix-term/src/compositor.rs | 2 - helix-term/src/main.rs | 14 +- helix-term/src/ui/editor.rs | 3 +- helix-view/Cargo.toml | 2 +- helix-view/src/document.rs | 6 +- helix-view/src/editor.rs | 16 +- 15 files changed, 169 insertions(+), 378 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b994ef237..2e6292d9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,112 +17,6 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" -[[package]] -name = "async-channel" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bbfd5cf2794b1e908ea8457e6c45f8f8f1f6ec5f74617bf4662623f47503c3b" -dependencies = [ - "concurrent-queue", - "fastrand", - "futures-lite", - "libc", - "log", - "once_cell", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi", -] - -[[package]] -name = "async-lock" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-net" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b0a74e7f70af3c8cf1aa539edbd044795706659ac52b78a71dc1a205ecefdf" -dependencies = [ - "async-io", - "blocking", - "fastrand", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f38756dd9ac84671c428afbf7c9f7495feff9ec5b0710f17100098e5b354ac" -dependencies = [ - "async-io", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "libc", - "once_cell", - "signal-hook 0.3.8", - "winapi", -] - -[[package]] -name = "async-task" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - [[package]] name = "autocfg" version = "1.0.1" @@ -135,20 +29,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "blocking" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - [[package]] name = "bstr" version = "0.2.16" @@ -159,10 +39,10 @@ dependencies = [ ] [[package]] -name = "cache-padded" -version = "1.1.1" +name = "bytes" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] name = "cassowary" @@ -212,15 +92,6 @@ dependencies = [ "vec_map", ] -[[package]] -name = "concurrent-queue" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - [[package]] name = "crossbeam-utils" version = "0.8.4" @@ -245,7 +116,7 @@ dependencies = [ "libc", "mio", "parking_lot", - "signal-hook 0.1.17", + "signal-hook", "winapi", ] @@ -279,21 +150,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "event-listener" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" - -[[package]] -name = "fastrand" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" -dependencies = [ - "instant", -] - [[package]] name = "fern" version = "0.6.0" @@ -377,21 +233,6 @@ version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" -[[package]] -name = "futures-lite" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite 0.2.6", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.14" @@ -429,7 +270,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.6", + "pin-project-lite", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -515,9 +356,9 @@ dependencies = [ "serde", "serde_json", "shellexpand", - "smol", - "smol-timeout", "thiserror", + "tokio", + "tokio-stream", "url", ] @@ -553,8 +394,7 @@ dependencies = [ "pulldown-cmark", "serde", "serde_json", - "smol", - "smol-timeout", + "tokio", "toml", "tui", ] @@ -571,7 +411,7 @@ dependencies = [ "once_cell", "serde", "slotmap", - "smol", + "tokio", "toml", "tui", "url", @@ -801,12 +641,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - [[package]] name = "parking_lot" version = "0.11.1" @@ -844,12 +678,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - [[package]] name = "pin-project-lite" version = "0.2.6" @@ -862,19 +690,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "polling" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc12d774e799ee9ebae13f4076ca003b40d18a11ac0f3641e6f899618580b7b" -dependencies = [ - "cfg-if", - "libc", - "log", - "wepoll-sys", - "winapi", -] - [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1044,16 +859,6 @@ dependencies = [ "signal-hook-registry", ] -[[package]] -name = "signal-hook" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef33d6d0cd06e0840fba9985aab098c147e67e05cee14d412d3345ed14ff30ac" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.3.0" @@ -1084,44 +889,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" -[[package]] -name = "smol" -version = "1.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" -dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "smol-timeout" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847d777e2c6c166bad26264479e80a9820f3d364fcb4a0e23cd57bbfa8e94961" -dependencies = [ - "async-io", - "pin-project-lite 0.1.12", -] - -[[package]] -name = "socket2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "syn" version = "1.0.71" @@ -1206,6 +973,48 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tokio" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.8" @@ -1314,12 +1123,6 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "walkdir" version = "2.3.2" @@ -1337,15 +1140,6 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" -[[package]] -name = "wepoll-sys" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" -dependencies = [ - "cc", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 549e098ed..24bb20806 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -12,8 +12,8 @@ helix-core = { path = "../helix-core" } once_cell = "1.4" lsp-types = { version = "0.89", features = ["proposed"] } -smol = "1.2" -smol-timeout = "0.6" +tokio = { version = "1", features = ["full"] } +tokio-stream = "0.1.5" url = "2" pathdiff = "0.2" shellexpand = "2.0" diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index c70e6e789..c3de4fd76 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -13,18 +13,18 @@ use jsonrpc_core as jsonrpc; use lsp_types as lsp; use serde_json::Value; -use smol::{ - channel::{Receiver, Sender}, +use std::process::Stdio; +use tokio::{ io::{BufReader, BufWriter}, // prelude::*, - process::{Child, Command, Stdio}, - Executor, + process::{Child, Command}, + sync::mpsc::{channel, UnboundedReceiver, UnboundedSender}, }; pub struct Client { _process: Child, - outgoing: Sender, + outgoing: UnboundedSender, // pub incoming: Receiver, pub request_counter: AtomicU64, @@ -33,13 +33,14 @@ pub struct Client { } impl Client { - pub fn start(ex: &Executor, cmd: &str, args: &[String]) -> Result<(Self, Receiver)> { - // smol makes sure the process is reaped on drop, but using kill_on_drop(true) maybe? + pub fn start(cmd: &str, args: &[String]) -> Result<(Self, UnboundedReceiver)> { let process = Command::new(cmd) .args(args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) + // make sure the process is reaped on drop + .kill_on_drop(true) .spawn(); // use std::io::ErrorKind; @@ -58,7 +59,7 @@ impl Client { let reader = BufReader::new(process.stdout.take().expect("Failed to open stdout")); let stderr = BufReader::new(process.stderr.take().expect("Failed to open stderr")); - let (incoming, outgoing) = Transport::start(ex, reader, writer, stderr); + let (incoming, outgoing) = Transport::start(reader, writer, stderr); let client = Client { _process: process, @@ -134,49 +135,53 @@ impl Client { params: Self::value_into_params(params), }; - let (tx, rx) = smol::channel::bounded::>(1); + let (tx, mut rx) = channel::>(1); self.outgoing .send(Payload::Request { chan: tx, value: request, }) - .await .map_err(|e| Error::Other(e.into()))?; - use smol_timeout::TimeoutExt; use std::time::Duration; + use tokio::time::timeout; let future = async move { - rx.recv() - .timeout(Duration::from_secs(2)) + timeout(Duration::from_secs(2), rx.recv()) .await - .ok_or(Error::Timeout)? // return Timeout - .map_err(|e| Error::Other(e.into()))? + .map_err(|e| Error::Timeout)? // return Timeout + .unwrap() // TODO: None if channel closed }; Ok(future) } /// Send a RPC notification to the language server. - pub async fn notify(&self, params: R::Params) -> Result<()> + pub fn notify( + &self, + params: R::Params, + ) -> impl Future> where R::Params: serde::Serialize, { - let params = serde_json::to_value(params)?; + let outgoing = self.outgoing.clone(); - let notification = jsonrpc::Notification { - jsonrpc: Some(jsonrpc::Version::V2), - method: R::METHOD.to_string(), - params: Self::value_into_params(params), - }; + async move { + let params = serde_json::to_value(params)?; - self.outgoing - .send(Payload::Notification(notification)) - .await - .map_err(|e| Error::Other(e.into()))?; + let notification = jsonrpc::Notification { + jsonrpc: Some(jsonrpc::Version::V2), + method: R::METHOD.to_string(), + params: Self::value_into_params(params), + }; - Ok(()) + outgoing + .send(Payload::Notification(notification)) + .map_err(|e| Error::Other(e.into()))?; + + Ok(()) + } } /// Reply to a language server RPC call. @@ -202,7 +207,6 @@ impl Client { self.outgoing .send(Payload::Response(output)) - .await .map_err(|e| Error::Other(e.into()))?; Ok(()) @@ -387,13 +391,13 @@ impl Client { changes } - pub async fn text_document_did_change( + pub fn text_document_did_change( &self, text_document: lsp::VersionedTextDocumentIdentifier, old_text: &Rope, new_text: &Rope, changes: &ChangeSet, - ) -> Result<()> { + ) -> Option>> { // figure out what kind of sync the server supports let capabilities = self.capabilities.as_ref().unwrap(); @@ -405,7 +409,7 @@ impl Client { .. })) => kind, // None | SyncOptions { changes: None } - _ => return Ok(()), + _ => return None, }; let changes = match sync_capabilities { @@ -420,14 +424,15 @@ impl Client { lsp::TextDocumentSyncKind::Incremental => { Self::changeset_to_changes(old_text, new_text, changes, self.offset_encoding) } - lsp::TextDocumentSyncKind::None => return Ok(()), + lsp::TextDocumentSyncKind::None => return None, }; - self.notify::(lsp::DidChangeTextDocumentParams { - text_document, - content_changes: changes, - }) - .await + Some(self.notify::( + lsp::DidChangeTextDocumentParams { + text_document, + content_changes: changes, + }, + )) } pub async fn text_document_did_close( diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 6dcc66052..fd7e6fd32 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -18,6 +18,8 @@ use std::{collections::HashMap, sync::Arc}; use serde::{Deserialize, Serialize}; +use tokio_stream::wrappers::UnboundedReceiverStream; + #[derive(Error, Debug)] pub enum Error { #[error("protocol error: {0}")] @@ -163,12 +165,11 @@ pub use jsonrpc::Call; type LanguageId = String; use crate::select_all::SelectAll; -use smol::channel::Receiver; pub struct Registry { inner: HashMap>>, - pub incoming: SelectAll>, + pub incoming: SelectAll>, } impl Default for Registry { @@ -185,11 +186,7 @@ impl Registry { } } - pub fn get( - &mut self, - language_config: &LanguageConfiguration, - ex: &smol::Executor, - ) -> Option> { + pub fn get(&mut self, language_config: &LanguageConfiguration) -> Option> { // TODO: propagate the error if let Some(config) = &language_config.language_server { // avoid borrow issues @@ -203,12 +200,13 @@ impl Registry { // initialize a new client let (mut client, incoming) = - Client::start(&ex, &config.command, &config.args).ok()?; + Client::start(&config.command, &config.args).ok()?; // TODO: run this async without blocking - smol::block_on(client.initialize()).unwrap(); + let rt = tokio::runtime::Handle::current(); + rt.block_on(client.initialize()).unwrap(); - s_incoming.push(incoming); + s_incoming.push(UnboundedReceiverStream::new(incoming)); Some(Arc::new(client)) }) diff --git a/helix-lsp/src/select_all.rs b/helix-lsp/src/select_all.rs index 4a7f0cbff..edfd1f38c 100644 --- a/helix-lsp/src/select_all.rs +++ b/helix-lsp/src/select_all.rs @@ -6,10 +6,10 @@ use core::{ pin::Pin, }; -use smol::{ready, stream::Stream}; use std::task::{Context, Poll}; use futures_util::stream::{FusedStream, FuturesUnordered, StreamExt, StreamFuture}; +use futures_util::{ready, stream::Stream}; /// An unbounded set of streams /// diff --git a/helix-lsp/src/transport.rs b/helix-lsp/src/transport.rs index d3e25b9c9..4a5ae45a0 100644 --- a/helix-lsp/src/transport.rs +++ b/helix-lsp/src/transport.rs @@ -10,15 +10,13 @@ type Result = core::result::Result; use jsonrpc_core as jsonrpc; use serde_json::Value; -use smol::prelude::*; - -use smol::{ - channel::{Receiver, Sender}, - io::{BufReader, BufWriter}, +use tokio::{ + io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, - Executor, + sync::mpsc::{unbounded_channel, Sender, UnboundedReceiver, UnboundedSender}, }; +#[derive(Debug)] pub enum Payload { Request { chan: Sender>, @@ -41,8 +39,8 @@ enum Message { } pub struct Transport { - incoming: Sender, - outgoing: Receiver, + incoming: UnboundedSender, + outgoing: UnboundedReceiver, pending_requests: HashMap>>, headers: HashMap, @@ -54,13 +52,12 @@ pub struct Transport { impl Transport { pub fn start( - ex: &Executor, reader: BufReader, writer: BufWriter, stderr: BufReader, - ) -> (Receiver, Sender) { - let (incoming, rx) = smol::channel::unbounded(); - let (tx, outgoing) = smol::channel::unbounded(); + ) -> (UnboundedReceiver, UnboundedSender) { + let (incoming, rx) = unbounded_channel(); + let (tx, outgoing) = unbounded_channel(); let transport = Self { reader, @@ -72,7 +69,7 @@ impl Transport { headers: HashMap::default(), }; - ex.spawn(transport.duplex()).detach(); + tokio::spawn(transport.duplex()); (rx, tx) } @@ -168,7 +165,7 @@ impl Transport { match msg { Message::Output(output) => self.recv_response(output).await?, Message::Call(call) => { - self.incoming.send(call).await?; + self.incoming.send(call).unwrap(); // let notification = Notification::parse(&method, params); } }; @@ -204,11 +201,10 @@ impl Transport { } pub async fn duplex(mut self) { - use futures_util::{select, FutureExt}; loop { - select! { + tokio::select! { // client -> server - msg = self.outgoing.next().fuse() => { + msg = self.outgoing.recv() => { if msg.is_none() { break; } @@ -217,7 +213,7 @@ impl Transport { self.send_payload(msg).await.unwrap(); } // server <- client - msg = Self::recv(&mut self.reader, &mut self.headers).fuse() => { + msg = Self::recv(&mut self.reader, &mut self.headers) => { if msg.is_err() { error!("err: <- {:?}", msg); break; @@ -226,7 +222,7 @@ impl Transport { self.recv_msg(msg).await.unwrap(); } - _msg = Self::err(&mut self.stderr).fuse() => {} + _msg = Self::err(&mut self.stderr) => {} } } } diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 8f3a28fd9..b9f27cae2 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -19,8 +19,7 @@ helix-lsp = { path = "../helix-lsp"} anyhow = "1" once_cell = "1.4" -smol = "1" -smol-timeout = "0.6" +tokio = { version = "1", features = ["full"] } num_cpus = "1" tui = { version = "0.15", default-features = false, features = ["crossterm"] } crossterm = { version = "0.19", features = ["event-stream"] } diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 8849ee818..3bf746eaa 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -7,14 +7,13 @@ use crate::{compositor::Compositor, ui}; use log::{error, info}; use std::{ + future::Future, io::{self, stdout, Stdout, Write}, path::PathBuf, sync::Arc, time::Duration, }; -use smol::prelude::*; - use anyhow::Error; use crossterm::{ @@ -39,16 +38,15 @@ pub struct Application { compositor: Compositor, editor: Editor, - executor: &'static smol::Executor<'static>, callbacks: LspCallbacks, } impl Application { - pub fn new(mut args: Args, executor: &'static smol::Executor<'static>) -> Result { + pub fn new(mut args: Args) -> Result { use helix_view::editor::Action; let mut compositor = Compositor::new()?; let size = compositor.size(); - let mut editor = Editor::new(executor, size); + let mut editor = Editor::new(size); if let Ok(files) = args.values_of_t::("files") { for file in files { @@ -64,7 +62,6 @@ impl Application { compositor, editor, - executor, callbacks: FuturesUnordered::new(), }; @@ -72,14 +69,12 @@ impl Application { } fn render(&mut self) { - let executor = &self.executor; let editor = &mut self.editor; let compositor = &mut self.compositor; let callbacks = &mut self.callbacks; let mut cx = crate::compositor::Context { editor, - executor, callbacks, scroll: None, }; @@ -97,7 +92,7 @@ impl Application { break; } - use futures_util::{select, FutureExt}; + use futures_util::{select, FutureExt, StreamExt}; select! { event = reader.next().fuse() => { self.handle_terminal_events(event) @@ -125,7 +120,6 @@ impl Application { pub fn handle_terminal_events(&mut self, event: Option>) { let mut cx = crate::compositor::Context { editor: &mut self.editor, - executor: self.executor, callbacks: &mut self.callbacks, scroll: None, }; diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 72ebf7f95..3292d39f5 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -108,6 +108,16 @@ impl<'a> Context<'a> { /// state (usually by creating and applying a transaction). pub type Command = fn(cx: &mut Context); +#[inline] +fn block_on(future: impl Future) -> T { + use tokio::runtime::Runtime; + // let rt = Runtime::new().unwrap(); + let rt = tokio::runtime::Handle::current(); + // let local = LocalSet::new(); + // local.block_on(&rt, future) + rt.block_on(future) +} + pub fn move_char_left(cx: &mut Context) { let count = cx.count; let (view, doc) = cx.current(); @@ -861,7 +871,6 @@ pub fn command_mode(cx: &mut Context) { match *parts.as_slice() { ["q"] | ["quit"] => { editor.close(editor.view().id); - // editor.should_close = true, } ["o", path] | ["open", path] => { use helix_view::editor::Action; @@ -871,7 +880,7 @@ pub fn command_mode(cx: &mut Context) { // TODO: non-blocking via save() command let id = editor.view().doc; let doc = &mut editor.documents[id]; - smol::block_on(doc.save()); + block_on(doc.save()); } _ => (), @@ -1175,8 +1184,7 @@ pub fn goto_definition(cx: &mut Context) { let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding); // TODO: handle fails - let res = - smol::block_on(language_server.goto_definition(doc.identifier(), pos)).unwrap_or_default(); + let res = block_on(language_server.goto_definition(doc.identifier(), pos)).unwrap_or_default(); _goto(cx, res, offset_encoding); } @@ -1192,8 +1200,8 @@ pub fn goto_type_definition(cx: &mut Context) { let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding); // TODO: handle fails - let res = smol::block_on(language_server.goto_type_definition(doc.identifier(), pos)) - .unwrap_or_default(); + let res = + block_on(language_server.goto_type_definition(doc.identifier(), pos)).unwrap_or_default(); _goto(cx, res, offset_encoding); } @@ -1209,8 +1217,8 @@ pub fn goto_implementation(cx: &mut Context) { let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding); // TODO: handle fails - let res = smol::block_on(language_server.goto_implementation(doc.identifier(), pos)) - .unwrap_or_default(); + let res = + block_on(language_server.goto_implementation(doc.identifier(), pos)).unwrap_or_default(); _goto(cx, res, offset_encoding); } @@ -1226,8 +1234,7 @@ pub fn goto_reference(cx: &mut Context) { let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding); // TODO: handle fails - let res = - smol::block_on(language_server.goto_reference(doc.identifier(), pos)).unwrap_or_default(); + let res = block_on(language_server.goto_reference(doc.identifier(), pos)).unwrap_or_default(); _goto(cx, res, offset_encoding); } @@ -1247,7 +1254,7 @@ pub fn signature_help(cx: &mut Context) { // TODO: handle fails - let res = smol::block_on(language_server.text_document_signature_help(doc.identifier(), pos)) + let res = block_on(language_server.text_document_signature_help(doc.identifier(), pos)) .unwrap_or_default(); if let Some(signature_help) = res { @@ -1636,7 +1643,7 @@ pub fn format_selections(cx: &mut Context) { }; // TODO: handle fails // TODO: concurrent map - let edits = smol::block_on(language_server.text_document_range_formatting( + let edits = block_on(language_server.text_document_range_formatting( doc.identifier(), range, lsp::FormattingOptions::default(), @@ -1726,7 +1733,8 @@ pub fn save(cx: &mut Context) { // Spawns an async task to actually do the saving. This way we prevent blocking. // TODO: handle save errors somehow? - cx.editor.executor.spawn(cx.doc().save()).detach(); + // TODO: don't block + block_on(cx.doc().save()); } pub fn completion(cx: &mut Context) { @@ -1782,7 +1790,7 @@ pub fn completion(cx: &mut Context) { ); // TODO: handle fails - let res = smol::block_on(language_server.completion(doc.identifier(), pos)).unwrap(); + let res = block_on(language_server.completion(doc.identifier(), pos)).unwrap(); let trigger_offset = doc.selection(view.id).cursor(); @@ -1839,8 +1847,8 @@ pub fn hover(cx: &mut Context) { ); // TODO: handle fails - let res = smol::block_on(language_server.text_document_hover(doc.identifier(), pos)) - .unwrap_or_default(); + let res = + block_on(language_server.text_document_hover(doc.identifier(), pos)).unwrap_or_default(); if let Some(hover) = res { // hover.contents / .range <- used for visualizing @@ -1963,7 +1971,7 @@ pub fn space_mode(cx: &mut Context) { 'w' => { // save current buffer let doc = cx.doc(); - smol::block_on(doc.save()); + block_on(doc.save()); } 'c' => { // close current split diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index 6e81cc81b..4570cab97 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -4,7 +4,6 @@ use crossterm::event::Event; use helix_core::Position; -use smol::Executor; use tui::{buffer::Buffer as Surface, layout::Rect}; pub type Callback = Box; @@ -29,7 +28,6 @@ use crate::application::LspCallbacks; pub struct Context<'a> { pub editor: &'a mut Editor, - pub executor: &'static smol::Executor<'static>, pub scroll: Option, pub callbacks: &'a mut LspCallbacks, } diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index 612f8cf08..b0d244b72 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -14,8 +14,6 @@ use std::path::PathBuf; use anyhow::Error; -static EX: smol::Executor = smol::Executor::new(); - fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> { let mut base_config = fern::Dispatch::new(); @@ -88,12 +86,12 @@ fn main() { Loader::new(config) }); - for _ in 0..num_cpus::get() { - std::thread::spawn(move || smol::block_on(EX.run(smol::future::pending::<()>()))); - } + let runtime = tokio::runtime::Runtime::new().unwrap(); - let mut app = Application::new(args, &EX).unwrap(); + // TODO: use the thread local executor to spawn the application task separately from the work pool + runtime.block_on(async move { + let mut app = Application::new(args).unwrap(); - // we use the thread local executor to spawn the application task separately from the work pool - smol::block_on(app.run()); + app.run().await; + }); } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 041096d62..49024226c 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -207,7 +207,7 @@ impl EditorView { }); let style = if is_diagnostic { - style.clone().add_modifier(Modifier::UNDERLINED) + style.add_modifier(Modifier::UNDERLINED) } else { style }; @@ -569,7 +569,6 @@ impl Component for EditorView { let mut cx = Context { editor: cxt.editor, callbacks: cxt.callbacks, - executor: cx.executor, scroll: None, }; let res = completion.handle_event(event, &mut cx); diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index a49d440c8..2ed58707c 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -21,7 +21,7 @@ crossterm = { version = "0.19", features = ["event-stream"], optional = true } once_cell = "1.4" url = "2" -smol = "1" +tokio = { version = "1", features = ["full"] } futures-util = "0.3" slotmap = "1" diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 9b6f3cca8..8d6144cf6 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -134,7 +134,7 @@ impl Document { self.last_saved_revision = self.history.current_revision(); async move { - use smol::{fs::File, prelude::*}; + use tokio::{fs::File, io::AsyncWriteExt}; let mut file = File::create(path).await?; // write all the rope chunks to file @@ -232,7 +232,9 @@ impl Document { transaction.changes(), ); - smol::block_on(notify).expect("failed to emit textDocument/didChange"); + if let Some(notify) = notify { + tokio::spawn(notify); + } //.expect("failed to emit textDocument/didChange"); } } success diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 0eab4fe70..014364e0b 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -13,7 +13,6 @@ pub struct Editor { pub count: Option, pub theme: Theme, pub language_servers: helix_lsp::Registry, - pub executor: &'static smol::Executor<'static>, } #[derive(Copy, Clone)] @@ -24,7 +23,7 @@ pub enum Action { } impl Editor { - pub fn new(executor: &'static smol::Executor<'static>, mut area: tui::layout::Rect) -> Self { + pub fn new(mut area: tui::layout::Rect) -> Self { use helix_core::config_dir; let config = std::fs::read(config_dir().join("theme.toml")); // load $HOME/.config/helix/theme.toml, fallback to default config @@ -44,7 +43,6 @@ impl Editor { count: None, theme, language_servers, - executor, } } @@ -122,7 +120,7 @@ impl Editor { let language_server = doc .language .as_ref() - .and_then(|language| self.language_servers.get(language, self.executor)); + .and_then(|language| self.language_servers.get(language)); if let Some(language_server) = language_server { doc.set_language_server(Some(language_server.clone())); @@ -133,7 +131,8 @@ impl Editor { .map(ToOwned::to_owned) .unwrap_or_default(); - smol::block_on(language_server.text_document_did_open( + let rt = tokio::runtime::Handle::current(); + rt.block_on(language_server.text_document_did_open( doc.url().unwrap(), doc.version(), doc.text(), @@ -154,17 +153,18 @@ impl Editor { let view = self.tree.get(self.tree.focus); // get around borrowck issues let language_servers = &mut self.language_servers; - let executor = self.executor; let doc = &self.documents[view.doc]; let language_server = doc .language .as_ref() - .and_then(|language| language_servers.get(language, executor)); + .and_then(|language| language_servers.get(language)); if let Some(language_server) = language_server { - smol::block_on(language_server.text_document_did_close(doc.identifier())).unwrap(); + let rt = tokio::runtime::Handle::current(); + rt.block_on(language_server.text_document_did_close(doc.identifier())) + .unwrap(); } // remove selection