Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 71 additions & 2 deletions src/configuration/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,67 @@ impl ConfigurationBuilder {
self.insert("whileStatement.spaceAround", value.into())
}

/* alignment */

/// Enable all alignment options at once.
///
/// When set to `true`, all individual alignment settings are enabled by default.
/// Individual settings can still be set to `false` to override this global setting.
///
/// Default: `false`
pub fn alignment_enable_all(&mut self, value: bool) -> &mut Self {
self.insert("alignment.enableAll", value.into())
}

/// Whether to align assignments with surrounding assignments.
///
/// Default: `false`
pub fn variable_statement_align_assignments(&mut self, value: bool) -> &mut Self {
self.insert("variableStatement.alignAssignments", value.into())
}

/// Whether to align object properties with surrounding properties.
///
/// Default: `false`
pub fn object_expression_align_properties(&mut self, value: bool) -> &mut Self {
self.insert("objectExpression.alignProperties", value.into())
}

/// Whether to align interface properties with surrounding properties.
///
/// Default: `false`
pub fn interface_declaration_align_properties(&mut self, value: bool) -> &mut Self {
self.insert("interfaceDeclaration.alignProperties", value.into())
}

/// Whether to align type literal properties with surrounding properties.
///
/// Default: `false`
pub fn type_literal_align_properties(&mut self, value: bool) -> &mut Self {
self.insert("typeLiteral.alignProperties", value.into())
}

/// Whether to align class properties with surrounding properties.
///
/// Default: `false`
pub fn class_declaration_align_properties(&mut self, value: bool) -> &mut Self {
self.insert("classDeclaration.alignProperties", value.into())
}

/// Whether to align enum members with surrounding members.
///
/// Default: `false`
pub fn enum_declaration_align_members(&mut self, value: bool) -> &mut Self {
self.insert("enumDeclaration.alignMembers", value.into())
}

/// Whether to align module declaration properties with surrounding properties.
///
/// Default: `false`
pub fn module_declaration_align_properties(&mut self, value: bool) -> &mut Self {
self.insert("moduleDeclaration.alignProperties", value.into())
}

#[cfg(test)]
pub(super) fn get_inner_config(&self) -> ConfigKeyMap {
self.config.clone()
Expand Down Expand Up @@ -1294,10 +1355,18 @@ mod tests {
.paren_expression_space_around(true)
.switch_statement_space_around(true)
.tuple_type_space_around(true)
.while_statement_space_around(true);
.while_statement_space_around(true)
.alignment_enable_all(true)
.variable_statement_align_assignments(true)
.object_expression_align_properties(true)
.interface_declaration_align_properties(true)
.type_literal_align_properties(true)
.class_declaration_align_properties(true)
.enum_declaration_align_members(true)
.module_declaration_align_properties(true);

let inner_config = config.get_inner_config();
assert_eq!(inner_config.len(), 182);
assert_eq!(inner_config.len(), 190);
let diagnostics = resolve_config(inner_config, &Default::default()).diagnostics;
assert_eq!(diagnostics.len(), 0);
}
Expand Down
85 changes: 85 additions & 0 deletions src/configuration/resolve_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,32 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration)
switch_statement_space_around: get_value(&mut config, "switchStatement.spaceAround", space_around, &mut diagnostics),
tuple_type_space_around: get_value(&mut config, "tupleType.spaceAround", space_around, &mut diagnostics),
while_statement_space_around: get_value(&mut config, "whileStatement.spaceAround", space_around, &mut diagnostics),
/* alignment - initialize with defaults */
alignment_enable_all: false,
variable_statement_align_assignments: false,
object_expression_align_properties: false,
interface_declaration_align_properties: false,
type_literal_align_properties: false,
class_declaration_align_properties: false,
enum_declaration_align_members: false,
module_declaration_align_properties: false,
};

// Get alignment.enableAll setting to use as default for individual alignment settings
let alignment_enable_all = get_value(&mut config, "alignment.enableAll", false, &mut diagnostics);

let resolved_config = Configuration {
/* alignment */
alignment_enable_all,
variable_statement_align_assignments: get_value(&mut config, "variableStatement.alignAssignments", alignment_enable_all, &mut diagnostics),
object_expression_align_properties: get_value(&mut config, "objectExpression.alignProperties", alignment_enable_all, &mut diagnostics),
interface_declaration_align_properties: get_value(&mut config, "interfaceDeclaration.alignProperties", alignment_enable_all, &mut diagnostics),
type_literal_align_properties: get_value(&mut config, "typeLiteral.alignProperties", alignment_enable_all, &mut diagnostics),
class_declaration_align_properties: get_value(&mut config, "classDeclaration.alignProperties", alignment_enable_all, &mut diagnostics),
enum_declaration_align_members: get_value(&mut config, "enumDeclaration.alignMembers", alignment_enable_all, &mut diagnostics),
module_declaration_align_properties: get_value(&mut config, "moduleDeclaration.alignProperties", alignment_enable_all, &mut diagnostics),
// Copy all the previous fields from resolved_config
..resolved_config
Comment on lines +348 to +361
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convenient way to enable all alignment, though I’m unsure if it’s right approach

};

diagnostics.extend(get_unknown_property_diagnostics(config));
Expand Down Expand Up @@ -410,4 +436,63 @@ mod tests {
assert_eq!(result.config.line_width, expected_config.line_width);
assert_eq!(result.diagnostics.len(), 0);
}

#[test]
fn handle_global_alignment_enable_all() {
let mut config = ConfigKeyMap::new();
config.insert(String::from("alignment.enableAll"), ConfigKeyValue::from_bool(true));
let global_config = GlobalConfiguration::default();
let result = resolve_config(config, &global_config);

// When alignment.enableAll is true, all individual alignment settings should be true by default
assert!(result.config.alignment_enable_all);
assert!(result.config.variable_statement_align_assignments);
assert!(result.config.object_expression_align_properties);
assert!(result.config.interface_declaration_align_properties);
assert!(result.config.type_literal_align_properties);
assert!(result.config.class_declaration_align_properties);
assert!(result.config.enum_declaration_align_members);
assert!(result.config.module_declaration_align_properties);
assert_eq!(result.diagnostics.len(), 0);
}

#[test]
fn handle_global_alignment_enable_all_with_individual_override() {
let mut config = ConfigKeyMap::new();
config.insert(String::from("alignment.enableAll"), ConfigKeyValue::from_bool(true));
// Override one individual setting to false
config.insert(String::from("variableStatement.alignAssignments"), ConfigKeyValue::from_bool(false));
let global_config = GlobalConfiguration::default();
let result = resolve_config(config, &global_config);

// alignment.enableAll is true but variableStatement.alignAssignments should be false due to override
assert!(result.config.alignment_enable_all);
assert!(!result.config.variable_statement_align_assignments); // overridden to false
assert!(result.config.object_expression_align_properties); // should still be true from global setting
assert!(result.config.interface_declaration_align_properties);
assert!(result.config.type_literal_align_properties);
assert!(result.config.class_declaration_align_properties);
assert!(result.config.enum_declaration_align_members);
assert!(result.config.module_declaration_align_properties);
assert_eq!(result.diagnostics.len(), 0);
}

#[test]
fn handle_alignment_defaults() {
let config = ConfigKeyMap::new();
// No alignment settings provided
let global_config = GlobalConfiguration::default();
let result = resolve_config(config, &global_config);

// All alignment settings should be false by default
assert!(!result.config.alignment_enable_all);
assert!(!result.config.variable_statement_align_assignments);
assert!(!result.config.object_expression_align_properties);
assert!(!result.config.interface_declaration_align_properties);
assert!(!result.config.type_literal_align_properties);
assert!(!result.config.class_declaration_align_properties);
assert!(!result.config.enum_declaration_align_members);
assert!(!result.config.module_declaration_align_properties);
assert_eq!(result.diagnostics.len(), 0);
}
}
17 changes: 17 additions & 0 deletions src/configuration/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,4 +662,21 @@ pub struct Configuration {
pub tuple_type_space_around: bool,
#[serde(rename = "whileStatement.spaceAround")]
pub while_statement_space_around: bool,
/* alignment */
#[serde(rename = "alignment.enableAll")]
pub alignment_enable_all: bool,
#[serde(rename = "variableStatement.alignAssignments")]
pub variable_statement_align_assignments: bool,
#[serde(rename = "objectExpression.alignProperties")]
pub object_expression_align_properties: bool,
#[serde(rename = "interfaceDeclaration.alignProperties")]
pub interface_declaration_align_properties: bool,
#[serde(rename = "typeLiteral.alignProperties")]
pub type_literal_align_properties: bool,
#[serde(rename = "classDeclaration.alignProperties")]
pub class_declaration_align_properties: bool,
#[serde(rename = "enumDeclaration.alignMembers")]
pub enum_declaration_align_members: bool,
#[serde(rename = "moduleDeclaration.alignProperties")]
pub module_declaration_align_properties: bool,
}
67 changes: 67 additions & 0 deletions src/generation/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ use dprint_core::formatting::LineStartIndentLevel;
use rustc_hash::FxHashMap;
use rustc_hash::FxHashSet;

/// Stores alignment information for variable statements and object expressions.
#[derive(Debug, Clone)]
pub struct AlignmentGroup {
/// Maximum width of the left-hand side for alignment calculation.
pub max_left_width: usize,
}

use deno_ast::SourceTextProvider;
use super::swc::is_text_valid_identifier;
use super::*;
use crate::configuration::*;
use crate::utils::Stack;
Expand Down Expand Up @@ -72,6 +81,8 @@ pub struct Context<'a> {
before_comments_start_info_stack: Stack<(SourceRange, LineNumber, IsStartOfLine)>,
if_stmt_last_brace_condition_ref: Option<ConditionReference>,
expr_stmt_single_line_parent_brace_ref: Option<ConditionReference>,
/// Stores alignment information for variable statements and object expressions.
pub alignment_groups: FxHashMap<SourceRange, AlignmentGroup>,
/// Used for ensuring nodes are parsed in order.
#[cfg(debug_assertions)]
pub last_generated_node_pos: SourcePos,
Expand Down Expand Up @@ -106,6 +117,7 @@ impl<'a> Context<'a> {
before_comments_start_info_stack: Default::default(),
if_stmt_last_brace_condition_ref: None,
expr_stmt_single_line_parent_brace_ref: None,
alignment_groups: Default::default(),
#[cfg(debug_assertions)]
last_generated_node_pos: deno_ast::SourceTextInfoProvider::text_info(&program).range().start.into(),
diagnostics: Vec::new(),
Expand Down Expand Up @@ -241,4 +253,59 @@ impl<'a> Context<'a> {
panic!("Debug Panic Expected text `{expected_text}`, but found `{actual_text}`")
}
}

/// Measures the display width of a node's text representation
pub fn measure_node_width(&self, node: Node) -> usize {
match node {
Node::Ident(ident) => ident.sym().chars().count(),
Node::BindingIdent(binding) => binding.id.sym().chars().count(),
Node::PrivateName(name) => name.name().chars().count() + 1, // +1 for the #
Node::Str(str_lit) => {
// For string keys in objects, measure the actual key length
if self.config.quote_props == QuoteProps::AsNeeded {
// Check if quotes are needed
let value = str_lit.value();
if is_text_valid_identifier(value) {
value.chars().count()
} else {
value.chars().count() + 2 // +2 for quotes
}
} else {
str_lit.value().chars().count() + 2 // +2 for quotes
}
}
Node::Number(num) => num.raw().as_ref().map(|r| r.chars().count()).unwrap_or(8), // fallback for numeric keys
Node::ComputedPropName(computed) => {
// For computed properties like [key]: value, measure [key]
2 + self.measure_node_width(computed.expr.as_node()) // +2 for brackets
}
Node::ArrayPat(array_pat) => {
// For array destructuring: [a, b, c] = ...
let elems_width: usize = array_pat.elems.iter()
.filter_map(|elem| elem.as_ref())
.map(|elem| self.measure_node_width(elem.as_node()))
.sum();
let separators_width = if array_pat.elems.len() > 1 { (array_pat.elems.len() - 1) * 2 } else { 0 }; // ", " between elements
2 + elems_width + separators_width // +2 for brackets
}
Node::ObjectPat(object_pat) => {
// For object destructuring: { a, b, c } = ...
let props_width: usize = object_pat.props.iter()
.map(|prop| match prop {
ObjectPatProp::Assign(assign) => assign.key.id.sym().chars().count(),
ObjectPatProp::KeyValue(kv) => self.measure_node_width(kv.key.as_node()),
ObjectPatProp::Rest(_) => 3, // "..."
})
.sum();
let separators_width = if object_pat.props.len() > 1 { (object_pat.props.len() - 1) * 2 } else { 0 }; // ", " between props
4 + props_width + separators_width // +4 for "{ " and " }"
}
_ => {
// Fallback: estimate based on node text span (less accurate but works)
let range = node.range();
let start_pos = self.program.start_pos();
(range.end.as_byte_index(start_pos) - range.start.as_byte_index(start_pos)).min(50) // cap at reasonable width
}
}
}
}
Loading