mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-30 07:08:07 +00:00
274 lines
7.3 KiB
Rust
274 lines
7.3 KiB
Rust
use std::error::Error;
|
|
use std::fmt;
|
|
use std::hash::Hasher;
|
|
use std::num::{NonZeroU8, NonZeroU16, ParseIntError};
|
|
use std::str::FromStr;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use unicode_width::UnicodeWidthChar;
|
|
|
|
use ruff_cache::{CacheKey, CacheKeyHasher};
|
|
use ruff_macros::CacheKey;
|
|
use ruff_text_size::TextSize;
|
|
|
|
/// The length of a line of text that is considered too long.
|
|
///
|
|
/// 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(
|
|
#[cfg_attr(feature = "schemars", schemars(range(min = 1, max = 320)))] 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()
|
|
}
|
|
|
|
pub fn text_len(&self) -> TextSize {
|
|
TextSize::from(u32::from(self.value()))
|
|
}
|
|
}
|
|
|
|
impl Default for LineLength {
|
|
fn default() -> Self {
|
|
Self(NonZeroU16::new(88).unwrap())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for LineLength {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(&self.0, f)
|
|
}
|
|
}
|
|
|
|
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<Self, Self::Err> {
|
|
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<u16> for LineLength {
|
|
type Error = LineLengthFromIntError;
|
|
|
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
|
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<LineLength> for u16 {
|
|
fn from(value: LineLength) -> Self {
|
|
value.0.get()
|
|
}
|
|
}
|
|
|
|
impl From<LineLength> for NonZeroU16 {
|
|
fn from(value: LineLength) -> Self {
|
|
value.0
|
|
}
|
|
}
|
|
|
|
/// A measure of the width of a line of text.
|
|
///
|
|
/// This is used to determine if a line is too long.
|
|
/// It should be compared to a [`LineLength`].
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct LineWidthBuilder {
|
|
/// The width of the line.
|
|
width: usize,
|
|
/// The column of the line.
|
|
/// This is used to calculate the width of tabs.
|
|
column: usize,
|
|
/// The tab size to use when calculating the width of tabs.
|
|
tab_size: IndentWidth,
|
|
}
|
|
|
|
impl Default for LineWidthBuilder {
|
|
fn default() -> Self {
|
|
Self::new(IndentWidth::default())
|
|
}
|
|
}
|
|
|
|
impl PartialEq for LineWidthBuilder {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.width == other.width
|
|
}
|
|
}
|
|
|
|
impl Eq for LineWidthBuilder {}
|
|
|
|
impl PartialOrd for LineWidthBuilder {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for LineWidthBuilder {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
self.width.cmp(&other.width)
|
|
}
|
|
}
|
|
|
|
impl LineWidthBuilder {
|
|
pub fn get(&self) -> usize {
|
|
self.width
|
|
}
|
|
|
|
/// Creates a new `LineWidth` with the given tab size.
|
|
pub fn new(tab_size: IndentWidth) -> Self {
|
|
LineWidthBuilder {
|
|
width: 0,
|
|
column: 0,
|
|
tab_size,
|
|
}
|
|
}
|
|
|
|
fn update(mut self, chars: impl Iterator<Item = char>) -> Self {
|
|
let tab_size: usize = self.tab_size.as_usize();
|
|
for c in chars {
|
|
match c {
|
|
'\t' => {
|
|
let tab_offset = tab_size - (self.column % tab_size);
|
|
self.width += tab_offset;
|
|
self.column += tab_offset;
|
|
}
|
|
'\n' | '\r' => {
|
|
self.width = 0;
|
|
self.column = 0;
|
|
}
|
|
_ => {
|
|
self.width += c.width().unwrap_or(0);
|
|
self.column += 1;
|
|
}
|
|
}
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Adds the given text to the line width.
|
|
#[must_use]
|
|
pub fn add_str(self, text: &str) -> Self {
|
|
self.update(text.chars())
|
|
}
|
|
|
|
/// Adds the given character to the line width.
|
|
#[must_use]
|
|
pub fn add_char(self, c: char) -> Self {
|
|
self.update(std::iter::once(c))
|
|
}
|
|
|
|
/// Adds the given width to the line width.
|
|
/// Also adds the given width to the column.
|
|
/// 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]
|
|
pub fn add_width(mut self, width: usize) -> Self {
|
|
self.width += width;
|
|
self.column += width;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl PartialEq<LineLength> for LineWidthBuilder {
|
|
fn eq(&self, other: &LineLength) -> bool {
|
|
self.width == (other.value() as usize)
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<LineLength> for LineWidthBuilder {
|
|
fn partial_cmp(&self, other: &LineLength) -> Option<std::cmp::Ordering> {
|
|
self.width.partial_cmp(&(other.value() as usize))
|
|
}
|
|
}
|
|
|
|
/// The size of a tab.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, CacheKey)]
|
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
|
pub struct IndentWidth(NonZeroU8);
|
|
|
|
impl IndentWidth {
|
|
pub(crate) fn as_usize(self) -> usize {
|
|
self.0.get() as usize
|
|
}
|
|
}
|
|
|
|
impl Default for IndentWidth {
|
|
fn default() -> Self {
|
|
Self(NonZeroU8::new(4).unwrap())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for IndentWidth {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(&self.0, f)
|
|
}
|
|
}
|
|
|
|
impl From<NonZeroU8> for IndentWidth {
|
|
fn from(tab_size: NonZeroU8) -> Self {
|
|
Self(tab_size)
|
|
}
|
|
}
|
|
|
|
impl From<IndentWidth> for NonZeroU8 {
|
|
fn from(value: IndentWidth) -> Self {
|
|
value.0
|
|
}
|
|
}
|