From 5e486815d0cf0a08cf1f026b2c4353829e1f7a3a Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Wed, 31 Aug 2022 14:15:05 +0900 Subject: [PATCH 01/12] feat: Mixin Sorter (#1565) Self Solved adding `mixin` sort options for `rust` like package systems ``` package.rs package/ __inside__ lib.rs lib/ _inside_ a.rs b.rs module.rs ``` --- doc/nvim-tree-lua.txt | 3 ++- lua/nvim-tree/explorer/sorters.lua | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 0c1b714fddf..1a7ca90662a 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -416,7 +416,8 @@ if the tree was previously open. *nvim-tree.sort_by* Changes how files within the same directory are sorted. -Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension'. +Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension' or +'mixin'. Type: `string`, Default: `"name"` *nvim-tree.hijack_unnamed_buffer_when_opening* diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index 4168a931c95..e34b3c9763e 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -73,6 +73,9 @@ function M.merge_sort(t, comparator) end split_merge(t, 1, #t, comparator) + if M.sort_by == "mixin" then + M.node_inserter_mixin(t) + end end local function node_comparator_name_ignorecase_or_not(a, b, ignorecase) @@ -148,6 +151,26 @@ function M.node_comparator_extension(a, b) return a.extension:lower() <= b.extension:lower() end +function M.node_inserter_mixin(t) + local i = 1 + + while i <= #t do + if t[i] and t[i].nodes then + local j = i + 1 + while j <= #t do + if t[j] and not t[j].nodes and t[i].name:lower() == t[j].name:lower():match "(.+)%..+$" then + local change_target = t[j] + table.remove(t, j) + table.insert(t, i, change_target) + break + end + j = j + 1 + end + end + i = i + 1 + end +end + function M.setup(opts) M.sort_by = opts.sort_by if M.sort_by == "modification_time" then From 481dbb24dc524eaec696cd5be92b8514cb350aaa Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Mon, 5 Sep 2022 13:17:50 +0900 Subject: [PATCH 02/12] feat: sort_by, after_sort options for more convinient using ``` *nvim-tree.sort_by* Changes how files within the same directory are sorted. Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension', 'function'. > sort_by = function(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 end return a.name:lower() <= b.name:lower() end end Type: `string | function(a, b)`, Default: `"name"` *nvim-tree.after_sort* Related to nvim-tree.sort_by, this function runs without mergesort. Can be defined by your own after-sort works. Type: `function(table)`, Default: `disable` > after_sort = function(t) local i = 1 while i <= #t do if t[i] and t[i].nodes then local j = i + 1 while j <= #t do if t[j] and not t[j].nodes and t[i].name:lower() == t[j].name:lower():match "(.+)%..+$" then local change_target = t[j] table.remove(t, j) table.insert(t, i, change_target) break end j = j + 1 end end i = i + 1 end end ``` --- doc/nvim-tree-lua.txt | 50 ++++++++++++++++++++++++++++-- lua/nvim-tree.lua | 3 ++ lua/nvim-tree/explorer/sorters.lua | 30 +++++------------- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 1a7ca90662a..9d866261d87 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -169,6 +169,7 @@ Subsequent calls to setup will replace the previous configuration. open_on_tab = false, ignore_buf_on_tab_change = {}, sort_by = "name", + after_sort = false, root_dirs = {}, prefer_startup_root = false, sync_root_with_cwd = false, @@ -414,11 +415,54 @@ Opens the tree automatically when switching tabpage or opening a new tabpage if the tree was previously open. Type: `boolean`, Default: `false` + *nvim-tree.sort_by* Changes how files within the same directory are sorted. -Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension' or -'mixin'. - Type: `string`, Default: `"name"` +Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension', +'function'. +> + sort_by = function(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 + end + + return a.name:lower() <= b.name:lower() + end + + + end + Type: `string | function(a, b)`, Default: `"name"` + +*nvim-tree.after_sort* +Related to nvim-tree.sort_by, this function runs without mergesort. +Can be defined by your own after-sort works. + Type: `function(table)`, Default: `disable` + +> + after_sort = function(t) + local i = 1 + + while i <= #t do + if t[i] and t[i].nodes then + local j = i + 1 + while j <= #t do + if t[j] and not t[j].nodes and t[i].name:lower() == t[j].name:lower():match "(.+)%..+$" then + local change_target = t[j] + table.remove(t, j) + table.insert(t, i, change_target) + break + end + j = j + 1 + end + end + i = i + 1 + end + end *nvim-tree.hijack_unnamed_buffer_when_opening* Opens in place of the unnamed buffer if it's empty. diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index a1a81b5a94f..335c353fdf7 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -433,6 +433,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS open_on_tab = false, ignore_buf_on_tab_change = {}, sort_by = "name", + after_sort = false, root_dirs = {}, prefer_startup_root = false, sync_root_with_cwd = false, @@ -640,6 +641,8 @@ local FIELD_OVERRIDE_TYPECHECK = { height = { string = true, ["function"] = true, number = true }, remove_keymaps = { boolean = true, table = true }, on_attach = { ["function"] = true, string = true }, + sort_by = { ["function"] = true, string = true }, + after_sort = { ["function"] = true, boolean = true }, } local function validate_options(conf) diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index e34b3c9763e..53df965d18f 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -73,8 +73,8 @@ function M.merge_sort(t, comparator) end split_merge(t, 1, #t, comparator) - if M.sort_by == "mixin" then - M.node_inserter_mixin(t) + if M.after_sort then + M.after_sort(t) end end @@ -151,29 +151,13 @@ function M.node_comparator_extension(a, b) return a.extension:lower() <= b.extension:lower() end -function M.node_inserter_mixin(t) - local i = 1 - - while i <= #t do - if t[i] and t[i].nodes then - local j = i + 1 - while j <= #t do - if t[j] and not t[j].nodes and t[i].name:lower() == t[j].name:lower():match "(.+)%..+$" then - local change_target = t[j] - table.remove(t, j) - table.insert(t, i, change_target) - break - end - j = j + 1 - end - end - i = i + 1 - end -end - function M.setup(opts) M.sort_by = opts.sort_by - if M.sort_by == "modification_time" then + M.after_sort = opts.after_sort + + if M.sort_by and type(M.sort_by) == "function" then + M.node_comparator = M.sort_by + elseif M.sort_by == "modification_time" then M.node_comparator = M.node_comparator_modification_time elseif M.sort_by == "case_sensitive" then M.node_comparator = M.node_comparator_name_case_sensisive From e5e8b405971dca3b148ceb17cf90fb00fe752027 Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Mon, 5 Sep 2022 15:05:35 +0900 Subject: [PATCH 03/12] remove: after_sort ( misunderstood feature ) sort_by parameter can be function. ``` lua sort_by = function(t) local sorters = require "nvim-tree.explorer.sorters" local comparator = sorters.retrieve_comparator("name") sorters.split_merge(t, 1, #t, comparator) -- run default merge_sort local i = 1 while i <= #t do if t[i] and t[i].nodes then local j = i + 1 while j <= #t do if t[j] and not t[j].nodes and t[i].name:lower() == t[j].name:lower():match "(.+)%..+$" then local change_target = t[j] table.remove(t, j) table.insert(t, i, change_target) break end j = j + 1 end end i = i + 1 end end, ``` --- doc/nvim-tree-lua.txt | 29 ++++------------------ lua/nvim-tree.lua | 2 -- lua/nvim-tree/explorer/sorters.lua | 39 +++++++++++++++--------------- 3 files changed, 25 insertions(+), 45 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 9d866261d87..e94cfa96181 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -169,7 +169,6 @@ Subsequent calls to setup will replace the previous configuration. open_on_tab = false, ignore_buf_on_tab_change = {}, sort_by = "name", - after_sort = false, root_dirs = {}, prefer_startup_root = false, sync_root_with_cwd = false, @@ -420,31 +419,13 @@ if the tree was previously open. Changes how files within the same directory are sorted. Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension', 'function'. + Type: `string | function(table)`, Default: `"name"` > - sort_by = function(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 - end - - return a.name:lower() <= b.name:lower() - end + sort_by = function(t) + local sorters = require "nvim-tree.explorer.sorters" + local comparator = sorters.retrieve_comparator("name") + sorters.split_merge(t, 1, #t, comparator) -- run default merge_sort - - end - Type: `string | function(a, b)`, Default: `"name"` - -*nvim-tree.after_sort* -Related to nvim-tree.sort_by, this function runs without mergesort. -Can be defined by your own after-sort works. - Type: `function(table)`, Default: `disable` - -> - after_sort = function(t) local i = 1 while i <= #t do diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 335c353fdf7..0af27560180 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -433,7 +433,6 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS open_on_tab = false, ignore_buf_on_tab_change = {}, sort_by = "name", - after_sort = false, root_dirs = {}, prefer_startup_root = false, sync_root_with_cwd = false, @@ -642,7 +641,6 @@ local FIELD_OVERRIDE_TYPECHECK = { remove_keymaps = { boolean = true, table = true }, on_attach = { ["function"] = true, string = true }, sort_by = { ["function"] = true, string = true }, - after_sort = { ["function"] = true, boolean = true }, } local function validate_options(conf) diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index 53df965d18f..ec6a6a5bc5e 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -50,15 +50,15 @@ local function merge(t, first, mid, last, comparator) end end -local function split_merge(t, first, last, comparator) +function M.split_merge(t, first, last, comparator) if (last - first) < 1 then return end local mid = math.floor((first + last) / 2) - split_merge(t, first, mid, comparator) - split_merge(t, mid + 1, last, comparator) + M.split_merge(t, first, mid, comparator) + M.split_merge(t, mid + 1, last, comparator) merge(t, first, mid, last, comparator) end @@ -72,9 +72,10 @@ function M.merge_sort(t, comparator) end end - split_merge(t, 1, #t, comparator) - if M.after_sort then - M.after_sort(t) + if type(M.sort_by) == "function" then + M.sort_by(t) + else + M.split_merge(t, 1, #t, comparator) end end @@ -151,21 +152,21 @@ function M.node_comparator_extension(a, b) return a.extension:lower() <= b.extension:lower() end -function M.setup(opts) - M.sort_by = opts.sort_by - M.after_sort = opts.after_sort - - if M.sort_by and type(M.sort_by) == "function" then - M.node_comparator = M.sort_by - elseif M.sort_by == "modification_time" then - M.node_comparator = M.node_comparator_modification_time - elseif M.sort_by == "case_sensitive" then - M.node_comparator = M.node_comparator_name_case_sensisive - elseif M.sort_by == "extension" then - M.node_comparator = M.node_comparator_extension +function M.retrieve_comparator(comparator_name) -- NOTE: for user can use comparator directly + if comparator_name == "modification_time" then + return M.node_comparator_modification_time + elseif comparator_name == "case_sensitive" then + return M.node_comparator_name_case_sensisive + elseif comparator_name == "extension" then + return M.node_comparator_extension else - M.node_comparator = M.node_comparator_name_ignorecase + return M.node_comparator_name_ignorecase end end +function M.setup(opts) + M.sort_by = opts.sort_by + M.node_comparator = M.retrieve_comparator(M.sort_by) +end + return M From c34b7d25d3def6c29a07e2c7de71ed27cc4e6ecd Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Tue, 6 Sep 2022 16:40:58 +0900 Subject: [PATCH 04/12] try-fix: change existing merge_sort function, call user's sort_by hope.. like it...? --- doc/nvim-tree-lua.txt | 28 ++++--------- lua/nvim-tree/explorer/sorters.lua | 66 +++++++++++++++++++----------- 2 files changed, 50 insertions(+), 44 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index e94cfa96181..ac8d0da6108 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -421,28 +421,14 @@ Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension', 'function'. Type: `string | function(table)`, Default: `"name"` > - sort_by = function(t) - local sorters = require "nvim-tree.explorer.sorters" - local comparator = sorters.retrieve_comparator("name") - sorters.split_merge(t, 1, #t, comparator) -- run default merge_sort - - local i = 1 - - while i <= #t do - if t[i] and t[i].nodes then - local j = i + 1 - while j <= #t do - if t[j] and not t[j].nodes and t[i].name:lower() == t[j].name:lower():match "(.+)%..+$" then - local change_target = t[j] - table.remove(t, j) - table.insert(t, i, change_target) - break - end - j = j + 1 - end - end - i = i + 1 + sort_by = function(tbl) + + local user_order = {}; + for i = 1, #tbl do + table.insert(user_order, tbl.absolute_path) end + + return user_oder end *nvim-tree.hijack_unnamed_buffer_when_opening* diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index ec6a6a5bc5e..a07b5b13673 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -50,15 +50,15 @@ local function merge(t, first, mid, last, comparator) end end -function M.split_merge(t, first, last, comparator) +local function split_merge(t, first, last, comparator) if (last - first) < 1 then return end local mid = math.floor((first + last) / 2) - M.split_merge(t, first, mid, comparator) - M.split_merge(t, mid + 1, last, comparator) + split_merge(t, first, mid, comparator) + split_merge(t, mid + 1, last, comparator) merge(t, first, mid, last, comparator) end @@ -66,16 +66,38 @@ end ---@param t any[] ---@param comparator function|nil function M.merge_sort(t, comparator) - if not comparator then - comparator = function(left, right) - return left < right + if type(M.sort_by) == "function" then + local t_user = {} + for _, n in ipairs(t) do + table.insert(t_user, { + absolute_path = n.absolute_path, + executable = n.executable, + extension = n.extension, + link_to = n.link_to, + name = n.name, + type = n.type, + }) end - end - if type(M.sort_by) == "function" then - M.sort_by(t) + local user_order = M.sort_by(t_user) + + for i = 1, #user_order, 1 do + for j = 1, #t, 1 do + if t[j].absolute_path == user_order[i] then + local tmp = t[i] + t[i] = t[j] + t[j] = tmp + break + end + end + end -- reorder the list according to the user order else - M.split_merge(t, 1, #t, comparator) + if not comparator then + comparator = function(left, right) + return left < right + end + end + split_merge(t, 1, #t, comparator) end end @@ -152,21 +174,19 @@ function M.node_comparator_extension(a, b) return a.extension:lower() <= b.extension:lower() end -function M.retrieve_comparator(comparator_name) -- NOTE: for user can use comparator directly - if comparator_name == "modification_time" then - return M.node_comparator_modification_time - elseif comparator_name == "case_sensitive" then - return M.node_comparator_name_case_sensisive - elseif comparator_name == "extension" then - return M.node_comparator_extension - else - return M.node_comparator_name_ignorecase - end -end - function M.setup(opts) M.sort_by = opts.sort_by - M.node_comparator = M.retrieve_comparator(M.sort_by) + if M.sort_by and type(M.sort_by) ~= "function" then + M.node_comparator = M.sort_by + elseif M.sort_by == "modification_time" then + M.node_comparator = M.node_comparator_modification_time + elseif M.sort_by == "case_sensitive" then + M.node_comparator = M.node_comparator_name_case_sensisive + elseif M.sort_by == "extension" then + M.node_comparator = M.node_comparator_extension + else + M.node_comparator = M.node_comparator_name_ignorecase + end end return M From 10ac41d154ce20edfd17181836f1c54a6522aad9 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 10 Sep 2022 13:27:13 +1000 Subject: [PATCH 05/12] doc: explain function parameter and return, add more complex example --- doc/nvim-tree-lua.txt | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index ac8d0da6108..50b4cd3e8f6 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -414,23 +414,34 @@ Opens the tree automatically when switching tabpage or opening a new tabpage if the tree was previously open. Type: `boolean`, Default: `false` - *nvim-tree.sort_by* Changes how files within the same directory are sorted. -Can be one of 'name', 'case_sensitive', 'modification_time' or 'extension', -'function'. - Type: `string | function(table)`, Default: `"name"` -> - sort_by = function(tbl) - - local user_order = {}; - for i = 1, #tbl do - table.insert(user_order, tbl.absolute_path) +Can be one of `name`, `case_sensitive`, `modification_time`, `extension` or a +function. + Type: `string` | `function(nodes)`, Default: `"name"` + + Function is passed a table of nodes, each node containing: + - `absolute_path`: `string` + - `executable`: `boolean` + - `extension`: `string` + - `link_to`: `string` + - `name`: `string` + - `type`: `"directory"` | `"file"` | `"link"` + + Function will return a table of `absolute_path` + + Example: sort by name length: > + local sort_by = function(nodes) + table.sort(nodes, function(a, b) + return #a.name < #b.name + end) + local order = {} + for _, n in ipairs(nodes) do + table.insert(order, n.absolute_path) + end + return order end - - return user_oder - end - +< *nvim-tree.hijack_unnamed_buffer_when_opening* Opens in place of the unnamed buffer if it's empty. Type: `boolean`, Default: `false` From ece71278ee8432040b3e75e137a6d08f1b99c729 Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Fri, 16 Sep 2022 11:41:29 +0900 Subject: [PATCH 06/12] fix: reorder with user-comparator exceed memory limit apply merge_sort check nil & type for senitize --- doc/nvim-tree-lua.txt | 117 ++++++++++++++++++++++++++++- lua/nvim-tree/explorer/sorters.lua | 35 +++++++-- 2 files changed, 143 insertions(+), 9 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index d88e456599e..cbc472348f9 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -430,6 +430,17 @@ Can be one of `name`, `case_sensitive`, `modification_time`, `extension` or a function. Type: `string` | `function(nodes)`, Default: `"name"` +* Note that function(table) return Type `Table{ absolute_path }` + +For example, to use pre-defined sorting method is done like this: +> + sort_by = "case_sensitive" + +For example, to use simple user-defined sort_by function is done like this: + +> + sort_by = function(tbl) + Function is passed a table of nodes, each node containing: - `absolute_path`: `string` - `executable`: `boolean` @@ -451,7 +462,111 @@ function. end return order end -< + + return user_oder + end + +Furthermore, You can try more complex example for `rust` sort_by function is done like this: + +> + sort_by = function(tbl) + local function tbl_slice(t, first, last) + local slice = {} + for i = first, last or #t, 1 do + table.insert(slice, t[i]) + end + return slice + end + + local function merge(t, first, mid, last, comparator) + local n1 = mid - first + 1 + local n2 = last - mid + local ls = tbl_slice(t, first, mid) + local rs = tbl_slice(t, mid + 1, last) + local i = 1 + local j = 1 + local k = first + while i <= n1 and j <= n2 do + if comparator(ls[i], rs[j]) then + t[k] = ls[i] + i = i + 1 + else + t[k] = rs[j] + j = j + 1 + end + k = k + 1 + end + while i <= n1 do + t[k] = ls[i] + i = i + 1 + k = k + 1 + end + while j <= n2 do + t[k] = rs[j] + j = j + 1 + k = k + 1 + end + end + + local function split_merge(t, first, last, comparator) + if (last - first) < 1 then + return + end + + local mid = math.floor((first + last) / 2) + + split_merge(t, first, mid, comparator) + split_merge(t, mid + 1, last, comparator) + merge(t, first, mid, last, comparator) + 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 + end + + if ignorecase then + return a.name:lower() <= b.name:lower() + else + return a.name <= b.name + end + end + + + local comparartor = function(a, b) node_comparator_name_ignorecase_or_not(a, b, true) end + split_merge(tbl, 1, #tbl, comparartor) + + local i = 1 + while i <= #tbl do + if tbl[i] and tbl[i].nodes then + local j = i + 1 + while j <= #tbl do + if tbl[j] and not tbl[j].nodes and tbl[i].name:lower() == tbl[j].name:lower():match "(.+)%..+$" then + local change_target = tbl[j] + table.remove(tbl, j) + table.insert(tbl, i, change_target) + break + end + j = j + 1 + end + end + i = i + 1 + end + + local user_order = {} + for j = 1, #tbl do + table.insert(user_order, tbl[j].absolute_path) + end + return user_order + end, + + + *nvim-tree.hijack_unnamed_buffer_when_opening* Opens in place of the unnamed buffer if it's empty. Type: `boolean`, Default: `false` diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index a07b5b13673..6328a281dea 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -81,16 +81,35 @@ function M.merge_sort(t, comparator) local user_order = M.sort_by(t_user) - for i = 1, #user_order, 1 do - for j = 1, #t, 1 do - if t[j].absolute_path == user_order[i] then - local tmp = t[i] - t[i] = t[j] - t[j] = tmp - break + if type(user_order) == "table" then + -- do merge sort for prevent memory exceed + local mini_comparator = function(a, b) + local a_index = vim.tbl_indexof(user_order, a.absolute_path) + local b_index = vim.tbl_indexof(user_order, b.absolute_path) + if a_index == -1 then + a_index = 9999999999 end + if b_index == -1 then + b_index = 9999999999 + end + return a_index < b_index end - end -- reorder the list according to the user order + + split_merge(t, 1, #t, mini_comparator) -- sort by user order + + -- for _, n in ipairs(user_order) do + -- for i = 1, #user_order, 1 do + -- for j = 1, #t, 1 do + -- if t[j] and t[j].absolute_path == user_order[i] then + -- local tmp = t[i] + -- t[i] = t[j] + -- t[j] = tmp + -- break + -- end + -- end + -- end -- reorder the list according to the user order + -- end + end else if not comparator then comparator = function(left, right) From 7492886ead62fef636dd180f60ec1d71b2e723a9 Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Fri, 16 Sep 2022 11:56:55 +0900 Subject: [PATCH 07/12] fix: user_index based sorting ( create index ) for performance, create index once, using index to re-ordering --- lua/nvim-tree/explorer/sorters.lua | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index 6328a281dea..6531b79e866 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -83,32 +83,25 @@ function M.merge_sort(t, comparator) if type(user_order) == "table" then -- do merge sort for prevent memory exceed + local user_index = {} + for k, v in pairs(user_order) do + if user_index[v] == nil then + user_index[v] = k + end + end local mini_comparator = function(a, b) - local a_index = vim.tbl_indexof(user_order, a.absolute_path) - local b_index = vim.tbl_indexof(user_order, b.absolute_path) - if a_index == -1 then + local a_index = user_index[a.absolute_path] + local b_index = user_index[b.absolute_path] + if a_index == -1 or a_index == nil then a_index = 9999999999 end - if b_index == -1 then + if b_index == -1 or a_index == nil then b_index = 9999999999 end return a_index < b_index end split_merge(t, 1, #t, mini_comparator) -- sort by user order - - -- for _, n in ipairs(user_order) do - -- for i = 1, #user_order, 1 do - -- for j = 1, #t, 1 do - -- if t[j] and t[j].absolute_path == user_order[i] then - -- local tmp = t[i] - -- t[i] = t[j] - -- t[j] = tmp - -- break - -- end - -- end - -- end -- reorder the list according to the user order - -- end end else if not comparator then From d4f72d25e1d1293734e7d3654e9acfd53bebfbce Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Fri, 16 Sep 2022 11:59:48 +0900 Subject: [PATCH 08/12] fix: fence problems --- lua/nvim-tree/explorer/sorters.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index 6531b79e866..dcb94bb0a01 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -98,7 +98,7 @@ function M.merge_sort(t, comparator) if b_index == -1 or a_index == nil then b_index = 9999999999 end - return a_index < b_index + return a_index <= b_index end split_merge(t, 1, #t, mini_comparator) -- sort by user order From d921a20974a49b4d9d3da6ebf93beb3cd1092885 Mon Sep 17 00:00:00 2001 From: shellcodesniper Date: Fri, 16 Sep 2022 15:23:22 +0900 Subject: [PATCH 09/12] doc & fix: merge_sort problem fix & nil sorting add complex example --- doc/nvim-tree-lua.txt | 87 +++++++----------------------- lua/nvim-tree/explorer/sorters.lua | 22 ++++---- 2 files changed, 31 insertions(+), 78 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index cbc472348f9..b2ad529e59d 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -191,7 +191,6 @@ Subsequent calls to setup will replace the previous configuration. number = false, relativenumber = false, signcolumn = "yes", - -- @deprecated mappings = { custom_only = false, list = { @@ -466,87 +465,37 @@ For example, to use simple user-defined sort_by function is done like this: return user_oder end -Furthermore, You can try more complex example for `rust` sort_by function is done like this: +Furthermore, You can try more complex example for sort_by function is done like this: > sort_by = function(tbl) - local function tbl_slice(t, first, last) - local slice = {} - for i = first, last or #t, 1 do - table.insert(slice, t[i]) - end - return slice - end - - local function merge(t, first, mid, last, comparator) - local n1 = mid - first + 1 - local n2 = last - mid - local ls = tbl_slice(t, first, mid) - local rs = tbl_slice(t, mid + 1, last) - local i = 1 - local j = 1 - local k = first - while i <= n1 and j <= n2 do - if comparator(ls[i], rs[j]) then - t[k] = ls[i] - i = i + 1 - else - t[k] = rs[j] - j = j + 1 - end - k = k + 1 - end - while i <= n1 do - t[k] = ls[i] - i = i + 1 - k = k + 1 - end - while j <= n2 do - t[k] = rs[j] - j = j + 1 - k = k + 1 - end - end - - local function split_merge(t, first, last, comparator) - if (last - first) < 1 then - return - end - - local mid = math.floor((first + last) / 2) - - split_merge(t, first, mid, comparator) - split_merge(t, mid + 1, last, comparator) - merge(t, first, mid, last, comparator) - end - - local function node_comparator_name_ignorecase_or_not(a, b, ignorecase) + table.sort(tbl, function(a, b) if not (a and b) then return true end - if a.nodes and not b.nodes then + if a.type == "directory" and b.type ~= "directory" then return true - elseif not a.nodes and b.nodes then + elseif a.type ~= "directory" and b.type == "directory" then return false end - if ignorecase then - return a.name:lower() <= b.name:lower() - else - return a.name <= b.name + if a.type == "link" and b.type ~= "link" then + return true + elseif a.type ~= "link" and b.type == "link" then + return false end - end - - local comparartor = function(a, b) node_comparator_name_ignorecase_or_not(a, b, true) end - split_merge(tbl, 1, #tbl, comparartor) + return a.name:lower() <= b.name:lower() + -- return a.name <= b.name + end) -- NOTE: organize directory, link, file in name order + -- sort by name, is directory? local i = 1 while i <= #tbl do - if tbl[i] and tbl[i].nodes then + if tbl[i] and tbl[i].type == "directory" then local j = i + 1 while j <= #tbl do - if tbl[j] and not tbl[j].nodes and tbl[i].name:lower() == tbl[j].name:lower():match "(.+)%..+$" then + if tbl[j] and tbl[j].type ~= "directory" and tbl[i].name:lower() == tbl[j].name:lower():match "(.+)%..+$" then local change_target = tbl[j] table.remove(tbl, j) table.insert(tbl, i, change_target) @@ -560,12 +509,12 @@ Furthermore, You can try more complex example for `rust` sort_by function is don local user_order = {} for j = 1, #tbl do - table.insert(user_order, tbl[j].absolute_path) + if tbl[j] then + table.insert(user_order, tbl[j].absolute_path) + end end return user_order - end, - - + end *nvim-tree.hijack_unnamed_buffer_when_opening* Opens in place of the unnamed buffer if it's empty. diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index dcb94bb0a01..40e8d006960 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -68,6 +68,8 @@ end function M.merge_sort(t, comparator) if type(M.sort_by) == "function" then local t_user = {} + local origin_index = {} + for _, n in ipairs(t) do table.insert(t_user, { absolute_path = n.absolute_path, @@ -77,28 +79,30 @@ function M.merge_sort(t, comparator) name = n.name, type = n.type, }) + table.insert(origin_index, n) end local user_order = M.sort_by(t_user) if type(user_order) == "table" then -- do merge sort for prevent memory exceed + local user_index = {} for k, v in pairs(user_order) do - if user_index[v] == nil then + if type(v) == "string" and user_index[v] == nil then user_index[v] = k end end + + -- if missing value found, then using origin_index local mini_comparator = function(a, b) - local a_index = user_index[a.absolute_path] - local b_index = user_index[b.absolute_path] - if a_index == -1 or a_index == nil then - a_index = 9999999999 - end - if b_index == -1 or a_index == nil then - b_index = 9999999999 + local a_index = user_index[a.absolute_path] or origin_index[a.absolute_path] + local b_index = user_index[b.absolute_path] or origin_index[b.absolute_path] + + if type(a_index) == "number" and type(b_index) == "number" then + return a_index <= b_index end - return a_index <= b_index + return (a_index or 0) <= (b_index or 0) end split_merge(t, 1, #t, mini_comparator) -- sort by user order From fda6d4c7d5dabb895f4ebac1a7470b2be896b438 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sun, 18 Sep 2022 14:57:29 +1000 Subject: [PATCH 10/12] fix: sort_by detect and use string and nil --- lua/nvim-tree/explorer/sorters.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index 40e8d006960..907b53ceaae 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -192,7 +192,7 @@ end function M.setup(opts) M.sort_by = opts.sort_by - if M.sort_by and type(M.sort_by) ~= "function" then + if M.sort_by and type(M.sort_by) == "function" then M.node_comparator = M.sort_by elseif M.sort_by == "modification_time" then M.node_comparator = M.node_comparator_modification_time From 26e4066a8656ed3fce17fe9ad7fe957ee231f290 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sun, 18 Sep 2022 15:04:30 +1000 Subject: [PATCH 11/12] doc: revert sort_by to simple --- doc/nvim-tree-lua.txt | 67 +------------------------------------------ 1 file changed, 1 insertion(+), 66 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index b2ad529e59d..d488963b92e 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -429,17 +429,6 @@ Can be one of `name`, `case_sensitive`, `modification_time`, `extension` or a function. Type: `string` | `function(nodes)`, Default: `"name"` -* Note that function(table) return Type `Table{ absolute_path }` - -For example, to use pre-defined sorting method is done like this: -> - sort_by = "case_sensitive" - -For example, to use simple user-defined sort_by function is done like this: - -> - sort_by = function(tbl) - Function is passed a table of nodes, each node containing: - `absolute_path`: `string` - `executable`: `boolean` @@ -461,61 +450,7 @@ For example, to use simple user-defined sort_by function is done like this: end return order end - - return user_oder - end - -Furthermore, You can try more complex example for sort_by function is done like this: - -> - sort_by = function(tbl) - table.sort(tbl, function(a, b) - if not (a and b) then - return true - end - if a.type == "directory" and b.type ~= "directory" then - return true - elseif a.type ~= "directory" and b.type == "directory" then - return false - end - - if a.type == "link" and b.type ~= "link" then - return true - elseif a.type ~= "link" and b.type == "link" then - return false - end - - return a.name:lower() <= b.name:lower() - -- return a.name <= b.name - end) -- NOTE: organize directory, link, file in name order - - -- sort by name, is directory? - local i = 1 - while i <= #tbl do - if tbl[i] and tbl[i].type == "directory" then - local j = i + 1 - while j <= #tbl do - if tbl[j] and tbl[j].type ~= "directory" and tbl[i].name:lower() == tbl[j].name:lower():match "(.+)%..+$" then - local change_target = tbl[j] - table.remove(tbl, j) - table.insert(tbl, i, change_target) - break - end - j = j + 1 - end - end - i = i + 1 - end - - local user_order = {} - for j = 1, #tbl do - if tbl[j] then - table.insert(user_order, tbl[j].absolute_path) - end - end - return user_order - end - +< *nvim-tree.hijack_unnamed_buffer_when_opening* Opens in place of the unnamed buffer if it's empty. Type: `boolean`, Default: `false` From dfbfec60fb9f6b00961101ec3eeac86d51022931 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sun, 18 Sep 2022 15:24:02 +1000 Subject: [PATCH 12/12] fix: sort_by does not return anything --- doc/nvim-tree-lua.txt | 9 +------- lua/nvim-tree/explorer/sorters.lua | 35 ++++++++++++++---------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index d488963b92e..9dc36cada63 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -429,7 +429,7 @@ Can be one of `name`, `case_sensitive`, `modification_time`, `extension` or a function. Type: `string` | `function(nodes)`, Default: `"name"` - Function is passed a table of nodes, each node containing: + Function is passed a table of nodes to be sorted, each node containing: - `absolute_path`: `string` - `executable`: `boolean` - `extension`: `string` @@ -437,18 +437,11 @@ function. - `name`: `string` - `type`: `"directory"` | `"file"` | `"link"` - Function will return a table of `absolute_path` - Example: sort by name length: > local sort_by = function(nodes) table.sort(nodes, function(a, b) return #a.name < #b.name end) - local order = {} - for _, n in ipairs(nodes) do - table.insert(order, n.absolute_path) - end - return order end < *nvim-tree.hijack_unnamed_buffer_when_opening* diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index 907b53ceaae..be0d7fcfbd1 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -82,31 +82,28 @@ function M.merge_sort(t, comparator) table.insert(origin_index, n) end - local user_order = M.sort_by(t_user) + M.sort_by(t_user) - if type(user_order) == "table" then - -- do merge sort for prevent memory exceed - - local user_index = {} - for k, v in pairs(user_order) do - if type(v) == "string" and user_index[v] == nil then - user_index[v] = k - end + -- do merge sort for prevent memory exceed + local user_index = {} + for i, v in ipairs(t_user) do + if type(v.absolute_path) == "string" and user_index[v.absolute_path] == nil then + user_index[v.absolute_path] = i end + end - -- if missing value found, then using origin_index - local mini_comparator = function(a, b) - local a_index = user_index[a.absolute_path] or origin_index[a.absolute_path] - local b_index = user_index[b.absolute_path] or origin_index[b.absolute_path] + -- if missing value found, then using origin_index + local mini_comparator = function(a, b) + local a_index = user_index[a.absolute_path] or origin_index[a.absolute_path] + local b_index = user_index[b.absolute_path] or origin_index[b.absolute_path] - if type(a_index) == "number" and type(b_index) == "number" then - return a_index <= b_index - end - return (a_index or 0) <= (b_index or 0) + if type(a_index) == "number" and type(b_index) == "number" then + return a_index <= b_index end - - split_merge(t, 1, #t, mini_comparator) -- sort by user order + return (a_index or 0) <= (b_index or 0) end + + split_merge(t, 1, #t, mini_comparator) -- sort by user order else if not comparator then comparator = function(left, right)