@@ -10,22 +10,22 @@ M.defaults = {
10
10
},
11
11
}
12
12
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
13
25
local should_cache_lines = true
14
26
local cached_lines
15
27
local prev_lazyredraw
16
28
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
-
29
29
-- Inserts str_2 into str_1 at the given position.
30
30
local function string_insert (str_1 , str_2 , pos )
31
31
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
47
47
local line_a = splice (cached_lns [line ])
48
48
local line_b = splice (updated_lines [line ])
49
49
local line_diff = vim .diff (line_a , line_b , { result_type = " indices" })
50
- log (function ()
50
+ logger . trace (function ()
51
51
return (" Changed lines (line %d):\n Original: '%s' (len=%d)\n Updated: '%s' (len=%d)\n\n Inline hunks: %s" ):format (
52
52
line ,
53
53
cached_lns [line ],
@@ -85,6 +85,7 @@ local function add_inline_highlights(line, cached_lns, updated_lines, undo_delet
85
85
-- Observation: when changing "line" to "tes", there should not be an offset (-2)
86
86
-- after changing "lin" to "t" (because we are not modifying the line)
87
87
highlight .column = highlight .column + col_offset
88
+ highlight .hunk = nil
88
89
table.insert (highlights , highlight )
89
90
90
91
if defer then
@@ -104,10 +105,10 @@ local function get_diff_highlights(cached_lns, updated_lines, line_range, opts)
104
105
local hunks = vim .diff (table.concat (cached_lns , " \n " ), table.concat (updated_lines , " \n " ), {
105
106
result_type = " indices" ,
106
107
})
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 ]))
108
109
109
110
for i , hunk in ipairs (hunks ) do
110
- log (function ()
111
+ logger . trace (function ()
111
112
return (" Hunk %d/%d: %s" ):format (i , # hunks , vim .inspect (hunk ))
112
113
end )
113
114
@@ -123,7 +124,7 @@ local function get_diff_highlights(cached_lns, updated_lines, line_range, opts)
123
124
end_line = start_line + (count_a - count_b ) - 1
124
125
end
125
126
126
- log (function ()
127
+ logger . trace (function ()
127
128
return (" Lines %d-%d:\n Original: %s\n Updated: %s" ):format (
128
129
start_line ,
129
130
end_line ,
@@ -176,29 +177,35 @@ end
176
177
-- Expose functions to tests
177
178
M ._preview_across_lines = get_diff_highlights
178
179
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 )
185
192
end )
193
+ return remote .run_cmd (chan_id , cmd , cursor_row , cursor_col )
186
194
end
187
195
188
196
-- Called when the user is still typing the command or the command arguments
189
197
local function command_preview (opts , preview_ns , preview_buf )
190
198
-- Any errors that occur in the preview function are not directly shown to the user but stored in vim.v.errmsg.
191
199
-- Related: https://github.com/neovim/neovim/issues/18910.
192
200
vim .v .errmsg = " "
193
- logs = {}
194
201
local args = opts .cmd_args
195
202
local command = opts .command
196
203
197
- local bufnr = vim . api .nvim_get_current_buf ()
204
+ local bufnr = api .nvim_get_current_buf ()
198
205
if should_cache_lines then
199
206
prev_lazyredraw = vim .o .lazyredraw
200
207
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 )
202
209
should_cache_lines = false
203
210
end
204
211
@@ -207,10 +214,11 @@ local function command_preview(opts, preview_ns, preview_buf)
207
214
local prev_errmsg = vim .v .errmsg
208
215
local visible_line_range = { vim .fn .line (" w0" ), vim .fn .line (" w$" ) }
209
216
217
+ local updated_lines
210
218
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 ))
212
220
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 ))
214
222
end
215
223
216
224
vim .v .errmsg = prev_errmsg
@@ -220,20 +228,19 @@ local function command_preview(opts, preview_ns, preview_buf)
220
228
math.max (visible_line_range [2 ], vim .fn .line (" w$" )),
221
229
}
222
230
223
- local updated_lines = vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
224
231
local set_lines = function (lines )
225
232
-- 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 )
227
234
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 )
229
236
end
230
237
end
231
238
232
239
if not opts .line1 or not command .enable_highlighting then
233
240
set_lines (updated_lines )
234
241
-- This should not happen
235
242
if not opts .line1 then
236
- log (" No line1 range provided" , " ERROR " )
243
+ logger . error (" No line1 range provided" )
237
244
end
238
245
return 2
239
246
end
@@ -247,15 +254,15 @@ local function command_preview(opts, preview_ns, preview_buf)
247
254
undo_deletions = command .hl_groups [" deletion" ] ~= false ,
248
255
inline_highlighting = command .inline_highlighting ,
249
256
})
250
- log (function ()
257
+ logger . trace (function ()
251
258
return " Highlights: " .. vim .inspect (highlights )
252
259
end )
253
260
254
261
set_lines (updated_lines )
255
262
for _ , hl in ipairs (highlights ) do
256
263
local hl_group = command .hl_groups [hl .kind ]
257
264
if hl_group ~= false then
258
- vim . api .nvim_buf_add_highlight (
265
+ api .nvim_buf_add_highlight (
259
266
bufnr ,
260
267
preview_ns ,
261
268
hl_group ,
@@ -272,20 +279,20 @@ local function restore_buffer_state()
272
279
vim .o .lazyredraw = prev_lazyredraw
273
280
should_cache_lines = true
274
281
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 )))
276
283
end
277
284
end
278
285
279
286
local function execute_command (command )
280
- log (" Executing command: " .. command )
287
+ logger . trace (" Executing command: " .. command )
281
288
vim .cmd (command )
282
289
restore_buffer_state ()
283
290
end
284
291
285
292
local create_user_commands = function (commands )
286
293
for name , command in pairs (commands ) do
287
294
local args , range
288
- vim . api .nvim_create_user_command (name , function (opts )
295
+ api .nvim_create_user_command (name , function (opts )
289
296
local range_string = range and range
290
297
or (
291
298
opts .range == 2 and (" %s,%s" ):format (opts .line1 , opts .line2 )
@@ -341,7 +348,47 @@ local validate_config = function(config)
341
348
end
342
349
end
343
350
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 .sync (chan_id )
358
+ end ,
359
+ })
360
+
361
+ -- We need to be able to tell when the command was cancelled so the buffer lines are refetched next time
362
+ api .nvim_create_autocmd (" CmdLineLeave" , {
363
+ group = id ,
364
+ -- Schedule wrap to run after a potential command execution
365
+ callback = vim .schedule_wrap (function ()
366
+ restore_buffer_state ()
367
+ end ),
368
+ })
369
+
370
+ api .nvim_create_autocmd (" VimLeavePre" , {
371
+ group = id ,
372
+ callback = function ()
373
+ if chan_id then
374
+ vim .fn .chanclose (chan_id )
375
+ end
376
+ end ,
377
+ })
378
+
379
+ -- Setting dirty = true on FocusGained is important with multiple Nvim instances
380
+ api .nvim_create_autocmd ({ " TextChanged" , " TextChangedI" , " BufEnter" , " FocusGained" }, {
381
+ group = id ,
382
+ callback = remote .on_buffer_updated ,
383
+ })
384
+ end
385
+
344
386
M .setup = function (user_config )
387
+ -- Avoid an infinite loop when invoked from a child process
388
+ if vim .env .LIVECOMMAND_NVIM_SERVER == " 1" then
389
+ return
390
+ end
391
+
345
392
if vim .fn .has (" nvim-0.8.0" ) ~= 1 then
346
393
vim .notify (
347
394
" [live-command] This plugin requires at least Neovim 0.8. Please upgrade your Neovim version." ,
@@ -353,28 +400,21 @@ M.setup = function(user_config)
353
400
local config = vim .tbl_deep_extend (" force" , M .defaults , user_config or {})
354
401
validate_config (config )
355
402
create_user_commands (config .commands )
403
+ logger = require (" live-command.logger" )
404
+ remote = require (" live-command.remote" )
356
405
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
406
+ remote .init_rpc (logger , function (id )
407
+ chan_id = id
408
+ logger .set_chan_id (chan_id )
409
+ end )
410
+ create_autocmds ()
411
+ end
368
412
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 })
413
+ --- @param logger_ Logger
414
+ M ._set_logger = function (logger_ )
415
+ logger = logger_
376
416
end
377
417
378
- M .version = " 1.2.1 "
418
+ M .version = " 1.3.0 "
379
419
380
420
return M
0 commit comments