Skip to content

Add option for organize imports case sensitivity - #51733

Merged
Andrew Branch (andrewbranch) merged 10 commits into
microsoft:mainfrom
andrewbranch:bug/organize-imports-case-sensitivity
Dec 13, 2022
Merged

Add option for organize imports case sensitivity#51733
Andrew Branch (andrewbranch) merged 10 commits into
microsoft:mainfrom
andrewbranch:bug/organize-imports-case-sensitivity

Conversation

@andrewbranch

Copy link
Copy Markdown
Member

Fixes #51616
Related: #51579

A new user preference (editor PR to follow) allows setting the case sensitivity for import sorting. By default, we detect.

@jakebailey

Copy link
Copy Markdown
Member

I'm finding it hard to deduce from the tests, but what is the behavior when you have both HasDecorator and hasDecorator, in various orders? Does it always output the same thing?

This is something that frustrates me currently about the eslint plugin; it doesn't care which order you have, which leaves a hole where we can end up with conflicts when the result should not depend on the input ordering at all.

@andrewbranch

Andrew Branch (andrewbranch) commented Dec 6, 2022

Copy link
Copy Markdown
Member Author

Currently, case-insensitive is truly case-insensitive like eslint, which means any order between specifiers that differ only in case is allowed, and a reshuffle will preserve the pre-existing relative order (eslint doesn’t use a stable sort, which means pre-existing order between case-different specifiers is not preserved, resulting in sorts that look totally random). This is something we can definitely change or improve on over the eslint behavior, but if we change back to case sensitive sorting, I wasn’t sure if anyone would end up caring about it.

/** @internal */
export function getImportDeclarationInsertionIndex(sortedImports: SortedReadonlyArray<AnyImportOrRequireStatement>, newImport: AnyImportOrRequireStatement) {
const index = binarySearch(sortedImports, newImport, identity, compareImportsOrRequireStatements);
export const detectImportSpecifierSorting = memoizeWeak((specifiers: readonly ImportSpecifier[]): SortKind => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a particular reason why this one is cached but others aren't? Would everything benefit from being cached like this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As it stands now, this is the only one that benefits from being cached. It gets called several times from auto-imports in ways that are cumbersome to cache locally.

Comment thread src/compiler/core.ts
Comment thread src/compiler/core.ts
let prevElement = array[0];
for (const element of array.slice(1)) {
if (comparer(prevElement, element) === Comparison.GreaterThan) {
for (let i = 1, len = array.length; i < len; i++) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:yikes: nice catch

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Daniel Rosenwasser (@DanielRosenwasser) noticed this while I was screensharing working on this. Turns out I wrote it years ago, before I was terrified of allocations 🙈

Comment thread src/compiler/core.ts Outdated
for (let i = 1, len = array.length; i < len; i++) {
const prevElement = array[i - 1];
const element = array[i];
if (caseSensitiveComparer(prevElement, element) === Comparison.GreaterThan) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be more obvious to me what was going on if this was something like !== Comparison.LessThanOrEqualTo