mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:45:24 +00:00
Add rome_formatter
fork as ruff_formatter
(#2872)
The Ruff autoformatter is going to be based on an intermediate representation (IR) formatted via [Wadler's algorithm](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf). This is architecturally similar to [Rome](https://github.com/rome/tools), Prettier, [Skip](https://github.com/skiplang/skip/blob/master/src/tools/printer/printer.sk), and others. This PR adds a fork of the `rome_formatter` crate from [Rome](https://github.com/rome/tools), renamed here to `ruff_formatter`, which provides generic definitions for a formatter IR as well as a generic IR printer. (We've also pulled in `rome_rowan`, `rome_text_size`, and `rome_text_edit`, though some of these will be removed in future PRs.) Why fork? `rome_formatter` contains code that's specific to Rome's AST representation (e.g., it relies on a fork of rust-analyzer's `rowan`), and we'll likely want to support different abstractions and formatting capabilities (there are already a few changes coming in future PRs). Once we've dropped `ruff_rowan` and trimmed down `ruff_formatter` to the code we currently need, it's also not a huge surface area to maintain and update.
This commit is contained in:
parent
ac028cd9f8
commit
3ef1c2e303
83 changed files with 27547 additions and 1 deletions
176
crates/ruff_formatter/src/diagnostics.rs
Normal file
176
crates/ruff_formatter/src/diagnostics.rs
Normal file
|
@ -0,0 +1,176 @@
|
|||
use crate::prelude::TagKind;
|
||||
use ruff_rowan::{SyntaxError, TextRange};
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
/// Series of errors encountered during formatting
|
||||
pub enum FormatError {
|
||||
/// In case a node can't be formatted because it either misses a require child element or
|
||||
/// a child is present that should not (e.g. a trailing comma after a rest element).
|
||||
SyntaxError,
|
||||
/// In case range formatting failed because the provided range was larger
|
||||
/// than the formatted syntax tree
|
||||
RangeError { input: TextRange, tree: TextRange },
|
||||
|
||||
/// In case printing the document failed because it has an invalid structure.
|
||||
InvalidDocument(InvalidDocumentError),
|
||||
|
||||
/// Formatting failed because some content encountered a situation where a layout
|
||||
/// choice by an enclosing [crate::Format] resulted in a poor layout for a child [crate::Format].
|
||||
///
|
||||
/// It's up to an enclosing [crate::Format] to handle the error and pick another layout.
|
||||
/// This error should not be raised if there's no outer [crate::Format] handling the poor layout error,
|
||||
/// avoiding that formatting of the whole document fails.
|
||||
PoorLayout,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FormatError {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FormatError::SyntaxError => fmt.write_str("syntax error"),
|
||||
FormatError::RangeError { input, tree } => std::write!(
|
||||
fmt,
|
||||
"formatting range {input:?} is larger than syntax tree {tree:?}"
|
||||
),
|
||||
FormatError::InvalidDocument(error) => std::write!(fmt, "Invalid document: {error}\n\n This is an internal Rome error. Please report if necessary."),
|
||||
FormatError::PoorLayout => {
|
||||
std::write!(fmt, "Poor layout: The formatter wasn't able to pick a good layout for your document. This is an internal Rome error. Please report if necessary.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for FormatError {}
|
||||
|
||||
impl From<SyntaxError> for FormatError {
|
||||
fn from(error: SyntaxError) -> Self {
|
||||
FormatError::from(&error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SyntaxError> for FormatError {
|
||||
fn from(syntax_error: &SyntaxError) -> Self {
|
||||
match syntax_error {
|
||||
SyntaxError::MissingRequiredChild => FormatError::SyntaxError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrintError> for FormatError {
|
||||
fn from(error: PrintError) -> Self {
|
||||
FormatError::from(&error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PrintError> for FormatError {
|
||||
fn from(error: &PrintError) -> Self {
|
||||
match error {
|
||||
PrintError::InvalidDocument(reason) => FormatError::InvalidDocument(*reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum InvalidDocumentError {
|
||||
/// Mismatching start/end kinds
|
||||
///
|
||||
/// ```plain
|
||||
/// StartIndent
|
||||
/// ...
|
||||
/// EndGroup
|
||||
/// ```
|
||||
StartEndTagMismatch {
|
||||
start_kind: TagKind,
|
||||
end_kind: TagKind,
|
||||
},
|
||||
|
||||
/// End tag without a corresponding start tag.
|
||||
///
|
||||
/// ```plain
|
||||
/// Text
|
||||
/// EndGroup
|
||||
/// ```
|
||||
StartTagMissing { kind: TagKind },
|
||||
|
||||
/// Expected a specific start tag but instead is:
|
||||
/// * at the end of the document
|
||||
/// * at another start tag
|
||||
/// * at an end tag
|
||||
ExpectedStart {
|
||||
expected_start: TagKind,
|
||||
actual: ActualStart,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum ActualStart {
|
||||
/// The actual element is not a tag.
|
||||
Content,
|
||||
|
||||
/// The actual element was a start tag of another kind.
|
||||
Start(TagKind),
|
||||
|
||||
/// The actual element is an end tag instead of a start tag.
|
||||
End(TagKind),
|
||||
|
||||
/// Reached the end of the document
|
||||
EndOfDocument,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidDocumentError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InvalidDocumentError::StartEndTagMismatch {
|
||||
start_kind,
|
||||
end_kind,
|
||||
} => {
|
||||
std::write!(
|
||||
f,
|
||||
"Expected end tag of kind {start_kind:?} but found {end_kind:?}."
|
||||
)
|
||||
}
|
||||
InvalidDocumentError::StartTagMissing { kind } => {
|
||||
std::write!(f, "End tag of kind {kind:?} without matching start tag.")
|
||||
}
|
||||
InvalidDocumentError::ExpectedStart {
|
||||
expected_start,
|
||||
actual,
|
||||
} => {
|
||||
match actual {
|
||||
ActualStart::EndOfDocument => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but at the end of document.")
|
||||
}
|
||||
ActualStart::Start(start) => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but found start tag of kind {start:?}.")
|
||||
}
|
||||
ActualStart::End(end) => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but found end tag of kind {end:?}.")
|
||||
}
|
||||
ActualStart::Content => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but found non-tag element.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum PrintError {
|
||||
InvalidDocument(InvalidDocumentError),
|
||||
}
|
||||
|
||||
impl Error for PrintError {}
|
||||
|
||||
impl std::fmt::Display for PrintError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PrintError::InvalidDocument(inner) => {
|
||||
std::write!(f, "Invalid document: {inner}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue