1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use std::collections::HashSet;
12 use std::path::{Path, PathBuf};
16 use crate::config::config_type::ConfigType;
17 use crate::config::lists::*;
18 use crate::config::{Config, FileName};
20 /// Macro that will stringify the enum variants or a provided textual repr
22 macro_rules! configuration_option_enum_stringify {
27 ($_variant:ident: $value:expr) => {
32 /// Macro for deriving implementations of Serialize/Deserialize for enums
34 macro_rules! impl_enum_serialize_and_deserialize {
35 ( $e:ident, $( $variant:ident $(: $value:expr)* ),* ) => {
36 impl ::serde::ser::Serialize for $e {
37 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
38 where S: ::serde::ser::Serializer
40 use serde::ser::Error;
42 // We don't know whether the user of the macro has given us all options.
43 #[allow(unreachable_patterns)]
46 $e::$variant => serializer.serialize_str(
47 configuration_option_enum_stringify!($variant $(: $value)*)
51 Err(S::Error::custom(format!("Cannot serialize {:?}", self)))
57 impl<'de> ::serde::de::Deserialize<'de> for $e {
58 fn deserialize<D>(d: D) -> Result<Self, D::Error>
59 where D: ::serde::Deserializer<'de> {
60 use serde::de::{Error, Visitor};
61 use std::marker::PhantomData;
63 struct StringOnly<T>(PhantomData<T>);
64 impl<'de, T> Visitor<'de> for StringOnly<T>
65 where T: ::serde::Deserializer<'de> {
67 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
68 formatter.write_str("string")
70 fn visit_str<E>(self, value: &str) -> Result<String, E> {
71 Ok(String::from(value))
74 let s = d.deserialize_string(StringOnly::<D>(PhantomData))?;
76 if configuration_option_enum_stringify!($variant $(: $value)*)
77 .eq_ignore_ascii_case(&s) {
78 return Ok($e::$variant);
81 static ALLOWED: &'static[&str] = &[
82 $(configuration_option_enum_stringify!($variant $(: $value)*),)*];
83 Err(D::Error::unknown_variant(&s, ALLOWED))
87 impl ::std::str::FromStr for $e {
88 type Err = &'static str;
90 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 if configuration_option_enum_stringify!($variant $(: $value)*)
93 .eq_ignore_ascii_case(s) {
94 return Ok($e::$variant);
101 impl ConfigType for $e {
102 fn doc_hint() -> String {
103 let mut variants = Vec::new();
106 configuration_option_enum_stringify!($variant $(: $value)*)
109 format!("[{}]", variants.join("|"))
115 macro_rules! configuration_option_enum {
116 ($e:ident: $( $name:ident $(: $value:expr)* ),+ $(,)*) => (
117 #[derive(Copy, Clone, Eq, PartialEq)]
122 impl ::std::fmt::Debug for $e {
123 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
124 f.write_str(match self {
126 $e::$name => configuration_option_enum_stringify!($name $(: $value)*),
132 impl_enum_serialize_and_deserialize!($e, $( $name $(: $value)* ),+);
136 configuration_option_enum! { NewlineStyle:
137 Auto, // Auto-detect based on the raw source input
140 Native, // \r\n in Windows, \n on other platforms
144 fn auto_detect(raw_input_text: &str) -> NewlineStyle {
145 if let Some(pos) = raw_input_text.find('\n') {
146 let pos = pos.saturating_sub(1);
147 if let Some('\r') = raw_input_text.chars().nth(pos) {
148 NewlineStyle::Windows
157 fn native() -> NewlineStyle {
159 NewlineStyle::Windows
165 /// Apply this newline style to the formatted text. When the style is set
166 /// to `Auto`, the `raw_input_text` is used to detect the existing line
169 /// If the style is set to `Auto` and `raw_input_text` contains no
170 /// newlines, the `Native` style will be used.
171 pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) {
172 use crate::NewlineStyle::*;
173 let mut style = self;
175 style = Self::auto_detect(raw_input_text);
178 style = Self::native();
182 let mut transformed = String::with_capacity(2 * formatted_text.capacity());
183 for c in formatted_text.chars() {
185 '\n' => transformed.push_str("\r\n"),
187 c => transformed.push(c),
190 *formatted_text = transformed;
193 Native => unreachable!("NewlineStyle::Native"),
194 Auto => unreachable!("NewlineStyle::Auto"),
199 configuration_option_enum! { BraceStyle:
202 // Prefer same line except where there is a where clause, in which case force
203 // the brace to the next line.
207 configuration_option_enum! { ControlBraceStyle:
208 // K&R style, Rust community default
216 configuration_option_enum! { IndentStyle:
217 // First line on the same line as the opening brace, all lines aligned with
220 // First line is on a new line and all lines align with block indent.
224 configuration_option_enum! { Density:
225 // Fit as much on one line as possible.
229 // Place every item on a separate line.
233 configuration_option_enum! { TypeDensity:
234 // No spaces around "=" and "+"
236 // Spaces around " = " and " + "
240 configuration_option_enum! { Heuristics:
241 // Turn off any heuristics
243 // Turn on max heuristics
245 // Use Rustfmt's defaults
250 pub fn to_list_tactic(self) -> ListTactic {
252 Density::Compressed => ListTactic::Mixed,
253 Density::Tall => ListTactic::HorizontalVertical,
254 Density::Vertical => ListTactic::Vertical,
259 configuration_option_enum! { ReportTactic:
265 // What Rustfmt should emit. Mostly corresponds to the `--emit` command line
267 configuration_option_enum! { EmitMode:
270 // Writes the output to stdout.
272 // Displays how much of the input file was processed
276 // Output the changed lines (for internal value only)
278 // Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
279 // This option is designed to be run in CI where a non-zero exit signifies non-standard code
280 // formatting. Used for `--check`.
284 // Client-preference for coloured output.
285 configuration_option_enum! { Color:
286 // Always use color, whether it is a piped or terminal output
290 // Automatically use color, if supported by terminal
294 configuration_option_enum! { Version:
302 /// Whether we should use a coloured terminal.
303 pub fn use_colored_tty(self) -> bool {
305 Color::Always => true,
306 Color::Never => false,
307 Color::Auto => atty::is(atty::Stream::Stdout),
312 // How chatty should Rustfmt be?
313 configuration_option_enum! { Verbosity:
317 // Emit as little as possible.
321 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
322 pub struct WidthHeuristics {
323 // Maximum width of the args of a function call before falling back
324 // to vertical formatting.
325 pub fn_call_width: usize,
326 // Maximum width of the args of a function-like attributes before falling
327 // back to vertical formatting.
328 pub attr_fn_like_width: usize,
329 // Maximum width in the body of a struct lit before falling back to
330 // vertical formatting.
331 pub struct_lit_width: usize,
332 // Maximum width in the body of a struct variant before falling back
333 // to vertical formatting.
334 pub struct_variant_width: usize,
335 // Maximum width of an array literal before falling back to vertical
337 pub array_width: usize,
338 // Maximum length of a chain to fit on a single line.
339 pub chain_width: usize,
340 // Maximum line length for single line if-else expressions. A value
341 // of zero means always break if-else expressions.
342 pub single_line_if_else_max_width: usize,
345 impl WidthHeuristics {
346 // Using this WidthHeuristics means we ignore heuristics.
347 pub fn null() -> WidthHeuristics {
349 fn_call_width: usize::max_value(),
350 attr_fn_like_width: usize::max_value(),
352 struct_variant_width: 0,
353 array_width: usize::max_value(),
354 chain_width: usize::max_value(),
355 single_line_if_else_max_width: 0,
359 pub fn set(max_width: usize) -> WidthHeuristics {
361 fn_call_width: max_width,
362 attr_fn_like_width: max_width,
363 struct_lit_width: max_width,
364 struct_variant_width: max_width,
365 array_width: max_width,
366 chain_width: max_width,
367 single_line_if_else_max_width: max_width,
371 // scale the default WidthHeuristics according to max_width
372 pub fn scaled(max_width: usize) -> WidthHeuristics {
373 const DEFAULT_MAX_WIDTH: usize = 100;
374 let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
375 let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
376 // round to the closest 0.1
377 (ratio * 10.0).round() / 10.0
382 fn_call_width: (60.0 * max_width_ratio).round() as usize,
383 attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
384 struct_lit_width: (18.0 * max_width_ratio).round() as usize,
385 struct_variant_width: (35.0 * max_width_ratio).round() as usize,
386 array_width: (60.0 * max_width_ratio).round() as usize,
387 chain_width: (60.0 * max_width_ratio).round() as usize,
388 single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
393 impl ::std::str::FromStr for WidthHeuristics {
394 type Err = &'static str;
396 fn from_str(_: &str) -> Result<Self, Self::Err> {
397 Err("WidthHeuristics is not parsable")
401 impl Default for EmitMode {
402 fn default() -> EmitMode {
407 /// A set of directories, files and modules that rustfmt should ignore.
408 #[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)]
409 pub struct IgnoreList(HashSet<PathBuf>);
412 pub fn add_prefix(&mut self, dir: &Path) {
420 let mut path = PathBuf::from(dir);
428 fn skip_file_inner(&self, file: &Path) -> bool {
429 self.0.iter().any(|path| file.starts_with(path))
432 pub fn skip_file(&self, file: &FileName) -> bool {
433 if let FileName::Real(ref path) = file {
434 self.skip_file_inner(path)
441 impl ::std::str::FromStr for IgnoreList {
442 type Err = &'static str;
444 fn from_str(_: &str) -> Result<Self, Self::Err> {
445 Err("IgnoreList is not parsable")
449 /// Maps client-supplied options to Rustfmt's internals, mostly overriding
450 /// values in a config with values from the command line.
451 pub trait CliOptions {
452 fn apply_to(self, config: &mut Config);
453 fn config_path(&self) -> Option<&Path>;
456 /// The edition of the compiler (RFC 2052)
457 configuration_option_enum! { Edition:
462 impl Default for Edition {
463 fn default() -> Edition {
469 pub(crate) fn to_libsyntax_pos_edition(self) -> syntax_pos::edition::Edition {
471 Edition::Edition2015 => syntax_pos::edition::Edition::Edition2015,
472 Edition::Edition2018 => syntax_pos::edition::Edition::Edition2018,
478 fn test_newline_style_auto_detect() {
479 let lf = "One\nTwo\nThree";
480 let crlf = "One\r\nTwo\r\nThree";
481 let none = "One Two Three";
483 assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf));
484 assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf));
485 assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none));
489 fn test_newline_style_auto_apply() {
490 let auto = NewlineStyle::Auto;
492 let formatted_text = "One\nTwo\nThree";
493 let raw_input_text = "One\nTwo\nThree";
495 let mut out = String::from(formatted_text);
496 auto.apply(&mut out, raw_input_text);
497 assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
499 let formatted_text = "One\nTwo\nThree";
500 let raw_input_text = "One\r\nTwo\r\nThree";
502 let mut out = String::from(formatted_text);
503 auto.apply(&mut out, raw_input_text);
504 assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
508 let formatted_text = "One\nTwo\nThree";
509 let raw_input_text = "One Two Three";
511 let mut out = String::from(formatted_text);
512 auto.apply(&mut out, raw_input_text);
514 "One\nTwo\nThree", &out,
515 "auto-native-unix should detect 'lf'"
521 let formatted_text = "One\nTwo\nThree";
522 let raw_input_text = "One Two Three";
524 let mut out = String::from(formatted_text);
525 auto.apply(&mut out, raw_input_text);
527 "One\r\nTwo\r\nThree", &out,
528 "auto-native-windows should detect 'crlf'"