/nvim-deck

nvim-deck: A plugin to display, filter, and choose from lists of items.

Primary LanguageLua

Intro

nvim-deck : A plugin for displaying, filtering, and selecting items from customizable lists.

Demo

Open project files
CleanShot.2024-12-08.at.11.24.57.mp4
Grep and Replace
CleanShot.2024-12-08.at.11.45.57.mp4
Git integration
CleanShot.2024-12-08.at.12.01.21.mp4

Concept

nvim-deck revolves around four core concepts:

  • Source:
    • A source provides a list of items to display.
  • Item:
    • An item represents a single entry from the source, containing data and display text.
  • Action:
    • Actions define what happens when the user interacts with an item.
  • Context:
    • Context represents the current state, The user can control deck UI via invoke context methods.

Features

  • Built-in Git integration.
    • :Deck git to open the git launcher.
  • Built-in ripgrep integration.
    • :Deck grep to start a grep search.
  • Built-in file listing.
    • :Deck files to show files under the root directory (ripgrep, PureLua).
  • Highly customizable: sources, actions, previewers, decorators, views, and matchers.

Why nvim-deck?

  • Use normal-window by default
    • IMO, floating-window is fancy but normal window is more handy for edit & preview.
  • UX focused
    • Use vim.wait carefully, it makes a crisp and smooth experience.
  • Configuration over Object control
    • nvim-deck does not require huge setup functions
    • Instead of this, nvim-deck allows you to customize by controlling |deck.Context| object.

Setup

Here’s an example of how to set up nvim-deck:

local deck = require('deck')

-- Apply pre-defined easy settings.
-- For manual configuration, refer to the code in `deck/easy.lua`.
require('deck.easy').setup()

-- Set up buffer-specific key mappings for nvim-deck.
vim.api.nvim_create_autocmd('User', {
  pattern = 'DeckStart',
  callback = function(e)
    local ctx = e.data.ctx --[[@as deck.Context]]

    ctx.keymap('n', '<Tab>', deck.action_mapping('choose_action'))
    ctx.keymap('n', '<C-l>', deck.action_mapping('refresh'))
    ctx.keymap('n', 'i', deck.action_mapping('prompt'))
    ctx.keymap('n', 'a', deck.action_mapping('prompt'))
    ctx.keymap('n', '@', deck.action_mapping('toggle_select'))
    ctx.keymap('n', '*', deck.action_mapping('toggle_select_all'))
    ctx.keymap('n', 'p', deck.action_mapping('toggle_preview_mode'))
    ctx.keymap('n', 'd', deck.action_mapping('delete'))
    ctx.keymap('n', '<CR>', deck.action_mapping('default'))
    ctx.keymap('n', 'o', deck.action_mapping('open'))
    ctx.keymap('n', 'O', deck.action_mapping('open_keep'))
    ctx.keymap('n', 's', deck.action_mapping('open_split'))
    ctx.keymap('n', 'v', deck.action_mapping('open_vsplit'))
    ctx.keymap('n', 'N', deck.action_mapping('create'))
    ctx.keymap('n', 'w', deck.action_mapping('write'))
    ctx.keymap('n', '<C-u>', deck.action_mapping('scroll_preview_up'))
    ctx.keymap('n', '<C-d>', deck.action_mapping('scroll_preview_down'))

    -- If you want to start the filter by default, call ctx.prompt() here
    ctx.prompt()
  end
})

--key-mapping for explorer source (requires `require('deck.easy').setup()`).
vim.api.nvim_create_autocmd('User', {
  pattern = 'DeckStart:explorer',
  callback = function(e)
    local ctx = e.data.ctx --[[@as deck.Context]]
    ctx.keymap('n', 'h', deck.action_mapping('explorer.collapse'))
    ctx.keymap('n', 'l', deck.action_mapping('explorer.expand'))
    ctx.keymap('n', '.', deck.action_mapping('explorer.toggle_dotfiles'))
    ctx.keymap('n', 'c', deck.action_mapping('explorer.clipboard.save_copy'))
    ctx.keymap('n', 'm', deck.action_mapping('explorer.clipboard.save_move'))
    ctx.keymap('n', 'p', deck.action_mapping('explorer.clipboard.paste'))
    ctx.keymap('n', 'x', deck.action_mapping('explorer.clipboard.paste'))
    ctx.keymap('n', '<Leader>ff', deck.action_mapping('explorer.dirs'))
    ctx.keymap('n', 'P', deck.action_mapping('toggle_preview_mode'))
    ctx.keymap('n', '~', function()
      ctx.do_action('explorer.get_api').set_cwd(vim.fs.normalize('~'))
    end)
    ctx.keymap('n', '\\', function()
      ctx.do_action('explorer.get_api').set_cwd(vim.fs.normalize('/'))
    end)
  end
})

-- Example key bindings for launching nvim-deck sources. (These mapping required `deck.easy` calls.)
vim.keymap.set('n', '<Leader>ff', '<Cmd>Deck files<CR>', { desc = 'Show recent files, buffers, and more' })
vim.keymap.set('n', '<Leader>gr', '<Cmd>Deck grep<CR>', { desc = 'Start grep search' })
vim.keymap.set('n', '<Leader>gi', '<Cmd>Deck git<CR>', { desc = 'Open git launcher' })
vim.keymap.set('n', '<Leader>he', '<Cmd>Deck helpgrep<CR>', { desc = 'Live grep all help tags' })

-- Show the latest deck context.
vim.keymap.set('n', '<Leader>;', function()
  local context = deck.get_history()[vim.v.count == 0 and 1 or vim.v.count]
  if context then
    context.show()
  end
end)

-- Do default action on next item.
vim.keymap.set('n', '<Leader>n', function()
  local ctx = require('deck').get_history()[1]
  if ctx then
    ctx.set_cursor(ctx.get_cursor() + 1)
    ctx.do_action('default')
  end
end)

Development Guideline

If you create nvim-deck source or action, we recommend to follow guidelines.

Register alias action for common action names.

nvim-deck supports the concept of duck-typing.

So if your custom source has source-specific actions, you should register alias actions for them.

  • default
  • create
  • delete
  • rename
  • write
  • refresh
  • open
  • open_split
  • open_vsplit

The source definition looks like this:

source = {
  ...
  actions = {
    deck.alias_action('default', 'source.default'),
    {
      name = 'source.default',
      execute = ...
    }
  }
  ...
}

The source.default action will be shown in action picker. The user can have a unified experience across many different sources just by writing ctx.keymap('n', '<CR>', deck.action_mapping('default')).

Customization

!!! We strongly recommend using lua-language-server !!!

Appearance

You can customize your nvim-deck appearance.

  1. guicursor
require('deck').setup({
  guicursor = 'a:ver25',
})
  1. winhighlight
vim.api.nvim_create_autocmd('User', {
  group = misc.group,
  pattern = 'DeckShow',
  callback = function()
    vim.wo.winhighlight = 'CursorLine:Visual'
  end
})

Source

The source is typed as |deck.Source|. The source can be executed by deck.start().

The source can specify source-level actions, decorators, and previewers.

require('deck').start({
  name = 'my_source',
  execute = function(ctx)
    ctx.item({
      display_text = 'Hello, World!',
    })
    ctx.done()
  end,
  actions = {
    ...
  },
  decorators = {
    ...
  },
  previewers = {
    ...
  },
})

Action

The action is typed as |deck.Action| and can be registered below three different levels.

  1. Config Action : Provided by a start configuration.

This level can be registered by start_config.actions field.

require('deck').start(some_of_the_source, {
  actions = {
    {
      name = 'my_open',
      execute = function(ctx)
        -- some of the process.
      end
    }
  }
})
  1. Source Action : Provided by a source.

This level of actions can be registered by source.actions field.

require('deck').start({
  name = 'my_source',
  ...
  actions = {
    {
      name = 'my_open',
      execute = function(ctx)
        -- some of the process.
      end
    }
  }
})
  1. Global Action : Registered globally.

This level of actions can be registered by deck.register_action().

require('deck').register_action({
  name = 'my_open',
  resolve = function(ctx)
    -- Action is available only if there is exactly one action item with a filename.
    return #ctx.get_action_items() == 1 and ctx.get_action_items()[1].data.filename
  end,
  execute = function(ctx)
    -- Open the file.
    vim.cmd.edit(ctx.get_action_items()[1].data.filename)
  end
})

Note: The same name actions are choosen in the order of 1 -> 2 -> 3.

StartPreset

The start-preset is typed as |deck.StartPreset| and can be registered globally.

A start-preset in nvim-deck allows you to define shortcut command. In the below example, you can use :Deck recent command.

-- After registration, you can start the preset using the `:Deck recent` command.
require('deck').register_start_preset('recent', {
  require('deck').start({
    require('deck.builtin.source.recent_files')(),
    require('deck.builtin.source.buffers')(),
  })
})

Decorator

nvim-deck has decorator concept. It's designed to decorate the deck-buffer via nvim_buf_set_extmark. The below example shows how to create your own decorator.

--- This is example decorator.
--- To display the basename of the file and dirname as a comment.
--- This decorator highlight basename and make dirname less noticeable.
require('deck').register_decorator({
  name = 'basename_dirname',
  resolve = function(_, item)
    -- This decorator is available only if the item has a filename.
    return item.data.filename
  end,
  decorate = function(ctx, item, row)
    local dirname = vim.fn.fnamemodify(item.data.filename, ':~:h')
    local display_text = item.display_text
    local s, e = display_text:find(dirname, 1, true)
    if s then
      return {
        -- Hide the directory part (using conceal)
        {
          row = row,
          col = s - 1,
          end_row = row,
          end_col = e + 1,
          conceal = '',
          ephemeral = true,
        },
        -- Display the directory name as a comment at the end of the line
        {
          row = row,
          col = 0,
          virt_text = { { dirname, 'Comment' } },
          virt_text_pos = 'eol'
        }
      }
    end
    return {}
  end
})

Previewer

nvim-deck has previewer concept. It's designed to show the item preview.

require('deck').register_previewer({
  name = 'bat',
  resolve = function(_, item)
    return item.data.filename and vim.fn.filereadable(item.data.filename) == 1
  end,
  preview = function(_, item, env)
    vim.api.nvim_win_call(env.win, function()
      vim.fn.termopen(('bat --color=always %s'):format(item.data.filename))
    end)
  end
})

Built-in

Sources

buffers

Show buffers.

Name Type Default Description
ignore_paths string[]? [vim.fn.expand('%:p')] Ignore paths. The default value is intented to hide current buffer.
nofile boolean? false Ignore nofile buffers.
deck.start(require('deck.builtin.source.buffers')({
  ignore_paths = { vim.fn.expand('%:p'):gsub('/$', '') },
  nofile = false,
}))

deck.actions

Show available actions from |deck.Context|

Name Type Default Description
context |deck.Context|
deck.start(require('deck.builtin.source.deck.actions')({
  context = context
}))

deck.history

Show deck.start history.

No options

deck.start(require('deck.builtin.source.deck.history')())

dirs

Show dirs under specified root directory.

Name Type Default Description
ignore_globs string[]? [] Ignore glob patterns.
root_dir string Target root directory.
deck.start(require('deck.builtin.source.dirs')({
  root_dir = vim.fn.getcwd(),
  ignore_globs = { '**/node_modules/', '**/.git/' },
}))

explorer

Explorer source.

Name Type Default Description
cwd string Target directory.
mode 'drawer' | 'filer' Mode of explorer.
narrow { enabled?: boolean, ignore_globs?: string[] } Narrow finder options.
reveal string Reveal target path.
To use explorer, you must set `start_preset` or use `require('deck.easy').setup()`.
If you call `require('deck.easy').setup()`, then you can use explorer by `:Deck explorer` command.

files

Show files under specified root directory.

Name Type Default Description
ignore_globs string[]? [] Ignore glob patterns.
root_dir string Target root directory.
deck.start(require('deck.builtin.source.files')({
  root_dir = vim.fn.getcwd(),
  ignore_globs = { '**/node_modules/', '**/.git/' },
}))

git

Show git launcher.

Name Type Default Description
cwd string Target git root.
deck.start(require('deck.builtin.source.git.changeset')({
  cwd = vim.fn.getcwd(),
}))

git.branch

Show git branches

Name Type Default Description
cwd string Target git root.
deck.start(require('deck.builtin.source.git.branch')({
  cwd = vim.fn.getcwd()
}))

git.changeset

Show git changeset for specified revision.

Name Type Default Description
cwd string Target git root.
from_rev string From revision.
to_rev string? To revision. If you omit this option, it will be HEAD.
deck.start(require('deck.builtin.source.git.changeset')({
  cwd = vim.fn.getcwd(),
  from_rev = 'HEAD~3',
  to_rev = 'HEAD'
}))

git.log

Show git log.

Name Type Default Description
cwd string Target git root.
max_count integer? Max count for log
deck.start(require('deck.builtin.source.git.log')({
  cwd = vim.fn.getcwd(),
}))

git.reflog

Show git reflog.

Name Type Default Description
cwd string Target git root.
max_count integer? Max count for reflog
deck.start(require('deck.builtin.source.git.reflog')({
  cwd = vim.fn.getcwd(),
}))

git.remote

Show git remotes.

Name Type Default Description
cwd string Target git root.
deck.start(require('deck.builtin.source.git.remote')({
  cwd = vim.fn.getcwd(),
}))

git.status

Show git status.

Name Type Default Description
cwd string Target git root.
deck.start(require('deck.builtin.source.git.status')({
  cwd = vim.fn.getcwd(),
}))

grep

Grep files under specified root directory. (required ripgrep)

Name Type Default Description
root_dir string Target root directory.
ignore_globs string[]? [] Ignore glob patterns.
deck.start(require('deck.builtin.source.grep')({
  root_dir = vim.fn.getcwd(),
  pattern = vim.fn.input('grep: '),
  ignore_globs = { '**/node_modules/', '**/.git/' },
}))

helpgrep

Live grep all helptags. (required ripgrep)

No options

deck.start(require('deck.builtin.source.helpgrep')())

items

Listing any provided items.

Name Type Default Description
items string[]|deck.ItemSpecifier[] Items to list.
deck.start(require('deck.builtin.source.items')({
  items = vim.iter(vim.api.nvim_list_bufs()):map(function(buf)
    return ('#%s'):format(buf)
  end):totable()
}))

lines

Show buffer lines.

No options

deck.start(require('deck.builtin.source.lines')({
  bufnrs = { vim.api.nvim_get_current_buf() },
}))

recent_dirs

List recent directories.

Name Type Default Description
ignore_paths string[]? [] Ignore paths.
require('deck.builtin.source.recent_dirs'):setup({
  path = '~/.deck.recent_dirs'
})
vim.api.nvim_create_autocmd('DirChanged', {
  callback = function(e)
    require('deck.builtin.source.recent_dirs'):add(e.cwd)
  end,
})
deck.start(require('deck.builtin.source.recent_dirs')({
  ignore_paths = { '**/node_modules/', '**/.git/' },
}))

recent_files

List recent files.

Name Type Default Description
ignore_paths string[]? [] Ignore paths.
require('deck.builtin.source.recent_files'):setup({
  path = '~/.deck.recent_files'
})
vim.api.nvim_create_autocmd('BufEnter', {
  callback = function()
    local bufname = vim.api.nvim_buf_get_name(0)
    if vim.fn.filereadable(bufname) == 1 then
      require('deck.builtin.source.recent_files'):add(vim.fs.normalize(bufname))
    end
  end,
})
deck.start(require('deck.builtin.source.recent_files')({
  ignore_paths = { '**/node_modules/', '**/.git/' },
}))

Actions

  • choose_action

    • Open action source.

      The actions listed are filtered by whether they are valid in the current context.

  • delete_buffer

    • Delete item.data.bufnr from buffers list.

      If multiple items are selected, they will be deleted in order.

  • delete_file

    • Delete item.data.filename from filesystem.

      If multiple items are selected, they will be deleted in order.

  • open

    • Open item.data.filename or item.data.bufnr.

      Open at the recently normal window.

  • open_keep

    • Open item.data.filename or item.data.bufnr.

      But keep the deck window and cursor.

  • open_split

    • Open item.data.filename or item.data.bufnr.

      Open at the recently normal window with split.

  • open_split_keep

    • Open item.data.filename or item.data.bufnr.

      Open at the recently normal window with split. But keep the deck window and cursor.

  • open_tabnew

    • Open item.data.filename or item.data.bufnr.

      Open at the new tabpage.

  • open_vsplit

    • Open item.data.filename or item.data.bufnr.

      Open at the recently normal window with vsplit.

  • open_vsplit_keep

    • Open item.data.filename or item.data.bufnr.

      Open at the recently normal window with vsplit. But keep the deck window and cursor.

  • print

    • Print selected items.
  • prompt

    • Open filtering prompt
  • refresh

    • Re-execute source. (it can be used to refresh the items)
  • scroll_preview_down

    • Scroll preview window down.
  • scroll_preview_up

    • Scroll preview window up.
  • substitute

    • Open substitute buffer with selected items (item.data.filename and item.data.lnum are required).

      You can modify and save the buffer to reflect the changes to the original files.

  • toggle_preview_mode

    • Toggle preview mode
  • toggle_select

    • Toggle selected state of the cursor item.
  • toggle_select_all

    • Toggle selected state of all items.
  • write_buffer

    • Write modified item.data.bufnr or item.data.filename that has buffer.
  • yank

    • Yank item.display_text field to default register.

Autocmd

  • DeckHide

    • Triggered after deck window hidden.
  • DeckShow

    • Triggered after deck window shown.
  • DeckStart

    • Triggered when deck starts.
  • DeckStart:{source.name}

    • Triggered when deck starts for source.

API

deck.action_mapping(mapping): fun(ctx: |deck.Context|)

Create action mapping function for ctx.keymap.

Name Type Description
action_names string|string[] action name or action names to use for mappings.

 

deck.alias_action(alias_name, alias_action_name): |deck.Action|

Create alias action.

Name Type Description
alias_name string new action name.
alias_action_name string existing action name.

 

deck.get_actions(): |deck.Action|[]

Get all registered actions.

No arguments  

deck.get_decorators(): |deck.Decorator|[]

Get all registered decorators.

No arguments  

deck.get_history(): |deck.Context|[]

Get all history (first history is latest).

No arguments  

deck.get_previewers(): |deck.Previewer|[]

Get all registered previewers.

No arguments  

deck.get_start_presets(): |deck.StartPreset|[]

Get all registered start presets.

No arguments  

deck.register_action(action)

Register action.

Name Type Description
action |deck.Action| action to register.

 

deck.register_decorator(decorator)

Register decorator.

Name Type Description
decorator |deck.Decorator| decorator to register.

 

deck.register_previewer(previewer)

Register previewer.

Name Type Description
previewer |deck.Previewer| previewer to register.

 

deck.register_start_preset(name, start_fn)

Register start_preset.

Name Type Description
name string preset name.
start_fn fun() Start function.

 

deck.register_start_preset(start_preset)

Register start_preset.

Name Type Description
start_preset deck.StartPreset |deck.StartPreset|

 

deck.remove_actions(predicate)

Remove specific action.

Name Type Description
predicate fun(action: |deck.Action|): boolean Predicate function. If return true, remove action.

 

deck.remove_decorators(predicate)

Remove specific decorator.

Name Type Description
predicate fun(decorator: |deck.Decorator|): boolean Predicate function. If return true, remove decorator.

 

deck.remove_previewers(predicate)

Remove previewer.

Name Type Description
predicate fun(previewer: |deck.Previewer|): boolean Predicate function. If return true, remove previewer.

 

deck.remove_start_presets(predicate)

Remove specific start_preset.

Name Type Description
predicate fun(start_preset: |deck.StartPreset|): boolean Predicate function. If return true, remove start_preset.

 

deck.setup(config)

Setup deck globally.

Name Type Description
config deck.ConfigSpecifier Setup deck configuration.

 

deck.start(sources, start_config): |deck.Context|

Start deck with given sources.

Name Type Description
source deck.Source|deck.Source[] source or sources to start.
start_config deck.StartConfigSpecifier start configuration.

 

Type

*deck.Action*
---@class deck.Action
---@field public name string
---@field public desc? string
---@field public hidden? boolean
---@field public resolve? deck.ActionResolveFunction
---@field public execute deck.ActionExecuteFunction
*deck.Config*
---@class deck.Config: deck.ConfigSpecifier
---@field public guicursor? string
---@field public max_history_size integer
---@field public default_start_config? deck.StartConfigSpecifier
*deck.ConfigSpecifier*
---@class deck.ConfigSpecifier
---@field public guicursor? string
---@field public max_history_size? integer
---@field public default_start_config? deck.StartConfigSpecifier
*deck.Context*
---@class deck.Context
---@field id integer
---@field ns integer
---@field buf integer
---@field name string
---@field get_config fun(): deck.StartConfig
---@field execute fun()
---@field is_visible fun(): boolean
---@field show fun()
---@field hide fun()
---@field focus fun()
---@field prompt fun()
---@field scroll_preview fun(delta: integer)
---@field get_status fun(): deck.Context.Status
---@field is_filtering fun(): boolean
---@field is_syncing fun(): boolean
---@field get_cursor fun(): integer
---@field set_cursor fun(cursor: integer)
---@field get_query fun(): string
---@field set_query fun(query: string)
---@field get_matcher_query fun(): string
---@field get_dynamic_query fun(): string
---@field set_selected fun(item: deck.Item, selected: boolean)
---@field get_selected fun(item: deck.Item): boolean
---@field set_select_all fun(select_all: boolean)
---@field get_select_all fun(): boolean
---@field set_preview_mode fun(preview_mode: boolean)
---@field get_preview_mode fun(): boolean
---@field get_items fun(): deck.Item[]
---@field get_cursor_item fun(): deck.Item?
---@field get_action_items fun(): deck.Item[]
---@field get_filtered_items fun(): deck.Item[]
---@field get_rendered_items fun(): deck.Item[]
---@field get_selected_items fun(): deck.Item[]
---@field get_actions fun(): deck.Action[]
---@field get_decorators fun(): deck.Decorator[]
---@field get_previewer fun(): deck.Previewer?
---@field sync fun()
---@field keymap fun(mode: string|string[], lhs: string, rhs: fun(ctx: deck.Context))
---@field do_action fun(name: string): any
---@field dispose fun()
---@field disposed fun(): boolean
---@field on_show fun(callback: fun())
---@field on_hide fun(callback: fun())
---@field on_dispose fun(callback: fun()): fun()
*deck.Decoration*
---@class deck.Decoration
---@field public col? integer
---@field public end_col? integer
---@field public hl_group? string
---@field public hl_eol? boolean
---@field public virt_text? deck.VirtualText[]
---@field public virt_text_pos? 'eol' | 'overlay' | 'right_align' | 'inline'
---@field public virt_text_win_col? integer
---@field public virt_text_hide? boolean
---@field public virt_text_repeat_linebreak? boolean
---@field public virt_lines? deck.VirtualText[][]
---@field public virt_lines_above? boolean
---@field public ephemeral? boolean
---@field public priority? integer
---@field public sign_text? string
---@field public sign_hl_group? string
---@field public number_hl_group? string
---@field public line_hl_group? string
---@field public conceal? string
*deck.Decorator*
---@class deck.Decorator
---@field public name string
---@field public dynamic? boolean
---@field public resolve? deck.DecoratorResolveFunction
---@field public decorate deck.DecoratorDecorateFunction
*deck.ExecuteContext*
---@class deck.ExecuteContext
---@field public item fun(item: deck.ItemSpecifier)
---@field public done fun( )
---@field public queue fun(task: fun())
---@field public get_query fun(): string
---@field public get_config fun(): deck.StartConfig
---@field public aborted fun(): boolean
---@field public on_abort fun(callback: fun())
*deck.Item*
---@class deck.Item: deck.ItemSpecifier
---@field public display_text string
---@field public data table
*deck.ItemSpecifier*
---@class deck.ItemSpecifier
---@field public display_text string|(deck.VirtualText[])
---@field public highlights? deck.Highlight[]
---@field public filter_text? string
---@field public dedup_id? string
---@field public data? table
*deck.PerformanceConfig*
---@class deck.PerformanceConfig
---@field public sync_timeout_ms integer
---@field public interrupt_ms integer
---@field public gather_budget_ms integer
---@field public gather_batch_size integer
---@field public filter_bugdet_ms integer
---@field public filter_batch_size integer
---@field public render_bugdet_ms integer
---@field public render_batch_size integer
---@field public render_delay_ms integer
*deck.Previewer*
---@class deck.Previewer
---@field public name string
---@field public priority? integer
---@field public resolve? deck.PreviewerResolveFunction
---@field public preview deck.PreviewerPreviewFunction
*deck.Source*
---@class deck.Source
---@field public name string
---@field public dynamic? boolean
---@field public events? { Start?: fun(ctx: deck.Context), BufWinEnter?: fun(ctx: deck.Context, env: { first: boolean }) }
---@field public execute deck.SourceExecuteFunction
---@field public actions? deck.Action[]
---@field public decorators? deck.Decorator[]
---@field public previewers? deck.Previewer[]
---@field public parse_query? deck.ParseQuery
*deck.StartConfig*
---@class deck.StartConfig: deck.StartConfigSpecifier
---@field public name string
---@field public view fun(): deck.View
---@field public matcher deck.Matcher
---@field public history boolean
---@field public performance deck.PerformanceConfig
---@field public disable_actions? string[]
---@field public disable_decorators? string[]
---@field public disable_previewers? string[]
---@field public dedup boolean
---@field public query string
*deck.StartConfigSpecifier*
---@class deck.StartConfigSpecifier
---@field public name? string
---@field public view? fun(): deck.View
---@field public matcher? deck.Matcher
---@field public history? boolean
---@field public actions? deck.Action[]
---@field public decorators? deck.Decorator[]
---@field public previewers? deck.Previewer[]
---@field public performance? deck.PerformanceConfig|{}
---@field public disable_actions? string[]
---@field public disable_decorators? string[]
---@field public disable_previewers? string[]
---@field public dedup? boolean
---@field public query? string
---@field public auto_abort? boolean
*deck.StartPreset*
---@class deck.StartPreset
---@field public name string
---@field public args? table<string|integer, { complete?: (fun(prefix: string):string[]), required?: boolean }>
---@field public start fun(args: table<string|integer, string>)
*deck.View*
---@class deck.View
---@field public get_win fun(): integer?
---@field public is_visible fun(ctx: deck.Context): boolean
---@field public show fun(ctx: deck.Context)
---@field public hide fun(ctx: deck.Context)
---@field public redraw fun(ctx: deck.Context)
---@field public prompt fun(ctx: deck.Context)
---@field public scroll_preview fun(ctx: deck.Context, delta: integer)