Commit graph

89 commits

Author SHA1 Message Date
1e5c96ca2c
feat(config): add per-host/bucket extra args for SSH, S3, and FTP
Problem: `extra_scp_args`, `extra_s3_args`, and `extra_curl_args` are
global — there's no way to pass adapter-specific args only to a single
host or bucket (e.g. `-O` for a Synology NAS that requires legacy SCP
protocol, or `--endpoint-url` for an R2 bucket).

Solution: add `ssh_hosts`, `s3_buckets`, and `ftp_hosts` config tables
that map exact hostnames/bucket names to per-target arg lists. Per-target
args are appended after the global args at call time. The `scp()` helper
in `ssh.lua` accepts a `hosts` list so cross-host copies deduplicate
host lookups. `create_s3_command` in `s3fs.lua` extracts the bucket from
the command args with no call-site changes needed. `resolved_curl_args`
in `ftp.lua` is called by both `curl()` and `ftpcmd()`.

Based on: stevearc/oil.nvim#607
2026-03-18 12:37:14 -04:00
Barrett Ruth
a410507846
feat(ftp): add FTP/FTPS adapter via curl (#167)
* feat(ftp): add FTP/FTPS adapter via curl

Problem: canola has no way to browse or edit files on FTP servers,
despite the adapter system being designed for exactly this pattern.
curl speaks FTP natively, including FTPS (FTP over TLS), and requires
no new dependencies.

Solution: implement `lua/oil/adapters/ftp.lua` with `oil-ftp://` and
`oil-ftps://` schemes. Parses Unix and IIS LIST output, supports
`size`, `mtime`, and `permissions` columns, and implements the full
adapter API (list, read_file, write_file, render_action, perform_action).
Same-host renames use RNFR/RNTO; cross-host and local↔FTP copies use
curl download/upload through a tmpfile. Adds `extra_curl_args` config
option and documents the adapter in `doc/oil.txt`.

Based on: stevearc/oil.nvim#210

* docs(upstream): mark #210 fixed in #167

* fix(ftp): use python3 ftplib for control-channel FTP operations

Problem: DELE, RMD, MKD, and RNFR/RNTO were implemented using
curl --quote, which requires a subsequent LIST or STOR to trigger
the FTP connection. That data-channel operation hangs on slow or
busy servers, making every mutation appear stuck.

Solution: replace the curl --quote approach with a python3 ftplib
one-liner for all control-channel operations. ftplib executes DELE,
RMD, MKD, RNFR/RNTO, and SITE CHMOD without opening a data channel,
so they complete instantly. The curl wrapper is retained for LIST,
read_file, and write_file, which genuinely need a data channel.

* fix(ftp): use nil entry ID so cache assigns unique IDs

Problem: `M.list` returned entries as `{0, name, type, meta}`.
`cache.store_entry` only assigns a fresh ID when `entry[FIELD_ID] == nil`;
passing 0 caused every entry to be stored as ID 0, all overwriting
each other. `get_entry_by_id(0)` then always returned the last-stored
entry, breaking navigation (always opened the same file), rename
(wrong entry matched), and create (wrong diff).

Solution: change the placeholder from 0 to nil, matching how
`cache.create_entry` itself builds entries.

* fix(ftp): use ftp.rename() for RNFR/RNTO and raw Python lines in ftpcmd

Problem: `ftpcmd` wrapped every command in `ftp.voidcmd()`, which
expects a final 2xx response. `RNFR` returns 350 (intermediate),
so `voidcmd` raised an exception before `RNTO` was ever sent,
causing every rename to fail with '350 Ready for destination name'.

Solution: change `ftpcmd` to accept raw Python lines instead of FTP
command strings, then use `ftp.rename(src, dst)` for the rename case.
`ftplib.rename` handles the 350 intermediate response correctly
internally. All other callers now wrap their FTP commands in
`ftp.voidcmd()` explicitly.

* fix(ftp): recursively delete directory contents before RMD

Problem: FTP's RMD command fails with '550 Directory not empty'
if the directory has any contents. Unlike the S3 adapter which uses
`aws s3 rm --recursive`, FTP has no protocol-level recursive delete.

Solution: emit a Python rmtree helper inside the ftpcmd script that
walks the directory via MLSD, recursively deletes children (DELE for
files, rmtree for subdirs), then sends RMD on the now-empty directory.

* fix(ftp): give oil-ftps:// its own adapter name to prevent scheme clobbering

Problem: both oil-ftp:// and oil-ftps:// mapped to the adapter name
'ftp', so config.adapter_to_scheme['ftp'] was set to whichever scheme
pairs() iterated last — non-deterministic. init.lua uses
adapter_to_scheme[adapter.name] to reconstruct the parent URL, so
roughly half the time it injected 'oil-ftps://' into ftp:// buffer
navigation, causing the ssl-reqd error on '-' press.

Solution: register oil-ftps:// under adapter name 'ftps' via a
one-line shim that returns require('oil.adapters.ftp'). Now
adapter_to_scheme['ftp'] = 'oil-ftp://' and
adapter_to_scheme['ftps'] = 'oil-ftps://' are both stable.

* fix(ftp): percent-encode path in curl FTP URLs

Problem: filenames containing spaces (or other URL-unsafe characters)
caused curl to fail with "Unknown error" because the raw path was
concatenated directly into the FTP URL.

Solution: add `url_encode_path` to encode non-safe characters (excluding
`/`) before building the curl URL in `curl_ftp_url`.

* fix(ftp): fix STARTTLS, error visibility, and robustness

Problem: `curl_ftp_url` emitted `ftps://` (implicit TLS) for
`oil-ftps://` URLs, causing listing to fail against STARTTLS servers
while Python mutations worked — the two paths spoke different TLS
modes. curl's `-s` flag silenced all error output, producing "Unknown
error" on any curl failure. File creation used `/dev/null` (breaks on
Windows, diverges from S3). The Python TLS context didn't honour
`--insecure`/`-k` from `extra_curl_args`. Deleting non-empty dirs on
servers without MLSD gave a cryptic `500 Unknown command`.

Solution: Always emit `ftp://` in `curl_ftp_url`; TLS is enforced
solely via `--ssl-reqd`, making STARTTLS consistent between curl and
Python. Add `-S` to expose curl errors. Replace `/dev/null` with
`curl -T -` + `stdin='null'` (matches `s3fs` pattern). Mirror
`--insecure`/`-k` into the Python SSL context. Wrap `mlsd()` in
try/except with a clear actionable message. Add `spec/ftp_spec.lua`
with 28 unit tests covering URL parsing, list parsing, and curl URL
building. Update `doc/oil.txt` to document STARTTLS and MLSD.

* ci: format

* fix(ftp): resolve LuaLS type warnings in `curl` wrapper and `parse_unix_list_line`
2026-03-17 23:47:20 -04:00
Barrett Ruth
adff65b377 feat(columns): per-character permission column highlights (#375) (#146)
Problem: the permissions column rendered as a monolithic unstyled
string, making it hard to scan `rwx` bits at a glance.

Solution: add per-character highlight groups for permission characters
following the `eza`/`lsd` convention. All groups link to standard
Neovim highlights so every colorscheme works out of the box.
2026-03-16 15:53:23 -04:00
Barrett Ruth
42903b6beb refactor: revert canola namespace to oil and remove vim.g config (#120)
* refactor: revert module namespace from canola back to oil

Problem: the canola rename creates unnecessary friction for users
migrating from stevearc/oil.nvim — every `require('oil')` call and
config reference must change.

Solution: revert all module paths, URL schemes, autocmd groups,
highlight groups, and filetype names back to `oil`. The repo stays
`canola.nvim` for identity; the code is a drop-in replacement.

* refactor: remove `vim.g.oil` declarative config

Problem: the `vim.g.oil` configuration path was added prematurely.
It adds a second config entrypoint before the plugin has stabilized
enough to justify it.

Solution: remove `vim.g.oil` support from `plugin/oil.lua`,
`config.setup()`, docs, and tests. Users configure via
`require("oil").setup({})`.
2026-03-10 22:49:56 -04:00
Barrett Ruth
0d3088f57e
refactor: rename oil to canola across entire codebase (#70)
Problem: the codebase still used the upstream \`oil\` naming everywhere —
URL schemes, the \`:Oil\` command, highlight groups, user events, module
paths, filetypes, buffer/window variables, LuaCATS type annotations,
vimdoc help tags, syntax groups, and internal identifiers.

Solution: mechanical rename of every reference. URL schemes now use
\`canola://\` (plus \`canola-ssh://\`, \`canola-s3://\`, \`canola-sss://\`,
\`canola-trash://\`, \`canola-test://\`). The \`:Canola\` command replaces
\`:Oil\`. All highlight groups, user events, augroups, namespaces,
filetypes, require paths, type annotations, help tags, and identifiers
follow suit. The \`upstream\` remote to \`stevearc/oil.nvim\` has been
removed and the \`vim.g.oil\` deprecation shim dropped.
2026-03-05 14:50:10 -05:00
Barrett Ruth
86f553cd0a
build: replace luacheck with selene, add nix devshell and pre-commit (#20)
* build: replace luacheck with selene

Problem: luacheck is unmaintained (last release 2018) and required
suppressing four warning classes to avoid false positives. It also
lacks first-class vim/neovim awareness.

Solution: switch to selene with std='vim' for vim-aware linting.
Replace the luacheck CI job with selene, update the Makefile lint
target, and delete .luacheckrc.

* build: add nix devshell and pre-commit hooks

Problem: oil.nvim had no reproducible dev environment. The .envrc
set up a Python venv for the now-removed docgen pipeline, and there
were no pre-commit hooks for local formatting checks.

Solution: add flake.nix with stylua, selene, and prettier in the
devshell. Replace the stale Python .envrc with 'use flake'. Add
.pre-commit-config.yaml with stylua and prettier hooks matching
other plugins in the repo collection.

* fix: format with stylua

* build(selene): configure lints and add inline suppressions

Problem: selene fails on 5 errors and 3 warnings from upstream code
patterns that are intentional (mixed tables in config API, unused
callback parameters, identical if branches for readability).

Solution: globally allow mixed_table and unused_variable (high volume,
inherent to the codebase design). Add inline selene:allow directives
for the 8 remaining issues: if_same_then_else (4), mismatched_arg_count
(1), empty_if (2), global_usage (1). Remove .envrc from tracking.

* build: switch typecheck action to mrcjkb/lua-typecheck-action

Problem: oil.nvim used stevearc/nvim-typecheck-action, which required
cloning the action repo locally for the Makefile lint target. All
other plugins in the collection use mrcjkb/lua-typecheck-action.

Solution: swap to mrcjkb/lua-typecheck-action@v0 for consistency.
Remove the nvim-typecheck-action git clone from the Makefile and
.gitignore. Drop LuaLS from the local lint target since it requires
a full language server install — CI handles it.
2026-02-21 23:52:27 -05:00
c6b4a7a07b
feat: add configurable file and directory creation permissions
Problem: files were always created with mode 0644 and directories
with 0755, hardcoded in fs.touch and uv.fs_mkdir. Users who need
different defaults (e.g. 0600 for security) had no config option.

Solution: add new_file_mode (default 420 = 0644) and new_dir_mode
(default 493 = 0755) config options, passed through to fs.touch and
uv.fs_mkdir in the files and mac trash adapters. The fs.touch
signature accepts an optional mode parameter with backwards
compatibility (detects function argument to support old callers).
Local cache directories (SSH, S3) continue using standard system
permissions rather than the user-configured mode.

Based on: stevearc/oil.nvim#537
2026-02-20 20:26:07 -05:00
b92ecb04ee fix(trash): update freedesktop trash specification url
Problem: the old URL specifications.freedesktop.org/trash-spec/1.0/ is
dead and redirects or 404s.

Solution: update to the current URL trash/1.0/ in docs, source, and
the generation script.

Cherry-picked from: stevearc/oil.nvim#722
2026-02-20 16:15:27 -05:00
Steven Arcangeli
81b8a91735 cleanup: remove deprecated trash_command 2026-01-01 00:22:58 -05:00
Muhammad Imaduddin
634049414b
fix: open files under cwd with relative name (#693) 2025-12-29 10:15:58 -08:00
Daniel Kongsgaard
e5bd931edb
feat: new adapter for S3 buckets (#677)
* Added s3 support

* Save work

* Various bug fixes

* Minor cleanup

* Minor bug fixes

* Fix typo

* Update following feedback + minor bug fix

* Fix CI

* Cleanup and remove bucket entry_type

* Make suggested changes

* Better aws existence check

* Fix typo

* refactor: don't bother caching aws executable status

---------

Co-authored-by: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
2025-11-30 12:41:37 -08:00
Steven Arcangeli
71948729cd lint: use more specific type for internal entries 2025-10-15 10:42:52 -07:00
jiz4oh
1498d2fccf
fix: ssh adapter supports iso8601 dates (#635)
* fix: add iso8601 format compatibility

* Update sshfs.lua

---------

Co-authored-by: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
2025-06-30 16:54:22 -07:00
Steven Arcangeli
5b38bfe279 doc: fix typecheck errors in nvim 0.11 2025-03-30 21:56:41 -07:00
Steven Arcangeli
ba1f50a9a8 fix: file time column escapes ()[] chars in parser (#603) 2025-03-30 15:14:11 -07:00
Luis Calle
8649818fb2
fix(trash-win): don't hang when shellslash is enabled (#592) 2025-03-19 15:10:58 -07:00
Steven Arcangeli
d7c61c7084 fix: silent handling when buffer has no oil adapter (#573) 2025-03-04 12:57:01 -08:00
Steven Arcangeli
32dd3e378d feat: most moves and copies will copy the undofile (#583) 2025-02-13 09:40:01 -08:00
Steven Arcangeli
5313690956 fix: more robust parsing of custom column timestamp formats (#582) 2025-02-12 22:12:24 -08:00
Steven Arcangeli
09fa1d22f5
fix: work around incorrect link detection on windows (#557)
* fix: work around incorrect link detection on windows

* fix: gracefully handle lstat error on windows
2025-01-13 10:22:59 -08:00
Steven Arcangeli
7c26a59ac0
fix: gracefully handle fs_stat failures (#558)
* fix: gracefully handle fs_stat failures

* fix: make log methods safe to call in luv callbacks

* fix: replace another vimscript call
2025-01-12 14:29:46 -08:00
Benedict Ozua
7041528bde
fix: support permissions checks on windows and virtual filesystems (#555)
* use access(2) over file permission checks to workaround systems that change expected file permission view

* cleanup: delete unused function

---------

Co-authored-by: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
2025-01-07 21:04:25 -08:00
Steven Arcangeli
254bc6635c fix: guard against nil metadata values (#548) 2025-01-04 12:52:26 -08:00
Steven Arcangeli
c6a39a69b2 fix: stat files if fs_readdir doesn't provide a type (#543) 2025-01-03 11:55:50 -08:00
Steven Arcangeli
1f7da07a3e refactor: remove overcomplicated meta_fields abstraction
This abstraction is overly generic for what it does. It's only ever used
to help us conditionally perform a fs_stat for the local files adapter.
We can replace that with a much dumber, much simpler bit of logic.
2025-01-03 11:55:50 -08:00
Steven Arcangeli
0472d9296a lint: fix typechecking for new LuaLS version 2024-11-13 08:58:16 -08:00
Éric NICOLAS
9e6fb844fe
doc: Update links to FreeDesktop's Trash spec (#490)
Keeping it pinned to 1.0

Fixes #489
2024-09-30 22:11:09 -07:00
Steven Arcangeli
1360be5fda lint: stricter type checking 2024-09-17 13:00:48 -07:00
Steven Arcangeli
b05374428e fix: wrap git rm callback in schedule_wrap (#475) 2024-09-10 11:44:15 -07:00
Steven Arcangeli
70337eb77f fix: gracefully handle trashing file that does not exist 2024-08-25 20:51:27 -07:00
Anna Arad
71c972fbd2
fix: Force standard C locale when getting ls input for parsing in SSH (#455) 2024-07-22 21:17:38 -07:00
Anna Arad
a6cea1a5b9
fix: Handle users and groups with spaces over SSH (#448) 2024-07-21 16:07:10 -07:00
DerpDays
65c53dbe4f
fix: correctly check group permissions in unix (#428)
* fix: set modifiable when user in group

* feat: add mode caching, fallback to previous, and better checking of permissions

* fix: make is_modifiable check group permissions even if the user is owner of the directory

* refactor: simplify group ID caching

---------

Co-authored-by: Steven Arcangeli <stevearc@stevearc.com>
2024-06-21 09:07:03 -04:00
Steven Arcangeli
2cb39e838e doc: more detailed type annotations for setup() call 2024-05-22 13:46:11 -07:00
Steven Arcangeli
27d9f37161 ci: run tests against Neovim v0.10.0 2024-05-16 11:09:15 -07:00
Steven Arcangeli
3283deec96 lint: ignore some type errors 2024-05-13 20:02:11 -06:00
Steven Arcangeli
aa0c00c7fd fix(ssh): bad argument when editing files over ssh (#370) 2024-05-13 11:35:50 -06:00
ericguin
3abb6077d7
fix(ssh): config option to pass extra args to SCP (#340)
* Adding in SCP options configuration

This changeset adds in additional SCP options to the config. This allows
the user to specify a list of flags to send to the SCP command that will
be expanded into each shell command.

The primary driver for this is from newe boxes SSHing into pre 9 openSSH
boxes. New openSSH uses sftp server under the hood, rather than the
older SCP protocol. If you go into a system that does not have these
changes, SCP fails to work. The '-O' command line flag was introduced to
resolve this.

Using this change, the user can now pass in `extra_scp_options = {"-O"}`
to resolve the issue.

* Replacing table.unpack with global unpack

* lint: apply stylua

* refactor: change option name and shuffle config around

---------

Co-authored-by: Eric Guinn <eric_guinn@selinc.com>
Co-authored-by: Steven Arcangeli <stevearc@stevearc.com>
2024-05-06 10:42:23 -07:00
Steven Arcangeli
bcfc0a2e01 fix(ssh): garbled output when directory has broken symlinks
The stderr was interleaving with the stdout when performing one of the
ls operations. This was causing the parsing to sometimes fail and crash.
2024-05-01 16:10:10 -07:00
Matthew Wilding
f3a31eba24
fix(windows): navigating into drive letter root directories (#341)
* Fixed drive browsing on windows

* Fixed naming

* fix: Uppercase drive letter only

* updated: Filter out network drives on windows

* Update files.lua

* Update files.lua

* fixed: mapped drives

* addslash to check for double slash

* Fixed indents

* Reverted addslash change

* Fixed windows initial buffer name

* Reverted formatting

* Cleaned up callback

* Fix addslash to handle \ too

* Allow running tests workflow from fork

* Fix workflow

* Test

* Tests

* refactor: readability and comments

* fix: convert buffer name to posix when hijacking directory

---------

Co-authored-by: Steven Arcangeli <stevearc@stevearc.com>
2024-04-23 22:06:59 -07:00
Steven Arcangeli
96f0983e75 fix(windows): file operation preview uses only backslash path separator (#336) 2024-04-23 20:57:22 -07:00
Steven Arcangeli
1f05774e1c feat: experimental support for git operations (#290) 2024-04-19 18:00:44 -04:00
Kevin Oberlies
8bb35eb81a
fix(ssh): escape all file paths for the ssh adapter (#353)
* Escape all paths for ssh file changes using `vim.fn.shellescape()`

* Change away from `vim.fn.shellescape` to custom implementation

Really, only escape `'` with `'\\''` so that it will:
- exit the single quote mode
- escape out a single quote character
- and get back into the single quote mode

Also format long line so linter doesn't complain

* Adding doc comments to the shellescape function

* Adding actual words to the doc comment
2024-04-17 16:19:10 -04:00
Luis Calle
18dfd2458d
fix(windows): can delete non-ascii filenames to trash (#323) 2024-03-04 10:50:22 -08:00
Reinder van Bochove
e27cc4e138
feat: add border config for SSH and keymaps help window (#299)
* feat: add config for ssh window border

* chore: add documentation for ssh window border

* feat: add config for keymaps help window border

* chore: add documentatoin for keymaps help window border
2024-02-19 20:00:49 -08:00
TheLeoP
e71b6caa95
perf(windows): use a single powershell process for trash operations (#271)
* perf(trash_windows): use a single powershell instance for operations

* refactor(trash_windows): encapsulate powershell connection logic

* refactor(windows_trash): better name for functions

* fix(windows_trash): set connection error on initializatino if needed

* refactor(windows_trash): simplify initialization code

* refactor: extract some powershell logic into separate file

---------

Co-authored-by: Steven Arcangeli <stevearc@stevearc.com>
2024-01-15 17:22:11 -08:00
Steven Arcangeli
49b2b3f4a5 fix(trash): mac error deleting dangling symbolic links to trash (#251) 2024-01-07 12:13:37 -08:00
TheLeoP
553b7a0ac1
feat(trash): support for deleting to windows recycle bin (#243)
* feat(windows-trash): support for deleting to windows trash

* feat(windows-trash): add support for view, restore and purge

* fix(windows-trash): undefined path on M.list

* chore(windows-trash): modify comments

* fix(windows-trash): show correct original_path

* fix(windows-trash): add self to powershell_date_grammar

* fix(windows-trash-support): parse deleted date as number

* fix(fs): do not add innecesary \\ on Windows

* feat: extend windows trash adapter

* perf(windows-trash): powershell -> libuv (move, purge and copy)

* fix: don't prompt to save when opening trashed file

* lint: fix luacheck error

* lint: fix luacheck errors

* lint: luacheck error

---------

Co-authored-by: Steven Arcangeli <506791+stevearc@users.noreply.github.com>
2024-01-02 22:05:01 -08:00
Steven Arcangeli
5d9e4368d4 fix(trash): error deleting dangling symbolic links to trash (#251) 2023-12-26 18:03:00 +00:00
Steven Arcangeli
e89a8f8ade fix: crash in ssh and trash adapter detail columns (#235) 2023-11-24 14:13:56 -08:00