Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ancient-ancestors-ascent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Fixed [#7268](https://github.com/biomejs/biome/issues/7268): Files that are explicitly passed as CLI arguments are now correctly ignored if they reside in an ignored folder.
2 changes: 1 addition & 1 deletion crates/biome_cli/src/execute/traverse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ impl TraversalContext for TraversalOptions<'_, '_> {
project_key: self.project_key,
path: biome_path.clone(),
features: self.execution.to_feature(),
ignore_kind: IgnoreKind::Path,
ignore_kind: IgnoreKind::Ancestors,
})
.unwrap_or_else(|err| {
self.push_diagnostic(err.into());
Expand Down
5 changes: 2 additions & 3 deletions crates/biome_cli/tests/cases/included_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,10 @@ fn does_not_handle_included_files_if_overridden_by_ignore() {
fn does_not_handle_files_in_ignored_folder() {
let mut console = BufferConsole::default();
let fs = MemoryFileSystem::default();
let file_path = Utf8Path::new("biome.json");
fs.insert(
file_path.into(),
"biome.json".into(),
r#"{
"files": { "includes": ["test.js", "!**/folder"] }
"files": { "includes": ["**/*.js", "!**/folder"] }
}
"#
.as_bytes(),
Expand Down
4 changes: 2 additions & 2 deletions crates/biome_cli/tests/commands/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ fn include_files_in_symlinked_subdir() {

#[cfg(target_os = "windows")]
{
check_windows_symlink!(symlink_file(
check_windows_symlink!(symlink_dir(
root_path.join("symlinked"),
subroot_path.join("symlink")
));
Expand Down Expand Up @@ -1273,7 +1273,7 @@ fn ignore_file_in_subdir_in_symlinked_dir() {

#[cfg(target_os = "windows")]
{
check_windows_symlink!(symlink_file(
check_windows_symlink!(symlink_dir(
root_path.join("symlinked"),
subroot_path.join("symlink")
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ expression: redactor(content)

```json
{
"files": { "includes": ["test.js", "!**/folder"] }
"files": { "includes": ["**/*.js", "!**/folder"] }
}
```

Expand Down
36 changes: 26 additions & 10 deletions crates/biome_service/src/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::settings::Settings;
use crate::workspace::{
DocumentFileSource, FeatureName, FeaturesSupported, FileFeaturesResult, IgnoreKind,
};
use biome_fs::ConfigName;
use biome_fs::{ConfigName, FileSystem};
use camino::{Utf8Path, Utf8PathBuf};
use papaya::HashMap;
use rustc_hash::FxBuildHasher;
Expand Down Expand Up @@ -140,19 +140,23 @@ impl Projects {

pub fn is_ignored_by_top_level_config(
&self,
fs: &dyn FileSystem,
project_key: ProjectKey,
path: &Utf8Path,
ignore_kind: IgnoreKind,
) -> bool {
match self.0.pin().get(&project_key) {
Some(project_data) => is_ignored_by_top_level_config(project_data, path, ignore_kind),
Some(project_data) => {
is_ignored_by_top_level_config(fs, project_data, path, ignore_kind)
}
None => false,
}
}

#[inline]
pub fn is_ignored(
&self,
fs: &dyn FileSystem,
project_key: ProjectKey,
path: &Utf8Path,
features: FeatureName,
Expand All @@ -164,7 +168,7 @@ impl Projects {
};

let is_ignored_by_top_level_config =
is_ignored_by_top_level_config(project_data, path, ignore_kind);
is_ignored_by_top_level_config(fs, project_data, path, ignore_kind);

// If there are specific features enabled, but all of them ignore the
// path, then we treat the path as ignored too.
Expand All @@ -181,6 +185,7 @@ impl Projects {
#[inline(always)]
pub fn get_file_features(
&self,
fs: &dyn FileSystem,
project_key: ProjectKey,
path: &Utf8Path,
features: FeatureName,
Expand Down Expand Up @@ -211,12 +216,7 @@ impl Projects {
.is_some_and(|dir_path| dir_path == project_data.path)
{
// Never ignore Biome's top-level config file
} else if !settings.files.includes.is_included(path)
|| project_data
.root_settings
.vcs_settings
.is_ignored(path, Some(project_data.path.as_path()))
{
} else if self.is_ignored(fs, project_key, path, features, IgnoreKind::Ancestors) {
file_features.set_ignored_for_all_features();
} else {
for feature in features.iter() {
Expand Down Expand Up @@ -340,6 +340,7 @@ impl Projects {

#[inline]
fn is_ignored_by_top_level_config(
fs: &dyn FileSystem,
project_data: &ProjectData,
path: &Utf8Path,
ignore_kind: IgnoreKind,
Expand All @@ -354,7 +355,22 @@ fn is_ignored_by_top_level_config(
&project_data.root_settings.files.includes,
|(_, settings)| &settings.files.includes,
);
let is_included = includes.is_included(path);
let mut is_included = if fs.path_is_dir(path) {
includes.is_dir_included(path)
} else {
includes.is_file_included(path)
};

// If necessary, check all the ancestors too.
if ignore_kind == IgnoreKind::Ancestors {
for ancestor in path.ancestors().skip(1) {
if !is_included || ancestor == project_data.path {
break;
}

is_included = is_included && includes.is_dir_included(ancestor)
}
}

let root_path = match ignore_kind {
IgnoreKind::Ancestors => Some(project_data.path.as_path()),
Expand Down
31 changes: 22 additions & 9 deletions crates/biome_service/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ impl Settings {

/// Returns whether the given `path` is ignored for the given `feature`,
/// based on the current settings.
///
/// `path` is expected to point to a file and not a directory.
#[inline]
pub fn is_path_ignored_for_feature(&self, path: &Utf8Path, feature: FeatureKind) -> bool {
let feature_includes_files = match feature {
Expand All @@ -321,7 +323,11 @@ impl Settings {
FeatureKind::Debug => return false,
};

!feature_includes_files.is_included(path)
if is_dir(path) {
!feature_includes_files.is_dir_included(path)
} else {
!feature_includes_files.is_file_included(path)
}
}
}

Expand Down Expand Up @@ -933,15 +939,22 @@ impl Includes {
current_globs.extend(globs.into());
}

/// Returns whether the given `path` is included.
/// Returns whether the given `file_path` is included.
///
/// `file_path` must point to an ordinary file. If it is a directory, you
/// should use [Self::is_dir_included()] instead.
#[inline]
pub fn is_included(&self, path: &Utf8Path) -> bool {
self.is_unset()
|| if is_dir(path) {
self.matches_directory_with_exceptions(path)
} else {
self.matches_with_exceptions(path)
}
pub fn is_file_included(&self, file_path: &Utf8Path) -> bool {
self.is_unset() || self.matches_with_exceptions(file_path)
}

/// Returns whether the given `dir_path` is included.
///
/// `file_path` must point to a directory. If it is a file, you should use
/// [Self::is_file_included()] instead.
#[inline]
pub fn is_dir_included(&self, dir_path: &Utf8Path) -> bool {
self.is_unset() || self.matches_directory_with_exceptions(dir_path)
}

/// Returns `true` is no globs are set.
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_service/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ pub struct PathIsIgnoredParams {
/// Controls how to ignore check should be done
pub ignore_kind: IgnoreKind,
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub enum IgnoreKind {
Expand Down
15 changes: 11 additions & 4 deletions crates/biome_service/src/workspace/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,10 +574,12 @@ impl WorkspaceServer {
return Ok(!scan_kind.is_project());
}

if self
.projects
.is_ignored_by_top_level_config(project_key, &path, ignore_kind)
{
if self.projects.is_ignored_by_top_level_config(
self.fs.as_ref(),
project_key,
&path,
ignore_kind,
) {
return Ok(true); // Nobody cares about ignored paths.
}

Expand Down Expand Up @@ -606,6 +608,7 @@ impl WorkspaceServer {
IgnoreKind::Path => !path.is_required_during_scan(),
IgnoreKind::Ancestors => path.parent().is_none_or(|folder_path| {
self.projects.is_ignored_by_top_level_config(
self.fs.as_ref(),
project_key,
folder_path,
ignore_kind,
Expand All @@ -629,6 +632,7 @@ impl WorkspaceServer {
IgnoreKind::Path => false,
IgnoreKind::Ancestors => path.parent().is_none_or(|folder_path| {
self.projects.is_ignored_by_top_level_config(
self.fs.as_ref(),
project_key,
folder_path,
ignore_kind,
Expand All @@ -637,6 +641,7 @@ impl WorkspaceServer {
}
} else {
self.projects.is_ignored_by_top_level_config(
self.fs.as_ref(),
project_key,
&path,
ignore_kind,
Expand Down Expand Up @@ -967,6 +972,7 @@ impl Workspace for WorkspaceServer {
let capabilities = self.features.get_capabilities(language);

self.projects.get_file_features(
self.fs.as_ref(),
params.project_key,
&params.path,
params.features,
Expand All @@ -988,6 +994,7 @@ impl Workspace for WorkspaceServer {
};

Ok(self.projects.is_ignored(
self.fs.as_ref(),
params.project_key,
&params.path,
params.features,
Expand Down
Loading