ruff/crates/ruff_diagnostics/src/source_map.rs
Charlie Marsh bdf285225d
Enable formatting for Jupyter notebooks (#7749)
## Summary

This PR enables `ruff format` to format Jupyter notebooks.

Most of the work is contained in a new `format_source` method that
formats a generic `SourceKind`, then returns `Some(transformed)` if the
source required formatting, or `None` otherwise.

Closes https://github.com/astral-sh/ruff/issues/7598.

## Test Plan

Ran `cat foo.py | cargo run -p ruff_cli -- format --stdin-filename
Untitled.ipynb`; verified that the console showed a reasonable error:

```console
warning: Failed to read notebook Untitled.ipynb: Expected a Jupyter Notebook, which must be internally stored as JSON, but this file isn't valid JSON: EOF while parsing a value at line 1 column 0
```

Ran `cat Untitled.ipynb | cargo run -p ruff_cli -- format
--stdin-filename Untitled.ipynb`; verified that the JSON output
contained formatted source code.
2023-10-02 14:44:18 +00:00

72 lines
2.1 KiB
Rust

use ruff_text_size::{Ranged, TextSize};
use crate::Edit;
/// Lightweight sourcemap marker representing the source and destination
/// position for an [`Edit`].
#[derive(Debug, PartialEq, Eq)]
pub struct SourceMarker {
/// Position of the marker in the original source.
source: TextSize,
/// Position of the marker in the transformed code.
dest: TextSize,
}
impl SourceMarker {
pub fn new(source: TextSize, dest: TextSize) -> Self {
Self { source, dest }
}
pub const fn source(&self) -> TextSize {
self.source
}
pub const fn dest(&self) -> TextSize {
self.dest
}
}
/// A collection of [`SourceMarker`].
///
/// Sourcemaps are used to map positions in the original source to positions in
/// the transformed code. Here, only the boundaries of edits are tracked instead
/// of every single character.
#[derive(Default, PartialEq, Eq)]
pub struct SourceMap(Vec<SourceMarker>);
impl SourceMap {
/// Returns a slice of all the markers in the sourcemap in the order they
/// were added.
pub fn markers(&self) -> &[SourceMarker] {
&self.0
}
/// Push the start marker for an [`Edit`].
///
/// The `output_length` is the length of the transformed string before the
/// edit is applied.
pub fn push_start_marker(&mut self, edit: &Edit, output_length: TextSize) {
self.push_marker(edit.start(), output_length);
}
/// Push the end marker for an [`Edit`].
///
/// The `output_length` is the length of the transformed string after the
/// edit has been applied.
pub fn push_end_marker(&mut self, edit: &Edit, output_length: TextSize) {
if edit.is_insertion() {
self.push_marker(edit.start(), output_length);
} else {
// Deletion or replacement
self.push_marker(edit.end(), output_length);
}
}
/// Push a new marker to the sourcemap.
pub fn push_marker(&mut self, offset: TextSize, output_length: TextSize) {
self.0.push(SourceMarker {
source: offset,
dest: output_length,
});
}
}