attempt to better name and document things

This commit is contained in:
dylwil3 2025-12-12 14:19:17 -06:00
parent 9781d28a61
commit bb90d55ed5
4 changed files with 83 additions and 17 deletions

View File

@ -56,17 +56,17 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
match value.as_ref() { match value.as_ref() {
Expr::Attribute(expr) => { Expr::Attribute(expr) => {
expr.format() expr.format()
.with_options(call_chain_layout.increment_after_attribute()) .with_options(call_chain_layout.transition_after_attribute())
.fmt(f)?; .fmt(f)?;
} }
Expr::Call(expr) => { Expr::Call(expr) => {
expr.format() expr.format()
.with_options(call_chain_layout.increment_after_attribute()) .with_options(call_chain_layout.transition_after_attribute())
.fmt(f)?; .fmt(f)?;
} }
Expr::Subscript(expr) => { Expr::Subscript(expr) => {
expr.format() expr.format()
.with_options(call_chain_layout.increment_after_attribute()) .with_options(call_chain_layout.transition_after_attribute())
.fmt(f)?; .fmt(f)?;
} }
_ => { _ => {

View File

@ -49,7 +49,7 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
match func.as_ref() { match func.as_ref() {
Expr::Attribute(expr) => expr Expr::Attribute(expr) => expr
.format() .format()
.with_options(call_chain_layout.increment_call_like_attribute()) .with_options(call_chain_layout.decrement_call_like_count())
.fmt(f), .fmt(f),
Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f), Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f),
Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f), Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f),

View File

@ -53,7 +53,7 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
match value.as_ref() { match value.as_ref() {
Expr::Attribute(expr) => expr Expr::Attribute(expr) => expr
.format() .format()
.with_options(call_chain_layout.increment_call_like_attribute()) .with_options(call_chain_layout.decrement_call_like_count())
.fmt(f), .fmt(f),
Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f), Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f),
Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f), Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f),

View File

@ -905,25 +905,30 @@ pub enum CallChainLayout {
NonFluent, NonFluent,
} }
/// Records information about the position of a call chain /// Records information about the current position within
/// element relative to the first call or subscript. /// a call chain.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeState { pub enum AttributeState {
/// Stores the number of calls or subscripts on attributes /// Stores the number of calls or subscripts
/// to the left of the current position in a chain. /// to the left of the current position in a chain.
/// ///
/// Consecutive calls/subscripts on a single /// Consecutive calls/subscripts on a single
/// object only count once. For example, if we are at /// object only count once. For example, if we are at
/// `c` in `a.b()[0]()().c()` then this number would be 1. /// `c` in `a.b()[0]()().c()` then this number would be 1.
///
/// Caveat: If the root of the chain is parenthesized,
/// it contributes +1 to this count, even if it is not
/// a call or subscript. But the name
/// `CallsOrSubscriptsOrParenthesizedRootPreceding`
/// is a tad unwieldy, and this also rarely occurs.
CallsOrSubscriptsPreceding(u32), CallsOrSubscriptsPreceding(u32),
/// Indicates that we are at the first called or /// Indicates that we are at the first called or
/// subscripted attribute in the chain (and should /// subscripted object in the chain
/// therefore break).
/// ///
/// For example, if we are at `b` in `a.b()[0]()().c()` /// For example, if we are at `b` in `a.b()[0]()().c()`
FirstCallOrSubscript, FirstCallOrSubscript,
/// Indicates that we are to the left of the first /// Indicates that we are to the left of the first
/// called or subscripted attribute, and therefore /// called or subscripted object in the chain, and therefore
/// need not break. /// need not break.
/// ///
/// For example, if we are at `a` in `a.b()[0]()().c()` /// For example, if we are at `a` in `a.b()[0]()().c()`
@ -934,14 +939,15 @@ impl CallChainLayout {
/// Returns new state decreasing count of remaining calls/subscripts /// Returns new state decreasing count of remaining calls/subscripts
/// to traverse, or the state `FirstCallOrSubscript`, as appropriate. /// to traverse, or the state `FirstCallOrSubscript`, as appropriate.
#[must_use] #[must_use]
pub(crate) fn increment_call_like_attribute(self) -> Self { pub(crate) fn decrement_call_like_count(self) -> Self {
match self { match self {
Self::Fluent(AttributeState::CallsOrSubscriptsPreceding(x)) => { Self::Fluent(AttributeState::CallsOrSubscriptsPreceding(x)) => {
if x > 1 { if x > 1 {
// Recall that we traverse call chains from right to // Recall that we traverse call chains from right to
// left. So after moving past a call-like attribute // left. So after moving from a call/subscript into
// we _decrease_ the count of _remaining_ calls and // an attribute, we _decrease_ the count of
// subscripts to the left of our current position. // _remaining_ calls or subscripts to the left of our
// current position.
Self::Fluent(AttributeState::CallsOrSubscriptsPreceding(x - 1)) Self::Fluent(AttributeState::CallsOrSubscriptsPreceding(x - 1))
} else { } else {
Self::Fluent(AttributeState::FirstCallOrSubscript) Self::Fluent(AttributeState::FirstCallOrSubscript)
@ -955,7 +961,7 @@ impl CallChainLayout {
/// `FirstCallOrSubscript` -> `BeforeFirstCallOrSubscript` /// `FirstCallOrSubscript` -> `BeforeFirstCallOrSubscript`
/// and otherwise returns unchanged. /// and otherwise returns unchanged.
#[must_use] #[must_use]
pub(crate) fn increment_after_attribute(self) -> Self { pub(crate) fn transition_after_attribute(self) -> Self {
match self { match self {
Self::Fluent(AttributeState::FirstCallOrSubscript) => { Self::Fluent(AttributeState::FirstCallOrSubscript) => {
Self::Fluent(AttributeState::BeforeFirstCallOrSubscript) Self::Fluent(AttributeState::BeforeFirstCallOrSubscript)
@ -991,6 +997,11 @@ impl CallChainLayout {
comment_ranges: &CommentRanges, comment_ranges: &CommentRanges,
source: &str, source: &str,
) -> Self { ) -> Self {
// TODO(dylan): Once the fluent layout preview style is
// stabilized, see if it is possible to simplify some of
// the logic around parenthesized roots. (While supporting
// both styles it is more difficult to do this.)
// Count of attribute _values_ which are called or // Count of attribute _values_ which are called or
// subscripted, after the leftmost parenthesized // subscripted, after the leftmost parenthesized
// value. // value.
@ -1006,12 +1017,30 @@ impl CallChainLayout {
// ``` // ```
let mut computed_attribute_values_after_parentheses = 0; let mut computed_attribute_values_after_parentheses = 0;
// Similar to the above, but instead looks at all
// and subscripts rather than looking only at those on
// _attribute values_. So this count can differ from the
// above.
//
// Examples of `computed_attribute_values_after_parentheses` vs
// `call_like_count`:
//
// a().b ---> 1 vs 1
// a.b().c --> 1 vs 1
// a.b() ---> 0 vs 1
let mut call_like_count = 0; let mut call_like_count = 0;
// Going from right to left, we traverse calls, subscripts, // Going from right to left, we traverse calls, subscripts,
// and attributes until we get to an expression of a different // and attributes until we get to an expression of a different
// kind _or_ to a parenthesized expression. This records // kind _or_ to a parenthesized expression. This records
// the case where we end the traversal at a parenthesized expression. // the case where we end the traversal at a parenthesized expression.
//
// In these cases, the inferred semantics of the chain are different.
// We interpret this as the user indicating:
// "this parenthesized value is the object of interest and we are
// doing transformations on it". This increases our confidence that
// this should be fluently formatted, and also means we should make
// our first break after this value.
let mut root_value_parenthesized = false; let mut root_value_parenthesized = false;
loop { loop {
match expr { match expr {
@ -1066,7 +1095,44 @@ impl CallChainLayout {
if computed_attribute_values_after_parentheses + u32::from(root_value_parenthesized) < 2 { if computed_attribute_values_after_parentheses + u32::from(root_value_parenthesized) < 2 {
CallChainLayout::NonFluent CallChainLayout::NonFluent
} else { } else {
CallChainLayout::Fluent(AttributeState::CallsOrSubscriptsPreceding(call_like_count)) CallChainLayout::Fluent(AttributeState::CallsOrSubscriptsPreceding(
// We count a parenthesized root value as an extra
// call for the purposes of tracking state.
//
// The reason is that, in this case, we want the first
// "special" break to happen right after the root, as
// opposed to right after the first called/subscripted
// attribute.
//
// For example:
//
// ```
// (object_of_interest)
// .data.filter()
// .agg()
// .etc()
// ```
//
// instead of (in preview):
//
// ```
// (object_of_interest)
// .data
// .filter()
// .etc()
// ```
//
// For comparison, if we didn't have parentheses around
// the root, we want (and get, in preview):
//
// ```
// object_of_interest.data
// .filter()
// .agg()
// .etc()
// ```
call_like_count + u32::from(root_value_parenthesized),
))
} }
} }