Skip to content

Commit

Permalink
add inner use sort to formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
dean-starkware committed Oct 9, 2024
1 parent 0b80bfc commit 0e5b001
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 32 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

222 changes: 193 additions & 29 deletions crates/cairo-lang-formatter/src/formatter_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::fmt;
use cairo_lang_filesystem::span::TextWidth;
use cairo_lang_syntax as syntax;
use cairo_lang_syntax::attribute::consts::FMT_SKIP_ATTR;
use cairo_lang_syntax::node::ast::PathSegment;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{ast, SyntaxNode, Terminal, TypedSyntaxNode};
use itertools::Itertools;
Expand Down Expand Up @@ -826,43 +827,113 @@ impl<'a> FormatterImpl<'a> {
// TODO(ilya): consider not copying here.
let mut children = self.db.get_children(syntax_node.clone()).to_vec();
let n_children = children.len();

if self.config.sort_module_level_items {
let mut start_idx = 0;
while start_idx < children.len() {
let kind = children[start_idx].as_sort_kind(self.db);
let mut end_idx = start_idx + 1;
// Find the end of the current section.
while end_idx < children.len() {
if kind != children[end_idx].as_sort_kind(self.db) {
break;
if let SyntaxKind::UsePathList = syntax_node.kind(self.db) {
self.sort_inner_use_path(&mut children);
} else {
self.sort_items_sections(&mut children);
}

/*
// Filter and collect only UsePathLeaf and UsePathSingle, while excluding
// TokenComma.
let mut sorted_leaf_and_single: Vec<_> = children
.iter()
.filter(|node| {
matches!(
node.kind(self.db),
SyntaxKind::UsePathLeaf | SyntaxKind::UsePathSingle
)
})
.cloned()
.collect();
// Sort the filtered nodes by their ident (text).
sorted_leaf_and_single.sort_by_key(|node| {
match node.kind(self.db) {
SyntaxKind::UsePathLeaf | SyntaxKind::UsePathSingle => {
let path_segment =
ast::UsePathLeaf::from_syntax_node(self.db, node.clone())
.ident(self.db);
match path_segment {
PathSegment::Simple(simple_segment) => {
simple_segment.as_syntax_node().get_text_without_trivia(self.db)
}
PathSegment::WithGenericArgs(_) => {
// Handle the case with generic arguments if needed (e.g.,
// extract base name).
"".to_string()
}
}
}
_ => "".to_string(), // Default case (shouldn't happen).
}
end_idx += 1;
});
// Filter and collect the other node types (like commas).
let other_types: Vec<_> = children
.iter()
.filter(|node| {
!matches!(
node.kind(self.db),
SyntaxKind::UsePathLeaf | SyntaxKind::UsePathSingle
)
})
.cloned()
.collect();
// Combine the sorted UsePathLeaf/UsePathSingle nodes with the other node types.
children = sorted_leaf_and_single
.clone()
.into_iter()
.zip(other_types)
.flat_map(|(a, b)| vec![a, b])
.collect();
// Push the last remaining element from the sorted_leaf_and_single (we always have
// one less comma than the number of UsePathLeaf/UsePathSingle).
if let Some(last) = sorted_leaf_and_single.last() {
children.push(last.clone());
}
// Sort within this section if it's `Module` or `UseItem`.
match kind {
SortKind::Module => {
children[start_idx..end_idx].sort_by_key(|node| {
ast::ItemModule::from_syntax_node(self.db, node.clone())
.name(self.db)
.text(self.db)
});
} else {
// We sort sections of the items in the module.
let mut start_idx = 0;
while start_idx < children.len() {
let kind = children[start_idx].as_sort_kind(self.db);
let mut end_idx = start_idx + 1;
// Find the end of the current section.
while end_idx < children.len() {
if kind != children[end_idx].as_sort_kind(self.db) {
break;
}
end_idx += 1;
}
SortKind::UseItem => {
children[start_idx..end_idx].sort_by_key(|node| {
ast::ItemUse::from_syntax_node(self.db, node.clone())
.use_path(self.db)
.as_syntax_node()
.get_text_without_trivia(self.db)
});
// Sort within this section if it's `Module` or `UseItem`.
match kind {
SortKind::Module => {
children[start_idx..end_idx].sort_by_key(|node| {
ast::ItemModule::from_syntax_node(self.db, node.clone())
.name(self.db)
.text(self.db)
});
}
SortKind::UseItem => {
children[start_idx..end_idx].sort_by_key(|node| {
ast::ItemUse::from_syntax_node(self.db, node.clone())
.use_path(self.db)
.as_syntax_node()
.get_text_without_trivia(self.db)
});
}
SortKind::Immovable => {}
}
SortKind::Immovable => {}
}
// Move past the sorted section.
start_idx = end_idx;
// Move past the sorted section.
start_idx = end_idx;
}
}
}
*/
}
for (i, child) in children.iter().enumerate() {
if child.width(self.db) == TextWidth::default() {
continue;
Expand All @@ -878,6 +949,99 @@ impl<'a> FormatterImpl<'a> {
self.empty_lines_allowance = allowed_empty_between;
}
}
// Sorting function for UsePathMulti
fn sort_inner_use_path(&self, children: &mut Vec<SyntaxNode>) {
// Filter and collect only UsePathLeaf and UsePathSingle, while excluding TokenComma.
let mut sorted_leaf_and_single: Vec<_> = children
.iter()
.filter(|node| {
matches!(node.kind(self.db), SyntaxKind::UsePathLeaf | SyntaxKind::UsePathSingle)
})
.cloned()
.collect();

// Sort the filtered nodes by their ident (text).
sorted_leaf_and_single.sort_by_key(|node| {
match node.kind(self.db) {
SyntaxKind::UsePathLeaf | SyntaxKind::UsePathSingle => {
let path_segment =
ast::UsePathLeaf::from_syntax_node(self.db, node.clone()).ident(self.db);
match path_segment {
PathSegment::Simple(simple_segment) => {
simple_segment.as_syntax_node().get_text_without_trivia(self.db)
}
PathSegment::WithGenericArgs(_) => {
// Handle the case with generic arguments if needed (e.g., extract base
// name).
"".to_string()
}
}
}
_ => "".to_string(), // Default case (shouldn't happen).
}
});

// Filter and collect the other node types (like commas).
let other_types: Vec<_> = children
.iter()
.filter(|node| {
!matches!(node.kind(self.db), SyntaxKind::UsePathLeaf | SyntaxKind::UsePathSingle)
})
.cloned()
.collect();

// Combine the sorted UsePathLeaf/UsePathSingle nodes with the other node types.
*children = sorted_leaf_and_single
.clone()
.into_iter()
.zip(other_types)
.flat_map(|(a, b)| vec![a, b])
.collect();

// Push the last remaining element from the sorted_leaf_and_single (we always have one less
// comma than the number of UsePathLeaf/UsePathSingle).
if let Some(last) = sorted_leaf_and_single.last() {
children.push(last.clone());
}
}

// Sorting function for module-level items
fn sort_items_sections(&self, children: &mut Vec<SyntaxNode>) {
let mut start_idx = 0;
while start_idx < children.len() {
let kind = children[start_idx].as_sort_kind(self.db);
let mut end_idx = start_idx + 1;
// Find the end of the current section.
while end_idx < children.len() {
if kind != children[end_idx].as_sort_kind(self.db) {
break;
}
end_idx += 1;
}
// Sort within this section if it's `Module` or `UseItem`.
match kind {
SortKind::Module => {
children[start_idx..end_idx].sort_by_key(|node| {
ast::ItemModule::from_syntax_node(self.db, node.clone())
.name(self.db)
.text(self.db)
});
}
SortKind::UseItem => {
children[start_idx..end_idx].sort_by_key(|node| {
ast::ItemUse::from_syntax_node(self.db, node.clone())
.use_path(self.db)
.as_syntax_node()
.get_text_without_trivia(self.db)
});
}
SortKind::Immovable => {}
}

// Move past the sorted section.
start_idx = end_idx;
}
}
/// Formats a terminal node and appends the formatted string to the result.
fn format_terminal(&mut self, syntax_node: &SyntaxNode) {
// TODO(spapini): Introduce a Terminal and a Token enum in ast.rs to make this cleaner.
Expand Down
5 changes: 5 additions & 0 deletions crates/cairo-lang-formatter/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ impl Upcast<dyn FilesGroup> for DatabaseImpl {
"test_data/expected_results/sorted_mod_use.cairo",
true
)]
#[test_case(
"test_data/cairo_files/sort_inner_use.cairo",
"test_data/expected_results/sort_inner_use.cairo",
true
)]
fn format_and_compare_file(unformatted_filename: &str, expected_filename: &str, use_sorting: bool) {
let db_val = SimpleParserDatabase::default();
let db = &db_val;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use std::collections::HashMap;
use crate::utils::{a, b, d, c};
use b::{d, c, b, a};
use a::{d, b, a, c};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use a::{a, b, c, d};
use b::{a, b, c, d};
use crate::utils::{a, b, c, d};
use std::collections::HashMap;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod SRC5 {
//! Header comment, should not be moved by the formatter.
/// Doc comment, should be moved by the formatter.
use openzeppelin::introspection::interface;
use openzeppelin::introspection::{interface, AB};
use openzeppelin::introspection::{AB, interface};

#[storage]
struct Storage {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ pub fn completion_for_method(

let completion = CompletionItem {
label: format!("{}()", name),
insert_text: Some(format!("{}(", name)),
insert_text: Some(format!("{}($0)", name)),
insert_text_format: Some(tower_lsp::lsp_types::InsertTextFormat::SNIPPET),
detail: Some(detail),
kind: Some(CompletionItemKind::METHOD),
additional_text_edits: Some(additional_text_edits),
Expand Down
3 changes: 3 additions & 0 deletions crates/cairo-lang-language-server/tests/e2e/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ fn test_completions_text_edits(
if let Some(text_edit) = completion.additional_text_edits {
report.push_str("--------------------------\n");
report.push_str(format!("Completion: {}\n", completion.label).as_str());
if let Some(text) = completion.insert_text {
report.push_str(format!("Insert text: {text}\n").as_str());
}
for edit in text_edit {
report.push_str(format!("Text edit: {}", edit.new_text).as_str());
}
Expand Down

0 comments on commit 0e5b001

Please sign in to comment.