From 28df377759ee5625b0dad4c797be306c8d2624e3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 6 Sep 2019 16:25:24 +0300 Subject: [PATCH] add option to disable notify --- Cargo.lock | 10 +- crates/ra_batch/Cargo.toml | 2 +- crates/ra_batch/src/lib.rs | 3 +- crates/ra_lsp_server/Cargo.toml | 2 +- crates/ra_lsp_server/src/config.rs | 3 + crates/ra_lsp_server/src/main_loop.rs | 174 +++++++++++++++++--------- crates/ra_lsp_server/src/req.rs | 9 +- crates/ra_lsp_server/src/world.rs | 5 +- crates/ra_vfs_glob/Cargo.toml | 2 +- docs/user/README.md | 2 + editors/code/package.json | 5 + editors/code/src/config.ts | 4 + editors/code/src/server.ts | 1 + 13 files changed, 151 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6fbe8ed0f..f93f11a829 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -958,7 +958,7 @@ dependencies = [ "ra_hir 0.1.0", "ra_ide_api 0.1.0", "ra_project_model 0.1.0", - "ra_vfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_vfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ra_vfs_glob 0.1.0", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1065,7 +1065,7 @@ dependencies = [ "ra_project_model 0.1.0", "ra_syntax 0.1.0", "ra_text_edit 0.1.0", - "ra_vfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_vfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ra_vfs_glob 0.1.0", "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1165,7 +1165,7 @@ dependencies = [ [[package]] name = "ra_vfs" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1183,7 +1183,7 @@ name = "ra_vfs_glob" version = "0.1.0" dependencies = [ "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ra_vfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_vfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1945,7 +1945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum ra_vfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc8b709e0b7ceec822513451b610df1b9370b01953a8bc545a041a6b3bfef01" +"checksum ra_vfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdf6a0926414eb0c00866eb9274894182302f879cd06b5459c1d8ee7f1234aed" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" diff --git a/crates/ra_batch/Cargo.toml b/crates/ra_batch/Cargo.toml index 5fc2703ee9..62850746fe 100644 --- a/crates/ra_batch/Cargo.toml +++ b/crates/ra_batch/Cargo.toml @@ -9,7 +9,7 @@ log = "0.4.5" rustc-hash = "1.0" crossbeam-channel = "0.3.5" -ra_vfs = "0.3.0" +ra_vfs = "0.4.0" ra_vfs_glob = { path = "../ra_vfs_glob" } ra_db = { path = "../ra_db" } ra_ide_api = { path = "../ra_ide_api" } diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index 4e5bad0443..07a7e0c862 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs @@ -6,7 +6,7 @@ use crossbeam_channel::{unbounded, Receiver}; use ra_db::{CrateGraph, FileId, SourceRootId}; use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; use ra_project_model::{PackageRoot, ProjectWorkspace}; -use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask}; +use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; use ra_vfs_glob::RustPackageFilterBuilder; type Result = std::result::Result>; @@ -37,6 +37,7 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap, + #[serde(deserialize_with = "nullable_bool_false")] + pub use_client_watching: bool, pub lru_capacity: Option, @@ -31,6 +33,7 @@ impl Default for ServerConfig { ServerConfig { publish_decorations: false, exclude_globs: Vec::new(), + use_client_watching: false, lru_capacity: None, with_sysroot: true, feature_flags: FxHashMap::default(), diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 80f0216e85..25fa51b8a7 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -9,8 +9,9 @@ use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestI use lsp_types::{ClientCapabilities, NumberOrString}; use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; use ra_prof::profile; -use ra_vfs::VfsTask; +use ra_vfs::{VfsTask, Watch}; use relative_path::RelativePathBuf; +use rustc_hash::FxHashSet; use serde::{de::DeserializeOwned, Serialize}; use threadpool::ThreadPool; @@ -55,72 +56,96 @@ pub fn main_loop( ) -> Result<()> { log::info!("server_config: {:#?}", config); - // FIXME: support dynamic workspace loading. - let workspaces = { - let mut loaded_workspaces = Vec::new(); - for ws_root in &ws_roots { - let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( - ws_root.as_path(), - config.with_sysroot, - ); - match workspace { - Ok(workspace) => loaded_workspaces.push(workspace), - Err(e) => { - log::error!("loading workspace failed: {}", e); + let mut loop_state = LoopState::default(); + let mut world_state = { + // FIXME: support dynamic workspace loading. + let workspaces = { + let mut loaded_workspaces = Vec::new(); + for ws_root in &ws_roots { + let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( + ws_root.as_path(), + config.with_sysroot, + ); + match workspace { + Ok(workspace) => loaded_workspaces.push(workspace), + Err(e) => { + log::error!("loading workspace failed: {}", e); + show_message( + req::MessageType::Error, + format!("rust-analyzer failed to load workspace: {}", e), + &connection.sender, + ); + } + } + } + loaded_workspaces + }; + + let globs = config + .exclude_globs + .iter() + .map(|glob| ra_vfs_glob::Glob::new(glob)) + .collect::, _>>()?; + + if config.use_client_watching { + let registration_options = req::DidChangeWatchedFilesRegistrationOptions { + watchers: workspaces + .iter() + .flat_map(|ws| ws.to_roots()) + .filter(|root| root.is_member()) + .map(|root| format!("{}/**/*.rs", root.path().display())) + .map(|glob_pattern| req::FileSystemWatcher { glob_pattern, kind: None }) + .collect(), + }; + let registration = req::Registration { + id: "file-watcher".to_string(), + method: "workspace/didChangeWatchedFiles".to_string(), + register_options: Some(serde_json::to_value(registration_options).unwrap()), + }; + let params = req::RegistrationParams { registrations: vec![registration] }; + let request = + request_new::(loop_state.next_request_id(), params); + connection.sender.send(request.into()).unwrap(); + } + + let feature_flags = { + let mut ff = FeatureFlags::default(); + for (flag, value) in config.feature_flags { + if let Err(_) = ff.set(flag.as_str(), value) { + log::error!("unknown feature flag: {:?}", flag); show_message( req::MessageType::Error, - format!("rust-analyzer failed to load workspace: {}", e), + format!("unknown feature flag: {:?}", flag), &connection.sender, ); } } - } - loaded_workspaces + ff + }; + log::info!("feature_flags: {:#?}", feature_flags); + + WorldState::new( + ws_roots, + workspaces, + config.lru_capacity, + &globs, + Watch(!config.use_client_watching), + Options { + publish_decorations: config.publish_decorations, + supports_location_link: client_caps + .text_document + .and_then(|it| it.definition) + .and_then(|it| it.link_support) + .unwrap_or(false), + }, + feature_flags, + ) }; - let globs = config - .exclude_globs - .iter() - .map(|glob| ra_vfs_glob::Glob::new(glob)) - .collect::, _>>()?; - - let feature_flags = { - let mut ff = FeatureFlags::default(); - for (flag, value) in config.feature_flags { - if let Err(_) = ff.set(flag.as_str(), value) { - log::error!("unknown feature flag: {:?}", flag); - show_message( - req::MessageType::Error, - format!("unknown feature flag: {:?}", flag), - &connection.sender, - ); - } - } - ff - }; - log::info!("feature_flags: {:#?}", feature_flags); - - let mut world_state = WorldState::new( - ws_roots, - workspaces, - config.lru_capacity, - &globs, - Options { - publish_decorations: config.publish_decorations, - supports_location_link: client_caps - .text_document - .and_then(|it| it.definition) - .and_then(|it| it.link_support) - .unwrap_or(false), - }, - feature_flags, - ); - let pool = ThreadPool::new(THREADPOOL_SIZE); let (task_sender, task_receiver) = unbounded::(); let (libdata_sender, libdata_receiver) = unbounded::(); - let mut loop_state = LoopState::default(); log::info!("server initialized, serving requests"); { @@ -227,6 +252,8 @@ impl fmt::Debug for Event { #[derive(Debug, Default)] struct LoopState { + next_request_id: u64, + pending_responses: FxHashSet, pending_requests: PendingRequests, subscriptions: Subscriptions, // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same @@ -236,6 +263,16 @@ struct LoopState { workspace_loaded: bool, } +impl LoopState { + fn next_request_id(&mut self) -> RequestId { + self.next_request_id += 1; + let res: RequestId = self.next_request_id.into(); + let inserted = self.pending_responses.insert(res.clone()); + assert!(inserted); + res + } +} + fn loop_turn( pool: &ThreadPool, task_sender: &Sender, @@ -290,7 +327,12 @@ fn loop_turn( )?; state_changed = true; } - Message::Response(resp) => log::error!("unexpected response: {:?}", resp), + Message::Response(resp) => { + let removed = loop_state.pending_responses.remove(&resp.id); + if !removed { + log::error!("unexpected response: {:?}", resp) + } + } }, }; @@ -479,6 +521,18 @@ fn on_notification( } Err(not) => not, }; + let not = match notification_cast::(not) { + Ok(params) => { + let mut vfs = state.vfs.write(); + for change in params.changes { + let uri = change.uri; + let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; + vfs.notify_changed(path) + } + return Ok(()); + } + Err(not) => not, + }; log::error!("unhandled notification: {:?}", not); Ok(()) } @@ -682,3 +736,11 @@ where { Notification::new(N::METHOD.to_string(), params) } + +fn request_new(id: RequestId, params: R::Params) -> Request +where + R: lsp_types::request::Request, + R::Params: Serialize, +{ + Request::new(id, R::METHOD.to_string(), params) +} diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 1b23f0c3dd..0540f166ee 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -5,10 +5,11 @@ use serde::{Deserialize, Serialize}; pub use lsp_types::{ notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, - DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, Hover, - InitializeResult, MessageType, PublishDiagnosticsParams, ReferenceParams, ShowMessageParams, - SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, - WorkspaceSymbolParams, + DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, + DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, + FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, + ReferenceParams, Registration, RegistrationParams, ShowMessageParams, SignatureHelp, + TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, }; pub enum AnalyzerStatus {} diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index e1c5c3343f..086ecd587f 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -12,7 +12,7 @@ use ra_ide_api::{ SourceRootId, }; use ra_project_model::ProjectWorkspace; -use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask}; +use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; use relative_path::RelativePathBuf; @@ -60,6 +60,7 @@ impl WorldState { workspaces: Vec, lru_capacity: Option, exclude_globs: &[Glob], + watch: Watch, options: Options, feature_flags: FeatureFlags, ) -> WorldState { @@ -85,7 +86,7 @@ impl WorldState { } let (task_sender, task_receiver) = unbounded(); let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); - let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender); + let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch); let roots_to_scan = vfs_roots.len(); for r in vfs_roots { let vfs_root_path = vfs.root2path(r); diff --git a/crates/ra_vfs_glob/Cargo.toml b/crates/ra_vfs_glob/Cargo.toml index 09ba3d3bfe..d1073b2be8 100644 --- a/crates/ra_vfs_glob/Cargo.toml +++ b/crates/ra_vfs_glob/Cargo.toml @@ -5,5 +5,5 @@ version = "0.1.0" authors = ["rust-analyzer developers"] [dependencies] -ra_vfs = "0.3.0" +ra_vfs = "0.4.0" globset = "0.4.4" diff --git a/docs/user/README.md b/docs/user/README.md index 453e8e273f..56c2d9eb50 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -74,6 +74,8 @@ See https://github.com/microsoft/vscode/issues/72308[microsoft/vscode#72308] for * `rust-analyzer.excludeGlobs`: a list of glob-patterns for exclusion (see globset [docs](https://docs.rs/globset) for syntax). Note: glob patterns are applied to all Cargo packages and a rooted at a package root. This is not very intuitive and a limitation of a current implementation. +* `rust-analyzer.useClientWatching`: use client provided file watching instead + of notify watching. * `rust-analyzer.cargo-watch.check-arguments`: cargo-watch check arguments. (e.g: `--features="shumway,pdf"` will run as `cargo watch -x "check --features="shumway,pdf""` ) * `rust-analyzer.trace.server`: enables internal logging diff --git a/editors/code/package.json b/editors/code/package.json index e2bc72f325..7a48d6794f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -205,6 +205,11 @@ "default": [], "description": "Paths to exclude from analysis" }, + "rust-analyzer.useClientWatching": { + "type": "boolean", + "default": false, + "description": "client provided file watching instead of notify watching." + }, "rust-analyzer.cargo-watch.arguments": { "type": "string", "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 570ddca464..a4581485cc 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -23,6 +23,7 @@ export class Config { public lruCapacity: null | number = null; public displayInlayHints = true; public excludeGlobs = []; + public useClientWatching = false; public featureFlags = {}; public cargoWatchOptions: CargoWatchOptions = { enableOnStartup: 'ask', @@ -133,6 +134,9 @@ export class Config { if (config.has('excludeGlobs')) { this.excludeGlobs = config.get('excludeGlobs') || []; } + if (config.has('useClientWatching')) { + this.useClientWatching = config.get('useClientWatching') || false; + } if (config.has('featureFlags')) { this.featureFlags = config.get('featureFlags') || {}; } diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 0d2a99c70d..ff50fcd994 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts @@ -46,6 +46,7 @@ export class Server { Server.config.showWorkspaceLoadedNotification, lruCapacity: Server.config.lruCapacity, excludeGlobs: Server.config.excludeGlobs, + useClientWatching: Server.config.useClientWatching, featureFlags: Server.config.featureFlags }, traceOutputChannel