minimal extui fuzzy finder for neovim

feat: add preview window

+94 -2
+6
lua/artio/builtins.lua
··· 51 51 get_icon = config.get().opts.use_icons and function(item) 52 52 return require("mini.icons").get("file", item.v) 53 53 end or nil, 54 + preview_item = function(item) 55 + return vim.fn.bufadd(item) 56 + end, 54 57 }) 55 58 end 56 59 ··· 100 103 get_icon = config.get().opts.use_icons and function(item) 101 104 return require("mini.icons").get("file", vim.api.nvim_buf_get_name(item.v)) 102 105 end or nil, 106 + preview_item = function(item) 107 + return item 108 + end, 103 109 }) 104 110 end 105 111
+10 -1
lua/artio/config.lua
··· 16 16 ---@class artio.config.win 17 17 ---@field height? integer|number 18 18 ---@field hidestatusline? boolean 19 + ---@field preview_opts? fun(view: artio.View): vim.api.keyset.win_config 19 20 20 21 local M = {} 21 22 ··· 30 31 promptprefix = "", 31 32 prompt_title = true, 32 33 pointer = "", 33 - use_icons = package.loaded['mini.icons'] and true or false, 34 + use_icons = package.loaded["mini.icons"] and true or false, 34 35 }, 35 36 win = { 36 37 height = 0.4, 37 38 hidestatusline = false, -- works best with laststatus=3 39 + preview_opts = function(view) 40 + return { 41 + width = vim.o.columns, 42 + height = view.win.height, 43 + col = 0, 44 + row = vim.o.lines - vim.o.cmdheight * 2 - 1 - (vim.o.winborder == 'none' and 0 or 2), 45 + } 46 + end, 38 47 }, 39 48 } 40 49
+5 -1
lua/artio/picker.lua
··· 3 3 ---@alias artio.Picker.item { id: integer, v: string, text: string, icon?: string, icon_hl?: string } 4 4 ---@alias artio.Picker.match [integer, integer[], integer] [item, pos[], score] 5 5 6 - ---@class artio.Picker.proto 6 + ---@class artio.Picker.proto<T> 7 7 ---@field items? artio.Picker.item[] 8 8 ---@field fn? fun(lst: artio.Picker.item[], input: string): artio.Picker.match[] 9 9 ---@field on_close? fun(text: string, idx: integer) 10 10 ---@field format_item? fun(item: string): string 11 + ---@field preview_item? fun(item: string): integer, fun(win: integer) 11 12 ---@field get_icon? fun(item: artio.Picker.item): string, string 12 13 ---@field opts? artio.config.opts 13 14 ---@field win? artio.config.win ··· 99 100 elseif typed == "<esc>" then 100 101 cancelled = true 101 102 coroutine.resume(co) 103 + return "" 104 + elseif typed == "<c-l>" then 105 + self.view:togglepreview() 102 106 return "" 103 107 end 104 108 end)
+73
lua/artio/view.lua
··· 53 53 ---@field picker artio.Picker 54 54 ---@field closed boolean 55 55 ---@field win artio.View.win 56 + ---@field preview_win integer 56 57 local View = {} 57 58 View.__index = View 58 59 ··· 244 245 end 245 246 self.closed = true 246 247 cmdline.cmdline_show = self.prev_show 248 + self:closepreview() 247 249 vim.schedule(function() 248 250 vim.cmd.stopinsert() 249 251 self:revertopts() ··· 427 429 vim.api.nvim_buf_del_extmark(ext.bufs.cmd, view_ns, self.select_ext) 428 430 end 429 431 432 + self:softupdatepreview() 433 + 430 434 self.picker:fix() 431 435 local idx = self.picker.idx 432 436 if idx == 0 then ··· 451 455 self.select_ext = result 452 456 end 453 457 end 458 + end 459 + 460 + function View:togglepreview() 461 + if self.preview_win then 462 + self:closepreview() 463 + return 464 + end 465 + 466 + self:updatepreview() 467 + end 468 + 469 + ---@return integer 470 + ---@return fun(win: integer)? 471 + function View:openpreview() 472 + if self.picker.idx == 0 then 473 + return -1 474 + end 475 + 476 + local match = self.picker.matches[self.picker.idx] 477 + local item = self.picker.items[match[1]] 478 + 479 + if not item or not (self.picker.preview_item and vim.is_callable(self.picker.preview_item)) then 480 + return -1 481 + end 482 + 483 + return self.picker.preview_item(item.v) 484 + end 485 + 486 + function View:updatepreview() 487 + local buf, on_win = self:openpreview() 488 + if buf < 0 then 489 + return 490 + end 491 + 492 + if not self.preview_win then 493 + self.preview_win = vim.api.nvim_open_win(buf, false, vim.tbl_extend("force", self.picker.win.preview_opts(self), { 494 + relative = "editor", 495 + style = "minimal", 496 + })) 497 + else 498 + vim.api.nvim_win_set_buf(self.preview_win, buf) 499 + end 500 + 501 + vim.api.nvim_set_option_value("previewwindow", true, { scope = "local", win = self.preview_win }) 502 + 503 + if on_win and vim.is_callable(on_win) then 504 + on_win(self.preview_win) 505 + end 506 + end 507 + 508 + function View:softupdatepreview() 509 + if self.picker.idx == 0 then 510 + self:closepreview() 511 + end 512 + 513 + if not self.preview_win then 514 + return 515 + end 516 + 517 + self:updatepreview() 518 + end 519 + 520 + function View:closepreview() 521 + if not self.preview_win then 522 + return 523 + end 524 + 525 + vim.api.nvim_win_close(self.preview_win, true) 526 + self.preview_win = nil 454 527 end 455 528 456 529 return View