From eae59cf088d761b6c4136dfd7e51c508d25b8bf5 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Sat, 26 Aug 2023 18:00:43 +0200 Subject: [PATCH] Optional source map generation (#6894) --- crates/ruff_formatter/src/builders.rs | 17 ++-- .../src/format_element/document.rs | 18 ++-- crates/ruff_formatter/src/lib.rs | 12 ++- crates/ruff_formatter/src/printer/mod.rs | 4 + .../src/printer/printer_options/mod.rs | 93 ++++++++++++------- crates/ruff_python_formatter/src/lib.rs | 34 +++---- crates/ruff_python_formatter/src/options.rs | 12 ++- 7 files changed, 117 insertions(+), 73 deletions(-) diff --git a/crates/ruff_formatter/src/builders.rs b/crates/ruff_formatter/src/builders.rs index 881a80e557..2605bf87e6 100644 --- a/crates/ruff_formatter/src/builders.rs +++ b/crates/ruff_formatter/src/builders.rs @@ -1,16 +1,17 @@ +use std::cell::Cell; +use std::marker::PhantomData; +use std::num::NonZeroU8; + +use ruff_text_size::TextRange; +#[allow(clippy::enum_glob_use)] +use Tag::*; + use crate::format_element::tag::{Condition, Tag}; use crate::prelude::tag::{DedentMode, GroupMode, LabelId}; use crate::prelude::*; use crate::{format_element, write, Argument, Arguments, FormatContext, GroupId, TextSize}; use crate::{Buffer, VecBuffer}; -use ruff_text_size::TextRange; -use std::cell::Cell; -use std::marker::PhantomData; -use std::num::NonZeroU8; -#[allow(clippy::enum_glob_use)] -use Tag::*; - /// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line. /// It's omitted if the enclosing `Group` fits on a single line. /// A soft line break is identical to a hard line break when not enclosed inside of a `Group`. @@ -283,7 +284,6 @@ impl std::fmt::Debug for StaticText { /// ## Examples /// /// ``` -/// /// ``` /// use ruff_formatter::format; /// use ruff_formatter::prelude::*; /// @@ -329,6 +329,7 @@ pub struct SourcePosition(TextSize); impl Format for SourcePosition { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { f.write_element(FormatElement::SourcePosition(self.0)); + Ok(()) } } diff --git a/crates/ruff_formatter/src/format_element/document.rs b/crates/ruff_formatter/src/format_element/document.rs index 69fef71adf..23b64645aa 100644 --- a/crates/ruff_formatter/src/format_element/document.rs +++ b/crates/ruff_formatter/src/format_element/document.rs @@ -1,17 +1,19 @@ -use super::tag::Tag; +use std::collections::HashMap; +use std::ops::Deref; + +use rustc_hash::FxHashMap; + use crate::format_element::tag::{Condition, DedentMode}; use crate::prelude::tag::GroupMode; use crate::prelude::*; -use crate::printer::LineEnding; use crate::source_code::SourceCode; use crate::{format, write, TabWidth}; use crate::{ BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter, IndentStyle, LineWidth, PrinterOptions, }; -use rustc_hash::FxHashMap; -use std::collections::HashMap; -use std::ops::Deref; + +use super::tag::Tag; /// A formatted document. #[derive(Debug, Clone, Eq, PartialEq, Default)] @@ -225,10 +227,9 @@ impl FormatOptions for IrFormatOptions { fn as_print_options(&self) -> PrinterOptions { PrinterOptions { - tab_width: TabWidth::default(), print_width: self.line_width().into(), - line_ending: LineEnding::LineFeed, indent_style: IndentStyle::Space(2), + ..PrinterOptions::default() } } } @@ -781,10 +782,11 @@ impl Format> for Condition { #[cfg(test)] mod tests { + use ruff_text_size::{TextRange, TextSize}; + use crate::prelude::*; use crate::{format, format_args, write}; use crate::{SimpleFormatContext, SourceCode}; - use ruff_text_size::{TextRange, TextSize}; #[test] fn display_elements() { diff --git a/crates/ruff_formatter/src/lib.rs b/crates/ruff_formatter/src/lib.rs index 3a6dc8cd6d..18bc134610 100644 --- a/crates/ruff_formatter/src/lib.rs +++ b/crates/ruff_formatter/src/lib.rs @@ -39,7 +39,7 @@ use std::fmt::{Debug, Display}; use std::marker::PhantomData; use crate::format_element::document::Document; -use crate::printer::{Printer, PrinterOptions}; +use crate::printer::{Printer, PrinterOptions, SourceMapGeneration}; pub use arguments::{Argument, Arguments}; pub use buffer::{ Buffer, BufferExtensions, BufferSnapshot, Inspect, RemoveSoftLinesBuffer, VecBuffer, @@ -311,10 +311,12 @@ impl FormatOptions for SimpleFormatOptions { } fn as_print_options(&self) -> PrinterOptions { - PrinterOptions::default() - .with_indent(self.indent_style) - .with_tab_width(self.tab_width()) - .with_print_width(self.line_width.into()) + PrinterOptions { + print_width: self.line_width.into(), + indent_style: self.indent_style, + source_map_generation: SourceMapGeneration::Enabled, + ..PrinterOptions::default() + } } } diff --git a/crates/ruff_formatter/src/printer/mod.rs b/crates/ruff_formatter/src/printer/mod.rs index d3f535f7e4..a096bd31fa 100644 --- a/crates/ruff_formatter/src/printer/mod.rs +++ b/crates/ruff_formatter/src/printer/mod.rs @@ -396,6 +396,10 @@ impl<'a> Printer<'a> { } fn push_marker(&mut self) { + if self.options.source_map_generation.is_disabled() { + return; + } + let marker = SourceMarker { source: self.state.source_position, dest: self.state.buffer.text_len(), diff --git a/crates/ruff_formatter/src/printer/printer_options/mod.rs b/crates/ruff_formatter/src/printer/printer_options/mod.rs index 756d8c8a9a..92fdf0fc10 100644 --- a/crates/ruff_formatter/src/printer/printer_options/mod.rs +++ b/crates/ruff_formatter/src/printer/printer_options/mod.rs @@ -14,39 +14,10 @@ pub struct PrinterOptions { /// Whether the printer should use tabs or spaces to indent code and if spaces, by how many. pub indent_style: IndentStyle, -} -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct PrintWidth(u32); - -impl PrintWidth { - pub fn new(width: u32) -> Self { - Self(width) - } -} - -impl Default for PrintWidth { - fn default() -> Self { - LineWidth::default().into() - } -} - -impl From for PrintWidth { - fn from(width: LineWidth) -> Self { - Self(u32::from(u16::from(width))) - } -} - -impl From for usize { - fn from(width: PrintWidth) -> Self { - width.0 as usize - } -} - -impl From for u32 { - fn from(width: PrintWidth) -> Self { - width.0 - } + /// Whether the printer should build a source map that allows mapping positions in the source document + /// to positions in the formatted document. + pub source_map_generation: SourceMapGeneration, } impl<'a, O> From<&'a O> for PrinterOptions @@ -94,6 +65,64 @@ impl PrinterOptions { } } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct PrintWidth(u32); + +impl PrintWidth { + pub fn new(width: u32) -> Self { + Self(width) + } +} + +impl Default for PrintWidth { + fn default() -> Self { + LineWidth::default().into() + } +} + +impl From for PrintWidth { + fn from(width: LineWidth) -> Self { + Self(u32::from(u16::from(width))) + } +} + +impl From for usize { + fn from(width: PrintWidth) -> Self { + width.0 as usize + } +} + +impl From for u32 { + fn from(width: PrintWidth) -> Self { + width.0 + } +} + +/// Configures whether the formatter and printer generate a source map that allows mapping +/// positions in the source document to positions in the formatted code. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum SourceMapGeneration { + /// The formatter generates no source map. + #[default] + Disabled, + + /// The formatter generates a source map that allows mapping positions in the source document + /// to positions in the formatted document. The ability to map positions is useful for range formatting + /// or when trying to identify where to move the cursor so that it matches its position in the source document. + Enabled, +} + +impl SourceMapGeneration { + pub const fn is_enabled(self) -> bool { + matches!(self, SourceMapGeneration::Enabled) + } + + pub const fn is_disabled(self) -> bool { + matches!(self, SourceMapGeneration::Disabled) + } +} + #[allow(dead_code)] #[derive(Clone, Debug, Eq, PartialEq, Default)] pub enum LineEnding { diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 4303eac0d8..a6253f1088 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -1,11 +1,8 @@ use thiserror::Error; use ruff_formatter::format_element::tag; -use ruff_formatter::prelude::{source_position, text, Formatter, Tag}; -use ruff_formatter::{ - format, write, Buffer, Format, FormatElement, FormatError, FormatResult, PrintError, -}; -use ruff_formatter::{Formatted, Printed, SourceCode}; +use ruff_formatter::prelude::*; +use ruff_formatter::{format, FormatError, Formatted, PrintError, Printed, SourceCode}; use ruff_python_ast::node::{AnyNodeRef, AstNode}; use ruff_python_ast::Mod; use ruff_python_index::{CommentRanges, CommentRangesBuilder}; @@ -54,23 +51,22 @@ where if self.is_suppressed(node_comments.trailing, f.context()) { suppressed_node(node.as_any_node_ref()).fmt(f) } else { - write!( - f, - [ - leading_comments(node_comments.leading), - source_position(node.start()) - ] - )?; + leading_comments(node_comments.leading).fmt(f)?; + + let is_source_map_enabled = f.options().source_map_generation().is_enabled(); + + if is_source_map_enabled { + source_position(node.start()).fmt(f)?; + } + self.fmt_fields(node, f)?; self.fmt_dangling_comments(node_comments.dangling, f)?; - write!( - f, - [ - source_position(node.end()), - trailing_comments(node_comments.trailing) - ] - ) + if is_source_map_enabled { + source_position(node.end()).fmt(f)?; + } + + trailing_comments(node_comments.trailing).fmt(f) } } diff --git a/crates/ruff_python_formatter/src/options.rs b/crates/ruff_python_formatter/src/options.rs index c255c0fd0e..2763b516ed 100644 --- a/crates/ruff_python_formatter/src/options.rs +++ b/crates/ruff_python_formatter/src/options.rs @@ -1,4 +1,4 @@ -use ruff_formatter::printer::{LineEnding, PrinterOptions}; +use ruff_formatter::printer::{LineEnding, PrinterOptions, SourceMapGeneration}; use ruff_formatter::{FormatOptions, IndentStyle, LineWidth, TabWidth}; use ruff_python_ast::PySourceType; use std::path::Path; @@ -33,6 +33,10 @@ pub struct PyFormatOptions { /// Whether to expand lists or elements if they have a trailing comma such as `(a, b,)`. magic_trailing_comma: MagicTrailingComma, + + /// Should the formatter generate a source map that allows mapping source positions to positions + /// in the formatted document. + source_map_generation: SourceMapGeneration, } fn default_line_width() -> LineWidth { @@ -56,6 +60,7 @@ impl Default for PyFormatOptions { tab_width: default_tab_width(), quote_style: QuoteStyle::default(), magic_trailing_comma: MagicTrailingComma::default(), + source_map_generation: SourceMapGeneration::default(), } } } @@ -85,6 +90,10 @@ impl PyFormatOptions { self.source_type } + pub fn source_map_generation(&self) -> SourceMapGeneration { + self.source_map_generation + } + #[must_use] pub fn with_quote_style(mut self, style: QuoteStyle) -> Self { self.quote_style = style; @@ -129,6 +138,7 @@ impl FormatOptions for PyFormatOptions { print_width: self.line_width.into(), line_ending: LineEnding::LineFeed, indent_style: self.indent_style, + source_map_generation: self.source_map_generation, } } }