Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18998 from ChayimFriedman2/exclude
fix: Make `rust-analyzer.files.excludeDirs` work, actually
Lukas Wirth 2025-02-11
parent f5e7172 · parent 549b49f · commit 78e7515
-rw-r--r--crates/load-cargo/src/lib.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/global_state.rs25
-rw-r--r--crates/rust-analyzer/src/handlers/notification.rs16
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs170
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs14
-rw-r--r--crates/rust-analyzer/src/lib.rs11
-rw-r--r--crates/rust-analyzer/src/lsp/from_proto.rs30
-rw-r--r--crates/rust-analyzer/src/main_loop.rs31
-rw-r--r--crates/rust-analyzer/src/reload.rs4
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs36
-rw-r--r--crates/vfs-notify/src/lib.rs5
-rw-r--r--crates/vfs/src/lib.rs27
-rw-r--r--docs/book/src/configuration_generated.md4
-rw-r--r--editors/code/package.json4
15 files changed, 262 insertions, 131 deletions
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 5654c04a59..67ee9d1119 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -94,7 +94,9 @@ pub fn load_workspace(
let contents = loader.load_sync(path);
let path = vfs::VfsPath::from(path.to_path_buf());
vfs.set_file_contents(path.clone(), contents);
- vfs.file_id(&path)
+ vfs.file_id(&path).and_then(|(file_id, excluded)| {
+ (excluded == vfs::FileExcluded::No).then_some(file_id)
+ })
},
extra_env,
);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 2e9bb8c08e..d7e9a5c586 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -84,10 +84,10 @@ config_data! {
completion_snippets_custom: FxHashMap<String, SnippetDef> = Config::completion_snippets_default(),
- /// These directories will be ignored by rust-analyzer. They are
+ /// These paths (file/directories) will be ignored by rust-analyzer. They are
/// relative to the workspace root, and globs are not supported. You may
/// also need to add the folders to Code's `files.watcherExclude`.
- files_excludeDirs: Vec<Utf8PathBuf> = vec![],
+ files_exclude | files_excludeDirs: Vec<Utf8PathBuf> = vec![],
@@ -1792,7 +1792,7 @@ impl Config {
fn discovered_projects(&self) -> Vec<ManifestOrProjectJson> {
let exclude_dirs: Vec<_> =
- self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
+ self.files_exclude().iter().map(|p| self.root_path.join(p)).collect();
let mut projects = vec![];
for fs_proj in &self.discovered_projects_from_filesystem {
@@ -1914,10 +1914,14 @@ impl Config {
}
_ => FilesWatcher::Server,
},
- exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(),
+ exclude: self.excluded().collect(),
}
}
+ pub fn excluded(&self) -> impl Iterator<Item = AbsPathBuf> + use<'_> {
+ self.files_exclude().iter().map(|it| self.root_path.join(it))
+ }
+
pub fn notifications(&self) -> NotificationsConfig {
NotificationsConfig {
cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(),
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index b52f64aaac..70105cda00 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -650,7 +650,8 @@ impl GlobalStateSnapshot {
RwLockReadGuard::map(self.vfs.read(), |(it, _)| it)
}
- pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<FileId> {
+ /// Returns `None` if the file was excluded.
+ pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<Option<FileId>> {
url_to_file_id(&self.vfs_read(), url)
}
@@ -658,7 +659,8 @@ impl GlobalStateSnapshot {
file_id_to_url(&self.vfs_read(), id)
}
- pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result<FileId> {
+ /// Returns `None` if the file was excluded.
+ pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result<Option<FileId>> {
vfs_path_to_file_id(&self.vfs_read(), vfs_path)
}
@@ -750,14 +752,21 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
url_from_abs_path(path)
}
-pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result<FileId> {
+/// Returns `None` if the file was excluded.
+pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result<Option<FileId>> {
let path = from_proto::vfs_path(url)?;
- let res = vfs.file_id(&path).ok_or_else(|| anyhow::format_err!("file not found: {path}"))?;
- Ok(res)
+ vfs_path_to_file_id(vfs, &path)
}
-pub(crate) fn vfs_path_to_file_id(vfs: &vfs::Vfs, vfs_path: &VfsPath) -> anyhow::Result<FileId> {
- let res =
+/// Returns `None` if the file was excluded.
+pub(crate) fn vfs_path_to_file_id(
+ vfs: &vfs::Vfs,
+ vfs_path: &VfsPath,
+) -> anyhow::Result<Option<FileId>> {
+ let (file_id, excluded) =
vfs.file_id(vfs_path).ok_or_else(|| anyhow::format_err!("file not found: {vfs_path}"))?;
- Ok(res)
+ match excluded {
+ vfs::FileExcluded::Yes => Ok(None),
+ vfs::FileExcluded::No => Ok(Some(file_id)),
+ }
}
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index 48856d19e1..55344a4d6a 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -22,6 +22,7 @@ use crate::{
mem_docs::DocumentData,
reload,
target_spec::TargetSpec,
+ try_default,
};
pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> {
@@ -74,6 +75,14 @@ pub(crate) fn handle_did_open_text_document(
tracing::error!("duplicate DidOpenTextDocument: {}", path);
}
+ if let Some(abs_path) = path.as_path() {
+ if state.config.excluded().any(|excluded| abs_path.starts_with(&excluded)) {
+ tracing::trace!("opened excluded file {abs_path}");
+ state.vfs.write().0.insert_excluded_file(path);
+ return Ok(());
+ }
+ }
+
let contents = params.text_document.text.into_bytes();
state.vfs.write().0.set_file_contents(path, Some(contents));
if state.config.discover_workspace_config().is_some() {
@@ -127,7 +136,8 @@ pub(crate) fn handle_did_close_text_document(
tracing::error!("orphan DidCloseTextDocument: {}", path);
}
- if let Some(file_id) = state.vfs.read().0.file_id(&path) {
+ // Clear diagnostics also for excluded files, just in case.
+ if let Some((file_id, _)) = state.vfs.read().0.file_id(&path) {
state.diagnostics.clear_native_for(file_id);
}
@@ -146,7 +156,7 @@ pub(crate) fn handle_did_save_text_document(
) -> anyhow::Result<()> {
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
let snap = state.snapshot();
- let file_id = snap.vfs_path_to_file_id(&vfs_path)?;
+ let file_id = try_default!(snap.vfs_path_to_file_id(&vfs_path)?);
let sr = snap.analysis.source_root_id(file_id)?;
if state.config.script_rebuild_on_save(Some(sr)) && state.build_deps_changed {
@@ -290,7 +300,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
let _p = tracing::info_span!("run_flycheck").entered();
let file_id = state.vfs.read().0.file_id(&vfs_path);
- if let Some(file_id) = file_id {
+ if let Some((file_id, vfs::FileExcluded::No)) = file_id {
let world = state.snapshot();
let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once();
let may_flycheck_workspace = state.config.flycheck_workspace(None);
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 37e4a4940b..1b144d9073 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -53,6 +53,7 @@ use crate::{
},
target_spec::{CargoTargetSpec, TargetSpec},
test_runner::{CargoTestHandle, TestTarget},
+ try_default,
};
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
@@ -83,7 +84,8 @@ pub(crate) fn handle_analyzer_status(
let mut file_id = None;
if let Some(tdi) = params.text_document {
match from_proto::file_id(&snap, &tdi.uri) {
- Ok(it) => file_id = Some(it),
+ Ok(Some(it)) => file_id = Some(it),
+ Ok(None) => {}
Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri),
}
}
@@ -141,7 +143,7 @@ pub(crate) fn handle_view_syntax_tree(
params: lsp_ext::ViewSyntaxTreeParams,
) -> anyhow::Result<String> {
let _p = tracing::info_span!("handle_view_syntax_tree").entered();
- let id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let res = snap.analysis.view_syntax_tree(id)?;
Ok(res)
}
@@ -151,7 +153,7 @@ pub(crate) fn handle_view_hir(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<String> {
let _p = tracing::info_span!("handle_view_hir").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let res = snap.analysis.view_hir(position)?;
Ok(res)
}
@@ -161,7 +163,7 @@ pub(crate) fn handle_view_mir(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<String> {
let _p = tracing::info_span!("handle_view_mir").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let res = snap.analysis.view_mir(position)?;
Ok(res)
}
@@ -171,7 +173,7 @@ pub(crate) fn handle_interpret_function(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<String> {
let _p = tracing::info_span!("handle_interpret_function").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let res = snap.analysis.interpret_function(position)?;
Ok(res)
}
@@ -180,7 +182,7 @@ pub(crate) fn handle_view_file_text(
snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentIdentifier,
) -> anyhow::Result<String> {
- let file_id = from_proto::file_id(&snap, &params.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.uri)?);
Ok(snap.analysis.file_text(file_id)?.to_string())
}
@@ -189,7 +191,7 @@ pub(crate) fn handle_view_item_tree(
params: lsp_ext::ViewItemTreeParams,
) -> anyhow::Result<String> {
let _p = tracing::info_span!("handle_view_item_tree").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let res = snap.analysis.view_item_tree(file_id)?;
Ok(res)
}
@@ -315,7 +317,7 @@ pub(crate) fn handle_expand_macro(
params: lsp_ext::ExpandMacroParams,
) -> anyhow::Result<Option<lsp_ext::ExpandedMacro>> {
let _p = tracing::info_span!("handle_expand_macro").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
let offset = from_proto::offset(&line_index, params.position)?;
@@ -328,7 +330,7 @@ pub(crate) fn handle_selection_range(
params: lsp_types::SelectionRangeParams,
) -> anyhow::Result<Option<Vec<lsp_types::SelectionRange>>> {
let _p = tracing::info_span!("handle_selection_range").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
let res: anyhow::Result<Vec<lsp_types::SelectionRange>> = params
.positions
@@ -371,7 +373,7 @@ pub(crate) fn handle_matching_brace(
params: lsp_ext::MatchingBraceParams,
) -> anyhow::Result<Vec<Position>> {
let _p = tracing::info_span!("handle_matching_brace").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
params
.positions
@@ -395,7 +397,7 @@ pub(crate) fn handle_join_lines(
) -> anyhow::Result<Vec<lsp_types::TextEdit>> {
let _p = tracing::info_span!("handle_join_lines").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let config = snap.config.join_lines();
let line_index = snap.file_line_index(file_id)?;
@@ -419,7 +421,7 @@ pub(crate) fn handle_on_enter(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
let _p = tracing::info_span!("handle_on_enter").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let edit = match snap.analysis.on_enter(position)? {
None => return Ok(None),
Some(it) => it,
@@ -439,7 +441,8 @@ pub(crate) fn handle_on_type_formatting(
return Ok(None);
}
- let mut position = from_proto::file_position(&snap, params.text_document_position)?;
+ let mut position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position)?);
let line_index = snap.file_line_index(position.file_id)?;
// in `ide`, the `on_type` invariant is that
@@ -465,32 +468,33 @@ pub(crate) fn handle_on_type_formatting(
Ok(Some(change))
}
+pub(crate) fn empty_diagnostic_report() -> lsp_types::DocumentDiagnosticReportResult {
+ lsp_types::DocumentDiagnosticReportResult::Report(lsp_types::DocumentDiagnosticReport::Full(
+ lsp_types::RelatedFullDocumentDiagnosticReport {
+ related_documents: None,
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: Some("rust-analyzer".to_owned()),
+ items: vec![],
+ },
+ },
+ ))
+}
+
pub(crate) fn handle_document_diagnostics(
snap: GlobalStateSnapshot,
params: lsp_types::DocumentDiagnosticParams,
) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> {
- let empty = || {
- lsp_types::DocumentDiagnosticReportResult::Report(
- lsp_types::DocumentDiagnosticReport::Full(
- lsp_types::RelatedFullDocumentDiagnosticReport {
- related_documents: None,
- full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
- result_id: Some("rust-analyzer".to_owned()),
- items: vec![],
- },
- },
- ),
- )
+ let file_id = match from_proto::file_id(&snap, &params.text_document.uri)? {
+ Some(it) => it,
+ None => return Ok(empty_diagnostic_report()),
};
-
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let source_root = snap.analysis.source_root_id(file_id)?;
if !snap.analysis.is_local_source_root(source_root)? {
- return Ok(empty());
+ return Ok(empty_diagnostic_report());
}
let config = snap.config.diagnostics(Some(source_root));
if !config.enabled {
- return Ok(empty());
+ return Ok(empty_diagnostic_report());
}
let line_index = snap.file_line_index(file_id)?;
let supports_related = snap.config.text_document_diagnostic_related_document_support();
@@ -546,7 +550,7 @@ pub(crate) fn handle_document_symbol(
params: lsp_types::DocumentSymbolParams,
) -> anyhow::Result<Option<lsp_types::DocumentSymbolResponse>> {
let _p = tracing::info_span!("handle_document_symbol").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new();
@@ -760,7 +764,7 @@ pub(crate) fn handle_will_rename_files(
}
})
.filter_map(|(file_id, new_name)| {
- snap.analysis.will_rename_file(file_id, &new_name).ok()?
+ snap.analysis.will_rename_file(file_id?, &new_name).ok()?
})
.collect();
@@ -782,7 +786,8 @@ pub(crate) fn handle_goto_definition(
params: lsp_types::GotoDefinitionParams,
) -> anyhow::Result<Option<lsp_types::GotoDefinitionResponse>> {
let _p = tracing::info_span!("handle_goto_definition").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+ let position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
let nav_info = match snap.analysis.goto_definition(position)? {
None => return Ok(None),
Some(it) => it,
@@ -797,7 +802,10 @@ pub(crate) fn handle_goto_declaration(
params: lsp_types::request::GotoDeclarationParams,
) -> anyhow::Result<Option<lsp_types::request::GotoDeclarationResponse>> {
let _p = tracing::info_span!("handle_goto_declaration").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params.clone())?;
+ let position = try_default!(from_proto::file_position(
+ &snap,
+ params.text_document_position_params.clone()
+ )?);
let nav_info = match snap.analysis.goto_declaration(position)? {
None => return handle_goto_definition(snap, params),
Some(it) => it,
@@ -812,7 +820,8 @@ pub(crate) fn handle_goto_implementation(
params: lsp_types::request::GotoImplementationParams,
) -> anyhow::Result<Option<lsp_types::request::GotoImplementationResponse>> {
let _p = tracing::info_span!("handle_goto_implementation").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+ let position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
let nav_info = match snap.analysis.goto_implementation(position)? {
None => return Ok(None),
Some(it) => it,
@@ -827,7 +836,8 @@ pub(crate) fn handle_goto_type_definition(
params: lsp_types::request::GotoTypeDefinitionParams,
) -> anyhow::Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
let _p = tracing::info_span!("handle_goto_type_definition").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+ let position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
let nav_info = match snap.analysis.goto_type_definition(position)? {
None => return Ok(None),
Some(it) => it,
@@ -880,7 +890,7 @@ pub(crate) fn handle_parent_module(
}
// check if invoked at the crate root
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let crate_id = match snap.analysis.crates_for(file_id)?.first() {
Some(&crate_id) => crate_id,
None => return Ok(None),
@@ -904,7 +914,7 @@ pub(crate) fn handle_parent_module(
}
// locate parent module by semantics
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let navs = snap.analysis.parent_module(position)?;
let res = to_proto::goto_definition_response(&snap, None, navs)?;
Ok(Some(res))
@@ -915,7 +925,7 @@ pub(crate) fn handle_runnables(
params: lsp_ext::RunnablesParams,
) -> anyhow::Result<Vec<lsp_ext::Runnable>> {
let _p = tracing::info_span!("handle_runnables").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let source_root = snap.analysis.source_root_id(file_id).ok();
let line_index = snap.file_line_index(file_id)?;
let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok());
@@ -1035,7 +1045,7 @@ pub(crate) fn handle_related_tests(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<Vec<lsp_ext::TestInfo>> {
let _p = tracing::info_span!("handle_related_tests").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let tests = snap.analysis.related_tests(position, None)?;
let mut res = Vec::new();
@@ -1053,7 +1063,8 @@ pub(crate) fn handle_completion(
lsp_types::CompletionParams { text_document_position, context,.. }: lsp_types::CompletionParams,
) -> anyhow::Result<Option<lsp_types::CompletionResponse>> {
let _p = tracing::info_span!("handle_completion").entered();
- let mut position = from_proto::file_position(&snap, text_document_position.clone())?;
+ let mut position =
+ try_default!(from_proto::file_position(&snap, text_document_position.clone())?);
let line_index = snap.file_line_index(position.file_id)?;
let completion_trigger_character =
context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
@@ -1102,7 +1113,8 @@ pub(crate) fn handle_completion_resolve(
let resolve_data: lsp_ext::CompletionResolveData = serde_json::from_value(data)?;
- let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
+ let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?
+ .expect("we never provide completions for excluded files");
let line_index = snap.file_line_index(file_id)?;
// FIXME: We should fix up the position when retrying the cancelled request instead
let Ok(offset) = from_proto::offset(&line_index, resolve_data.position.position) else {
@@ -1185,7 +1197,7 @@ pub(crate) fn handle_folding_range(
params: FoldingRangeParams,
) -> anyhow::Result<Option<Vec<FoldingRange>>> {
let _p = tracing::info_span!("handle_folding_range").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let folds = snap.analysis.folding_ranges(file_id)?;
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
@@ -1202,7 +1214,8 @@ pub(crate) fn handle_signature_help(
params: lsp_types::SignatureHelpParams,
) -> anyhow::Result<Option<lsp_types::SignatureHelp>> {
let _p = tracing::info_span!("handle_signature_help").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+ let position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
let help = match snap.analysis.signature_help(position)? {
Some(it) => it,
None => return Ok(None),
@@ -1221,7 +1234,7 @@ pub(crate) fn handle_hover(
PositionOrRange::Position(position) => Range::new(position, position),
PositionOrRange::Range(range) => range,
};
- let file_range = from_proto::file_range(&snap, &params.text_document, range)?;
+ let file_range = try_default!(from_proto::file_range(&snap, &params.text_document, range)?);
let hover = snap.config.hover();
let info = match snap.analysis.hover(&hover, file_range)? {
@@ -1255,7 +1268,7 @@ pub(crate) fn handle_prepare_rename(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<Option<PrepareRenameResponse>> {
let _p = tracing::info_span!("handle_prepare_rename").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
@@ -1269,7 +1282,7 @@ pub(crate) fn handle_rename(
params: RenameParams,
) -> anyhow::Result<Option<WorkspaceEdit>> {
let _p = tracing::info_span!("handle_rename").entered();
- let position = from_proto::file_position(&snap, params.text_document_position)?;
+ let position = try_default!(from_proto::file_position(&snap, params.text_document_position)?);
let mut change =
snap.analysis.rename(position, &params.new_name)?.map_err(to_proto::rename_error)?;
@@ -1304,7 +1317,7 @@ pub(crate) fn handle_references(
params: lsp_types::ReferenceParams,
) -> anyhow::Result<Option<Vec<Location>>> {
let _p = tracing::info_span!("handle_references").entered();
- let position = from_proto::file_position(&snap, params.text_document_position)?;
+ let position = try_default!(from_proto::file_position(&snap, params.text_document_position)?);
let exclude_imports = snap.config.find_all_refs_exclude_imports();
let exclude_tests = snap.config.find_all_refs_exclude_tests();
@@ -1375,9 +1388,9 @@ pub(crate) fn handle_code_action(
return Ok(None);
}
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
- let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
+ let frange = try_default!(from_proto::file_range(&snap, &params.text_document, params.range)?);
let source_root = snap.analysis.source_root_id(file_id)?;
let mut assists_config = snap.config.assist(Some(source_root));
@@ -1455,7 +1468,8 @@ pub(crate) fn handle_code_action_resolve(
return Err(invalid_params_error("code action without data".to_owned()).into());
};
- let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
+ let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?
+ .expect("we never provide code actions for excluded files");
if snap.file_version(file_id) != params.version {
return Err(invalid_params_error("stale code action".to_owned()).into());
}
@@ -1551,7 +1565,7 @@ pub(crate) fn handle_code_lens(
return Ok(Some(Vec::default()));
}
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let target_spec = TargetSpec::for_file(&snap, file_id)?;
let annotations = snap.analysis.annotations(
@@ -1613,7 +1627,8 @@ pub(crate) fn handle_document_highlight(
params: lsp_types::DocumentHighlightParams,
) -> anyhow::Result<Option<Vec<lsp_types::DocumentHighlight>>> {
let _p = tracing::info_span!("handle_document_highlight").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+ let position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
let line_index = snap.file_line_index(position.file_id)?;
let source_root = snap.analysis.source_root_id(position.file_id)?;
@@ -1639,12 +1654,12 @@ pub(crate) fn handle_ssr(
params: lsp_ext::SsrParams,
) -> anyhow::Result<lsp_types::WorkspaceEdit> {
let _p = tracing::info_span!("handle_ssr").entered();
- let selections = params
+ let selections = try_default!(params
.selections
.iter()
.map(|range| from_proto::file_range(&snap, &params.position.text_document, *range))
- .collect::<Result<Vec<_>, _>>()?;
- let position = from_proto::file_position(&snap, params.position)?;
+ .collect::<Result<Option<Vec<_>>, _>>()?);
+ let position = try_default!(from_proto::file_position(&snap, params.position)?);
let source_change = snap.analysis.structural_search_replace(
&params.query,
params.parse_only,
@@ -1660,11 +1675,11 @@ pub(crate) fn handle_inlay_hints(
) -> anyhow::Result<Option<Vec<InlayHint>>> {
let _p = tracing::info_span!("handle_inlay_hints").entered();
let document_uri = &params.text_document.uri;
- let FileRange { file_id, range } = from_proto::file_range(
+ let FileRange { file_id, range } = try_default!(from_proto::file_range(
&snap,
&TextDocumentIdentifier::new(document_uri.to_owned()),
params.range,
- )?;
+ )?);
let line_index = snap.file_line_index(file_id)?;
let range = TextRange::new(
range.start().min(line_index.index.len()),
@@ -1744,7 +1759,8 @@ pub(crate) fn handle_call_hierarchy_prepare(
params: CallHierarchyPrepareParams,
) -> anyhow::Result<Option<Vec<CallHierarchyItem>>> {
let _p = tracing::info_span!("handle_call_hierarchy_prepare").entered();
- let position = from_proto::file_position(&snap, params.text_document_position_params)?;
+ let position =
+ try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
let nav_info = match snap.analysis.call_hierarchy(position)? {
None => return Ok(None),
@@ -1769,7 +1785,7 @@ pub(crate) fn handle_call_hierarchy_incoming(
let item = params.item;
let doc = TextDocumentIdentifier::new(item.uri);
- let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
+ let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?);
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let config = snap.config.call_hierarchy();
@@ -1807,7 +1823,7 @@ pub(crate) fn handle_call_hierarchy_outgoing(
let item = params.item;
let doc = TextDocumentIdentifier::new(item.uri);
- let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
+ let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?);
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let line_index = snap.file_line_index(fpos.file_id)?;
@@ -1842,7 +1858,7 @@ pub(crate) fn handle_semantic_tokens_full(
) -> anyhow::Result<Option<SemanticTokensResult>> {
let _p = tracing::info_span!("handle_semantic_tokens_full").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
@@ -1872,7 +1888,7 @@ pub(crate) fn handle_semantic_tokens_full_delta(
) -> anyhow::Result<Option<SemanticTokensFullDeltaResult>> {
let _p = tracing::info_span!("handle_semantic_tokens_full_delta").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
@@ -1915,7 +1931,7 @@ pub(crate) fn handle_semantic_tokens_range(
) -> anyhow::Result<Option<SemanticTokensRangeResult>> {
let _p = tracing::info_span!("handle_semantic_tokens_range").entered();
- let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
+ let frange = try_default!(from_proto::file_range(&snap, &params.text_document, params.range)?);
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
@@ -1940,7 +1956,7 @@ pub(crate) fn handle_open_docs(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<ExternalDocsResponse> {
let _p = tracing::info_span!("handle_open_docs").entered();
- let position = from_proto::file_position(&snap, params)?;
+ let position = try_default!(from_proto::file_position(&snap, params)?);
let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match &ws.kind {
ProjectWorkspaceKind::Cargo { cargo, .. }
@@ -1982,7 +1998,7 @@ pub(crate) fn handle_open_cargo_toml(
params: lsp_ext::OpenCargoTomlParams,
) -> anyhow::Result<Option<lsp_types::GotoDefinitionResponse>> {
let _p = tracing::info_span!("handle_open_cargo_toml").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let cargo_spec = match TargetSpec::for_file(&snap, file_id)? {
Some(TargetSpec::Cargo(it)) => it,
@@ -2000,8 +2016,8 @@ pub(crate) fn handle_move_item(
params: lsp_ext::MoveItemParams,
) -> anyhow::Result<Vec<lsp_ext::SnippetTextEdit>> {
let _p = tracing::info_span!("handle_move_item").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
- let range = from_proto::file_range(&snap, &params.text_document, params.range)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
+ let range = try_default!(from_proto::file_range(&snap, &params.text_document, params.range)?);
let direction = match params.direction {
lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
@@ -2022,7 +2038,7 @@ pub(crate) fn handle_view_recursive_memory_layout(
params: lsp_types::TextDocumentPositionParams,
) -> anyhow::Result<Option<lsp_ext::RecursiveMemoryLayout>> {
let _p = tracing::info_span!("handle_view_recursive_memory_layout").entered();
- let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
let offset = from_proto::offset(&line_index, params.position)?;
@@ -2210,7 +2226,7 @@ fn run_rustfmt(
text_document: TextDocumentIdentifier,
range: Option<lsp_types::Range>,
) -> anyhow::Result<Option<Vec<lsp_types::TextEdit>>> {
- let file_id = from_proto::file_id(snap, &text_document.uri)?;
+ let file_id = try_default!(from_proto::file_id(snap, &text_document.uri)?);
let file = snap.analysis.file_text(file_id)?;
// Determine the edition of the crate the file belongs to (if there's multiple, we pick the
@@ -2275,7 +2291,7 @@ fn run_rustfmt(
.into());
}
- let frange = from_proto::file_range(snap, &text_document, range)?;
+ let frange = try_default!(from_proto::file_range(snap, &text_document, range)?);
let start_line = line_index.index.line_col(frange.range.start()).line;
let end_line = line_index.index.line_col(frange.range.end()).line;
@@ -2417,15 +2433,15 @@ pub(crate) fn internal_testing_fetch_config(
state: GlobalStateSnapshot,
params: InternalTestingFetchConfigParams,
) -> anyhow::Result<Option<InternalTestingFetchConfigResponse>> {
- let source_root = params
- .text_document
- .map(|it| {
+ let source_root = match params.text_document {
+ Some(it) => Some(
state
.analysis
- .source_root_id(from_proto::file_id(&state, &it.uri)?)
- .map_err(anyhow::Error::from)
- })
- .transpose()?;
+ .source_root_id(try_default!(from_proto::file_id(&state, &it.uri)?))
+ .map_err(anyhow::Error::from)?,
+ ),
+ None => None,
+ };
Ok(Some(match params.config {
InternalTestingFetchConfigOption::AssistEmitMustUse => {
InternalTestingFetchConfigResponse::AssistEmitMustUse(
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 5cdc51a1c1..c6aa8ba170 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -25,6 +25,14 @@ use vfs::{AbsPathBuf, VfsPath};
use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
+#[track_caller]
+fn file_id(vfs: &vfs::Vfs, path: &VfsPath) -> vfs::FileId {
+ match vfs.file_id(path) {
+ Some((file_id, vfs::FileExcluded::No)) => file_id,
+ None | Some((_, vfs::FileExcluded::Yes)) => panic!("can't find virtual file for {path}"),
+ }
+}
+
#[test]
fn integrated_highlighting_benchmark() {
if std::env::var("RUN_SLOW_BENCHES").is_err() {
@@ -62,7 +70,7 @@ fn integrated_highlighting_benchmark() {
let file_id = {
let file = workspace_to_load.join(file);
let path = VfsPath::from(AbsPathBuf::assert(file));
- vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
+ file_id(&vfs, &path)
};
{
@@ -130,7 +138,7 @@ fn integrated_completion_benchmark() {
let file_id = {
let file = workspace_to_load.join(file);
let path = VfsPath::from(AbsPathBuf::assert(file));
- vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
+ file_id(&vfs, &path)
};
// kick off parsing and index population
@@ -324,7 +332,7 @@ fn integrated_diagnostics_benchmark() {
let file_id = {
let file = workspace_to_load.join(file);
let path = VfsPath::from(AbsPathBuf::assert(file));
- vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}"))
+ file_id(&vfs, &path)
};
let diagnostics_config = DiagnosticsConfig {
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 1221f7c701..27d6225cdb 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -173,3 +173,14 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
hasher.finalize()
}
+
+#[doc(hidden)]
+macro_rules! try_default_ {
+ ($it:expr $(,)?) => {
+ match $it {
+ Some(it) => it,
+ None => return Ok(Default::default()),
+ }
+ };
+}
+pub(crate) use try_default_ as try_default;
diff --git a/crates/rust-analyzer/src/lsp/from_proto.rs b/crates/rust-analyzer/src/lsp/from_proto.rs
index 47e9961cf1..6375a1a054 100644
--- a/crates/rust-analyzer/src/lsp/from_proto.rs
+++ b/crates/rust-analyzer/src/lsp/from_proto.rs
@@ -9,7 +9,7 @@ use vfs::AbsPathBuf;
use crate::{
global_state::GlobalStateSnapshot,
line_index::{LineIndex, PositionEncoding},
- lsp_ext,
+ lsp_ext, try_default,
};
pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> {
@@ -61,37 +61,44 @@ pub(crate) fn text_range(
}
}
-pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> anyhow::Result<FileId> {
+/// Returns `None` if the file was excluded.
+pub(crate) fn file_id(
+ snap: &GlobalStateSnapshot,
+ url: &lsp_types::Url,
+) -> anyhow::Result<Option<FileId>> {
snap.url_to_file_id(url)
}
+/// Returns `None` if the file was excluded.
pub(crate) fn file_position(
snap: &GlobalStateSnapshot,
tdpp: lsp_types::TextDocumentPositionParams,
-) -> anyhow::Result<FilePosition> {
- let file_id = file_id(snap, &tdpp.text_document.uri)?;
+) -> anyhow::Result<Option<FilePosition>> {
+ let file_id = try_default!(file_id(snap, &tdpp.text_document.uri)?);
let line_index = snap.file_line_index(file_id)?;
let offset = offset(&line_index, tdpp.position)?;
- Ok(FilePosition { file_id, offset })
+ Ok(Some(FilePosition { file_id, offset }))
}
+/// Returns `None` if the file was excluded.
pub(crate) fn file_range(
snap: &GlobalStateSnapshot,
text_document_identifier: &lsp_types::TextDocumentIdentifier,
range: lsp_types::Range,
-) -> anyhow::Result<FileRange> {
+) -> anyhow::Result<Option<FileRange>> {
file_range_uri(snap, &text_document_identifier.uri, range)
}
+/// Returns `None` if the file was excluded.
pub(crate) fn file_range_uri(
snap: &GlobalStateSnapshot,
document: &lsp_types::Url,
range: lsp_types::Range,
-) -> anyhow::Result<FileRange> {
- let file_id = file_id(snap, document)?;
+) -> anyhow::Result<Option<FileRange>> {
+ let file_id = try_default!(file_id(snap, document)?);
let line_index = snap.file_line_index(file_id)?;
let range = text_range(&line_index, range)?;
- Ok(FileRange { file_id, range })
+ Ok(Some(FileRange { file_id, range }))
}
pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> {
@@ -108,6 +115,7 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
Some(assist_kind)
}
+/// Returns `None` if the file was excluded.
pub(crate) fn annotation(
snap: &GlobalStateSnapshot,
range: lsp_types::Range,
@@ -121,7 +129,7 @@ pub(crate) fn annotation(
return Ok(None);
}
let pos @ FilePosition { file_id, .. } =
- file_position(snap, params.text_document_position_params)?;
+ try_default!(file_position(snap, params.text_document_position_params)?);
let line_index = snap.file_line_index(file_id)?;
Ok(Annotation {
@@ -133,7 +141,7 @@ pub(crate) fn annotation(
if snap.url_file_version(&params.text_document.uri) != Some(data.version) {
return Ok(None);
}
- let pos @ FilePosition { file_id, .. } = file_position(snap, params)?;
+ let pos @ FilePosition { file_id, .. } = try_default!(file_position(snap, params)?);
let line_index = snap.file_line_index(file_id)?;
Ok(Annotation {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ebc65373b5..f5d9469f26 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -27,7 +27,10 @@ use crate::{
FetchWorkspaceResponse, GlobalState,
},
hack_recover_crate_name,
- handlers::dispatch::{NotificationDispatcher, RequestDispatcher},
+ handlers::{
+ dispatch::{NotificationDispatcher, RequestDispatcher},
+ request::empty_diagnostic_report,
+ },
lsp::{
from_proto, to_proto,
utils::{notification_is, Progress},
@@ -548,6 +551,9 @@ impl GlobalState {
self.mem_docs
.iter()
.map(|path| vfs.file_id(path).unwrap())
+ .filter_map(|(file_id, excluded)| {
+ (excluded == vfs::FileExcluded::No).then_some(file_id)
+ })
.filter(|&file_id| {
let source_root = db.file_source_root(file_id);
// Only publish diagnostics for files in the workspace, not from crates.io deps
@@ -632,6 +638,9 @@ impl GlobalState {
.mem_docs
.iter()
.map(|path| self.vfs.read().0.file_id(path).unwrap())
+ .filter_map(|(file_id, excluded)| {
+ (excluded == vfs::FileExcluded::No).then_some(file_id)
+ })
.filter(|&file_id| {
let source_root = db.file_source_root(file_id);
!db.source_root(source_root).is_library
@@ -879,7 +888,10 @@ impl GlobalState {
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
let _p = tracing::info_span!("GlobalState::check_if_indexed").entered();
tracing::debug!(?uri, "handling uri");
- let id = from_proto::file_id(&snap, &uri).expect("unable to get FileId");
+ let Some(id) = from_proto::file_id(&snap, &uri).expect("unable to get FileId")
+ else {
+ return;
+ };
if let Ok(crates) = &snap.analysis.crates_for(id) {
if crates.is_empty() {
if snap.config.discover_workspace_config().is_some() {
@@ -987,13 +999,14 @@ impl GlobalState {
);
for diag in diagnostics {
match url_to_file_id(&self.vfs.read().0, &diag.url) {
- Ok(file_id) => self.diagnostics.add_check_diagnostic(
+ Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic(
id,
&package_id,
file_id,
diag.diagnostic,
diag.fix,
),
+ Ok(None) => {}
Err(err) => {
error!(
"flycheck {id}: File with cargo diagnostic not found in VFS: {}",
@@ -1115,17 +1128,7 @@ impl GlobalState {
.on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
// FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
// All other request handlers
- .on_with_vfs_default::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report(
- lsp_types::DocumentDiagnosticReport::Full(
- lsp_types::RelatedFullDocumentDiagnosticReport {
- related_documents: None,
- full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
- result_id: Some("rust-analyzer".to_owned()),
- items: vec![],
- },
- },
- ),
- ), || lsp_server::ResponseError {
+ .on_with_vfs_default::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, empty_diagnostic_report, || lsp_server::ResponseError {
code: lsp_server::ErrorCode::ServerCancelled as i32,
message: "server cancelled the request".to_owned(),
data: serde_json::to_value(lsp_types::DiagnosticServerCancellationData {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index d18e577047..e3c003dbf8 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -705,7 +705,9 @@ impl GlobalState {
let load = |path: &AbsPath| {
let vfs_path = vfs::VfsPath::from(path.to_path_buf());
self.crate_graph_file_dependencies.insert(vfs_path.clone());
- vfs.file_id(&vfs_path)
+ vfs.file_id(&vfs_path).and_then(|(file_id, excluded)| {
+ (excluded == vfs::FileExcluded::No).then_some(file_id)
+ })
};
ws_to_crate_graph(&self.workspaces, self.config.extra_env(None), load)
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 7f95641746..e764bd4770 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -1438,6 +1438,40 @@ pub fn foo() {}
name = "bar"
version = "0.0.0"
+[dependencies]
+foo = { path = "../foo" }
+
+//- /bar/src/lib.rs
+"#,
+ )
+ .root("foo")
+ .root("bar")
+ .root("baz")
+ .with_config(json!({
+ "files": {
+ "exclude": ["foo"]
+ }
+ }))
+ .server()
+ .wait_until_workspace_is_loaded();
+
+ server.request::<WorkspaceSymbolRequest>(Default::default(), json!([]));
+
+ let server = Project::with_fixture(
+ r#"
+//- /foo/Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- /foo/src/lib.rs
+pub fn foo() {}
+
+//- /bar/Cargo.toml
+[package]
+name = "bar"
+version = "0.0.0"
+
//- /bar/src/lib.rs
pub fn bar() {}
@@ -1454,7 +1488,7 @@ version = "0.0.0"
.root("baz")
.with_config(json!({
"files": {
- "excludeDirs": ["foo", "bar"]
+ "exclude": ["foo", "bar"]
}
}))
.server()
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index 0ae8b7baf4..3200334176 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -280,8 +280,9 @@ impl NotifyActor {
return false;
}
- root == path
- || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path)
+ // We want to filter out subdirectories that are roots themselves, because they will be visited separately.
+ dirs.exclude.iter().all(|it| it != path)
+ && (root == path || dirs.include.iter().all(|it| it != path))
});
let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| {
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index a26444e9ea..3feca512e5 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -100,6 +100,9 @@ pub enum FileState {
Exists(u64),
/// The file is deleted.
Deleted,
+ /// The file was specifically excluded by the user. We still include excluded files
+ /// when they're opened (without their contents).
+ Excluded,
}
/// Changed file in the [`Vfs`].
@@ -164,10 +167,22 @@ pub enum ChangeKind {
Delete,
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FileExcluded {
+ Yes,
+ No,
+}
+
impl Vfs {
/// Id of the given path if it exists in the `Vfs` and is not deleted.
- pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
- self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists(_)))
+ pub fn file_id(&self, path: &VfsPath) -> Option<(FileId, FileExcluded)> {
+ let file_id = self.interner.get(path)?;
+ let file_state = self.get(file_id);
+ match file_state {
+ FileState::Exists(_) => Some((file_id, FileExcluded::No)),
+ FileState::Deleted => None,
+ FileState::Excluded => Some((file_id, FileExcluded::Yes)),
+ }
}
/// File path corresponding to the given `file_id`.
@@ -216,6 +231,7 @@ impl Vfs {
}
Change::Modify(v, new_hash)
}
+ (FileState::Excluded, _) => return false,
};
let mut set_data = |change_kind| {
@@ -297,6 +313,13 @@ impl Vfs {
fn get(&self, file_id: FileId) -> FileState {
self.data[file_id.0 as usize]
}
+
+ /// We cannot ignore excluded files, because this will lead to errors when the client
+ /// requests semantic information for them, so we instead mark them specially.
+ pub fn insert_excluded_file(&mut self, path: VfsPath) {
+ let file_id = self.alloc_file_id(path);
+ self.data[file_id.0 as usize] = FileState::Excluded;
+ }
}
impl fmt::Debug for Vfs {
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 5567892660..0c6674b140 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -470,9 +470,9 @@ The warnings will be indicated by a blue squiggly underline in code
and a blue icon in the `Problems Panel`.
-**rust-analyzer.files.excludeDirs** (default: [])
+**rust-analyzer.files.exclude** (default: [])
- These directories will be ignored by rust-analyzer. They are
+ These paths (file/directories) will be ignored by rust-analyzer. They are
relative to the workspace root, and globs are not supported. You may
also need to add the folders to Code's `files.watcherExclude`.
diff --git a/editors/code/package.json b/editors/code/package.json
index 0a60376770..58571a540e 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1473,8 +1473,8 @@
{
"title": "files",
"properties": {
- "rust-analyzer.files.excludeDirs": {
- "markdownDescription": "These directories will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.",
+ "rust-analyzer.files.exclude": {
+ "markdownDescription": "These paths (file/directories) will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.",
"default": [],
"type": "array",
"items": {