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