Skip to content

Commit a3256e9

Browse files
farbodszsindrets
andauthored
feat: File tree (#73)
This implements a file tree as the new default listing style in the file panel. The listing style is configurable, and the old list format is still available. You can cycle listing styles from the file panel by pressing 'i' by default. Co-authored-by: Sindre T. Strøm <[email protected]>
1 parent 520bb5c commit a3256e9

16 files changed

+832
-226
lines changed

.luacheckrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Global objects defined by C code.
2+
read_globals = {
3+
"vim"
4+
}

README.md

+23-12
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,25 @@ local cb = require'diffview.config'.diffview_callback
4141

4242
require'diffview'.setup {
4343
diff_binaries = false, -- Show diffs for binaries
44-
use_icons = true, -- Requires nvim-web-devicons
4544
enhanced_diff_hl = false, -- See ':h diffview-config-enhanced_diff_hl'
45+
use_icons = true, -- Requires nvim-web-devicons
46+
icons = { -- Only applies when use_icons is true.
47+
folder_closed = "",
48+
folder_open = "",
49+
},
4650
signs = {
4751
fold_closed = "",
4852
fold_open = "",
4953
},
5054
file_panel = {
51-
position = "left", -- One of 'left', 'right', 'top', 'bottom'
52-
width = 35, -- Only applies when position is 'left' or 'right'
53-
height = 10, -- Only applies when position is 'top' or 'bottom'
55+
position = "left", -- One of 'left', 'right', 'top', 'bottom'
56+
width = 35, -- Only applies when position is 'left' or 'right'
57+
height = 10, -- Only applies when position is 'top' or 'bottom'
58+
listing_style = "tree", -- One of 'list' or 'tree'
59+
tree_options = { -- Only applies when listing_style is 'tree'
60+
flatten_dirs = true,
61+
folder_statuses = "always" -- One of 'never', 'only_folded' or 'always'.
62+
}
5463
},
5564
file_history_panel = {
5665
position = "bottom",
@@ -79,23 +88,25 @@ require'diffview'.setup {
7988
["<leader>b"] = cb("toggle_files"), -- Toggle the files panel.
8089
},
8190
file_panel = {
82-
["j"] = cb("next_entry"), -- Bring the cursor to the next file entry
91+
["j"] = cb("next_entry"), -- Bring the cursor to the next file entry
8392
["<down>"] = cb("next_entry"),
84-
["k"] = cb("prev_entry"), -- Bring the cursor to the previous file entry.
93+
["k"] = cb("prev_entry"), -- Bring the cursor to the previous file entry.
8594
["<up>"] = cb("prev_entry"),
86-
["<cr>"] = cb("select_entry"), -- Open the diff for the selected entry.
95+
["<cr>"] = cb("select_entry"), -- Open the diff for the selected entry.
8796
["o"] = cb("select_entry"),
8897
["<2-LeftMouse>"] = cb("select_entry"),
89-
["-"] = cb("toggle_stage_entry"), -- Stage / unstage the selected entry.
90-
["S"] = cb("stage_all"), -- Stage all entries.
91-
["U"] = cb("unstage_all"), -- Unstage all entries.
92-
["X"] = cb("restore_entry"), -- Restore entry to the state on the left side.
93-
["R"] = cb("refresh_files"), -- Update stats and entries in the file list.
98+
["-"] = cb("toggle_stage_entry"), -- Stage / unstage the selected entry.
99+
["S"] = cb("stage_all"), -- Stage all entries.
100+
["U"] = cb("unstage_all"), -- Unstage all entries.
101+
["X"] = cb("restore_entry"), -- Restore entry to the state on the left side.
102+
["R"] = cb("refresh_files"), -- Update stats and entries in the file list.
94103
["<tab>"] = cb("select_next_entry"),
95104
["<s-tab>"] = cb("select_prev_entry"),
96105
["gf"] = cb("goto_file"),
97106
["<C-w><C-f>"] = cb("goto_file_split"),
98107
["<C-w>gf"] = cb("goto_file_tab"),
108+
["i"] = cb("listing_style"), -- Toggle between 'list' and 'tree' views
109+
["f"] = cb("toggle_flatten_dirs"), -- Flatten empty subdirectories in tree listing style.
99110
["<leader>e"] = cb("focus_files"),
100111
["<leader>b"] = cb("toggle_files"),
101112
},

doc/diffview.txt

+23-12
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,25 @@ Example configuration with default settings:
3838
local cb = require'diffview.config'.diffview_callback
3939
require'diffview'.setup {
4040
diff_binaries = false, -- Show diffs for binaries
41-
use_icons = true, -- Requires nvim-web-devicons
4241
enhanced_diff_hl = false, -- See |diffview-config-enhanced_diff_hl|
42+
use_icons = true, -- Requires nvim-web-devicons
43+
icons = { -- Only applies when use_icons is true.
44+
folder_closed = "",
45+
folder_open = "",
46+
},
4347
signs = {
4448
fold_closed = "",
4549
fold_open = "",
4650
},
4751
file_panel = {
48-
position = "left", -- One of 'left', 'right', 'top', 'bottom'
49-
width = 35, -- Only applies when position is 'left' or 'right'
50-
height = 10, -- Only applies when position is 'top' or 'bottom'
52+
position = "left", -- One of 'left', 'right', 'top', 'bottom'
53+
width = 35, -- Only applies when position is 'left' or 'right'
54+
height = 10, -- Only applies when position is 'top' or 'bottom'
55+
listing_style = "tree", -- One of 'list' or 'tree'
56+
tree_options = { -- Only applies when listing_style is 'tree'
57+
flatten_dirs = true,
58+
folder_statuses = "always" -- One of 'never', 'only_folded' or 'always'.
59+
}
5160
},
5261
file_history_panel = {
5362
position = "bottom",
@@ -76,23 +85,25 @@ Example configuration with default settings:
7685
["<leader>b"] = cb("toggle_files"), -- Toggle the files panel.
7786
},
7887
file_panel = {
79-
["j"] = cb("next_entry"), -- Bring the cursor to the next file entry
88+
["j"] = cb("next_entry"), -- Bring the cursor to the next file entry
8089
["<down>"] = cb("next_entry"),
81-
["k"] = cb("prev_entry"), -- Bring the cursor to the previous file entry.
90+
["k"] = cb("prev_entry"), -- Bring the cursor to the previous file entry.
8291
["<up>"] = cb("prev_entry"),
83-
["<cr>"] = cb("select_entry"), -- Open the diff for the selected entry.
92+
["<cr>"] = cb("select_entry"), -- Open the diff for the selected entry.
8493
["o"] = cb("select_entry"),
8594
["<2-LeftMouse>"] = cb("select_entry"),
86-
["-"] = cb("toggle_stage_entry"), -- Stage / unstage the selected entry.
87-
["S"] = cb("stage_all"), -- Stage all entries.
88-
["U"] = cb("unstage_all"), -- Unstage all entries.
89-
["X"] = cb("restore_entry"), -- Restore entry to the state on the left side.
90-
["R"] = cb("refresh_files"), -- Update stats and entries in the file list.
95+
["-"] = cb("toggle_stage_entry"), -- Stage / unstage the selected entry.
96+
["S"] = cb("stage_all"), -- Stage all entries.
97+
["U"] = cb("unstage_all"), -- Unstage all entries.
98+
["X"] = cb("restore_entry"), -- Restore entry to the state on the left side.
99+
["R"] = cb("refresh_files"), -- Update stats and entries in the file list.
91100
["<tab>"] = cb("select_next_entry"),
92101
["<s-tab>"] = cb("select_prev_entry"),
93102
["gf"] = cb("goto_file"),
94103
["<C-w><C-f>"] = cb("goto_file_split"),
95104
["<C-w>gf"] = cb("goto_file_tab"),
105+
["i"] = cb("listing_style"), -- Toggle between 'list' and 'tree' views
106+
["f"] = cb("toggle_flatten_dirs"), -- Flatten empty subdirectories in tree listing style.
96107
["<leader>e"] = cb("focus_files"),
97108
["<leader>b"] = cb("toggle_files"),
98109
},

lua/diffview/api/views/diff/diff_view.lua

+4-7
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ function CDiffView:init(opt)
4343
self.right = opt.right
4444
self.options = opt.options
4545
self.files = FileDict()
46-
self.file_idx = 1
4746
self.fetch_files = opt.update_files
4847
self.get_file_data = opt.get_file_data
4948
self.panel = FilePanel(
@@ -53,14 +52,14 @@ function CDiffView:init(opt)
5352
self.rev_arg or git.rev_to_pretty_string(self.left, self.right)
5453
)
5554

56-
local files, selected = self:create_file_entries(opt.files)
57-
self.file_idx = selected
55+
local files = self:create_file_entries(opt.files)
5856

5957
for kind, entries in pairs(files) do
6058
for _, entry in ipairs(entries) do
6159
table.insert(self.files[kind], entry)
6260
end
6361
end
62+
self.files:update_file_trees()
6463
end
6564

6665
---@Override
@@ -70,7 +69,6 @@ end
7069

7170
function CDiffView:create_file_entries(files)
7271
local entries = {}
73-
local i, file_idx = 1, 1
7472

7573
local sections = {
7674
{ kind = "working", files = files.working, left = self.left, right = self.right },
@@ -103,13 +101,12 @@ function CDiffView:create_file_entries(files)
103101
)
104102

105103
if file_data.selected == true then
106-
file_idx = i
104+
self.panel.cur_file = entries[v.kind][#entries[v.kind]]
107105
end
108-
i = i + 1
109106
end
110107
end
111108

112-
return entries, file_idx
109+
return entries
113110
end
114111

115112
M.CDiffView = CDiffView

lua/diffview/colors.lua

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ M.hl_links = {
6666
FilePanelPath = "Comment",
6767
FilePanelInsertions = "diffAdded",
6868
FilePanelDeletions = "diffRemoved",
69+
FolderName = "Directory",
70+
FolderSign = "PreProc",
6971
StatusAdded = "diffAdded",
7072
StatusUntracked = "diffAdded",
7173
StatusModified = "diffChanged",

lua/diffview/config.lua

+15-4
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,25 @@ local cb = M.diffview_callback
1010
-- stylua: ignore start
1111
M.defaults = {
1212
diff_binaries = false,
13-
use_icons = true,
1413
enhanced_diff_hl = false,
14+
use_icons = true,
15+
icons = {
16+
folder_closed = "",
17+
folder_open = "",
18+
},
1519
signs = {
1620
fold_closed = "",
1721
fold_open = "",
1822
},
1923
file_panel = {
20-
position = "left",
21-
width = 35,
22-
height = 10,
24+
position = "left", -- One of 'left', 'right', 'top', 'bottom'
25+
width = 35, -- Only applies when position is 'left' or 'right'
26+
height = 10, -- Only applies when position is 'top' or 'bottom'
27+
listing_style = "tree", -- One of 'list' or 'tree'
28+
tree_options = { -- Only applies when listing_style is 'tree'
29+
flatten_dirs = true,
30+
folder_statuses = "always" -- One of 'never', 'only_folded' or 'always'.
31+
}
2332
},
2433
file_history_panel = {
2534
position = "bottom",
@@ -63,6 +72,8 @@ M.defaults = {
6372
["gf"] = cb("goto_file"),
6473
["<C-w><C-f>"] = cb("goto_file_split"),
6574
["<C-w>gf"] = cb("goto_file_tab"),
75+
["i"] = cb("listing_style"),
76+
["f"] = cb("toggle_flatten_dirs"),
6677
["<leader>e"] = cb("focus_files"),
6778
["<leader>b"] = cb("toggle_files"),
6879
},

lua/diffview/git/file_dict.lua

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
local oop = require("diffview.oop")
2+
local FileTree = require("diffview.views.file_tree.file_tree").FileTree
23
local M = {}
34

5+
---@type table<integer, FileEntry>
46
---@class FileDict
57
---@field working FileEntry[]
68
---@field staged FileEntry[]
9+
---@field working_tree FileTree
10+
---@field staged_tree FileTree
711
local FileDict = oop.Object
812
FileDict = oop.create_class("FileDict")
913

@@ -28,6 +32,11 @@ function FileDict:init()
2832
end
2933
end
3034

35+
function FileDict:update_file_trees()
36+
self.working_tree = FileTree(self.working)
37+
self.staged_tree = FileTree(self.staged)
38+
end
39+
3140
function FileDict:size()
3241
return #self.working + #self.staged
3342
end
@@ -46,10 +55,10 @@ end
4655
function FileDict:ipairs()
4756
local i = 0
4857
local n = #self.working + #self.staged
58+
---@return integer, FileEntry
4959
return function()
5060
i = i + 1
5161
if i <= n then
52-
---@type integer, FileEntry
5362
return i, self[i]
5463
end
5564
end

lua/diffview/git/utils.lua

+1
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ function M.diff_file_list(git_root, left, right, path_args, opt)
274274
files.staged = tracked_files(git_root, left_rev, right_rev, "--cached HEAD" .. p_args, "staged")
275275
end
276276

277+
files:update_file_trees()
277278
return files
278279
end
279280

0 commit comments

Comments
 (0)