added support for nvim-dap (#101)

This commit is contained in:
bhagwan 2022-02-11 17:08:34 -08:00
parent da2a04d052
commit 930cc5f487
10 changed files with 366 additions and 21 deletions

View File

@ -201,6 +201,14 @@ vim.api.nvim_set_keymap('n', '<c-P>',
| `filetypes` | neovim filetypes |
| `packadd` | :packadd <package> |
### nvim-dap
| Command | List |
| -------------------- | ------------------------------------------ |
| `dap_commands` | list,run `nvim-dap` builtin commands |
| `dap_configurations` | list,run debug configurations |
| `dap_breakpoints` | list,delete breakpoints |
| `dap_variables` | active session variables |
| `dap_frames` | active session jump to frame |
### Neovim API
@ -573,7 +581,7 @@ require'fzf-lua'.setup {
fzf_opts = {
-- do not include bufnr in fuzzy matching
-- tiebreak by line no.
['--delimiter'] = vim.fn.shellescape(']'),
['--delimiter'] = vim.fn.shellescape('[\\]]'),
["--nth"] = '2..',
["--tiebreak"] = 'index',
},

View File

@ -236,6 +236,18 @@ MISC *fzf-lua-misc*
NVIM-DAP *fzf-lua-nvim-dap*
| Command | List |
| -------------------- | ------------------------------------------ |
| `dap_commands` | list,run `nvim-dap` builtin commands |
| `dap_configurations` | list,run debug configurations |
| `dap_breakpoints` | list,delete breakpoints |
| `dap_variables` | active session variables |
| `dap_frames` | active session jump to frame |
NEOVIM API *fzf-lua-neovim-api*
`:help vim.ui.select` for more info
@ -612,7 +624,7 @@ Consult the list below for available settings:
fzf_opts = {
-- do not include bufnr in fuzzy matching
-- tiebreak by line no.
['--delimiter'] = vim.fn.shellescape(']'),
['--delimiter'] = vim.fn.shellescape('[\\]]'),
["--nth"] = '2..',
["--tiebreak"] = 'index',
},

View File

@ -118,7 +118,9 @@ M.vimcmd_file = function(vimcmd, selected, opts)
vim.api.nvim_win_set_cursor(0, {1, 0})
vim.fn.search(entry.ctag, "W")
elseif entry.line>1 or entry.col>1 then
entry.col = entry.col or 1
-- make sure we have valid column
-- 'nvim-dap' for example sets columns to 0
entry.col = entry.col and entry.col>0 and entry.col or 1
vim.api.nvim_win_set_cursor(0, {tonumber(entry.line), tonumber(entry.col)-1})
end
if not is_term then vim.cmd("norm! zvzz") end
@ -284,9 +286,7 @@ M.colorscheme = function(selected)
vim.cmd("colorscheme " .. colorscheme)
end
M.run_builtin = function(selected)
local method = selected[1]
vim.cmd(string.format("lua require'fzf-lua'.%s()", method))
M.ensure_insert_mode = function()
-- not sure what is causing this, tested with
-- 'NVIM v0.6.0-dev+575-g2ef9d2a66'
-- vim.cmd("startinsert") doesn't start INSERT mode
@ -311,6 +311,12 @@ M.run_builtin = function(selected)
end
end
M.run_builtin = function(selected)
local method = selected[1]
vim.cmd(string.format("lua require'fzf-lua'.%s()", method))
M.ensure_insert_mode()
end
M.ex_run = function(selected)
local cmd = selected[1]
vim.cmd("stopinsert")

View File

@ -343,7 +343,7 @@ M.globals.lines = {
show_unlisted = false,
no_term_buffers = true,
fzf_opts = {
['--delimiter'] = vim.fn.shellescape(']'),
['--delimiter'] = vim.fn.shellescape('[\\]]'),
["--nth"] = '2..',
["--tiebreak"] = 'index',
},
@ -519,6 +519,33 @@ M.globals.nvim = {
},
}
M.globals.dap = {
commands = {
prompt = 'DAP Commands> ',
},
configurations = {
prompt = 'DAP Configurations> ',
},
variables = {
prompt = 'DAP Variables> ',
},
frames = {
prompt = 'DAP Frames> ',
},
breakpoints = {
prompt = 'DAP Breakpoints> ',
file_icons = true and M._has_devicons,
color_icons = true,
git_icons = true,
previewer = M._default_previewer_fn,
_actions = function() return M.globals.actions.files end,
fzf_opts = {
['--delimiter'] = vim.fn.shellescape('[\\]]'),
["--with-nth"] = '2..',
},
},
}
M.globals.file_icon_padding = ''
if not M._has_devicons then

View File

@ -151,6 +151,12 @@ M.lsp_workspace_diagnostics = require'fzf-lua.providers.lsp'.workspace_diagnosti
M.register_ui_select = require'fzf-lua.providers.ui_select'.register
M.deregister_ui_select = require'fzf-lua.providers.ui_select'.deregister
M.dap_commands = require'fzf-lua.providers.dap'.commands
M.dap_configurations = require'fzf-lua.providers.dap'.configurations
M.dap_breakpoints = require'fzf-lua.providers.dap'.breakpoints
M.dap_variables = require'fzf-lua.providers.dap'.variables
M.dap_frames = require'fzf-lua.providers.dap'.frames
-- API shortcuts
M.fzf = require'fzf-lua.core'.fzf
M.fzf_wrap = require'fzf-lua.core'.fzf_wrap

View File

@ -48,9 +48,9 @@ function Previewer.base:fzf_delimiter()
delim = '[:]'
elseif not delim:match(":") then
if delim:match("%[.*%]")then
delim = '[:' .. delim:match("%[(.*%])")
delim = delim:match("(%[.*)%]") .. ':]'
else
delim = '[:' .. delim .. ']'
delim = '[' .. delim .. ':]'
end
end
return delim

View File

@ -260,6 +260,8 @@ M.buffer_lines = function(opts)
opts.fzf_opts['--query'] = vim.fn.shellescape(opts.search)
end
opts = core.set_fzf_line_args(opts)
core.fzf_wrap(opts, items, function(selected)
if not selected then return end

View File

@ -0,0 +1,265 @@
local core = require "fzf-lua.core"
local path = require "fzf-lua.path"
local utils = require "fzf-lua.utils"
local config = require "fzf-lua.config"
local actions = require "fzf-lua.actions"
local _has_dap, _dap = nil, nil
local M = {}
-- attempt to load 'nvim-dap' every call
-- in case the plugin was lazy loaded
local function dap()
if _has_dap and _dap then return _dap end
_has_dap, _dap = pcall(require, 'dap')
if not _has_dap or not _dap then
utils.info("DAP requires 'mfussenegger/nvim-dap'")
return false
end
return true
end
M.commands = function(opts)
if not dap() then return end
opts = config.normalize_opts(opts, config.globals.dap.commands)
if not opts then return end
local entries = {}
for k, v in pairs(_dap) do
if type(v) == "function" then
table.insert(entries, k)
end
end
opts.actions = {
["default"] = opts.actions and opts.actions.default or
function(selected, _)
_dap[selected[1]]()
if require'fzf-lua.providers.ui_select'.is_registered() then
-- opening an fzf-lua win from another requires this
actions.ensure_insert_mode()
end
end,
}
opts.fzf_opts['--no-multi'] = ''
core.fzf_wrap(opts, entries, function(selected)
if not selected then return end
actions.act(opts.actions, selected)
end)()
end
M.configurations = function(opts)
if not dap() then return end
opts = config.normalize_opts(opts, config.globals.dap.configurations)
if not opts then return end
local entries = {}
opts._cfgs = {}
for lang, lang_cfgs in pairs(_dap.configurations) do
for _, cfg in ipairs(lang_cfgs) do
opts._cfgs[#entries+1] = cfg
table.insert(entries, ("[%s] %s. %s"):format(
utils.ansi_codes.green(lang),
utils.ansi_codes.magenta(tostring(#entries+1)),
cfg.name
))
end
end
opts.actions = {
["default"] = opts.actions and opts.actions.default or
function(selected, _)
-- cannot run while in session
if _dap.session() then return end
local idx = selected and tonumber(selected[1]:match("(%d+).")) or nil
if idx and opts._cfgs[idx] then
_dap.run(opts._cfgs[idx])
end
end,
}
opts.fzf_opts['--no-multi'] = ''
core.fzf_wrap(opts, entries, function(selected)
if not selected then return end
actions.act(opts.actions, selected)
end)()
end
M.breakpoints = function(opts)
if not dap() then return end
local dap_bps = require'dap.breakpoints'
opts = config.normalize_opts(opts, config.globals.dap.breakpoints)
if not opts then return end
-- so we can have accurate info on resume
opts.fn_pre_fzf = function()
opts._locations = dap_bps.to_qf_list(dap_bps.get())
end
-- run once to prevent opening an empty dialog
opts.fn_pre_fzf()
if vim.tbl_isempty(opts._locations) then
utils.info("Breakpoint list is empty.")
return
end
if not opts.cwd then opts.cwd = vim.loop.cwd() end
opts.actions = vim.tbl_deep_extend("keep", opts.actions or {},
{
["ctrl-x"] = opts.actions and opts.actions['ctrl-x'] or
{
function(selected, o)
for _, e in ipairs(selected) do
local entry = path.entry_to_file(e, o.cwd)
if entry.bufnr>0 and entry.line then
dap_bps.remove(entry.bufnr, entry.line)
end
end
end,
-- resume after bp deletion
actions.resume
}
})
local contents = function (cb)
local entries = {}
for _, entry in ipairs(opts._locations) do
table.insert(entries, core.make_entry_lcol(opts, entry))
end
for i, x in ipairs(entries) do
x = ("[%s] %s"):format(
-- tostring(opts._locations[i].bufnr),
utils.ansi_codes.yellow(tostring(opts._locations[i].bufnr)),
core.make_entry_file(opts, x))
if x then
cb(x, function(err)
if err then return end
-- close the pipe to fzf, this
-- removes the loading indicator in fzf
cb(nil, function() end)
end)
end
end
cb(nil, function() end)
end
if opts.fzf_opts['--header'] == nil then
opts.fzf_opts['--header'] = vim.fn.shellescape((':: %s to delete a Breakpoint')
:format(utils.ansi_codes.yellow("<Ctrl-x>")))
end
opts = core.set_fzf_line_args(opts)
core.fzf_wrap(opts, contents, function(selected)
if not selected then return end
actions.act(opts.actions, selected, opts)
end)()
end
M.variables = function(opts)
if not dap() then return end
opts = config.normalize_opts(opts, config.globals.dap.variables)
if not opts then return end
local session = _dap.session()
if not session then
utils.info("No active DAP session.")
return
end
local entries = {}
for _, s in pairs(session.current_frame.scopes or {}) do
if s.variables then
for _, v in pairs(s.variables) do
if v.type ~= '' and v.value ~= '' then
table.insert(entries, ("[%s] %s = %s"):format(
utils.ansi_codes.green(v.type),
-- utils.ansi_codes.red(v.name),
v.name,
v.value
))
end
end
end
end
core.fzf_wrap(opts, entries, function(selected)
if not selected then return end
actions.act(opts.actions, selected)
end)()
end
M.frames = function(opts)
if not dap() then return end
opts = config.normalize_opts(opts, config.globals.dap.frames)
if not opts then return end
local session = _dap.session()
if not session then
utils.info("No active DAP session.")
return
end
if not session.stopped_thread_id then
utils.info("Unable to switch frames unless stopped.")
return
end
opts._frames = session.threads[session.stopped_thread_id].frames
opts.actions = {
["default"] = opts.actions and opts.actions.default or
function(selected, o)
local sess = _dap.session()
if not sess or not sess.stopped_thread_id then return end
local idx = selected and tonumber(selected[1]:match("(%d+).")) or nil
if idx and o._frames[idx] then
session:_frame_set(o._frames[idx])
end
end,
}
local entries = {}
for i, f in ipairs(opts._frames) do
table.insert(entries, ("%s. [%s] %s%s"):format(
utils.ansi_codes.magenta(tostring(i)),
utils.ansi_codes.green(f.name),
f.source and f.source.name or '' ,
f.line and ((":%d"):format(f.line)) or ''
))
end
opts.fzf_opts['--no-multi'] = ''
core.fzf_wrap(opts, entries, function(selected)
if not selected then return end
actions.act(opts.actions, selected, opts)
end)()
end
return M

View File

@ -345,6 +345,29 @@ end
M.code_actions = function(opts)
opts = normalize_lsp_opts(opts, config.globals.lsp)
if not opts then return end
-- irrelevant for code actions and can cause
-- single results to be skipped with 'async = false'
opts.jump_to_single_result = false
opts.lsp_params = vim.lsp.util.make_range_params()
opts.lsp_params.context = {
diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
}
-- we use `vim.ui.select` for neovim > 0.6
-- so make sure 'set_lsp_fzf_fn' is run synchronously
if vim.fn.has('nvim-0.6') == 1 then
opts.sync, opts.async = true, false
end
-- when 'opts.sync == true' calls 'vim.lsp.buf_request_sync'
-- so we can avoid calling 'ui_select.register' when no code
-- actions are available
opts = set_lsp_fzf_fn(opts)
-- error or no sync request no results
if not opts.fzf_fn then return end
-- use `vim.ui.select` for neovim > 0.6
-- the original method is now deprecated
if vim.fn.has('nvim-0.6') == 1 then
@ -357,15 +380,11 @@ M.code_actions = function(opts)
end
ui_select.register(opts, true)
vim.lsp.buf.code_action()
-- vim.defer_fn(function()
-- ui_select.deregister({}, true, true)
-- end, 100)
return
end
-- irrelevant for code actions and can cause
-- single results to be skipped with 'async = false'
opts.jump_to_single_result = false
opts.lsp_params = vim.lsp.util.make_range_params()
opts.lsp_params.context = {
diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
}
-- see discussion in:
-- https://github.com/nvim-telescope/telescope.nvim/pull/738
@ -451,10 +470,6 @@ M.code_actions = function(opts)
opts.fzf_opts["--no-multi"] = ''
opts.fzf_opts["--preview-window"] = 'right:0'
opts.fzf_opts["--delimiter"] = vim.fn.shellescape(':')
opts = set_lsp_fzf_fn(opts)
-- error or no sync request no results
if not opts.fzf_fn then return end
core.fzf_wrap(opts, opts.fzf_fn, function(selected)

View File

@ -8,6 +8,10 @@ local M = {}
local _opts = nil
local _old_ui_select = nil
M.is_registered = function()
return vim.ui.select == M.ui_select
end
M.deregister = function(_, silent, noclear)
if not _old_ui_select then
if not silent then
@ -83,7 +87,7 @@ M.ui_select = function(items, opts, on_choice)
_opts = _opts or {}
_opts.fzf_opts = {
['--no-multi'] = '',
['--prompt'] = prompt:gsub(":$", "> "),
['--prompt'] = prompt:gsub(":%s?$", "> "),
['--preview-window'] = 'hidden:right:0',
}