提交 5fb7d2aa 编写于 作者: L Leila Pearson

Compare full filenames

This change fixes issue #99955

When comparing file and folder names, after grouping  by type if applicable, compare the full name all at once instead of separately
comparing the name and the extension.

This change makes filename comparisons work the same as they
do in the Mac File Explorer and the same as a standard locale-based
sort with the numeric option (a form of natural sort) enabled.

Windows File Explorer has a different sort order due to treating the
dot character differently. Many terminal programs and sites like
github also differ, using a unicode-sort instead of a locale-based sort.
上级 4e180998
......@@ -33,8 +33,7 @@ const intlFileNameCollatorNumericCaseInsenstive: IdleValue<{ collator: Intl.Coll
return {
collator: collator
};
});
});/** Compares filenames without distinguishing the name from the extension. Disambiguates by unicode comparison. */
export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
const a = one || '';
const b = other || '';
......@@ -49,36 +48,16 @@ export function compareFileNames(one: string | null, other: string | null, caseS
return result;
}
/** Compares filenames by name then extension, sorting numbers numerically instead of alphabetically. */
export function compareFileNamesNumeric(one: string | null, other: string | null): number {
const [oneName, oneExtension] = extractNameAndExtension(one, true);
const [otherName, otherExtension] = extractNameAndExtension(other, true);
/** Compares filenames without distinguishing the name from the extension. Disambiguates by length, not unicode comparison. */
export function compareFileNamesDefault(one: string | null, other: string | null): number {
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;
}
one = one || '';
other = other || '';
// 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;
// Compare the entire filename - both name and extension - and disambiguate by length if needed
return compareAndDisambiguateByLength(collatorNumeric, one, other);
}
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
export function noIntlCompareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
if (!caseSensitive) {
one = one && one.toLowerCase();
......@@ -123,10 +102,12 @@ export function compareFileExtensions(one: string | null, other: string | null):
return result;
}
/** Compares filenames by extenson, then by name. Sorts numbers numerically, not alphabetically. */
export function compareFileExtensionsNumeric(one: string | null, other: string | null): number {
const [oneName, oneExtension] = extractNameAndExtension(one, true);
const [otherName, otherExtension] = extractNameAndExtension(other, true);
/** Compares filenames by extenson, then by full filename */
export function compareFileExtensionsDefault(one: string | null, other: string | null): number {
one = one || '';
other = other || '';
const oneExtension = extractExtension(one);
const otherExtension = extractExtension(other);
const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator;
let result;
......@@ -137,20 +118,12 @@ export function compareFileExtensionsNumeric(one: string | null, other: string |
return result;
}
// Compare names.
result = compareAndDisambiguateByLength(collatorNumeric, oneName, otherName);
if (result !== 0) {
return result;
}
// Disambiguate extension case if needed.
if (oneExtension !== otherExtension) {
return collatorNumeric.compare(oneExtension, otherExtension);
}
return 0;
// Compare full filenames
return compareAndDisambiguateByLength(collatorNumeric, one, other);
}
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
/** Extracts the name and extension from a full filename, with optional special handling for dotfiles */
function extractNameAndExtension(str?: string | null, dotfilesAsNames = false): [string, string] {
const match = str ? FileNameMatch.exec(str) as Array<string> : ([] as Array<string>);
......@@ -166,6 +139,13 @@ function extractNameAndExtension(str?: string | null, dotfilesAsNames = false):
return result;
}
/** Extracts the extension from a full filename. Treats dotfiles as names, not extensions. */
function extractExtension(str?: string | null): string {
const match = str ? FileNameMatch.exec(str) as Array<string> : ([] as Array<string>);
return (match && match[1] && match[1].charAt(0) !== '.' && match[3]) || '';
}
function compareAndDisambiguateByLength(collator: Intl.Collator, one: string, other: string) {
// Check for differences
let result = collator.compare(one, other);
......
......@@ -29,7 +29,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { equals, deepClone } from 'vs/base/common/objects';
import * as path from 'vs/base/common/path';
import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel';
import { compareFileExtensionsNumeric, compareFileNamesNumeric } from 'vs/base/common/comparers';
import { compareFileNamesDefault, compareFileExtensionsDefault } from 'vs/base/common/comparers';
import { fillResourceDataTransfers, CodeDataTransfers, extractResources, containsDragType } from 'vs/workbench/browser/dnd';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd';
......@@ -665,7 +665,7 @@ export class FileSorter implements ITreeSorter<ExplorerItem> {
}
if (statA.isDirectory && statB.isDirectory) {
return compareFileNamesNumeric(statA.name, statB.name);
return compareFileNamesDefault(statA.name, statB.name);
}
break;
......@@ -699,17 +699,17 @@ export class FileSorter implements ITreeSorter<ExplorerItem> {
// Sort Files
switch (sortOrder) {
case 'type':
return compareFileExtensionsNumeric(statA.name, statB.name);
return compareFileExtensionsDefault(statA.name, statB.name);
case 'modified':
if (statA.mtime !== statB.mtime) {
return (statA.mtime && statB.mtime && statA.mtime < statB.mtime) ? 1 : -1;
}
return compareFileNamesNumeric(statA.name, statB.name);
return compareFileNamesDefault(statA.name, statB.name);
default: /* 'default', 'mixed', 'filesFirst' */
return compareFileNamesNumeric(statA.name, statB.name);
return compareFileNamesDefault(statA.name, statB.name);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册