-
Notifications
You must be signed in to change notification settings - Fork 28.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Various kinds of sorting in explorer #29509
Changes from 12 commits
a3085ff
410f89d
40907c3
4f68100
4835047
b909230
794fcb0
968483b
81c8bfa
efb17ac
12ed0dd
66f978b
171ae31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr | |
import { IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, EditorInput, IFileEditorInput } from 'vs/workbench/common/editor'; | ||
import { AutoSaveConfiguration, HotExitConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files'; | ||
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; | ||
import { FILE_EDITOR_INPUT_ID, VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; | ||
import { FILE_EDITOR_INPUT_ID, VIEWLET_ID, SortOrderConfiguration } from 'vs/workbench/parts/files/common/files'; | ||
import { FileEditorTracker } from 'vs/workbench/parts/files/common/editors/fileEditorTracker'; | ||
import { SaveErrorHandler } from 'vs/workbench/parts/files/browser/saveErrorHandler'; | ||
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; | ||
|
@@ -32,6 +32,7 @@ import * as platform from 'vs/base/common/platform'; | |
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; | ||
import { DirtyFilesTracker } from 'vs/workbench/parts/files/common/dirtyFilesTracker'; | ||
|
||
|
||
// Viewlet Action | ||
export class OpenExplorerViewletAction extends ToggleViewletAction { | ||
public static ID = VIEWLET_ID; | ||
|
@@ -321,6 +322,19 @@ configurationRegistry.registerConfiguration({ | |
'type': 'boolean', | ||
'description': nls.localize('enableDragAndDrop', "Controls if the explorer should allow to move files and folders via drag and drop."), | ||
'default': true | ||
}, | ||
'explorer.sortOrder': { | ||
'type': 'string', | ||
'enum': [SortOrderConfiguration.DEFAULT, SortOrderConfiguration.MIXED, SortOrderConfiguration.FILES_FIRST, SortOrderConfiguration.TYPE, SortOrderConfiguration.MODIFIED], | ||
'default': SortOrderConfiguration.DEFAULT, | ||
'enumDescriptions': [ | ||
nls.localize('sortOrder.default', 'Files and directories are sorted by their names, in alphabetical order. Directories are displayed before files.'), | ||
nls.localize('sortOrder.mixed', 'Files and directories are sorted by their names, in alphabetical order. Files are interwoven with directories.'), | ||
nls.localize('sortOrder.filesFirst', 'Files and directories are sorted by their names, in alphabetical order. Files are displayed before directories.'), | ||
nls.localize('sortOrder.type', 'Files and directories are sorted by their extensions, in alphabetical order. Directories are displayed before files.'), | ||
nls.localize('sortOrder.modified', 'Files and directories are sorted by last modified date, in descending order. Directories are displayed before files.') | ||
], | ||
'description': nls.localize('sortOrder', "Controls the way of sorting files and directories.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest: |
||
} | ||
} | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ import glob = require('vs/base/common/glob'); | |
import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; | ||
import { IDisposable } from 'vs/base/common/lifecycle'; | ||
import { ContributableActionProvider } from 'vs/workbench/browser/actions'; | ||
import { IFilesConfiguration } from 'vs/workbench/parts/files/common/files'; | ||
import { IFilesConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; | ||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; | ||
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; | ||
import { ResourceMap } from 'vs/base/common/map'; | ||
|
@@ -526,20 +526,53 @@ export class FileController extends DefaultController { | |
|
||
// Explorer Sorter | ||
export class FileSorter implements ISorter { | ||
private toDispose: IDisposable[]; | ||
private sortOrder: SortOrder; | ||
|
||
constructor( | ||
@IConfigurationService private configurationService: IConfigurationService | ||
) { | ||
this.toDispose = []; | ||
|
||
this.onConfigurationUpdated(configurationService.getConfiguration<IFilesConfiguration>()); | ||
|
||
this.registerListeners(); | ||
} | ||
|
||
private registerListeners(): void { | ||
this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IFilesConfiguration>()))); | ||
} | ||
|
||
private onConfigurationUpdated(configuration: IFilesConfiguration): void { | ||
this.sortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; | ||
} | ||
|
||
public compare(tree: ITree, statA: FileStat, statB: FileStat): number { | ||
if (statA.isDirectory && !statB.isDirectory) { | ||
return -1; | ||
} | ||
switch (this.sortOrder) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I appreciate that we now can do this all from a single compare implementation, that is very cool. However, I am having some difficulties that we have the
I know that having a single switch statement might mean some duplication of sorting logic, but I think it would still be easier to understand. We can still in that model extract logic into methods if we wanted to share. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the order matters here. If I do this, it will affect the position of the new file/dir placeholder. It will appear at the very top of the current dir, not after all subdirs as currently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To implement the approach you described and keep current behavior of placeholders, the code may look like this: public compare(tree: ITree, statA: FileStat, statB: FileStat): number {
// Do not sort roots
if (statA.isRoot) {
return -1;
}
if (statB.isRoot) {
return 1;
}
switch (this.sortOrder) {
case 'default':
if (statA.isDirectory && !statB.isDirectory) {
return -1;
}
if (statB.isDirectory && !statA.isDirectory) {
return 1;
}
if (statA instanceof NewStatPlaceholder) {
return -1;
}
if (statB instanceof NewStatPlaceholder) {
return 1;
}
return comparers.compareFileNames(statA.name, statB.name);
case 'type':
if (statA.isDirectory && !statB.isDirectory) {
return -1;
}
if (statB.isDirectory && !statA.isDirectory) {
return 1;
}
if (statA instanceof NewStatPlaceholder) {
return -1;
}
if (statB instanceof NewStatPlaceholder) {
return 1;
}
return comparers.compareFileExtensions(statA.name, statB.name);
case 'modified':
if (statA.isDirectory && !statB.isDirectory) {
return -1;
}
if (statB.isDirectory && !statA.isDirectory) {
return 1;
}
if (statA instanceof NewStatPlaceholder) {
return -1;
}
if (statB instanceof NewStatPlaceholder) {
return 1;
}
if (statA.mtime !== statB.mtime) {
return statA.mtime < statB.mtime ? 1 : -1;
} else {
return comparers.compareFileNames(statA.name, statB.name);
}
case 'filesFirst':
if (statA.isDirectory && !statB.isDirectory) {
return 1;
}
if (statB.isDirectory && !statA.isDirectory) {
return -1;
}
if (statA instanceof NewStatPlaceholder) {
return -1;
}
if (statB instanceof NewStatPlaceholder) {
return 1;
}
return comparers.compareFileNames(statA.name, statB.name);
case 'mixed':
if (statA instanceof NewStatPlaceholder) {
return -1;
}
if (statB instanceof NewStatPlaceholder) {
return 1;
}
return comparers.compareFileNames(statA.name, statB.name);
}
} Yeah, it looks more verbose but definitely less confusing. Should I commit this? |
||
case 'default': | ||
case 'type': | ||
case 'modified': | ||
if (statA.isDirectory && !statB.isDirectory) { | ||
return -1; | ||
} | ||
if (statB.isDirectory && !statA.isDirectory) { | ||
return 1; | ||
} | ||
break; | ||
|
||
if (statB.isDirectory && !statA.isDirectory) { | ||
return 1; | ||
case 'filesFirst': | ||
if (statA.isDirectory && !statB.isDirectory) { | ||
return 1; | ||
} | ||
if (statB.isDirectory && !statA.isDirectory) { | ||
return -1; | ||
} | ||
break; | ||
} | ||
|
||
if (statA instanceof NewStatPlaceholder) { | ||
return -1; | ||
} | ||
|
||
if (statB instanceof NewStatPlaceholder) { | ||
return 1; | ||
} | ||
|
@@ -548,12 +581,26 @@ export class FileSorter implements ISorter { | |
if (statA.isRoot) { | ||
return -1; | ||
} | ||
|
||
if (statB.isRoot) { | ||
return 1; | ||
} | ||
|
||
return comparers.compareFileNames(statA.name, statB.name); | ||
switch (this.sortOrder) { | ||
case 'default': | ||
case 'mixed': | ||
case 'filesFirst': | ||
return comparers.compareFileNames(statA.name, statB.name); | ||
|
||
case 'type': | ||
return comparers.compareFileExtensions(statA.name, statB.name); | ||
|
||
case 'modified': | ||
if (statA.mtime !== statB.mtime) { | ||
return statA.mtime < statB.mtime ? 1 : -1; | ||
} else { | ||
return comparers.compareFileNames(statA.name, statB.name); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it not make sense to use the filename compare fallback here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and I do use it here. First, I compare extensions on line 104. And if they are equal, I compare filenames on lines 108-112.