Skip to content

Commit e3ccaf1

Browse files
committed
change detection method to "isSorted" check
1 parent 8067f5f commit e3ccaf1

File tree

2 files changed

+54
-42
lines changed

2 files changed

+54
-42
lines changed

src/services/organizeImports.ts

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,29 @@ export function organizeImports(
9696
// All of the old ImportDeclarations in the file, in syntactic order.
9797
const topLevelImportGroupDecls = groupByNewlineContiguous(sourceFile, sourceFile.statements.filter(isImportDeclaration));
9898

99-
const DefaultComparer = getOrganizeImportsComparer(preferences, /*ignoreCase*/ true);
99+
const DefaultComparer = getOrganizeImportsComparer(preferences, /*ignoreCase*/ typeof preferences.organizeImportsIgnoreCase === "boolean" ? preferences.organizeImportsIgnoreCase : true);
100100

101-
let detectedModuleCaseComparer = DefaultComparer;
102-
let detectedNamedImportCaseComparer = (s1, s2) => compareImportOrExportSpecifiers(s1, s2, DefaultComparer, {organizeImportsTypeOrder: "last"});
103-
let detectedTypeOrder : typeof preferences.organizeImportsTypeOrder = "last";
101+
// if case sensitivity is specified (true/false), then use the same setting for both.
102+
let detectedModuleCaseComparer: Comparer<string> | undefined = typeof preferences.organizeImportsIgnoreCase === "boolean" ? DefaultComparer : undefined;
103+
let detectedNamedImportCaseComparer: Comparer<string> | undefined = detectedModuleCaseComparer;
104+
105+
let detectedTypeOrder : typeof preferences.organizeImportsTypeOrder = preferences.organizeImportsTypeOrder;
106+
107+
if (!detectedModuleCaseComparer || !detectedNamedImportCaseComparer || !detectedTypeOrder) {
108+
const { moduleSpecifierComparer, namedImportComparer, typeOrder } = getDetectionByDiff(topLevelImportGroupDecls, preferences)
109+
110+
detectedModuleCaseComparer = detectedModuleCaseComparer ?? moduleSpecifierComparer ?? DefaultComparer;
111+
detectedNamedImportCaseComparer = detectedNamedImportCaseComparer ?? namedImportComparer ?? DefaultComparer;
112+
detectedTypeOrder = detectedTypeOrder ?? typeOrder;
113+
// TODO return unset comparer
114+
}
104115

105116
topLevelImportGroupDecls.forEach(importGroupDecl => organizeImportsWorker(importGroupDecl));
106117

107118
// Exports are always used
108119
if (mode !== OrganizeImportsMode.RemoveUnused) {
109120
// All of the old ExportDeclarations in the file, in syntactic order.
110-
getTopLevelExportGroups(sourceFile).forEach(exportGroupDecl => organizeExportsWorker(exportGroupDecl));
121+
getTopLevelExportGroups(sourceFile).forEach(exportGroupDecl => organizeExportsWorker(exportGroupDecl, detectedNamedImportCaseComparer));
111122
}
112123

113124
for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) {
@@ -119,7 +130,7 @@ export function organizeImports(
119130
// Exports are always used
120131
if (mode !== OrganizeImportsMode.RemoveUnused) {
121132
const ambientModuleExportDecls = ambientModule.body.statements.filter(isExportDeclaration);
122-
organizeExportsWorker(ambientModuleExportDecls);
133+
organizeExportsWorker(ambientModuleExportDecls, detectedNamedImportCaseComparer);
123134
}
124135
}
125136

@@ -144,7 +155,7 @@ export function organizeImports(
144155
? group(oldImportDecls, importDecl => getExternalModuleName(importDecl.moduleSpecifier)!)
145156
: [oldImportDecls];
146157
const sortedImportGroups = shouldSort
147-
? stableSort(oldImportGroups, (group1, group2) => compareModuleSpecifiersWorker(group1[0].moduleSpecifier, group2[0].moduleSpecifier, detectedModuleCaseComparer))
158+
? stableSort(oldImportGroups, (group1, group2) => compareModuleSpecifiersWorker(group1[0].moduleSpecifier, group2[0].moduleSpecifier, detectedModuleCaseComparer ?? DefaultComparer))
148159
: oldImportGroups;
149160
const newImportDecls = flatMap(sortedImportGroups, importGroup =>
150161
getExternalModuleName(importGroup[0].moduleSpecifier) || importGroup[0].moduleSpecifier === undefined
@@ -175,30 +186,22 @@ export function organizeImports(
175186
}
176187
}
177188

178-
function organizeImportsWorker(oldImportDecls: readonly ImportDeclaration[], comparer?: Comparer<string>) {
179-
if (!comparer) {
180-
const { moduleSpecifierComparer, namedImportComparer, typeOrder } = getDetectionByDiff(topLevelImportGroupDecls, preferences)
181-
182-
detectedModuleCaseComparer = moduleSpecifierComparer ?? detectedModuleCaseComparer;
183-
detectedNamedImportCaseComparer = namedImportComparer ?? detectedNamedImportCaseComparer;
184-
detectedTypeOrder = typeOrder ?? detectedTypeOrder;
185-
// TODO return unset comparer
186-
}
187-
189+
function organizeImportsWorker(oldImportDecls: readonly ImportDeclaration[]) {
190+
const specifierComparer = getOrganizeImportsSpecifierComparer({organizeImportsTypeOrder: detectedTypeOrder ?? preferences.organizeImportsTypeOrder}, detectedNamedImportCaseComparer);
188191
const processImportsOfSameModuleSpecifier = (importGroup: readonly ImportDeclaration[]) => {
189192
if (shouldRemove) importGroup = removeUnusedImports(importGroup, sourceFile, program);
190-
if (shouldCombine) importGroup = coalesceImportsWorker(importGroup, detectedNamedImportCaseComparer, sourceFile, { organizeImportsTypeOrder: detectedTypeOrder });
191-
if (shouldSort) importGroup = stableSort(importGroup, (s1, s2) => compareImportsOrRequireStatements(s1, s2, detectedModuleCaseComparer));
193+
if (shouldCombine) importGroup = coalesceImportsWorker(importGroup, detectedModuleCaseComparer ?? DefaultComparer, specifierComparer, sourceFile);
194+
if (shouldSort) importGroup = stableSort(importGroup, (s1, s2) => compareImportsOrRequireStatements(s1, s2, detectedModuleCaseComparer ?? DefaultComparer));
192195
return importGroup;
193196
};
194197

195198
organizeDeclsWorker(oldImportDecls, processImportsOfSameModuleSpecifier);
196199
// return { moduleSpecifierComparer, namedImportComparer, typeOrder };
197200
}
198201

199-
function organizeExportsWorker(oldExportDecls: readonly ExportDeclaration[], comparer?: Comparer<string>) {
200-
const useComparer = comparer ?? DefaultComparer;
201-
organizeDeclsWorker(oldExportDecls, group => coalesceExportsWorker(group, useComparer, { organizeImportsTypeOrder: detectedTypeOrder ?? preferences.organizeImportsTypeOrder }))
202+
function organizeExportsWorker(oldExportDecls: readonly ExportDeclaration[], specifierCaseComparer?: Comparer<string>) {
203+
const useComparer = getOrganizeImportsSpecifierComparer(preferences, specifierCaseComparer);
204+
organizeDeclsWorker(oldExportDecls, group => coalesceExportsWorker(group, useComparer))
202205
}
203206
}
204207

@@ -341,10 +344,11 @@ function getExternalModuleName(specifier: Expression | undefined) {
341344
*/
342345
export function coalesceImports(importGroup: readonly ImportDeclaration[], ignoreCase: boolean, sourceFile?: SourceFile, preferences?: UserPreferences): readonly ImportDeclaration[] {
343346
const comparer = getOrganizeImportsOrdinalStringComparer(ignoreCase);
344-
return coalesceImportsWorker(importGroup, comparer, sourceFile, preferences);
347+
const specifierComparer = getOrganizeImportsSpecifierComparer({organizeImportsTypeOrder: preferences?.organizeImportsTypeOrder}, comparer);
348+
return coalesceImportsWorker(importGroup, comparer, specifierComparer, sourceFile);
345349
}
346350

347-
function coalesceImportsWorker(importGroup: readonly ImportDeclaration[], comparer: Comparer<string>, sourceFile?: SourceFile, preferences?: UserPreferences): readonly ImportDeclaration[] {
351+
function coalesceImportsWorker(importGroup: readonly ImportDeclaration[], comparer: Comparer<string>, specifierComparer: Comparer<ImportSpecifier>, sourceFile?: SourceFile): readonly ImportDeclaration[] {
348352
if (importGroup.length === 0) {
349353
return importGroup;
350354
}
@@ -417,7 +421,7 @@ function coalesceImportsWorker(importGroup: readonly ImportDeclaration[], compar
417421
newImportSpecifiers.push(...getNewImportSpecifiers(namedImports));
418422

419423
const sortedImportSpecifiers = factory.createNodeArray(
420-
sortSpecifiers(newImportSpecifiers, comparer, preferences),
424+
sortSpecifiers(newImportSpecifiers, specifierComparer),
421425
firstNamedImport?.importClause.namedBindings.elements.hasTrailingComma,
422426
);
423427

@@ -536,11 +540,12 @@ function getCategorizedImports(importGroup: readonly ImportDeclaration[]) {
536540
* @internal
537541
*/
538542
export function coalesceExports(exportGroup: readonly ExportDeclaration[], ignoreCase: boolean, preferences?: UserPreferences) {
539-
const comparer = getOrganizeImportsOrdinalStringComparer(ignoreCase);
540-
return coalesceExportsWorker(exportGroup, comparer, preferences);
543+
const comparer = (s1: ExportSpecifier, s2: ExportSpecifier) =>
544+
compareImportOrExportSpecifiers(s1, s2, getOrganizeImportsOrdinalStringComparer(ignoreCase), {organizeImportsTypeOrder: preferences?.organizeImportsTypeOrder ?? "last"});
545+
return coalesceExportsWorker(exportGroup, comparer);
541546
}
542547

543-
function coalesceExportsWorker(exportGroup: readonly ExportDeclaration[], comparer: Comparer<string>, preferences?: UserPreferences) {
548+
function coalesceExportsWorker(exportGroup: readonly ExportDeclaration[], specifierComparer: Comparer<ExportSpecifier>) {
544549
if (exportGroup.length === 0) {
545550
return exportGroup;
546551
}
@@ -559,7 +564,7 @@ function coalesceExportsWorker(exportGroup: readonly ExportDeclaration[], compar
559564
const newExportSpecifiers: ExportSpecifier[] = [];
560565
newExportSpecifiers.push(...flatMap(exportGroup, i => i.exportClause && isNamedExports(i.exportClause) ? i.exportClause.elements : emptyArray));
561566

562-
const sortedExportSpecifiers = sortSpecifiers(newExportSpecifiers, comparer, preferences);
567+
const sortedExportSpecifiers = sortSpecifiers(newExportSpecifiers, specifierComparer);
563568

564569
const exportDecl = exportGroup[0];
565570
coalescedExports.push(
@@ -626,8 +631,8 @@ function updateImportDeclarationAndClause(
626631
);
627632
}
628633

629-
function sortSpecifiers<T extends ImportOrExportSpecifier>(specifiers: readonly T[], comparer: Comparer<string>, preferences?: UserPreferences): readonly T[] {
630-
return stableSort(specifiers, (s1, s2) => compareImportOrExportSpecifiers(s1, s2, comparer, preferences));
634+
function sortSpecifiers<T extends ImportOrExportSpecifier>(specifiers: readonly T[], specifierComparer: Comparer<T>): readonly T[] {
635+
return stableSort(specifiers, specifierComparer);
631636
}
632637

633638
/** @internal */
@@ -642,6 +647,11 @@ export function compareImportOrExportSpecifiers<T extends ImportOrExportSpecifie
642647
}
643648
}
644649

650+
function getOrganizeImportsSpecifierComparer<T extends ImportOrExportSpecifier>(preferences: UserPreferences, comparer?: Comparer<string>): Comparer<T> {
651+
const stringComparer = comparer ?? getOrganizeImportsOrdinalStringComparer(!!preferences.organizeImportsIgnoreCase);
652+
return (s1, s2) => compareImportOrExportSpecifiers(s1, s2, stringComparer, preferences);
653+
}
654+
645655
/**
646656
* Exported for testing
647657
*
@@ -963,21 +973,24 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
963973
moduleSpecifierComparer: Comparer<string>;
964974
namedImportComparer?: Comparer<string>;
965975
typeOrder?: "first" | "last" | "inline";
966-
} = { moduleSpecifierComparer: getOrganizeImportsComparer(preferences, typeof preferences.organizeImportsIgnoreCase === "boolean" ? preferences.organizeImportsIgnoreCase : true) };
976+
} = {
977+
moduleSpecifierComparer: getOrganizeImportsComparer(preferences, typeof preferences.organizeImportsIgnoreCase === "boolean" ? preferences.organizeImportsIgnoreCase : true),
978+
typeOrder: preferences.organizeImportsTypeOrder,
979+
};
967980

968-
let comparers: Comparer<string>[];
981+
let comparersToTest: Comparer<string>[];
969982
if (typeof preferences.organizeImportsIgnoreCase === "boolean") {
970983
// both moduleSpecifier and namedImport comparer to the correct case-sensitivity.
971984
// does not yet exit because we still need to detect for type order
972985
comparer.moduleSpecifierComparer = getOrganizeImportsComparer(preferences, preferences.organizeImportsIgnoreCase);
973986
comparer.namedImportComparer = comparer.moduleSpecifierComparer;
974-
comparers = [comparer.moduleSpecifierComparer];
987+
comparersToTest = [comparer.moduleSpecifierComparer];
975988
}
976989
else {
977990
// otherwise, we must test for both case-sensitivity and later, type order
978991
const CASE_INSENSITIVE_COMPARER = getOrganizeImportsComparer(preferences, /*ignoreCase*/ true);
979992
const CASE_SENSITIVE_COMPARER = getOrganizeImportsComparer(preferences, /*ignoreCase*/ false);
980-
comparers = [CASE_INSENSITIVE_COMPARER, CASE_SENSITIVE_COMPARER];
993+
comparersToTest = [CASE_INSENSITIVE_COMPARER, CASE_SENSITIVE_COMPARER];
981994

982995
getModuleSpecifierNames(importDeclsByGroup, comparer, detectCaseSensitivityBySort);
983996
}
@@ -1018,7 +1031,7 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
10181031

10191032
const { namedImportComparer, typeOrder } = detectNamedImportOrganizationBySort(namedImportsByDecl);
10201033
comparer.namedImportComparer = namedImportComparer;
1021-
comparer.typeOrder = typeOrder;
1034+
comparer.typeOrder = comparer.typeOrder ?? typeOrder;
10221035

10231036
return comparer;
10241037

@@ -1037,7 +1050,7 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
10371050
let bestComparer;
10381051
let bestDiff = Infinity;
10391052

1040-
for (const curComparer of comparers) {
1053+
for (const curComparer of comparersToTest) {
10411054
let diffOfCurrentComparer = 0;
10421055

10431056
for (const listToSort of originalGroups) {
@@ -1053,7 +1066,7 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
10531066
bestComparer = curComparer;
10541067
}
10551068
}
1056-
return bestComparer ?? comparers[0];
1069+
return bestComparer ?? comparersToTest[0];
10571070
}
10581071

10591072
// interface NamedImportByDecl {
@@ -1080,9 +1093,9 @@ export function getDetectionByDiff(importDeclsByGroup: ImportDeclaration[][], pr
10801093
type TypeOrder = "first" | "last" | "inline";
10811094

10821095
const bestDiff = { first: Infinity, last: Infinity, inline: Infinity };
1083-
const bestComparer = { first: comparers[0], last: comparers[0], inline: comparers[0] };
1096+
const bestComparer = { first: comparersToTest[0], last: comparersToTest[0], inline: comparersToTest[0] };
10841097

1085-
for (const curComparer of comparers) {
1098+
for (const curComparer of comparersToTest) {
10861099
const currDiff = { first: 0, last: 0, inline: 0 };
10871100

10881101
for (const importDecl of originalGroups) {

tests/cases/fourslash/organizeImportsType9.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ console.log(a, b, A, B);`,
4040
/*mode*/ undefined,
4141
{ organizeImportsIgnoreCase: true });
4242

43-
// the imports are correctly sorted when ignoreCase=false and typeOrder=first
4443
edit.replaceLine(0, 'import { type a, type A, b, B } from "foo5";');
4544
verify.organizeImports(
46-
`import { type A, type a, B, b } from "foo5";
45+
`import { type A, B, type a, b } from "foo5";
4746
console.log(a, b, A, B);`,
4847
/*mode*/ undefined,
4948
{ organizeImportsIgnoreCase: false });

0 commit comments

Comments
 (0)