|
| 1 | +local utils = require "nvim-tree.utils" |
| 2 | + |
| 3 | +local git = require "nvim-tree.renderer.components.git" |
| 4 | +local pad = require "nvim-tree.renderer.components.padding" |
| 5 | +local icons = require "nvim-tree.renderer.components.icons" |
| 6 | + |
| 7 | +-- TODO(refactor): the builder abstraction is not perfect yet. We shouldn't leak data in components. |
| 8 | +-- Components should return only and icon / highlight group pair at most. |
| 9 | +-- The code was mostly moved from renderer/init.lua and rearranged, so it's still under construction. |
| 10 | + |
| 11 | +local Builder = {} |
| 12 | +Builder.__index = Builder |
| 13 | + |
| 14 | +function Builder.new(root_cwd) |
| 15 | + return setmetatable({ |
| 16 | + index = 0, |
| 17 | + depth = nil, |
| 18 | + highlights = {}, |
| 19 | + lines = {}, |
| 20 | + markers = {}, |
| 21 | + root_cwd = root_cwd, |
| 22 | + }, Builder) |
| 23 | +end |
| 24 | + |
| 25 | +function Builder:configure_initial_depth(show_arrows) |
| 26 | + self.depth = show_arrows and 2 or 0 |
| 27 | + return self |
| 28 | +end |
| 29 | + |
| 30 | +function Builder:configure_root_modifier(root_folder_modifier) |
| 31 | + self.root_folder_modifier = root_folder_modifier or ":~" |
| 32 | + return self |
| 33 | +end |
| 34 | + |
| 35 | +function Builder:configure_trailing_slash(with_trailing) |
| 36 | + self.trailing_slash = with_trailing and "/" or "" |
| 37 | + return self |
| 38 | +end |
| 39 | + |
| 40 | +function Builder:configure_special_map(special_map) |
| 41 | + self.special_map = special_map |
| 42 | + return self |
| 43 | +end |
| 44 | + |
| 45 | +function Builder:configure_picture_map(picture_map) |
| 46 | + self.picture_map = picture_map |
| 47 | + return self |
| 48 | +end |
| 49 | + |
| 50 | +function Builder:configure_opened_file_highlighting(level) |
| 51 | + if level == 1 then |
| 52 | + self.open_file_highlight = "icon" |
| 53 | + elseif level == 2 then |
| 54 | + self.open_file_highlight = "name" |
| 55 | + elseif level == 3 then |
| 56 | + self.open_file_highlight = "all" |
| 57 | + end |
| 58 | + |
| 59 | + return self |
| 60 | +end |
| 61 | + |
| 62 | +function Builder:_insert_highlight(group, start, end_) |
| 63 | + table.insert(self.highlights, { group, self.index, start, end_ or -1 }) |
| 64 | +end |
| 65 | + |
| 66 | +function Builder:_insert_line(line) |
| 67 | + table.insert(self.lines, line) |
| 68 | +end |
| 69 | + |
| 70 | +local function get_folder_name(node) |
| 71 | + local name = node.name |
| 72 | + local next = node.group_next |
| 73 | + while next do |
| 74 | + name = name .. "/" .. next.name |
| 75 | + next = next.group_next |
| 76 | + end |
| 77 | + return name |
| 78 | +end |
| 79 | + |
| 80 | +function Builder:_build_folder(node, padding, git_hl) |
| 81 | + local offset = string.len(padding) |
| 82 | + |
| 83 | + local name = get_folder_name(node) |
| 84 | + local has_children = #node.nodes ~= 0 or node.has_children |
| 85 | + local icon = icons.get_folder_icon(node.open, node.link_to ~= nil, has_children) |
| 86 | + local git_icon = git.get_icons(node, self.index, offset, #icon, self.highlights) or "" |
| 87 | + |
| 88 | + local folder_hl = "NvimTreeFolderName" |
| 89 | + if self.special_map[node.absolute_path] then |
| 90 | + folder_hl = "NvimTreeSpecialFolderName" |
| 91 | + elseif node.open then |
| 92 | + folder_hl = "NvimTreeOpenedFolderName" |
| 93 | + elseif not has_children then |
| 94 | + folder_hl = "NvimTreeEmptyFolderName" |
| 95 | + end |
| 96 | + |
| 97 | + icons.set_folder_hl( |
| 98 | + self.index, |
| 99 | + offset, |
| 100 | + #icon + #git_icon, |
| 101 | + #name, |
| 102 | + "NvimTreeFolderIcon", |
| 103 | + folder_hl, |
| 104 | + self.highlights, |
| 105 | + self.open_file_highlight |
| 106 | + ) |
| 107 | + if git_hl then |
| 108 | + icons.set_folder_hl( |
| 109 | + self.index, |
| 110 | + offset, |
| 111 | + #icon + #git_icon, |
| 112 | + #name, |
| 113 | + git_hl, |
| 114 | + git_hl, |
| 115 | + self.highlights, |
| 116 | + self.open_file_highlight |
| 117 | + ) |
| 118 | + end |
| 119 | + self:_insert_line(padding .. icon .. git_icon .. name .. self.trailing_slash) |
| 120 | +end |
| 121 | + |
| 122 | +-- TODO: missing git icon for symlinks |
| 123 | +function Builder:_build_symlink(node, padding, git_highlight) |
| 124 | + local icon = icons.i.symlink |
| 125 | + local arrow = icons.i.symlink_arrow |
| 126 | + |
| 127 | + local link_highlight = git_highlight or "NvimTreeSymlink" |
| 128 | + |
| 129 | + local line = padding .. icon .. node.name .. arrow .. node.link_to |
| 130 | + self:_insert_highlight(link_highlight, string.len(padding), string.len(line)) |
| 131 | + self:_insert_line(line) |
| 132 | +end |
| 133 | + |
| 134 | +function Builder:_build_file_icons(node, offset) |
| 135 | + if self.special_map[node.absolute_path] or self.special_map[node.name] then |
| 136 | + local git_icons = git.get_icons(node, self.index, offset, 0, self.highlights) |
| 137 | + self:_insert_highlight("NvimTreeSpecialFile", offset + #git_icons) |
| 138 | + return icons.i.special, git_icons |
| 139 | + else |
| 140 | + local icon = icons.get_file_icon(node.name, node.extension, self.index, offset, self.highlights) |
| 141 | + return icon, git.get_icons(node, self.index, offset, #icon, self.highlights) |
| 142 | + end |
| 143 | +end |
| 144 | + |
| 145 | +function Builder:_highlight_opened_files(node, offset, icon, git_icons) |
| 146 | + local from = offset |
| 147 | + local to = offset |
| 148 | + |
| 149 | + if self.open_file_highlight == "icon" then |
| 150 | + to = from + #icon |
| 151 | + elseif self.open_file_highlight == "name" then |
| 152 | + from = offset + #icon + #git_icons |
| 153 | + to = from + #node.name |
| 154 | + elseif self.open_file_highlight == "all" then |
| 155 | + to = -1 |
| 156 | + end |
| 157 | + |
| 158 | + self:_insert_highlight("NvimTreeOpenedFile", from, to) |
| 159 | +end |
| 160 | + |
| 161 | +function Builder:_build_file(node, padding, git_highlight) |
| 162 | + local offset = string.len(padding) |
| 163 | + |
| 164 | + local icon, git_icons = self:_build_file_icons(node, offset) |
| 165 | + |
| 166 | + self:_insert_line(padding .. icon .. git_icons .. node.name) |
| 167 | + local col_start = offset + #icon + #git_icons |
| 168 | + |
| 169 | + if node.executable then |
| 170 | + self:_insert_highlight("NvimTreeExecFile", col_start) |
| 171 | + elseif self.picture_map[node.extension] then |
| 172 | + self:_insert_highlight("NvimTreeImageFile", col_start) |
| 173 | + end |
| 174 | + |
| 175 | + local should_highlight_opened_files = self.open_file_highlight and vim.fn.bufloaded(node.absolute_path) > 0 |
| 176 | + if should_highlight_opened_files then |
| 177 | + self:_highlight_opened_files(node, offset, icon, git_icons) |
| 178 | + end |
| 179 | + |
| 180 | + if git_highlight then |
| 181 | + self:_insert_highlight(git_highlight, col_start) |
| 182 | + end |
| 183 | +end |
| 184 | + |
| 185 | +function Builder:build(tree) |
| 186 | + for idx, node in ipairs(tree.nodes) do |
| 187 | + local padding = pad.get_padding(self.depth, idx, tree, node, self.markers) |
| 188 | + |
| 189 | + if self.depth > 0 then |
| 190 | + self:_insert_highlight("NvimTreeIndentMarker", 0, string.len(padding)) |
| 191 | + end |
| 192 | + |
| 193 | + local git_highlight = git.get_highlight(node) |
| 194 | + |
| 195 | + local is_folder = node.nodes ~= nil |
| 196 | + local is_symlink = node.link_to ~= nil |
| 197 | + |
| 198 | + if is_folder then |
| 199 | + self:_build_folder(node, padding, git_highlight) |
| 200 | + elseif is_symlink then |
| 201 | + self:_build_symlink(node, padding, git_highlight) |
| 202 | + else |
| 203 | + self:_build_file(node, padding, git_highlight) |
| 204 | + end |
| 205 | + self.index = self.index + 1 |
| 206 | + |
| 207 | + if node.open then |
| 208 | + self.depth = self.depth + 2 |
| 209 | + self:build(node) |
| 210 | + self.depth = self.depth - 2 |
| 211 | + end |
| 212 | + end |
| 213 | + |
| 214 | + return self |
| 215 | +end |
| 216 | + |
| 217 | +local function format_root_name(root_cwd, modifier) |
| 218 | + local base_root = utils.path_remove_trailing(vim.fn.fnamemodify(root_cwd, modifier)) |
| 219 | + return utils.path_join { base_root, ".." } |
| 220 | +end |
| 221 | + |
| 222 | +function Builder:build_header(show_header) |
| 223 | + if show_header then |
| 224 | + local root_name = format_root_name(self.root_cwd, self.root_folder_modifier) |
| 225 | + self:_insert_line(root_name) |
| 226 | + self:_insert_highlight("NvimTreeRootFolder", 0, string.len(root_name)) |
| 227 | + self.index = 1 |
| 228 | + end |
| 229 | + |
| 230 | + return self |
| 231 | +end |
| 232 | + |
| 233 | +function Builder:unwrap() |
| 234 | + return self.lines, self.highlights |
| 235 | +end |
| 236 | + |
| 237 | +return Builder |
0 commit comments