diff options
Diffstat (limited to 'src/tools/rustfmt/src/config/options.rs')
-rw-r--r-- | src/tools/rustfmt/src/config/options.rs | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs new file mode 100644 index 000000000..257a17b27 --- /dev/null +++ b/src/tools/rustfmt/src/config/options.rs @@ -0,0 +1,464 @@ +use std::collections::{hash_set, HashSet}; +use std::fmt; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use itertools::Itertools; +use rustfmt_config_proc_macro::config_type; +use serde::de::{SeqAccess, Visitor}; +use serde::ser::SerializeSeq; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::config::lists::*; +use crate::config::Config; + +#[config_type] +pub enum NewlineStyle { + /// Auto-detect based on the raw source input. + Auto, + /// Force CRLF (`\r\n`). + Windows, + /// Force CR (`\n). + Unix, + /// `\r\n` in Windows, `\n` on other platforms. + Native, +} + +#[config_type] +/// Where to put the opening brace of items (`fn`, `impl`, etc.). +pub enum BraceStyle { + /// Put the opening brace on the next line. + AlwaysNextLine, + /// Put the opening brace on the same line, if possible. + PreferSameLine, + /// Prefer the same line except where there is a where-clause, in which + /// case force the brace to be put on the next line. + SameLineWhere, +} + +#[config_type] +/// Where to put the opening brace of conditional expressions (`if`, `match`, etc.). +pub enum ControlBraceStyle { + /// K&R style, Rust community default + AlwaysSameLine, + /// Stroustrup style + ClosingNextLine, + /// Allman style + AlwaysNextLine, +} + +#[config_type] +/// How to indent. +pub enum IndentStyle { + /// First line on the same line as the opening brace, all lines aligned with + /// the first line. + Visual, + /// First line is on a new line and all lines align with **block** indent. + Block, +} + +#[config_type] +/// How to place a list-like items. +/// FIXME: Issue-3581: this should be renamed to ItemsLayout when publishing 2.0 +pub enum Density { + /// Fit as much on one line as possible. + Compressed, + /// Items are placed horizontally if sufficient space, vertically otherwise. + Tall, + /// Place every item on a separate line. + Vertical, +} + +#[config_type] +/// Spacing around type combinators. +pub enum TypeDensity { + /// No spaces around "=" and "+" + Compressed, + /// Spaces around " = " and " + " + Wide, +} + +#[config_type] +/// Heuristic settings that can be used to simply +/// the configuration of the granular width configurations +/// like `struct_lit_width`, `array_width`, etc. +pub enum Heuristics { + /// Turn off any heuristics + Off, + /// Turn on max heuristics + Max, + /// Use scaled values based on the value of `max_width` + Default, +} + +impl Density { + pub fn to_list_tactic(self, len: usize) -> ListTactic { + match self { + Density::Compressed => ListTactic::Mixed, + Density::Tall => ListTactic::HorizontalVertical, + Density::Vertical if len == 1 => ListTactic::Horizontal, + Density::Vertical => ListTactic::Vertical, + } + } +} + +#[config_type] +/// Configuration for import groups, i.e. sets of imports separated by newlines. +pub enum GroupImportsTactic { + /// Keep groups as they are. + Preserve, + /// Discard existing groups, and create new groups for + /// 1. `std` / `core` / `alloc` imports + /// 2. other imports + /// 3. `self` / `crate` / `super` imports + StdExternalCrate, + /// Discard existing groups, and create a single group for everything + One, +} + +#[config_type] +/// How to merge imports. +pub enum ImportGranularity { + /// Do not merge imports. + Preserve, + /// Use one `use` statement per crate. + Crate, + /// Use one `use` statement per module. + Module, + /// Use one `use` statement per imported item. + Item, + /// Use one `use` statement including all items. + One, +} + +/// Controls how rustfmt should handle case in hexadecimal literals. +#[config_type] +pub enum HexLiteralCase { + /// Leave the literal as-is + Preserve, + /// Ensure all literals use uppercase lettering + Upper, + /// Ensure all literals use lowercase lettering + Lower, +} + +#[config_type] +pub enum ReportTactic { + Always, + Unnumbered, + Never, +} + +/// What Rustfmt should emit. Mostly corresponds to the `--emit` command line +/// option. +#[config_type] +pub enum EmitMode { + /// Emits to files. + Files, + /// Writes the output to stdout. + Stdout, + /// Displays how much of the input file was processed + Coverage, + /// Unfancy stdout + Checkstyle, + /// Writes the resulting diffs in a JSON format. Returns an empty array + /// `[]` if there were no diffs. + Json, + /// Output the changed lines (for internal value only) + ModifiedLines, + /// Checks if a diff can be generated. If so, rustfmt outputs a diff and + /// quits with exit code 1. + /// This option is designed to be run in CI where a non-zero exit signifies + /// non-standard code formatting. Used for `--check`. + Diff, +} + +/// Client-preference for coloured output. +#[config_type] +pub enum Color { + /// Always use color, whether it is a piped or terminal output + Always, + /// Never use color + Never, + /// Automatically use color, if supported by terminal + Auto, +} + +#[config_type] +/// rustfmt format style version. +pub enum Version { + /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0. + One, + /// 2.x.y. When specified, rustfmt will format in the the latest style. + Two, +} + +impl Color { + /// Whether we should use a coloured terminal. + pub fn use_colored_tty(self) -> bool { + match self { + Color::Always | Color::Auto => true, + Color::Never => false, + } + } +} + +/// How chatty should Rustfmt be? +#[config_type] +pub enum Verbosity { + /// Emit more. + Verbose, + /// Default. + Normal, + /// Emit as little as possible. + Quiet, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +pub struct WidthHeuristics { + // Maximum width of the args of a function call before falling back + // to vertical formatting. + pub(crate) fn_call_width: usize, + // Maximum width of the args of a function-like attributes before falling + // back to vertical formatting. + pub(crate) attr_fn_like_width: usize, + // Maximum width in the body of a struct lit before falling back to + // vertical formatting. + pub(crate) struct_lit_width: usize, + // Maximum width in the body of a struct variant before falling back + // to vertical formatting. + pub(crate) struct_variant_width: usize, + // Maximum width of an array literal before falling back to vertical + // formatting. + pub(crate) array_width: usize, + // Maximum length of a chain to fit on a single line. + pub(crate) chain_width: usize, + // Maximum line length for single line if-else expressions. A value + // of zero means always break if-else expressions. + pub(crate) single_line_if_else_max_width: usize, +} + +impl fmt::Display for WidthHeuristics { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl WidthHeuristics { + // Using this WidthHeuristics means we ignore heuristics. + pub fn null() -> WidthHeuristics { + WidthHeuristics { + fn_call_width: usize::max_value(), + attr_fn_like_width: usize::max_value(), + struct_lit_width: 0, + struct_variant_width: 0, + array_width: usize::max_value(), + chain_width: usize::max_value(), + single_line_if_else_max_width: 0, + } + } + + pub fn set(max_width: usize) -> WidthHeuristics { + WidthHeuristics { + fn_call_width: max_width, + attr_fn_like_width: max_width, + struct_lit_width: max_width, + struct_variant_width: max_width, + array_width: max_width, + chain_width: max_width, + single_line_if_else_max_width: max_width, + } + } + + // scale the default WidthHeuristics according to max_width + pub fn scaled(max_width: usize) -> WidthHeuristics { + const DEFAULT_MAX_WIDTH: usize = 100; + let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH { + let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32; + // round to the closest 0.1 + (ratio * 10.0).round() / 10.0 + } else { + 1.0 + }; + WidthHeuristics { + fn_call_width: (60.0 * max_width_ratio).round() as usize, + attr_fn_like_width: (70.0 * max_width_ratio).round() as usize, + struct_lit_width: (18.0 * max_width_ratio).round() as usize, + struct_variant_width: (35.0 * max_width_ratio).round() as usize, + array_width: (60.0 * max_width_ratio).round() as usize, + chain_width: (60.0 * max_width_ratio).round() as usize, + single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize, + } + } +} + +impl ::std::str::FromStr for WidthHeuristics { + type Err = &'static str; + + fn from_str(_: &str) -> Result<Self, Self::Err> { + Err("WidthHeuristics is not parsable") + } +} + +impl Default for EmitMode { + fn default() -> EmitMode { + EmitMode::Files + } +} + +/// A set of directories, files and modules that rustfmt should ignore. +#[derive(Default, Clone, Debug, PartialEq)] +pub struct IgnoreList { + /// A set of path specified in rustfmt.toml. + path_set: HashSet<PathBuf>, + /// A path to rustfmt.toml. + rustfmt_toml_path: PathBuf, +} + +impl fmt::Display for IgnoreList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "[{}]", + self.path_set + .iter() + .format_with(", ", |path, f| f(&format_args!( + "{}", + path.to_string_lossy() + ))) + ) + } +} + +impl Serialize for IgnoreList { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?; + for e in &self.path_set { + seq.serialize_element(e)?; + } + seq.end() + } +} + +impl<'de> Deserialize<'de> for IgnoreList { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + struct HashSetVisitor; + impl<'v> Visitor<'v> for HashSetVisitor { + type Value = HashSet<PathBuf>; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a sequence of path") + } + + fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> + where + A: SeqAccess<'v>, + { + let mut path_set = HashSet::new(); + while let Some(elem) = seq.next_element()? { + path_set.insert(elem); + } + Ok(path_set) + } + } + Ok(IgnoreList { + path_set: deserializer.deserialize_seq(HashSetVisitor)?, + rustfmt_toml_path: PathBuf::new(), + }) + } +} + +impl<'a> IntoIterator for &'a IgnoreList { + type Item = &'a PathBuf; + type IntoIter = hash_set::Iter<'a, PathBuf>; + + fn into_iter(self) -> Self::IntoIter { + self.path_set.iter() + } +} + +impl IgnoreList { + pub fn add_prefix(&mut self, dir: &Path) { + self.rustfmt_toml_path = dir.to_path_buf(); + } + + pub fn rustfmt_toml_path(&self) -> &Path { + &self.rustfmt_toml_path + } +} + +impl FromStr for IgnoreList { + type Err = &'static str; + + fn from_str(_: &str) -> Result<Self, Self::Err> { + Err("IgnoreList is not parsable") + } +} + +/// Maps client-supplied options to Rustfmt's internals, mostly overriding +/// values in a config with values from the command line. +pub trait CliOptions { + fn apply_to(self, config: &mut Config); + fn config_path(&self) -> Option<&Path>; +} + +/// The edition of the syntax and semntics of code (RFC 2052). +#[config_type] +pub enum Edition { + #[value = "2015"] + #[doc_hint = "2015"] + /// Edition 2015. + Edition2015, + #[value = "2018"] + #[doc_hint = "2018"] + /// Edition 2018. + Edition2018, + #[value = "2021"] + #[doc_hint = "2021"] + /// Edition 2021. + Edition2021, + #[value = "2024"] + #[doc_hint = "2024"] + /// Edition 2024. + Edition2024, +} + +impl Default for Edition { + fn default() -> Edition { + Edition::Edition2015 + } +} + +impl From<Edition> for rustc_span::edition::Edition { + fn from(edition: Edition) -> Self { + match edition { + Edition::Edition2015 => Self::Edition2015, + Edition::Edition2018 => Self::Edition2018, + Edition::Edition2021 => Self::Edition2021, + Edition::Edition2024 => Self::Edition2024, + } + } +} + +impl PartialOrd for Edition { + fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> { + rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) + } +} + +/// Controls how rustfmt should handle leading pipes on match arms. +#[config_type] +pub enum MatchArmLeadingPipe { + /// Place leading pipes on all match arms + Always, + /// Never emit leading pipes on match arms + Never, + /// Preserve any existing leading pipes + Preserve, +} |