drasticp
drasticp

Reputation: 317

VSCode Folders Not Sorting Alphabetically

Visual Studio Code Explorer pane is sorting my folders oddly. I want to validate this is truly an issue before reporting it as a bug. I have vscode 1.47.2. I'm fairly sure this wasn't always an issue. Here's an example:

VSCode Explorer Screenshot

I would expect that a folder named "aaa.xxx.iii" would be alphabetically sorted before the folder named "aaa.yyy". In fact, when I look at the list in File Explorer, it is indeed sorted correctly.

I am not using a workspace file. I have searched the entire directory structure and I have no files suffixed with .code-workspace. I know this can be an issue in multi-root workspaces. However, I am just using "Open Folder" to open this solution.

I have also checked the settings under Workspace Settings, Features, Explorer, Sort and it is set to default (Alphabetic, Folders before Files). I tried changing to Modified sort order and back with no luck.

Upvotes: 1

Views: 2744

Answers (3)

aryzing
aryzing

Reputation: 5857

You may want to check out the "Sort Order Lexicographic Options" setting and set it to unicode to get the desired results.

Upvotes: 1

drasticp
drasticp

Reputation: 317

It appears this is an outstanding issue. I changed my search terms on GitHub and found the original discussion. It seems there's no resolution as of this comment.

https://github.com/microsoft/vscode/issues/99955

Upvotes: 0

soulshined
soulshined

Reputation: 10592

I would expect that a folder named "aaa.xxx.iii" would be alphabetically sorted before the folder named "aaa.yyy"

It is alphabetically sorted, just ASCII sorted!

ASCII values:

a = 97
b = 98
i = 105
x = 120
y = 121
. = 46

Therefore:

aaa         = 291
aaa.xxx     = 697
aaa.yyy     = 700
aaa.xxx.iii = 1,012

However, there seems to be some variation to their logic. They actually split filenames via a filename regex (and they use the same logic for comparing directories and filenames). They effectively compare against a filename first, before even considering the extension using the following regex:

const FileNameMatch = /^(.*?)(\.([^.]*))?$/;

And then it will only consider extensions when it returns 0, for not greater than or less than.

Using that regex: in your example, aaa.xxx, aaa is the filename, .xxx is the 'extension`.

With aaa.yyy; aaa is the filename and compared against aaa.xxx.iii, you get it .iii is the extension. Ergo, aaa.yyy, or the name without the .yyy extension: aaa < aaa.xxx or aaa.xxx.iii with the extension

Here is their logic:

explorerViewer.ts

comparers.ts:

export function compareFileNamesNumeric(one: string | null, other: string | null): number {
    const [oneName, oneExtension] = extractNameAndExtension(one, true);
    const [otherName, otherExtension] = extractNameAndExtension(other, true);
    const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
    const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator;
    let result;

    // Check for name differences, comparing numbers numerically instead of alphabetically.
    result = compareAndDisambiguateByLength(collatorNumeric, oneName, otherName);
    if (result !== 0) {
        return result;
    }

    // Check for case insensitive extension differences, comparing numbers numerically instead of alphabetically.
    result = compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension);
    if (result !== 0) {
        return result;
    }

    // Disambiguate the extension case if needed.
    if (oneExtension !== otherExtension) {
        return collatorNumeric.compare(oneExtension, otherExtension);
    }

    return 0;
}

So it appears, for the most part, they are just using Intl.Collator with basic name value a < b logic, with some variations of course.

It also appears they disambiguate by length, meaning foo1 and foo01 are considered equal. line 169


Settings

The setting you described only affects 'how they are displayed', in a way sorted yes, but mostly how they are shown to the user, not really how they are sorted programmatically as I interpret it.

// Controls sorting order of files and folders in the explorer.

// - default: Files and folders are sorted by their names, in alphabetical order. Folders are displayed before files.

// - mixed: Files and folders are sorted by their names, in alphabetical order. Files are interwoven with folders.

// - filesFirst: Files and folders are sorted by their names, in alphabetical order. Files are displayed before folders.

// - type: Files and folders are sorted by their extensions, in alphabetical order. Folders are displayed before files.

// - modified: Files and folders are sorted by last modified date, in descending order. Folders are displayed before files.

"explorer.sortOrder": "default",

So it's more of a presentation setting for files/directories not the names themselves. If you look at the explorerViewer.ts control flow you will see:

  switch (sortOrder) {
    case 'type':
    if (statA.isDirectory && !statB.isDirectory) {
        return -1;
    }

    if (statB.isDirectory && !statA.isDirectory) {
        return 1;
    }

    if (statA.isDirectory && statB.isDirectory) {
        return compareFileNamesNumeric(statA.name, statB.name);
    }

    break;

And now we can visualize going back to how directories are sorted with the same function; re if (statA.isDirectory && statB.isDirectory)

Upvotes: 1

Related Questions