]> git.lizzy.rs Git - rust.git/blob - src/config/options.rs
Rename `merge_imports` to `imports_granularity` and add a `Module` option.
[rust.git] / src / config / options.rs
1 use std::collections::{hash_set, HashSet};
2 use std::fmt;
3 use std::path::{Path, PathBuf};
4 use std::str::FromStr;
5
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 /// Configuration for import groups, i.e. sets of imports separated by newlines.
105 pub enum GroupImportsTactic {
106     /// Keep groups as they are.
107     Preserve,
108     /// Discard existing groups, and create new groups for
109     ///  1. `std` / `core` / `alloc` imports
110     ///  2. other imports
111     ///  3. `self` / `crate` / `super` imports
112     StdExternalCrate,
113 }
114
115 #[config_type]
116 /// How to merge imports.
117 pub enum ImportGranularity {
118     /// Do not merge imports.
119     Preserve,
120     /// Use one `use` statement per crate.
121     Crate,
122     /// Use one `use` statement per module.
123     Module,
124 }
125
126 #[config_type]
127 pub enum ReportTactic {
128     Always,
129     Unnumbered,
130     Never,
131 }
132
133 /// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
134 /// option.
135 #[config_type]
136 pub enum EmitMode {
137     /// Emits to files.
138     Files,
139     /// Writes the output to stdout.
140     Stdout,
141     /// Displays how much of the input file was processed
142     Coverage,
143     /// Unfancy stdout
144     Checkstyle,
145     /// Writes the resulting diffs in a JSON format. Returns an empty array
146     /// `[]` if there were no diffs.
147     Json,
148     /// Output the changed lines (for internal value only)
149     ModifiedLines,
150     /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
151     /// quits with exit code 1.
152     /// This option is designed to be run in CI where a non-zero exit signifies
153     /// non-standard code formatting. Used for `--check`.
154     Diff,
155 }
156
157 /// Client-preference for coloured output.
158 #[config_type]
159 pub enum Color {
160     /// Always use color, whether it is a piped or terminal output
161     Always,
162     /// Never use color
163     Never,
164     /// Automatically use color, if supported by terminal
165     Auto,
166 }
167
168 #[config_type]
169 /// rustfmt format style version.
170 pub enum Version {
171     /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
172     One,
173     /// 2.x.y. When specified, rustfmt will format in the the latest style.
174     Two,
175 }
176
177 impl Color {
178     /// Whether we should use a coloured terminal.
179     pub fn use_colored_tty(self) -> bool {
180         match self {
181             Color::Always | Color::Auto => true,
182             Color::Never => false,
183         }
184     }
185 }
186
187 /// How chatty should Rustfmt be?
188 #[config_type]
189 pub enum Verbosity {
190     /// Emit more.
191     Verbose,
192     /// Default.
193     Normal,
194     /// Emit as little as possible.
195     Quiet,
196 }
197
198 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
199 pub struct WidthHeuristics {
200     // Maximum width of the args of a function call before falling back
201     // to vertical formatting.
202     pub fn_call_width: usize,
203     // Maximum width of the args of a function-like attributes before falling
204     // back to vertical formatting.
205     pub attr_fn_like_width: usize,
206     // Maximum width in the body of a struct lit before falling back to
207     // vertical formatting.
208     pub struct_lit_width: usize,
209     // Maximum width in the body of a struct variant before falling back
210     // to vertical formatting.
211     pub struct_variant_width: usize,
212     // Maximum width of an array literal before falling back to vertical
213     // formatting.
214     pub array_width: usize,
215     // Maximum length of a chain to fit on a single line.
216     pub chain_width: usize,
217     // Maximum line length for single line if-else expressions. A value
218     // of zero means always break if-else expressions.
219     pub single_line_if_else_max_width: usize,
220 }
221
222 impl fmt::Display for WidthHeuristics {
223     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224         write!(f, "{:?}", self)
225     }
226 }
227
228 impl WidthHeuristics {
229     // Using this WidthHeuristics means we ignore heuristics.
230     pub fn null() -> WidthHeuristics {
231         WidthHeuristics {
232             fn_call_width: usize::max_value(),
233             attr_fn_like_width: usize::max_value(),
234             struct_lit_width: 0,
235             struct_variant_width: 0,
236             array_width: usize::max_value(),
237             chain_width: usize::max_value(),
238             single_line_if_else_max_width: 0,
239         }
240     }
241
242     pub fn set(max_width: usize) -> WidthHeuristics {
243         WidthHeuristics {
244             fn_call_width: max_width,
245             attr_fn_like_width: max_width,
246             struct_lit_width: max_width,
247             struct_variant_width: max_width,
248             array_width: max_width,
249             chain_width: max_width,
250             single_line_if_else_max_width: max_width,
251         }
252     }
253
254     // scale the default WidthHeuristics according to max_width
255     pub fn scaled(max_width: usize) -> WidthHeuristics {
256         const DEFAULT_MAX_WIDTH: usize = 100;
257         let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
258             let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
259             // round to the closest 0.1
260             (ratio * 10.0).round() / 10.0
261         } else {
262             1.0
263         };
264         WidthHeuristics {
265             fn_call_width: (60.0 * max_width_ratio).round() as usize,
266             attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
267             struct_lit_width: (18.0 * max_width_ratio).round() as usize,
268             struct_variant_width: (35.0 * max_width_ratio).round() as usize,
269             array_width: (60.0 * max_width_ratio).round() as usize,
270             chain_width: (60.0 * max_width_ratio).round() as usize,
271             single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
272         }
273     }
274 }
275
276 impl ::std::str::FromStr for WidthHeuristics {
277     type Err = &'static str;
278
279     fn from_str(_: &str) -> Result<Self, Self::Err> {
280         Err("WidthHeuristics is not parsable")
281     }
282 }
283
284 impl Default for EmitMode {
285     fn default() -> EmitMode {
286         EmitMode::Files
287     }
288 }
289
290 /// A set of directories, files and modules that rustfmt should ignore.
291 #[derive(Default, Clone, Debug, PartialEq)]
292 pub struct IgnoreList {
293     /// A set of path specified in rustfmt.toml.
294     path_set: HashSet<PathBuf>,
295     /// A path to rustfmt.toml.
296     rustfmt_toml_path: PathBuf,
297 }
298
299 impl fmt::Display for IgnoreList {
300     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301         write!(
302             f,
303             "[{}]",
304             self.path_set
305                 .iter()
306                 .format_with(", ", |path, f| f(&format_args!(
307                     "{}",
308                     path.to_string_lossy()
309                 )))
310         )
311     }
312 }
313
314 impl Serialize for IgnoreList {
315     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
316     where
317         S: Serializer,
318     {
319         let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
320         for e in &self.path_set {
321             seq.serialize_element(e)?;
322         }
323         seq.end()
324     }
325 }
326
327 impl<'de> Deserialize<'de> for IgnoreList {
328     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329     where
330         D: Deserializer<'de>,
331     {
332         struct HashSetVisitor;
333         impl<'v> Visitor<'v> for HashSetVisitor {
334             type Value = HashSet<PathBuf>;
335
336             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
337                 formatter.write_str("a sequence of path")
338             }
339
340             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
341             where
342                 A: SeqAccess<'v>,
343             {
344                 let mut path_set = HashSet::new();
345                 while let Some(elem) = seq.next_element()? {
346                     path_set.insert(elem);
347                 }
348                 Ok(path_set)
349             }
350         }
351         Ok(IgnoreList {
352             path_set: deserializer.deserialize_seq(HashSetVisitor)?,
353             rustfmt_toml_path: PathBuf::new(),
354         })
355     }
356 }
357
358 impl<'a> IntoIterator for &'a IgnoreList {
359     type Item = &'a PathBuf;
360     type IntoIter = hash_set::Iter<'a, PathBuf>;
361
362     fn into_iter(self) -> Self::IntoIter {
363         self.path_set.iter()
364     }
365 }
366
367 impl IgnoreList {
368     pub fn add_prefix(&mut self, dir: &Path) {
369         self.rustfmt_toml_path = dir.to_path_buf();
370     }
371
372     pub fn rustfmt_toml_path(&self) -> &Path {
373         &self.rustfmt_toml_path
374     }
375 }
376
377 impl FromStr for IgnoreList {
378     type Err = &'static str;
379
380     fn from_str(_: &str) -> Result<Self, Self::Err> {
381         Err("IgnoreList is not parsable")
382     }
383 }
384
385 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
386 /// values in a config with values from the command line.
387 pub trait CliOptions {
388     fn apply_to(self, config: &mut Config);
389     fn config_path(&self) -> Option<&Path>;
390 }
391
392 /// The edition of the syntax and semntics of code (RFC 2052).
393 #[config_type]
394 pub enum Edition {
395     #[value = "2015"]
396     #[doc_hint = "2015"]
397     /// Edition 2015.
398     Edition2015,
399     #[value = "2018"]
400     #[doc_hint = "2018"]
401     /// Edition 2018.
402     Edition2018,
403     #[value = "2021"]
404     #[doc_hint = "2021"]
405     /// Edition 2021.
406     Edition2021,
407 }
408
409 impl Default for Edition {
410     fn default() -> Edition {
411         Edition::Edition2015
412     }
413 }
414
415 impl From<Edition> for rustc_span::edition::Edition {
416     fn from(edition: Edition) -> Self {
417         match edition {
418             Edition::Edition2015 => Self::Edition2015,
419             Edition::Edition2018 => Self::Edition2018,
420             Edition::Edition2021 => Self::Edition2021,
421         }
422     }
423 }
424
425 impl PartialOrd for Edition {
426     fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
427         rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
428     }
429 }
430
431 /// Controls how rustfmt should handle leading pipes on match arms.
432 #[config_type]
433 pub enum MatchArmLeadingPipe {
434     /// Place leading pipes on all match arms
435     Always,
436     /// Never emit leading pipes on match arms
437     Never,
438     /// Preserve any existing leading pipes
439     Preserve,
440 }