diff --git a/README.md b/README.md index c178a89..91ea01b 100644 --- a/README.md +++ b/README.md @@ -354,6 +354,7 @@ Built-in functions. Ready to be bound to any key you like. :smile: | `builtin.git_files` | Lists Git files in current directory. | | `builtin.grep_string` | Searches for a string under the cursor in current directory. | | `builtin.live_grep` | Searches in current directory files. (respecting .gitignore) | +| `builtin.file_browser` | Ivy-like file explorer. Creates files by typing in filename and pressing . Press without prompt for more info | ### Vim Pickers @@ -373,7 +374,7 @@ Built-in functions. Ready to be bound to any key you like. :smile: | `builtin.vim_options` | Lists vim options and on enter edit the options value. | | `builtin.registers` | Lists vim registers and edit or paste selection. | | `builtin.autocommands` | Lists vim autocommands and go to their declaration. | -| `builtin.spell_suggest` | Lists spelling suggestions for . +| `builtin.spell_suggest` | Lists spelling suggestions for . | | `builtin.keymaps` | Lists normal-mode mappings. | | `builtin.filetypes` | Lists all filetypes. | | `builtin.highlights` | Lists all highlights. | diff --git a/developers.md b/developers.md index 1940784..37b02ff 100644 --- a/developers.md +++ b/developers.md @@ -39,7 +39,7 @@ action_set.select:replace_if( end, function(_, type) -- type is { "default", "horizontal", "vertical", "tab" } local path = actions.get_selected_entry().path - actions.refresh(prompt_bufnr, gen_new_finder(vim.fn.expand(path:sub(1, -2))), { reset_prompt = true }) + action_state.get_current_picker(prompt_bufnr):refresh(gen_new_finder(new_cwd), { reset_prompt = true}) end ) ``` diff --git a/lua/telescope/actions/init.lua b/lua/telescope/actions/init.lua index c2354b4..9286833 100644 --- a/lua/telescope/actions/init.lua +++ b/lua/telescope/actions/init.lua @@ -245,16 +245,11 @@ actions.git_staging_toggle = function(prompt_bufnr) local cwd = action_state.get_current_picker(prompt_bufnr).cwd local selection = action_state.get_selected_entry() - -- If parts of the file are staged and unstaged at the same time, stage - -- changes. Else toggle between staged and unstaged if the file is tracked, - -- and between added and untracked if the file is untracked. if selection.status:sub(2) == ' ' then utils.get_os_command_output({ 'git', 'restore', '--staged', selection.value }, cwd) else utils.get_os_command_output({ 'git', 'add', selection.value }, cwd) end - do_close(prompt_bufnr, true) - require('telescope.builtin').git_status() end local entry_to_qf = function(entry) diff --git a/lua/telescope/builtin/files.lua b/lua/telescope/builtin/files.lua index 8a46817..762c895 100644 --- a/lua/telescope/builtin/files.lua +++ b/lua/telescope/builtin/files.lua @@ -1,3 +1,4 @@ +local actions = require('telescope.actions') local action_state = require('telescope.actions.state') local action_set = require('telescope.actions.set') local finders = require('telescope.finders') @@ -7,6 +8,10 @@ local previewers = require('telescope.previewers') local utils = require('telescope.utils') local conf = require('telescope.config').values +local scan = require('plenary.scandir') +local Path = require('plenary.path') +local os_sep = Path.path.sep + local flatten = vim.tbl_flatten local files = {} @@ -191,6 +196,84 @@ local function prepare_match(entry, kind) return entries end +files.file_browser = function(opts) + opts = opts or {} + + opts.cwd = opts.cwd and vim.fn.expand(opts.cwd) or vim.loop.cwd() + + local gen_new_finder = function(path) + opts.cwd = path + local data = {} + + scan.scan_dir(path, { + add_dirs = true, + depth = 1, + on_insert = function(entry, typ) + table.insert(data, typ == 'directory' and (entry .. os_sep) or entry) + end + }) + table.insert(data, 1, '../') + + return finders.new_table { + results = data, + entry_maker = (function() + local tele_path = require'telescope.path' + local gen = make_entry.gen_from_file(opts) + return function(entry) + local tmp = gen(entry) + tmp.ordinal = tele_path.make_relative(entry, opts.cwd) + return tmp + end + end)() + } + end + + pickers.new(opts, { + prompt_title = 'Find Files', + finder = gen_new_finder(opts.cwd), + previewer = conf.file_previewer(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function(prompt_bufnr, map) + action_set.select:replace_if(function() + return action_state.get_selected_entry().path:sub(-1) == os_sep + end, function() + local new_cwd = vim.fn.expand(action_state.get_selected_entry().path:sub(1, -2)) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker.cwd = new_cwd + current_picker:refresh(gen_new_finder(new_cwd), { reset_prompt = true }) + end) + + local create_new_file = function() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local file = action_state.get_current_line() + if file == "" then + print('To create a new file or directory(add ' .. os_sep .. ' at the end of file) ' .. + 'write the desired new into the prompt and press . ' .. + 'It works for not existing nested input as well.' .. + 'Example: this' .. os_sep .. 'is' .. os_sep .. 'a' .. os_sep .. 'new_file.lua') + return + end + + local fpath = current_picker.cwd .. os_sep .. file + if string.sub(fpath, -1) ~= os_sep then + actions.close(prompt_bufnr) + Path:new(fpath):touch({ parents = true }) + vim.cmd(string.format(':e %s', fpath)) + else + Path:new(fpath:sub(1, -2)):mkdir({ parents = true }) + local new_cwd = vim.fn.expand(fpath) + current_picker.cwd = new_cwd + current_picker:refresh(gen_new_finder(new_cwd), { reset_prompt = true }) + end + end + + map('i', '', create_new_file) + map('n', '', create_new_file) + return true + end, + }):find() +end + files.treesitter = function(opts) opts.show_line = utils.get_default(opts.show_line, true) @@ -295,7 +378,7 @@ files.tags = function(opts) attach_mappings = function() action_set.select:enhance { post = function() - local selection = action_set.get_selected_entry() + local selection = action_state.get_selected_entry() if selection.scode then local scode = string.gsub(selection.scode, '[$]$', '') diff --git a/lua/telescope/builtin/git.lua b/lua/telescope/builtin/git.lua index ba08aba..c403f14 100644 --- a/lua/telescope/builtin/git.lua +++ b/lua/telescope/builtin/git.lua @@ -1,4 +1,5 @@ local actions = require('telescope.actions') +local action_state = require('telescope.actions.state') local finders = require('telescope.finders') local make_entry = require('telescope.make_entry') local pickers = require('telescope.pickers') @@ -108,22 +109,35 @@ git.branches = function(opts) end git.status = function(opts) - local output = utils.get_os_command_output({ 'git', 'status', '-s' }, opts.cwd) + local gen_new_finder = function() + local output = utils.get_os_command_output({ 'git', 'status', '-s' }, opts.cwd) - if table.getn(output) == 0 then - print('No changes found') - return + if table.getn(output) == 0 then + print('No changes found') + return + end + + return finders.new_table { + results = output, + entry_maker = opts.entry_maker or make_entry.gen_from_git_status(opts) + } end + local initial_finder = gen_new_finder() + if not initial_finder then return end + pickers.new(opts, { prompt_title = 'Git Status', - finder = finders.new_table { - results = output, - entry_maker = make_entry.gen_from_git_status(opts) - }, + finder = initial_finder, previewer = previewers.git_file_diff.new(opts), sorter = conf.file_sorter(opts), - attach_mappings = function(_, map) + attach_mappings = function(prompt_bufnr, map) + actions.git_staging_toggle:enhance { + post = function() + action_state.get_current_picker(prompt_bufnr):refresh(gen_new_finder(), { reset_prompt = true }) + end, + } + map('i', '', actions.git_staging_toggle) map('n', '', actions.git_staging_toggle) return true diff --git a/lua/telescope/builtin/init.lua b/lua/telescope/builtin/init.lua index 85739e9..304e9f1 100644 --- a/lua/telescope/builtin/init.lua +++ b/lua/telescope/builtin/init.lua @@ -23,6 +23,7 @@ builtin.live_grep = require('telescope.builtin.files').live_grep builtin.grep_string = require('telescope.builtin.files').grep_string builtin.find_files = require('telescope.builtin.files').find_files builtin.fd = builtin.find_files +builtin.file_browser = require('telescope.builtin.files').file_browser builtin.treesitter = require('telescope.builtin.files').treesitter builtin.current_buffer_fuzzy_find = require('telescope.builtin.files').current_buffer_fuzzy_find builtin.tags = require('telescope.builtin.files').tags diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua index 2f23fad..4b13e7a 100644 --- a/lua/telescope/config.lua +++ b/lua/telescope/config.lua @@ -54,11 +54,11 @@ function config.set_defaults(defaults) set("borderchars", { '─', '│', '─', '│', '╭', '╮', '╯', '╰'}) set("get_status_text", function(self) - return string.format( - "%s / %s", - (self.stats.processed or 0) - (self.stats.filtered or 0), - self.stats.processed or 0 - ) + local xx = (self.stats.processed or 0) - (self.stats.filtered or 0) + local yy = self.stats.processed or 0 + if xx == 0 and yy == 0 then return "" end + + return string.format("%s / %s", xx, yy) end) -- Builtin configuration diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua index 5462559..7dc89ae 100644 --- a/lua/telescope/pickers.lua +++ b/lua/telescope/pickers.lua @@ -380,10 +380,9 @@ function Picker:find() prompt_prefix = prompt_prefix .. " " end vim.fn.prompt_setprompt(prompt_bufnr, prompt_prefix) - - a.nvim_buf_add_highlight(prompt_bufnr, ns_telescope_prompt_prefix, 'TelescopePromptPrefix', 0, 0, #prompt_prefix) end self.prompt_prefix = prompt_prefix + self:_reset_prefix_color() -- Temporarily disabled: Draw the screen ASAP. This makes things feel speedier. -- vim.cmd [[redraw]] @@ -444,6 +443,8 @@ function Picker:find() end end + self.__on_lines = on_lines + on_lines(nil, nil, nil, 0, 1) status_updater() @@ -633,6 +634,59 @@ function Picker:reset_selection() self.multi_select = {} end +function Picker:_reset_prefix_color(hl_group) + self._current_prefix_hl_group = hl_group or nil + + if self.prompt_prefix ~= '' then + vim.api.nvim_buf_add_highlight(self.prompt_bufnr, + ns_telescope_prompt_prefix, + self._current_prefix_hl_group or 'TelescopePromptPrefix', + 0, + 0, + #self.prompt_prefix + ) + end +end + +-- TODO(conni2461): Maybe _ prefix these next two functions +-- TODO(conni2461): Next two functions only work together otherwise color doesn't work +-- Probably a issue with prompt buffers +function Picker:change_prompt_prefix(new_prefix, hl_group) + if not new_prefix then return end + + if new_prefix ~= '' then + if not vim.endswith(new_prefix, " ") then + new_prefix = new_prefix .. " " + end + vim.fn.prompt_setprompt(self.prompt_bufnr, new_prefix) + else + vim.api.nvim_buf_set_text(self.prompt_bufnr, 0, 0, 0, #self.prompt_prefix, {}) + vim.api.nvim_buf_set_option(self.prompt_bufnr, 'buftype', '') + end + self.prompt_prefix = new_prefix + self:_reset_prefix_color(hl_group) +end + +function Picker:reset_prompt() + vim.api.nvim_buf_set_lines(self.prompt_bufnr, 0, -1, false, { self.prompt_prefix }) + self:_reset_prefix_color(self._current_prefix_hl_group) +end + +--- opts.new_prefix: Either as string or { new_string, hl_group } +--- opts.reset_prompt: bool +function Picker:refresh(finder, opts) + opts = opts or {} + if opts.new_prefix then + local handle = type(opts.new_prefix) == 'table' and unpack or function(x) return x end + self:change_prompt_prefix(handle(opts.new_prefix)) + end + if opts.reset_prompt then self:reset_prompt() end + + self.finder:close() + self.finder = finder + self.__on_lines(nil, nil, nil, 0, 1) +end + function Picker:set_selection(row) row = self.scroller(self.max_results, self.manager:num_results(), row) @@ -648,7 +702,6 @@ function Picker:set_selection(row) end local entry = self.manager:get_entry(self:get_index(row)) - local status = state.get_status(self.prompt_bufnr) local results_bufnr = self.results_bufnr if row > a.nvim_buf_line_count(results_bufnr) then @@ -723,17 +776,21 @@ function Picker:set_selection(row) self._selection_entry = entry self._selection_row = row + self:refresh_previewer() +end + +function Picker:refresh_previewer() + local status = state.get_status(self.prompt_bufnr) if status.preview_win and self.previewer then self:_increment("previewed") self.previewer:preview( - entry, + self._selection_entry, status ) end end - function Picker:entry_adder(index, entry, _, insert) local row = self:get_row(index) @@ -838,6 +895,10 @@ function Picker:_increment(key) self.stats[key] = (self.stats[key] or 0) + 1 end +function Picker:_decrement(key) + self.stats[key] = (self.stats[key] or 0) - 1 +end + -- TODO: Decide how much we want to use this. -- Would allow for better debugging of items. @@ -925,6 +986,10 @@ function Picker:get_result_processor(prompt, status_updater) return end + if entry.ignore_count ~= nil and entry.ignore_count == true then + self:_decrement("processed") + end + if sort_score == -1 then self:_increment("filtered") log.trace("Filtering out result: ", entry)