Skip to content

feat(#2313): sort_by -> sort.sorter, add sort.folders_first default true #2314

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

Merged
merged 2 commits into from
Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 39 additions & 26 deletions doc/nvim-tree-lua.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ Setup the plugin in your `init.lua` >

-- OR setup with some options
require("nvim-tree").setup({
sort_by = "case_sensitive",
sort = {
sorter = "case_sensitive",
},
view = {
width = 30,
},
Expand Down Expand Up @@ -312,7 +314,10 @@ applying configuration.
hijack_cursor = false,
hijack_netrw = true,
hijack_unnamed_buffer_when_opening = false,
sort_by = "name",
sort = {
sorter = "name",
folders_first = true,
},
root_dirs = {},
prefer_startup_root = false,
sync_root_with_cwd = false,
Expand Down Expand Up @@ -566,31 +571,39 @@ Hijack netrw windows (overridden if |disable_netrw| is `true`)
Reloads the explorer every time a buffer is written to.
Type: `boolean`, Default: `true`

*nvim-tree.sort_by*
Changes how files within the same directory are sorted.
Can be one of `"name"`, `"case_sensitive"`, `"modification_time"`, `"extension"`,
`"suffix"`, `"filetype"` or a function.
`"extension"` uses all suffixes e.g. `foo.tar.gz` -> `.tar.gz`
`"suffix"` uses the last e.g. `.gz`
Type: `string` | `function(nodes)`, Default: `"name"`

Function may perform a sort or return a string with one of the above
methods. It is passed a table of nodes to be sorted, each node containing:
- `absolute_path`: `string`
- `executable`: `boolean`
- `extension`: `string`
- `filetype`: `string`
- `link_to`: `string`
- `name`: `string`
- `type`: `"directory"` | `"file"` | `"link"`

Example: sort by name length: >
local sort_by = function(nodes)
table.sort(nodes, function(a, b)
return #a.name < #b.name
end)
end
*nvim-tree.sort*
File and folder sorting options.

*nvim-tree.sort.sorter* (previously `sort_by`)
Changes how files within the same directory are sorted.
Can be one of `"name"`, `"case_sensitive"`, `"modification_time"`, `"extension"`,
`"suffix"`, `"filetype"` or a function.
`"extension"` uses all suffixes e.g. `foo.tar.gz` -> `.tar.gz`
`"suffix"` uses the last e.g. `.gz`
Type: `string` | `function(nodes)`, Default: `"name"`

Function may perform a sort or return a string with one of the above
methods. It is passed a table of nodes to be sorted, each node containing:
- `absolute_path`: `string`
- `executable`: `boolean`
- `extension`: `string`
- `filetype`: `string`
- `link_to`: `string`
- `name`: `string`
- `type`: `"directory"` | `"file"` | `"link"`

Example: sort by name length: >
local sorter = function(nodes)
table.sort(nodes, function(a, b)
return #a.name < #b.name
end)
end
<
*nvim-tree.sort.sort_folders_first*
Sort folders before files. Has no effect when |nvim-tree.sort.sorter| is a
function.
Type: `boolean`, Default: `true`

*nvim-tree.hijack_unnamed_buffer_when_opening*
Opens in place of the unnamed buffer if it's empty.
Type: `boolean`, Default: `false`
Expand Down
7 changes: 5 additions & 2 deletions lua/nvim-tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,10 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
hijack_cursor = false,
hijack_netrw = true,
hijack_unnamed_buffer_when_opening = false,
sort_by = "name",
sort = {
sorter = "name",
folders_first = true,
},
root_dirs = {},
prefer_startup_root = false,
sync_root_with_cwd = false,
Expand Down Expand Up @@ -613,7 +616,7 @@ local FIELD_OVERRIDE_TYPECHECK = {
min = { string = true, ["function"] = true, number = true },
remove_keymaps = { boolean = true, table = true },
on_attach = { ["function"] = true, string = true },
sort_by = { ["function"] = true, string = true },
sorter = { ["function"] = true, string = true },
root_folder_label = { ["function"] = true, string = true, boolean = true },
picker = { ["function"] = true, string = true },
}
Expand Down
72 changes: 42 additions & 30 deletions lua/nvim-tree/explorer/sorters.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ local M = {}
local C = {}

--- Predefined comparator, defaulting to name
--- @param sort_by string as per options
--- @param sorter string as per options
--- @return function
local function get_comparator(sort_by)
return C[sort_by] or C.name
local function get_comparator(sorter)
return C[sorter] or C.name
end

---Create a shallow copy of a portion of a list.
Expand Down Expand Up @@ -68,7 +68,7 @@ local function split_merge(t, first, last, comparator)
merge(t, first, mid, last, comparator)
end

---Perform a merge sort using sort_by option.
---Perform a merge sort using sorter option.
---@param t table nodes
function M.sort(t)
if C.user then
Expand Down Expand Up @@ -115,18 +115,21 @@ function M.sort(t)

split_merge(t, 1, #t, mini_comparator) -- sort by user order
else
split_merge(t, 1, #t, get_comparator(M.config.sort_by))
split_merge(t, 1, #t, get_comparator(M.config.sort.sorter))
end
end

local function node_comparator_name_ignorecase_or_not(a, b, ignorecase)
if not (a and b) then
return true
end
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false

if M.config.sort.folders_first then
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
end
end

if ignorecase then
Expand All @@ -148,10 +151,13 @@ function C.modification_time(a, b)
if not (a and b) then
return true
end
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false

if M.config.sort.folders_first then
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
end
end

local last_modified_a = 0
Expand All @@ -174,12 +180,14 @@ function C.suffix(a, b)
end

-- directories go first
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
elseif a.nodes and b.nodes then
return C.name(a, b)
if M.config.sort.folders_first then
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
elseif a.nodes and b.nodes then
return C.name(a, b)
Copy link
Member Author

Choose a reason for hiding this comment

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

@moniquelive should we sort folders by suffix? Does that make any sense?

Copy link
Collaborator

@moniquelive moniquelive Jul 11, 2023

Choose a reason for hiding this comment

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

it's not very usual, but sometimes you may have folders with suffixes to disambiguate their names such as .tmp, .old, .bkp, etc. But I believe people will want to turn this directory-grouping-by-suffix on or off. Should this be another setting? This is getting fractal pretty quickly... 👀

Copy link
Member Author

Choose a reason for hiding this comment

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

We have too many options... I'm happy for this to be opinionated.

Other sort methods apply the same rules to folders and files, this should too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Tried it, it looked odd, abandoned.

end
end

-- dotfiles go second
Expand Down Expand Up @@ -223,10 +231,12 @@ function C.extension(a, b)
return true
end

if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
if M.config.sort.folders_first then
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
end
end

if a.extension and not b.extension then
Expand All @@ -249,10 +259,12 @@ function C.filetype(a, b)
local b_ft = vim.filetype.match { filename = b.name }

-- directories first
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
if M.config.sort.folders_first then
if a.nodes and not b.nodes then
return true
elseif not a.nodes and b.nodes then
return false
end
end

-- one is nil, the other wins
Expand All @@ -272,10 +284,10 @@ end

function M.setup(opts)
M.config = {}
M.config.sort_by = opts.sort_by
M.config.sort = opts.sort

if type(opts.sort_by) == "function" then
C.user = opts.sort_by
if type(M.config.sort.sorter) == "function" then
C.user = M.config.sort.sorter
end
end

Expand Down
3 changes: 3 additions & 0 deletions lua/nvim-tree/legacy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ local function refactored(opts)
end
opts.view.adaptive_size = nil
end

-- 2023/07/15
utils.move_missing_val(opts, "", "sort_by", opts, "sort", "sorter", true)
end

local function deprecated(opts)
Expand Down