Add ignore config option

For example, with the following config file, rustfmt will ignore `src/types.rs`,
`src/foo/bar.rs` and every file under `examples/` directory.

```toml
[ignore]
files = [
    "src/types.rs",
    "src/foo/bar.rs",
]
directories = [
    "examples",
]
```
This commit is contained in:
Seiichi Uchida 2018-03-09 13:27:43 +09:00
parent c829875960
commit 84ad70c151
7 changed files with 124 additions and 11 deletions

View File

@ -2135,3 +2135,32 @@ Copyright 2018 The Rust Project Developers.`, etc.:
```
`\{`, `\}` and `\\` match literal braces / backslashes.
## `ignore`
Skip formatting the specified files and directories.
- **Default value**: format every files
- **Possible values**: See an example below
- **Stable**: No
### Example
If you want to ignore specific files, put the following to your config file:
```toml
[ignore]
files = [
"src/types.rs",
"src/foo/bar.rs",
]
```
If you want to ignore every file under `examples/`, put the following to your config file:
```toml
[ignore]
directories = [
"examples",
]
```

View File

@ -18,6 +18,7 @@ use std::{env, error};
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use getopts::{Matches, Options};
@ -25,8 +26,6 @@ use rustfmt::config::{get_toml_path, Color, Config, WriteMode};
use rustfmt::config::file_lines::FileLines;
use rustfmt::{run, FileName, Input, Summary};
use std::str::FromStr;
type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

View File

@ -9,7 +9,7 @@
// except according to those terms.
use config::file_lines::FileLines;
use config::options::WidthHeuristics;
use config::options::{IgnoreList, WidthHeuristics};
/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
@ -54,6 +54,12 @@ impl ConfigType for WidthHeuristics {
}
}
impl ConfigType for IgnoreList {
fn doc_hint() -> String {
String::from("[<string>,..]")
}
}
/// Check if we're in a nightly build.
///
/// The environment variable `CFG_RELEASE_CHANNEL` is set during the rustc bootstrap
@ -176,7 +182,7 @@ macro_rules! create_config {
ConfigWasSet(self)
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config {
fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
$(
if let Some(val) = parsed.$i {
if self.$i.3 {
@ -195,6 +201,7 @@ macro_rules! create_config {
)+
self.set_heuristics();
self.set_license_template();
self.set_ignore(dir);
self
}
@ -216,7 +223,7 @@ macro_rules! create_config {
}
}
pub fn from_toml(toml: &str) -> Result<Config, String> {
pub fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
let parsed: ::toml::Value =
toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
let mut err: String = String::new();
@ -236,7 +243,7 @@ macro_rules! create_config {
if !err.is_empty() {
eprint!("{}", err);
}
Ok(Config::default().fill_from_parsed_config(parsed_config))
Ok(Config::default().fill_from_parsed_config(parsed_config, dir: &Path))
}
Err(e) => {
err.push_str("Error: Decoding config file failed:\n");
@ -300,7 +307,8 @@ macro_rules! create_config {
let mut file = File::open(&file_path)?;
let mut toml = String::new();
file.read_to_string(&mut toml)?;
Config::from_toml(&toml).map_err(|err| Error::new(ErrorKind::InvalidData, err))
Config::from_toml(&toml, file_path.parent().unwrap())
.map_err(|err| Error::new(ErrorKind::InvalidData, err))
}
/// Resolve the config for input in `dir`.
@ -401,6 +409,10 @@ macro_rules! create_config {
}
}
}
fn set_ignore(&mut self, dir: &Path) {
self.ignore.2.add_prefix(dir);
}
}
// Template for the default configuration

View File

@ -141,6 +141,8 @@ create_config! {
"Report all, none or unnumbered occurrences of TODO in source file comments";
report_fixme: ReportTactic, ReportTactic::Never, false,
"Report all, none or unnumbered occurrences of FIXME in source file comments";
ignore: IgnoreList, IgnoreList::default(), false,
"Skip formatting the specified files and directories.";
// Not user-facing.
verbose: bool, false, false, "Use verbose output";
@ -208,7 +210,8 @@ mod test {
#[test]
fn test_was_set() {
let config = Config::from_toml("hard_tabs = true").unwrap();
use std::path::Path;
let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap();
assert_eq!(config.was_set().hard_tabs(), true);
assert_eq!(config.was_set().verbose(), false);

View File

@ -8,9 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use syntax::codemap::FileName;
use config::config_type::ConfigType;
use config::lists::*;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
/// Macro for deriving implementations of Serialize/Deserialize for enums
#[macro_export]
macro_rules! impl_enum_serialize_and_deserialize {
@ -244,3 +249,68 @@ impl ::std::str::FromStr for WidthHeuristics {
Err("WidthHeuristics is not parsable")
}
}
/// A set of directories, files and modules that rustfmt should ignore.
#[derive(Default, Deserialize, Serialize, Clone, Debug)]
pub struct IgnoreList {
directories: Option<HashSet<PathBuf>>,
files: Option<HashSet<PathBuf>>,
}
impl IgnoreList {
fn add_prefix_inner(set: &HashSet<PathBuf>, dir: &Path) -> HashSet<PathBuf> {
set.iter()
.map(|s| {
if s.has_root() {
s.clone()
} else {
let mut path = PathBuf::from(dir);
path.push(s);
path
}
})
.collect()
}
pub fn add_prefix(&mut self, dir: &Path) {
macro add_prefix_inner_with ($($field: ident),* $(,)*) {
$(if let Some(set) = self.$field.as_mut() {
*set = IgnoreList::add_prefix_inner(set, dir);
})*
}
add_prefix_inner_with!(directories, files);
}
fn is_ignore_file(&self, path: &Path) -> bool {
self.files.as_ref().map_or(false, |set| set.contains(path))
}
fn is_under_ignore_dir(&self, path: &Path) -> bool {
if let Some(ref dirs) = self.directories {
for dir in dirs {
if path.starts_with(dir) {
return true;
}
}
}
false
}
pub fn skip_file(&self, file: &FileName) -> bool {
if let FileName::Real(ref path) = file {
self.is_ignore_file(path) || self.is_under_ignore_dir(path)
} else {
false
}
}
}
impl ::std::str::FromStr for IgnoreList {
type Err = &'static str;
fn from_str(_: &str) -> Result<Self, Self::Err> {
Err("IgnoreList is not parsable")
}
}

View File

@ -338,7 +338,7 @@ where
// nothing to distinguish the nested module contents.
let skip_children = config.skip_children() || config.write_mode() == config::WriteMode::Plain;
for (path, module) in modules::list_files(krate, parse_session.codemap())? {
if skip_children && path != *main_file {
if (skip_children && path != *main_file) || config.ignore().skip_file(&path) {
continue;
}
should_emit_verbose(&path, config, || println!("Formatting {}", path));

View File

@ -293,7 +293,7 @@ fn format_lines_errors_are_reported() {
fn format_lines_errors_are_reported_with_tabs() {
let long_identifier = String::from_utf8(vec![b'a'; 97]).unwrap();
let input = Input::Text(format!("fn a() {{\n\t{}\n}}", long_identifier));
let config = Config::from_toml("hard_tabs = true").unwrap();
let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap();
let (error_summary, _file_map, _report) =
format_input::<io::Stdout>(input, &config, None).unwrap();
assert!(error_summary.has_formatting_errors());
@ -433,7 +433,7 @@ fn get_config(config_file: Option<&Path>) -> Config {
.read_to_string(&mut def_config)
.expect("Couldn't read config");
Config::from_toml(&def_config).expect("Invalid toml")
Config::from_toml(&def_config, Path::new("tests/config/")).expect("Invalid toml")
}
// Reads significant comments of the form: // rustfmt-key: value