Skip to content

Commit b73f859

Browse files
committed
feat(formatter/sort-imports): Implement options.ignoreCase: bool
1 parent a1b2d2b commit b73f859

File tree

4 files changed

+174
-7
lines changed

4 files changed

+174
-7
lines changed

crates/oxc_formatter/examples/sort_imports.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ fn main() -> Result<(), String> {
1818
let partition_by_comment = args.contains("--partition_by_comment");
1919
let sort_side_effects = args.contains("--sort_side_effects");
2020
let order = args.opt_value_from_str("--order").unwrap_or(None).unwrap_or(SortOrder::Asc);
21+
let ignore_case = !args.contains("--no_ignore_case");
2122

22-
let sort_imports_options =
23-
SortImports { order, partition_by_newline, partition_by_comment, sort_side_effects };
23+
let sort_imports_options = SortImports {
24+
order,
25+
partition_by_newline,
26+
partition_by_comment,
27+
sort_side_effects,
28+
ignore_case,
29+
};
2430

2531
// Read source file
2632
let path = Path::new(&name);

crates/oxc_formatter/src/ir_transform/sort_imports.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::ops::Range;
22

3+
use cow_utils::CowUtils;
4+
35
use crate::{
46
JsLabels,
57
formatter::format_element::{
@@ -434,7 +436,7 @@ impl IntoIterator for ImportUnits {
434436

435437
impl ImportUnits {
436438
// TODO: Sort based on `options.groups`, `options.type`, etc...
437-
// TODO: Consider `options.ignore_case`, `special_characters`, removing `?raw`, etc...
439+
// TODO: Consider `special_characters`, removing `?raw`, etc...
438440
fn sort_imports(&mut self, elements: &[FormatElement], options: options::SortImports) {
439441
let imports_len = self.0.len();
440442

@@ -458,7 +460,15 @@ impl ImportUnits {
458460

459461
// Sort indices by comparing their corresponding import sources
460462
sortable_indices.sort_by(|&a, &b| {
461-
let ord = self.0[a].get_source(elements).cmp(self.0[b].get_source(elements));
463+
let source_a = self.0[a].get_source(elements);
464+
let source_b = self.0[b].get_source(elements);
465+
466+
let ord = if options.ignore_case {
467+
source_a.cow_to_lowercase().cmp(&source_b.cow_to_lowercase())
468+
} else {
469+
source_a.cmp(source_b)
470+
};
471+
462472
if options.order.is_desc() { ord.reverse() } else { ord }
463473
});
464474

crates/oxc_formatter/src/options.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -917,16 +917,35 @@ impl fmt::Display for OperatorPosition {
917917

918918
// ---
919919

920-
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
920+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
921921
pub struct SortImports {
922-
/// Sort order (asc or desc).
923-
pub order: SortOrder,
924922
/// Partition imports by newlines.
923+
/// Default is `false`.
925924
pub partition_by_newline: bool,
926925
/// Partition imports by comments.
926+
/// Default is `false`.
927927
pub partition_by_comment: bool,
928928
/// Sort side effects imports.
929+
/// Default is `false`.
929930
pub sort_side_effects: bool,
931+
/// Sort order (asc or desc).
932+
/// Default is ascending (asc).
933+
pub order: SortOrder,
934+
/// Ignore case when sorting.
935+
/// Default is `true`.
936+
pub ignore_case: bool,
937+
}
938+
939+
impl Default for SortImports {
940+
fn default() -> Self {
941+
Self {
942+
partition_by_newline: false,
943+
partition_by_comment: false,
944+
sort_side_effects: false,
945+
order: SortOrder::default(),
946+
ignore_case: true,
947+
}
948+
}
930949
}
931950

932951
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]

crates/oxc_formatter/tests/ir_transform/sort_imports.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,24 @@ import { log } from "./log";
704704
import { log10 } from "./log10";
705705
import { log1p } from "./log1p";
706706
import { log2 } from "./log2";
707+
"#,
708+
);
709+
assert_format(
710+
r#"
711+
import { log } from "./log";
712+
import { log10 } from "./log10";
713+
import { log1p } from "./log1p";
714+
import { log2 } from "./log2";
715+
"#,
716+
&FormatOptions {
717+
experimental_sort_imports: Some(SortImports::default()),
718+
..Default::default()
719+
},
720+
r#"
721+
import { log } from "./log";
722+
import { log10 } from "./log10";
723+
import { log1p } from "./log1p";
724+
import { log2 } from "./log2";
707725
"#,
708726
);
709727
}
@@ -818,3 +836,117 @@ import "c";
818836
"#,
819837
);
820838
}
839+
840+
// ---
841+
842+
#[test]
843+
fn should_sort_with_ignore_case_option() {
844+
// Case-insensitive (ignore_case: true by default)
845+
assert_format(
846+
r#"
847+
import { A } from "a";
848+
import { b } from "B";
849+
"#,
850+
&FormatOptions {
851+
experimental_sort_imports: Some(SortImports::default()),
852+
..Default::default()
853+
},
854+
r#"
855+
import { A } from "a";
856+
import { b } from "B";
857+
"#,
858+
);
859+
// "a" and "A" are treated as the same, maintaining original order
860+
assert_format(
861+
r#"
862+
import x from "A";
863+
import y from "a";
864+
"#,
865+
&FormatOptions {
866+
experimental_sort_imports: Some(SortImports::default()),
867+
..Default::default()
868+
},
869+
r#"
870+
import x from "A";
871+
import y from "a";
872+
"#,
873+
);
874+
// Mixed case sorting with ignore_case: true
875+
assert_format(
876+
r#"
877+
import { z } from "Z";
878+
import { b } from "B";
879+
import { a } from "a";
880+
"#,
881+
&FormatOptions {
882+
experimental_sort_imports: Some(SortImports {
883+
ignore_case: true,
884+
..Default::default()
885+
}),
886+
..Default::default()
887+
},
888+
r#"
889+
import { a } from "a";
890+
import { b } from "B";
891+
import { z } from "Z";
892+
"#,
893+
);
894+
// Case-sensitive, lowercase comes after uppercase in ASCII
895+
assert_format(
896+
r#"
897+
import { a } from "a";
898+
import { B } from "B";
899+
"#,
900+
&FormatOptions {
901+
experimental_sort_imports: Some(SortImports {
902+
ignore_case: false,
903+
..Default::default()
904+
}),
905+
..Default::default()
906+
},
907+
r#"
908+
import { B } from "B";
909+
import { a } from "a";
910+
"#,
911+
);
912+
// Capital A vs lowercase a
913+
assert_format(
914+
r#"
915+
import x from "a";
916+
import y from "A";
917+
"#,
918+
&FormatOptions {
919+
experimental_sort_imports: Some(SortImports {
920+
ignore_case: false,
921+
..Default::default()
922+
}),
923+
..Default::default()
924+
},
925+
r#"
926+
import y from "A";
927+
import x from "a";
928+
"#,
929+
);
930+
// Multiple imports with mixed case
931+
assert_format(
932+
r#"
933+
import { z } from "z";
934+
import { B } from "B";
935+
import { a } from "a";
936+
import { Z } from "Z";
937+
"#,
938+
&FormatOptions {
939+
experimental_sort_imports: Some(SortImports {
940+
ignore_case: false,
941+
..Default::default()
942+
}),
943+
..Default::default()
944+
},
945+
r#"
946+
import { B } from "B";
947+
import { Z } from "Z";
948+
import { a } from "a";
949+
import { z } from "z";
950+
"#,
951+
);
952+
}

0 commit comments

Comments
 (0)