1780: add option to disable notify r=matklad a=matklad

This should help if notify uses 100% of CPU. Put

```
{
    "rust-analyzer.useClientWatching": true,
}
```

into `.vscode/settings.json` (or appropriate config of your editor) to use editor's file watching capabilites instead of notify

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-09-06 14:22:00 +00:00 committed by GitHub
commit 1acd9d5540
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 151 additions and 71 deletions

10
Cargo.lock generated
View file

@ -958,7 +958,7 @@ dependencies = [
"ra_hir 0.1.0", "ra_hir 0.1.0",
"ra_ide_api 0.1.0", "ra_ide_api 0.1.0",
"ra_project_model 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", "ra_vfs_glob 0.1.0",
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "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_project_model 0.1.0",
"ra_syntax 0.1.0", "ra_syntax 0.1.0",
"ra_text_edit 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", "ra_vfs_glob 0.1.0",
"relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1165,7 +1165,7 @@ dependencies = [
[[package]] [[package]]
name = "ra_vfs" name = "ra_vfs"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "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" version = "0.1.0"
dependencies = [ dependencies = [
"globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[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 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 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 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.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 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" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"

View file

@ -9,7 +9,7 @@ log = "0.4.5"
rustc-hash = "1.0" rustc-hash = "1.0"
crossbeam-channel = "0.3.5" crossbeam-channel = "0.3.5"
ra_vfs = "0.3.0" ra_vfs = "0.4.0"
ra_vfs_glob = { path = "../ra_vfs_glob" } ra_vfs_glob = { path = "../ra_vfs_glob" }
ra_db = { path = "../ra_db" } ra_db = { path = "../ra_db" }
ra_ide_api = { path = "../ra_ide_api" } ra_ide_api = { path = "../ra_ide_api" }

View file

@ -6,7 +6,7 @@ use crossbeam_channel::{unbounded, Receiver};
use ra_db::{CrateGraph, FileId, SourceRootId}; use ra_db::{CrateGraph, FileId, SourceRootId};
use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags}; use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags};
use ra_project_model::{PackageRoot, ProjectWorkspace}; 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; use ra_vfs_glob::RustPackageFilterBuilder;
type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>; type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
@ -37,6 +37,7 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId,
}) })
.collect(), .collect(),
sender, sender,
Watch(false),
); );
let crate_graph = ws.to_crate_graph(&mut |path: &Path| { let crate_graph = ws.to_crate_graph(&mut |path: &Path| {
let vfs_file = vfs.load(path); let vfs_file = vfs.load(path);

View file

@ -16,7 +16,7 @@ lsp-types = { version = "0.61.0", features = ["proposed"] }
rustc-hash = "1.0" rustc-hash = "1.0"
parking_lot = "0.9.0" parking_lot = "0.9.0"
jod-thread = "0.1.0" jod-thread = "0.1.0"
ra_vfs = "0.3.0" ra_vfs = "0.4.0"
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_text_edit = { path = "../ra_text_edit" } ra_text_edit = { path = "../ra_text_edit" }
ra_ide_api = { path = "../ra_ide_api" } ra_ide_api = { path = "../ra_ide_api" }

View file

@ -15,6 +15,8 @@ pub struct ServerConfig {
pub publish_decorations: bool, pub publish_decorations: bool,
pub exclude_globs: Vec<String>, pub exclude_globs: Vec<String>,
#[serde(deserialize_with = "nullable_bool_false")]
pub use_client_watching: bool,
pub lru_capacity: Option<usize>, pub lru_capacity: Option<usize>,
@ -31,6 +33,7 @@ impl Default for ServerConfig {
ServerConfig { ServerConfig {
publish_decorations: false, publish_decorations: false,
exclude_globs: Vec::new(), exclude_globs: Vec::new(),
use_client_watching: false,
lru_capacity: None, lru_capacity: None,
with_sysroot: true, with_sysroot: true,
feature_flags: FxHashMap::default(), feature_flags: FxHashMap::default(),

View file

@ -9,8 +9,9 @@ use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestI
use lsp_types::{ClientCapabilities, NumberOrString}; use lsp_types::{ClientCapabilities, NumberOrString};
use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId};
use ra_prof::profile; use ra_prof::profile;
use ra_vfs::VfsTask; use ra_vfs::{VfsTask, Watch};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use rustc_hash::FxHashSet;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use threadpool::ThreadPool; use threadpool::ThreadPool;
@ -55,72 +56,96 @@ pub fn main_loop(
) -> Result<()> { ) -> Result<()> {
log::info!("server_config: {:#?}", config); log::info!("server_config: {:#?}", config);
// FIXME: support dynamic workspace loading. let mut loop_state = LoopState::default();
let workspaces = { let mut world_state = {
let mut loaded_workspaces = Vec::new(); // FIXME: support dynamic workspace loading.
for ws_root in &ws_roots { let workspaces = {
let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( let mut loaded_workspaces = Vec::new();
ws_root.as_path(), for ws_root in &ws_roots {
config.with_sysroot, let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot(
); ws_root.as_path(),
match workspace { config.with_sysroot,
Ok(workspace) => loaded_workspaces.push(workspace), );
Err(e) => { match workspace {
log::error!("loading workspace failed: {}", e); 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::<std::result::Result<Vec<_>, _>>()?;
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::<req::RegisterCapability>(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( show_message(
req::MessageType::Error, req::MessageType::Error,
format!("rust-analyzer failed to load workspace: {}", e), format!("unknown feature flag: {:?}", flag),
&connection.sender, &connection.sender,
); );
} }
} }
} ff
loaded_workspaces };
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::<std::result::Result<Vec<_>, _>>()?;
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 pool = ThreadPool::new(THREADPOOL_SIZE);
let (task_sender, task_receiver) = unbounded::<Task>(); let (task_sender, task_receiver) = unbounded::<Task>();
let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>(); let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>();
let mut loop_state = LoopState::default();
log::info!("server initialized, serving requests"); log::info!("server initialized, serving requests");
{ {
@ -227,6 +252,8 @@ impl fmt::Debug for Event {
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct LoopState { struct LoopState {
next_request_id: u64,
pending_responses: FxHashSet<RequestId>,
pending_requests: PendingRequests, pending_requests: PendingRequests,
subscriptions: Subscriptions, subscriptions: Subscriptions,
// We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same
@ -236,6 +263,16 @@ struct LoopState {
workspace_loaded: bool, 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( fn loop_turn(
pool: &ThreadPool, pool: &ThreadPool,
task_sender: &Sender<Task>, task_sender: &Sender<Task>,
@ -290,7 +327,12 @@ fn loop_turn(
)?; )?;
state_changed = true; 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, Err(not) => not,
}; };
let not = match notification_cast::<req::DidChangeWatchedFiles>(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); log::error!("unhandled notification: {:?}", not);
Ok(()) Ok(())
} }
@ -682,3 +736,11 @@ where
{ {
Notification::new(N::METHOD.to_string(), params) Notification::new(N::METHOD.to_string(), params)
} }
fn request_new<R>(id: RequestId, params: R::Params) -> Request
where
R: lsp_types::request::Request,
R::Params: Serialize,
{
Request::new(id, R::METHOD.to_string(), params)
}

View file

@ -5,10 +5,11 @@ use serde::{Deserialize, Serialize};
pub use lsp_types::{ pub use lsp_types::{
notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams,
DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, Hover, DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions,
InitializeResult, MessageType, PublishDiagnosticsParams, ReferenceParams, ShowMessageParams, DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse,
SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams,
WorkspaceSymbolParams, ReferenceParams, Registration, RegistrationParams, ShowMessageParams, SignatureHelp,
TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams,
}; };
pub enum AnalyzerStatus {} pub enum AnalyzerStatus {}

View file

@ -12,7 +12,7 @@ use ra_ide_api::{
SourceRootId, SourceRootId,
}; };
use ra_project_model::ProjectWorkspace; 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 ra_vfs_glob::{Glob, RustPackageFilterBuilder};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
@ -60,6 +60,7 @@ impl WorldState {
workspaces: Vec<ProjectWorkspace>, workspaces: Vec<ProjectWorkspace>,
lru_capacity: Option<usize>, lru_capacity: Option<usize>,
exclude_globs: &[Glob], exclude_globs: &[Glob],
watch: Watch,
options: Options, options: Options,
feature_flags: FeatureFlags, feature_flags: FeatureFlags,
) -> WorldState { ) -> WorldState {
@ -85,7 +86,7 @@ impl WorldState {
} }
let (task_sender, task_receiver) = unbounded(); let (task_sender, task_receiver) = unbounded();
let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); 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(); let roots_to_scan = vfs_roots.len();
for r in vfs_roots { for r in vfs_roots {
let vfs_root_path = vfs.root2path(r); let vfs_root_path = vfs.root2path(r);

View file

@ -5,5 +5,5 @@ version = "0.1.0"
authors = ["rust-analyzer developers"] authors = ["rust-analyzer developers"]
[dependencies] [dependencies]
ra_vfs = "0.3.0" ra_vfs = "0.4.0"
globset = "0.4.4" globset = "0.4.4"

View file

@ -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). * `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. 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. 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. * `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""` ) (e.g: `--features="shumway,pdf"` will run as `cargo watch -x "check --features="shumway,pdf""` )
* `rust-analyzer.trace.server`: enables internal logging * `rust-analyzer.trace.server`: enables internal logging

View file

@ -205,6 +205,11 @@
"default": [], "default": [],
"description": "Paths to exclude from analysis" "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": { "rust-analyzer.cargo-watch.arguments": {
"type": "string", "type": "string",
"description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )",

View file

@ -23,6 +23,7 @@ export class Config {
public lruCapacity: null | number = null; public lruCapacity: null | number = null;
public displayInlayHints = true; public displayInlayHints = true;
public excludeGlobs = []; public excludeGlobs = [];
public useClientWatching = false;
public featureFlags = {}; public featureFlags = {};
public cargoWatchOptions: CargoWatchOptions = { public cargoWatchOptions: CargoWatchOptions = {
enableOnStartup: 'ask', enableOnStartup: 'ask',
@ -133,6 +134,9 @@ export class Config {
if (config.has('excludeGlobs')) { if (config.has('excludeGlobs')) {
this.excludeGlobs = config.get('excludeGlobs') || []; this.excludeGlobs = config.get('excludeGlobs') || [];
} }
if (config.has('useClientWatching')) {
this.useClientWatching = config.get('useClientWatching') || false;
}
if (config.has('featureFlags')) { if (config.has('featureFlags')) {
this.featureFlags = config.get('featureFlags') || {}; this.featureFlags = config.get('featureFlags') || {};
} }

View file

@ -46,6 +46,7 @@ export class Server {
Server.config.showWorkspaceLoadedNotification, Server.config.showWorkspaceLoadedNotification,
lruCapacity: Server.config.lruCapacity, lruCapacity: Server.config.lruCapacity,
excludeGlobs: Server.config.excludeGlobs, excludeGlobs: Server.config.excludeGlobs,
useClientWatching: Server.config.useClientWatching,
featureFlags: Server.config.featureFlags featureFlags: Server.config.featureFlags
}, },
traceOutputChannel traceOutputChannel