diff --git a/lua/telescope/builtin.lua b/lua/telescope/builtin.lua index e90797d..d4030ae 100644 --- a/lua/telescope/builtin.lua +++ b/lua/telescope/builtin.lua @@ -24,14 +24,13 @@ if 2 > vim.o.report then end --- TODO: Give some bonus weight to files we've picked before --- TODO: Give some bonus weight to oldfiles - local actions = require('telescope.actions') local finders = require('telescope.finders') +local log = require('telescope.log') local make_entry = require('telescope.make_entry') -local previewers = require('telescope.previewers') +local path = require('telescope.path') local pickers = require('telescope.pickers') +local previewers = require('telescope.previewers') local sorters = require('telescope.sorters') local utils = require('telescope.utils') @@ -46,11 +45,17 @@ local builtin = {} builtin.git_files = function(opts) opts = opts or {} + local show_untracked = utils.get_default(opts.show_untracked, true) + if opts.cwd then opts.cwd = vim.fn.expand(opts.cwd) else --- Find root of git directory and remove trailing newline characters - opts.cwd = string.gsub(vim.fn.system("git rev-parse --show-toplevel"), '[\n\r]+', '') + opts.cwd = vim.fn.systemlist("git rev-parse --show-toplevel")[1] + + if not vim.fn.isdirectory(opts.cwd) then + error("Not a working directory for git_files:", opts.cwd) + end end -- By creating the entry maker after the cwd options, @@ -60,7 +65,7 @@ builtin.git_files = function(opts) pickers.new(opts, { prompt_title = 'Git File', finder = finders.new_oneshot_job( - { "git", "ls-tree", "--full-tree", "-r", "--name-only", "HEAD" }, + { "git", "ls-files", "--exclude-standard", "--cached", show_untracked and "--others" }, opts ), previewer = previewers.cat.new(opts), @@ -412,6 +417,72 @@ builtin.command_history = function(opts) }):find() end +builtin.vim_options = function(opts) + opts = opts or {} + + -- Load vim options. + local vim_opts = loadfile(utils.data_directory() .. path.separator .. 'options' .. path.separator .. 'options.lua')().options + + pickers.new(opts, { + prompt = 'options', + finder = finders.new_table { + results = vim_opts, + entry_maker = make_entry.gen_from_vimoptions(opts), + }, + -- TODO: previewer for Vim options + -- previewer = previewers.help.new(opts), + sorter = sorters.get_fzy_sorter(), + attach_mappings = function(prompt_bufnr, map) + local edit_option = function() + local selection = actions.get_selected_entry(prompt_bufnr) + local esc = "" + + + if vim.fn.mode() == "i" then + -- TODO: don't make this local + esc = vim.api.nvim_replace_termcodes("", true, false, true) + end + + -- TODO: Make this actually work. + + -- actions.close(prompt_bufnr) + -- vim.api.nvim_win_set_var(vim.fn.nvim_get_current_win(), "telescope", 1) + -- print(prompt_bufnr) + -- print(vim.fn.bufnr()) + -- vim.cmd([[ autocmd BufEnter ++nested ++once startinsert!]]) + -- print(vim.fn.winheight(0)) + + -- local prompt_winnr = vim.fn.getbufinfo(prompt_bufnr)[1].windows[1] + -- print(prompt_winnr) + + -- local float_opts = {} + -- float_opts.relative = "editor" + -- float_opts.anchor = "sw" + -- float_opts.focusable = false + -- float_opts.style = "minimal" + -- float_opts.row = vim.api.nvim_get_option("lines") - 2 -- TODO: include `cmdheight` and `laststatus` in this calculation + -- float_opts.col = 2 + -- float_opts.height = 10 + -- float_opts.width = string.len(selection.last_set_from)+15 + -- local buf = vim.fn.nvim_create_buf(false, true) + -- vim.fn.nvim_buf_set_lines(buf, 0, 0, false, {"default value: abcdef", "last set from: " .. selection.last_set_from}) + -- local status_win = vim.fn.nvim_open_win(buf, false, float_opts) + -- -- vim.api.nvim_win_set_option(status_win, "winblend", 100) + -- vim.api.nvim_win_set_option(status_win, "winhl", "Normal:PmenuSel") + -- -- vim.api.nvim_set_current_win(status_win) + -- vim.cmd[[redraw!]] + -- vim.cmd("autocmd CmdLineLeave : ++once echom 'beep'") + vim.api.nvim_feedkeys(string.format("%s:set %s=%s", esc, selection.name, selection.current_value), "m", true) + end + + map('i', '', edit_option) + map('n', '', edit_option) + + return true + end + }):find() +end + builtin.help_tags = function(opts) opts = opts or {} diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index 76e5275..7707122 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -1,6 +1,7 @@ local has_devicons, devicons = pcall(require, 'nvim-web-devicons') local conf = require('telescope.config').values +local entry_display = require('telescope.pickers.entry_display') local path = require('telescope.path') local utils = require('telescope.utils') @@ -465,4 +466,110 @@ function make_entry.gen_from_marks(_) end end +function make_entry.gen_from_vimoptions(opts) + -- TODO: Can we just remove this from `options.lua`? + function N_(s) + return s + end + + local process_one_opt = function(o) + local ok, value_origin + + local option = { + name = "", + description = "", + current_value = "", + default_value = "", + value_type = "", + set_by_user = false, + last_set_from = "", + } + + local is_global = false + for _, v in ipairs(o.scope) do + if v == "global" then + is_global = true + end + end + + if not is_global then + return + end + + if is_global then + option.name = o.full_name + + ok, option.current_value = pcall(vim.api.nvim_get_option, o.full_name) + if not ok then + return + end + + local str_funcname = o.short_desc() + option.description = assert(loadstring("return " .. str_funcname))() + -- if #option.description > opts.desc_col_length then + -- opts.desc_col_length = #option.description + -- end + + if o.defaults ~= nil then + option.default_value = o.defaults.if_true.vim or o.defaults.if_true.vi + end + + if type(option.default_value) == "function" then + option.default_value = "Macro: " .. option.default_value() + end + + option.value_type = (type(option.current_value) == "boolean" and "bool" or type(option.current_value)) + + if option.current_value ~= option.default_value then + option.set_by_user = true + value_origin = vim.fn.execute("verbose set " .. o.full_name .. "?") + if string.match(value_origin, "Last set from") then + -- TODO: parse file and line number as separate items + option.last_set_from = value_origin:gsub("^.*Last set from ", "") + end + end + + return option + end + end + + -- TODO: don't call this 'line' + local displayer = entry_display.create { + separator = "│", + items = { + { width = 25 }, + { width = 50 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + + return displayer { + entry.name, + string.format( + "[%s] %s", + entry.value_type, + utils.display_termcodes(tostring(entry.current_value))), + entry.description, + } + end + + return function(line) + local entry = process_one_opt(line) + if not entry then + return + end + + entry.valid = true + entry.display = make_display + entry.value = line + entry.ordinal = line.full_name + -- entry.raw_value = d.raw_value + -- entry.last_set_from = d.last_set_from + + return entry + end +end + return make_entry diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua index d4b5d71..fea687d 100644 --- a/lua/telescope/pickers.lua +++ b/lua/telescope/pickers.lua @@ -7,12 +7,14 @@ local actions = require('telescope.actions') local config = require('telescope.config') local debounce = require('telescope.debounce') local resolve = require('telescope.config.resolve') -local layout_strategies = require('telescope.pickers.layout_strategies') local log = require('telescope.log') local mappings = require('telescope.mappings') local state = require('telescope.state') local utils = require('telescope.utils') +local layout_strategies = require('telescope.pickers.layout_strategies') +local entry_display = require('telescope.pickers.entry_display') + local EntryManager = require('telescope.entry_manager') local get_default = utils.get_default @@ -307,7 +309,9 @@ function Picker:find() local results_win, results_opts = popup.create('', popup_opts.results) local results_bufnr = a.nvim_win_get_buf(results_win) + self.results_bufnr = results_bufnr + self.results_win = results_win -- TODO: Should probably always show all the line for results win, so should implement a resize for the windows a.nvim_win_set_option(results_win, 'wrap', false) @@ -785,13 +789,8 @@ function Picker:entry_adder(index, entry, score) return end - local display, display_highlights - if type(entry.display) == 'function' then - self:_increment("display_fn") - display, display_highlights = entry:display() - elseif type(entry.display) == 'string' then - display = entry.display - else + local display, display_highlights = entry_display.resolve(self, entry) + if not display then log.info("Weird entry", entry) return end diff --git a/lua/telescope/pickers/entry_display.lua b/lua/telescope/pickers/entry_display.lua new file mode 100644 index 0000000..c661b89 --- /dev/null +++ b/lua/telescope/pickers/entry_display.lua @@ -0,0 +1,100 @@ +local log = require('telescope.log') + +local entry_display = {} + +-- index are used to determine the correct order +-- elements = { +-- [1] = { element, max width }, -- max width should be greater than 0 +-- [2] = { a, 0 } -- Use 0 to disable max width +-- [3] = { b, 0 } -- If b is nil, skip this column, should skip column for all rows +-- }, +-- separator = " " -- either arbitrary string, when you wanna use the same separator between all elements +-- separator = { " ", ":" } -- or table, where [1] is separator between elements[1] and elements[2], etc + +-- TODO: Remove this and move ONLY to create method. + +local table_format = function(picker, elements, separator) + -- TODO: Truncate... + local win_width = vim.api.nvim_win_get_width(picker.results_win) + + local output = "" + for k, v in ipairs(elements) do + local text = v[1] + local width = v[2] + if text ~= nil then + if k > 1 then + output = output .. (type(separator) == "table" and separator[k - 1] or separator) + end + if width then + if width == 0 then + output = output .. string.format("%s", text) + elseif width < 1 then + output = output .. string.format("%-" .. math.floor(width * win_width) .. "s", text) + else + output = output .. string.format("%-" .. width .."s", text) + end + else + output = output .. text + end + end + end + return output +end + +local function truncate(str, len) + -- TODO: This doesn't handle multi byte chars... + if vim.fn.strdisplaywidth(str) > len - 1 then + str = str:sub(1, len) + str = str .. "…" + end + return str +end + +entry_display.create = function(configuration) + local generator = {} + for _, v in ipairs(configuration.items) do + if v.width then + local justify = not v.right_justify and "-" or "" + local format_str = "%" .. justify .. v.width .. "s" + table.insert(generator, function(item) + return string.format(format_str, truncate(item, v.width)) + end) + else + table.insert(generator, function(item) + return item + end) + end + end + + return function(self, picker) + local results = {} + for k, v in ipairs(self) do + table.insert(results, generator[k](v, picker)) + end + + return table.concat(results, configuration.separator or "│") + end +end + + +entry_display.resolve = function(self, entry) + local display, display_highlights + if type(entry.display) == 'function' then + self:_increment("display_fn") + display, display_highlights = entry:display(self) + + if type(display) == 'string' then + return display, display_highlights + end + else + display = entry.display + end + + if type(display) == 'string' then + return display, display_highlights + elseif type(display) == 'table' then + return table_format(self, display, "│"), display_highlights + end +end + +return entry_display diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index 58d8183..1daea57 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -178,4 +178,16 @@ function utils.max_split(s, pattern, maxsplit) return t end + +function utils.data_directory() + local sourced_file = require('plenary.debug_utils').sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h") + + return base_directory .. pathlib.separator .. 'data' .. pathlib.separator +end + +function utils.display_termcodes(str) + return str:gsub(string.char(9), ""):gsub("", ""):gsub(" ", "") +end + return utils