ruff/crates/ruff_python_formatter/src/comments/debug.rs
konstin db301c14bd
Consistently name comment own line/end-of-line line_position() (#5215)
## Summary

Previously, `DecoratedComment` used `text_position()` and
`SourceComment` used `position()`. This PR unifies this to
`line_position` everywhere.

## Test Plan

This is a rename refactoring.
2023-06-21 11:04:56 +02:00

235 lines
6.7 KiB
Rust

use crate::comments::node_key::NodeRefEqualityKey;
use crate::comments::{CommentsMap, SourceComment};
use itertools::Itertools;
use ruff_formatter::SourceCode;
use ruff_python_ast::prelude::*;
use std::fmt::{Debug, Formatter, Write};
/// Prints a debug representation of [`SourceComment`] that includes the comment's text
pub(crate) struct DebugComment<'a> {
comment: &'a SourceComment,
source_code: SourceCode<'a>,
}
impl<'a> DebugComment<'a> {
pub(super) fn new(comment: &'a SourceComment, source_code: SourceCode<'a>) -> Self {
Self {
comment,
source_code,
}
}
}
impl Debug for DebugComment<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut strut = f.debug_struct("SourceComment");
strut
.field("text", &self.comment.slice.text(self.source_code))
.field("position", &self.comment.line_position);
#[cfg(debug_assertions)]
strut.field("formatted", &self.comment.formatted.get());
strut.finish()
}
}
/// Pretty-printed debug representation of [`Comments`].
pub(crate) struct DebugComments<'a> {
comments: &'a CommentsMap<'a>,
source_code: SourceCode<'a>,
}
impl<'a> DebugComments<'a> {
pub(super) fn new(comments: &'a CommentsMap, source_code: SourceCode<'a>) -> Self {
Self {
comments,
source_code,
}
}
}
impl Debug for DebugComments<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_map();
for node in self.comments.keys().sorted_by_key(|key| key.node().start()) {
map.entry(
&NodeKindWithSource {
key: *node,
source: self.source_code,
},
&DebugNodeComments {
comments: self.comments,
source_code: self.source_code,
key: *node,
},
);
}
map.finish()
}
}
/// Prints the source code up to the first new line character. Truncates the text if it exceeds 40 characters.
struct NodeKindWithSource<'a> {
key: NodeRefEqualityKey<'a>,
source: SourceCode<'a>,
}
impl Debug for NodeKindWithSource<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
struct TruncatedSource<'a>(&'a str);
impl Debug for TruncatedSource<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_char('`')?;
let first_line = if let Some(line_end_pos) = self.0.find(['\n', '\r']) {
&self.0[..line_end_pos]
} else {
self.0
};
if first_line.len() > 40 {
let (head, rest) = first_line.split_at(27);
f.write_str(head)?;
f.write_str("...")?;
// Take the last 10 characters
let tail = &rest[rest.len().saturating_sub(10)..];
f.write_str(tail)?;
} else {
f.write_str(first_line)?;
}
if first_line.len() < self.0.len() {
f.write_str("\u{23ce}")?;
}
f.write_char('`')
}
}
let kind = self.key.node().kind();
let source = self.source.slice(self.key.node().range()).text(self.source);
f.debug_struct("Node")
.field("kind", &kind)
.field("range", &self.key.node().range())
.field("source", &TruncatedSource(source))
.finish()
}
}
struct DebugNodeComments<'a> {
comments: &'a CommentsMap<'a>,
source_code: SourceCode<'a>,
key: NodeRefEqualityKey<'a>,
}
impl Debug for DebugNodeComments<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entry(
&"leading",
&DebugNodeCommentSlice {
node_comments: self.comments.leading(&self.key),
source_code: self.source_code,
},
)
.entry(
&"dangling",
&DebugNodeCommentSlice {
node_comments: self.comments.dangling(&self.key),
source_code: self.source_code,
},
)
.entry(
&"trailing",
&DebugNodeCommentSlice {
node_comments: self.comments.trailing(&self.key),
source_code: self.source_code,
},
)
.finish()
}
}
struct DebugNodeCommentSlice<'a> {
node_comments: &'a [SourceComment],
source_code: SourceCode<'a>,
}
impl Debug for DebugNodeCommentSlice<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut list = f.debug_list();
for comment in self.node_comments {
list.entry(&comment.debug(self.source_code));
}
list.finish()
}
}
#[cfg(test)]
mod tests {
use crate::comments::map::MultiMap;
use crate::comments::{CommentLinePosition, Comments, CommentsMap, SourceComment};
use insta::assert_debug_snapshot;
use ruff_formatter::SourceCode;
use ruff_python_ast::node::AnyNode;
use ruff_text_size::{TextRange, TextSize};
use rustpython_parser::ast::{StmtBreak, StmtContinue};
#[test]
fn debug() {
let continue_statement = AnyNode::from(StmtContinue {
range: TextRange::default(),
});
let break_statement = AnyNode::from(StmtBreak {
range: TextRange::default(),
});
let source = r#"# leading comment
continue; # trailing
# break leading
break;
"#;
let source_code = SourceCode::new(source);
let mut comments_map: CommentsMap = MultiMap::new();
comments_map.push_leading(
continue_statement.as_ref().into(),
SourceComment::new(
source_code.slice(TextRange::at(TextSize::new(0), TextSize::new(17))),
CommentLinePosition::OwnLine,
),
);
comments_map.push_trailing(
continue_statement.as_ref().into(),
SourceComment::new(
source_code.slice(TextRange::at(TextSize::new(28), TextSize::new(10))),
CommentLinePosition::EndOfLine,
),
);
comments_map.push_leading(
break_statement.as_ref().into(),
SourceComment::new(
source_code.slice(TextRange::at(TextSize::new(39), TextSize::new(15))),
CommentLinePosition::OwnLine,
),
);
let comments = Comments::new(comments_map);
assert_debug_snapshot!(comments.debug(source_code));
}
}