mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[ty] Anchor all exclude patterns (#18685)
Co-authored-by: Andrew Gallant <andrew@astral.sh>
This commit is contained in:
parent
8184dae287
commit
37fdece72f
10 changed files with 134 additions and 194 deletions
50
crates/ty/docs/configuration.md
generated
50
crates/ty/docs/configuration.md
generated
|
@ -270,7 +270,7 @@ A list of file and directory patterns to exclude from type checking.
|
|||
Patterns follow a syntax similar to `.gitignore`:
|
||||
- `./src/` matches only a directory
|
||||
- `./src` matches both files and directories
|
||||
- `src` matches files or directories named `src` anywhere in the tree (e.g. `./src` or `./tests/src`)
|
||||
- `src` matches files or directories named `src`
|
||||
- `*` matches any (possibly empty) sequence of characters (except `/`).
|
||||
- `**` matches zero or more path components.
|
||||
This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error.
|
||||
|
@ -280,28 +280,32 @@ Patterns follow a syntax similar to `.gitignore`:
|
|||
so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid.
|
||||
- `!pattern` negates a pattern (undoes the exclusion of files that would otherwise be excluded)
|
||||
|
||||
By default, the following directories are excluded:
|
||||
All paths are anchored relative to the project root (`src` only
|
||||
matches `<project_root>/src` and not `<project_root>/test/src`).
|
||||
To exclude any directory or file named `src`, use `**/src` instead.
|
||||
|
||||
- `.bzr`
|
||||
- `.direnv`
|
||||
- `.eggs`
|
||||
- `.git`
|
||||
- `.git-rewrite`
|
||||
- `.hg`
|
||||
- `.mypy_cache`
|
||||
- `.nox`
|
||||
- `.pants.d`
|
||||
- `.pytype`
|
||||
- `.ruff_cache`
|
||||
- `.svn`
|
||||
- `.tox`
|
||||
- `.venv`
|
||||
- `__pypackages__`
|
||||
- `_build`
|
||||
- `buck-out`
|
||||
- `dist`
|
||||
- `node_modules`
|
||||
- `venv`
|
||||
By default, ty excludes commonly ignored directories:
|
||||
|
||||
- `**/.bzr/`
|
||||
- `**/.direnv/`
|
||||
- `**/.eggs/`
|
||||
- `**/.git/`
|
||||
- `**/.git-rewrite/`
|
||||
- `**/.hg/`
|
||||
- `**/.mypy_cache/`
|
||||
- `**/.nox/`
|
||||
- `**/.pants.d/`
|
||||
- `**/.pytype/`
|
||||
- `**/.ruff_cache/`
|
||||
- `**/.svn/`
|
||||
- `**/.tox/`
|
||||
- `**/.venv/`
|
||||
- `**/__pypackages__/`
|
||||
- `**/_build/`
|
||||
- `**/buck-out/`
|
||||
- `**/dist/`
|
||||
- `**/node_modules/`
|
||||
- `**/venv/`
|
||||
|
||||
You can override any default exclude by using a negated pattern. For example,
|
||||
to re-include `dist` use `exclude = ["!dist"]`
|
||||
|
@ -342,7 +346,7 @@ are type checked.
|
|||
- `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode,
|
||||
so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid.
|
||||
|
||||
Unlike `exclude`, all paths are anchored relative to the project root (`src` only
|
||||
All paths are anchored relative to the project root (`src` only
|
||||
matches `<project_root>/src` and not `<project_root>/test/src`).
|
||||
|
||||
`exclude` takes precedence over `include`.
|
||||
|
|
|
@ -5,9 +5,7 @@ use clap::{ArgAction, ArgMatches, Error, Parser};
|
|||
use ruff_db::system::SystemPathBuf;
|
||||
use ty_project::combine::Combine;
|
||||
use ty_project::metadata::options::{EnvironmentOptions, Options, SrcOptions, TerminalOptions};
|
||||
use ty_project::metadata::value::{
|
||||
RangedValue, RelativeExcludePattern, RelativePathBuf, ValueSource,
|
||||
};
|
||||
use ty_project::metadata::value::{RangedValue, RelativeGlobPattern, RelativePathBuf, ValueSource};
|
||||
use ty_python_semantic::lint;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
@ -205,12 +203,7 @@ impl CheckCommand {
|
|||
src: Some(SrcOptions {
|
||||
respect_ignore_files,
|
||||
exclude: self.exclude.map(|excludes| {
|
||||
RangedValue::cli(
|
||||
excludes
|
||||
.iter()
|
||||
.map(|exclude| RelativeExcludePattern::cli(exclude))
|
||||
.collect(),
|
||||
)
|
||||
RangedValue::cli(excludes.iter().map(RelativeGlobPattern::cli).collect())
|
||||
}),
|
||||
..SrcOptions::default()
|
||||
}),
|
||||
|
|
|
@ -283,7 +283,7 @@ fn exclude_precedence_over_include() -> anyhow::Result<()> {
|
|||
r#"
|
||||
[src]
|
||||
include = ["src"]
|
||||
exclude = ["test_*.py"]
|
||||
exclude = ["**/test_*.py"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
|
@ -404,7 +404,7 @@ fn remove_default_exclude() -> anyhow::Result<()> {
|
|||
"ty.toml",
|
||||
r#"
|
||||
[src]
|
||||
exclude = ["!dist"]
|
||||
exclude = ["!**/dist/"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ use ruff_db::system::SystemPath;
|
|||
|
||||
pub(crate) use exclude::{ExcludeFilter, ExcludeFilterBuilder};
|
||||
pub(crate) use include::{IncludeFilter, IncludeFilterBuilder};
|
||||
pub(crate) use portable::{AbsolutePortableGlobPattern, PortableGlobError, PortableGlobPattern};
|
||||
pub(crate) use portable::{
|
||||
AbsolutePortableGlobPattern, PortableGlobError, PortableGlobKind, PortableGlobPattern,
|
||||
};
|
||||
|
||||
mod exclude;
|
||||
mod include;
|
||||
|
|
|
@ -100,16 +100,16 @@ impl ExcludeFilterBuilder {
|
|||
/// Matcher for gitignore like globs.
|
||||
///
|
||||
/// This code is our own vendored copy of the ignore's crate `Gitignore` type.
|
||||
/// The main difference to `ignore`'s version is that it makes use
|
||||
/// of the fact that all our globs are absolute. This simplifies the implementation a fair bit.
|
||||
/// Making globs absolute is also because the globs can come from both the CLI and configuration files,
|
||||
/// where the paths are anchored relative to the current working directory or the project root respectively.
|
||||
///
|
||||
/// Vendoring our own copy has the added benefit that we don't need to deal with ignore's `Error` type.
|
||||
/// Instead, we can exclusively use [`globset::Error`].
|
||||
/// The differences with the ignore's crate version are:
|
||||
///
|
||||
/// This implementation also removes supported for comments, because the patterns aren't read
|
||||
/// from a `.gitignore` file. This removes the need to escape `#` for file names starting with `#`,
|
||||
/// * All globs are anchored. `src` matches `./src` only and not `**/src` to be consistent with `include`.
|
||||
/// * It makes use of the fact that all our globs are absolute. This simplifies the implementation a fair bit.
|
||||
/// Making globs absolute is also motivated by the fact that the globs can come from both the CLI and configuration files,
|
||||
/// where the paths are anchored relative to the current working directory or the project root respectively.
|
||||
/// * It uses [`globset::Error`] over the ignore's crate `Error` type.
|
||||
/// * Removes supported for commented lines, because the patterns aren't read
|
||||
/// from a `.gitignore` file. This removes the need to escape `#` for file names starting with `#`,
|
||||
///
|
||||
/// You can find the original source on [GitHub](https://github.com/BurntSushi/ripgrep/blob/cbc598f245f3c157a872b69102653e2e349b6d92/crates/ignore/src/gitignore.rs#L81).
|
||||
///
|
||||
|
@ -267,15 +267,6 @@ impl GitignoreBuilder {
|
|||
|
||||
let mut actual = pattern.to_string();
|
||||
|
||||
// If there is a literal slash, then this is a glob that must match the
|
||||
// entire path name. Otherwise, we should let it match anywhere, so use
|
||||
// a **/ prefix.
|
||||
if !pattern.chars().any(|c| c == '/') {
|
||||
// ... but only if we don't already have a **/ prefix.
|
||||
if !pattern.starts_with("**/") {
|
||||
actual = format!("**/{actual}");
|
||||
}
|
||||
}
|
||||
// If the glob ends with `/**`, then we should only match everything
|
||||
// inside a directory, but not the directory itself. Standard globs
|
||||
// will match the directory. So we add `/*` to force the issue.
|
||||
|
|
|
@ -241,8 +241,8 @@ impl IncludeFilterBuilder {
|
|||
mod tests {
|
||||
use std::path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR};
|
||||
|
||||
use crate::glob::PortableGlobPattern;
|
||||
use crate::glob::include::{IncludeFilter, IncludeFilterBuilder};
|
||||
use crate::glob::{PortableGlobKind, PortableGlobPattern};
|
||||
use ruff_db::system::{MemoryFileSystem, walk_directory::WalkState};
|
||||
|
||||
fn create_filter(patterns: impl IntoIterator<Item = &'static str>) -> IncludeFilter {
|
||||
|
@ -250,7 +250,7 @@ mod tests {
|
|||
for pattern in patterns {
|
||||
builder
|
||||
.add(
|
||||
&PortableGlobPattern::parse(pattern, false)
|
||||
&PortableGlobPattern::parse(pattern, PortableGlobKind::Include)
|
||||
.unwrap()
|
||||
.into_absolute(""),
|
||||
)
|
||||
|
|
|
@ -32,15 +32,15 @@ use thiserror::Error;
|
|||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct PortableGlobPattern<'a> {
|
||||
pattern: &'a str,
|
||||
is_exclude: bool,
|
||||
kind: PortableGlobKind,
|
||||
}
|
||||
|
||||
impl<'a> PortableGlobPattern<'a> {
|
||||
/// Parses a portable glob pattern. Returns an error if the pattern isn't valid.
|
||||
pub(crate) fn parse(glob: &'a str, is_exclude: bool) -> Result<Self, PortableGlobError> {
|
||||
pub(crate) fn parse(glob: &'a str, kind: PortableGlobKind) -> Result<Self, PortableGlobError> {
|
||||
let mut chars = glob.chars().enumerate().peekable();
|
||||
|
||||
if is_exclude {
|
||||
if matches!(kind, PortableGlobKind::Exclude) {
|
||||
chars.next_if(|(_, c)| *c == '!');
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ impl<'a> PortableGlobPattern<'a> {
|
|||
}
|
||||
Ok(PortableGlobPattern {
|
||||
pattern: glob,
|
||||
is_exclude,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -138,21 +138,12 @@ impl<'a> PortableGlobPattern<'a> {
|
|||
let mut pattern = self.pattern;
|
||||
let mut negated = false;
|
||||
|
||||
if self.is_exclude {
|
||||
if matches!(self.kind, PortableGlobKind::Exclude) {
|
||||
// If the pattern starts with `!`, we need to remove it and then anchor the rest.
|
||||
if let Some(after) = self.pattern.strip_prefix('!') {
|
||||
pattern = after;
|
||||
negated = true;
|
||||
}
|
||||
|
||||
// Patterns that don't contain any `/`, e.g. `.venv` are unanchored patterns
|
||||
// that match anywhere.
|
||||
if !self.chars().any(|c| c == '/') {
|
||||
return AbsolutePortableGlobPattern {
|
||||
absolute: self.to_string(),
|
||||
relative: self.pattern.to_string(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if pattern.starts_with('/') {
|
||||
|
@ -242,6 +233,15 @@ impl AbsolutePortableGlobPattern {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) enum PortableGlobKind {
|
||||
/// An include pattern. Doesn't allow negated patterns.
|
||||
Include,
|
||||
|
||||
/// An exclude pattern. Allows for negated patterns.
|
||||
Exclude,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum PortableGlobError {
|
||||
/// Shows the failing glob in the error message.
|
||||
|
@ -284,7 +284,7 @@ impl std::fmt::Display for InvalidChar {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::glob::PortableGlobPattern;
|
||||
use crate::glob::{PortableGlobKind, PortableGlobPattern};
|
||||
use insta::assert_snapshot;
|
||||
use ruff_db::system::SystemPath;
|
||||
|
||||
|
@ -292,7 +292,7 @@ mod tests {
|
|||
fn test_error() {
|
||||
#[track_caller]
|
||||
fn parse_err(glob: &str) -> String {
|
||||
let error = PortableGlobPattern::parse(glob, true).unwrap_err();
|
||||
let error = PortableGlobPattern::parse(glob, PortableGlobKind::Exclude).unwrap_err();
|
||||
error.to_string()
|
||||
}
|
||||
|
||||
|
@ -376,13 +376,13 @@ mod tests {
|
|||
r"**/\@test",
|
||||
];
|
||||
for case in cases.iter().chain(cases_uv.iter()) {
|
||||
PortableGlobPattern::parse(case, true).unwrap();
|
||||
PortableGlobPattern::parse(case, PortableGlobKind::Exclude).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_absolute_path(pattern: &str, relative_to: impl AsRef<SystemPath>, expected: &str) {
|
||||
let pattern = PortableGlobPattern::parse(pattern, true).unwrap();
|
||||
let pattern = PortableGlobPattern::parse(pattern, PortableGlobKind::Exclude).unwrap();
|
||||
let pattern = pattern.into_absolute(relative_to);
|
||||
assert_eq!(pattern.absolute(), expected);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::Db;
|
||||
use crate::combine::Combine;
|
||||
use crate::glob::{ExcludeFilter, IncludeExcludeFilter, IncludeFilter};
|
||||
use crate::glob::{ExcludeFilter, IncludeExcludeFilter, IncludeFilter, PortableGlobKind};
|
||||
use crate::metadata::settings::{OverrideSettings, SrcSettings};
|
||||
use crate::metadata::value::{
|
||||
RangedValue, RelativeExcludePattern, RelativeIncludePattern, RelativePathBuf, ValueSource,
|
||||
ValueSourceGuard,
|
||||
RangedValue, RelativeGlobPattern, RelativePathBuf, ValueSource, ValueSourceGuard,
|
||||
};
|
||||
|
||||
use ordermap::OrderMap;
|
||||
|
@ -475,7 +474,7 @@ pub struct SrcOptions {
|
|||
/// - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode,
|
||||
/// so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid.
|
||||
///
|
||||
/// Unlike `exclude`, all paths are anchored relative to the project root (`src` only
|
||||
/// All paths are anchored relative to the project root (`src` only
|
||||
/// matches `<project_root>/src` and not `<project_root>/test/src`).
|
||||
///
|
||||
/// `exclude` takes precedence over `include`.
|
||||
|
@ -490,14 +489,14 @@ pub struct SrcOptions {
|
|||
]
|
||||
"#
|
||||
)]
|
||||
pub include: Option<RangedValue<Vec<RelativeIncludePattern>>>,
|
||||
pub include: Option<RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
|
||||
/// A list of file and directory patterns to exclude from type checking.
|
||||
///
|
||||
/// Patterns follow a syntax similar to `.gitignore`:
|
||||
/// - `./src/` matches only a directory
|
||||
/// - `./src` matches both files and directories
|
||||
/// - `src` matches files or directories named `src` anywhere in the tree (e.g. `./src` or `./tests/src`)
|
||||
/// - `src` matches files or directories named `src`
|
||||
/// - `*` matches any (possibly empty) sequence of characters (except `/`).
|
||||
/// - `**` matches zero or more path components.
|
||||
/// This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error.
|
||||
|
@ -507,28 +506,32 @@ pub struct SrcOptions {
|
|||
/// so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid.
|
||||
/// - `!pattern` negates a pattern (undoes the exclusion of files that would otherwise be excluded)
|
||||
///
|
||||
/// By default, the following directories are excluded:
|
||||
/// All paths are anchored relative to the project root (`src` only
|
||||
/// matches `<project_root>/src` and not `<project_root>/test/src`).
|
||||
/// To exclude any directory or file named `src`, use `**/src` instead.
|
||||
///
|
||||
/// - `.bzr`
|
||||
/// - `.direnv`
|
||||
/// - `.eggs`
|
||||
/// - `.git`
|
||||
/// - `.git-rewrite`
|
||||
/// - `.hg`
|
||||
/// - `.mypy_cache`
|
||||
/// - `.nox`
|
||||
/// - `.pants.d`
|
||||
/// - `.pytype`
|
||||
/// - `.ruff_cache`
|
||||
/// - `.svn`
|
||||
/// - `.tox`
|
||||
/// - `.venv`
|
||||
/// - `__pypackages__`
|
||||
/// - `_build`
|
||||
/// - `buck-out`
|
||||
/// - `dist`
|
||||
/// - `node_modules`
|
||||
/// - `venv`
|
||||
/// By default, ty excludes commonly ignored directories:
|
||||
///
|
||||
/// - `**/.bzr/`
|
||||
/// - `**/.direnv/`
|
||||
/// - `**/.eggs/`
|
||||
/// - `**/.git/`
|
||||
/// - `**/.git-rewrite/`
|
||||
/// - `**/.hg/`
|
||||
/// - `**/.mypy_cache/`
|
||||
/// - `**/.nox/`
|
||||
/// - `**/.pants.d/`
|
||||
/// - `**/.pytype/`
|
||||
/// - `**/.ruff_cache/`
|
||||
/// - `**/.svn/`
|
||||
/// - `**/.tox/`
|
||||
/// - `**/.venv/`
|
||||
/// - `**/__pypackages__/`
|
||||
/// - `**/_build/`
|
||||
/// - `**/buck-out/`
|
||||
/// - `**/dist/`
|
||||
/// - `**/node_modules/`
|
||||
/// - `**/venv/`
|
||||
///
|
||||
/// You can override any default exclude by using a negated pattern. For example,
|
||||
/// to re-include `dist` use `exclude = ["!dist"]`
|
||||
|
@ -545,7 +548,7 @@ pub struct SrcOptions {
|
|||
"#
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub exclude: Option<RangedValue<Vec<RelativeExcludePattern>>>,
|
||||
pub exclude: Option<RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
}
|
||||
|
||||
impl SrcOptions {
|
||||
|
@ -672,33 +675,33 @@ impl Rules {
|
|||
|
||||
/// Default exclude patterns for src options.
|
||||
const DEFAULT_SRC_EXCLUDES: &[&str] = &[
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".git-rewrite",
|
||||
".hg",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pytype",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"venv",
|
||||
"**/.bzr/",
|
||||
"**/.direnv/",
|
||||
"**/.eggs/",
|
||||
"**/.git/",
|
||||
"**/.git-rewrite/",
|
||||
"**/.hg/",
|
||||
"**/.mypy_cache/",
|
||||
"**/.nox/",
|
||||
"**/.pants.d/",
|
||||
"**/.pytype/",
|
||||
"**/.ruff_cache/",
|
||||
"**/.svn/",
|
||||
"**/.tox/",
|
||||
"**/.venv/",
|
||||
"**/__pypackages__/",
|
||||
"**/_build/",
|
||||
"**/buck-out/",
|
||||
"**/dist/",
|
||||
"**/node_modules/",
|
||||
"**/venv/",
|
||||
];
|
||||
|
||||
/// Helper function to build an include filter from patterns with proper error handling.
|
||||
fn build_include_filter(
|
||||
db: &dyn Db,
|
||||
project_root: &SystemPath,
|
||||
include_patterns: Option<&RangedValue<Vec<RelativeIncludePattern>>>,
|
||||
include_patterns: Option<&RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
context: GlobFilterContext,
|
||||
diagnostics: &mut Vec<OptionDiagnostic>,
|
||||
) -> Result<IncludeFilter, Box<OptionDiagnostic>> {
|
||||
|
@ -735,7 +738,7 @@ fn build_include_filter(
|
|||
}
|
||||
|
||||
for pattern in include_patterns {
|
||||
pattern.absolute(project_root, system)
|
||||
pattern.absolute(project_root, system, PortableGlobKind::Include)
|
||||
.and_then(|include| Ok(includes.add(&include)?))
|
||||
.map_err(|err| {
|
||||
let diagnostic = OptionDiagnostic::new(
|
||||
|
@ -773,7 +776,7 @@ fn build_include_filter(
|
|||
} else {
|
||||
includes
|
||||
.add(
|
||||
&PortableGlobPattern::parse("**", false)
|
||||
&PortableGlobPattern::parse("**", PortableGlobKind::Include)
|
||||
.unwrap()
|
||||
.into_absolute(""),
|
||||
)
|
||||
|
@ -797,7 +800,7 @@ fn build_include_filter(
|
|||
fn build_exclude_filter(
|
||||
db: &dyn Db,
|
||||
project_root: &SystemPath,
|
||||
exclude_patterns: Option<&RangedValue<Vec<RelativeExcludePattern>>>,
|
||||
exclude_patterns: Option<&RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
default_patterns: &[&str],
|
||||
context: GlobFilterContext,
|
||||
) -> Result<ExcludeFilter, Box<OptionDiagnostic>> {
|
||||
|
@ -807,7 +810,7 @@ fn build_exclude_filter(
|
|||
let mut excludes = ExcludeFilterBuilder::new();
|
||||
|
||||
for pattern in default_patterns {
|
||||
PortableGlobPattern::parse(pattern, true)
|
||||
PortableGlobPattern::parse(pattern, PortableGlobKind::Exclude)
|
||||
.and_then(|exclude| Ok(excludes.add(&exclude.into_absolute(""))?))
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("Expected default exclude to be valid glob but adding it failed with: {err}")
|
||||
|
@ -817,7 +820,7 @@ fn build_exclude_filter(
|
|||
// Add user-specified excludes
|
||||
if let Some(exclude_patterns) = exclude_patterns {
|
||||
for exclude in exclude_patterns {
|
||||
exclude.absolute(project_root, system)
|
||||
exclude.absolute(project_root, system, PortableGlobKind::Exclude)
|
||||
.and_then(|pattern| Ok(excludes.add(&pattern)?))
|
||||
.map_err(|err| {
|
||||
let diagnostic = OptionDiagnostic::new(
|
||||
|
@ -1001,7 +1004,7 @@ pub struct OverrideOptions {
|
|||
]
|
||||
"#
|
||||
)]
|
||||
pub include: Option<RangedValue<Vec<RelativeIncludePattern>>>,
|
||||
pub include: Option<RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
|
||||
/// A list of file and directory patterns to exclude from this override.
|
||||
///
|
||||
|
@ -1023,7 +1026,7 @@ pub struct OverrideOptions {
|
|||
]
|
||||
"#
|
||||
)]
|
||||
pub exclude: Option<RangedValue<Vec<RelativeExcludePattern>>>,
|
||||
pub exclude: Option<RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
|
||||
/// Rule overrides for files matching the include/exclude patterns.
|
||||
///
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::Db;
|
||||
use crate::combine::Combine;
|
||||
use crate::glob::{AbsolutePortableGlobPattern, PortableGlobError, PortableGlobPattern};
|
||||
use crate::glob::{
|
||||
AbsolutePortableGlobPattern, PortableGlobError, PortableGlobKind, PortableGlobPattern,
|
||||
};
|
||||
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
||||
use ruff_macros::Combine;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
@ -372,14 +374,14 @@ impl RelativePathBuf {
|
|||
)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct RelativeIncludePattern(RangedValue<String>);
|
||||
pub struct RelativeGlobPattern(RangedValue<String>);
|
||||
|
||||
impl RelativeIncludePattern {
|
||||
pub fn new(pattern: &str, source: ValueSource) -> Self {
|
||||
Self(RangedValue::new(pattern.to_string(), source))
|
||||
impl RelativeGlobPattern {
|
||||
pub fn new(pattern: impl AsRef<str>, source: ValueSource) -> Self {
|
||||
Self(RangedValue::new(pattern.as_ref().to_string(), source))
|
||||
}
|
||||
|
||||
pub fn cli(pattern: &str) -> Self {
|
||||
pub fn cli(pattern: impl AsRef<str>) -> Self {
|
||||
Self::new(pattern, ValueSource::Cli)
|
||||
}
|
||||
|
||||
|
@ -396,74 +398,19 @@ impl RelativeIncludePattern {
|
|||
&self,
|
||||
project_root: &SystemPath,
|
||||
system: &dyn System,
|
||||
kind: PortableGlobKind,
|
||||
) -> Result<AbsolutePortableGlobPattern, PortableGlobError> {
|
||||
let relative_to = match &self.0.source {
|
||||
ValueSource::File(_) => project_root,
|
||||
ValueSource::Cli => system.current_directory(),
|
||||
};
|
||||
|
||||
let pattern = PortableGlobPattern::parse(&self.0, false)?;
|
||||
let pattern = PortableGlobPattern::parse(&self.0, kind)?;
|
||||
Ok(pattern.into_absolute(relative_to))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RelativeIncludePattern {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Combine,
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct RelativeExcludePattern(RangedValue<String>);
|
||||
|
||||
impl RelativeExcludePattern {
|
||||
pub fn new(pattern: &str, source: ValueSource) -> Self {
|
||||
Self(RangedValue::new(pattern.to_string(), source))
|
||||
}
|
||||
|
||||
pub fn cli(pattern: &str) -> Self {
|
||||
Self::new(pattern, ValueSource::Cli)
|
||||
}
|
||||
|
||||
pub(crate) fn source(&self) -> &ValueSource {
|
||||
self.0.source()
|
||||
}
|
||||
|
||||
pub(crate) fn range(&self) -> Option<TextRange> {
|
||||
self.0.range()
|
||||
}
|
||||
|
||||
/// Resolves the absolute pattern for `self` based on its origin.
|
||||
pub(crate) fn absolute(
|
||||
&self,
|
||||
project_root: &SystemPath,
|
||||
system: &dyn System,
|
||||
) -> Result<AbsolutePortableGlobPattern, PortableGlobError> {
|
||||
let relative_to = match &self.0.source {
|
||||
ValueSource::File(_) => project_root,
|
||||
ValueSource::Cli => system.current_directory(),
|
||||
};
|
||||
|
||||
let pattern = PortableGlobPattern::parse(&self.0, true)?;
|
||||
|
||||
Ok(pattern.into_absolute(relative_to))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RelativeExcludePattern {
|
||||
impl std::fmt::Display for RelativeGlobPattern {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
|
|
4
ty.schema.json
generated
4
ty.schema.json
generated
|
@ -920,7 +920,7 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"exclude": {
|
||||
"description": "A list of file and directory patterns to exclude from type checking.\n\nPatterns follow a syntax similar to `.gitignore`: - `./src/` matches only a directory - `./src` matches both files and directories - `src` matches files or directories named `src` anywhere in the tree (e.g. `./src` or `./tests/src`) - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid. - `!pattern` negates a pattern (undoes the exclusion of files that would otherwise be excluded)\n\nBy default, the following directories are excluded:\n\n- `.bzr` - `.direnv` - `.eggs` - `.git` - `.git-rewrite` - `.hg` - `.mypy_cache` - `.nox` - `.pants.d` - `.pytype` - `.ruff_cache` - `.svn` - `.tox` - `.venv` - `__pypackages__` - `_build` - `buck-out` - `dist` - `node_modules` - `venv`\n\nYou can override any default exclude by using a negated pattern. For example, to re-include `dist` use `exclude = [\"!dist\"]`",
|
||||
"description": "A list of file and directory patterns to exclude from type checking.\n\nPatterns follow a syntax similar to `.gitignore`: - `./src/` matches only a directory - `./src` matches both files and directories - `src` matches files or directories named `src` - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid. - `!pattern` negates a pattern (undoes the exclusion of files that would otherwise be excluded)\n\nAll paths are anchored relative to the project root (`src` only matches `<project_root>/src` and not `<project_root>/test/src`). To exclude any directory or file named `src`, use `**/src` instead.\n\nBy default, ty excludes commonly ignored directories:\n\n- `**/.bzr/` - `**/.direnv/` - `**/.eggs/` - `**/.git/` - `**/.git-rewrite/` - `**/.hg/` - `**/.mypy_cache/` - `**/.nox/` - `**/.pants.d/` - `**/.pytype/` - `**/.ruff_cache/` - `**/.svn/` - `**/.tox/` - `**/.venv/` - `**/__pypackages__/` - `**/_build/` - `**/buck-out/` - `**/dist/` - `**/node_modules/` - `**/venv/`\n\nYou can override any default exclude by using a negated pattern. For example, to re-include `dist` use `exclude = [\"!dist\"]`",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
|
@ -930,7 +930,7 @@
|
|||
}
|
||||
},
|
||||
"include": {
|
||||
"description": "A list of files and directories to check. The `include` option follows a similar syntax to `.gitignore` but reversed: Including a file or directory will make it so that it (and its contents) are type checked.\n\n- `./src/` matches only a directory - `./src` matches both files and directories - `src` matches a file or directory named `src` - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid.\n\nUnlike `exclude`, all paths are anchored relative to the project root (`src` only matches `<project_root>/src` and not `<project_root>/test/src`).\n\n`exclude` takes precedence over `include`.",
|
||||
"description": "A list of files and directories to check. The `include` option follows a similar syntax to `.gitignore` but reversed: Including a file or directory will make it so that it (and its contents) are type checked.\n\n- `./src/` matches only a directory - `./src` matches both files and directories - `src` matches a file or directory named `src` - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid.\n\nAll paths are anchored relative to the project root (`src` only matches `<project_root>/src` and not `<project_root>/test/src`).\n\n`exclude` takes precedence over `include`.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue