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:
Charlie Marsh 2023-02-14 19:22:55 -05:00 committed by GitHub
parent ac028cd9f8
commit 3ef1c2e303
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
83 changed files with 27547 additions and 1 deletions

View 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}")
}
}
}
}