* feat: emit \`CanolaFileCreated\` autocmd on file creation Problem: no way to hook into individual file creation to populate initial contents, without a plugin-specific config callback. Solution: fire \`User CanolaFileCreated\` with \`data.path\` after each successful \`fs.touch\` in the files adapter. Users listen with \`nvim_create_autocmd\` and write to the path however they like. * build: gitignore `doc/upstream.html` * docs(upstream): mark #721 fixed, triage #735 * docs(upstream): simplify #735 note
1182 lines
48 KiB
Text
1182 lines
48 KiB
Text
*canola.txt*
|
|
*Canola* *canola* *canola.nvim*
|
|
--------------------------------------------------------------------------------
|
|
CONTENTS *canola-contents*
|
|
|
|
1. Introduction |canola-introduction|
|
|
2. Requirements |canola-requirements|
|
|
3. Config |canola-config|
|
|
4. Options |canola-options|
|
|
5. Api |canola-api|
|
|
6. Columns |canola-columns|
|
|
7. Actions |canola-actions|
|
|
8. Highlights |canola-highlights|
|
|
9. Adapters |canola-adapters|
|
|
10. Recipes |canola-recipes|
|
|
11. Events |canola-events|
|
|
12. Trash |canola-trash|
|
|
|
|
--------------------------------------------------------------------------------
|
|
INTRODUCTION *canola-introduction*
|
|
|
|
canola.nvim is a file explorer for Neovim in the style of vim-vinegar. It lets
|
|
you edit your filesystem like a normal buffer: create files and directories by
|
|
typing new lines, delete them by removing lines, rename or move them by
|
|
changing the text. When you save the buffer, canola diffs it against the original
|
|
listing and performs the corresponding filesystem operations.
|
|
|
|
Open a directory with `nvim .`, `:edit <path>`, or `:Canola <path>`. Use `<CR>`
|
|
to open a file or descend into a directory, and `-` to go up. Treat the
|
|
listing like any other buffer — edit freely, then `:w` to apply changes.
|
|
|
|
To open canola in a floating window, use `:Canola --float <path>`.
|
|
|
|
To mimic vim-vinegar's parent-directory keymap: >lua
|
|
vim.keymap.set("n", "-", "<CMD>Canola<CR>", { desc = "Open parent directory" })
|
|
<
|
|
|
|
File operations work across adapters. You can copy files between your local
|
|
machine and a remote server over SSH, or between local directories and S3
|
|
buckets, using the same buffer-editing workflow.
|
|
|
|
--------------------------------------------------------------------------------
|
|
REQUIREMENTS *canola-requirements*
|
|
|
|
- Neovim 0.8+
|
|
- (optional) mini.icons or nvim-web-devicons for file icons
|
|
|
|
--------------------------------------------------------------------------------
|
|
CONFIG *canola-config*
|
|
|
|
Canola can be configured in two ways:
|
|
|
|
1. The traditional `setup()` call: >lua
|
|
require("canola").setup({
|
|
-- your opts here
|
|
})
|
|
<
|
|
|
|
2. Declarative configuration via `vim.g.canola`, which does not require calling
|
|
`setup()`: >lua
|
|
vim.g.canola = {
|
|
-- your opts here
|
|
}
|
|
<
|
|
|
|
When `vim.g.canola` is set, canola initializes automatically during plugin
|
|
loading. If `setup()` is called with explicit opts, those take precedence
|
|
over `vim.g.canola`.
|
|
|
|
The full list of options with their defaults:
|
|
|
|
>lua
|
|
require("canola").setup({
|
|
-- Canola will take over directory buffers (e.g. `vim .` or `:e src/`)
|
|
-- Set to false if you want some other plugin (e.g. netrw) to open when you edit directories.
|
|
default_file_explorer = true,
|
|
-- Id is automatically added at the beginning, and name at the end
|
|
-- See :help canola-columns
|
|
columns = {
|
|
"icon",
|
|
-- "permissions",
|
|
-- "size",
|
|
-- "mtime",
|
|
},
|
|
-- Buffer-local options to use for canola buffers
|
|
buf_options = {
|
|
buflisted = false,
|
|
bufhidden = "hide",
|
|
},
|
|
-- Window-local options to use for canola buffers
|
|
win_options = {
|
|
wrap = false,
|
|
signcolumn = "no",
|
|
cursorcolumn = false,
|
|
foldcolumn = "0",
|
|
spell = false,
|
|
list = false,
|
|
conceallevel = 3,
|
|
concealcursor = "nvic",
|
|
},
|
|
-- Send deleted files to the trash instead of permanently deleting them (:help canola-trash)
|
|
delete_to_trash = false,
|
|
-- Skip the confirmation popup for simple operations (:help canola.skip_confirm_for_simple_edits)
|
|
skip_confirm_for_simple_edits = false,
|
|
-- Selecting a new/moved/renamed file or directory will prompt you to save changes first
|
|
-- (:help prompt_save_on_select_new_entry)
|
|
prompt_save_on_select_new_entry = true,
|
|
-- Canola will automatically delete hidden buffers after this delay
|
|
-- You can set the delay to false to disable cleanup entirely
|
|
-- Note that the cleanup process only starts when none of the canola buffers are currently displayed
|
|
cleanup_delay_ms = 2000,
|
|
lsp_file_methods = {
|
|
-- Enable or disable LSP file operations
|
|
enabled = true,
|
|
-- Time to wait for LSP file operations to complete before skipping
|
|
timeout_ms = 1000,
|
|
-- Set to true to autosave buffers that are updated with LSP willRenameFiles
|
|
-- Set to "unmodified" to only save unmodified buffers
|
|
autosave_changes = false,
|
|
},
|
|
-- Constrain the cursor to the editable parts of the canola buffer
|
|
-- Set to `false` to disable, or "name" to keep it on the file names
|
|
constrain_cursor = "editable",
|
|
-- Set to true to watch the filesystem for changes and reload canola
|
|
watch_for_changes = false,
|
|
-- Keymaps in canola buffer. Can be any value that `vim.keymap.set` accepts OR a table of keymap
|
|
-- options with a `callback` (e.g. { callback = function() ... end, desc = "", mode = "n" })
|
|
-- Additionally, if it is a string that matches "actions.<name>",
|
|
-- it will use the mapping at require("canola.actions").<name>
|
|
-- Set to `false` to remove a keymap
|
|
-- See :help canola-actions for a list of all available actions
|
|
keymaps = {
|
|
["g?"] = { "actions.show_help", mode = "n" },
|
|
["<CR>"] = "actions.select",
|
|
["<C-s>"] = { "actions.select", opts = { vertical = true } },
|
|
["<C-h>"] = { "actions.select", opts = { horizontal = true } },
|
|
["<C-t>"] = { "actions.select", opts = { tab = true } },
|
|
["<C-p>"] = "actions.preview",
|
|
["<C-c>"] = { "actions.close", mode = "n" },
|
|
["<C-l>"] = "actions.refresh",
|
|
["-"] = { "actions.parent", mode = "n" },
|
|
["_"] = { "actions.open_cwd", mode = "n" },
|
|
["`"] = { "actions.cd", mode = "n" },
|
|
["g~"] = { "actions.cd", opts = { scope = "tab" }, mode = "n" },
|
|
["gs"] = { "actions.change_sort", mode = "n" },
|
|
["gx"] = "actions.open_external",
|
|
["g."] = { "actions.toggle_hidden", mode = "n" },
|
|
["g\\"] = { "actions.toggle_trash", mode = "n" },
|
|
},
|
|
-- Set to false to disable all of the above keymaps
|
|
use_default_keymaps = true,
|
|
view_options = {
|
|
-- Show files and directories that start with "."
|
|
show_hidden = false,
|
|
-- This function defines what is considered a "hidden" file
|
|
is_hidden_file = function(name, bufnr)
|
|
local m = name:match("^%.")
|
|
return m ~= nil
|
|
end,
|
|
-- This function defines what will never be shown, even when `show_hidden` is set
|
|
is_always_hidden = function(name, bufnr)
|
|
return false
|
|
end,
|
|
-- Sort file names with numbers in a more intuitive order for humans.
|
|
-- Can be "fast", true, or false. "fast" will turn it off for large directories.
|
|
natural_order = "fast",
|
|
-- Sort file and directory names case insensitive
|
|
case_insensitive = false,
|
|
sort = {
|
|
-- sort order can be "asc" or "desc"
|
|
-- see :help canola-columns to see which columns are sortable
|
|
{ "type", "asc" },
|
|
{ "name", "asc" },
|
|
},
|
|
-- Customize the highlight group for the file name
|
|
highlight_filename = function(entry, is_hidden, is_link_target, is_link_orphan)
|
|
return nil
|
|
end,
|
|
},
|
|
new_file_mode = 420,
|
|
new_dir_mode = 493,
|
|
-- Extra arguments to pass to SCP when moving/copying files over SSH
|
|
extra_scp_args = {},
|
|
-- Extra arguments to pass to aws s3 when creating/deleting/moving/copying files using aws s3
|
|
extra_s3_args = {},
|
|
-- EXPERIMENTAL support for performing file operations with git
|
|
git = {
|
|
-- Return true to automatically git add/mv/rm files
|
|
add = function(path)
|
|
return false
|
|
end,
|
|
mv = function(src_path, dest_path)
|
|
return false
|
|
end,
|
|
rm = function(path)
|
|
return false
|
|
end,
|
|
},
|
|
-- Configuration for the floating window in canola.open_float
|
|
float = {
|
|
-- Padding around the floating window
|
|
padding = 2,
|
|
-- max_width and max_height can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
|
max_width = 0,
|
|
max_height = 0,
|
|
border = nil,
|
|
win_options = {
|
|
winblend = 0,
|
|
},
|
|
-- optionally override the canola buffers window title with custom function: fun(winid: integer): string
|
|
get_win_title = nil,
|
|
-- preview_split: Split direction: "auto", "left", "right", "above", "below".
|
|
preview_split = "auto",
|
|
-- This is the config that will be passed to nvim_open_win.
|
|
-- Change values here to customize the layout
|
|
override = function(conf)
|
|
return conf
|
|
end,
|
|
},
|
|
-- Configuration for the file preview window
|
|
preview_win = {
|
|
-- Whether the preview window is automatically updated when the cursor is moved
|
|
update_on_cursor_moved = true,
|
|
-- How to open the preview window "load"|"scratch"|"fast_scratch"
|
|
preview_method = "fast_scratch",
|
|
-- A function that returns true to disable preview on a file e.g. to avoid lag
|
|
disable_preview = function(filename)
|
|
return false
|
|
end,
|
|
-- Window-local options to use for preview window buffers
|
|
win_options = {},
|
|
},
|
|
-- Configuration for the floating action confirmation window
|
|
confirmation = {
|
|
-- Width dimensions can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
|
-- min_width and max_width can be a single value or a list of mixed integer/float types.
|
|
-- max_width = {100, 0.8} means "the lesser of 100 columns or 80% of total"
|
|
max_width = 0.9,
|
|
-- min_width = {40, 0.4} means "the greater of 40 columns or 40% of total"
|
|
min_width = { 40, 0.4 },
|
|
-- optionally define an integer/float for the exact width of the preview window
|
|
width = nil,
|
|
-- Height dimensions can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
|
-- min_height and max_height can be a single value or a list of mixed integer/float types.
|
|
-- max_height = {80, 0.9} means "the lesser of 80 columns or 90% of total"
|
|
max_height = 0.9,
|
|
-- min_height = {5, 0.1} means "the greater of 5 columns or 10% of total"
|
|
min_height = { 5, 0.1 },
|
|
-- optionally define an integer/float for the exact height of the preview window
|
|
height = nil,
|
|
border = nil,
|
|
win_options = {
|
|
winblend = 0,
|
|
},
|
|
},
|
|
-- Configuration for the floating progress window
|
|
progress = {
|
|
max_width = 0.9,
|
|
min_width = { 40, 0.4 },
|
|
width = nil,
|
|
max_height = { 10, 0.9 },
|
|
min_height = { 5, 0.1 },
|
|
height = nil,
|
|
border = nil,
|
|
minimized_border = "none",
|
|
win_options = {
|
|
winblend = 0,
|
|
},
|
|
},
|
|
-- Configuration for the floating SSH window
|
|
ssh = {
|
|
border = nil,
|
|
},
|
|
-- Configuration for the floating keymaps help window
|
|
keymaps_help = {
|
|
border = nil,
|
|
},
|
|
})
|
|
<
|
|
|
|
--------------------------------------------------------------------------------
|
|
OPTIONS *canola-options*
|
|
|
|
|
|
skip_confirm_for_simple_edits *canola.skip_confirm_for_simple_edits*
|
|
type: `boolean` default: `false`
|
|
Before performing filesystem operations, Canola displays a confirmation popup to ensure
|
|
that all operations are intentional. When this option is `true`, the popup will be
|
|
skipped if the operations:
|
|
* contain no deletes
|
|
* contain no cross-adapter moves or copies (e.g. from local to ssh)
|
|
* contain at most one copy or move
|
|
* contain at most five creates
|
|
|
|
prompt_save_on_select_new_entry *canola.prompt_save_on_select_new_entry*
|
|
type: `boolean` default: `true`
|
|
There are two cases where this option is relevant:
|
|
1. You copy a file to a new location, then you select it and make edits before
|
|
saving.
|
|
2. You copy a directory to a new location, then you enter the directory and make
|
|
changes before saving.
|
|
|
|
In case 1, when you edit the file you are actually editing the original file because
|
|
canola has not yet moved/copied it to its new location. This means that the original
|
|
file will, perhaps unexpectedly, also be changed by any edits you make.
|
|
|
|
Case 2 is similar; when you edit the directory you are again actually editing the
|
|
original location of the directory. If you add new files, those files will be
|
|
created in both the original location and the copied directory.
|
|
|
|
When this option is `true`, Canola will prompt you to save before entering a file or
|
|
directory that is pending within canola, but does not exist on disk.
|
|
|
|
cleanup_buffers_on_delete *canola.cleanup_buffers_on_delete*
|
|
type: `boolean` default: `false`
|
|
When `true`, canola will wipe any open buffer whose path matches a file that
|
|
was successfully deleted via canola. This prevents stale buffers from
|
|
appearing in the jumplist after a deletion.
|
|
|
|
--------------------------------------------------------------------------------
|
|
API *canola-api*
|
|
|
|
get_entry_on_line({bufnr}, {lnum}): nil|canola.Entry *canola.get_entry_on_line*
|
|
Get the entry on a specific line (1-indexed)
|
|
|
|
Parameters:
|
|
{bufnr} `integer`
|
|
{lnum} `integer`
|
|
|
|
get_cursor_entry(): nil|canola.Entry *canola.get_cursor_entry*
|
|
Get the entry currently under the cursor
|
|
|
|
|
|
discard_all_changes() *canola.discard_all_changes*
|
|
Discard all changes made to canola buffers
|
|
|
|
|
|
set_columns({cols}) *canola.set_columns*
|
|
Change the display columns for canola
|
|
|
|
Parameters:
|
|
{cols} `canola.ColumnSpec[]`
|
|
|
|
set_sort({sort}) *canola.set_sort*
|
|
Change the sort order for canola
|
|
|
|
Parameters:
|
|
{sort} `canola.SortSpec[]` List of columns plus direction. See :help canola-
|
|
columns to see which ones are sortable.
|
|
|
|
Examples: >lua
|
|
require("canola").set_sort({ { "type", "asc" }, { "size", "desc" } })
|
|
<
|
|
|
|
set_is_hidden_file({is_hidden_file}) *canola.set_is_hidden_file*
|
|
Change how canola determines if the file is hidden
|
|
|
|
Parameters:
|
|
{is_hidden_file} `fun(filename: string, bufnr: integer, entry: canola.Entry): boolean`
|
|
Return true if the file/dir should be hidden
|
|
|
|
toggle_hidden() *canola.toggle_hidden*
|
|
Toggle hidden files and directories
|
|
|
|
|
|
get_current_dir({bufnr}): nil|string *canola.get_current_dir*
|
|
Get the current directory
|
|
|
|
Parameters:
|
|
{bufnr} `nil|integer`
|
|
|
|
open_float({dir}, {opts}, {cb}) *canola.open_float*
|
|
Open canola browser in a floating window
|
|
|
|
Parameters:
|
|
{dir} `nil|string` When nil, open the parent of the current buffer, or
|
|
the cwd if current buffer is not a file
|
|
{opts} `nil|canola.OpenOpts`
|
|
{preview} `nil|canola.OpenPreviewOpts` When present, open the preview
|
|
window after opening canola
|
|
{vertical} `nil|boolean` Open the buffer in a vertical split
|
|
{horizontal} `nil|boolean` Open the buffer in a horizontal split
|
|
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` S
|
|
plit modifier
|
|
{cb} `nil|fun()` Called after the canola buffer is ready
|
|
|
|
toggle_float({dir}, {opts}, {cb}) *canola.toggle_float*
|
|
Open canola browser in a floating window, or close it if open
|
|
|
|
Parameters:
|
|
{dir} `nil|string` When nil, open the parent of the current buffer, or
|
|
the cwd if current buffer is not a file
|
|
{opts} `nil|canola.OpenOpts`
|
|
{preview} `nil|canola.OpenPreviewOpts` When present, open the preview
|
|
window after opening canola
|
|
{vertical} `nil|boolean` Open the buffer in a vertical split
|
|
{horizontal} `nil|boolean` Open the buffer in a horizontal split
|
|
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` S
|
|
plit modifier
|
|
{cb} `nil|fun()` Called after the canola buffer is ready
|
|
|
|
open({dir}, {opts}, {cb}) *canola.open*
|
|
Open canola browser for a directory
|
|
|
|
Parameters:
|
|
{dir} `nil|string` When nil, open the parent of the current buffer, or
|
|
the cwd if current buffer is not a file
|
|
{opts} `nil|canola.OpenOpts`
|
|
{preview} `nil|canola.OpenPreviewOpts` When present, open the preview
|
|
window after opening canola
|
|
{vertical} `nil|boolean` Open the buffer in a vertical split
|
|
{horizontal} `nil|boolean` Open the buffer in a horizontal split
|
|
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` S
|
|
plit modifier
|
|
{cb} `nil|fun()` Called after the canola buffer is ready
|
|
|
|
close({opts}) *canola.close*
|
|
Restore the buffer that was present when canola was opened
|
|
|
|
Parameters:
|
|
{opts} `nil|canola.CloseOpts`
|
|
{exit_if_last_buf} `nil|boolean` Exit vim if this canola buffer is the
|
|
last open buffer
|
|
|
|
open_preview({opts}, {callback}) *canola.open_preview*
|
|
Preview the entry under the cursor in a split
|
|
|
|
Parameters:
|
|
{opts} `nil|canola.OpenPreviewOpts`
|
|
{vertical} `nil|boolean` Open the buffer in a vertical split
|
|
{horizontal} `nil|boolean` Open the buffer in a horizontal split
|
|
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` Split
|
|
modifier
|
|
{callback} `nil|fun(err: nil|string)` Called once the preview window has
|
|
been opened
|
|
|
|
select({opts}, {callback}) *canola.select*
|
|
Select the entry under the cursor
|
|
|
|
Parameters:
|
|
{opts} `nil|canola.SelectOpts`
|
|
{vertical} `nil|boolean` Open the buffer in a vertical split
|
|
{horizontal} `nil|boolean` Open the buffer in a horizontal split
|
|
{split} `nil|"aboveleft"|"belowright"|"topleft"|"botright"` Split
|
|
modifier
|
|
{tab} `nil|boolean` Open the buffer in a new tab
|
|
{close} `nil|boolean` Close the original canola buffer once
|
|
selection is made
|
|
{handle_buffer_callback} `nil|fun(buf_id: integer)` If defined, all
|
|
other buffer related options here would be ignored. This
|
|
callback allows you to take over the process of opening
|
|
the buffer yourself.
|
|
{callback} `nil|fun(err: nil|string)` Called once all entries have been
|
|
opened
|
|
|
|
save({opts}, {cb}) *canola.save*
|
|
Save all changes
|
|
|
|
Parameters:
|
|
{opts} `nil|table`
|
|
{confirm} `nil|boolean` Show confirmation when true, never when false,
|
|
respect skip_confirm_for_simple_edits if nil
|
|
{cb} `nil|fun(err: nil|string)` Called when mutations complete.
|
|
|
|
Note:
|
|
If you provide your own callback function, there will be no notification for errors.
|
|
|
|
setup({opts}) *canola.setup*
|
|
Initialize canola
|
|
|
|
Parameters:
|
|
{opts} `canola.setupOpts|nil`
|
|
|
|
--------------------------------------------------------------------------------
|
|
COLUMNS *canola-columns*
|
|
|
|
Columns can be specified as a string to use default arguments (e.g. `"icon"`),
|
|
or as a table to pass parameters (e.g. `{"size", highlight = "Special"}`)
|
|
|
|
type *column-type*
|
|
Adapters: *
|
|
Sortable: this column can be used in view_props.sort
|
|
The type of the entry (file, directory, link, etc)
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
{icons} `table<string, string>` Mapping of entry type to icon
|
|
|
|
icon *column-icon*
|
|
Adapters: *
|
|
An icon for the entry's type (requires nvim-web-devicons)
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
{default_file} `string` Fallback icon for files when nvim-web-devicons
|
|
returns nil
|
|
{directory} `string` Icon for directories
|
|
{add_padding} `boolean` Set to false to remove the extra whitespace after
|
|
the icon
|
|
{use_slow_filetype_detection} `boolean` Set to true to detect filetypes by
|
|
reading the first lines of each file (e.g. shebangs).
|
|
|
|
size *column-size*
|
|
Adapters: files, ssh, s3
|
|
Sortable: this column can be used in view_props.sort
|
|
The size of the file
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
|
|
permissions *column-permissions*
|
|
Adapters: files, ssh
|
|
Editable: this column is read/write
|
|
Access permissions of the file
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
|
|
ctime *column-ctime*
|
|
Adapters: files
|
|
Sortable: this column can be used in view_props.sort
|
|
Change timestamp of the file
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
{format} `string` Format string (see :help strftime)
|
|
|
|
mtime *column-mtime*
|
|
Adapters: files
|
|
Sortable: this column can be used in view_props.sort
|
|
Last modified time of the file
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
{format} `string` Format string (see :help strftime)
|
|
|
|
atime *column-atime*
|
|
Adapters: files
|
|
Sortable: this column can be used in view_props.sort
|
|
Last access time of the file
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
{format} `string` Format string (see :help strftime)
|
|
|
|
birthtime *column-birthtime*
|
|
Adapters: files, s3
|
|
Sortable: this column can be used in view_props.sort
|
|
The time the file was created
|
|
|
|
Parameters:
|
|
{highlight} `string|fun(value: string): string` Highlight group, or
|
|
function that returns a highlight group
|
|
{align} `"left"|"center"|"right"` Text alignment within the column
|
|
{format} `string` Format string (see :help strftime)
|
|
|
|
--------------------------------------------------------------------------------
|
|
ACTIONS *canola-actions*
|
|
|
|
The `keymaps` option in `canola.setup` allow you to create mappings using all the same parameters as |vim.keymap.set|.
|
|
>lua
|
|
keymaps = {
|
|
-- Mappings can be a string
|
|
["~"] = "<cmd>edit $HOME<CR>",
|
|
-- Mappings can be a function
|
|
["gd"] = function()
|
|
require("canola").set_columns({ "icon", "permissions", "size", "mtime" })
|
|
end,
|
|
-- You can pass additional opts to vim.keymap.set by using
|
|
-- a table with the mapping as the first element.
|
|
["<leader>ff"] = {
|
|
function()
|
|
require("telescope.builtin").find_files({
|
|
cwd = require("canola").get_current_dir()
|
|
})
|
|
end,
|
|
mode = "n",
|
|
nowait = true,
|
|
desc = "Find files in the current directory"
|
|
},
|
|
-- Mappings that are a string starting with "actions." will be
|
|
-- one of the built-in actions, documented below.
|
|
["`"] = "actions.tcd",
|
|
-- Some actions have parameters. These are passed in via the `opts` key.
|
|
["<leader>:"] = {
|
|
"actions.open_cmdline",
|
|
opts = {
|
|
shorten_path = true,
|
|
modify = ":h",
|
|
},
|
|
desc = "Open the command line with the current directory as an argument",
|
|
},
|
|
}
|
|
|
|
Below are the actions that can be used in the `keymaps` section of config
|
|
options. You can refer to them as strings (e.g. "actions.<action_name>") or you
|
|
can use the functions directly with
|
|
`require("canola.actions").action_name.callback()`
|
|
|
|
cd *actions.cd*
|
|
:cd to the current canola directory
|
|
|
|
Parameters:
|
|
{scope} `nil|"tab"|"win"` Scope of the directory change (e.g. use |:tcd|
|
|
or |:lcd|)
|
|
{silent} `boolean` Do not show a message when changing directories
|
|
|
|
change_sort *actions.change_sort*
|
|
Change the sort order
|
|
|
|
Parameters:
|
|
{sort} `canola.SortSpec[]` List of columns plus direction (see
|
|
|canola.set_sort|) instead of interactive selection
|
|
|
|
close *actions.close*
|
|
Close canola and restore original buffer
|
|
|
|
Parameters:
|
|
{exit_if_last_buf} `boolean` Exit vim if canola is closed as the last buffer
|
|
|
|
close_float *actions.close_float*
|
|
Close canola if the window is floating, otherwise do nothing
|
|
|
|
Parameters:
|
|
{exit_if_last_buf} `boolean` Exit vim if canola is closed as the last buffer
|
|
|
|
copy_to_system_clipboard *actions.copy_to_system_clipboard*
|
|
Copy the entry under the cursor to the system clipboard
|
|
|
|
open_cmdline *actions.open_cmdline*
|
|
Open vim cmdline with current entry as an argument
|
|
|
|
Parameters:
|
|
{modify} `string` Modify the path with |fnamemodify()| using this as
|
|
the mods argument
|
|
{shorten_path} `boolean` Use relative paths when possible
|
|
|
|
open_cwd *actions.open_cwd*
|
|
Open canola in Neovim's current working directory
|
|
|
|
open_external *actions.open_external*
|
|
Open the entry under the cursor in an external program
|
|
|
|
open_terminal *actions.open_terminal*
|
|
Open a terminal in the current directory
|
|
|
|
parent *actions.parent*
|
|
Navigate to the parent path
|
|
|
|
paste_from_system_clipboard *actions.paste_from_system_clipboard*
|
|
Paste the system clipboard into the current canola directory
|
|
|
|
Parameters:
|
|
{delete_original} `boolean` Delete the original file after copying
|
|
|
|
preview *actions.preview*
|
|
Open the entry under the cursor in a preview window, or close the preview
|
|
window if already open
|
|
|
|
Parameters:
|
|
{horizontal} `boolean` Open the buffer in a horizontal split
|
|
{split} `"aboveleft"|"belowright"|"topleft"|"botright"` Split
|
|
modifier
|
|
{vertical} `boolean` Open the buffer in a vertical split
|
|
|
|
preview_scroll_down *actions.preview_scroll_down*
|
|
Scroll down in the preview window
|
|
|
|
preview_scroll_left *actions.preview_scroll_left*
|
|
Scroll left in the preview window
|
|
|
|
preview_scroll_right *actions.preview_scroll_right*
|
|
Scroll right in the preview window
|
|
|
|
preview_scroll_up *actions.preview_scroll_up*
|
|
Scroll up in the preview window
|
|
|
|
refresh *actions.refresh*
|
|
Refresh current directory list
|
|
|
|
Parameters:
|
|
{force} `boolean` When true, do not prompt user if they will be discarding
|
|
changes
|
|
|
|
select *actions.select*
|
|
Open the entry under the cursor
|
|
|
|
Parameters:
|
|
{close} `boolean` Close the original canola buffer once selection is
|
|
made
|
|
{horizontal} `boolean` Open the buffer in a horizontal split
|
|
{split} `"aboveleft"|"belowright"|"topleft"|"botright"` Split
|
|
modifier
|
|
{tab} `boolean` Open the buffer in a new tab
|
|
{vertical} `boolean` Open the buffer in a vertical split
|
|
|
|
send_to_qflist *actions.send_to_qflist*
|
|
Sends files in the current canola directory to the quickfix list, replacing the
|
|
previous entries.
|
|
|
|
Parameters:
|
|
{action} `"r"|"a"` Replace or add to current quickfix list (see
|
|
|setqflist-action|)
|
|
{only_matching_search} `boolean` Whether to only add the files that
|
|
matches the last search. This option only applies when search
|
|
highlighting is active
|
|
{target} `"qflist"|"loclist"` The target list to send files to
|
|
|
|
show_help *actions.show_help*
|
|
Show default keymaps
|
|
|
|
toggle_hidden *actions.toggle_hidden*
|
|
Toggle hidden files and directories
|
|
|
|
toggle_trash *actions.toggle_trash*
|
|
Jump to and from the trash for the current directory
|
|
|
|
yank_entry *actions.yank_entry*
|
|
Yank the filepath of the entry under the cursor to a register
|
|
|
|
Parameters:
|
|
{modify} `string` Modify the path with |fnamemodify()| using this as the
|
|
mods argument
|
|
|
|
--------------------------------------------------------------------------------
|
|
HIGHLIGHTS *canola-highlights*
|
|
|
|
CanolaEmpty *hl-CanolaEmpty*
|
|
Empty column values
|
|
|
|
CanolaHidden *hl-CanolaHidden*
|
|
Hidden entry in an canola buffer
|
|
|
|
CanolaDir *hl-CanolaDir*
|
|
Directory names in an canola buffer
|
|
|
|
CanolaDirHidden *hl-CanolaDirHidden*
|
|
Hidden directory names in an canola buffer
|
|
|
|
CanolaDirIcon *hl-CanolaDirIcon*
|
|
Icon for directories
|
|
|
|
CanolaFileIcon *hl-CanolaFileIcon*
|
|
Icon for files
|
|
|
|
CanolaSocket *hl-CanolaSocket*
|
|
Socket files in an canola buffer
|
|
|
|
CanolaSocketHidden *hl-CanolaSocketHidden*
|
|
Hidden socket files in an canola buffer
|
|
|
|
CanolaLink *hl-CanolaLink*
|
|
Soft links in an canola buffer
|
|
|
|
CanolaOrphanLink *hl-CanolaOrphanLink*
|
|
Orphaned soft links in an canola buffer
|
|
|
|
CanolaLinkHidden *hl-CanolaLinkHidden*
|
|
Hidden soft links in an canola buffer
|
|
|
|
CanolaOrphanLinkHidden *hl-CanolaOrphanLinkHidden*
|
|
Hidden orphaned soft links in an canola buffer
|
|
|
|
CanolaLinkTarget *hl-CanolaLinkTarget*
|
|
The target of a soft link
|
|
|
|
CanolaOrphanLinkTarget *hl-CanolaOrphanLinkTarget*
|
|
The target of an orphaned soft link
|
|
|
|
CanolaLinkTargetHidden *hl-CanolaLinkTargetHidden*
|
|
The target of a hidden soft link
|
|
|
|
CanolaOrphanLinkTargetHidden *hl-CanolaOrphanLinkTargetHidden*
|
|
The target of an hidden orphaned soft link
|
|
|
|
CanolaFile *hl-CanolaFile*
|
|
Normal files in an canola buffer
|
|
|
|
CanolaFileHidden *hl-CanolaFileHidden*
|
|
Hidden normal files in an canola buffer
|
|
|
|
CanolaExecutable *hl-CanolaExecutable*
|
|
Executable files in an canola buffer
|
|
|
|
CanolaExecutableHidden *hl-CanolaExecutableHidden*
|
|
Hidden executable files in an canola buffer
|
|
|
|
CanolaCreate *hl-CanolaCreate*
|
|
Create action in the canola preview window
|
|
|
|
CanolaDelete *hl-CanolaDelete*
|
|
Delete action in the canola preview window
|
|
|
|
CanolaMove *hl-CanolaMove*
|
|
Move action in the canola preview window
|
|
|
|
CanolaCopy *hl-CanolaCopy*
|
|
Copy action in the canola preview window
|
|
|
|
CanolaChange *hl-CanolaChange*
|
|
Change action in the canola preview window
|
|
|
|
CanolaRestore *hl-CanolaRestore*
|
|
Restore (from the trash) action in the canola preview window
|
|
|
|
CanolaPurge *hl-CanolaPurge*
|
|
Purge (Permanently delete a file from trash) action in the canola preview
|
|
window
|
|
|
|
CanolaTrash *hl-CanolaTrash*
|
|
Trash (delete a file to trash) action in the canola preview window
|
|
|
|
CanolaTrashSourcePath *hl-CanolaTrashSourcePath*
|
|
Virtual text that shows the original path of file in the trash
|
|
|
|
--------------------------------------------------------------------------------
|
|
ADAPTERS *canola-adapters*
|
|
|
|
Canola performs all filesystem interaction through an adapter abstraction. This
|
|
means canola can view and modify files in places beyond the local filesystem, as
|
|
long as the destination has an adapter implementation. File operations work
|
|
across adapters — you can copy files between local and remote with the same
|
|
buffer-editing workflow.
|
|
|
|
SSH *canola-adapter-ssh*
|
|
|
|
Browse files over SSH, much like netrw. Open a buffer with: >
|
|
nvim canola-ssh://[username@]hostname[:port]/[path]
|
|
<
|
|
This is the same URL format that netrw uses.
|
|
|
|
The SSH adapter does not support Windows machines, and it requires the
|
|
server to have `/bin/sh` as well as standard unix commands (`ls`, `rm`,
|
|
`mv`, `mkdir`, `chmod`, `cp`, `touch`, `ln`, `echo`).
|
|
|
|
S3 *canola-adapter-s3*
|
|
|
|
Browse files stored in AWS S3. Make sure `aws` is configured correctly,
|
|
then open a buffer with: >
|
|
nvim canola-s3://[bucket]/[path]
|
|
<
|
|
Older versions of Neovim (0.11 and earlier) don't support numbers in the
|
|
URL scheme, so use `canola-sss` instead of `canola-s3`.
|
|
|
|
Trash *canola-adapter-trash*
|
|
|
|
See |canola-trash| for details on the built-in trash adapter.
|
|
|
|
--------------------------------------------------------------------------------
|
|
RECIPES *canola-recipes*
|
|
|
|
Toggle file detail view ~
|
|
*canola-recipe-toggle-detail-view*
|
|
>lua
|
|
local detail = false
|
|
require("canola").setup({
|
|
keymaps = {
|
|
["gd"] = {
|
|
desc = "Toggle file detail view",
|
|
callback = function()
|
|
detail = not detail
|
|
if detail then
|
|
require("canola").set_columns({ "icon", "permissions", "size", "mtime" })
|
|
else
|
|
require("canola").set_columns({ "icon" })
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
})
|
|
<
|
|
|
|
Show CWD in the winbar ~
|
|
*canola-recipe-cwd-winbar*
|
|
>lua
|
|
function _G.get_canola_winbar()
|
|
local bufnr = vim.api.nvim_win_get_buf(vim.g.statusline_winid)
|
|
local dir = require("canola").get_current_dir(bufnr)
|
|
if dir then
|
|
return vim.fn.fnamemodify(dir, ":~")
|
|
else
|
|
return vim.api.nvim_buf_get_name(0)
|
|
end
|
|
end
|
|
|
|
require("canola").setup({
|
|
win_options = {
|
|
winbar = "%!v:lua.get_canola_winbar()",
|
|
},
|
|
})
|
|
<
|
|
|
|
Hide gitignored files and show git tracked hidden files ~
|
|
*canola-recipe-git-is-hidden*
|
|
>lua
|
|
local function parse_output(proc)
|
|
local result = proc:wait()
|
|
local ret = {}
|
|
if result.code == 0 then
|
|
for line in vim.gsplit(result.stdout, "\n", { plain = true, trimempty = true }) do
|
|
line = line:gsub("/$", "")
|
|
ret[line] = true
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
local function new_git_status()
|
|
return setmetatable({}, {
|
|
__index = function(self, key)
|
|
local ignore_proc = vim.system(
|
|
{ "git", "ls-files", "--ignored", "--exclude-standard", "--others", "--directory" },
|
|
{
|
|
cwd = key,
|
|
text = true,
|
|
}
|
|
)
|
|
local tracked_proc = vim.system({ "git", "ls-tree", "HEAD", "--name-only" }, {
|
|
cwd = key,
|
|
text = true,
|
|
})
|
|
local ret = {
|
|
ignored = parse_output(ignore_proc),
|
|
tracked = parse_output(tracked_proc),
|
|
}
|
|
|
|
rawset(self, key, ret)
|
|
return ret
|
|
end,
|
|
})
|
|
end
|
|
local git_status = new_git_status()
|
|
|
|
local refresh = require("canola.actions").refresh
|
|
local orig_refresh = refresh.callback
|
|
refresh.callback = function(...)
|
|
git_status = new_git_status()
|
|
orig_refresh(...)
|
|
end
|
|
|
|
require("canola").setup({
|
|
view_options = {
|
|
is_hidden_file = function(name, bufnr)
|
|
local dir = require("canola").get_current_dir(bufnr)
|
|
local is_dotfile = vim.startswith(name, ".") and name ~= ".."
|
|
if not dir then
|
|
return is_dotfile
|
|
end
|
|
if is_dotfile then
|
|
return not git_status[dir].tracked[name]
|
|
else
|
|
return git_status[dir].ignored[name]
|
|
end
|
|
end,
|
|
},
|
|
})
|
|
<
|
|
|
|
Open Telescope file finder in the current canola directory ~
|
|
*canola-recipe-telescope*
|
|
|
|
When using `get_current_dir()` in a keymap that also opens another plugin's UI
|
|
(like Telescope), capture the directory in a local variable before the call
|
|
that changes the buffer context.
|
|
>lua
|
|
require("canola").setup({
|
|
keymaps = {
|
|
["<leader>ff"] = {
|
|
desc = "Find files in the current directory",
|
|
callback = function()
|
|
local dir = require("canola").get_current_dir()
|
|
if not dir then
|
|
vim.notify("Could not get canola directory", vim.log.levels.WARN)
|
|
return
|
|
end
|
|
require("telescope.builtin").find_files({ cwd = dir })
|
|
end,
|
|
},
|
|
["<leader>fg"] = {
|
|
desc = "Live grep in the current directory",
|
|
callback = function()
|
|
local dir = require("canola").get_current_dir()
|
|
if not dir then
|
|
vim.notify("Could not get canola directory", vim.log.levels.WARN)
|
|
return
|
|
end
|
|
require("telescope.builtin").live_grep({ cwd = dir })
|
|
end,
|
|
},
|
|
},
|
|
})
|
|
<
|
|
|
|
If you need the directory after an operation that might change the current
|
|
buffer, pass the buffer number explicitly: >lua
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
-- ... some operation that changes the current buffer ...
|
|
local dir = require("canola").get_current_dir(bufnr)
|
|
<
|
|
|
|
Add custom column for file extension ~
|
|
*canola-recipe-extension-column*
|
|
>lua
|
|
local canola_cfg = require "canola.config"
|
|
local canola_constant = require "canola.constants"
|
|
local canola_column = require "canola.columns"
|
|
|
|
local FIELD_TYPE = canola_constant.FIELD_TYPE
|
|
local FIELD_NAME = canola_constant.FIELD_NAME
|
|
|
|
local function adjust_number(int)
|
|
return string.format("%03d%s", #int, int)
|
|
end
|
|
|
|
local function format(output)
|
|
return vim.fn.fnamemodify(output, ":e")
|
|
end
|
|
|
|
canola_column.register("extension", {
|
|
render = function(entry, _)
|
|
local field_type = entry[FIELD_TYPE]
|
|
local name = entry[FIELD_NAME]
|
|
|
|
if field_type == "file" then
|
|
if name then
|
|
local extension = format(name)
|
|
|
|
if not extension:match "%s" then
|
|
return extension
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
parse = function(line, _)
|
|
return line:match "^(%S+)%s+(.*)$"
|
|
end,
|
|
create_sort_value_factory = function(num_entries)
|
|
if
|
|
canola_cfg.view_options.natural_order == false
|
|
or (canola_cfg.view_options.natural_order == "fast" and num_entries > 5000)
|
|
then
|
|
return function(entry)
|
|
return format(entry[FIELD_NAME]:lower())
|
|
end
|
|
else
|
|
local memo = {}
|
|
|
|
return function(entry)
|
|
if memo[entry] == nil and entry[FIELD_TYPE] == "file" then
|
|
local name = entry[FIELD_NAME]:gsub("0*(%d+)", adjust_number)
|
|
|
|
memo[entry] = format(name:lower())
|
|
end
|
|
|
|
return memo[entry]
|
|
end
|
|
end
|
|
end,
|
|
})
|
|
|
|
require("canola").setup({
|
|
columns = {
|
|
"size",
|
|
"extension",
|
|
"icon",
|
|
},
|
|
view_options = {
|
|
sort = {
|
|
{ "type", "asc" },
|
|
{ "extension", "asc" },
|
|
{ "name", "asc" },
|
|
},
|
|
},
|
|
})
|
|
<
|
|
|
|
Disable dimming of hidden files ~
|
|
*canola-recipe-no-hidden-dimming*
|
|
|
|
By default, hidden files (toggled with `g.`) are dimmed via the `CanolaHidden`
|
|
highlight group, which links to `Comment`. To make hidden files look identical
|
|
to their visible counterparts, relink each hidden group to its non-hidden
|
|
variant after calling `setup()`:
|
|
>lua
|
|
for _, hl in ipairs(require("canola")._get_highlights()) do
|
|
local base = hl.name:match("^(Canola.+)Hidden$")
|
|
if base then
|
|
vim.api.nvim_set_hl(0, hl.name, { link = base })
|
|
end
|
|
end
|
|
<
|
|
|
|
--------------------------------------------------------------------------------
|
|
EVENTS *canola-events*
|
|
|
|
Canola emits the following |User| autocmd events. Listen for them with
|
|
|nvim_create_autocmd|: >lua
|
|
vim.api.nvim_create_autocmd("User", {
|
|
pattern = "CanolaEnter",
|
|
callback = function(args)
|
|
vim.print("Entered canola buffer: " .. args.data.buf)
|
|
end,
|
|
})
|
|
<
|
|
|
|
CanolaEnter *CanolaEnter*
|
|
Fired once per canola buffer, after the initial directory listing has been
|
|
rendered and the buffer is ready. The `args.data.buf` field contains the
|
|
buffer number. Use this event for one-time buffer setup such as setting
|
|
keymaps or window options.
|
|
|
|
CanolaReadPost *CanolaReadPost*
|
|
Fired after every successful buffer render, including the initial render
|
|
and all subsequent re-renders (e.g. after directory changes, refreshes, or
|
|
mutations). The `args.data.buf` field contains the buffer number. Use this
|
|
event for logic that must run each time the directory listing updates.
|
|
|
|
CanolaMutationComplete *CanolaMutationComplete*
|
|
Fired after all pending mutations (create, delete, rename, move, copy)
|
|
have been executed and the affected buffers have been re-rendered. No
|
|
additional data fields. Use this event for post-mutation side effects such
|
|
as refreshing external status indicators.
|
|
|
|
CanolaFileCreated *CanolaFileCreated*
|
|
Fired after a new file is successfully created on the local filesystem.
|
|
The `args.data.path` field contains the absolute path of the created file.
|
|
Use this event to populate initial file contents, run formatters, or
|
|
perform any other setup on newly created files. >lua
|
|
vim.api.nvim_create_autocmd("User", {
|
|
pattern = "CanolaFileCreated",
|
|
callback = function(args)
|
|
local path = args.data.path
|
|
if path:match("%.sh$") then
|
|
vim.fn.writefile({ "#!/usr/bin/env bash" }, path)
|
|
end
|
|
end,
|
|
})
|
|
<
|
|
|
|
--------------------------------------------------------------------------------
|
|
TRASH *canola-trash*
|
|
|
|
|
|
Canola has built-in support for using the system trash. When
|
|
`delete_to_trash = true`, any deleted files will be sent to the trash instead
|
|
of being permanently deleted. You can browse the trash for a directory using
|
|
the `toggle_trash` action (bound to `g\` by default). You can view all files
|
|
in the trash with `:Canola --trash /`.
|
|
|
|
To restore files, simply move them from the trash to the desired destination,
|
|
the same as any other file operation. If you delete files from the trash they
|
|
will be permanently deleted (purged).
|
|
|
|
Linux:
|
|
Canola supports the FreeDesktop trash specification.
|
|
https://specifications.freedesktop.org/trash/1.0/
|
|
All features should work.
|
|
|
|
Mac:
|
|
Canola has limited support for MacOS due to the proprietary nature of the
|
|
implementation. The trash bin can only be viewed as a single dir
|
|
(instead of being able to see files that were trashed from a directory).
|
|
|
|
Windows:
|
|
Canola supports the Windows Recycle Bin. All features should work.
|
|
|
|
================================================================================
|
|
vim:tw=80:ts=2:ft=help:norl:syntax=help:
|