feat: v1 options menu (#133)

Still has a bunch of improvements that can be done, but wanted to merge in some of the related changes.

* options parser

* wip: vimoptions finder

* feat: pre-populate ex-command line with `:set foo=`

* use options current value when populating command line

* fix: use result.raw_value to store original option value

* .

* options: Continue work on option finder

* [WIP]: Tue 27 Oct 2020 10:34:09 PM EDT

* [WIP]: Mon 02 Nov 2020 08:20:13 PM EST

* [WIP]: Mon 02 Nov 2020 09:04:23 PM EST

Co-authored-by: TJ DeVries <devries.timothyj@gmail.com>
This commit is contained in:
Senghan Bright
2020-11-03 03:05:10 +01:00
committed by GitHub
parent 855d818a5d
commit 051aefdb8c
5 changed files with 303 additions and 14 deletions

View File

@@ -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("<esc>", 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 <buffer> ++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', '<CR>', edit_option)
map('n', '<CR>', edit_option)
return true
end
}):find()
end
builtin.help_tags = function(opts)
opts = opts or {}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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), "<TAB>"):gsub("", "<C-F>"):gsub(" ", "<Space>")
end
return utils