diff --git a/README.md b/README.md index 90d56f06..4ee51ee8 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,20 @@ ## Notice -- I am working on this plugin to learn lua, learn neovim api and create a **simple** file explorer with features i need. +- I am working on this plugin to learn lua, learn neovim api and create a file explorer with features i need. - I really don't like any of the vim trees, they are all too complicated for their purposes and are kind of buggy. I have my shell to do most commands. -- This plugin does not work on windows. +- This plugin will not work on windows. ## TODO - [x] moving around the file structure like any basic tree - [x] open file in current buffer or in split with FzF like bindings (CR, C-v, C-x) -- [ ] add / delete file in directory - [x] icons for files +- [ ] add / delete file in directory - [ ] syntax highlighting -- [ ] quickly find file in the directory structure - [ ] simple git integration (color of file changing when staged/changed) -- [ ] update automatically on window change +- [ ] quickly find file in the directory structure +- [ ] update tree automatically on window change ## TOFIX diff --git a/lua/lib/file.lua b/lua/lib/file.lua new file mode 100644 index 00000000..d77512e2 --- /dev/null +++ b/lua/lib/file.lua @@ -0,0 +1,97 @@ +local api = vim.api +local buf, win + +local function scratch_buffer() + buf = api.nvim_create_buf(false, true) + api.nvim_buf_set_option(buf, 'bufhidden', 'wipe') + + local width = api.nvim_get_option("columns") + local height = api.nvim_get_option("lines") + + local win_height = 2 + local win_width = 90 + + local row = math.ceil((height - win_height) / 2 - 1) + local col = math.ceil((width - win_width) / 2) + + local opts = { + style = "minimal", + relative = "editor", + width = win_width, + height = win_height, + row = row, + col = col + } + + local border_buf = api.nvim_create_buf(false, true) + + local border_opts = { + style = "minimal", + relative = "editor", + width = win_width + 2, + height = win_height + 2, + row = row - 1, + col = col - 1 + } + + local border_lines = { '┌' .. string.rep('─', win_width) .. '┐' } + local middle_line = '│' .. string.rep(' ', win_width) .. '│' + for _ = 1, win_height do + table.insert(border_lines, middle_line) + end + table.insert(border_lines, '└' .. string.rep('─', win_width) .. '┘') + api.nvim_buf_set_lines(border_buf, 0, -1, false, border_lines) + + api.nvim_open_win(border_buf, true, border_opts) + api.nvim_command('setlocal nocursorline winhighlight=Normal:LuaNoEndOfBufferPopup') + win = api.nvim_open_win(buf, true, opts) + api.nvim_command('setlocal nocursorline winhighlight=Normal:LuaTreePopup') + api.nvim_command('au BufWipeout exe "silent bwipeout! "'..border_buf) +end + +local function set_mappings() + local chars = { + 'a', 'b', 'c', 'd', 'e', 'f', 'h', 'j', 'l', 'q', 'k', 'g', 'i', 'n', 'o', 'p', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' + } + for _,v in ipairs(chars) do + api.nvim_buf_set_keymap(buf, 'n', v, '', { nowait = true, noremap = true, silent = true }) + api.nvim_buf_set_keymap(buf, 'n', v:upper(), '', { nowait = true, noremap = true, silent = true }) + api.nvim_buf_set_keymap(buf, 'n', '', '', { nowait = true, noremap = true, silent = true }) + api.nvim_buf_set_keymap(buf, 'i', '', '', { nowait = true, noremap = true, silent = true }) + api.nvim_buf_set_keymap(buf, 'i', '', '', { nowait = true, noremap = true, silent = true }) + end +end + +local function update_view(...) + api.nvim_buf_set_lines(buf, 0, -1, false, ...) + api.nvim_command('normal G') + api.nvim_command('startinsert!') +end + +local function wrapper(...) + scratch_buffer() + update_view(...) + set_mappings() +end + +local function add_file(path) + wrapper({ "Create File", path }) +end + +local function remove_file(filename, path, isdir) + local name = "File" + if isdir == true then name = "Directory" end + wrapper({ "Remove " .. name .. " " .. filename .. " ?", "y/n: " }) +end + +local function rename_file(filename, path, isdir) + local name = "File" + if isdir == true then name = "Directory" end + wrapper({ "Rename " .. name, path .. filename }) +end + +return { + add_file = add_file; + remove_file = remove_file; + rename_file = rename_file; +} diff --git a/lua/lib/format.lua b/lua/lib/format.lua new file mode 100644 index 00000000..7de296db --- /dev/null +++ b/lua/lib/format.lua @@ -0,0 +1,53 @@ +local api = vim.api + +local function get_padding(depth) + local str = "" + + while 0 < depth do + str = str .. " " + depth = depth - 1 + end + + return str +end + +local function default_icons(_, isdir, open) + if isdir == true then + if open == true then return " " end + return " " + end + + return "" +end + +local function dev_icons(pathname, isdir, open) + if isdir == true then return default_icons(pathname, isdir, open) end + + return api.nvim_call_function('WebDevIconsGetFileTypeSymbol', { pathname, isdir }) .. " " +end + +local function get_icon_func_gen() + if api.nvim_call_function('exists', { "WebDevIconsGetFileTypeSymbol" }) == 0 then + return dev_icons + else + return default_icons + end +end + +local get_icon = get_icon_func_gen() + +local function format_tree(tree) + local dirs = {} + + for i, node in pairs(tree) do + local padding = get_padding(node.depth) + local icon = get_icon(node.path .. node.name, node.dir, node.open) + dirs[i] = padding .. icon .. node.name + end + + return dirs +end + +return { + format_tree = format_tree; +} diff --git a/lua/tree.lua b/lua/tree.lua index a3e3513e..ea892cdb 100644 --- a/lua/tree.lua +++ b/lua/tree.lua @@ -1,9 +1,13 @@ -local api = vim.api -local function sys(v) return vim.fn.system(v) end -local function syslist(v) return vim.fn.systemlist(v) end +local lib_file = require 'lib/file' +local format = require 'lib/format'.format_tree +local api = vim.api +local function sys(v) return api.nvim_call_function('system', { v }) end +local function syslist(v) return api.nvim_call_function('systemlist', { v }) end + +-- get rid of \n and add leading '/' +local ROOT_PATH = string.sub(sys('pwd'), 1, -2) .. '/' local BUF_NAME = '_LuaTree_' -local ROOT_PATH = string.sub(sys('pwd'), 1, -2) .. '/' -- get rid of \n and add leading '/' local function is_dir(path) local stat = vim.loop.fs_stat(path) @@ -43,55 +47,6 @@ end local Tree = create_dirs(ROOT_PATH, 0, syslist('ls')) -local function get_padding(depth) - local str = "" - - while 0 < depth do - str = str .. " " - depth = depth - 1 - end - - return str -end - -local function default_icons(_, isdir, open) - if isdir == true then - if open == true then return " " end - return " " - end - - return "" -end - -local function dev_icons(pathname, isdir, open) - if isdir == true then return default_icons(pathname, isdir, open) end - - return api.nvim_call_function('WebDevIconsGetFileTypeSymbol', { pathname, isdir }) .. " " -end - -local function get_icon_func_gen() - if api.nvim_call_function('exists', { "WebDevIconsGetFileTypeSymbol" }) == 0 then - return dev_icons - else - return default_icons - end -end - -local get_icon = get_icon_func_gen() - -local function format_tree(tree) - local dirs = {} - local previous_parent_index = -1 - - for i, node in pairs(tree) do - local padding = get_padding(node.depth) - local icon = get_icon(node.path .. node.name, node.dir, node.open) - dirs[i] = padding .. icon .. node.name - end - - return dirs -end - local function get_buf() local regex = '.*'..BUF_NAME..'$'; @@ -118,8 +73,7 @@ end local function buf_setup() api.nvim_command('setlocal nonumber norelativenumber winfixwidth winfixheight') - api.nvim_command('hi NoEndOfBuffer guifg=bg') - api.nvim_command('setlocal winhighlight=EndOfBuffer:NoEndOfBuffer') + api.nvim_command('setlocal winhighlight=EndOfBuffer:LuaTreeEndOfBuffer') end local function open() @@ -156,7 +110,7 @@ local function update_view() local cursor_pos = api.nvim_win_get_cursor(0) api.nvim_buf_set_option(buf, 'modifiable', true) - api.nvim_buf_set_lines(buf, 0, -1, false, format_tree(Tree)) + api.nvim_buf_set_lines(buf, 0, -1, false, format(Tree)) api.nvim_buf_set_option(buf, 'modifiable', false) api.nvim_win_set_cursor(0, cursor_pos) end @@ -200,6 +154,9 @@ local function set_mappings() [''] = 'open_file("edit")'; [''] = 'open_file("vsplit")'; [''] = 'open_file("split")'; + a = 'edit_file("add")'; + d = 'edit_file("delete")'; + r = 'edit_file("rename")'; f = 'find_file()'; } @@ -208,6 +165,19 @@ local function set_mappings() nowait = true, noremap = true, silent = true }) end + + local maps = { + j = 'j$B', + k = 'k$B', + l = '', + h = '' + } + + for k,v in pairs(maps) do + api.nvim_buf_set_keymap(buf, 'n', k, v, { + nowait = true, noremap = true, silent = true + }) + end end local function toggle() @@ -220,8 +190,26 @@ local function toggle() end end +local function edit_file(edit_type) + local tree_index = api.nvim_win_get_cursor(0)[1] + local node = Tree[tree_index] + + if edit_type == 'add' then + if node.dir == true then + lib_file.add_file(node.path .. node.name .. '/') + else + lib_file.add_file(node.path) + end + elseif edit_type == 'delete' then + lib_file.remove_file(node.name, node.path, node.dir) + elseif edit_type == 'rename' then + lib_file.rename_file(node.name, node.path, node.dir) + end +end + return { toggle = toggle; open_file = open_file; + edit_file = edit_file; } diff --git a/plugin/tree.vim b/plugin/tree.vim index 80a72863..06c8cc0b 100644 --- a/plugin/tree.vim +++ b/plugin/tree.vim @@ -3,6 +3,9 @@ if exists('g:loaded_tree') | finish | endif let s:save_cpo = &cpo set cpo&vim +hi def link LuaTreePopup Normal +hi def LuaTreeEndOfBuffer guifg=bg + command! LuaTree lua require'tree'.toggle() let &cpo = s:save_cpo