]> git.lizzy.rs Git - rust.git/blob - src/config/options.rs
add new flag to list names of misformatted files (#3747)
[rust.git] / src / config / options.rs
1 use std::collections::{hash_set, HashSet};
2 use std::fmt;
3 use std::path::{Path, PathBuf};
4
5 use atty;
6 use itertools::Itertools;
7 use rustfmt_config_proc_macro::config_type;
8 use serde::de::{SeqAccess, Visitor};
9 use serde::ser::SerializeSeq;
10 use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
12 use crate::config::lists::*;
13 use crate::config::Config;
14
15 #[config_type]
16 pub enum NewlineStyle {
17     /// Auto-detect based on the raw source input.
18     Auto,
19     /// Force CRLF (`\r\n`).
20     Windows,
21     /// Force CR (`\n).
22     Unix,
23     /// `\r\n` in Windows, `\n`` on other platforms.
24     Native,
25 }
26
27 #[config_type]
28 /// Where to put the opening brace of items (`fn`, `impl`, etc.).
29 pub enum BraceStyle {
30     /// Put the opening brace on the next line.
31     AlwaysNextLine,
32     /// Put the opening brace on the same line, if possible.
33     PreferSameLine,
34     /// Prefer the same line except where there is a where-clause, in which
35     /// case force the brace to be put on the next line.
36     SameLineWhere,
37 }
38
39 #[config_type]
40 /// Where to put the opening brace of conditional expressions (`if`, `match`, etc.).
41 pub enum ControlBraceStyle {
42     /// K&R style, Rust community default
43     AlwaysSameLine,
44     /// Stroustrup style
45     ClosingNextLine,
46     /// Allman style
47     AlwaysNextLine,
48 }
49
50 #[config_type]
51 /// How to indent.
52 pub enum IndentStyle {
53     /// First line on the same line as the opening brace, all lines aligned with
54     /// the first line.
55     Visual,
56     /// First line is on a new line and all lines align with **block** indent.
57     Block,
58 }
59
60 #[config_type]
61 /// How to place a list-like items.
62 /// FIXME: Issue-3581: this should be renamed to ItemsLayout when publishing 2.0
63 pub enum Density {
64     /// Fit as much on one line as possible.
65     Compressed,
66     /// Items are placed horizontally if sufficient space, vertically otherwise.
67     Tall,
68     /// Place every item on a separate line.
69     Vertical,
70 }
71
72 #[config_type]
73 /// Spacing around type combinators.
74 pub enum TypeDensity {
75     /// No spaces around "=" and "+"
76     Compressed,
77     /// Spaces around " = " and " + "
78     Wide,
79 }
80
81 #[config_type]
82 /// To what extent does rustfmt pursue its heuristics?
83 pub enum Heuristics {
84     /// Turn off any heuristics
85     Off,
86     /// Turn on max heuristics
87     Max,
88     /// Use Rustfmt's defaults
89     Default,
90 }
91
92 impl Density {
93     pub fn to_list_tactic(self, len: usize) -> ListTactic {
94         match self {
95             Density::Compressed => ListTactic::Mixed,
96             Density::Tall => ListTactic::HorizontalVertical,
97             Density::Vertical if len == 1 => ListTactic::Horizontal,
98             Density::Vertical => ListTactic::Vertical,
99         }
100     }
101 }
102
103 #[config_type]
104 pub enum ReportTactic {
105     Always,
106     Unnumbered,
107     Never,
108 }
109
110 /// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
111 /// option.
112 #[config_type]
113 pub enum EmitMode {
114     /// Emits to files.
115     Files,
116     /// Writes the output to stdout.
117     Stdout,
118     /// Displays how much of the input file was processed
119     Coverage,
120     /// Unfancy stdout
121     Checkstyle,
122     /// Writes the resulting diffs in a JSON format. Returns an empty array
123     /// `[]` if there were no diffs.
124     Json,
125     /// Output the changed lines (for internal value only)
126     ModifiedLines,
127     /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
128     /// quits with exit code 1.
129     /// This option is designed to be run in CI where a non-zero exit signifies
130     /// non-standard code formatting. Used for `--check`.
131     Diff,
132 }
133
134 /// Client-preference for coloured output.
135 #[config_type]
136 pub enum Color {
137     /// Always use color, whether it is a piped or terminal output
138     Always,
139     /// Never use color
140     Never,
141     /// Automatically use color, if supported by terminal
142     Auto,
143 }
144
145 #[config_type]
146 /// rustfmt format style version.
147 pub enum Version {
148     /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
149     One,
150     /// 2.x.y. When specified, rustfmt will formatin the the latest style.
151     Two,
152 }
153
154 impl Color {
155     /// Whether we should use a coloured terminal.
156     pub fn use_colored_tty(self) -> bool {
157         match self {
158             Color::Always => true,
159             Color::Never => false,
160             Color::Auto => atty::is(atty::Stream::Stdout),
161         }
162     }
163 }
164
165 /// How chatty should Rustfmt be?
166 #[config_type]
167 pub enum Verbosity {
168     /// Emit more.
169     Verbose,
170     /// Default.
171     Normal,
172     /// Emit as little as possible.
173     Quiet,
174 }
175
176 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
177 pub struct WidthHeuristics {
178     // Maximum width of the args of a function call before falling back
179     // to vertical formatting.
180     pub fn_call_width: usize,
181     // Maximum width of the args of a function-like attributes before falling
182     // back to vertical formatting.
183     pub attr_fn_like_width: usize,
184     // Maximum width in the body of a struct lit before falling back to
185     // vertical formatting.
186     pub struct_lit_width: usize,
187     // Maximum width in the body of a struct variant before falling back
188     // to vertical formatting.
189     pub struct_variant_width: usize,
190     // Maximum width of an array literal before falling back to vertical
191     // formatting.
192     pub array_width: usize,
193     // Maximum length of a chain to fit on a single line.
194     pub chain_width: usize,
195     // Maximum line length for single line if-else expressions. A value
196     // of zero means always break if-else expressions.
197     pub single_line_if_else_max_width: usize,
198 }
199
200 impl fmt::Display for WidthHeuristics {
201     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202         write!(f, "{:?}", self)
203     }
204 }
205
206 impl WidthHeuristics {
207     // Using this WidthHeuristics means we ignore heuristics.
208     pub fn null() -> WidthHeuristics {
209         WidthHeuristics {
210             fn_call_width: usize::max_value(),
211             attr_fn_like_width: usize::max_value(),
212             struct_lit_width: 0,
213             struct_variant_width: 0,
214             array_width: usize::max_value(),
215             chain_width: usize::max_value(),
216             single_line_if_else_max_width: 0,
217         }
218     }
219
220     pub fn set(max_width: usize) -> WidthHeuristics {
221         WidthHeuristics {
222             fn_call_width: max_width,
223             attr_fn_like_width: max_width,
224             struct_lit_width: max_width,
225             struct_variant_width: max_width,
226             array_width: max_width,
227             chain_width: max_width,
228             single_line_if_else_max_width: max_width,
229         }
230     }
231
232     // scale the default WidthHeuristics according to max_width
233     pub fn scaled(max_width: usize) -> WidthHeuristics {
234         const DEFAULT_MAX_WIDTH: usize = 100;
235         let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
236             let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
237             // round to the closest 0.1
238             (ratio * 10.0).round() / 10.0
239         } else {
240             1.0
241         };
242         WidthHeuristics {
243             fn_call_width: (60.0 * max_width_ratio).round() as usize,
244             attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
245             struct_lit_width: (18.0 * max_width_ratio).round() as usize,
246             struct_variant_width: (35.0 * max_width_ratio).round() as usize,
247             array_width: (60.0 * max_width_ratio).round() as usize,
248             chain_width: (60.0 * max_width_ratio).round() as usize,
249             single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
250         }
251     }
252 }
253
254 impl ::std::str::FromStr for WidthHeuristics {
255     type Err = &'static str;
256
257     fn from_str(_: &str) -> Result<Self, Self::Err> {
258         Err("WidthHeuristics is not parsable")
259     }
260 }
261
262 impl Default for EmitMode {
263     fn default() -> EmitMode {
264         EmitMode::Files
265     }
266 }
267
268 /// A set of directories, files and modules that rustfmt should ignore.
269 #[derive(Default, Clone, Debug, PartialEq)]
270 pub struct IgnoreList {
271     /// A set of path specified in rustfmt.toml.
272     path_set: HashSet<PathBuf>,
273     /// A path to rustfmt.toml.
274     rustfmt_toml_path: PathBuf,
275 }
276
277 impl fmt::Display for IgnoreList {
278     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279         write!(
280             f,
281             "[{}]",
282             self.path_set
283                 .iter()
284                 .format_with(", ", |path, f| f(&format_args!(
285                     "{}",
286                     path.to_string_lossy()
287                 )))
288         )
289     }
290 }
291
292 impl Serialize for IgnoreList {
293     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
294     where
295         S: Serializer,
296     {
297         let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
298         for e in &self.path_set {
299             seq.serialize_element(e)?;
300         }
301         seq.end()
302     }
303 }
304
305 impl<'de> Deserialize<'de> for IgnoreList {
306     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
307     where
308         D: Deserializer<'de>,
309     {
310         struct HashSetVisitor;
311         impl<'v> Visitor<'v> for HashSetVisitor {
312             type Value = HashSet<PathBuf>;
313
314             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
315                 formatter.write_str("a sequence of path")
316             }
317
318             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
319             where
320                 A: SeqAccess<'v>,
321             {
322                 let mut path_set = HashSet::new();
323                 while let Some(elem) = seq.next_element()? {
324                     path_set.insert(elem);
325                 }
326                 Ok(path_set)
327             }
328         }
329         Ok(IgnoreList {
330             path_set: deserializer.deserialize_seq(HashSetVisitor)?,
331             rustfmt_toml_path: PathBuf::new(),
332         })
333     }
334 }
335
336 impl<'a> IntoIterator for &'a IgnoreList {
337     type Item = &'a PathBuf;
338     type IntoIter = hash_set::Iter<'a, PathBuf>;
339
340     fn into_iter(self) -> Self::IntoIter {
341         self.path_set.iter()
342     }
343 }
344
345 impl IgnoreList {
346     pub fn add_prefix(&mut self, dir: &Path) {
347         self.rustfmt_toml_path = dir.to_path_buf();
348     }
349
350     pub fn rustfmt_toml_path(&self) -> &Path {
351         &self.rustfmt_toml_path
352     }
353 }
354
355 impl ::std::str::FromStr for IgnoreList {
356     type Err = &'static str;
357
358     fn from_str(_: &str) -> Result<Self, Self::Err> {
359         Err("IgnoreList is not parsable")
360     }
361 }
362
363 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
364 /// values in a config with values from the command line.
365 pub trait CliOptions {
366     fn apply_to(self, config: &mut Config);
367     fn config_path(&self) -> Option<&Path>;
368 }
369
370 /// The edition of the syntax and semntics of code (RFC 2052).
371 #[config_type]
372 pub enum Edition {
373     #[value = "2015"]
374     #[doc_hint = "2015"]
375     /// Edition 2015.
376     Edition2015,
377     #[value = "2018"]
378     #[doc_hint = "2018"]
379     /// Edition 2018.
380     Edition2018,
381 }
382
383 impl Default for Edition {
384     fn default() -> Edition {
385         Edition::Edition2015
386     }
387 }
388
389 impl Edition {
390     pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
391         match self {
392             Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
393             Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
394         }
395     }
396 }