Skip to content

Commit cdbd154

Browse files
committed
feat(file_size): Add options to show a right column display of file sizes
1 parent 48d0e82 commit cdbd154

File tree

5 files changed

+159
-0
lines changed

5 files changed

+159
-0
lines changed

doc/nvim-tree-lua.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,12 @@ Following is the default configuration. See |nvim-tree-opts| for details.
514514
cmd = "",
515515
args = {},
516516
},
517+
size = {
518+
enable = true,
519+
column_width = 12,
520+
show_folder_size = false,
521+
format_unit = "double",
522+
},
517523
git = {
518524
enable = true,
519525
show_on_dirs = true,

lua/nvim-tree.lua

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,12 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
489489
cmd = "",
490490
args = {},
491491
},
492+
size = {
493+
enable = true,
494+
column_width = 12,
495+
show_folder_size = false,
496+
format_unit = "double",
497+
},
492498
git = {
493499
enable = true,
494500
show_on_dirs = true,
@@ -653,6 +659,12 @@ local ACCEPTED_TYPES = {
653659
update_focused_file = {
654660
exclude = { "function" },
655661
},
662+
size = {
663+
enable = { "boolean" },
664+
column_width = { "integer" },
665+
show_folder_size = { "boolean" },
666+
format_unit = { "function", "string" },
667+
},
656668
git = {
657669
disable_for_dirs = { "function" },
658670
},
@@ -698,6 +710,9 @@ local ACCEPTED_STRINGS = {
698710
help = {
699711
sort_by = { "key", "desc" },
700712
},
713+
size = {
714+
format_unit = { "single", "double" },
715+
},
701716
}
702717

703718
---@param conf table|nil
@@ -827,6 +842,22 @@ function M.setup(conf)
827842
log.raw("config", "%s\n", vim.inspect(opts))
828843
end
829844

845+
if M.config.size.column_width < 6 then
846+
notify.warn "`size.right_padding` is a small number, problably won't show any size numbers, try using 12."
847+
end
848+
849+
if M.config.size.format_unit == "single" then
850+
-- The unit. Ex: 10.12M
851+
M.config.size.format_unit = function(unit)
852+
return string.format("%1s", unit:sub(1, 1))
853+
end
854+
elseif M.config.size.format_unit == "double" then
855+
-- The unit. Ex: 10.12 MB
856+
M.config.size.format_unit = function(unit)
857+
return string.format(" %2s", unit)
858+
end
859+
end
860+
830861
require("nvim-tree.actions").setup(opts)
831862
require("nvim-tree.keymap").setup(opts)
832863
require("nvim-tree.appearance").setup()

lua/nvim-tree/appearance/init.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ M.HIGHLIGHT_GROUPS = {
5959
-- Picker
6060
{ group = "NvimTreeWindowPicker", def = "guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=DarkBlue" },
6161

62+
-- File Size
63+
{ group = "NvimTreeFileSize", link = "Conceal" },
64+
6265
-- LiveFilter
6366
{ group = "NvimTreeLiveFilterPrefix", link = "PreProc" },
6467
{ group = "NvimTreeLiveFilterValue", link = "ModeMsg" },

lua/nvim-tree/renderer/builder.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ local DecoratorGit = require "nvim-tree.renderer.decorator.git"
1212
local DecoratorModified = require "nvim-tree.renderer.decorator.modified"
1313
local DecoratorHidden = require "nvim-tree.renderer.decorator.hidden"
1414
local DecoratorOpened = require "nvim-tree.renderer.decorator.opened"
15+
local DecoratorSize = require "nvim-tree.renderer.decorator.size"
1516

1617
local pad = require "nvim-tree.renderer.components.padding"
1718
local icons = require "nvim-tree.renderer.components.icons"
@@ -447,6 +448,7 @@ function Builder.setup(opts)
447448

448449
-- priority order
449450
M.decorators = {
451+
DecoratorSize:new(opts),
450452
DecoratorCut:new(opts),
451453
DecoratorCopied:new(opts),
452454
DecoratorDiagnostics:new(opts),
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
2+
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
3+
local Decorator = require "nvim-tree.renderer.decorator"
4+
5+
---@class DecoratorSize: Decorator
6+
---@field icon HighlightedString|nil
7+
local DecoratorSize = Decorator:new()
8+
9+
---@param opts table
10+
---@return DecoratorSize
11+
function DecoratorSize:new(opts)
12+
local o = Decorator.new(self, {
13+
enabled = opts.size.enable,
14+
column_width = opts.size.column_width,
15+
show_folder_size = opts.size.show_folder_size,
16+
format_unit = opts.size.format_unit, -- Assumed to be a function
17+
units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" },
18+
icon_placement = ICON_PLACEMENT.right_align,
19+
})
20+
21+
o.format_size = function(size)
22+
local formatted = string
23+
.format("%.2f", size)
24+
-- Remove trailing zeros after the decimal point
25+
:gsub("(%..-)0+$", "%1")
26+
-- Remove the trailing decimal point if it's a whole number
27+
:gsub("%.$", "")
28+
return formatted
29+
end
30+
31+
return o
32+
end
33+
34+
---@param node Node
35+
---@return string|nil group
36+
function DecoratorSize:calculate_highlight(_)
37+
return nil
38+
end
39+
40+
--- Convert a size to a human-readable format (e.g., KB, MB, GB) with fixed width
41+
---@private
42+
---@param size number size in bytes
43+
---@return string
44+
---NOTE: This function tries it's best to minified the string
45+
--- generated, but this implies that we have more than 3 branchs
46+
--- to determined how much bytes can we shave from the string to
47+
--- comply to self.max_lengh. Since we know self.max_length doesn't
48+
--- change, a better way would be decide a version of human_readable_size based
49+
--- on self.max_lenght once at this object's construction.
50+
--- Basically, instead of this method, we would baking all ifs first to decide which function to bind to possible field `self.human_readable_size`
51+
--- I don't actually know if it would be faster without test, but most likely it would be faster.
52+
function DecoratorSize:human_readable_size(size)
53+
-- Check for nan, negative, etc.
54+
if type(size) ~= "number" or size ~= size or size < 0 then
55+
return ""
56+
end
57+
local index = 1
58+
59+
-- We check for index here because for exaple, let's say
60+
-- on a given iteration you have :
61+
-- 1024 YB, then index is equal to #units, normally we would devide again,
62+
-- but we don't have more units, so keep as is.
63+
while size >= 1024 and index < #self.units do
64+
size = size / 1024
65+
index = index + 1
66+
end
67+
68+
local unit_str = self.format_unit(self.units[index])
69+
70+
-- Apparently string.format already rounds then number
71+
local size_str = self.format_size(size)
72+
local result = size_str .. unit_str
73+
74+
-- We Need a max length to align size redering properly
75+
-- So the result must at at most this column width
76+
local max_length = self.column_width
77+
78+
if #result > max_length then
79+
if (index + 1) < #self.units then
80+
vim.fn.confirm(result)
81+
size = size / 1024
82+
index = index + 1
83+
size_str = self.format_size(size)
84+
unit_str = self.format_unit(self.units[index])
85+
result = size_str .. unit_str
86+
-- Now that we divided one more time to make it even smaller
87+
-- we are garanteed the size string to have lenght of <= 4 (from 0.xx size)
88+
assert(#size_str <= 4)
89+
end
90+
end
91+
92+
-- After we're sure we divided as much as we can when we
93+
-- actually need it, only then we add the padding of max_length
94+
result = string.format("%" .. max_length .. "s", result)
95+
96+
-- If still too big after all that,
97+
-- then we just set to empty string
98+
if #result > max_length then
99+
result = ""
100+
end
101+
102+
return result
103+
end
104+
105+
---@param node Node
106+
---@return HighlightedString[]|nil icons
107+
function DecoratorSize:calculate_icons(node)
108+
local size = node and node.fs_stat and node.fs_stat.size or 0
109+
local folder_size_str = self.column_width > 0 and string.rep(" ", self.column_width - 1) .. "-" or ""
110+
local icon = {
111+
str = (self.show_folder_size or node.nodes == nil) and self:human_readable_size(size) or folder_size_str,
112+
hl = { "NvimTreeFileSize" },
113+
}
114+
return { icon }
115+
end
116+
117+
return DecoratorSize

0 commit comments

Comments
 (0)