Skip to content

Commit

Permalink
Merge pull request #238 from PlasmaFAIR/feature/exclude_files
Browse files Browse the repository at this point in the history
Add options to exclude files
  • Loading branch information
ZedThree authored Dec 20, 2024
2 parents a185b11 + e921ae7 commit 79559f9
Show file tree
Hide file tree
Showing 8 changed files with 636 additions and 75 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ fortitude check
```

You can also call `check` on individual files, globs, and
directories. You can configure what extensions `fortitude` searches
directories. You can configure what extensions Fortitude searches
for in directories with `--file-extensions`:

```bash
fortitude check src --file-extensions=f90,fpp
fortitude check --file-extensions=f90,fpp
```

You can select or ignore individual rules or whole groups with
Expand All @@ -77,7 +77,7 @@ fortitude check --select=typing --ignore=superfluous-implicit-none
Use `--output-format=concise` to get shorter output:

```bash
$ fortitude check --concise
$ fortitude check --output-format=concise
test.f90:2:1: M001 function not contained within (sub)module or program
test.f90:5:1: S061 end statement should read 'end function double'
test.f90:7:1: M001 subroutine not contained within (sub)module or program
Expand All @@ -90,11 +90,11 @@ The `explain` command can be used to get extra information about any rules:
# Print extra information for all rules
fortitude explain
# Only get information for selected rules
fortitude explain T001,T011
fortitude explain T001 T011
# Print information on all style rules
fortitude explain S
# Rules and categories can also be referred to by name
fortitude explain style,superfluous-implicit-none
fortitude explain style superfluous-implicit-none
```

To see further commands and optional arguments, try using `--help`:
Expand Down Expand Up @@ -152,6 +152,8 @@ preview = true

Run `fortitude explain` to see which rules are in preview mode.



## Configuration

Fortitude will look for either a `fortitude.toml` or `fpm.toml` file in the
Expand Down
126 changes: 121 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,128 @@ Written in Rust :crab: and installable with Python :snake:.

Fortitude can be installed directly into your Python environment:

```console
$ pip install fortitude-lint
```bash
pip install fortitude-lint
```

Run Fortitude on your project:
You can then lint your whole project under the current working directory
using the `check` command:

```console
$ fortitude check
```bash
fortitude check
```

You can also call `check` on individual files, directories, and globs:

```bash
fortitude check main.f90 src/ extra/*.f90
```

Some rule violations can even be fixed automatically:

```bash
fortitude check --fix
```

The `explain` command can be used to get extra information about any rules:

```bash
# Print extra information for all rules
fortitude explain
# Only get information for selected rules, by code or by name
fortitude explain T001 trailing-whitespace
# Print information on all style rules
fortitude explain style
```

New rules and other features may be in 'preview' mode while they undergo further review
and testing. To activate them, use the `--preview` flag:

```bash
fortitude check --preview
```

To see further commands and optional arguments, try using `--help`:

```bash
fortitude --help
fortitude check --help
```

### Rule Selection

You can select or ignore individual rules or whole groups with
`--select` and `--ignore`:

```bash
# Just check for missing `implicit none`
fortitude check --select=T001
# Also check for missing `implicit none` in interfaces
fortitude check --select=T001,T002
# Ignore all styling rules
fortitude check --ignore=S
# Only check for typing rules, but ignore superfluous implicit none
fortitude check --select=T --ignore=T003
# Rules and categories can also be referred to by name
fortitude check --select=typing --ignore=superfluous-implicit-none
```

It is also possible to switch off individual rules or rule categories for specific
files using `--per-file-ignores`:

```bash
fortitude check --per-file-ignores=**/*.f95:non-standard-file-extension
```

### Filtering Files

Fortitude will automatically ignore files in some directories (`build/`, `.git/`,
`.venv/`, etc.), and this behaviour can be extended using the `--exclude` option. For
example, to ignore all files in the directory `benchmarks/`:

```bash
fortitude check --exclude=benchmarks
```

You can also configure what extensions Fortitude searches for in directories with
`--file-extensions`:

```bash
fortitude check --file-extensions=f90,fpp
```

### Configuration

Fortitude will look for either a `fortitude.toml` or `fpm.toml` file in the
current directory or one of its parents. If using `fortitude.toml`, settings
should be under the command name:

```toml
[check]
select = ["S", "T"]
ignore = ["S001", "S051"]
line-length = 132
```

For `fpm.toml` files, this has to be additionally nested under the
`extra.fortitude` table:

```toml
[extra.fortitude.check]
select = ["S", "T"]
ignore = ["S001", "S051"]
line-length = 132
```

Arguments on the command line take precedence over those in the configuration file,
so using `--select` will override the choices shown above. You should instead use
`--extend-select` from the command line to select additional rules on top of those in
the configuration file:

```bash
# Selects S, T, and M categories
fortitude check --extend-select=M
```

Similar options include `--extend-exclude`, `--extend-ignore`, and
`--extend-per-file-ignores`.
1 change: 0 additions & 1 deletion fortitude/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ unicode-width = "0.2.0"
url = { version = "2.5.0" }
walkdir = "2.4.0"
globset = "0.4.15"
glob = "0.3.1"

[build-dependencies]
shadow-rs = { version = "0.36.0", default-features = false }
Expand Down
74 changes: 60 additions & 14 deletions fortitude/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::rule_selector::{
use crate::rules::Rule;
use crate::rules::{error::ioerror::IoError, AstRuleEnum, PathRuleEnum, TextRuleEnum};
use crate::settings::{
FixMode, OutputFormat, PatternPrefixPair, PreviewMode, ProgressBar, Settings, UnsafeFixes,
DEFAULT_SELECTORS,
ExcludeMode, FilePattern, FilePatternSet, FixMode, OutputFormat, PatternPrefixPair,
PreviewMode, ProgressBar, Settings, UnsafeFixes, DEFAULT_SELECTORS,
};

use anyhow::{anyhow, Context, Result};
Expand Down Expand Up @@ -55,6 +55,25 @@ struct CheckSection {
check: Option<CheckArgs>,
}

// Default paths to exclude when searching paths
pub(crate) static EXCLUDE_BUILTINS: &[FilePattern] = &[
FilePattern::Builtin(".git"),
FilePattern::Builtin(".git-rewrite"),
FilePattern::Builtin(".hg"),
FilePattern::Builtin(".svn"),
FilePattern::Builtin("venv"),
FilePattern::Builtin(".venv"),
FilePattern::Builtin("pyenv"),
FilePattern::Builtin(".pyenv"),
FilePattern::Builtin(".eggs"),
FilePattern::Builtin("site-packages"),
FilePattern::Builtin(".vscode"),
FilePattern::Builtin("build"),
FilePattern::Builtin("_build"),
FilePattern::Builtin("dist"),
FilePattern::Builtin("_dist"),
];

// Adapted from ruff
fn parse_fpm_toml<P: AsRef<Path>>(path: P) -> Result<Fpm> {
let contents = std::fs::read_to_string(path.as_ref())
Expand Down Expand Up @@ -154,6 +173,9 @@ pub struct CheckSettings {
pub output_format: OutputFormat,
pub progress_bar: ProgressBar,
pub preview: PreviewMode,
pub exclude: Option<Vec<FilePattern>>,
pub extend_exclude: Vec<FilePattern>,
pub exclude_mode: ExcludeMode,
}

/// Read either fpm.toml or fortitude.toml into our "known good" file
Expand Down Expand Up @@ -192,6 +214,11 @@ fn parse_config_file(config_file: &Option<PathBuf>) -> Result<CheckSettings> {
preview: resolve_bool_arg(value.preview, value.no_preview)
.map(PreviewMode::from)
.unwrap_or_default(),
exclude: value.exclude,
extend_exclude: value.extend_exclude.unwrap_or_default(),
exclude_mode: resolve_bool_arg(value.force_exclude, value.no_force_exclude)
.map(ExcludeMode::from)
.unwrap_or_default(),
},
None => CheckSettings::default(),
};
Expand Down Expand Up @@ -241,9 +268,8 @@ fn ruleset(args: RuleSelection, preview: &PreviewMode) -> anyhow::Result<Vec<Rul
Ok(rules)
}

/// Helper function used with `filter` to select only paths that end in a Fortran extension.
/// Includes non-standard extensions, as these should be reported.
fn filter_fortran_extensions<S: AsRef<str>>(path: &Path, extensions: &[S]) -> bool {
/// Helper function used with `get_files` to select only paths that end in a Fortran extension.
fn is_valid_extension<S: AsRef<str>>(path: &Path, extensions: &[S]) -> bool {
if let Some(ext) = path.extension() {
// Can't use '&[&str].contains()', as extensions are of type OsStr
extensions.iter().any(|x| x.as_ref() == ext)
Expand All @@ -256,24 +282,28 @@ fn filter_fortran_extensions<S: AsRef<str>>(path: &Path, extensions: &[S]) -> bo
fn get_files<P: AsRef<Path>, S: AsRef<str>>(
paths: &[P],
extensions: &[S],
) -> anyhow::Result<Vec<PathBuf>> {
excludes: &FilePatternSet,
exclude_mode: ExcludeMode,
) -> Vec<PathBuf> {
paths
.iter()
.flat_map(|path| {
if path.as_ref().is_dir() {
if matches!(exclude_mode, ExcludeMode::Force) && excludes.matches(path) {
vec![]
} else if path.as_ref().is_dir() {
WalkDir::new(path)
.min_depth(1)
.into_iter()
.filter_map(|x| x.ok()) // skip dirs if user doesn't have permission
.filter(|x| filter_fortran_extensions(x.path(), extensions))
.map(|x| std::path::absolute(x.path()))
.filter_entry(|e| !excludes.matches(e.path()))
.filter_map(|p| p.ok()) // skip dirs if user doesn't have permission
.filter(|p| is_valid_extension(p.path(), extensions))
.map(|p| fs::normalize_path(p.path()))
.collect::<Vec<_>>()
} else {
vec![std::path::absolute(path)]
vec![fs::normalize_path(path)]
}
})
.collect::<Result<Vec<_>, _>>()
.map_err(anyhow::Error::new)
.collect()
}

/// Parse a file, check it for issues, and return the report.
Expand Down Expand Up @@ -718,6 +748,22 @@ pub fn check(args: CheckArgs, global_options: &GlobalConfigArgs) -> Result<ExitC
.collect::<Vec<_>>(),
))?;

let file_excludes = FilePatternSet::try_from_iter(
EXCLUDE_BUILTINS
.iter()
.cloned()
.chain(
args.exclude
.unwrap_or(file_settings.exclude.unwrap_or_default())
.into_iter(),
)
.chain(args.extend_exclude.unwrap_or_default().into_iter())
.chain(file_settings.extend_exclude.into_iter()),
)?;
let exclude_mode = resolve_bool_arg(args.force_exclude, args.no_force_exclude)
.map(ExcludeMode::from)
.unwrap_or(file_settings.exclude_mode);

let output_format = args.output_format.unwrap_or(file_settings.output_format);
let preview_mode = resolve_bool_arg(args.preview, args.no_preview)
.map(PreviewMode::from)
Expand Down Expand Up @@ -762,7 +808,7 @@ pub fn check(args: CheckArgs, global_options: &GlobalConfigArgs) -> Result<ExitC
let text_rules = rules_to_text_rules(&rules);
let ast_entrypoints = ast_entrypoint_map(&rules);

let files = get_files(files, file_extensions)?;
let files = get_files(files, file_extensions, &file_excludes, exclude_mode);
let file_digits = files.len().to_string().len();
let progress_bar_style = match progress_bar {
ProgressBar::Fancy => {
Expand Down
Loading

0 comments on commit 79559f9

Please sign in to comment.