diff options
Diffstat (limited to 'src/tools/rust-analyzer/lib')
-rw-r--r-- | src/tools/rust-analyzer/lib/la-arena/src/lib.rs | 2 | ||||
-rw-r--r-- | src/tools/rust-analyzer/lib/line-index/Cargo.toml | 4 | ||||
-rw-r--r-- | src/tools/rust-analyzer/lib/line-index/src/lib.rs | 5 | ||||
-rw-r--r-- | src/tools/rust-analyzer/lib/line-index/src/tests.rs | 28 | ||||
-rw-r--r-- | src/tools/rust-analyzer/lib/lsp-server/Cargo.toml | 7 | ||||
-rw-r--r-- | src/tools/rust-analyzer/lib/lsp-server/src/lib.rs | 171 |
6 files changed, 198 insertions, 19 deletions
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs index f39c3a3e4..d195bdd15 100644 --- a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs +++ b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs @@ -1,6 +1,6 @@ //! Yet another index-based arena. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(missing_docs)] use std::{ diff --git a/src/tools/rust-analyzer/lib/line-index/Cargo.toml b/src/tools/rust-analyzer/lib/line-index/Cargo.toml index 6c0d06f47..494a7fa97 100644 --- a/src/tools/rust-analyzer/lib/line-index/Cargo.toml +++ b/src/tools/rust-analyzer/lib/line-index/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "line-index" -version = "0.1.0" +version = "0.1.1" description = "Maps flat `TextSize` offsets to/from `(line, column)` representation." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-index" edition = "2021" [dependencies] -text-size = "1.1.0" +text-size = "1.1.1" nohash-hasher = "0.2.0" diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs index 03371c9c8..58f266d67 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -363,7 +363,10 @@ fn analyze_source_file_generic( let c = src[i..].chars().next().unwrap(); char_len = c.len_utf8(); - let pos = TextSize::from(i as u32) + output_offset; + // The last element of `lines` represents the offset of the start of + // current line. To get the offset inside the line, we subtract it. + let pos = TextSize::from(i as u32) + output_offset + - lines.last().unwrap_or(&TextSize::default()); if char_len > 1 { assert!((2..=4).contains(&char_len)); diff --git a/src/tools/rust-analyzer/lib/line-index/src/tests.rs b/src/tools/rust-analyzer/lib/line-index/src/tests.rs index 8f3762d19..981008e34 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/tests.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{LineIndex, TextSize, WideChar}; +use crate::{LineCol, LineIndex, TextSize, WideChar, WideEncoding, WideLineCol}; macro_rules! test { ( @@ -102,7 +102,7 @@ test!( case: multi_byte_with_new_lines, text: "01\t345\n789abcΔf01234567\u{07}9\nbcΔf", lines: vec![7, 27], - multi_byte_chars: vec![(1, (13, 15)), (2, (29, 31))], + multi_byte_chars: vec![(1, (6, 8)), (2, (2, 4))], ); test!( @@ -118,3 +118,27 @@ test!( lines: vec![16], multi_byte_chars: vec![], ); + +#[test] +fn test_try_line_col() { + let text = "\n\n\n\n\n宽3456"; + assert_eq!(&text[5..8], "宽"); + assert_eq!(&text[11..12], "6"); + let line_index = LineIndex::new(text); + let before_6 = TextSize::from(11); + let line_col = line_index.try_line_col(before_6); + assert_eq!(line_col, Some(LineCol { line: 5, col: 6 })); +} + +#[test] +fn test_to_wide() { + let text = "\n\n\n\n\n宽3456"; + assert_eq!(&text[5..8], "宽"); + assert_eq!(&text[11..12], "6"); + let line_index = LineIndex::new(text); + let before_6 = TextSize::from(11); + let line_col = line_index.try_line_col(before_6); + assert_eq!(line_col, Some(LineCol { line: 5, col: 6 })); + let wide_line_col = line_index.to_wide(WideEncoding::Utf16, line_col.unwrap()); + assert_eq!(wide_line_col, Some(WideLineCol { line: 5, col: 4 })); +} diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 7ec3247e9..2a70aedbe 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.4" +version = "0.7.5" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -8,9 +8,10 @@ edition = "2021" [dependencies] log = "0.4.17" -serde_json = "1.0.96" -serde = { version = "1.0.156", features = ["derive"] } +serde_json = "1.0.108" +serde = { version = "1.0.192", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] lsp-types = "=0.94" +ctrlc = "3.4.1" diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs index affab60a2..2797a6b60 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs @@ -4,7 +4,7 @@ //! //! Run with `RUST_LOG=lsp_server=debug` to see all the messages. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] mod msg; mod stdio; @@ -17,7 +17,7 @@ use std::{ net::{TcpListener, TcpStream, ToSocketAddrs}, }; -use crossbeam_channel::{Receiver, Sender}; +use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; pub use crate::{ error::{ExtractError, ProtocolError}, @@ -113,11 +113,62 @@ impl Connection { /// } /// ``` pub fn initialize_start(&self) -> Result<(RequestId, serde_json::Value), ProtocolError> { - loop { - break match self.receiver.recv() { - Ok(Message::Request(req)) if req.is_initialize() => Ok((req.id, req.params)), + self.initialize_start_while(|| true) + } + + /// Starts the initialization process by waiting for an initialize as described in + /// [`Self::initialize_start`] as long as `running` returns + /// `true` while the return value can be changed through a sig handler such as `CTRL + C`. + /// + /// # Example + /// + /// ```rust + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// use std::sync::Arc; + /// # use std::error::Error; + /// # use lsp_types::{ClientCapabilities, InitializeParams, ServerCapabilities}; + /// # use lsp_server::{Connection, Message, Request, RequestId, Response}; + /// # fn main() -> Result<(), Box<dyn Error + Sync + Send>> { + /// let running = Arc::new(AtomicBool::new(true)); + /// # running.store(true, Ordering::SeqCst); + /// let r = running.clone(); + /// + /// ctrlc::set_handler(move || { + /// r.store(false, Ordering::SeqCst); + /// }).expect("Error setting Ctrl-C handler"); + /// + /// let (connection, io_threads) = Connection::stdio(); + /// + /// let res = connection.initialize_start_while(|| running.load(Ordering::SeqCst)); + /// # assert!(res.is_err()); + /// + /// # Ok(()) + /// # } + /// ``` + pub fn initialize_start_while<C>( + &self, + running: C, + ) -> Result<(RequestId, serde_json::Value), ProtocolError> + where + C: Fn() -> bool, + { + while running() { + let msg = match self.receiver.recv_timeout(std::time::Duration::from_secs(1)) { + Ok(msg) => msg, + Err(RecvTimeoutError::Timeout) => { + continue; + } + Err(e) => { + return Err(ProtocolError(format!( + "expected initialize request, got error: {e}" + ))) + } + }; + + match msg { + Message::Request(req) if req.is_initialize() => return Ok((req.id, req.params)), // Respond to non-initialize requests with ServerNotInitialized - Ok(Message::Request(req)) => { + Message::Request(req) => { let resp = Response::new_err( req.id.clone(), ErrorCode::ServerNotInitialized as i32, @@ -126,15 +177,18 @@ impl Connection { self.sender.send(resp.into()).unwrap(); continue; } - Ok(Message::Notification(n)) if !n.is_exit() => { + Message::Notification(n) if !n.is_exit() => { continue; } - Ok(msg) => Err(ProtocolError(format!("expected initialize request, got {msg:?}"))), - Err(e) => { - Err(ProtocolError(format!("expected initialize request, got error: {e}"))) + msg => { + return Err(ProtocolError(format!("expected initialize request, got {msg:?}"))); } }; } + + return Err(ProtocolError(String::from( + "Initialization has been aborted during initialization", + ))); } /// Finishes the initialization process by sending an `InitializeResult` to the client @@ -156,6 +210,51 @@ impl Connection { } } + /// Finishes the initialization process as described in [`Self::initialize_finish`] as + /// long as `running` returns `true` while the return value can be changed through a sig + /// handler such as `CTRL + C`. + pub fn initialize_finish_while<C>( + &self, + initialize_id: RequestId, + initialize_result: serde_json::Value, + running: C, + ) -> Result<(), ProtocolError> + where + C: Fn() -> bool, + { + let resp = Response::new_ok(initialize_id, initialize_result); + self.sender.send(resp.into()).unwrap(); + + while running() { + let msg = match self.receiver.recv_timeout(std::time::Duration::from_secs(1)) { + Ok(msg) => msg, + Err(RecvTimeoutError::Timeout) => { + continue; + } + Err(e) => { + return Err(ProtocolError(format!( + "expected initialized notification, got error: {e}", + ))); + } + }; + + match msg { + Message::Notification(n) if n.is_initialized() => { + return Ok(()); + } + msg => { + return Err(ProtocolError(format!( + r#"expected initialized notification, got: {msg:?}"# + ))); + } + } + } + + return Err(ProtocolError(String::from( + "Initialization has been aborted during initialization", + ))); + } + /// Initialize the connection. Sends the server capabilities /// to the client and returns the serialized client capabilities /// on success. If more fine-grained initialization is required use @@ -198,6 +297,58 @@ impl Connection { Ok(params) } + /// Initialize the connection as described in [`Self::initialize`] as long as `running` returns + /// `true` while the return value can be changed through a sig handler such as `CTRL + C`. + /// + /// # Example + /// + /// ```rust + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// use std::sync::Arc; + /// # use std::error::Error; + /// # use lsp_types::ServerCapabilities; + /// # use lsp_server::{Connection, Message, Request, RequestId, Response}; + /// + /// # fn main() -> Result<(), Box<dyn Error + Sync + Send>> { + /// let running = Arc::new(AtomicBool::new(true)); + /// # running.store(true, Ordering::SeqCst); + /// let r = running.clone(); + /// + /// ctrlc::set_handler(move || { + /// r.store(false, Ordering::SeqCst); + /// }).expect("Error setting Ctrl-C handler"); + /// + /// let (connection, io_threads) = Connection::stdio(); + /// + /// let server_capabilities = serde_json::to_value(&ServerCapabilities::default()).unwrap(); + /// let initialization_params = connection.initialize_while( + /// server_capabilities, + /// || running.load(Ordering::SeqCst) + /// ); + /// + /// # assert!(initialization_params.is_err()); + /// # Ok(()) + /// # } + /// ``` + pub fn initialize_while<C>( + &self, + server_capabilities: serde_json::Value, + running: C, + ) -> Result<serde_json::Value, ProtocolError> + where + C: Fn() -> bool, + { + let (id, params) = self.initialize_start_while(&running)?; + + let initialize_data = serde_json::json!({ + "capabilities": server_capabilities, + }); + + self.initialize_finish_while(id, initialize_data, running)?; + + Ok(params) + } + /// If `req` is `Shutdown`, respond to it and return `true`, otherwise return `false` pub fn handle_shutdown(&self, req: &Request) -> Result<bool, ProtocolError> { if !req.is_shutdown() { |