Skip to content

Commit f357e74

Browse files
committed
feat: Add multiline input for repl
1 parent 6ae8a14 commit f357e74

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

lua/dap.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ local DAP_QUICKFIX_CONTEXT = DAP_QUICKFIX_TITLE
166166
---@field options nil|AdapterOptions
167167
---@field enrich_config? fun(config: Configuration, on_config: fun(config: Configuration))
168168
---@field reverse_request_handlers? table<string, fun(session: Session, request: dap.Request)>
169+
---@field is_multiline? fun(current_inputs: string[]): boolean
169170

170171
---@class AdapterOptions
171172
---@field initialize_timeout_sec nil|number

lua/dap/repl.lua

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,31 @@ local function get_session()
1616
return require('dap').session()
1717
end
1818

19+
local repl
1920
local execute -- required for forward reference
21+
local function execute_current(opts)
22+
local current_text = api.nvim_buf_get_lines(repl.buf, -2, -1, false)[1]
23+
local prompt_length = #vim.fn.prompt_getprompt(repl.buf)
24+
execute(string.sub(current_text, prompt_length + 1), opts)
25+
end
26+
27+
28+
local MULTILINE_INPUT = 'MULTILINE_INPUT'
29+
30+
---@param buf number
31+
---@return string[]
32+
local function get_multiline_input(buf)
33+
return vim.b[buf][MULTILINE_INPUT] or {}
34+
end
35+
36+
local function get_prompt(buf)
37+
buf = buf or repl.buf
38+
if #get_multiline_input(buf) == 0 then
39+
return 'dap> '
40+
else
41+
return '...> '
42+
end
43+
end
2044

2145

2246
local function new_buf()
@@ -34,7 +58,11 @@ local function new_buf()
3458
api.nvim_buf_set_keymap(buf, 'n', 'o', "<Cmd>lua require('dap.ui').trigger_actions()<CR>", {})
3559
api.nvim_buf_set_keymap(buf, 'i', '<up>', "<Cmd>lua require('dap.repl').on_up()<CR>", {})
3660
api.nvim_buf_set_keymap(buf, 'i', '<down>', "<Cmd>lua require('dap.repl').on_down()<CR>", {})
37-
vim.fn.prompt_setprompt(buf, 'dap> ')
61+
62+
-- CR keybindings may require additional configuration for some terminals, see https://stackoverflow.com/a/42461580
63+
vim.keymap.set('i', '<S-CR>', function() execute_current({force_multiline = true}) end, {buffer = buf})
64+
vim.keymap.set('i', '<C-CR>', function() execute_current({force_finish = true}) end, {buffer = buf})
65+
vim.fn.prompt_setprompt(buf, get_prompt(buf))
3866
vim.fn.prompt_setcallback(buf, execute)
3967
if vim.fn.has('nvim-0.7') == 1 then
4068
vim.keymap.set('n', 'G', function()
@@ -67,7 +95,7 @@ local function new_win(buf, winopts, wincmd)
6795
return win
6896
end
6997

70-
local repl = ui.new_view(
98+
repl = ui.new_view(
7199
new_buf,
72100
new_win, {
73101
before_open = function()
@@ -212,7 +240,34 @@ local function print_threads(threads)
212240
end
213241

214242

215-
function execute(text)
243+
---@return string|nil full_input nil if input is incomplete
244+
local function handle_multiline_input(last_line, force_multiline, force_finish)
245+
local session = get_session()
246+
if not session then
247+
return last_line
248+
end
249+
local current_inputs = get_multiline_input(repl.buf)
250+
table.insert(current_inputs, last_line)
251+
local is_multiline = session.adapter.is_multiline
252+
if not force_finish and (force_multiline or (is_multiline and is_multiline(current_inputs))) then
253+
vim.b[repl.buf][MULTILINE_INPUT] = current_inputs
254+
if #current_inputs == 1 then
255+
vim.fn.prompt_setprompt(repl.buf, get_prompt())
256+
end
257+
return nil
258+
end
259+
vim.b[repl.buf][MULTILINE_INPUT] = nil
260+
vim.fn.prompt_setprompt(repl.buf, get_prompt())
261+
return table.concat(current_inputs, '\n')
262+
end
263+
264+
function execute(text, opts)
265+
opts = opts or {}
266+
text = handle_multiline_input(text, opts.force_multiline, opts.force_finish)
267+
if text == nil then
268+
return
269+
end
270+
216271
if text == '' then
217272
if history.last then
218273
text = history.last
@@ -337,9 +392,9 @@ local function select_history(delta)
337392
history.idx = 1
338393
end
339394
local text = history.entries[history.idx]
340-
if text then
395+
if text and string.find(text, '\n') == nil then
341396
local lnum = vim.fn.line('$')
342-
api.nvim_buf_set_lines(repl.buf, lnum - 1, lnum, true, {'dap> ' .. text })
397+
api.nvim_buf_set_lines(repl.buf, lnum - 1, lnum, true, {get_prompt() .. text })
343398
vim.fn.setcursorcharpos({ lnum, vim.fn.col('$') }) -- move cursor to the end of line
344399
end
345400
end
@@ -439,7 +494,7 @@ do
439494
local session = get_session()
440495
local col = api.nvim_win_get_cursor(0)[2]
441496
local line = api.nvim_get_current_line()
442-
local offset = vim.startswith(line, 'dap> ') and 5 or 0
497+
local offset = vim.startswith(line, get_prompt()) and 5 or 0
443498
local line_to_cursor = line:sub(offset + 1, col)
444499
local text_match = vim.fn.match(line_to_cursor, '\\k*$')
445500
if vim.startswith(line_to_cursor, '.') or base ~= '' then

0 commit comments

Comments
 (0)