]> git.lizzy.rs Git - rust.git/blob - src/config/options.rs
stabilise fn_args_density (#3581)
[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 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     /// Output the changed lines (for internal value only)
122     ModifiedLines,
123     /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
124     /// quits with exit code 1.
125     /// This option is designed to be run in CI where a non-zero exit signifies
126     /// non-standard code formatting. Used for `--check`.
127     Diff,
128 }
129
130 /// Client-preference for coloured output.
131 #[config_type]
132 pub enum Color {
133     /// Always use color, whether it is a piped or terminal output
134     Always,
135     /// Never use color
136     Never,
137     /// Automatically use color, if supported by terminal
138     Auto,
139 }
140
141 #[config_type]
142 /// rustfmt format style version.
143 pub enum Version {
144     /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
145     One,
146     /// 2.x.y. When specified, rustfmt will formatin the the latest style.
147     Two,
148 }
149
150 impl Color {
151     /// Whether we should use a coloured terminal.
152     pub fn use_colored_tty(self) -> bool {
153         match self {
154             Color::Always => true,
155             Color::Never => false,
156             Color::Auto => atty::is(atty::Stream::Stdout),
157         }
158     }
159 }
160
161 /// How chatty should Rustfmt be?
162 #[config_type]
163 pub enum Verbosity {
164     /// Emit more.
165     Verbose,
166     /// Default.
167     Normal,
168     /// Emit as little as possible.
169     Quiet,
170 }
171
172 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
173 pub struct WidthHeuristics {
174     // Maximum width of the args of a function call before falling back
175     // to vertical formatting.
176     pub fn_call_width: usize,
177     // Maximum width of the args of a function-like attributes before falling
178     // back to vertical formatting.
179     pub attr_fn_like_width: usize,
180     // Maximum width in the body of a struct lit before falling back to
181     // vertical formatting.
182     pub struct_lit_width: usize,
183     // Maximum width in the body of a struct variant before falling back
184     // to vertical formatting.
185     pub struct_variant_width: usize,
186     // Maximum width of an array literal before falling back to vertical
187     // formatting.
188     pub array_width: usize,
189     // Maximum length of a chain to fit on a single line.
190     pub chain_width: usize,
191     // Maximum line length for single line if-else expressions. A value
192     // of zero means always break if-else expressions.
193     pub single_line_if_else_max_width: usize,
194 }
195
196 impl WidthHeuristics {
197     // Using this WidthHeuristics means we ignore heuristics.
198     pub fn null() -> WidthHeuristics {
199         WidthHeuristics {
200             fn_call_width: usize::max_value(),
201             attr_fn_like_width: usize::max_value(),
202             struct_lit_width: 0,
203             struct_variant_width: 0,
204             array_width: usize::max_value(),
205             chain_width: usize::max_value(),
206             single_line_if_else_max_width: 0,
207         }
208     }
209
210     pub fn set(max_width: usize) -> WidthHeuristics {
211         WidthHeuristics {
212             fn_call_width: max_width,
213             attr_fn_like_width: max_width,
214             struct_lit_width: max_width,
215             struct_variant_width: max_width,
216             array_width: max_width,
217             chain_width: max_width,
218             single_line_if_else_max_width: max_width,
219         }
220     }
221
222     // scale the default WidthHeuristics according to max_width
223     pub fn scaled(max_width: usize) -> WidthHeuristics {
224         const DEFAULT_MAX_WIDTH: usize = 100;
225         let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
226             let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
227             // round to the closest 0.1
228             (ratio * 10.0).round() / 10.0
229         } else {
230             1.0
231         };
232         WidthHeuristics {
233             fn_call_width: (60.0 * max_width_ratio).round() as usize,
234             attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
235             struct_lit_width: (18.0 * max_width_ratio).round() as usize,
236             struct_variant_width: (35.0 * max_width_ratio).round() as usize,
237             array_width: (60.0 * max_width_ratio).round() as usize,
238             chain_width: (60.0 * max_width_ratio).round() as usize,
239             single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
240         }
241     }
242 }
243
244 impl ::std::str::FromStr for WidthHeuristics {
245     type Err = &'static str;
246
247     fn from_str(_: &str) -> Result<Self, Self::Err> {
248         Err("WidthHeuristics is not parsable")
249     }
250 }
251
252 impl Default for EmitMode {
253     fn default() -> EmitMode {
254         EmitMode::Files
255     }
256 }
257
258 /// A set of directories, files and modules that rustfmt should ignore.
259 #[derive(Default, Clone, Debug, PartialEq)]
260 pub struct IgnoreList {
261     /// A set of path specified in rustfmt.toml.
262     path_set: HashSet<PathBuf>,
263     /// A path to rustfmt.toml.
264     rustfmt_toml_path: PathBuf,
265 }
266
267 impl Serialize for IgnoreList {
268     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
269     where
270         S: Serializer,
271     {
272         let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
273         for e in &self.path_set {
274             seq.serialize_element(e)?;
275         }
276         seq.end()
277     }
278 }
279
280 impl<'de> Deserialize<'de> for IgnoreList {
281     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
282     where
283         D: Deserializer<'de>,
284     {
285         struct HashSetVisitor;
286         impl<'v> Visitor<'v> for HashSetVisitor {
287             type Value = HashSet<PathBuf>;
288
289             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
290                 formatter.write_str("a sequence of path")
291             }
292
293             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
294             where
295                 A: SeqAccess<'v>,
296             {
297                 let mut path_set = HashSet::new();
298                 while let Some(elem) = seq.next_element()? {
299                     path_set.insert(elem);
300                 }
301                 Ok(path_set)
302             }
303         }
304         Ok(IgnoreList {
305             path_set: deserializer.deserialize_seq(HashSetVisitor)?,
306             rustfmt_toml_path: PathBuf::new(),
307         })
308     }
309 }
310
311 impl<'a> IntoIterator for &'a IgnoreList {
312     type Item = &'a PathBuf;
313     type IntoIter = hash_set::Iter<'a, PathBuf>;
314
315     fn into_iter(self) -> Self::IntoIter {
316         self.path_set.iter()
317     }
318 }
319
320 impl IgnoreList {
321     pub fn add_prefix(&mut self, dir: &Path) {
322         self.rustfmt_toml_path = dir.to_path_buf();
323     }
324
325     pub fn rustfmt_toml_path(&self) -> &Path {
326         &self.rustfmt_toml_path
327     }
328 }
329
330 impl ::std::str::FromStr for IgnoreList {
331     type Err = &'static str;
332
333     fn from_str(_: &str) -> Result<Self, Self::Err> {
334         Err("IgnoreList is not parsable")
335     }
336 }
337
338 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
339 /// values in a config with values from the command line.
340 pub trait CliOptions {
341     fn apply_to(self, config: &mut Config);
342     fn config_path(&self) -> Option<&Path>;
343 }
344
345 /// The edition of the syntax and semntics of code (RFC 2052).
346 #[config_type]
347 pub enum Edition {
348     #[value = "2015"]
349     #[doc_hint = "2015"]
350     /// Edition 2015.
351     Edition2015,
352     #[value = "2018"]
353     #[doc_hint = "2018"]
354     /// Edition 2018.
355     Edition2018,
356 }
357
358 impl Default for Edition {
359     fn default() -> Edition {
360         Edition::Edition2015
361     }
362 }
363
364 impl Edition {
365     pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
366         match self {
367             Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
368             Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
369         }
370     }
371 }