@@ -10,22 +10,22 @@ M.defaults = {
1010 },
1111}
1212
13+ local api = vim .api
14+
15+ --- @type Logger
16+ local logger
17+
18+ --- @class Remote
19+ local remote
20+
21+ --- @type number ?
22+ local chan_id
23+
24+ local cursor_row , cursor_col
1325local should_cache_lines = true
1426local cached_lines
1527local prev_lazyredraw
1628
17- local logs = {}
18- local function log (msg , level )
19- level = level or " TRACE"
20- if M .debug or level ~= " TRACE" then
21- msg = type (msg ) == " function" and msg () or msg
22- logs [level ] = logs [level ] or {}
23- for _ , line in ipairs (vim .split (msg .. " \n " , " \n " )) do
24- table.insert (logs [level ], line )
25- end
26- end
27- end
28-
2929-- Inserts str_2 into str_1 at the given position.
3030local function string_insert (str_1 , str_2 , pos )
3131 return str_1 :sub (1 , pos - 1 ) .. str_2 .. str_1 :sub (pos )
@@ -47,7 +47,7 @@ local function add_inline_highlights(line, cached_lns, updated_lines, undo_delet
4747 local line_a = splice (cached_lns [line ])
4848 local line_b = splice (updated_lines [line ])
4949 local line_diff = vim .diff (line_a , line_b , { result_type = " indices" })
50- log (function ()
50+ logger . trace (function ()
5151 return (" Changed lines (line %d):\n Original: '%s' (len=%d)\n Updated: '%s' (len=%d)\n\n Inline hunks: %s" ):format (
5252 line ,
5353 cached_lns [line ],
@@ -85,6 +85,7 @@ local function add_inline_highlights(line, cached_lns, updated_lines, undo_delet
8585 -- Observation: when changing "line" to "tes", there should not be an offset (-2)
8686 -- after changing "lin" to "t" (because we are not modifying the line)
8787 highlight .column = highlight .column + col_offset
88+ highlight .hunk = nil
8889 table.insert (highlights , highlight )
8990
9091 if defer then
@@ -104,10 +105,10 @@ local function get_diff_highlights(cached_lns, updated_lines, line_range, opts)
104105 local hunks = vim .diff (table.concat (cached_lns , " \n " ), table.concat (updated_lines , " \n " ), {
105106 result_type = " indices" ,
106107 })
107- log ((" Visible line range: %d-%d" ):format (line_range [1 ], line_range [2 ]))
108+ logger . trace ((" Visible line range: %d-%d" ):format (line_range [1 ], line_range [2 ]))
108109
109110 for i , hunk in ipairs (hunks ) do
110- log (function ()
111+ logger . trace (function ()
111112 return (" Hunk %d/%d: %s" ):format (i , # hunks , vim .inspect (hunk ))
112113 end )
113114
@@ -123,7 +124,7 @@ local function get_diff_highlights(cached_lns, updated_lines, line_range, opts)
123124 end_line = start_line + (count_a - count_b ) - 1
124125 end
125126
126- log (function ()
127+ logger . trace (function ()
127128 return (" Lines %d-%d:\n Original: %s\n Updated: %s" ):format (
128129 start_line ,
129130 end_line ,
@@ -176,29 +177,35 @@ end
176177-- Expose functions to tests
177178M ._preview_across_lines = get_diff_highlights
178179
179- local function run_buf_cmd (buf , cmd )
180- vim .api .nvim_buf_call (buf , function ()
181- log (function ()
182- return (" Previewing command: %s (current line = %d)" ):format (cmd , vim .api .nvim_win_get_cursor (0 )[1 ])
183- end )
184- vim .cmd (cmd )
180+ --- @param cmd string
181+ local function run_cmd (cmd )
182+ if not chan_id then
183+ logger .trace (" run_cmd: skipped as chan_id is not set" )
184+ return
185+ end
186+
187+ local cursor_pos = api .nvim_win_get_cursor (0 )
188+ cursor_row , cursor_col = cursor_pos [1 ], cursor_pos [2 ]
189+
190+ logger .trace (function ()
191+ return (" Previewing command: %s (l=%d,c=%d)" ):format (cmd , cursor_row , cursor_col )
185192 end )
193+ return remote .run_cmd (chan_id , cmd , cursor_row , cursor_col )
186194end
187195
188196-- Called when the user is still typing the command or the command arguments
189197local function command_preview (opts , preview_ns , preview_buf )
190198 -- Any errors that occur in the preview function are not directly shown to the user but stored in vim.v.errmsg.
191199 -- Related: https://github.com/neovim/neovim/issues/18910.
192200 vim .v .errmsg = " "
193- logs = {}
194201 local args = opts .cmd_args
195202 local command = opts .command
196203
197- local bufnr = vim . api .nvim_get_current_buf ()
204+ local bufnr = api .nvim_get_current_buf ()
198205 if should_cache_lines then
199206 prev_lazyredraw = vim .o .lazyredraw
200207 vim .o .lazyredraw = true
201- cached_lines = vim . api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
208+ cached_lines = api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
202209 should_cache_lines = false
203210 end
204211
@@ -207,10 +214,11 @@ local function command_preview(opts, preview_ns, preview_buf)
207214 local prev_errmsg = vim .v .errmsg
208215 local visible_line_range = { vim .fn .line (" w0" ), vim .fn .line (" w$" ) }
209216
217+ local updated_lines
210218 if opts .line1 == opts .line2 then
211- run_buf_cmd ( bufnr , (" %s %s" ):format (command .cmd , args ))
219+ updated_lines = run_cmd ( (" %s %s" ):format (command .cmd , args ))
212220 else
213- run_buf_cmd ( bufnr , (" %d,%d%s %s" ):format (opts .line1 , opts .line2 , command .cmd , args ))
221+ updated_lines = run_cmd ( (" %d,%d%s %s" ):format (opts .line1 , opts .line2 , command .cmd , args ))
214222 end
215223
216224 vim .v .errmsg = prev_errmsg
@@ -220,20 +228,19 @@ local function command_preview(opts, preview_ns, preview_buf)
220228 math.max (visible_line_range [2 ], vim .fn .line (" w$" )),
221229 }
222230
223- local updated_lines = vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
224231 local set_lines = function (lines )
225232 -- TODO: is this worth optimizing?
226- vim . api .nvim_buf_set_lines (bufnr , 0 , - 1 , false , lines )
233+ api .nvim_buf_set_lines (bufnr , 0 , - 1 , false , lines )
227234 if preview_buf then
228- vim . api .nvim_buf_set_lines (preview_buf , 0 , - 1 , false , lines )
235+ api .nvim_buf_set_lines (preview_buf , 0 , - 1 , false , lines )
229236 end
230237 end
231238
232239 if not opts .line1 or not command .enable_highlighting then
233240 set_lines (updated_lines )
234241 -- This should not happen
235242 if not opts .line1 then
236- log (" No line1 range provided" , " ERROR " )
243+ logger . error (" No line1 range provided" )
237244 end
238245 return 2
239246 end
@@ -247,15 +254,15 @@ local function command_preview(opts, preview_ns, preview_buf)
247254 undo_deletions = command .hl_groups [" deletion" ] ~= false ,
248255 inline_highlighting = command .inline_highlighting ,
249256 })
250- log (function ()
257+ logger . trace (function ()
251258 return " Highlights: " .. vim .inspect (highlights )
252259 end )
253260
254261 set_lines (updated_lines )
255262 for _ , hl in ipairs (highlights ) do
256263 local hl_group = command .hl_groups [hl .kind ]
257264 if hl_group ~= false then
258- vim . api .nvim_buf_add_highlight (
265+ api .nvim_buf_add_highlight (
259266 bufnr ,
260267 preview_ns ,
261268 hl_group ,
@@ -272,20 +279,20 @@ local function restore_buffer_state()
272279 vim .o .lazyredraw = prev_lazyredraw
273280 should_cache_lines = true
274281 if vim .v .errmsg ~= " " then
275- log ((" An error occurred in the preview function:\n %s" ):format (vim .inspect (vim .v .errmsg )), " ERROR " )
282+ logger . error ((" An error occurred in the preview function:\n %s" ):format (vim .inspect (vim .v .errmsg )))
276283 end
277284end
278285
279286local function execute_command (command )
280- log (" Executing command: " .. command )
287+ logger . trace (" Executing command: " .. command )
281288 vim .cmd (command )
282289 restore_buffer_state ()
283290end
284291
285292local create_user_commands = function (commands )
286293 for name , command in pairs (commands ) do
287294 local args , range
288- vim . api .nvim_create_user_command (name , function (opts )
295+ api .nvim_create_user_command (name , function (opts )
289296 local range_string = range and range
290297 or (
291298 opts .range == 2 and (" %s,%s" ):format (opts .line1 , opts .line2 )
@@ -341,7 +348,57 @@ local validate_config = function(config)
341348 end
342349end
343350
351+ local create_autocmds = function ()
352+ local id = api .nvim_create_augroup (" command_preview.nvim" , { clear = true })
353+
354+ api .nvim_create_autocmd (" CmdlineEnter" , {
355+ group = id ,
356+ callback = function ()
357+ remote .init_rpc (logger , function (chan_id_ )
358+ chan_id = chan_id_
359+ end )
360+ end ,
361+ once = true ,
362+ })
363+
364+ api .nvim_create_autocmd (" CmdlineEnter" , {
365+ group = id ,
366+ callback = function ()
367+ remote .sync (chan_id )
368+ end ,
369+ })
370+
371+ -- We need to be able to tell when the command was cancelled so the buffer lines are refetched next time
372+ api .nvim_create_autocmd (" CmdLineLeave" , {
373+ group = id ,
374+ -- Schedule wrap to run after a potential command execution
375+ callback = vim .schedule_wrap (function ()
376+ restore_buffer_state ()
377+ end ),
378+ })
379+
380+ api .nvim_create_autocmd (" VimLeavePre" , {
381+ group = id ,
382+ callback = function ()
383+ if chan_id then
384+ vim .fn .chanclose (chan_id )
385+ end
386+ end ,
387+ })
388+
389+ -- Setting dirty = true on FocusGained is important with multiple Nvim instances
390+ api .nvim_create_autocmd ({ " TextChanged" , " TextChangedI" , " BufEnter" , " FocusGained" }, {
391+ group = id ,
392+ callback = remote .on_buffer_updated ,
393+ })
394+ end
395+
344396M .setup = function (user_config )
397+ -- Avoid an infinite loop when invoked from a child process
398+ if vim .env .LIVECOMMAND_NVIM_SERVER == " 1" then
399+ return
400+ end
401+
345402 if vim .fn .has (" nvim-0.8.0" ) ~= 1 then
346403 vim .notify (
347404 " [live-command] This plugin requires at least Neovim 0.8. Please upgrade your Neovim version." ,
@@ -353,28 +410,16 @@ M.setup = function(user_config)
353410 local config = vim .tbl_deep_extend (" force" , M .defaults , user_config or {})
354411 validate_config (config )
355412 create_user_commands (config .commands )
413+ logger = require (" live-command.logger" )
414+ remote = require (" live-command.remote" )
415+ create_autocmds ()
416+ end
356417
357- local id = vim .api .nvim_create_augroup (" command_preview.nvim" , { clear = true })
358- -- We need to be able to tell when the command was cancelled so the buffer lines are refetched next time.
359- vim .api .nvim_create_autocmd ({ " CmdLineLeave" }, {
360- group = id ,
361- -- Schedule wrap to run after a potential command execution
362- callback = vim .schedule_wrap (function ()
363- restore_buffer_state ()
364- end ),
365- })
366-
367- M .debug = user_config .debug
368-
369- vim .api .nvim_create_user_command (" LiveCommandLog" , function ()
370- local msg = (" live-command log\n ================\n\n %s%s" ):format (
371- logs .ERROR and " [ERROR]\n " .. table.concat (logs .ERROR , " \n " ) .. (logs .TRACE and " \n " or " " ) or " " ,
372- logs .TRACE and " [TRACE]\n " .. table.concat (logs .TRACE , " \n " ) or " "
373- )
374- vim .notify (msg )
375- end , { nargs = 0 })
418+ --- @param logger_ Logger
419+ M ._set_logger = function (logger_ )
420+ logger = logger_
376421end
377422
378- M .version = " 1.2.1 "
423+ M .version = " 1.3.0 "
379424
380425return M
0 commit comments