From e6158706590fd31ac6388ddc17495480c7fd0265 Mon Sep 17 00:00:00 2001 From: konsti Date: Mon, 28 Aug 2023 08:44:56 +0200 Subject: [PATCH] Unify line size settings between ruff and the formatter (#6873) --- crates/flake8_to_ruff/src/black.rs | 3 +- crates/flake8_to_ruff/src/converter.rs | 13 +- crates/ruff/src/checkers/physical_lines.rs | 10 +- crates/ruff/src/line_width.rs | 129 ++++++++++++++---- crates/ruff/src/message/text.rs | 4 +- .../src/rules/flake8_simplify/rules/ast_if.rs | 8 +- .../rules/flake8_simplify/rules/ast_with.rs | 4 +- .../rules/reimplemented_builtin.rs | 6 +- crates/ruff/src/rules/isort/format.rs | 8 +- crates/ruff/src/rules/isort/mod.rs | 6 +- .../src/rules/isort/rules/organize_imports.rs | 4 +- crates/ruff/src/rules/pycodestyle/helpers.rs | 10 +- crates/ruff/src/rules/pycodestyle/mod.rs | 7 +- .../pycodestyle/rules/doc_line_too_long.rs | 2 +- .../rules/pycodestyle/rules/line_too_long.rs | 7 +- .../src/rules/pyupgrade/rules/f_strings.rs | 3 +- crates/ruff_cli/src/args.rs | 8 +- crates/ruff_cli/src/commands/format.rs | 9 +- crates/ruff_dev/src/format_dev.rs | 9 +- crates/ruff_formatter/src/builders.rs | 1 - .../src/format_element/document.rs | 9 +- crates/ruff_formatter/src/lib.rs | 86 +++--------- crates/ruff_formatter/src/printer/mod.rs | 13 +- .../src/printer/printer_options/mod.rs | 20 +-- crates/ruff_python_formatter/src/options.rs | 2 +- crates/ruff_text_size/Cargo.toml | 6 +- crates/ruff_workspace/src/pyproject.rs | 4 +- ruff.schema.json | 6 +- 28 files changed, 217 insertions(+), 180 deletions(-) diff --git a/crates/flake8_to_ruff/src/black.rs b/crates/flake8_to_ruff/src/black.rs index 23296d4369384..e9c6df6fb6db1 100644 --- a/crates/flake8_to_ruff/src/black.rs +++ b/crates/flake8_to_ruff/src/black.rs @@ -1,12 +1,13 @@ //! Extract Black configuration settings from a pyproject.toml. +use ruff::line_width::LineLength; use ruff::settings::types::PythonVersion; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub(crate) struct Black { #[serde(alias = "line-length", alias = "line_length")] - pub(crate) line_length: Option, + pub(crate) line_length: Option, #[serde(alias = "target-version", alias = "target_version")] pub(crate) target_version: Option>, } diff --git a/crates/flake8_to_ruff/src/converter.rs b/crates/flake8_to_ruff/src/converter.rs index e764b5faeb275..5903cfed8006a 100644 --- a/crates/flake8_to_ruff/src/converter.rs +++ b/crates/flake8_to_ruff/src/converter.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::str::FromStr; use itertools::Itertools; @@ -119,10 +120,8 @@ pub(crate) fn convert( "builtins" => { options.builtins = Some(parser::parse_strings(value.as_ref())); } - "max-line-length" | "max_line_length" => match value.parse::() { - Ok(line_length) => { - options.line_length = Some(LineLength::from(line_length)); - } + "max-line-length" | "max_line_length" => match LineLength::from_str(value) { + Ok(line_length) => options.line_length = Some(line_length), Err(e) => { warn_user!("Unable to parse '{key}' property: {e}"); } @@ -405,7 +404,7 @@ pub(crate) fn convert( // Extract any settings from the existing `pyproject.toml`. if let Some(black) = &external_config.black { if let Some(line_length) = &black.line_length { - options.line_length = Some(LineLength::from(*line_length)); + options.line_length = Some(*line_length); } if let Some(target_version) = &black.target_version { @@ -512,7 +511,7 @@ mod tests { Some(vec![]), ); let expected = Pyproject::new(Options { - line_length: Some(LineLength::from(100)), + line_length: Some(LineLength::try_from(100).unwrap()), ..default_options([]) }); assert_eq!(actual, expected); @@ -529,7 +528,7 @@ mod tests { Some(vec![]), ); let expected = Pyproject::new(Options { - line_length: Some(LineLength::from(100)), + line_length: Some(LineLength::try_from(100).unwrap()), ..default_options([]) }); assert_eq!(actual, expected); diff --git a/crates/ruff/src/checkers/physical_lines.rs b/crates/ruff/src/checkers/physical_lines.rs index cf4479a411325..b8739fef735b8 100644 --- a/crates/ruff/src/checkers/physical_lines.rs +++ b/crates/ruff/src/checkers/physical_lines.rs @@ -1,10 +1,9 @@ //! Lint rules based on checking physical lines. -use ruff_text_size::TextSize; - use ruff_diagnostics::Diagnostic; use ruff_python_codegen::Stylist; use ruff_python_index::Indexer; use ruff_source_file::{Locator, UniversalNewlines}; +use ruff_text_size::TextSize; use crate::registry::Rule; use crate::rules::flake8_copyright::rules::missing_copyright_notice; @@ -99,11 +98,10 @@ pub(crate) fn check_physical_lines( #[cfg(test)] mod tests { - use ruff_python_parser::lexer::lex; - use ruff_python_parser::Mode; - use ruff_python_codegen::Stylist; use ruff_python_index::Indexer; + use ruff_python_parser::lexer::lex; + use ruff_python_parser::Mode; use ruff_source_file::Locator; use crate::line_width::LineLength; @@ -132,7 +130,7 @@ mod tests { }, ) }; - let line_length = LineLength::from(8); + let line_length = LineLength::try_from(8).unwrap(); assert_eq!(check_with_max_line_length(line_length), vec![]); assert_eq!(check_with_max_line_length(line_length), vec![]); } diff --git a/crates/ruff/src/line_width.rs b/crates/ruff/src/line_width.rs index ee8e4f6bc9561..5b869751fb039 100644 --- a/crates/ruff/src/line_width.rs +++ b/crates/ruff/src/line_width.rs @@ -1,30 +1,111 @@ +use ruff_cache::{CacheKey, CacheKeyHasher}; use serde::{Deserialize, Serialize}; -use std::num::NonZeroU8; +use std::error::Error; +use std::hash::Hasher; +use std::num::{NonZeroU16, NonZeroU8, ParseIntError}; +use std::str::FromStr; use unicode_width::UnicodeWidthChar; use ruff_macros::CacheKey; /// The length of a line of text that is considered too long. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, CacheKey)] +/// +/// The allowed range of values is 1..=320 +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct LineLength(usize); +pub struct LineLength(NonZeroU16); + +impl LineLength { + /// Maximum allowed value for a valid [`LineLength`] + pub const MAX: u16 = 320; + + /// Return the numeric value for this [`LineLength`] + pub fn value(&self) -> u16 { + self.0.get() + } +} impl Default for LineLength { - /// The default line length. fn default() -> Self { - Self(88) + Self(NonZeroU16::new(88).unwrap()) } } -impl LineLength { - pub const fn get(&self) -> usize { - self.0 +impl CacheKey for LineLength { + fn cache_key(&self, state: &mut CacheKeyHasher) { + state.write_u16(self.0.get()); + } +} + +/// Error type returned when parsing a [`LineLength`] from a string fails +pub enum ParseLineWidthError { + /// The string could not be parsed as a valid [u16] + ParseError(ParseIntError), + /// The [u16] value of the string is not a valid [LineLength] + TryFromIntError(LineLengthFromIntError), +} + +impl std::fmt::Debug for ParseLineWidthError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +impl std::fmt::Display for ParseLineWidthError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParseLineWidthError::ParseError(err) => std::fmt::Display::fmt(err, fmt), + ParseLineWidthError::TryFromIntError(err) => std::fmt::Display::fmt(err, fmt), + } + } +} + +impl Error for ParseLineWidthError {} + +impl FromStr for LineLength { + type Err = ParseLineWidthError; + + fn from_str(s: &str) -> Result { + let value = u16::from_str(s).map_err(ParseLineWidthError::ParseError)?; + let value = Self::try_from(value).map_err(ParseLineWidthError::TryFromIntError)?; + Ok(value) + } +} + +/// Error type returned when converting a u16 to a [`LineLength`] fails +#[derive(Clone, Copy, Debug)] +pub struct LineLengthFromIntError(pub u16); + +impl TryFrom for LineLength { + type Error = LineLengthFromIntError; + + fn try_from(value: u16) -> Result { + match NonZeroU16::try_from(value) { + Ok(value) if value.get() <= Self::MAX => Ok(LineLength(value)), + Ok(_) | Err(_) => Err(LineLengthFromIntError(value)), + } + } +} + +impl std::fmt::Display for LineLengthFromIntError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + "The line width must be a value between 1 and {}.", + LineLength::MAX + ) + } +} + +impl From for u16 { + fn from(value: LineLength) -> Self { + value.0.get() } } -impl From for LineLength { - fn from(value: usize) -> Self { - Self(value) +impl From for NonZeroU16 { + fn from(value: LineLength) -> Self { + value.0 } } @@ -33,7 +114,7 @@ impl From for LineLength { /// This is used to determine if a line is too long. /// It should be compared to a [`LineLength`]. #[derive(Clone, Copy, Debug)] -pub struct LineWidth { +pub struct LineWidthBuilder { /// The width of the line. width: usize, /// The column of the line. @@ -43,40 +124,40 @@ pub struct LineWidth { tab_size: TabSize, } -impl Default for LineWidth { +impl Default for LineWidthBuilder { fn default() -> Self { Self::new(TabSize::default()) } } -impl PartialEq for LineWidth { +impl PartialEq for LineWidthBuilder { fn eq(&self, other: &Self) -> bool { self.width == other.width } } -impl Eq for LineWidth {} +impl Eq for LineWidthBuilder {} -impl PartialOrd for LineWidth { +impl PartialOrd for LineWidthBuilder { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for LineWidth { +impl Ord for LineWidthBuilder { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.width.cmp(&other.width) } } -impl LineWidth { +impl LineWidthBuilder { pub fn get(&self) -> usize { self.width } /// Creates a new `LineWidth` with the given tab size. pub fn new(tab_size: TabSize) -> Self { - LineWidth { + LineWidthBuilder { width: 0, column: 0, tab_size, @@ -119,7 +200,7 @@ impl LineWidth { /// Adds the given width to the line width. /// Also adds the given width to the column. - /// It is generally better to use [`LineWidth::add_str`] or [`LineWidth::add_char`]. + /// It is generally better to use [`LineWidthBuilder::add_str`] or [`LineWidthBuilder::add_char`]. /// The width and column should be the same for the corresponding text. /// Currently, this is only used to add spaces. #[must_use] @@ -130,15 +211,15 @@ impl LineWidth { } } -impl PartialEq for LineWidth { +impl PartialEq for LineWidthBuilder { fn eq(&self, other: &LineLength) -> bool { - self.width == other.0 + self.width == (other.value() as usize) } } -impl PartialOrd for LineWidth { +impl PartialOrd for LineWidthBuilder { fn partial_cmp(&self, other: &LineLength) -> Option { - self.width.partial_cmp(&other.0) + self.width.partial_cmp(&(other.value() as usize)) } } diff --git a/crates/ruff/src/message/text.rs b/crates/ruff/src/message/text.rs index d712a5ed4a433..1daec724d3f59 100644 --- a/crates/ruff/src/message/text.rs +++ b/crates/ruff/src/message/text.rs @@ -12,7 +12,7 @@ use ruff_text_size::{Ranged, TextRange, TextSize}; use crate::fs::relativize_path; use crate::jupyter::{Notebook, NotebookIndex}; -use crate::line_width::{LineWidth, TabSize}; +use crate::line_width::{LineWidthBuilder, TabSize}; use crate::message::diff::Diff; use crate::message::{Emitter, EmitterContext, Message}; use crate::registry::AsRule; @@ -292,7 +292,7 @@ fn replace_whitespace(source: &str, annotation_range: TextRange) -> SourceCode { let mut result = String::new(); let mut last_end = 0; let mut range = annotation_range; - let mut line_width = LineWidth::new(TabSize::default()); + let mut line_width = LineWidthBuilder::new(TabSize::default()); for (index, c) in source.char_indices() { let old_width = line_width.get(); diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs index d7b8ee3d2460d..3ceee59438b68 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs @@ -15,7 +15,7 @@ use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; use ruff_source_file::{Locator, UniversalNewlines}; use crate::checkers::ast::Checker; -use crate::line_width::LineWidth; +use crate::line_width::LineWidthBuilder; use crate::registry::AsRule; use crate::rules::flake8_simplify::rules::fix_if; @@ -414,7 +414,7 @@ pub(crate) fn nested_if_statements( .unwrap_or_default() .universal_newlines() .all(|line| { - LineWidth::new(checker.settings.tab_size).add_str(&line) + LineWidthBuilder::new(checker.settings.tab_size).add_str(&line) <= checker.settings.line_length }) { @@ -660,7 +660,7 @@ pub(crate) fn use_ternary_operator(checker: &mut Checker, stmt: &Stmt) { // Don't flag if the resulting expression would exceed the maximum line length. let line_start = checker.locator().line_start(stmt.start()); - if LineWidth::new(checker.settings.tab_size) + if LineWidthBuilder::new(checker.settings.tab_size) .add_str(&checker.locator().contents()[TextRange::new(line_start, stmt.start())]) .add_str(&contents) > checker.settings.line_length @@ -964,7 +964,7 @@ pub(crate) fn use_dict_get_with_default(checker: &mut Checker, stmt_if: &ast::St // Don't flag if the resulting expression would exceed the maximum line length. let line_start = checker.locator().line_start(stmt_if.start()); - if LineWidth::new(checker.settings.tab_size) + if LineWidthBuilder::new(checker.settings.tab_size) .add_str(&checker.locator().contents()[TextRange::new(line_start, stmt_if.start())]) .add_str(&contents) > checker.settings.line_length diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_with.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_with.rs index 9fb96c0716c0f..da34dc0841bcd 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_with.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_with.rs @@ -9,7 +9,7 @@ use ruff_source_file::UniversalNewlines; use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; -use crate::line_width::LineWidth; +use crate::line_width::LineWidthBuilder; use crate::registry::AsRule; use super::fix_with; @@ -142,7 +142,7 @@ pub(crate) fn multiple_with_statements( .unwrap_or_default() .universal_newlines() .all(|line| { - LineWidth::new(checker.settings.tab_size).add_str(&line) + LineWidthBuilder::new(checker.settings.tab_size).add_str(&line) <= checker.settings.line_length }) { diff --git a/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs b/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs index 52f7465d731a6..7529ba9ee4854 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs @@ -10,7 +10,7 @@ use ruff_python_ast::traversal; use ruff_python_codegen::Generator; use crate::checkers::ast::Checker; -use crate::line_width::LineWidth; +use crate::line_width::LineWidthBuilder; use crate::registry::AsRule; /// ## What it does @@ -98,7 +98,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) { // Don't flag if the resulting expression would exceed the maximum line length. let line_start = checker.locator().line_start(stmt.start()); - if LineWidth::new(checker.settings.tab_size) + if LineWidthBuilder::new(checker.settings.tab_size) .add_str(&checker.locator().contents()[TextRange::new(line_start, stmt.start())]) .add_str(&contents) > checker.settings.line_length @@ -180,7 +180,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) { // Don't flag if the resulting expression would exceed the maximum line length. let line_start = checker.locator().line_start(stmt.start()); - if LineWidth::new(checker.settings.tab_size) + if LineWidthBuilder::new(checker.settings.tab_size) .add_str(&checker.locator().contents()[TextRange::new(line_start, stmt.start())]) .add_str(&contents) > checker.settings.line_length diff --git a/crates/ruff/src/rules/isort/format.rs b/crates/ruff/src/rules/isort/format.rs index cfabdb5364b90..1226bbc10317e 100644 --- a/crates/ruff/src/rules/isort/format.rs +++ b/crates/ruff/src/rules/isort/format.rs @@ -1,6 +1,6 @@ use ruff_python_codegen::Stylist; -use crate::line_width::{LineLength, LineWidth}; +use crate::line_width::{LineLength, LineWidthBuilder}; use super::types::{AliasData, CommentSet, ImportFromData, Importable}; @@ -46,7 +46,7 @@ pub(crate) fn format_import_from( comments: &CommentSet, aliases: &[(AliasData, CommentSet)], line_length: LineLength, - indentation_width: LineWidth, + indentation_width: LineWidthBuilder, stylist: &Stylist, force_wrap_aliases: bool, is_first: bool, @@ -103,8 +103,8 @@ fn format_single_line( aliases: &[(AliasData, CommentSet)], is_first: bool, stylist: &Stylist, - indentation_width: LineWidth, -) -> (String, LineWidth) { + indentation_width: LineWidthBuilder, +) -> (String, LineWidthBuilder) { let mut output = String::with_capacity(CAPACITY); let mut line_width = indentation_width; diff --git a/crates/ruff/src/rules/isort/mod.rs b/crates/ruff/src/rules/isort/mod.rs index 027c164b7e50c..184700668966f 100644 --- a/crates/ruff/src/rules/isort/mod.rs +++ b/crates/ruff/src/rules/isort/mod.rs @@ -19,7 +19,7 @@ use sorting::cmp_either_import; use types::EitherImport::{Import, ImportFrom}; use types::{AliasData, EitherImport, TrailingComma}; -use crate::line_width::{LineLength, LineWidth}; +use crate::line_width::{LineLength, LineWidthBuilder}; use crate::rules::isort::categorize::KnownModules; use crate::rules::isort::types::ImportBlock; use crate::settings::types::PythonVersion; @@ -69,7 +69,7 @@ pub(crate) fn format_imports( comments: Vec, locator: &Locator, line_length: LineLength, - indentation_width: LineWidth, + indentation_width: LineWidthBuilder, stylist: &Stylist, src: &[PathBuf], package: Option<&Path>, @@ -179,7 +179,7 @@ pub(crate) fn format_imports( fn format_import_block( block: ImportBlock, line_length: LineLength, - indentation_width: LineWidth, + indentation_width: LineWidthBuilder, stylist: &Stylist, src: &[PathBuf], package: Option<&Path>, diff --git a/crates/ruff/src/rules/isort/rules/organize_imports.rs b/crates/ruff/src/rules/isort/rules/organize_imports.rs index 53dfcb86846eb..f3f0ced20c260 100644 --- a/crates/ruff/src/rules/isort/rules/organize_imports.rs +++ b/crates/ruff/src/rules/isort/rules/organize_imports.rs @@ -12,7 +12,7 @@ use ruff_python_trivia::{leading_indentation, textwrap::indent, PythonWhitespace use ruff_source_file::{Locator, UniversalNewlines}; use ruff_text_size::{Ranged, TextRange}; -use crate::line_width::LineWidth; +use crate::line_width::LineWidthBuilder; use crate::registry::AsRule; use crate::settings::Settings; @@ -121,7 +121,7 @@ pub(crate) fn organize_imports( comments, locator, settings.line_length, - LineWidth::new(settings.tab_size).add_str(indentation), + LineWidthBuilder::new(settings.tab_size).add_str(indentation), stylist, &settings.src, package, diff --git a/crates/ruff/src/rules/pycodestyle/helpers.rs b/crates/ruff/src/rules/pycodestyle/helpers.rs index bead20f8b5a5a..ce8bd311a3290 100644 --- a/crates/ruff/src/rules/pycodestyle/helpers.rs +++ b/crates/ruff/src/rules/pycodestyle/helpers.rs @@ -6,7 +6,7 @@ use ruff_python_ast::{CmpOp, Expr}; use ruff_source_file::{Line, Locator}; use ruff_text_size::{Ranged, TextLen, TextRange}; -use crate::line_width::{LineLength, LineWidth, TabSize}; +use crate::line_width::{LineLength, LineWidthBuilder, TabSize}; pub(super) fn is_ambiguous_name(name: &str) -> bool { name == "l" || name == "I" || name == "O" @@ -66,11 +66,11 @@ pub(super) fn is_overlong( // worst-case scenario is that the line is all tabs). If the maximum width is less than the // limit, then the line is not overlong. let max_width = line.len() * tab_size.as_usize(); - if max_width < limit.get() { + if max_width < limit.value() as usize { return None; } - let mut width = LineWidth::new(tab_size); + let mut width = LineWidthBuilder::new(tab_size); width = width.add_str(line.as_str()); if width <= limit { return None; @@ -95,14 +95,14 @@ pub(super) fn is_overlong( // begins before the limit. let last_chunk = chunks.last().unwrap_or(second_chunk); if last_chunk.contains("://") { - if width.get() - last_chunk.width() <= limit.get() { + if width.get() - last_chunk.width() <= limit.value() as usize { return None; } } // Obtain the start offset of the part of the line that exceeds the limit let mut start_offset = line.start(); - let mut start_width = LineWidth::new(tab_size); + let mut start_width = LineWidthBuilder::new(tab_size); for c in line.chars() { if start_width < limit { start_offset += c.text_len(); diff --git a/crates/ruff/src/rules/pycodestyle/mod.rs b/crates/ruff/src/rules/pycodestyle/mod.rs index 95495cbae70a9..c6387432637e8 100644 --- a/crates/ruff/src/rules/pycodestyle/mod.rs +++ b/crates/ruff/src/rules/pycodestyle/mod.rs @@ -10,6 +10,7 @@ mod tests { use std::path::Path; use anyhow::Result; + use test_case::test_case; use crate::line_width::LineLength; @@ -170,7 +171,7 @@ mod tests { Path::new("pycodestyle/W505.py"), &settings::Settings { pycodestyle: Settings { - max_doc_length: Some(LineLength::from(50)), + max_doc_length: Some(LineLength::try_from(50).unwrap()), ..Settings::default() }, ..settings::Settings::for_rule(Rule::DocLineTooLong) @@ -186,7 +187,7 @@ mod tests { Path::new("pycodestyle/W505_utf_8.py"), &settings::Settings { pycodestyle: Settings { - max_doc_length: Some(LineLength::from(50)), + max_doc_length: Some(LineLength::try_from(50).unwrap()), ..Settings::default() }, ..settings::Settings::for_rule(Rule::DocLineTooLong) @@ -206,7 +207,7 @@ mod tests { Path::new("pycodestyle/E501_2.py"), &settings::Settings { tab_size: NonZeroU8::new(tab_size).unwrap().into(), - line_length: LineLength::from(6), + line_length: LineLength::try_from(6).unwrap(), ..settings::Settings::for_rule(Rule::LineTooLong) }, )?; diff --git a/crates/ruff/src/rules/pycodestyle/rules/doc_line_too_long.rs b/crates/ruff/src/rules/pycodestyle/rules/doc_line_too_long.rs index caaaa2032c0ab..0faa662370d35 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/doc_line_too_long.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/doc_line_too_long.rs @@ -78,7 +78,7 @@ pub(crate) fn doc_line_too_long(line: &Line, settings: &Settings) -> Option Option offset = 0 // ``` let offset = if idx == 0 { col_offset.to_usize() } else { 0 }; - offset + line.chars().count() > line_length.get() + offset + line.chars().count() > line_length.value() as usize }) { return; } diff --git a/crates/ruff_cli/src/args.rs b/crates/ruff_cli/src/args.rs index 71f4807ba3feb..6de964031e158 100644 --- a/crates/ruff_cli/src/args.rs +++ b/crates/ruff_cli/src/args.rs @@ -3,9 +3,9 @@ use std::str::FromStr; use clap::{command, Parser}; use regex::Regex; +use ruff::line_width::LineLength; use rustc_hash::FxHashMap; -use ruff::line_width::LineLength; use ruff::logging::LogLevel; use ruff::registry::Rule; use ruff::settings::types::{ @@ -256,7 +256,7 @@ pub struct CheckArgs { /// Set the line-length for length-associated rules and automatic /// formatting. #[arg(long, help_heading = "Rule configuration", hide = true)] - pub line_length: Option, + pub line_length: Option, /// Regular expression matching the name of dummy variables. #[arg(long, help_heading = "Rule configuration", hide = true)] pub dummy_variable_rgx: Option, @@ -497,7 +497,7 @@ pub struct Overrides { pub extend_unfixable: Option>, pub fixable: Option>, pub ignore: Option>, - pub line_length: Option, + pub line_length: Option, pub per_file_ignores: Option>, pub respect_gitignore: Option, pub select: Option>, @@ -560,7 +560,7 @@ impl ConfigProcessor for Overrides { config.force_exclude = Some(*force_exclude); } if let Some(line_length) = &self.line_length { - config.line_length = Some(LineLength::from(*line_length)); + config.line_length = Some(*line_length); } if let Some(per_file_ignores) = &self.per_file_ignores { config.per_file_ignores = Some(collect_per_file_ignores(per_file_ignores.clone())); diff --git a/crates/ruff_cli/src/commands/format.rs b/crates/ruff_cli/src/commands/format.rs index a11803d43eee3..2440408e21dbd 100644 --- a/crates/ruff_cli/src/commands/format.rs +++ b/crates/ruff_cli/src/commands/format.rs @@ -1,4 +1,5 @@ use std::io; +use std::num::NonZeroU16; use std::path::{Path, PathBuf}; use anyhow::Result; @@ -49,12 +50,8 @@ pub(crate) fn format(cli: &Arguments, overrides: &Overrides) -> Result Self { Self { - line_length: 88, + line_length: NonZeroU16::new(88).unwrap(), skip_magic_trailing_comma: false, force_exclude: None, } @@ -893,9 +894,7 @@ impl BlackOptions { fn to_py_format_options(&self, file: &Path) -> PyFormatOptions { PyFormatOptions::from_extension(file) - .with_line_width( - LineWidth::try_from(self.line_length).expect("Invalid line length limit"), - ) + .with_line_width(LineWidth::from(self.line_length)) .with_magic_trailing_comma(if self.skip_magic_trailing_comma { MagicTrailingComma::Ignore } else { diff --git a/crates/ruff_formatter/src/builders.rs b/crates/ruff_formatter/src/builders.rs index a0060452f661c..cf65ece8fe43f 100644 --- a/crates/ruff_formatter/src/builders.rs +++ b/crates/ruff_formatter/src/builders.rs @@ -1703,7 +1703,6 @@ impl Format for ExpandParent { /// ``` /// use ruff_formatter::{format_args, format, LineWidth, SimpleFormatOptions}; /// use ruff_formatter::prelude::*; -/// use ruff_formatter::printer::PrintWidth; /// /// fn main() -> FormatResult<()> { /// let context = SimpleFormatContext::new(SimpleFormatOptions { diff --git a/crates/ruff_formatter/src/format_element/document.rs b/crates/ruff_formatter/src/format_element/document.rs index 1d682218746da..46ca5a7104944 100644 --- a/crates/ruff_formatter/src/format_element/document.rs +++ b/crates/ruff_formatter/src/format_element/document.rs @@ -7,10 +7,9 @@ use crate::format_element::tag::{Condition, DedentMode}; use crate::prelude::tag::GroupMode; use crate::prelude::*; use crate::source_code::SourceCode; -use crate::{format, write, TabWidth}; use crate::{ - BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter, - IndentStyle, LineWidth, PrinterOptions, + format, write, BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, + FormatResult, Formatter, IndentStyle, LineWidth, PrinterOptions, TabWidth, }; use super::tag::Tag; @@ -222,12 +221,12 @@ impl FormatOptions for IrFormatOptions { } fn line_width(&self) -> LineWidth { - LineWidth(80) + LineWidth::try_from(80).unwrap() } fn as_print_options(&self) -> PrinterOptions { PrinterOptions { - print_width: self.line_width().into(), + line_width: self.line_width(), indent_style: IndentStyle::Space(2), ..PrinterOptions::default() } diff --git a/crates/ruff_formatter/src/lib.rs b/crates/ruff_formatter/src/lib.rs index 18bc1346100e2..41a3b43f1e775 100644 --- a/crates/ruff_formatter/src/lib.rs +++ b/crates/ruff_formatter/src/lib.rs @@ -37,6 +37,7 @@ use crate::group_id::UniqueGroupIdBuilder; use crate::prelude::TagKind; use std::fmt::{Debug, Display}; use std::marker::PhantomData; +use std::num::{NonZeroU16, NonZeroU8, TryFromIntError}; use crate::format_element::document::Document; use crate::printer::{Printer, PrinterOptions, SourceMapGeneration}; @@ -51,7 +52,6 @@ pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, Pri pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS}; pub use group_id::GroupId; use ruff_text_size::{TextRange, TextSize}; -use std::num::{NonZeroU8, ParseIntError, TryFromIntError}; use std::str::FromStr; #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] @@ -135,92 +135,48 @@ impl TryFrom for TabWidth { } } -/// Validated value for the `line_width` formatter options -/// -/// The allowed range of values is 1..=320 +/// The maximum visual width to which the formatter should try to limit a line. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct LineWidth(u16); +pub struct LineWidth(NonZeroU16); impl LineWidth { - /// Maximum allowed value for a valid [`LineWidth`] - pub const MAX: u16 = 320; - /// Return the numeric value for this [`LineWidth`] - pub fn value(&self) -> u16 { - self.0 + pub const fn value(&self) -> u16 { + self.0.get() } } impl Default for LineWidth { fn default() -> Self { - Self(80) - } -} - -/// Error type returned when parsing a [`LineWidth`] from a string fails -pub enum ParseLineWidthError { - /// The string could not be parsed as a valid [u16] - ParseError(ParseIntError), - /// The [u16] value of the string is not a valid [LineWidth] - TryFromIntError(LineWidthFromIntError), -} - -impl Debug for ParseLineWidthError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self, f) - } -} - -impl std::fmt::Display for ParseLineWidthError { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ParseLineWidthError::ParseError(err) => std::fmt::Display::fmt(err, fmt), - ParseLineWidthError::TryFromIntError(err) => std::fmt::Display::fmt(err, fmt), - } + Self(NonZeroU16::new(80).unwrap()) } } -impl FromStr for LineWidth { - type Err = ParseLineWidthError; +impl TryFrom for LineWidth { + type Error = TryFromIntError; - fn from_str(s: &str) -> Result { - let value = u16::from_str(s).map_err(ParseLineWidthError::ParseError)?; - let value = Self::try_from(value).map_err(ParseLineWidthError::TryFromIntError)?; - Ok(value) + fn try_from(value: u16) -> Result { + NonZeroU16::try_from(value).map(LineWidth) } } -/// Error type returned when converting a u16 to a [`LineWidth`] fails -#[derive(Clone, Copy, Debug)] -pub struct LineWidthFromIntError(pub u16); - -impl TryFrom for LineWidth { - type Error = LineWidthFromIntError; - - fn try_from(value: u16) -> Result { - if value > 0 && value <= Self::MAX { - Ok(Self(value)) - } else { - Err(LineWidthFromIntError(value)) - } +impl From for u16 { + fn from(value: LineWidth) -> Self { + value.0.get() } } -impl std::fmt::Display for LineWidthFromIntError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!( - f, - "The line width exceeds the maximum value ({})", - LineWidth::MAX - ) +impl From for u32 { + fn from(value: LineWidth) -> Self { + u32::from(value.0.get()) } } -impl From for u16 { - fn from(value: LineWidth) -> Self { - value.0 +impl From for LineWidth { + fn from(value: NonZeroU16) -> Self { + Self(value) } } @@ -312,7 +268,7 @@ impl FormatOptions for SimpleFormatOptions { fn as_print_options(&self) -> PrinterOptions { PrinterOptions { - print_width: self.line_width.into(), + line_width: self.line_width, indent_style: self.indent_style, source_map_generation: SourceMapGeneration::Enabled, ..PrinterOptions::default() @@ -489,7 +445,7 @@ pub type FormatResult = Result; /// Implementing `Format` for a custom struct /// /// ``` -/// use ruff_formatter::{format, write, IndentStyle, LineWidth}; +/// use ruff_formatter::{format, write, IndentStyle}; /// use ruff_formatter::prelude::*; /// use ruff_text_size::TextSize; /// diff --git a/crates/ruff_formatter/src/printer/mod.rs b/crates/ruff_formatter/src/printer/mod.rs index 9851a63c4600c..f1cbe097c3226 100644 --- a/crates/ruff_formatter/src/printer/mod.rs +++ b/crates/ruff_formatter/src/printer/mod.rs @@ -1194,7 +1194,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> { FormatElement::Tag(StartLineSuffix { reserved_width }) => { self.state.line_width += reserved_width; - if self.state.line_width > self.options().print_width.into() { + if self.state.line_width > self.options().line_width.into() { return Ok(Fits::No); } self.queue.skip_content(TagKind::LineSuffix); @@ -1320,7 +1320,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> { self.state.line_width += char_width; } - if self.state.line_width > self.options().print_width.into() { + if self.state.line_width > self.options().line_width.into() { return Fits::No; } @@ -1437,10 +1437,11 @@ impl From for MeasureMode { #[cfg(test)] mod tests { use crate::prelude::*; - use crate::printer::{LineEnding, PrintWidth, Printer, PrinterOptions}; + use crate::printer::{LineEnding, Printer, PrinterOptions}; use crate::source_code::SourceCode; use crate::{ - format_args, write, Document, FormatState, IndentStyle, Printed, TabWidth, VecBuffer, + format_args, write, Document, FormatState, IndentStyle, LineWidth, Printed, TabWidth, + VecBuffer, }; fn format(root: &dyn Format) -> Printed { @@ -1592,7 +1593,7 @@ two lines`, let options = PrinterOptions { indent_style: IndentStyle::Tab, tab_width: TabWidth::try_from(4).unwrap(), - print_width: PrintWidth::new(19), + line_width: LineWidth::try_from(19).unwrap(), ..PrinterOptions::default() }; @@ -1697,7 +1698,7 @@ two lines`, let printed = Printer::new( SourceCode::default(), - PrinterOptions::default().with_print_width(PrintWidth::new(10)), + PrinterOptions::default().with_line_width(LineWidth::try_from(10).unwrap()), ) .print(&document) .unwrap(); diff --git a/crates/ruff_formatter/src/printer/printer_options/mod.rs b/crates/ruff_formatter/src/printer/printer_options/mod.rs index 92fdf0fc10a74..f4d058d34fec1 100644 --- a/crates/ruff_formatter/src/printer/printer_options/mod.rs +++ b/crates/ruff_formatter/src/printer/printer_options/mod.rs @@ -7,7 +7,7 @@ pub struct PrinterOptions { pub tab_width: TabWidth, /// What's the max width of a line. Defaults to 80 - pub print_width: PrintWidth, + pub line_width: LineWidth, /// The type of line ending to apply to the printed input pub line_ending: LineEnding, @@ -27,14 +27,14 @@ where fn from(options: &'a O) -> Self { PrinterOptions::default() .with_indent(options.indent_style()) - .with_print_width(options.line_width().into()) + .with_line_width(options.line_width()) } } impl PrinterOptions { #[must_use] - pub fn with_print_width(mut self, width: PrintWidth) -> Self { - self.print_width = width; + pub fn with_line_width(mut self, width: LineWidth) -> Self { + self.line_width = width; self } @@ -66,10 +66,10 @@ impl PrinterOptions { } #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct PrintWidth(u32); +pub struct PrintWidth(u16); impl PrintWidth { - pub fn new(width: u32) -> Self { + pub fn new(width: u16) -> Self { Self(width) } } @@ -82,17 +82,17 @@ impl Default for PrintWidth { impl From for PrintWidth { fn from(width: LineWidth) -> Self { - Self(u32::from(u16::from(width))) + Self(u16::from(width)) } } -impl From for usize { +impl From for u32 { fn from(width: PrintWidth) -> Self { - width.0 as usize + u32::from(width.0) } } -impl From for u32 { +impl From for u16 { fn from(width: PrintWidth) -> Self { width.0 } diff --git a/crates/ruff_python_formatter/src/options.rs b/crates/ruff_python_formatter/src/options.rs index 2763b516edcc0..fe7c7a9e861bf 100644 --- a/crates/ruff_python_formatter/src/options.rs +++ b/crates/ruff_python_formatter/src/options.rs @@ -135,7 +135,7 @@ impl FormatOptions for PyFormatOptions { fn as_print_options(&self) -> PrinterOptions { PrinterOptions { tab_width: self.tab_width, - print_width: self.line_width.into(), + line_width: self.line_width, line_ending: LineEnding::LineFeed, indent_style: self.indent_style, source_map_generation: self.source_map_generation, diff --git a/crates/ruff_text_size/Cargo.toml b/crates/ruff_text_size/Cargo.toml index d3ce185167849..7878d5e16b517 100644 --- a/crates/ruff_text_size/Cargo.toml +++ b/crates/ruff_text_size/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" rust-version = "1.67.1" [dependencies] -serde = { version="1.0.152", optional = true, default_features = false } -schemars = { version = "0.8.12", optional = true } +serde = { workspace = true, optional = true } +schemars = { workspace = true, optional = true } [dev-dependencies] serde_test = { version = "1.0.152" } @@ -19,4 +19,4 @@ serde = ["dep:serde"] [[test]] name = "serde" path = "tests/serde.rs" -required-features = ["serde"] \ No newline at end of file +required-features = ["serde"] diff --git a/crates/ruff_workspace/src/pyproject.rs b/crates/ruff_workspace/src/pyproject.rs index cc483f67e7683..ed6609b5eebd8 100644 --- a/crates/ruff_workspace/src/pyproject.rs +++ b/crates/ruff_workspace/src/pyproject.rs @@ -199,7 +199,7 @@ line-length = 79 pyproject.tool, Some(Tools { ruff: Some(Options { - line_length: Some(LineLength::from(79)), + line_length: Some(LineLength::try_from(79).unwrap()), ..Options::default() }) }) @@ -299,7 +299,7 @@ other-attribute = 1 assert_eq!( config, Options { - line_length: Some(LineLength::from(88)), + line_length: Some(LineLength::try_from(88).unwrap()), extend_exclude: Some(vec![ "excluded_file.py".to_string(), "migrations".to_string(), diff --git a/ruff.schema.json b/ruff.schema.json index 43e7c07cc5212..b6844e3c5f2da 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -1386,10 +1386,10 @@ "additionalProperties": false }, "LineLength": { - "description": "The length of a line of text that is considered too long.", + "description": "The length of a line of text that is considered too long.\n\nThe allowed range of values is 1..=320", "type": "integer", - "format": "uint", - "minimum": 0.0 + "format": "uint16", + "minimum": 1.0 }, "McCabeOptions": { "type": "object",