@@ -9,25 +9,65 @@ local function get_image()
99 return image
1010end
1111
12- -- Helper function to safely set buffer lines
13- local function safe_set_buffer_lines (bufnr , start , end_line , strict_indexing , lines )
14- if not bufnr or not vim .api .nvim_buf_is_valid (bufnr ) then return false end
12+ -- Set lines in preview buffer (simpler approach)
13+ local function set_buffer_lines (bufnr , lines )
14+ if not bufnr or not vim .api .nvim_buf_is_valid (bufnr ) then return end
1515
16- -- Make buffer modifiable temporarily
17- local was_modifiable = vim .api .nvim_buf_get_option (bufnr , ' modifiable' )
1816 vim .api .nvim_buf_set_option (bufnr , ' modifiable' , true )
17+ vim .api .nvim_buf_set_lines (bufnr , 0 , - 1 , false , lines )
18+ vim .api .nvim_buf_set_option (bufnr , ' modifiable' , false )
19+ end
20+
21+ -- Check if file is already loaded in a buffer
22+ local function find_existing_buffer (file_path )
23+ local abs_path = vim .fn .resolve (vim .fn .fnamemodify (file_path , ' :p' ))
24+
25+ for _ , bufnr in ipairs (vim .api .nvim_list_bufs ()) do
26+ if vim .api .nvim_buf_is_loaded (bufnr ) then
27+ local buf_name = vim .api .nvim_buf_get_name (bufnr )
28+ if buf_name ~= ' ' then
29+ local buf_path = vim .fn .resolve (vim .fn .fnamemodify (buf_name , ' :p' ))
30+ if buf_path == abs_path then return bufnr end
31+ end
32+ end
33+ end
34+ return nil
35+ end
1936
20- -- Set lines
21- local ok , err = pcall (vim .api .nvim_buf_set_lines , bufnr , start , end_line , strict_indexing , lines )
37+ local function read_file_fast (file_path , max_lines )
38+ local fd = vim .uv .fs_open (file_path , ' r' , 0x666 )
39+ if not fd then return nil end
2240
23- -- Restore modifiable state
24- vim .api .nvim_buf_set_option (bufnr , ' modifiable' , was_modifiable )
41+ local stat = vim .uv .fs_fstat (fd )
42+ if not stat then
43+ vim .uv .fs_close (fd )
44+ return nil
45+ end
2546
26- if not ok then
27- vim .notify (' Error setting buffer lines: ' .. err , vim .log .levels .WARN )
28- return false
47+ local data = vim .uv .fs_read (fd , stat .size , 0 )
48+ vim .uv .fs_close (fd )
49+
50+ if not data then return nil end
51+
52+ local lines = vim .split (data , ' \n ' , { plain = true })
53+ if max_lines and # lines > max_lines then
54+ local limited_lines = {}
55+ for i = 1 , max_lines do
56+ table.insert (limited_lines , lines [i ])
57+ end
58+ lines = limited_lines
2959 end
3060
61+ return lines
62+ end
63+
64+ local function copy_buffer_content (source_bufnr , target_bufnr )
65+ local lines = vim .api .nvim_buf_get_lines (source_bufnr , 0 , - 1 , false )
66+ set_buffer_lines (target_bufnr , lines )
67+
68+ local source_ft = vim .api .nvim_buf_get_option (source_bufnr , ' filetype' )
69+ if source_ft ~= ' ' then vim .api .nvim_buf_set_option (target_bufnr , ' filetype' , source_ft ) end
70+
3171 return true
3272end
3373
@@ -289,33 +329,55 @@ function M.preview_file(file_path, bufnr)
289329 ' ' ,
290330 ' Use a text editor to view this file.' ,
291331 }
292- safe_set_buffer_lines (bufnr , 0 , - 1 , false , lines )
332+ set_buffer_lines (bufnr , lines )
293333 return true
294334 end
295335
336+ -- if the buffer is already opened for this file we reuse the buffer directly
337+ local existing_bufnr = find_existing_buffer (file_path )
338+
339+ if existing_bufnr then
340+ local success = copy_buffer_content (existing_bufnr , bufnr )
341+ if success then
342+ local file_config = M .get_file_config (file_path )
343+
344+ vim .api .nvim_buf_set_option (bufnr , ' modifiable' , false )
345+ vim .api .nvim_buf_set_option (bufnr , ' readonly' , true )
346+ vim .api .nvim_buf_set_option (bufnr , ' buftype' , ' nofile' )
347+ vim .api .nvim_buf_set_option (bufnr , ' wrap' , file_config .wrap_lines or M .config .wrap_lines )
348+ vim .api .nvim_buf_set_option (bufnr , ' number' , M .config .line_numbers )
349+
350+ local content = vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
351+ M .state .content_height = # content
352+ M .state .scroll_offset = 0
353+
354+ return true
355+ end
356+ end
357+
358+ -- Fallback: Manual file reading with fast UV operations
296359 local file_config = M .get_file_config (file_path )
297360
298361 local content
299362 if file_config .tail_lines then
300363 content = M .read_file_tail (file_path , file_config .tail_lines )
301- if content then
302- -- Add virtual text showing tail lines are showed
303- end
304364 else
305- content = M .read_file_content (file_path , M .config .max_lines )
365+ content = read_file_fast (file_path , M .config .max_lines )
366+ if not content then content = M .read_file_content (file_path , M .config .max_lines ) end
306367 end
307368
308369 if not content then return false end
309370
310- safe_set_buffer_lines (bufnr , 0 , - 1 , false , content )
371+ set_buffer_lines (bufnr , content )
311372
312373 vim .api .nvim_buf_set_option (bufnr , ' filetype' , info .filetype )
313374 vim .api .nvim_buf_set_option (bufnr , ' modifiable' , false )
314375 vim .api .nvim_buf_set_option (bufnr , ' readonly' , true )
315376 vim .api .nvim_buf_set_option (bufnr , ' buftype' , ' nofile' )
316377 vim .api .nvim_buf_set_option (bufnr , ' wrap' , file_config .wrap_lines or M .config .wrap_lines )
378+ vim .api .nvim_buf_set_option (bufnr , ' number' , M .config .line_numbers )
317379
318- M .state .content_height = content
380+ M .state .content_height = # content
319381 M .state .scroll_offset = 0
320382
321383 return true
@@ -324,12 +386,10 @@ end
324386--- Preview a binary file
325387--- @param file_path string Path to the file
326388--- @param bufnr number Buffer number for preview
327- --- @param info table File information
328- --- @param file table | nil Optional file information from search results for debug info
329389--- @return boolean Success status
330- function M .preview_binary_file (file_path , bufnr , info , file )
390+ function M .preview_binary_file (file_path , bufnr )
331391 local lines = {}
332-
392+ local info = M . get_file_info ( file_path )
333393 table.insert (lines , ' ⚠ Binary File Detected' )
334394 table.insert (lines , ' ' )
335395 table.insert (lines , ' This file contains binary data and cannot be displayed as text.' )
@@ -363,7 +423,7 @@ function M.preview_binary_file(file_path, bufnr, info, file)
363423 table.insert (lines , ' Use a hex editor or appropriate application to view this file.' )
364424 end
365425
366- safe_set_buffer_lines (bufnr , 0 , - 1 , false , lines )
426+ set_buffer_lines (bufnr , lines )
367427 vim .api .nvim_buf_set_option (bufnr , ' filetype' , ' text' )
368428 vim .api .nvim_buf_set_option (bufnr , ' modifiable' , false )
369429 vim .api .nvim_buf_set_option (bufnr , ' readonly' , true )
@@ -377,95 +437,67 @@ end
377437function M .get_file_config (file_path )
378438 if not M .config or not M .config .filetypes then return {} end
379439
380- -- Get filetype using Neovim's built-in filetype detection
381440 local filetype = vim .filetype .match ({ filename = file_path }) or ' text'
382-
383- -- Return filetype-specific configuration
384441 return M .config .filetypes [filetype ] or {}
385442end
386443
387- --- Main preview function
388444--- @param file_path string Path to the file or directory
389445--- @param bufnr number Buffer number for preview
390- --- @param file table Optional file information from search results for debug info
391- --- @return boolean Success status
392- function M .preview (file_path , bufnr , file )
446+ --- @return boolean if the preview was successful
447+ function M .preview (file_path , bufnr )
393448 if not file_path or file_path == ' ' then
394- M .clear_buffer_completely (bufnr )
395- safe_set_buffer_lines (bufnr , 0 , - 1 , false , { ' No file selected' })
449+ M .clear_buffer (bufnr )
450+ set_buffer_lines (bufnr , { ' No file selected' })
396451 return false
397452 end
398453
399454 M .state .current_file = file_path
400455 M .state .bufnr = bufnr
401456
402- local stat = vim .uv .fs_stat (file_path )
403- if not stat then
404- M .clear_buffer_completely (bufnr )
405- safe_set_buffer_lines (bufnr , 0 , - 1 , false , {
406- ' File not found or inaccessible:' ,
407- file_path ,
408- })
409- return false
410- end
411-
412- -- Clear buffer completely before switching content types
413- M .clear_buffer_completely (bufnr )
457+ M .clear_buffer (bufnr )
414458
415- -- Handle different file types
416- if stat .type == ' directory' then
417- -- This is a file search tool, directories shouldn't be previewed
418- safe_set_buffer_lines (bufnr , 0 , - 1 , false , {
419- ' Directory Preview Not Available' ,
420- ' ' ,
421- ' This is a file search tool.' ,
422- ' Directories are not meant to be previewed.' ,
423- ' ' ,
424- ' Path: ' .. file_path ,
425- })
426- return false
427- elseif get_image ().is_image (file_path ) then
428- -- Delegate to image preview
459+ if get_image ().is_image (file_path ) then
429460 local win_width = 80
430461 local win_height = 24
431462
432- -- Try to get actual window dimensions if available
433463 if M .state .winid and vim .api .nvim_win_is_valid (M .state .winid ) then
434464 win_width = vim .api .nvim_win_get_width (M .state .winid ) - 2
435465 win_height = vim .api .nvim_win_get_height (M .state .winid ) - 2
436466 end
437467
438- get_image ().display_image (file_path , bufnr , win_width , win_height )
439- return true
468+ return get_image ().display_image (file_path , bufnr , win_width , win_height )
440469 elseif M .is_binary_file (file_path ) then
441- -- Handle binary files before attempting to read as text
442- local info = M .get_file_info (file_path )
443- return M .preview_binary_file (file_path , bufnr , info , file )
470+ return M .preview_binary_file (file_path , bufnr )
444471 else
445472 return M .preview_file (file_path , bufnr )
446473 end
447474end
448475
449- --- Scroll preview content
450- --- @param lines number Number of lines to scroll (positive = down , negative = up )
451476function M .scroll (lines )
452477 if not M .state .bufnr or not vim .api .nvim_buf_is_valid (M .state .bufnr ) then return end
453-
454478 if not M .state .winid or not vim .api .nvim_win_is_valid (M .state .winid ) then return end
455479
456- -- Get current cursor position
457- local cursor = vim .api .nvim_win_get_cursor (M .state .winid )
458- local current_line = cursor [1 ]
459480 local win_height = vim .api .nvim_win_get_height (M .state .winid )
481+ vim .notify (win_height )
482+ local content_height = M .state .content_height or 0
483+
484+ -- allows scrolling for a full content + half window
485+ local half_screen = math.floor (win_height / 2 )
486+ local max_scroll = math.max (0 , content_height + half_screen - win_height )
487+
488+ local current_offset = M .state .scroll_offset or 0
489+ local new_offset = math.max (0 , math.min (max_scroll , current_offset + lines ))
460490
461- -- Calculate new position
462- local new_line = math.max ( 1 , math.min ( M .state .content_height , current_line + lines ))
491+ if new_offset ~= current_offset then
492+ M .state .scroll_offset = new_offset
463493
464- -- Set new cursor position
465- vim .api .nvim_win_set_cursor (M .state .winid , { new_line , 0 })
494+ local target_line = math.min (content_height , math.max (1 , new_offset + 1 ))
466495
467- -- Update scroll offset
468- M .state .scroll_offset = new_line
496+ vim .api .nvim_win_call (M .state .winid , function ()
497+ vim .api .nvim_win_set_cursor (M .state .winid , { target_line , 0 })
498+ vim .cmd (' normal! zt' )
499+ end )
500+ end
469501end
470502
471503--- Set preview window
@@ -498,18 +530,18 @@ end
498530--- @return boolean Success status
499531function M .update_file_info_buffer (file , bufnr , file_index )
500532 if not file then
501- safe_set_buffer_lines (bufnr , 0 , - 1 , false , { ' No file selected' })
533+ set_buffer_lines (bufnr , { ' No file selected' })
502534 return false
503535 end
504536
505537 local info = M .get_file_info (file .path )
506538 if not info then
507- safe_set_buffer_lines (bufnr , 0 , - 1 , false , { ' File info unavailable' })
539+ set_buffer_lines (bufnr , { ' File info unavailable' })
508540 return false
509541 end
510542
511543 local file_info_lines = M .create_file_info_content (file , info , file_index )
512- safe_set_buffer_lines (bufnr , 0 , - 1 , false , file_info_lines )
544+ set_buffer_lines (bufnr , file_info_lines )
513545
514546 vim .api .nvim_buf_set_option (bufnr , ' modifiable' , false )
515547 vim .api .nvim_buf_set_option (bufnr , ' readonly' , true )
@@ -521,24 +553,19 @@ end
521553
522554--- Clear buffer completely including any image attachments
523555--- @param bufnr number Buffer number to clear
524- function M .clear_buffer_completely (bufnr )
556+ function M .clear_buffer (bufnr )
525557 if not bufnr or not vim .api .nvim_buf_is_valid (bufnr ) then return end
526558
527- -- Clear any image attachments first
528559 get_image ().clear_buffer_images (bufnr )
529-
530- -- Clear text content
531- safe_set_buffer_lines (bufnr , 0 , - 1 , false , {})
532-
533- -- Reset filetype to prevent syntax highlighting issues
560+ set_buffer_lines (bufnr , {})
534561 vim .api .nvim_buf_set_option (bufnr , ' filetype' , ' ' )
535562end
536563
537564--- Clear preview
538565function M .clear ()
539566 if M .state .bufnr and vim .api .nvim_buf_is_valid (M .state .bufnr ) then
540- M .clear_buffer_completely (M .state .bufnr )
541- safe_set_buffer_lines (M .state .bufnr , 0 , - 1 , false , { ' No preview available' })
567+ M .clear_buffer (M .state .bufnr )
568+ set_buffer_lines (M .state .bufnr , { ' No preview available' })
542569 end
543570
544571 M .state .current_file = nil
0 commit comments